Bart Simons

Bart Simons

Swift + Perfect + Authentication API Example

 •  Filed under swift, perfect, example, authentication

So you want to build your next backend API in Swift? No problem! Perfect has got you covered. Perfect is a collection of libraries to turn your next server-side Swift application into reality. It is blazingly fast and awesome!

So I decided to dive a little bit deeper into the possibilities of Swift and Perfect, and so I quickly decided that an authentication API would be a 'perfect' idea (see what I did there?) to get started. So, what do we need?

  • A computer running Linux or macOS
  • Swift 3 installed
  • A MySQL Server

That's basically all that's needed. Now we need a functional database, so I came up with a SQL table like this:

CREATE TABLE `users` (  
  `id` int(32) NOT NULL,
  `username` varchar(128) NOT NULL,
  `password` varchar(128) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `users`  
  ADD PRIMARY KEY (`id`),
  ADD UNIQUE KEY `username` (`username`),
  ADD UNIQUE KEY `id` (`id`);

INSERT INTO `users` (`id`, `username`, `password`) VALUES  
(1, 'user1', '7c12772809c1c0c3deda6103b10fdfa0'),
(2, 'user2', '7c12772809c1c0c3deda6103b10fdfa0');

ALTER TABLE `users`  
  MODIFY `id` int(32) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;

Perfect. Now - for the next part - initialize a new Swift package in an empty folder:

swift package init --type executable  

This will generate an empty package, so now it's time to edit our Package.swift file. You'll need to add 2 repositories to your project, this is how I have done that:

import PackageDescription

let package = Package(  
    name: "backend-api",
    dependencies: [
        .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 2),
        .Package(url: "https://github.com/PerfectlySoft/Perfect-MySQL.git", majorVersion: 2)
    ]
)

Cool. So now it is about time to work on the real deal - programming! I've split the project into two separate swift files

  • main.swift : this is for all things related to the web server processing
  • DBOperations.swift : this is a provider to execute MySQL (prepared) statements in the form of a function

My main.swift:

import PerfectLib  
import PerfectHTTP  
import PerfectHTTPServer  
import Foundation

let server = HTTPServer()  
var routes = Routes()

server.serverPort = 8000

routes.add(method: .get, uri: "/", handler: {  
    request, response in

    response.status = HTTPResponseStatus.ok
    response.setHeader(.contentType, value: "application/json")

    response.setBody(string: "{\"status\":\"Backend server is up and running\"}")
    response.completed()
})

routes.add(method: .get, uri: "/oauth", handler: {  
    request, response in

    response.setHeader(.contentType, value: "application/json")

    let username = request.param(name: "username", defaultValue: "")
    let password = request.param(name: "password", defaultValue: "")

    if ((username ?? "").isEmpty || (password ?? "").isEmpty) {
        response.status = HTTPResponseStatus.ok
        response.setBody(string: "{\"status\":\"Invalid request\"}")
    } else {
        if (username ?? "").characters.count >= 8 || (username ?? "").characters.count <= 24{
            if (password ?? "").characters.count == 32 {
                if (username ?? "").rangeOfCharacter(from: CharacterSet.alphanumerics.inverted) == nil && (password ?? "").rangeOfCharacter(from: CharacterSet.alphanumerics.inverted) == nil {
                    response.status = HTTPResponseStatus.ok
                    let queryResult: [[[String: String]]] = executeQuery(query: "SELECT * FROM users WHERE username = ?", parameters: [username ?? ""])
                    if (queryResult.count == 1) {
                        if  ((queryResult[0][2]["password"] ?? "") as String == password ?? "") {
                            response.setBody(string: "{\"status\":\"Authentication request succeeded!\"}")
                        } else {
                            response.setBody(string: "{\"status\":\"Invalid request\"}")
                        }
                    } else {
                        response.setBody(string: "{\"status\":\"Invalid request\"}")
                    }
                } else {
                    response.status = HTTPResponseStatus.ok
                    response.setBody(string: "{\"status\":\"Invalid request\"}")
                }
            } else {
                response.status = HTTPResponseStatus.ok
                response.setBody(string: "{\"status\":\"Invalid request\"}")
            }
        } else {
            response.status = HTTPResponseStatus.ok
            response.setBody(string: "{\"status\":\"Invalid request\"}")
        }
    }

    response.completed()
})

server.addRoutes(routes)

do {  
    try server.start()
} catch PerfectError.networkError(let err, let msg) {
    print("Network error thrown: \(err) \(msg)")
}

My DBOperations.swift:

import MySQL

let sqlHost     = "127.0.0.1"  
let sqlUser     = "app_authentication"  
let sqlPassword = "testing01"  
let sqlDB       = "app_authentication"

func executeQuery(query: String, parameters: [String]? = nil) -> [[[String: String]]] {  
    var fStore = [[[String: String]]]()

    let mysql = MySQL()
    let connection = mysql.connect(host: sqlHost, user: sqlUser, password: sqlPassword, db: sqlDB)

    guard connection else {
        print(mysql.errorMessage())
        return [[[String: String]]]()
    }

    defer {
        mysql.close()
    }

    let fQuery = MySQLStmt(mysql)

    _ = fQuery.prepare(statement: query)

    if parameters != nil {
        for parameter in parameters! {
            fQuery.bindParam(parameter)
        }
    }

    _ = fQuery.execute()

    let fResults = fQuery.results()

    _ = fResults.forEachRow {
        e in

        var fSubStore = [[String: String]]()

        for i in 0..<e.count {
            let fFieldName: String? = fQuery.fieldInfo(index: i)!.name
            fSubStore.append(["\(fFieldName!)": "\(e[i]!)"])
        }

        fStore.append(fSubStore)
    }

    return fStore
}

You need to save both of these files inside the Sources folder of your package.

To build and run your app, run this in the root of your package directory:

swift build  
.build/debug/backend-api

Now it is time to test if everything works:
It works!

So: this is how you can build authentication APIs with Swift. Please bare in mind that all code examples given are NOT production ready. I know that it works, but I can definitely already see some points of improvements - too bad that I don't have time to fix that. This demo project is soon to be expected on my GitHub. Thanks for reading and have a nice day ;)

Pretty URLs in Apache - Getting Started Tutorial

 •  Filed under getting started, tutorial, Pretty URLs, Apache, mod_rewrite

Ever wanted to create beautiful URLs for your self-hosted websites? This page has got you covered with all just about the information you need to get started. All functionality which is demonstrated on this page is made possible by mod_rewrite, also known as the 'rewrite' module. This module gives system administrators the possibility to rewrite URL paths, as I shall demonstrate:

Let's say Mr. X has a website called mywebsite.corp with a single page called test.php. He currently visits his page like this:

mywebsite.corp before any modification

Mr.X is not happy with how the php file extension is exposed to the user, and wants to make his page URL look better by removing the php extension. The first thing he has to do is to enable the rewrite module which is fairly simple:

a2enmod rewrite  

Now, all that is left to do is to write the rewrite rule inside a Directory tag in apache.conf like this:

<Directory /var/www/html>  
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        RewriteEngine On
        RewriteRule ^test$ /test.php
</Directory>  

Restart your web server with service apache2 restart and let's see if things work the way we want:

mywebsite.corp after modification

It works! Now, let's dive deeper into what the RewriteRule line does

^ is the character to indicate the root URL of your website
$ is the character to indicate the end of the URL to match

This has been a simple demonstration of what you can do and accomplish with mod_rewrite. However, you can do so much more with this module. Let's take a look at regular expressions and how to use with rewrite rules:

mywebsite.corp before second modification

With this directory block:

<Directory /var/www/html>  
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        RewriteEngine On
        RewriteRule ^users/(\d+)$ /users.php?id=$1
</Directory>  

We are able to create an URL rewrite that functions like this:

mywebsite.corp after second modification

As you can see, you can use capture groups to capture URL parameters which you can use like in the previous example. Pretty handy!

This is just a handful of possibilities, I hope that you find 'em useful. Thanks for reading!

Mirrored object storage with Minio

 •  Filed under mirrored, distributed, object storage, minio

Object storage is on the rise - and that's not without a reason. When you have to deal with large pieces of data, it is very necessary to keep your data organised. Minio offers a perfectly fitted solution that is open source and well documented: it's an Amazon S3 compatible object storage server written in Go. Want to learn more about the project? This is their homepage.

Setting up a single node is a piece of cake, as I shall demonstrate.

# Create a directory for storing 'objects'
mkdir ~/ObjectStorage

# Download and prepare the Minio mode
wget https://dl.minio.io/server/minio/release/linux-amd64/minio  
chmod +x minio  
export MINIO_ACCESS_KEY=yourusernamegoeshere  
export MINIO_SECRET_KEY=yourpasswordgoeshere

# Run the Minio node
./minio server ~/ObjectStorage

It's that easy! You can now access your Minio node at port 9000, try it out in your browser.

Running a clustered of two nodes is also very easy. Let's say that we have two servers in a subnet:

minio01: 10.129.17.224  
minio02: 10.129.17.227  

We also have two disks on each server, so that means we also have the following folders mounted to their own disk:

/opt/storage01: disk 1
/opt/storage02: disk 2

Run the following command on both servers to initialise the cluster:

MINIO_ACCESS_KEY=yourusernamegoeshere MINIO_SECRET_KEY=yourpasswordgoeshere ./minio server http://10.129.17.227/opt/storage01 http://10.129.17.227/opt/storage02 http://10.129.17.224/opt/storage01 http://10.129.17.224/opt/storage02  

It automatically detects if a server is up or down, and automatically syncs data between nodes.

Compiling a LAMP stack for CentOS - Tutorial

 •  Filed under compiling, compile, LAMP, stack, CentOS

Getting the latest version of Apache, MySQL and PHP up and running on enterprise Linux operating systems can be a tough job. There are easy and quick ways to get around this, number 1 being the use of third-party repositories: but do you really want to use open-source software from third parties that might have been fiddled around with? I know, I can be a little bit paranoid about this. But, there is a second option: compiling the LAMP stack from source. This allows you to get control over:

  • The software versions used in the stack
  • The code that you compile (being able to review the code)
  • Enable and disable functionality that you (don't) need

Compiling the LAMP stack on CentOS from source is not that hard anymore, because I have written a script that automates the build process for you.

My script fetches all the source code that is needed to compile the LAMP stack and then compiles every dependency and/or program one by one. Afterwards, all the software gets installed in the /opt folder (you can always change this if you want).

So - without further ado - here's the script:

#!/bin/bash

TIMEFORMAT='It took %R seconds to complete this process.'  
time {  
    # Defining variables
    threads=$[$(nproc)+1]

    # Build local yum cache and install development tools
    echo "[1/16] Installing the 'Developer Tools' yum group.."
    yum -y makecache >/dev/null 2>&1 && yum -y groupinstall "Development Tools" >/dev/null 2>&1

    # Install other necessary binaries
    echo "[2/16] Installing necessary binaries.."
    yum -y install curl cmake >/dev/null 2>&1

    # Download source tarballs
    echo "[3/16] Downloading source tarballs.."
    curl ftp://xmlsoft.org/libxml2/LATEST_LIBXML2 -o libxml2.tar.gz >/dev/null 2>&1
    curl https://www.openssl.org/source/openssl-1.0.2l.tar.gz -o openssl.tar.gz >/dev/null 2>&1
    curl https://www.zlib.net/zlib-1.2.11.tar.gz -o zlib.tar.gz >/dev/null 2>&1
    curl http://apache.40b.nl/httpd/httpd-2.4.25.tar.gz -o httpd.tar.gz >/dev/null 2>&1
    curl https://ftp.pcre.org/pub/pcre/pcre-8.40.tar.gz -o pcre.tar.gz >/dev/null 2>&1
    curl http://apache.mirror.triple-it.nl/apr/apr-1.5.2.tar.gz -o apr.tar.gz >/dev/null 2>&1
    curl http://apache.mirror.triple-it.nl/apr/apr-util-1.5.4.tar.gz -o apr-util.tar.gz >/dev/null 2>&1
    curl https://ftp.gnu.org/gnu/ncurses/ncurses-6.0.tar.gz -o ncurses.tar.gz >/dev/null 2>&1
    curl https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-boost-5.7.18.tar.gz -o mysql.tar.gz -L >/dev/null 2>&1

    # Make source directories
    echo "[4/16] Creating source+build directories.."
    mkdir libxml2
    mkdir openssl
    mkdir zlib
    mkdir httpd
    mkdir pcre
    tar -xvzf httpd.tar.gz -C httpd --strip 1 >/dev/null 2>&1 && rm httpd.tar.gz
    mkdir httpd/srclib/apr
    mkdir httpd/srclib/apr-util
    mkdir ncurses
    mkdir mysql

    # Clone PHP sources
    echo "[5/16] Cloning PHP 7.1.5 from Github.."
    git clone -b php-7.1.5 https://github.com/php/php-src.git php7 >/dev/null 2>&1

    # Extract source packages
    echo "[6/16] Extracting source tarballs.."
    tar -xvzf libxml2.tar.gz -C libxml2 --strip 1 >/dev/null 2>&1 && rm libxml2.tar.gz
    tar -xvzf openssl.tar.gz -C openssl --strip 1 >/dev/null 2>&1 && rm openssl.tar.gz
    tar -xvzf zlib.tar.gz -C zlib --strip 1 >/dev/null 2>&1 && rm zlib.tar.gz
    tar -xvzf pcre.tar.gz -C pcre --strip 1 >/dev/null 2>&1 && rm pcre.tar.gz
    tar -xvzf apr.tar.gz -C httpd/srclib/apr --strip 1 >/dev/null 2>&1 && rm apr.tar.gz
    tar -xvzf apr-util.tar.gz -C httpd/srclib/apr-util --strip 1 >/dev/null 2>&1 && rm apr-util.tar.gz
    tar -xvzf ncurses.tar.gz -C ncurses --strip 1 >/dev/null 2>&1 && rm ncurses.tar.gz
    tar -xvzf mysql.tar.gz -C mysql --strip 1 >/dev/null 2>&1 && rm mysql.tar.gz

    # Pre-compile ncurses
    echo "[7/16] Compiling ncurses.."
    mkdir /opt/ncurses
    cd ncurses
    ./configure --prefix=/opt/ncurses --with-shared >/dev/null 2>&1 
    make -j$threads >/dev/null 2>&1 && make install >/dev/null 2>&1
    cd ..

    # Pre-compile mysql
    echo "[8/16] Compiling MySQL.."
    mkdir /opt/mysql
    cd mysql
    cmake -DWITH_BOOST=boost/ -DCMAKE_INSTALL_PREFIX:PATH=/opt/mysql -DCURSES_LIBRARY=/opt/ncurses/lib/libncurses.so -DCURSES_INCLUDE_PATH=/opt/ncurses/include . >/dev/null 2>&1
    make -j$threads >/dev/null 2>&1 && make install >/dev/null 2>&1
    cd ..

    # Pre-compile openssl
    echo "[9/16] Compiling OpenSSL.."
    mkdir /opt/openssl
    cd openssl
    ./config --prefix=/opt/openssl -fPIC >/dev/null 2>&1
    make -j$threads >/dev/null 2>&1 && make install >/dev/null 2>&1
    cd ..

    # Pre-compile libxml2
    echo "[10/16] Compiling LibXML2.."
    mkdir /opt/libxml2
    cd libxml2
    ./configure --without-python --prefix=/opt/libxml2 >/dev/null 2>&1
    make -j$threads >/dev/null 2>&1 && make install >/dev/null 2>&1
    cd ..

    # Pre-compile pcre
    echo "[11/16] Compiling PCRE.."
    mkdir /opt/pcre
    cd pcre
    ./configure --prefix=/opt/pcre >/dev/null 2>&1
    make -j$threads >/dev/null 2>&1 && make install >/dev/null 2>&1
    cd ..

    # Pre-compile zlib
    echo "[12/16] Compiling zlib.."
    mkdir /opt/zlib
    cd zlib
    ./configure --prefix=/opt/zlib >/dev/null 2>&1
    make -j$threads >/dev/null 2>&1 && make install >/dev/null 2>&1
    cd ..

    # Compile httpd
    echo "[13/16] Compiling httpd.."
    mkdir /opt/httpd
    cd httpd
    ./configure --with-pcre=/opt/pcre --with-z=/opt/zlib --with-libxml2=/opt/libxml2 --with-ssl=/opt/openssl --with-included-apr --enable-ssl --prefix=/opt/httpd >/dev/null 2>&1
    make -j$threads >/dev/null 2>&1 && make install >/dev/null 2>&1
    cd ..

    # Compile PHP7
    echo "[14/16] Compiling PHP7.."
    mkdir /opt/php7
    cd php7
    ./buildconf --force >/dev/null 2>&1 && ./configure --with-apxs2=/opt/httpd/bin/apxs --prefix=/opt/php7 --with-libxml-dir=/opt/libxml2 >/dev/null 2>&1
    make -j$threads >/dev/null 2>&1 && make install >/dev/null 2>&1
    cd .

    # Cleanup time!
    echo "[15/16] Cleaning up.."
    rm -rf php7 && rm -rf httpd && rm -rf zlib && rm -rf pcre && rm -rf libxml2 && rm -rf openssl && rm -rf mysql && rm -rf ncurses

    echo "[16/16] Finishing configuration for httpd, PHP7 and MySQL.."

    useradd -s /sbin/nologin mysql
    mkdir /opt/mysql/data
    chown -R mysql:mysql /opt/mysql/data
    mkdir /opt/mysql/data

    echo ""
    echo "Initializing MySQL:"
    echo ""

    /opt/mysql/bin/mysqld --initialize --datadir=/opt/mysql/data --user=mysql

    echo ""
    echo "You can start the MySQL daemon with:"
    echo "/opt/mysql/bin/mysqld --user=mysql --datadir=/opt/mysql/data --socket=/opt/mysql/data/mysql.sock"
    echo ""

    useradd -s /sbin/nologin www-data
    chown -R www-data:www-data /opt/httpd/htdocs
    sed -i 's/User daemon/User www-data/g' /opt/httpd/conf/httpd.conf
    sed -i 's/Group daemon/Group www-data/g' /opt/httpd/conf/httpd.conf

    mkdir /opt/certificates
    openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /opt/certificates/selfsigned.key -out /opt/certificates/selfsigned.crt -subj "/C=NL/ST=Zuid-Holland/L=Dordrecht/O=bartsimons.me/OU=IT Department/CN=bartsimons.me" >/dev/null 2>&1
    currentdate=$(date)
    echo "" >> /opt/httpd/conf/httpd.conf
    echo "# Appended at: ${currentdate}" >> /opt/httpd/conf/httpd.conf
    echo "LoadModule ssl_module modules/mod_ssl.so" >> /opt/httpd/conf/httpd.conf
    echo "<FilesMatch \.php$>" >> /opt/httpd/conf/httpd.conf
    echo "  SetHandler application/x-httpd-php" >> /opt/httpd/conf/httpd.conf
    echo "</FilesMatch>" >> /opt/httpd/conf/httpd.conf
    echo "Listen 443" >> /opt/httpd/conf/httpd.conf
    echo "<VirtualHost *:443>" >> /opt/httpd/conf/httpd.conf
    echo "  SSLEngine on" >> /opt/httpd/conf/httpd.conf
    echo "  SSLCertificateFile /opt/certificates/selfsigned.crt" >> /opt/httpd/conf/httpd.conf
    echo "  SSLCertificateKeyFile /opt/certificates/selfsigned.key" >> /opt/httpd/conf/httpd.conf
    echo "</VirtualHost>" >> /opt/httpd/conf/httpd.conf

    echo "You can start the HTTPD daemon with:"
    echo "/opt/httpd/bin/httpd"
    echo ""
    echo "Done!"
    echo ""
    echo ""
}

This has been tested on CentOS 7 x64.

You can start MySQL with this command:

/opt/mysql/bin/mysqld --user=mysql --datadir=/opt/mysql/data --socket=/opt/mysql/data/mysql.sock

And you can start httpd with this command:

/opt/httpd/bin/httpd

Should you run into an issue: feel free to leave a comment!

Creating HTML tables from JSON data in JavaScript

 •  Filed under json, data, html tables, javascript

A few weeks ago I have written a simple JavaScript library to convert JSON data to an HTML table, and vice versa if you need it. With that library comes even more functionality, like the ability to filter (search) through table rows and the ability to filter JSON data for a multi-page table layout.

You can find my project on GitHub.

This is the table and search box HTML code that I used for this article:

<div class="container">  
  <div class="row">
    <br>
    <input type="text" name="searchbox" id="searchbox" style="width: 100%;"><br><br>
  </div>

  <div class="row">
    <table id="userTable" class="table">
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>1</td>
          <td>Bart</td>
       </tr>
        <tr>
          <td>2</td>
          <td>Nick</td>
        </tr>
        <tr>
          <td>3</td>
          <td>Jason</td>
        </tr>
        <tr>
          <td>4</td>
          <td>Fred</td>
        </tr>
        <tr>
          <td>5</td>
          <td>Ed</td>
        </tr>
        <tr>
          <td>6</td>
          <td>Peter</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>  



So let's dive deeper into some code examples that you can use to create your tables from JSON data:


Example 1: Convert an HTML table to JSON

$(document).ready(function()
{
  // Create a new JSONTable Object from the basicTable table.
  var jsonTable = new JSONTable($("#basicTable"))

  // Convert existing HTML table to a JSON object.
  var tableData = jsonTable.toJSON()
})



Example 2: Search through a table with the filter function

$(document).ready(function()
{
  // Create a new JSONTable Object from the basicTable table.
  var jsonTable = new JSONTable($("#basicTable"))

  // Convert existing HTML table to a JSON object.
  var tableData = jsonTable.toJSON()

  // Create a jQuery event handler on the searchbox.
  $("#searchbox").on('input', function(e) {
    jsonTable.filter($(this).val())
  })
})



Example 3: Create a new HTML table directly from JSON data

// Build a JSON object array with items which are to be included in the table.
var jsonData = [{"ID":"1","Name":"User1"},{"ID":"2","Name":"User2"},{"ID":"3","Name":"User3"},{"ID":"4","Name":"Admin1"},{"ID":"5","Name":"Admin2"}]

// Create a new table inside the body tag
$("body").append("<table id='testTable'></table>")

// Spawn a new JSONTable object on the newly created table
var jsonTable = new JSONTable($("#testTable"))

// Create HTML table (data)structure from JSON data
jsonTable.fromJSON(jsonTest)  



You should definitely try these examples. Good luck!