Created framwork for backend, working authentication and session

management
This commit is contained in:
Lukas Metzger 2018-03-20 10:51:47 +01:00
parent 1aad0d7219
commit ac7f6f5b56
20 changed files with 1257 additions and 0 deletions

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
backend-legacy/config/config-user.php
*/nbproject/private
*/node_modules/*
backend/vendor/*
backend/config/ConfigUser.php

16
backend/composer.json Normal file
View file

@ -0,0 +1,16 @@
{
"require": {
"slim/slim": "^3.9",
"monolog/monolog": "^1.23"
},
"autoload": {
"psr-4": {
"Services\\": "services/",
"Controllers\\": "controllers/",
"Operations\\": "operations/",
"Plugins\\": "plugins/",
"Middlewares\\": "middlewares/",
"Exceptions\\": "exceptions/"
}
}
}

440
backend/composer.lock generated Normal file
View file

@ -0,0 +1,440 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "cacaa59ea8f8c157d77bb7e3a8d52295",
"packages": [
{
"name": "container-interop/container-interop",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "monolog/monolog",
"version": "1.23.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/log": "~1.0"
},
"provide": {
"psr/log-implementation": "1.0.0"
},
"require-dev": {
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
"doctrine/couchdb": "~1.0@dev",
"graylog2/gelf-php": "~1.0",
"jakub-onderka/php-parallel-lint": "0.9",
"php-amqplib/php-amqplib": "~2.4",
"php-console/php-console": "^3.1.3",
"phpunit/phpunit": "~4.5",
"phpunit/phpunit-mock-objects": "2.3.0",
"ruflin/elastica": ">=0.90 <3.0",
"sentry/sentry": "^0.13",
"swiftmailer/swiftmailer": "^5.3|^6.0"
},
"suggest": {
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
"php-console/php-console": "Allow sending log messages to Google Chrome",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
"sentry/sentry": "Allow sending log messages to a Sentry server"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Monolog\\": "src/Monolog"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
}
],
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
"homepage": "http://github.com/Seldaek/monolog",
"keywords": [
"log",
"logging",
"psr-3"
],
"time": "2017-06-19T01:22:40+00:00"
},
{
"name": "nikic/fast-route",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/FastRoute.git",
"reference": "181d480e08d9476e61381e04a71b34dc0432e812"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
"reference": "181d480e08d9476e61381e04a71b34dc0432e812",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|~5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"FastRoute\\": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Nikita Popov",
"email": "nikic@php.net"
}
],
"description": "Fast request router for PHP",
"keywords": [
"router",
"routing"
],
"time": "2018-02-13T20:26:39+00:00"
},
{
"name": "pimple/pimple",
"version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2018-01-21T07:42:36+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "psr/log",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2016-10-10T12:19:37+00:00"
},
{
"name": "slim/slim",
"version": "3.9.2",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118",
"shasum": ""
},
"require": {
"container-interop/container-interop": "^1.2",
"nikic/fast-route": "^1.0",
"php": ">=5.5.0",
"pimple/pimple": "^3.0",
"psr/container": "^1.0",
"psr/http-message": "^1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\": "Slim"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "https://joshlockhart.com"
},
{
"name": "Gabriel Manricks",
"email": "gmanricks@me.com",
"homepage": "http://gabrielmanricks.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "http://silentworks.co.uk"
}
],
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
"homepage": "https://slimframework.com",
"keywords": [
"api",
"framework",
"micro",
"router"
],
"time": "2017-11-26T19:13:09+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

View file

@ -0,0 +1,30 @@
<?php
$defaultConfig = [
'db' => [
'host' => 'localhost',
'user' => 'user',
'password' => 'password',
'dbname' => 'pdnsmanager',
'port' => 3306
],
'logging' => [
'level' => 'info',
'path' => ''
],
'sessionstorage' => [
'plugin' => 'apcu',
'timeout' => 3600,
'config' => null
],
'authentication' => [
'default' => [
'plugin' => 'native',
'config' => null
]
]
];
$userConfig = require('ConfigUser.php');
return array('config' => array_replace_recursive($defaultConfig, $userConfig));

View file

@ -0,0 +1,18 @@
<?php
namespace Controllers;
require '../vendor/autoload.php';
class NotAllowed
{
public function __invoke(\Slim\Container $c)
{
return function ($request, $response, $methods) use ($c) {
$c->logger->warning('Method ' . $request->getMethod() . ' is not valid for ' . $request->getUri()->getPath());
return $c['response']
->withHeader('Allow', \implode(', ', $methods))
->withJson(array('error' => 'Method ' . $request->getMethod() . ' is not valid use on of ' . implode(', ', $methods)), 405);
};
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Controllers;
require '../vendor/autoload.php';
class NotFound
{
public function __invoke(\Slim\Container $c)
{
return function ($request, $response) use ($c) {
$c->logger->warning('No valid endpoint found for: ' . $request->getUri()->getPath());
return $c['response']->withJson(array('error' => 'No valid endpoint found!'), 404);
};
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace Controllers;
require '../vendor/autoload.php';
use \Slim\Http\Request as Request;
use \Slim\Http\Response as Response;
class Sessions
{
/** @var \Monolog\Logger */
private $logger;
/** @var \Slim\Container */
private $container;
public function __construct(\Slim\Container $c)
{
$this->logger = $c->logger;
$this->container = $c;
}
public function post(Request $req, Response $res, array $args)
{
$body = $req->getParsedBody();
if (!array_key_exists('username', $body) ||
!array_key_exists('password', $body)) {
return $res->withJson(['error' => 'One of the required fields is missing'], 422);
}
$userAuth = new \Operations\UserAuth($this->container);
$sessionStorage = new \Operations\Sessionstorage($this->container);
$sessionTimeout = $this->container['config']['sessionstorage']['timeout'];
try {
$userId = $userAuth->authenticate($body['username'], $body['password']);
} catch (\Exceptions\PluginNotFoundException $e) {
return $res->withJson(['error' => $e->getMessage()], 500);
}
if ($userId >= 0) {
$secret = openssl_random_pseudo_bytes(64);
$secretString = base64_encode($secret);
$secretString = rtrim(strtr($secretString, '+/', '-_'), '=');
$sessionStorage->set($secretString, $userId, $sessionTimeout);
$this->logger->info('User authenticated successfully', ['username' => $body['username']]);
return $res->withJson([
'username' => $body['username'],
'token' => $secretString
], 201);
} else {
$this->logger->info('User failed to authenticate', ['username' => $body['username']]);
return $res->withJson(['error' => 'Username or password is invalid'], 403);
}
}
public function delete(Request $req, Response $res, array $args)
{
$sessionStorage = new \Operations\Sessionstorage($this->container);
if ($sessionStorage->exists($args['sessionId'])) {
$sessionStorage->delete($args['sessionId']);
$this->logger->info('Deleting session', ['token' => $args['sessionId']]);
return $res->withStatus(204);
} else {
$this->logger->warning('Trying to delete non existing session', ['token' => $args['sessionId']]);
return $res->withJson(['error' => 'Session not found'], 404);
}
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Exceptions;
require '../vendor/autoload.php';
class PluginNotFoundException extends \Exception
{
}

View file

@ -0,0 +1,44 @@
<?php
namespace Middlewares;
require '../vendor/autoload.php';
use \Slim\Http\Request as Request;
use \Slim\Http\Response as Response;
class Authentication
{
/** @var \Monolog\Logger */
private $logger;
/** @var \Slim\Container */
private $container;
public function __construct(\Slim\Container $c)
{
$this->logger = $c->logger;
$this->container = $c;
}
public function __invoke(Request $req, Response $res, callable $next)
{
$token = $req->getHeaderLine('X-Authentication');
$sessionStorage = new \Operations\Sessionstorage($this->container);
if ($sessionStorage->exists($token)) {
$sessionTimeout = $this->container['config']['sessionstorage']['timeout'];
$userId = $sessionStorage->get($token, $sessionTimeout);
$this->logger->debug('Authentication was successfull', ['token' => $token, 'userId' => $userId]);
$req = $req->withAttribute('userId', $userId);
return $next($req, $res);
} else {
$this->logger->warning('No valid authentication token found');
return $res->withJson(['error' => 'No valid authentication token suplied'], 403);
}
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace Middlewares;
require '../vendor/autoload.php';
use \Slim\Http\Request as Request;
use \Slim\Http\Response as Response;
class LogRequests
{
/** @var \Monolog\Logger */
private $logger;
public function __construct(\Slim\Container $c)
{
$this->logger = $c->logger;
}
public function __invoke(Request $req, Response $res, callable $next)
{
$this->logger->debug($req->getMethod() . ' ' . $req->getUri()->getPath());
return $next($req, $res);
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Middlewares;
require '../vendor/autoload.php';
use \Slim\Http\Request as Request;
use \Slim\Http\Response as Response;
class RejectEmptyBody
{
/** @var \Monolog\Logger */
private $logger;
public function __construct(\Slim\Container $c)
{
$this->logger = $c->logger;
}
public function __invoke(Request $req, Response $res, callable $next)
{
if (($req->isPost() || $req->isPut() || $req->isPatch()) && $req->getParsedBody() == null) {
$this->logger->warning('Got empty body in request with method ' . $req->getMethod());
return $res->withJson(['error' => 'The supplied body was empty'], 400);
} else {
return $next($req, $res);
}
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Operations;
require '../vendor/autoload.php';
/**
* This is a proxy class which load the configured plugin as
* backend and proxies queries to it.
*/
class Sessionstorage
{
/** @var \Monolog\Logger */
private $logger;
/** @var InterfaceSessionstorage */
private $backend;
public function __construct(\Slim\Container $c)
{
$this->logger = $c->logger;
$config = $c['config']['sessionstorage'];
$plugin = $config['plugin'];
$pluginConfig = $config['config'];
$pluginClass = '\\Plugins\\Sessionstorage\\' . $plugin;
//Check if plugin is available
if (!class_exists($pluginClass)) {
$this->logger->critical('The configured session storage plugin does not exist', ['plugin' => $plugin]);
exit();
}
//Try to create class with given name
$this->backend = new $pluginClass($this->logger, $pluginConfig);
if (!$this->backend instanceof \Plugins\Sessionstorage\InterfaceSessionstorage) {
$this->logger->critical('The configured plugin does not implement InterfaceSessionstorage', ['pluginname' => $plugin]);
exit();
}
$this->logger->debug("Session storage plugin was loaded", ['plugin' => $plugin]);
}
public function set(string $key, string $value, int $ttl) : void
{
$this->backend->set($key, $value, $ttl);
}
public function exists(string $key) : bool
{
return $this->backend->exists($key);
}
public function get(string $key, int $ttl) : string
{
return $this->backend->get($key, $ttl);
}
public function delete(string $key) : void
{
$this->backend->delete($key);
}
}

View file

@ -0,0 +1,151 @@
<?php
namespace Operations;
require '../vendor/autoload.php';
use \Exceptions\PluginNotFoundException as PluginNotFoundException;
/**
* This class provides user authentication for the application.
* Its main purpose is to find the apropriate authentication
* plugin. It also ensures that a user entry for that user is
* in the database.
*/
class UserAuth
{
/** @var \Monolog\Logger */
private $logger;
/** @var \PDO */
private $db;
/** @var \Slim\Container */
private $c;
public function __construct(\Slim\Container $c)
{
$this->logger = $c->logger;
$this->db = $c->db;
$this->c = $c;
}
/**
* Authenticates a user with username/password combination.
*
* @param $username Username
* @param $password Password
*
* @return int -1 if authentication failed, the user id otherwise
*
* @throws \Exceptions\PluginNotFoundExecption if no matching backend can be found
*/
public function authenticate(string $username, string $password) : int
{
if (strpos($username, '/') === false) { // no explicit backend specification
$backend = 'default';
$name = $username;
} else {
$parts = preg_split('/\//', $username, 2);
$backend = $parts[0];
$name = $parts[1];
}
$this->logger->debug('Trying to authenticate with info', ['backend' => $backend, 'name' => $name]);
try {
if ($this->authenticateBackend($backend, $name, $password)) {
return $this->localUser($backend, $name, $password);
} else {
return -1;
}
} catch (\Exceptions\PluginNotFoundException $e) {
throw $e;
}
}
/**
* This function searches for an apropriate backend and calls it
* to authenticate the user.
*
* @param $backend The name of the backend to use
* @param $username The username to use
* @param $password The password to use
*
* @return bool true if authentication successfull false otherwise
*
* @throws \Exceptions\PluginNotFoundExecption if no matching backend can be found
*/
private function authenticateBackend(string $backend, string $username, string $password) : bool
{
$config = $this->c['config']['authentication'];
if (!array_key_exists($backend, $config)) { // Check if backend is configured for prefix
$this->logger->warning('No authentication backend configured for prefix', ['prefix' => $backend]);
throw new PluginNotFoundException('No authentication backend configured for this user.');
}
$plugin = $config[$backend]['plugin'];
$pluginClass = '\\Plugins\\UserAuth\\' . $plugin;
$pluginConfig = $config[$backend]['config'];
if (!class_exists($pluginClass)) { // Check if given backend class exists
$this->logger->error('The configured UserAuth plugin does not exist', ['prefix' => $backend, 'plugin' => $plugin]);
throw new PluginNotFoundException('The authentication request can not be processed.');
}
//Try to create class with given name
$backendObj = new $pluginClass($this->logger, $this->db, $pluginConfig);
if (!$backendObj instanceof \Plugins\UserAuth\InterfaceUserAuth) { // Check if class implements interface
$this->logger->error('The configured plugin does not implement InterfaceUserAuth', ['plugin' => $plugin, 'prefix' => $backend]);
throw new PluginNotFoundException('The authentication request can not be processed.');
}
$this->logger->debug("UserAuth plugin was loaded", ['plugin' => $plugin, 'prefix' => $backend]);
return $backendObj->authenticate($username, $password);
}
/**
* Ensures the user from the given backend has a entry in the local database,
* then returns the user id.
*
* @param $backend The name of the backend to use
* @param $username The username to use
* @param $password The password to use
*
* @return int The local user id
*/
private function localUser(string $backend, string $username, string $password) : int
{
$this->db->beginTransaction();
$query = $this->db->prepare('SELECT id FROM users WHERE name=:name AND backend=:backend');
$query->bindValue(':name', $username, \PDO::PARAM_STR);
$query->bindValue(':backend', $backend);
$query->execute();
$record = $query->fetch();
if ($record === false) {
$insert = $this->db->prepare('INSERT INTO users (name,backend,type) VALUES (:name, :backend, \'user\')');
$insert->bindValue(':name', $username, \PDO::PARAM_STR);
$insert->bindValue(':backend', $backend, \PDO::PARAM_STR);
$insert->execute();
$query->execute();
$record = $query->fetch();
$this->logger->info('Non existing user created', ['username' => $username, 'backend' => $backend, 'newId' => $record['id']]);
} else {
$this->logger->debug('User was found in database', ['username' => $username, 'backend' => $backend, 'id' => $record['id']]);
}
$this->db->commit();
return $record['id'];
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Plugins\Sessionstorage;
require '../vendor/autoload.php';
/**
* This interface provides the neccessary functions for a session storage backend
*/
interface InterfaceSessionstorage
{
/**
* Construct the object
*
* @param $logger Monolog logger instance for error handling
* @param $config The configuration for the Plugin if any was provided
*/
public function __construct(\Monolog\Logger $logger, array $config = null);
/**
* Save new entry.
*
* @param $key The key for the entry
* @param $value The value for the entry
* @param $ttl The time (in s) for which this item should be available
*/
public function set(string $key, string $value, int $ttl) : void;
/**
* Queries the existence of some entry.
*
* @param $key The key to query
*/
public function exists(string $key) : bool;
/**
* Get the value for a given key. This should also reset the ttl to the given value.
*
* @param $key The key for the entry to get
* @param $ttl The new ttl for the entry
*/
public function get(string $key, int $ttl) : string;
/**
* Delete the value for a given key.
*
* @param $key The key to delete
*/
public function delete(string $key) : void;
}

View file

@ -0,0 +1,90 @@
<?php
namespace Plugins\Sessionstorage;
require '../vendor/autoload.php';
/**
* Implements a session storage plugin for using PHPs APCu.
*/
class apcu implements InterfaceSessionstorage
{
/** @var \Monolog\Logger */
private $logger;
/**
* Construct the object
*
* @param $logger Monolog logger instance for error handling
* @param $config The configuration for the Plugin if any was provided
*/
public function __construct(\Monolog\Logger $logger, array $config = null)
{
$this->logger = $logger;
if (!function_exists('apcu_store')) {
$this->$logger->critical('PHP APCu extension is not available but configured as session storage backend exiting now');
exit();
}
}
/**
* Save new entry.
*
* @param $key The key for the entry
* @param $value The value for the entry
* @param $ttl The time (in s) for which this item should be available
*/
public function set(string $key, string $value, int $ttl) : void
{
$this->logger->debug('Storing data to APCu', ['key' => $key, 'value' => $value, 'ttl' => $ttl]);
apcu_store($key, $value, $ttl);
}
/**
* Queries the existence of some entry.
*
* @param $key The key to query
*/
public function exists(string $key) : bool
{
$this->logger->debug('Checking for APCu key existence', ['key' => $key]);
return apcu_exists($key);
}
/**
* Get the value for a given key. This should also reset the ttl to the given value.
*
* @param $key The key for the entry to get
* @param $ttl The new ttl for the entry
*/
public function get(string $key, int $ttl) : string
{
$this->logger->debug('Getting data from APCu', ['key' => $key, 'ttl' => $ttl]);
$value = apcu_fetch($key);
if ($value == false) {
$this->logger->error('Non existing key was queried from APCu', ['key' => $key]);
throw new \InvalidArgumentException('The requested key was not in the database!');
}
apcu_store($key, $value, $ttl);
return $value;
}
/**
* Delete the value for a given key.
*
* @param $key The key to delete
*/
public function delete(string $key) : void
{
$this->logger->debug('Deleting key from APCu', ['key' => $key]);
apcu_delete($key);
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace Plugins\UserAuth;
require '../vendor/autoload.php';
/**
* This interface provides the neccessary functions for
* a user authentication backend.
*/
interface InterfaceUserAuth
{
/**
* Construct the object
*
* @param $logger Monolog logger instance for error handling
* @param $db Database connection
* @param $config The configuration for the Plugin if any was provided
*/
public function __construct(\Monolog\Logger $logger, \PDO $db, array $config = null);
/**
* Authenticate user.
*
* @param $username The key for the entry
* @param $password The value for the entry
*
* @return true if valid false otherwise
*/
public function authenticate(string $username, string $password) : bool;
}

View file

@ -0,0 +1,54 @@
<?php
namespace Plugins\UserAuth;
require '../vendor/autoload.php';
/**
* This interface provides the neccessary functions for
* a user authentication backend.
*/
class Native implements InterfaceUserAuth
{
/** @var \Monolog\Logger */
private $logger;
/** @var \PDO */
private $db;
/**
* Construct the object
*
* @param $logger Monolog logger instance for error handling
* @param $db Database connection
* @param $config The configuration for the Plugin if any was provided
*/
public function __construct(\Monolog\Logger $logger, \PDO $db, array $config = null)
{
$this->logger = $logger;
$this->db = $db;
}
/**
* Authenticate user.
*
* @param $username The key for the entry
* @param $password The value for the entry
*
* @return true if valid false otherwise
*/
public function authenticate(string $username, string $password) : bool
{
$query = $this->db->prepare('SELECT id, password FROM users WHERE name=:name AND backend=\'native\'');
$query->bindValue(':name', $username, \PDO::PARAM_STR);
$query->execute();
$record = $query->fetch();
if ($record === false) {
return false;
}
return password_verify($password, $record['password']);
}
}

39
backend/public/index.php Normal file
View file

@ -0,0 +1,39 @@
<?php
require '../vendor/autoload.php';
use \Slim\Http\Request as Request;
use \Slim\Http\Response as Response;
// Load config
$config = require('../config/ConfigDefault.php');
// Prepare dependency container
$container = new \Slim\Container($config);
$container['logger'] = new \Services\Logger;
$container['db'] = new \Services\Database;
$container['notFoundHandler'] = new \Controllers\NotFound;
$container['notAllowedHandler'] = new \Controllers\NotAllowed;
// Create application
$app = new \Slim\App($container);
// Configure routing
$app->group('/v1', function () {
$this->post('/sessions', '\Controllers\Sessions:post');
$this->group('', function () {
$this->delete('/sessions/{sessionId}', '\Controllers\Sessions:delete');
})->add('\Middlewares\Authentication');
});
// Add global middlewares
$app->add('\Middlewares\LogRequests');
$app->add('\Middlewares\RejectEmptyBody');
// Run application
$app->run();

View file

@ -0,0 +1,37 @@
<?php
namespace Services;
require '../vendor/autoload.php';
class Database
{
public function __invoke(\Slim\Container $c)
{
$config = $c['config']['db'];
try {
$pdo = new \PDO(
'mysql:host=' . $config['host'] . ';port=' . $config['port'] . ';dbname=' . $config['dbname'],
$config['user'],
$config['password']
);
} catch (\PDOException $e) {
$c->logger->critical("SQL Connect Error: " . $e->getMessage());
$c->logger->critical("DB Config was", $config);
exit();
}
try {
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
} catch (\PDOException $e) {
$c->logger->critical("SQL Parameter Error: " . $e->getMessage());
exit();
}
$c->logger->debug("Database setup successfull");
return $pdo;
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Services;
require '../vendor/autoload.php';
class Logger
{
public function __invoke(\Slim\Container $c)
{
$config = $c['config']['logging'];
$logger = new \Monolog\Logger('pdnsmanager');
$loglevel = \Monolog\Logger::toMonologLevel($config['level']);
$path = $config['path'];
if (\strlen($path) > 0) {
$fileHandler = new \Monolog\Handler\StreamHandler($path, $loglevel);
$logger->pushHandler($fileHandler);
} else {
$errorLogHandler = new \Monolog\Handler\ErrorLogHandler(0, $loglevel);
$logger->pushHandler($errorLogHandler);
}
return $logger;
}
}