From 17be6ea3fd4abebb5da6c1441a5b7259c2671d48 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 18 Feb 2021 14:03:59 +0100 Subject: [PATCH] Initial prototype --- .gitignore | 1 + public/api/composer.json | 5 + public/api/composer.lock | 678 ++++++++++++++++++++++ public/api/events.php | 44 ++ public/api/services/authenticator.inc.php | 7 + public/api/services/jsonBuilder.inc.php | 18 + public/api/services/jugglDbApi.inc.php | 102 ++++ public/api/sse_client.html | 14 + 8 files changed, 869 insertions(+) create mode 100644 public/api/composer.json create mode 100644 public/api/composer.lock create mode 100644 public/api/events.php create mode 100644 public/api/sse_client.html diff --git a/.gitignore b/.gitignore index a837efe..4b1ef6a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ graphics .vscode juggl-vue/package-lock.json package-lock.json +public/api/vendor/ \ No newline at end of file diff --git a/public/api/composer.json b/public/api/composer.json new file mode 100644 index 0000000..9c9df9c --- /dev/null +++ b/public/api/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "tonyhhyip/sse": "^2.1" + } +} diff --git a/public/api/composer.lock b/public/api/composer.lock new file mode 100644 index 0000000..75a513a --- /dev/null +++ b/public/api/composer.lock @@ -0,0 +1,678 @@ +{ + "_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#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "361f9482844d6c22d6efec567d317681", + "packages": [ + { + "name": "symfony/deprecation-contracts", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-09-07T11:33:47+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v4.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "8888741b633f6c3d1e572b7735ad2cae3e03f9c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8888741b633f6c3d1e572b7735ad2cae3e03f9c5", + "reference": "8888741b633f6c3d1e572b7735ad2cae3e03f9c5", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/mime": "^4.3|^5.0", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php80": "^1.15" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/expression-language": "^3.4|^4.0|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-27T09:09:26+00:00" + }, + { + "name": "symfony/mime", + "version": "v5.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/7dee6a43493f39b51ff6c5bb2bd576fe40a76c86", + "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.15" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<4.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/property-access": "^4.4|^5.1", + "symfony/property-info": "^4.4|^5.1", + "symfony/serializer": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-02T06:10:15+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "2d63434d922daf7da8dd863e7907e67ee3031483" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483", + "reference": "2d63434d922daf7da8dd863e7907e67ee3031483", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "tonyhhyip/sse", + "version": "2.1.3", + "source": { + "type": "git", + "url": "https://github.com/tonyhhyip/libSSE-php.git", + "reference": "804af074e46247b5d9290b509c5cab8de523c93a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tonyhhyip/libSSE-php/zipball/804af074e46247b5d9290b509c5cab8de523c93a", + "reference": "804af074e46247b5d9290b509c5cab8de523c93a", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/http-foundation": "~2.7 | ~3.0 | ~4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.2 | ~5.0" + }, + "suggest": { + "predis/predis": "For using RedisMechnism", + "symfony/polyfill-apcu": "For APCMecnism on PHP < 7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Sse\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Licson Lee", + "email": "licson0729@gmail.com", + "homepage": "https://licson.net/", + "role": "Owner" + }, + { + "name": "Tony Yip", + "email": "tony@opensource.hk", + "homepage": "https://github.com/tonyhhyip", + "role": "Developer" + } + ], + "description": "An easy-to-use, object-oriented library for Server-Sent Events", + "homepage": "https://github.com/licson0729/libSSE-php", + "keywords": [ + "sse" + ], + "time": "2018-03-08T02:55:29+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "1.1.0" +} diff --git a/public/api/events.php b/public/api/events.php new file mode 100644 index 0000000..f3e4761 --- /dev/null +++ b/public/api/events.php @@ -0,0 +1,44 @@ +isSseAuthenticated($_GET["user_id"], $_GET[$SSE_KEY_KEY])) { + respondStatus(403); +} + +$sse = new SSE(); //create a libSSE instance + +$sse->exec_limit = 600; //the execution time of the loop in seconds. Default: 600. Set to 0 to allow the script to run as long as possible. +$sse->sleep_time = 5; //The time to sleep after the data has been sent in seconds. Default: 0.5. +$sse->client_reconnect = 10; //the time for the client to reconnect after the connection has lost in seconds. Default: 1. +$sse->use_chunked_encoding = false; //Use chunked encoding. Some server may get problems with this and it defaults to false +$sse->keep_alive_time = 600; //The interval of sending a signal to keep the connection alive. Default: 300 seconds. +$sse->allow_cors = true; //Allow cross-domain access? Default: false. If you want others to access this must set to true. + +$sse->addEventListener('change', new YourEventHandler());//register your event handler +$sse->start();//start the event loop + +?> \ No newline at end of file diff --git a/public/api/services/authenticator.inc.php b/public/api/services/authenticator.inc.php index 39b94e5..afe645f 100644 --- a/public/api/services/authenticator.inc.php +++ b/public/api/services/authenticator.inc.php @@ -1,5 +1,7 @@ isApiKeyAuthenticated($params->get('api_key'), $params->get('user_id')); } + + function isSseAuthenticated($user_id, $sse_key) { + $params = new ParamCleaner(["sse_key" => $sse_key]); + return validatedSseKey($user_id, $params); + } } ?> \ No newline at end of file diff --git a/public/api/services/jsonBuilder.inc.php b/public/api/services/jsonBuilder.inc.php index 514c7a1..899953d 100644 --- a/public/api/services/jsonBuilder.inc.php +++ b/public/api/services/jsonBuilder.inc.php @@ -78,6 +78,24 @@ class JsonBuilder return $this; } + function addSseKey(array $sse) + { + if ($sse === null) return; + + $columns = array( + "sse_key_id" => "", + "sse_key" => "", + "user_id" => "", + "created" => "" + ); + + $this->jsonData['sse'] = array(); + foreach ($sse as $sse) { + $this->jsonData['sse'][] = $this->createJsonArray($sse, $columns); + } + return $this; + } + function addRecordTags(array $record_tags) { if ($record_tags === null) return; diff --git a/public/api/services/jugglDbApi.inc.php b/public/api/services/jugglDbApi.inc.php index 18170aa..f5b4971 100644 --- a/public/api/services/jugglDbApi.inc.php +++ b/public/api/services/jugglDbApi.inc.php @@ -286,6 +286,108 @@ function removeProject($user_id, $params) removeRecordsOfProject($user_id, $project_id); } +function secureRandomBytes () +{ + $pr_bits = ''; + + // Unix/Linux platform? + $fp = @fopen('/dev/urandom','rb'); + if ($fp !== FALSE) { + $pr_bits .= @fread($fp,16); + @fclose($fp); + } + + // MS-Windows platform? + if (@class_exists('COM')) { + // http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx + try { + $CAPI_Util = new COM('CAPICOM.Utilities.1'); + $pr_bits .= $CAPI_Util->GetRandom(16,0); + + // if we ask for binary data PHP munges it, so we + // request base64 return value. We squeeze out the + // redundancy and useless ==CRLF by hashing... + if ($pr_bits) { $pr_bits = md5($pr_bits,TRUE); } + } catch (Exception $ex) { + // echo 'Exception: ' . $ex->getMessage(); + } + } + + return $pr_bits; +} + +function createSseKey($user_id) +{ + // Generate random key + $sse_key = secureRandomBytes(); + + $data = [ + "user_id" => $user_id, + "sse_key" => $sse_key + ]; + + $db = new DbOperations(); + $db->insert("sse_keys", $data); + $db->execute(); + + return $sse_key; +} + +function deleteSseKey($user_id, $params) +{ + $sse_key_id = $params->get("sse_key_id"); + $sse_key = $params->get("sse_key"); + + $db = new DbOperations(); + $db->delete("sse_keys"); + $db->where("user_id", Comparison::EQUAL, $user_id); + if ($sse_key_id != null) { + $db->where("sse_key_id", Comparison::EQUAL, $sse_key_id); + } + if ($sse_key != null) { + $db->where("sse_key", Comparison::EQUAL, $sse_key); + } + $db->execute(); +} + +function getSseKey($user_id, $params) +{ + $sse_key_id = $params->get("sse_key_id"); + $sse_key = $params->get("sse_key"); + + $db = new DbOperations(); + $db->select("sse_keys"); + $db->where("user_id", Comparison::EQUAL, $user_id); + if ($sse_key_id != null) { + $db->where("sse_key_id", Comparison::EQUAL, $sse_key_id); + } + if ($sse_key != null) { + $db->where("sse_key", Comparison::EQUAL, $sse_key); + } + $result = $db->execute(); + + if (count($result) <= 0) { + return null; + } + $result = $result[0]; + + return $result; +} + +function getAllSseKeys($user_id) +{ + $db = new DbOperations(); + $db->select("sse_keys"); + $db->where("user_id", Comparison::EQUAL, $user_id); + return $db->execute(); +} + +function validatedSseKey($user_id, $params) +{ + $key_entry = getSseKey($user_id, $params); + return $key_entry != null; +} + function removeRecordsOfProject($user_id, $project_id) { $db = new DbOperations(); diff --git a/public/api/sse_client.html b/public/api/sse_client.html new file mode 100644 index 0000000..b229ea0 --- /dev/null +++ b/public/api/sse_client.html @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file