diff options
author | Maxence Lange <maxence@artificial-owl.com> | 2022-09-05 16:03:36 +0300 |
---|---|---|
committer | Maxence Lange <maxence@artificial-owl.com> | 2022-09-05 16:03:50 +0300 |
commit | 8a63ca346de6edf53b2409b73ae8dad5f10632f2 (patch) | |
tree | 4c97ba91526c36892fc7593b688518ac9b010706 /server | |
parent | 1cfe97fe50d3279620b98eb8e3b27bebeb7d2dac (diff) |
switch to php 7.4
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
Diffstat (limited to 'server')
-rw-r--r-- | server/composer.lock | 16 | ||||
-rwxr-xr-x | server/config/version.php | 3 | ||||
-rw-r--r-- | server/index.php | 155 | ||||
-rw-r--r-- | server/init.php | 58 | ||||
-rw-r--r-- | server/lib/BruteForceMiddleware.php | 3 | ||||
-rw-r--r-- | server/lib/Replication.php | 145 | ||||
-rw-r--r-- | server/lib/Service/DependenciesService.php | 55 | ||||
-rw-r--r-- | server/lib/SignatureHandler.php | 40 | ||||
-rw-r--r-- | server/lib/Status.php | 15 | ||||
-rw-r--r-- | server/lib/Tools/Exceptions/ArrayNotFoundException.php | 43 | ||||
-rw-r--r-- | server/lib/Tools/Exceptions/ItemNotFoundException.php | 43 | ||||
-rw-r--r-- | server/lib/Tools/Exceptions/MalformedArrayException.php | 43 | ||||
-rw-r--r-- | server/lib/Tools/Exceptions/UnknownTypeException.php | 42 | ||||
-rw-r--r-- | server/lib/Tools/Traits/TArrayTools.php | 20 | ||||
-rw-r--r-- | server/lib/Tools/Traits/TDebug.php (renamed from server/lib/TDebug.php) | 2 | ||||
-rw-r--r-- | server/lib/UserManager.php | 225 | ||||
-rw-r--r-- | server/lib/Validator/Email.php | 101 | ||||
-rw-r--r-- | server/lib/Validator/Twitter.php | 59 | ||||
-rw-r--r-- | server/lib/Validator/Website.php | 20 | ||||
-rwxr-xr-x | server/replicationcron.php | 17 | ||||
-rw-r--r-- | server/verifycron.php | 19 |
21 files changed, 794 insertions, 330 deletions
diff --git a/server/composer.lock b/server/composer.lock index cbec160..c4d8992 100644 --- a/server/composer.lock +++ b/server/composer.lock @@ -69,16 +69,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.3.2", + "version": "1.3.3", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "fd5dd441932a7e10ca6e5b490e272d34c8430640" + "reference": "30897edbfb15e784fe55587b4f73ceefd3c4d98c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/fd5dd441932a7e10ca6e5b490e272d34c8430640", - "reference": "fd5dd441932a7e10ca6e5b490e272d34c8430640", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/30897edbfb15e784fe55587b4f73ceefd3c4d98c", + "reference": "30897edbfb15e784fe55587b4f73ceefd3c4d98c", "shasum": "" }, "require": { @@ -125,7 +125,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.3.2" + "source": "https://github.com/composer/ca-bundle/tree/1.3.3" }, "funding": [ { @@ -141,7 +141,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:56:16+00:00" + "time": "2022-07-20T07:14:26+00:00" }, { "name": "guzzlehttp/guzzle", @@ -1281,7 +1281,7 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -1328,7 +1328,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" }, "funding": [ { diff --git a/server/config/version.php b/server/config/version.php deleted file mode 100755 index 97aba47..0000000 --- a/server/config/version.php +++ /dev/null @@ -1,3 +0,0 @@ -<?php - -$VERSION = '0.3.1'; diff --git a/server/index.php b/server/index.php index 816df3d..52c3284 100644 --- a/server/index.php +++ b/server/index.php @@ -1,78 +1,127 @@ <?php -use DI\Container; -use LookupServer\Service\DependenciesService; -use LookupServer\UserManager; +declare(strict_types=1); + + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2022 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace LookupServer; + + +use LookupServer\Validator\Email; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Slim\Factory\AppFactory; -require __DIR__ . '/vendor/autoload.php'; +require __DIR__ . '/init.php'; -$container = new Container(); -AppFactory::setContainer($container); -$app = AppFactory::create(); +if (!isset($app) || !isset($container)) { + return; +} -$settings = require __DIR__ . '/src/config.php'; -$container->set('Settings', function ($c) use ($settings) { - return $settings; -}); +$r_search = function (ServerRequestInterface $request, ResponseInterface $response, array $args) { + /** @var UserManager $userManager */ + $userManager = $this->get('UserManager'); + return $userManager->search($request, $response, $args); +}; +$app->get('/users', $r_search); +$app->get('/index.php/users', $r_search); -$container->set('DependenciesService', function ($c) { - return new DependenciesService($c->get('Settings')); -}); +$r_register = function (ServerRequestInterface $request, ResponseInterface $response, array $args) { + /** @var UserManager $userManager */ + $userManager = $this->get('UserManager'); + return $userManager->register($request, $response, $args); +}; +$app->post('/users', $r_register); +$app->post('/index.php/users', $r_register); -$container->get('DependenciesService')->initContainer($container); -//require __DIR__ . '/src/dependencies.php'; +$r_delete = function (ServerRequestInterface $request, ResponseInterface $response, array $args) { + /** @var UserManager $userManager */ + $userManager = $this->get('UserManager'); -// -// + return $userManager->delete($request, $response, $args); +}; +$app->delete('/users', $r_delete); +$app->delete('/index.php/users', $r_delete); -//$container->set('BruteForceMiddleware', function (Container $c) { -// return new \LookupServer\BruteForceMiddleware($c->get('db')); -//}); +$r_batchRegister = function (ServerRequestInterface $request, ResponseInterface $response, array $args) { + /** @var UserManager $userManager */ + $userManager = $this->get('UserManager'); -//$app->add(function ( -// \Psr\Http\Message\ServerRequestInterface $request, -// \Psr\Http\Server\RequestHandlerInterface $handler, -// $next -//) { -// return $next($request, $handler); -//}); + return $userManager->batchRegister($request, $response, $args); +}; +$app->post('/gs/users', $r_batchRegister); +$app->post('/index.php/gs/users', $r_batchRegister); -$app->setBasePath('/index.php'); -$app->get( - '/users', - function (ServerRequestInterface $request, ResponseInterface $response, array $args) { - /** @var UserManager $userManager */ - $userManager = $this->get('UserManager'); +$r_batchDelete = function (ServerRequestInterface $request, ResponseInterface $response, array $args) { + /** @var UserManager $userManager */ + $userManager = $this->get('UserManager'); - return $userManager->search($request, $response, $args); - } -); + return $userManager->batchDelete($request, $response, $args); +}; +$app->delete('/gs/users', $r_batchDelete); +$app->delete('/index.php/gs/users', $r_batchDelete); -$app->post( - '/users', - function (ServerRequestInterface $request, ResponseInterface $response, array $args) { - /** @var UserManager $userManager */ - $userManager = $this->get('UserManager'); - return $userManager->register($request, $response, $args); - } -); +$r_validateEmail = function (ServerRequestInterface $request, ResponseInterface $response, array $args) { + /** @var Email $emailValidator */ + $emailValidator = $this->get('EmailValidator'); + return $emailValidator->validate($request, $response, $args); +}; +$app->get('/validate/email/{token}', $r_validateEmail); +$app->get('/index.php/validate/email/{token}', $r_validateEmail); + + +$r_status = function (ServerRequestInterface $request, ResponseInterface $response, array $args) { + $response->getBody()->write( + json_encode( + ['version' => VERSION] + ) + ); + + return $response; +}; +$app->get('/status', $r_status); +$app->get('/index.php/status', $r_status); + + +$r_export = function (ServerRequestInterface $request, ResponseInterface $response, array $args) { + /** @var Replication $replication */ + $replication = $this->get('Replication'); + + return $replication->export($request, $response, $args); +}; +$app->get('/replication', $r_export); +$app->get('/index.php/replication', $r_export); -//$app->post('/gs/users', 'UserManager:batchRegister'); -//$app->delete('/gs/users', 'UserManager:batchDelete'); -//$app->delete('/users', 'UserManager:delete'); -//$app->get('/validate/email/{token}', 'EmailValidator:validate')->setName('validateEmail'); -//$app->get('/status', 'Status:status'); -// -//$app->get('/replication', 'Replication:export'); $app->run(); diff --git a/server/init.php b/server/init.php new file mode 100644 index 0000000..454513a --- /dev/null +++ b/server/init.php @@ -0,0 +1,58 @@ +<?php + +declare(strict_types=1); + + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2022 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace LookupServer; + + +use DI\Container; +use LookupServer\Service\DependenciesService; +use Slim\Factory\AppFactory; + + +define('VERSION', '1.0.0'); + + +require __DIR__ . '/vendor/autoload.php'; + +$container = new Container(); +AppFactory::setContainer($container); +$app = AppFactory::create(); +$app->setBasePath(''); + +$settings = require __DIR__ . '/src/config.php'; +$container->set('Settings', function (Container $c) use ($settings) { + return $settings; +}); + +$container->set('DependenciesService', function (Container $c) { + return new DependenciesService($c->get('Settings')); +}); +$container->get('DependenciesService')->initContainer($container, $app); + diff --git a/server/lib/BruteForceMiddleware.php b/server/lib/BruteForceMiddleware.php index af1080b..4a9c4d7 100644 --- a/server/lib/BruteForceMiddleware.php +++ b/server/lib/BruteForceMiddleware.php @@ -2,6 +2,9 @@ namespace LookupServer; +/** + * @deprecated + */ class BruteForceMiddleware { /** * Brute force middleware diff --git a/server/lib/Replication.php b/server/lib/Replication.php index 931ee87..04a8e45 100644 --- a/server/lib/Replication.php +++ b/server/lib/Replication.php @@ -1,73 +1,112 @@ <?php +declare(strict_types=1); + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Bjoern Schiessle <bjoern@schiessle.org> + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @copyright 2017 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + namespace LookupServer; use GuzzleHttp\Client; -use Slim\Http\Request; -use Slim\Http\Response; +use PDO; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ServerRequestInterface as Request; class Replication { - /** @var \PDO */ - private $db; - - /** @var string */ - private $auth; - - /** @var string[] */ - private $replicationHosts; + private PDO $db; + private string $auth; + private array $replicationHosts; - public function __construct(\PDO $db, $auth, $replicationHosts) { + public function __construct(PDO $db, string $auth, array $replicationHosts) { $this->db = $db; $this->auth = $auth; $this->replicationHosts = $replicationHosts; } - public function export(Request $request, Response $response) { + + /** + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * @param array $args + * + * @return ResponseInterface + */ + public function export(Request $request, Response $response, array $args = []): Response { $userInfo = $request->getUri()->getUserInfo(); $userInfo = explode(':', $userInfo, 2); - if (count($userInfo) !== 2 || $userInfo[0] !== 'lookup' || $userInfo[1] !== $this->auth) { + if (count($userInfo) !== 2 || $userInfo[0] !== 'lookup' || $userInfo[1] !== $this->auth) { return $response->withStatus(401); } $params = $request->getQueryParams(); - if (!isset($params['timestamp'], $params['page']) || !ctype_digit($params['timestamp']) || - !ctype_digit($params['page'])) { + if (!isset($params['timestamp'], $params['page']) || !ctype_digit($params['timestamp']) + || !ctype_digit($params['page'])) { return $response->withStatus(400); } $timestamp = (int)$params['timestamp']; $page = (int)$params['page']; - $stmt = $this->db->prepare('SELECT id, federationId, UNIX_TIMESTAMP(timestamp) AS timestamp + $stmt = $this->db->prepare( + 'SELECT id, federationId, UNIX_TIMESTAMP(timestamp) AS timestamp FROM users WHERE UNIX_TIMESTAMP(timestamp) >= :timestamp ORDER BY timestamp, id LIMIT :limit - OFFSET :offset'); + OFFSET :offset' + ); $stmt->bindParam('timestamp', $timestamp); - $stmt->bindValue('limit', 100, \PDO::PARAM_INT); - $stmt->bindValue('offset', 100 * $page, \PDO::PARAM_INT); + $stmt->bindValue('limit', 100, PDO::PARAM_INT); + $stmt->bindValue('offset', 100 * $page, PDO::PARAM_INT); $stmt->execute(); $result = []; - while($data = $stmt->fetch()) { + while ($data = $stmt->fetch()) { $user = [ 'cloudId' => $data['federationId'], 'timestamp' => (int)$data['timestamp'], 'data' => [], ]; - $stmt2 = $this->db->prepare('SELECT * + $stmt2 = $this->db->prepare( + 'SELECT * FROM store - WHERE userId = :uid'); + WHERE userId = :uid' + ); $stmt2->bindValue('uid', $data['id']); $stmt2->execute(); - while($userData = $stmt2->fetch()) { + while ($userData = $stmt2->fetch()) { $user['data'][] = [ 'key' => $userData['k'], 'value' => $userData['v'], @@ -80,14 +119,17 @@ class Replication { } $response->getBody()->write(json_encode($result)); + return $response; } - public function import(Request $request, Response $response) { + + public function import(): void { $replicationStatus = []; if (file_exists(__DIR__ . '/../config/replication.json')) { - $replicationStatus = json_decode(file_get_contents(__DIR__ . '/../config/replication.json'), true); + $replicationStatus = + json_decode(file_get_contents(__DIR__ . '/../config/replication.json'), true); } foreach ($this->replicationHosts as $replicationHost) { @@ -98,16 +140,18 @@ class Replication { } $page = 0; - while(true) { + while (true) { // Retrieve public key && store - $req = new \GuzzleHttp\Psr7\Request('GET', $replicationHost . '?timestamp=' . $timestamp . '&page=' . $page); + $req = new \GuzzleHttp\Psr7\Request( + 'GET', $replicationHost . '?timestamp=' . $timestamp . '&page=' . $page + ); $client = new Client(); $resp = $client->send($req, [ 'timeout' => 5, ]); - $data = json_decode($resp->getBody(), true); + $data = json_decode($resp->getBody()->getContents(), true); if (count($data) === 0) { break; } @@ -120,16 +164,22 @@ class Replication { $page++; } - file_put_contents(__DIR__. '/../config/replication.json', json_encode($replicationStatus, JSON_PRETTY_PRINT)); + file_put_contents( + __DIR__ . '/../config/replication.json', json_encode($replicationStatus, JSON_PRETTY_PRINT) + ); } - - return $response; } - private function parseUser($user) { - $stmt = $this->db->prepare('SELECT id, UNIX_TIMESTAMP(timestamp) AS timestamp + + /** + * @param array $user + */ + private function parseUser(array $user): void { + $stmt = $this->db->prepare( + 'SELECT id, UNIX_TIMESTAMP(timestamp) AS timestamp FROM users - WHERE federationId = :id'); + WHERE federationId = :id' + ); $stmt->bindParam('id', $user['cloudId']); $stmt->execute(); @@ -139,11 +189,14 @@ class Replication { $data = $stmt->fetch(); if ($data['timestamp'] > $user['timestamp']) { $stmt->closeCursor(); + return; } - $stmt2 = $this->db->prepare('DELETE FROM users - WHERE federationId = :id'); + $stmt2 = $this->db->prepare( + 'DELETE FROM users + WHERE federationId = :id' + ); $stmt2->bindParam('id', $user['cloudId']); $stmt2->execute(); $stmt2->closeCursor(); @@ -151,19 +204,23 @@ class Replication { $stmt->closeCursor(); - $stmt = $this->db->prepare('INSERT INTO users (federationId, timestamp) VALUES (:federationId, FROM_UNIXTIME(:timestamp))'); - $stmt->bindParam(':federationId', $user['cloudId'], \PDO::PARAM_STR); - $stmt->bindParam(':timestamp', $user['timestamp'], \PDO::PARAM_INT); + $stmt = $this->db->prepare( + 'INSERT INTO users (federationId, timestamp) VALUES (:federationId, FROM_UNIXTIME(:timestamp))' + ); + $stmt->bindParam(':federationId', $user['cloudId'], PDO::PARAM_STR); + $stmt->bindParam(':timestamp', $user['timestamp'], PDO::PARAM_INT); $stmt->execute(); $id = $this->db->lastInsertId(); $stmt->closeCursor(); foreach ($user['data'] as $data) { - $stmt = $this->db->prepare('INSERT INTO store (userId, k, v, valid) VALUES (:userId, :k, :v, :valid)'); - $stmt->bindParam(':userId', $id, \PDO::PARAM_INT); - $stmt->bindParam(':k', $data['key'], \PDO::PARAM_STR); - $stmt->bindParam(':v', $data['value'], \PDO::PARAM_STR); - $stmt->bindParam(':valid', $data['validated'], \PDO::PARAM_INT); + $stmt = $this->db->prepare( + 'INSERT INTO store (userId, k, v, valid) VALUES (:userId, :k, :v, :valid)' + ); + $stmt->bindParam(':userId', $id, PDO::PARAM_INT); + $stmt->bindParam(':k', $data['key'], PDO::PARAM_STR); + $stmt->bindParam(':v', $data['value'], PDO::PARAM_STR); + $stmt->bindParam(':valid', $data['validated'], PDO::PARAM_INT); $stmt->execute(); $stmt->closeCursor(); diff --git a/server/lib/Service/DependenciesService.php b/server/lib/Service/DependenciesService.php index 7f53b08..a6e9990 100644 --- a/server/lib/Service/DependenciesService.php +++ b/server/lib/Service/DependenciesService.php @@ -1,5 +1,33 @@ <?php +declare(strict_types=1); + + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2022 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + namespace LookupServer\Service; @@ -8,25 +36,29 @@ use DI\Container; use Exception; use LookupServer\Replication; use LookupServer\SignatureHandler; -use LookupServer\Status; use LookupServer\Tools\Traits\TArrayTools; use LookupServer\UserManager; use LookupServer\Validator\Email; use LookupServer\Validator\Twitter; use LookupServer\Validator\Website; use PDO; +use Slim\App; class DependenciesService { use TArrayTools; private array $settings; - public function __construct(array $settings) { + public function __construct(array $settings = []) { $this->settings = $settings; } - public function initContainer(Container $container): void { + /** + * @param Container $container + * @param App $app + */ + public function initContainer(Container $container, App $app): void { $container->set('db', function (Container $c) { $db = $this->getArray('settings.db', $c->get('Settings')); @@ -62,7 +94,7 @@ class DependenciesService { return new SignatureHandler(); }); - $container->set('TwitterOAuth', function ($c) { + $container->set('TwitterOAuth', function (Container $c) { /** @var array $settings */ $settings = $c->get('Settings'); @@ -75,24 +107,24 @@ class DependenciesService { }); - $container->set('EmailValidator', function ($c) { + $container->set('EmailValidator', function (Container $c) use ($app) { $settings = $c->get('Settings'); return new Email( $c->get('db'), -// $c->get('RouterInterface'), + $app->getRouteCollector()->getRouteParser(), $this->get('settings.host', $settings), $this->get('settings.emailfrom', $settings), $this->getBool('settings.global_scale', $settings) ); }); - $container->set('WebsiteValidator', function ($c) { + $container->set('WebsiteValidator', function (Container $c) { return new Website($c->get('SignatureHandler')); }); - $container->set('TwitterValidator', function ($c) { + $container->set('TwitterValidator', function (Container $c) { return new Twitter( $c->get('TwitterOAuth'), $c->get('SignatureHandler'), @@ -101,12 +133,7 @@ class DependenciesService { }); - $container->set('Status', function ($c) { - return new Status(); - }); - - - $container->set('Replication', function ($c) { + $container->set('Replication', function (Container $c) { $settings = $c->get('Settings'); return new Replication( diff --git a/server/lib/SignatureHandler.php b/server/lib/SignatureHandler.php index bf308d4..7d42bb3 100644 --- a/server/lib/SignatureHandler.php +++ b/server/lib/SignatureHandler.php @@ -1,7 +1,13 @@ <?php + +declare(strict_types=1); + + /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * + * @author Maxence Lange <maxence@artificial-owl.com> + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -22,7 +28,10 @@ namespace LookupServer; +use BadMethodCallException; +use Exception; use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; class SignatureHandler { @@ -32,57 +41,60 @@ class SignatureHandler { * @param string $cloudId * @param string $message * @param string $signature + * * @return bool - * @throws \Exception + * @throws GuzzleException */ - public function verify($cloudId, $message, $signature) { + public function verify(string $cloudId, string $message, string $signature): bool { // Get fed id list($user, $host) = $this->splitCloudId($cloudId); // Retrieve public key && store $ocsreq = new \GuzzleHttp\Psr7\Request( 'GET', - 'http://'.$host . '/ocs/v2.php/identityproof/key/' . $user, + 'http://' . $host . '/ocs/v2.php/identityproof/key/' . $user, [ 'OCS-APIREQUEST' => 'true', 'Accept' => 'application/json', - ]); + ] + ); $client = new Client(); $ocsresponse = $client->send($ocsreq, ['timeout' => 10]); - $ocsresponse = json_decode($ocsresponse->getBody(), true); + $ocsresponse = json_decode($ocsresponse->getBody()->getContents(), true); - if ($ocsresponse === null || !isset($ocsresponse['ocs']) || - !isset($ocsresponse['ocs']['data']) || !isset($ocsresponse['ocs']['data']['public'])) { - throw new \BadMethodCallException(); + if ($ocsresponse === null || !isset($ocsresponse['ocs']) + || !isset($ocsresponse['ocs']['data']) + || !isset($ocsresponse['ocs']['data']['public'])) { + throw new BadMethodCallException(); } $key = $ocsresponse['ocs']['data']['public']; // verify message $message = json_encode($message); - $signature= base64_decode($signature); + $signature = base64_decode($signature); $res = openssl_verify($message, $signature, $key, OPENSSL_ALGO_SHA512); return $res === 1; - } /** * Split a cloud id in a user and host post * - * @param $cloudId + * @param string $cloudId + * * @return string[] */ - private function splitCloudId($cloudId) { + private function splitCloudId(string $cloudId): array { $loc = strrpos($cloudId, '@'); $user = substr($cloudId, 0, $loc); - $host = substr($cloudId, $loc+1); + $host = substr($cloudId, $loc + 1); + return [$user, $host]; } - } diff --git a/server/lib/Status.php b/server/lib/Status.php deleted file mode 100644 index 69c87de..0000000 --- a/server/lib/Status.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -namespace LookupServer; - -use \Psr\Http\Message\ServerRequestInterface as Request; -use \Psr\Http\Message\ResponseInterface as Response; - -class Status { - - public function status(Request $request, Response $response) { - require __DIR__ . '/../config/version.php'; - - $response->getBody()->write(json_encode(array('version'=>$VERSION))); - } -} diff --git a/server/lib/Tools/Exceptions/ArrayNotFoundException.php b/server/lib/Tools/Exceptions/ArrayNotFoundException.php new file mode 100644 index 0000000..196a869 --- /dev/null +++ b/server/lib/Tools/Exceptions/ArrayNotFoundException.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); + + + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2022 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace LookupServer\Tools\Exceptions; + +use Exception; + +/** + * Class ArrayNotFoundException + * + * @package LookupServer\Tools\Exceptions + */ +class ArrayNotFoundException extends Exception { +} diff --git a/server/lib/Tools/Exceptions/ItemNotFoundException.php b/server/lib/Tools/Exceptions/ItemNotFoundException.php new file mode 100644 index 0000000..833507e --- /dev/null +++ b/server/lib/Tools/Exceptions/ItemNotFoundException.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); + + + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2022 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace LookupServer\Tools\Exceptions; + +use Exception; + +/** + * Class ItemNotFoundException + * + * @package LookupServer\Tools\Exceptions + */ +class ItemNotFoundException extends Exception { +} diff --git a/server/lib/Tools/Exceptions/MalformedArrayException.php b/server/lib/Tools/Exceptions/MalformedArrayException.php new file mode 100644 index 0000000..713b2d7 --- /dev/null +++ b/server/lib/Tools/Exceptions/MalformedArrayException.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); + + + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2022 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace LookupServer\Tools\Exceptions; + +use Exception; + +/** + * Class MalformedArrayException + * + * @package LookupServer\Tools\Exceptions + */ +class MalformedArrayException extends Exception { +} diff --git a/server/lib/Tools/Exceptions/UnknownTypeException.php b/server/lib/Tools/Exceptions/UnknownTypeException.php new file mode 100644 index 0000000..2e8aa62 --- /dev/null +++ b/server/lib/Tools/Exceptions/UnknownTypeException.php @@ -0,0 +1,42 @@ +<?php + +declare(strict_types=1); + + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Maxence Lange <maxence@artificial-owl.com> + * @copyright 2022 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +namespace LookupServer\Tools\Exceptions; + +use Exception; + +/** + * Class UnknownTypeException + * + * @package LookupServer\Tools\Exceptions + */ +class UnknownTypeException extends Exception { +} diff --git a/server/lib/Tools/Traits/TArrayTools.php b/server/lib/Tools/Traits/TArrayTools.php index 8d60a26..f41a925 100644 --- a/server/lib/Tools/Traits/TArrayTools.php +++ b/server/lib/Tools/Traits/TArrayTools.php @@ -33,18 +33,18 @@ namespace LookupServer\Tools\Traits; use Exception; use JsonSerializable; -use OCA\Circles\Tools\Exceptions\ArrayNotFoundException; -use OCA\Circles\Tools\Exceptions\ItemNotFoundException; -use OCA\Circles\Tools\Exceptions\MalformedArrayException; -use OCA\Circles\Tools\Exceptions\UnknownTypeException; +use LookupServer\Tools\Exceptions\ArrayNotFoundException; +use LookupServer\Tools\Exceptions\ItemNotFoundException; +use LookupServer\Tools\Exceptions\MalformedArrayException; +use LookupServer\Tools\Exceptions\UnknownTypeException; trait TArrayTools { - public static $TYPE_NULL = 'Null'; - public static $TYPE_STRING = 'String'; - public static $TYPE_ARRAY = 'Array'; - public static $TYPE_BOOLEAN = 'Boolean'; - public static $TYPE_INTEGER = 'Integer'; - public static $TYPE_SERIALIZABLE = 'Serializable'; + public static string $TYPE_NULL = 'Null'; + public static string $TYPE_STRING = 'String'; + public static string $TYPE_ARRAY = 'Array'; + public static string $TYPE_BOOLEAN = 'Boolean'; + public static string $TYPE_INTEGER = 'Integer'; + public static string $TYPE_SERIALIZABLE = 'Serializable'; /** diff --git a/server/lib/TDebug.php b/server/lib/Tools/Traits/TDebug.php index 789e5f8..73c9d12 100644 --- a/server/lib/TDebug.php +++ b/server/lib/Tools/Traits/TDebug.php @@ -1,6 +1,6 @@ <?php -namespace LookupServer; +namespace LookupServer\Tools\Traits; trait TDebug { diff --git a/server/lib/UserManager.php b/server/lib/UserManager.php index 9a36d8f..d08212f 100644 --- a/server/lib/UserManager.php +++ b/server/lib/UserManager.php @@ -2,44 +2,34 @@ namespace LookupServer; +use Exception; +use GuzzleHttp\Exception\GuzzleException; +use LookupServer\Tools\Traits\TArrayTools; +use LookupServer\Tools\Traits\TDebug; use LookupServer\Validator\Email; use LookupServer\Validator\Twitter; use LookupServer\Validator\Website; +use PDO; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; class UserManager { use TDebug; + use TArrayTools; - - /** @var \PDO */ - private $db; - - /** @var Email */ - private $emailValidator; - - /** @var Website */ - private $websiteValidator; - - /** @var Twitter */ - private $twitterValidator; - - /** @var SignatureHandler */ - private $signatureHandler; - - /** @var int try max. 10 times to verify a account */ - private $maxVerifyTries = 10; - - /** @var bool */ - private $globalScaleMode; - - /** @var string */ - private $authKey; + private PDO $db; + private Email $emailValidator; + private Website $websiteValidator; + private Twitter $twitterValidator; + private SignatureHandler $signatureHandler; + private int $maxVerifyTries = 10; + private bool $globalScaleMode; + private string $authKey; /** * UserManager constructor. * - * @param \PDO $db + * @param PDO $db * @param Email $emailValidator * @param Website $websiteValidator * @param Twitter $twitterValidator @@ -48,13 +38,13 @@ class UserManager { * @param string $authKey */ public function __construct( - \PDO $db, + PDO $db, Email $emailValidator, Website $websiteValidator, Twitter $twitterValidator, SignatureHandler $signatureHandler, - $globalScaleMode, - $authKey + bool $globalScaleMode, + string $authKey ) { $this->db = $db; $this->emailValidator = $emailValidator; @@ -65,33 +55,45 @@ class UserManager { $this->authKey = $authKey; } + + /** + * @param string $input + * + * @return string + */ private function escapeWildcard(string $input): string { //Escape % $output = str_replace('%', '\%', $input); - $output = str_replace('_', '\_', $output); - return $output; + return str_replace('_', '\_', $output); } + /** + * @param Request $request + * @param Response $response + * @param array $args + * + * @return Response + */ public function search(Request $request, Response $response, array $args = []): Response { $params = $request->getQueryParams(); - if (!isset($params['search']) || $params['search'] === '') { + if ($this->get('search', $params) === '') { return $response->withStatus(404); } - $search = $params['search']; + $search = (string)$params['search']; // search for a specific federated cloud ID - $searchCloudId = isset($params['exactCloudId']) ? $params['exactCloudId'] === '1' : '0'; + $searchCloudId = $this->getBool('exactCloudId', $params); // return unique exact match, e.g. the user with a specific email address - $exactMatch = isset($params['exact']) ? $params['exact'] === '1' : false; + $exactMatch = $this->getBool('exact', $params); // parameters allow you to specify which keys should be checked for a search query // by default we check all keys, this way you can for example search for email addresses only $parameters = []; if ($exactMatch === true) { - $keys = isset($params['keys']) ? $params['keys'] : '{}'; + $keys = $this->get('keys', $params, '{}'); $keysDecoded = json_decode($keys, false, 2); if (is_array($keysDecoded)) { $parameters = $keysDecoded; @@ -132,7 +134,12 @@ class UserManager { * * @return array */ - private function performSearch($search, $exactMatch, $parameters, $minKarma) { + private function performSearch( + string $search, + bool $exactMatch, + array $parameters, + int $minKarma + ): array { $operator = $exactMatch ? ' = ' : ' LIKE '; $limit = $exactMatch ? 1 : 50; @@ -166,16 +173,17 @@ ORDER BY karma LIMIT :limit' ); - $stmt->bindParam(':karma', $minKarma, \PDO::PARAM_INT); - $stmt->bindParam(':limit', $limit, \PDO::PARAM_INT); + $stmt->bindParam(':karma', $minKarma, PDO::PARAM_INT); + $stmt->bindParam(':limit', $limit, PDO::PARAM_INT); $search = $exactMatch ? $search : '%' . $this->escapeWildcard($search) . '%'; - $stmt->bindParam('search', $search, \PDO::PARAM_STR); + $stmt->bindParam('search', $search, PDO::PARAM_STR); // bind parameters foreach ($parameters as $parameter) { $i = 0; - $stmt->bindParam(':key' . $i, $this->db->quote($parameter)); + $q = $this->db->quote($parameter); + $stmt->bindParam(':key' . $i, $q); } $stmt->execute(); @@ -194,7 +202,7 @@ LIMIT :limit' } - private function getExactCloudId($cloudId) { + private function getExactCloudId(string $cloudId): array { $stmt = $this->db->prepare('SELECT id FROM users WHERE federationId = :id'); $stmt->bindParam(':id', $cloudId); $stmt->execute(); @@ -205,12 +213,17 @@ LIMIT :limit' } return $this->getForUserId((int)$data['id']); - } - private function getForUserId($userId) { + + /** + * @param int $userId + * + * @return array + */ + private function getForUserId(int $userId): array { $stmt = $this->db->prepare('SELECT * FROM users WHERE id = :id'); - $stmt->bindParam(':id', $userId, \PDO::PARAM_INT); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); $stmt->execute(); $data = $stmt->fetch(); $stmt->closeCursor(); @@ -224,7 +237,7 @@ LIMIT :limit' ]; $stmt = $this->db->prepare('SELECT * FROM store WHERE userId = :id'); - $stmt->bindParam(':id', $userId, \PDO::PARAM_INT); + $stmt->bindParam(':id', $userId, PDO::PARAM_INT); $stmt->execute(); while ($data = $stmt->fetch()) { @@ -244,12 +257,12 @@ LIMIT :limit' * @param string[] $data * @param int $timestamp */ - private function insert($cloudId, $data, $timestamp) { + private function insert(string $cloudId, array $data, int $timestamp): void { $stmt = $this->db->prepare( 'INSERT INTO users (federationId, timestamp) VALUES (:federationId, FROM_UNIXTIME(:timestamp))' ); - $stmt->bindParam(':federationId', $cloudId, \PDO::PARAM_STR); - $stmt->bindParam(':timestamp', $timestamp, \PDO::PARAM_INT); + $stmt->bindParam(':federationId', $cloudId, PDO::PARAM_STR); + $stmt->bindParam(':timestamp', $timestamp, PDO::PARAM_INT); $stmt->execute(); $id = $this->db->lastInsertId(); $stmt->closeCursor(); @@ -265,9 +278,9 @@ LIMIT :limit' } $stmt = $this->db->prepare('INSERT INTO store (userId, k, v) VALUES (:userId, :k, :v)'); - $stmt->bindParam(':userId', $id, \PDO::PARAM_INT); - $stmt->bindParam(':k', $field, \PDO::PARAM_STR); - $stmt->bindParam(':v', $data[$field], \PDO::PARAM_STR); + $stmt->bindParam(':userId', $id, PDO::PARAM_INT); + $stmt->bindParam(':k', $field, PDO::PARAM_STR); + $stmt->bindParam(':v', $data[$field], PDO::PARAM_STR); $stmt->execute(); $storeId = $this->db->lastInsertId(); $stmt->closeCursor(); @@ -283,10 +296,10 @@ LIMIT :limit' * @param string[] $data * @param int $timestamp */ - private function update($id, $data, $timestamp) { + private function update(int $id, array $data, int $timestamp): void { $stmt = $this->db->prepare('UPDATE users SET timestamp = FROM_UNIXTIME(:timestamp) WHERE id = :id'); - $stmt->bindParam(':id', $id, \PDO::PARAM_STR); - $stmt->bindParam(':timestamp', $timestamp, \PDO::PARAM_INT); + $stmt->bindParam(':id', $id, PDO::PARAM_STR); + $stmt->bindParam(':timestamp', $timestamp, PDO::PARAM_INT); $stmt->execute(); $stmt->closeCursor(); $fields = [ @@ -295,7 +308,7 @@ LIMIT :limit' ]; $stmt = $this->db->prepare('SELECT * FROM store WHERE userId = :userId'); - $stmt->bindParam(':userId', $id, \PDO::PARAM_INT); + $stmt->bindParam(':userId', $id, PDO::PARAM_INT); $stmt->execute(); $rows = $stmt->fetchAll(); $stmt->closeCursor(); @@ -306,14 +319,12 @@ LIMIT :limit' unset($fields[$loc]); } - if (!isset($data[$key]) || $data[$key] === '') { + if ($this->get($key, $data) === '') { // key not present in new data so delete $stmt = $this->db->prepare('DELETE FROM store WHERE id = :id'); $stmt->bindParam(':id', $row['id']); $stmt->execute(); $stmt->closeCursor(); - // remove verification request if correspondig data was deleted - $this->removeOpenVerificationRequestByStoreId($row['id']); } else { // Key present check if we need to update if ($data[$key] === $value) { @@ -328,23 +339,24 @@ LIMIT :limit' if ($key === 'email') { $this->emailValidator->emailUpdated($data[$key], $row['id']); } - // remove verification request from old data - $this->removeOpenVerificationRequestByStoreId($row['id']); } + + // remove verification request if correspondig data was deleted + $this->removeOpenVerificationRequestByStoreId($row['id']); } //Check for new fields foreach ($fields as $field) { // Not set or empty field - if (!isset($data[$field]) || $data[$field] === '') { + if ($this->get($field, $data) === '') { continue; } // Insert $stmt = $this->db->prepare('INSERT INTO store (userId, k, v) VALUES (:userId, :k, :v)'); - $stmt->bindParam(':userId', $id, \PDO::PARAM_INT); - $stmt->bindParam(':k', $field, \PDO::PARAM_STR); - $stmt->bindParam(':v', $data[$field], \PDO::PARAM_STR); + $stmt->bindParam(':userId', $id, PDO::PARAM_INT); + $stmt->bindParam(':k', $field, PDO::PARAM_STR); + $stmt->bindParam(':v', $data[$field], PDO::PARAM_STR); $stmt->execute(); $storeId = $this->db->lastInsertId(); $stmt->closeCursor(); @@ -355,9 +367,16 @@ LIMIT :limit' } } - private function needToVerify($userId, $storeId, $data, $key) { + + /** + * @param string $userId + * @param int $storeId + * @param array $data + * @param string $key + */ + private function needToVerify(string $userId, int $storeId, array $data, string $key): void { $stmt = $this->db->prepare('SELECT * FROM toVerify WHERE storeId = :storeId'); - $stmt->bindParam(':storeId', $storeId, \PDO::PARAM_INT); + $stmt->bindParam(':storeId', $storeId, PDO::PARAM_INT); $stmt->execute(); $alreadyExists = $stmt->fetch(); @@ -367,23 +386,24 @@ LIMIT :limit' $stmt = $this->db->prepare( 'INSERT INTO toVerify (userId, storeId, property, location, tries) VALUES (:userId, :storeId, :property, :location, :tries)' ); - $stmt->bindParam(':userId', $userId, \PDO::PARAM_INT); - $stmt->bindParam(':storeId', $storeId, \PDO::PARAM_INT); + $stmt->bindParam(':userId', $userId, PDO::PARAM_INT); + $stmt->bindParam(':storeId', $storeId, PDO::PARAM_INT); $stmt->bindParam(':property', $key); $stmt->bindParam(':location', $data[$key]); - $stmt->bindParam(':tries', $tries, \PDO::PARAM_INT); + $stmt->bindParam(':tries', $tries, PDO::PARAM_INT); $stmt->execute(); $stmt->closeCursor(); } } - public function register(Request $request, Response $response, array $args = []) { + public function register(Request $request, Response $response, array $args = []): Response { $body = json_decode($request->getBody(), true); - if ($body === null || !isset($body['message']) || !isset($body['message']['data']) || - !isset($body['message']['data']['federationId']) || !isset($body['signature']) || - !isset($body['message']['timestamp'])) { + if ($body === null || !isset($body['message']) || !isset($body['message']['data']) + || !isset($body['message']['data']['federationId']) + || !isset($body['signature']) + || !isset($body['message']['timestamp'])) { return $response->withStatus(400); } @@ -391,7 +411,7 @@ LIMIT :limit' try { $verified = $this->signatureHandler->verify($cloudId, $body['message'], $body['signature']); - } catch(\Exception $e) { + } catch (Exception $e) { return $response->withStatus(400); } @@ -399,7 +419,7 @@ LIMIT :limit' $result = $this->insertOrUpdate($cloudId, $body['message']['data'], $body['message']['timestamp']); if ($result === false) { - return $response->withStatus(403); + return $response->withStatus(403); } } else { // ERROR OUT @@ -414,10 +434,11 @@ LIMIT :limit' * * @param Request $request * @param Response $response + * @param array $args * * @return Response */ - public function batchRegister(Request $request, Response $response) { + public function batchRegister(Request $request, Response $response, array $args = []): Response { $body = json_decode($request->getBody(), true); @@ -442,10 +463,11 @@ LIMIT :limit' * * @param Request $request * @param Response $response + * @param array $args * * @return Response */ - public function batchDelete(Request $request, Response $response) { + public function batchDelete(Request $request, Response $response, array $args = []): Response { $body = json_decode($request->getBody(), true); @@ -466,12 +488,21 @@ LIMIT :limit' } - public function delete(Request $request, Response $response) { + /** + * @param Request $request + * @param Response $response + * @param array $args + * + * @return Response + * @throws GuzzleException + */ + public function delete(Request $request, Response $response, array $args = []): Response { $body = json_decode($request->getBody(), true); - if ($body === null || !isset($body['message']) || !isset($body['message']['data']) || - !isset($body['message']['data']['federationId']) || !isset($body['signature']) || - !isset($body['message']['timestamp'])) { + if ($body === null || !isset($body['message']) || !isset($body['message']['data']) + || !isset($body['message']['data']['federationId']) + || !isset($body['signature']) + || !isset($body['message']['timestamp'])) { return $response->withStatus(400); } @@ -479,7 +510,7 @@ LIMIT :limit' try { $verified = $this->signatureHandler->verify($cloudId, $body['message'], $body['signature']); - } catch(\Exception $e) { + } catch (Exception $e) { return $response->withStatus(400); } @@ -497,7 +528,19 @@ LIMIT :limit' return $response; } - public function verify(Request $request, Response $response) { + + /** + * @param Request|null $request + * @param Response|null $response + * @param array $args + * + * @return Response|null + */ + public function verify( + ?Request $request = null, + ?Response $response = null, + array $args = [] + ): ?Response { $verificationRequests = $this->getOpenVerificationRequests(); foreach ($verificationRequests as $verificationData) { $success = false; @@ -518,14 +561,16 @@ LIMIT :limit' $this->incMaxTries($verificationData); } } + + return $response; } /** * increase number of max tries to verify account data * - * @param $verificationData + * @param array $verificationData */ - private function incMaxTries($verificationData) { + private function incMaxTries(array $verificationData): void { $tries = (int)$verificationData['tries']; $tries++; @@ -546,9 +591,9 @@ LIMIT :limit' /** * if data could be verified successfully we update the information in the store table * - * @param $storeId + * @param int $storeId */ - private function updateVerificationStatus($storeId) { + private function updateVerificationStatus(int $storeId): void { $stmt = $this->db->prepare('UPDATE store SET valid = 1 WHERE id = :storeId'); $stmt->bindParam('storeId', $storeId); $stmt->execute(); @@ -585,7 +630,7 @@ LIMIT :limit' * * @return array */ - private function getOpenVerificationRequests() { + private function getOpenVerificationRequests(): array { $stmt = $this->db->prepare('SELECT * FROM toVerify LIMIT 10'); $stmt->execute(); $result = $stmt->fetchAll(); @@ -601,7 +646,7 @@ LIMIT :limit' * * @return bool */ - private function insertOrUpdate($cloudId, $data, $timestamp) { + private function insertOrUpdate(string $cloudId, array $data, int $timestamp): bool { $stmt = $this->db->prepare('SELECT * FROM users WHERE federationId = :federationId'); $stmt->bindParam(':federationId', $cloudId); $stmt->execute(); @@ -633,7 +678,7 @@ LIMIT :limit' * * @return bool */ - private function deleteDBRecord($cloudId) { + private function deleteDBRecord(string $cloudId): bool { $stmt = $this->db->prepare('SELECT * FROM users WHERE federationId = :federationId'); $stmt->bindParam(':federationId', $cloudId); diff --git a/server/lib/Validator/Email.php b/server/lib/Validator/Email.php index a71dd22..06c63c0 100644 --- a/server/lib/Validator/Email.php +++ b/server/lib/Validator/Email.php @@ -1,49 +1,77 @@ <?php +declare(strict_types=1); + + +/** + * lookup-server - Standalone Lookup Server. + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Bjoern Schiessle <bjoern@schiessle.org> + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @copyright 2017 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + namespace LookupServer\Validator; +use Exception; +use PDO; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; +use Slim\Interfaces\RouteParserInterface; +use Slim\Routing\Route; class Email { - /** @var \PDO */ - private $db; - - /** @var \Slim\Interfaces\RouterInterface */ - private $router; - - /** @var string */ - private $host; - - /** @var string */ - private $from; + private PDO $db; + private RouteParserInterface $routeParser; + private string $host; + private string $from; + private bool $globalScale; - /** @var bool */ - private $globalScale; /** - * Email constructor. - * @param \PDO $db - * @param \Slim\Interfaces\RouterInterface $router + * @param PDO $db + * @param RouteParserInterface $routeParser * @param string $host * @param string $from * @param bool $globalScale */ - public function __construct(\PDO $db, -// \Slim\Interfaces\RouterInterface $router, - $host, - $from, - $globalScale) { + public function __construct( + PDO $db, + RouteParserInterface $routeParser, + string $host, + string $from, + bool $globalScale + ) { $this->db = $db; -// $this->router = $router; + $this->routeParser = $routeParser; $this->host = $host; $this->from = $from; $this->globalScale = $globalScale; } - public function validate(Request $request, Response $response, array $args = []) { - /** @var $route \Slim\Route */ + public function validate(Request $request, Response $response, array $args = []): Response { + /** @var Route $route */ $route = $request->getAttribute('route'); $token = $route->getArgument('token'); @@ -72,7 +100,7 @@ class Email { return $response; } - public function emailUpdated($email, $storeId) { + public function emailUpdated(string $email, int $storeId): void { if ($this->globalScale) { // When in global scale mode we should not send e-mails return; @@ -95,23 +123,34 @@ class Email { $stmt->closeCursor(); // Actually send e-mail - $link = $this->host . $this->router->pathFor('validateEmail', ['token' => $token]); + $link = $this->host . $this->routeParser->urlFor('validateEmail', ['token' => $token]); $text = 'Please click this link to confirm your e-mail address: ' . $link; - $headers = 'From: '.$this->from."\r\n" .'X-Mailer: PHP/' . phpversion(); + $headers = 'From: ' . $this->from . "\r\n" . 'X-Mailer: PHP/' . phpversion(); mail($email, 'Email confirmation', $text, $headers); } - private function generate($length, - $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') { + + /** + * @param int $length + * @param string $characters + * + * @return string + * @throws Exception + */ + private function generate( + int $length, + string $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + ): string { $maxCharIndex = strlen($characters) - 1; $randomString = ''; - while($length > 0) { - $randomNumber = \random_int(0, $maxCharIndex); + while ($length > 0) { + $randomNumber = random_int(0, $maxCharIndex); $randomString .= $characters[$randomNumber]; $length--; } + return $randomString; } } diff --git a/server/lib/Validator/Twitter.php b/server/lib/Validator/Twitter.php index 7cd3724..b86a661 100644 --- a/server/lib/Validator/Twitter.php +++ b/server/lib/Validator/Twitter.php @@ -1,7 +1,12 @@ <?php + +declare(strict_types=1); + /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * + * @author Maxence Lange <maxence@artificial-owl.com> + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -24,27 +29,24 @@ namespace LookupServer\Validator; use Abraham\TwitterOAuth\TwitterOAuth; +use Exception; use LookupServer\SignatureHandler; +use PDO; class Twitter { - /** @var TwitterOAuth */ - private $twitterOAuth; - - /** @var SignatureHandler */ - private $signatureHandler; - - /** @var \PDO */ - private $db; + private TwitterOAuth $twitterOAuth; + private SignatureHandler $signatureHandler; + private PDO $db; /** * Twitter constructor. * * @param TwitterOAuth $twitterOAuth * @param SignatureHandler $signatureHandler - * @param \PDO $db + * @param PDO $db */ - public function __construct(TwitterOAuth $twitterOAuth, SignatureHandler $signatureHandler, \PDO $db) { + public function __construct(TwitterOAuth $twitterOAuth, SignatureHandler $signatureHandler, PDO $db) { $this->twitterOAuth = $twitterOAuth; $this->signatureHandler = $signatureHandler; $this->db = $db; @@ -55,9 +57,10 @@ class Twitter { * * @param array $verificationData from toVerify table * @param array $userData stored user data + * * @return bool */ - public function verify(array $verificationData, array $userData) { + public function verify(array $verificationData, array $userData): bool { $twitterHandle = $verificationData['location']; $isValid = $this->isValidTwitterHandle($twitterHandle); $result = false; @@ -76,8 +79,8 @@ class Twitter { $result = $this->signatureHandler->verify($cloudId, $message, $signature); $result = $result && md5($signature) === $md5signature; } - } catch (\Exception $e) { - // do nothing, just return false; + } catch (Exception $e) { + return false; } if ($result === true) { @@ -91,9 +94,10 @@ class Twitter { * get tweet text and id * * @param string $userName user name without the '@' + * * @return array */ - private function getTweet($userName) { + private function getTweet(string $userName): array { try { $search = 'from:' . $userName . ' Use my Federated Cloud ID to share with me'; @@ -101,7 +105,7 @@ class Twitter { $id = $statuses->statuses[0]->id; $text = $statuses->statuses[0]->text; - } catch (\Exception $e) { + } catch (Exception $e) { return [null, null]; } @@ -111,11 +115,13 @@ class Twitter { /** * check if we have a correct twitter Handle * - * @param $twitterHandle + * @param string $twitterHandle + * * @return bool */ - private function isValidTwitterHandle($twitterHandle) { + private function isValidTwitterHandle(string $twitterHandle): bool { $result = preg_match('/^@[A-Za-z0-9_]+$/', $twitterHandle); + return $result === 1; } @@ -123,9 +129,10 @@ class Twitter { * split message and signature * * @param string $proof + * * @return array */ - private function splitMessageSignature($proof) { + private function splitMessageSignature(string $proof): array { $signature = substr($proof, -32); $message = substr($proof, 0, -32); @@ -135,25 +142,25 @@ class Twitter { /** * store reference to tweet * - * @param $userId - * @param $tweetId + * @param int $userId + * @param string $tweetId */ - private function storeReference($userId, $tweetId) { + private function storeReference(int $userId, string $tweetId) { $key = 'tweet_id'; // delete old value, if exists $stmt = $this->db->prepare('DELETE FROM store WHERE userId = :userId AND k = :k'); - $stmt->bindParam(':userId', $userId, \PDO::PARAM_INT); - $stmt->bindParam(':k', $key, \PDO::PARAM_STR); + $stmt->bindParam(':userId', $userId, PDO::PARAM_INT); + $stmt->bindParam(':k', $key, PDO::PARAM_STR); $stmt->execute(); $stmt->closeCursor(); // add new value $stmt = $this->db->prepare('INSERT INTO store (userId, k, v) VALUES (:userId, :k, :v)'); - $stmt->bindParam(':userId', $userId, \PDO::PARAM_INT); - $stmt->bindParam(':k', $key, \PDO::PARAM_STR); - $stmt->bindParam(':v', $tweetId, \PDO::PARAM_STR); + $stmt->bindParam(':userId', $userId, PDO::PARAM_INT); + $stmt->bindParam(':k', $key, PDO::PARAM_STR); + $stmt->bindParam(':v', $tweetId, PDO::PARAM_STR); $stmt->execute(); $stmt->closeCursor(); } diff --git a/server/lib/Validator/Website.php b/server/lib/Validator/Website.php index e43bfb0..0fa904f 100644 --- a/server/lib/Validator/Website.php +++ b/server/lib/Validator/Website.php @@ -1,7 +1,12 @@ <?php + +declare(strict_types=1); + /** * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> * + * @author Maxence Lange <maxence@artificial-owl.com> + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -23,12 +28,12 @@ namespace LookupServer\Validator; +use Exception; use LookupServer\SignatureHandler; class Website { - /** @var SignatureHandler */ - private $signatureHandler; + private SignatureHandler $signatureHandler; public function __construct(SignatureHandler $signatureHandler) { $this->signatureHandler = $signatureHandler; @@ -39,9 +44,10 @@ class Website { * * @param array $verificationData from toVerify table * @param array $userData stored user data + * * @return bool */ - public function verify($verificationData, $userData) { + public function verify(array $verificationData, array $userData): bool { $url = $this->getValidUrl($verificationData['location']); $proof = @file_get_contents($url); $result = false; @@ -52,7 +58,7 @@ class Website { list($message, $signature) = $this->splitMessageSignature($proofSanitized); $result = $this->signatureHandler->verify($cloudId, $message, $signature); } - } catch (\Exception $e) { + } catch (Exception $e) { // do nothing, just return false } @@ -63,9 +69,10 @@ class Website { * construct valid URL to proof * * @param string $url + * * @return string */ - private function getValidUrl($url) { + private function getValidUrl(string $url): string { $url = trim($url); $url = rtrim($url, '/'); if (strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) { @@ -79,9 +86,10 @@ class Website { * split message and signature * * @param string $proof + * * @return array */ - private function splitMessageSignature($proof) { + private function splitMessageSignature(string $proof): array { $signature = substr($proof, -344); $message = substr($proof, 0, -344); diff --git a/server/replicationcron.php b/server/replicationcron.php index 5b1555c..96d95d7 100755 --- a/server/replicationcron.php +++ b/server/replicationcron.php @@ -1,19 +1,20 @@ <?php +use LookupServer\Replication; + require __DIR__ . '/vendor/autoload.php'; if (PHP_SAPI !== 'cli') { return; } -$env = \Slim\Http\Environment::mock(['REQUEST_URI' => '/import']); -$settings = require __DIR__ . '/src/config.php'; -$settings['environment'] = $env; -$container = new \Slim\Container($settings); -require __DIR__ . '/src/dependencies.php'; +require __DIR__ . '/init.php'; -$app = new \Slim\App($container); +if (!isset($app) || !isset($container)) { + return; +} -$app->map(['GET'], '/import', 'Replication:import'); -$app->run(); +/** @var Replication $replication */ +$replication = $container->get('Replication'); +$replication->import(); diff --git a/server/verifycron.php b/server/verifycron.php index ad4dc48..85c4e4a 100644 --- a/server/verifycron.php +++ b/server/verifycron.php @@ -19,20 +19,25 @@ * */ +use LookupServer\UserManager; + require __DIR__ . '/vendor/autoload.php'; if (PHP_SAPI !== 'cli') { - return; + return; } -$env = \Slim\Http\Environment::mock(['REQUEST_URI' => '/verify']); -$settings = require __DIR__ . '/src/config.php'; -$settings['environment'] = $env; -$container = new \Slim\Container($settings); -require __DIR__ . '/src/dependencies.php'; +require __DIR__ . '/init.php'; + +if (!isset($app) || !isset($container)) { + return; +} + +/** @var UserManager $userManager */ +$userManager = $container->get('UserManager'); +$userManager->verify(); -$app = new \Slim\App($container); $app->map(['GET'], '/verify', 'UserManager:verify'); $app->run(); |