From 33ab43bad82fc2e2a3bc3c729d0377c8c2718e2b Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Jan 2024 17:46:15 +0100 Subject: [PATCH 1/5] Add `nextcloud/coding-standard` as dev dependency. --- solid/composer.json | 1 + solid/composer.lock | 95 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/solid/composer.json b/solid/composer.json index f249dac..1c56310 100644 --- a/solid/composer.json +++ b/solid/composer.json @@ -37,6 +37,7 @@ }, "require-dev": { "doctrine/dbal": "*", + "nextcloud/coding-standard": "^1.1", "nextcloud/server": "*", "phpunit/phpunit": "^8 || ^9" }, diff --git a/solid/composer.lock b/solid/composer.lock index 45cc666..4eeb7dd 100644 --- a/solid/composer.lock +++ b/solid/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "aa2376c8b5177bdb34dc7ad46e32d576", + "content-hash": "04feb350329388d2b2973bf0c46d0936", "packages": [ { "name": "arc/base", @@ -2730,6 +2730,47 @@ ], "time": "2023-03-08T13:26:56+00:00" }, + { + "name": "nextcloud/coding-standard", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/nextcloud/coding-standard.git", + "reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/55def702fb9a37a219511e1d8c6fe8e37164c1fb", + "reference": "55def702fb9a37a219511e1d8c6fe8e37164c1fb", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0", + "php-cs-fixer/shim": "^3.17" + }, + "type": "library", + "autoload": { + "psr-4": { + "Nextcloud\\CodingStandard\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + } + ], + "description": "Nextcloud coding standards for the php cs fixer", + "support": { + "issues": "https://github.com/nextcloud/coding-standard/issues", + "source": "https://github.com/nextcloud/coding-standard/tree/v1.1.1" + }, + "time": "2023-06-01T12:05:01+00:00" + }, { "name": "nextcloud/server", "version": "27.0.0", @@ -2921,6 +2962,58 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "php-cs-fixer/shim", + "version": "v3.47.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/shim.git", + "reference": "851a945ee0ae0f9d08c426ec2b1702bb34bd4ea2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/shim/zipball/851a945ee0ae0f9d08c426ec2b1702bb34bd4ea2", + "reference": "851a945ee0ae0f9d08c426ec2b1702bb34bd4ea2", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "replace": { + "friendsofphp/php-cs-fixer": "self.version" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer", + "php-cs-fixer.phar" + ], + "type": "application", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz RumiƄski", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "support": { + "issues": "https://github.com/PHP-CS-Fixer/shim/issues", + "source": "https://github.com/PHP-CS-Fixer/shim/tree/v3.47.1" + }, + "time": "2024-01-16T18:54:50+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.30", From 3d43cc9aec857c4c5f530624d334047aba8ab23a Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Jan 2024 17:49:39 +0100 Subject: [PATCH 2/5] Add PHP CodeSniffer Fixer config. --- solid/.php-cs-fixer.dist.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 solid/.php-cs-fixer.dist.php diff --git a/solid/.php-cs-fixer.dist.php b/solid/.php-cs-fixer.dist.php new file mode 100644 index 0000000..06db036 --- /dev/null +++ b/solid/.php-cs-fixer.dist.php @@ -0,0 +1,16 @@ +getFinder() + ->ignoreVCSIgnored(true) + ->notPath('vendor') + ->in(__DIR__); + +return $config; From b6b461c7d23eee32a062f8a003717074aee24441 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Jan 2024 17:50:15 +0100 Subject: [PATCH 3/5] Add PHP CodeSniffer Fixer cache file to gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d8b8e4f..801ca12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ solid/bin solid/vendor +solid/.php-cs-fixer.cache From d1853be41843555241f7802b903bb2b3541aac59 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Jan 2024 18:11:01 +0100 Subject: [PATCH 4/5] Change PHP Codesniffer config to match `nextcloud/coding-standard` conventions. --- build/phpcs.xml.dist | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/build/phpcs.xml.dist b/build/phpcs.xml.dist index fc1286e..e4e2f1e 100644 --- a/build/phpcs.xml.dist +++ b/build/phpcs.xml.dist @@ -25,25 +25,10 @@ - - - - - - - - - - - - - - - From 0da2e8d63f73850a05b1a7b195cb6c804080d111 Mon Sep 17 00:00:00 2001 From: Ben Peachey Date: Fri, 19 Jan 2024 18:13:39 +0100 Subject: [PATCH 5/5] Fix PHP Codesniffer violations. --- solid/appinfo/routes.php | 104 ++--- solid/lib/AppInfo/Application.php | 130 +++--- solid/lib/BearerFactoryTrait.php | 41 +- solid/lib/Controller/AppController.php | 31 +- solid/lib/Controller/CalendarController.php | 44 +-- solid/lib/Controller/ContactsController.php | 45 ++- solid/lib/Controller/PageController.php | 29 +- solid/lib/Controller/ProfileController.php | 120 +++--- solid/lib/Controller/ServerController.php | 69 ++-- .../lib/Controller/SolidWebhookController.php | 46 +-- .../Controller/SolidWebsocketController.php | 32 +- solid/lib/Controller/StorageController.php | 76 ++-- solid/lib/Db/SolidWebhook.php | 1 - solid/lib/DpopFactoryTrait.php | 48 ++- solid/lib/JtiReplayDetector.php | 97 +++-- solid/lib/Middleware/SolidCorsMiddleware.php | 114 +++--- .../Version000000Date20220919084800.php | 2 +- .../Version000000Date20221020145600.php | 89 +++-- .../lib/Notifications/SolidNotifications.php | 44 +-- solid/lib/Notifications/SolidPubSub.php | 46 +-- solid/lib/Notifications/SolidWebhook.php | 144 +++---- solid/lib/PlainResponse.php | 91 ++--- solid/lib/ServerConfig.php | 373 +++++++++--------- solid/lib/Service/SolidWebhookService.php | 6 +- solid/lib/Service/UserService.php | 21 +- solid/lib/Settings.php | 1 - .../WellKnown/OpenIdConfigurationHandler.php | 56 ++- solid/lib/WellKnown/SolidHandler.php | 25 +- solid/templates/admin.php | 26 +- solid/templates/applauncher.php | 8 +- solid/templates/applauncher/index.php | 130 +++--- solid/templates/profile.php | 8 +- solid/templates/profile/index.php | 24 +- solid/templates/profile/turtle.php | 10 +- solid/templates/sharing.php | 22 +- solid/templates/turtle-profile.php | 6 +- solid/tests/Integration/AppTest.php | 21 +- .../Unit/Controller/PageControllerTest.php | 8 +- solid/tests/Unit/JtiReplayDetectorTest.php | 32 +- solid/tests/bootstrap.php | 12 +- 40 files changed, 1084 insertions(+), 1148 deletions(-) diff --git a/solid/appinfo/routes.php b/solid/appinfo/routes.php index c996d70..75e3701 100644 --- a/solid/appinfo/routes.php +++ b/solid/appinfo/routes.php @@ -1,4 +1,5 @@ [ - ['name' => 'page#profile', 'url' => '/@{userId}/', 'verb' => 'GET'], - ['name' => 'page#approval', 'url' => '/sharing/{clientId}', 'verb' => 'GET'], - ['name' => 'page#handleRevoke', 'url' => '/revoke/{clientId}', 'verb' => 'GET'], - ['name' => 'page#handleApproval', 'url' => '/sharing/{clientId}', 'verb' => 'POST'], - ['name' => 'page#dataJson', 'url' => '/@{userId}/data.json', 'verb' => 'GET' ], - - ['name' => 'server#cors', 'url' => '/{path}', 'verb' => 'OPTIONS', 'requirements' => array('path' => '.+') ], - ['name' => 'server#authorize', 'url' => '/authorize', 'verb' => 'GET'], - ['name' => 'server#jwks', 'url' => '/jwks', 'verb' => 'GET'], - ['name' => 'server#session', 'url' => '/session', 'verb' => 'GET'], - ['name' => 'server#logout', 'url' => '/logout', 'verb' => 'GET'], - ['name' => 'server#token', 'url' => '/token', 'verb' => 'POST'], - ['name' => 'server#userinfo', 'url' => '/userinfo', 'verb' => 'GET'], - ['name' => 'server#register', 'url' => '/register', 'verb' => 'POST'], - ['name' => 'server#registeredClient', 'url' => '/register/{clientId}', 'verb' => 'GET'], - - ['name' => 'profile#handleGet', 'url' => '/@{userId}/profile{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')], - ['name' => 'profile#handlePut', 'url' => '/@{userId}/profile{path}', 'verb' => 'PUT', 'requirements' => array('path' => '.+')], - ['name' => 'profile#handlePatch', 'url' => '/@{userId}/profile{path}', 'verb' => 'PATCH', 'requirements' => array('path' => '.+')], - ['name' => 'profile#handleHead', 'url' => '/@{userId}/profile{path}', 'verb' => 'HEAD', 'requirements' => array('path' => '.+')], - - ['name' => 'storage#handleGet', 'url' => '/@{userId}/storage{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')], - ['name' => 'storage#handlePost', 'url' => '/@{userId}/storage{path}', 'verb' => 'POST', 'requirements' => array('path' => '.+')], - ['name' => 'storage#handlePut', 'url' => '/@{userId}/storage{path}', 'verb' => 'PUT', 'requirements' => array('path' => '.+')], - ['name' => 'storage#handleDelete', 'url' => '/@{userId}/storage{path}', 'verb' => 'DELETE', 'requirements' => array('path' => '.+')], - ['name' => 'storage#handlePatch', 'url' => '/@{userId}/storage{path}', 'verb' => 'PATCH', 'requirements' => array('path' => '.+')], - ['name' => 'storage#handleHead', 'url' => '/@{userId}/storage{path}', 'verb' => 'HEAD', 'requirements' => array('path' => '.+')], - - ['name' => 'calendar#handleGet', 'url' => '/@{userId}/calendar{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')], - ['name' => 'calendar#handlePost', 'url' => '/@{userId}/calendar{path}', 'verb' => 'POST', 'requirements' => array('path' => '.+')], - ['name' => 'calendar#handlePut', 'url' => '/@{userId}/calendar{path}', 'verb' => 'PUT', 'requirements' => array('path' => '.+')], - ['name' => 'calendar#handleDelete', 'url' => '/@{userId}/calendar{path}', 'verb' => 'DELETE', 'requirements' => array('path' => '.+')], - ['name' => 'calendar#handlePatch', 'url' => '/@{userId}/calendar{path}', 'verb' => 'PATCH', 'requirements' => array('path' => '.+')], - ['name' => 'calendar#handleHead', 'url' => '/@{userId}/calendar{path}', 'verb' => 'HEAD', 'requirements' => array('path' => '.+')], - - ['name' => 'contacts#handleGet', 'url' => '/@{userId}/contacts{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')], - ['name' => 'contacts#handlePost', 'url' => '/@{userId}/contacts{path}', 'verb' => 'POST', 'requirements' => array('path' => '.+')], - ['name' => 'contacts#handlePut', 'url' => '/@{userId}/contacts{path}', 'verb' => 'PUT', 'requirements' => array('path' => '.+')], - ['name' => 'contacts#handleDelete', 'url' => '/@{userId}/contacts{path}', 'verb' => 'DELETE', 'requirements' => array('path' => '.+')], - ['name' => 'contacts#handlePatch', 'url' => '/@{userId}/contacts{path}', 'verb' => 'PATCH', 'requirements' => array('path' => '.+')], - ['name' => 'contacts#handleHead', 'url' => '/@{userId}/contacts{path}', 'verb' => 'HEAD', 'requirements' => array('path' => '.+')], - - ['name' => 'solidWebhook#listWebhooks', 'url' => '/webhook/list', 'verb' => 'GET'], - ['name' => 'solidWebhook#register', 'url' => '/webhook/register', 'verb' => 'POST'], - ['name' => 'solidWebhook#unregister', 'url' => '/webhook/unregister', 'verb' => 'POST'], - - ['name' => 'solidWebsocket#register', 'url' => '/websocket/register', 'verb' => 'POST'], - - ['name' => 'app#appLauncher', 'url' => '/', 'verb' => 'GET'], - ] + 'routes' => [ + ['name' => 'page#profile', 'url' => '/@{userId}/', 'verb' => 'GET'], + ['name' => 'page#approval', 'url' => '/sharing/{clientId}', 'verb' => 'GET'], + ['name' => 'page#handleRevoke', 'url' => '/revoke/{clientId}', 'verb' => 'GET'], + ['name' => 'page#handleApproval', 'url' => '/sharing/{clientId}', 'verb' => 'POST'], + ['name' => 'page#dataJson', 'url' => '/@{userId}/data.json', 'verb' => 'GET' ], + + ['name' => 'server#cors', 'url' => '/{path}', 'verb' => 'OPTIONS', 'requirements' => array('path' => '.+') ], + ['name' => 'server#authorize', 'url' => '/authorize', 'verb' => 'GET'], + ['name' => 'server#jwks', 'url' => '/jwks', 'verb' => 'GET'], + ['name' => 'server#session', 'url' => '/session', 'verb' => 'GET'], + ['name' => 'server#logout', 'url' => '/logout', 'verb' => 'GET'], + ['name' => 'server#token', 'url' => '/token', 'verb' => 'POST'], + ['name' => 'server#userinfo', 'url' => '/userinfo', 'verb' => 'GET'], + ['name' => 'server#register', 'url' => '/register', 'verb' => 'POST'], + ['name' => 'server#registeredClient', 'url' => '/register/{clientId}', 'verb' => 'GET'], + + ['name' => 'profile#handleGet', 'url' => '/@{userId}/profile{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')], + ['name' => 'profile#handlePut', 'url' => '/@{userId}/profile{path}', 'verb' => 'PUT', 'requirements' => array('path' => '.+')], + ['name' => 'profile#handlePatch', 'url' => '/@{userId}/profile{path}', 'verb' => 'PATCH', 'requirements' => array('path' => '.+')], + ['name' => 'profile#handleHead', 'url' => '/@{userId}/profile{path}', 'verb' => 'HEAD', 'requirements' => array('path' => '.+')], + + ['name' => 'storage#handleGet', 'url' => '/@{userId}/storage{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')], + ['name' => 'storage#handlePost', 'url' => '/@{userId}/storage{path}', 'verb' => 'POST', 'requirements' => array('path' => '.+')], + ['name' => 'storage#handlePut', 'url' => '/@{userId}/storage{path}', 'verb' => 'PUT', 'requirements' => array('path' => '.+')], + ['name' => 'storage#handleDelete', 'url' => '/@{userId}/storage{path}', 'verb' => 'DELETE', 'requirements' => array('path' => '.+')], + ['name' => 'storage#handlePatch', 'url' => '/@{userId}/storage{path}', 'verb' => 'PATCH', 'requirements' => array('path' => '.+')], + ['name' => 'storage#handleHead', 'url' => '/@{userId}/storage{path}', 'verb' => 'HEAD', 'requirements' => array('path' => '.+')], + + ['name' => 'calendar#handleGet', 'url' => '/@{userId}/calendar{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')], + ['name' => 'calendar#handlePost', 'url' => '/@{userId}/calendar{path}', 'verb' => 'POST', 'requirements' => array('path' => '.+')], + ['name' => 'calendar#handlePut', 'url' => '/@{userId}/calendar{path}', 'verb' => 'PUT', 'requirements' => array('path' => '.+')], + ['name' => 'calendar#handleDelete', 'url' => '/@{userId}/calendar{path}', 'verb' => 'DELETE', 'requirements' => array('path' => '.+')], + ['name' => 'calendar#handlePatch', 'url' => '/@{userId}/calendar{path}', 'verb' => 'PATCH', 'requirements' => array('path' => '.+')], + ['name' => 'calendar#handleHead', 'url' => '/@{userId}/calendar{path}', 'verb' => 'HEAD', 'requirements' => array('path' => '.+')], + + ['name' => 'contacts#handleGet', 'url' => '/@{userId}/contacts{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')], + ['name' => 'contacts#handlePost', 'url' => '/@{userId}/contacts{path}', 'verb' => 'POST', 'requirements' => array('path' => '.+')], + ['name' => 'contacts#handlePut', 'url' => '/@{userId}/contacts{path}', 'verb' => 'PUT', 'requirements' => array('path' => '.+')], + ['name' => 'contacts#handleDelete', 'url' => '/@{userId}/contacts{path}', 'verb' => 'DELETE', 'requirements' => array('path' => '.+')], + ['name' => 'contacts#handlePatch', 'url' => '/@{userId}/contacts{path}', 'verb' => 'PATCH', 'requirements' => array('path' => '.+')], + ['name' => 'contacts#handleHead', 'url' => '/@{userId}/contacts{path}', 'verb' => 'HEAD', 'requirements' => array('path' => '.+')], + + ['name' => 'solidWebhook#listWebhooks', 'url' => '/webhook/list', 'verb' => 'GET'], + ['name' => 'solidWebhook#register', 'url' => '/webhook/register', 'verb' => 'POST'], + ['name' => 'solidWebhook#unregister', 'url' => '/webhook/unregister', 'verb' => 'POST'], + + ['name' => 'solidWebsocket#register', 'url' => '/websocket/register', 'verb' => 'POST'], + + ['name' => 'app#appLauncher', 'url' => '/', 'verb' => 'GET'], + ] ]; diff --git a/solid/lib/AppInfo/Application.php b/solid/lib/AppInfo/Application.php index 134cd7e..d6efb76 100644 --- a/solid/lib/AppInfo/Application.php +++ b/solid/lib/AppInfo/Application.php @@ -4,83 +4,69 @@ namespace OCA\Solid\AppInfo; -use OC\AppFramework\Utility\TimeFactory; -use OC\Authentication\Events\AppPasswordCreatedEvent; -use OC\Authentication\Token\IProvider; -use OC\Server; - -use OCA\Solid\Service\UserService; -use OCA\Solid\WellKnown\OpenIdConfigurationHandler; -use OCA\Solid\WellKnown\SolidHandler; use OCA\Solid\Middleware\SolidCorsMiddleware; - use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; -use OCP\AppFramework\IAppContainer; -use OCP\Defaults; -use OCP\IServerContainer; -use OCP\Settings\IManager; -use OCP\Util; class Application extends App implements IBootstrap { - public const APP_ID = 'solid'; - - /** - * @param array $urlParams - */ - public function __construct(array $urlParams = []) { - parent::__construct(self::APP_ID, $urlParams); - - $container = $this->getContainer(); - - $container->registerService(SolidCorsMiddleware::class, function($c): SolidCorsMiddleware { - return new SolidCorsMiddleware( - $c->get(IRequest::class) - ); - }); - - // executed in the order that it is registered - $container->registerMiddleware(SolidCorsMiddleware::class); - - $container->registerService(SolidWebhookService::class, function($c): SolidWebhookService { - return new SolidWebhookService( - $c->query(SolidWebhookMapper::class) - ); - }); - - $container->registerService(SolidWebhookMapper::class, function($c): SolidWebhookMapper { - return new SolidWebhookMapper( - $c->get(IDBConnection::class) - ); - }); - } - - public function register(IRegistrationContext $context): void { - $context->registerWellKnownHandler(\OCA\Solid\WellKnown\OpenIdConfigurationHandler::class); - $context->registerWellKnownHandler(\OCA\Solid\WellKnown\SolidHandler::class); - - /** - * Core class wrappers - */ - - $context->registerService('UserService', function($c) { - return new \OCA\Solid\Service\UserService( - $c->query('UserSession') - ); - }); - $context->registerService('UserSession', function($c) { - return $c->query('ServerContainer')->getUserSession(); - }); - - // currently logged in user, userId can be gotten by calling the - // getUID() method on it - $context->registerService('User', function($c) { - return $c->query('UserSession')->getUser(); - }); - } - - public function boot(IBootContext $context): void { - } + public const APP_ID = 'solid'; + + /** + * @param array $urlParams + */ + public function __construct(array $urlParams = []) { + parent::__construct(self::APP_ID, $urlParams); + + $container = $this->getContainer(); + + $container->registerService(SolidCorsMiddleware::class, function ($c): SolidCorsMiddleware { + return new SolidCorsMiddleware( + $c->get(IRequest::class) + ); + }); + + // executed in the order that it is registered + $container->registerMiddleware(SolidCorsMiddleware::class); + + $container->registerService(SolidWebhookService::class, function ($c): SolidWebhookService { + return new SolidWebhookService( + $c->query(SolidWebhookMapper::class) + ); + }); + + $container->registerService(SolidWebhookMapper::class, function ($c): SolidWebhookMapper { + return new SolidWebhookMapper( + $c->get(IDBConnection::class) + ); + }); + } + + public function register(IRegistrationContext $context): void { + $context->registerWellKnownHandler(\OCA\Solid\WellKnown\OpenIdConfigurationHandler::class); + $context->registerWellKnownHandler(\OCA\Solid\WellKnown\SolidHandler::class); + + /** + * Core class wrappers + */ + + $context->registerService('UserService', function ($c) { + return new \OCA\Solid\Service\UserService( + $c->query('UserSession') + ); + }); + $context->registerService('UserSession', function ($c) { + return $c->query('ServerContainer')->getUserSession(); + }); + + // currently logged in user, userId can be gotten by calling the + // getUID() method on it + $context->registerService('User', function ($c) { + return $c->query('UserSession')->getUser(); + }); + } + + public function boot(IBootContext $context): void { + } } diff --git a/solid/lib/BearerFactoryTrait.php b/solid/lib/BearerFactoryTrait.php index b21e499..b2a3b5f 100644 --- a/solid/lib/BearerFactoryTrait.php +++ b/solid/lib/BearerFactoryTrait.php @@ -7,36 +7,33 @@ use Pdsinterop\Solid\Auth\Utils\Bearer; use Pdsinterop\Solid\Auth\Utils\JtiValidator; -trait BearerFactoryTrait -{ - ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ +trait BearerFactoryTrait { + ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ - private IDBConnection $connection; - private DateInterval $validFor; + private IDBConnection $connection; + private DateInterval $validFor; - //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ - final public function getBearer(): Bearer - { - $interval = $this->getBearerValidFor(); + final public function getBearer(): Bearer { + $interval = $this->getBearerValidFor(); - $replayDetector = new JtiReplayDetector($interval, $this->connection); + $replayDetector = new JtiReplayDetector($interval, $this->connection); - $jtiValidator = new JtiValidator($replayDetector); + $jtiValidator = new JtiValidator($replayDetector); - return new Bearer($jtiValidator); - } + return new Bearer($jtiValidator); + } - final public function getBearerValidFor(): DateInterval - { - static $validFor; + final public function getBearerValidFor(): DateInterval { + static $validFor; - if ($validFor === null) { - $validFor = new DateInterval('PT10M'); - } + if ($validFor === null) { + $validFor = new DateInterval('PT10M'); + } - return $validFor; - } + return $validFor; + } - ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ } diff --git a/solid/lib/Controller/AppController.php b/solid/lib/Controller/AppController.php index 94addc2..7a1deac 100644 --- a/solid/lib/Controller/AppController.php +++ b/solid/lib/Controller/AppController.php @@ -1,18 +1,17 @@ userId = $userId; $this->userManager = $userManager; $this->contactsManager = $contactsManager; - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; $this->config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); } - private function getUserApps($userId) { + private function getUserApps($userId) { $userApps = []; if ($this->userManager->userExists($userId)) { $allowedClients = $this->config->getAllowedClients($userId); @@ -46,7 +45,7 @@ private function getAppsList() { $path = __DIR__ . "/../solid-app-list.json"; $appsListJson = file_get_contents($path); $appsList = json_decode($appsListJson, true); - + $userApps = $this->getUserApps($this->userId); foreach ($appsList as $key => $app) { @@ -76,13 +75,13 @@ private function getStorageUrl($userId) { public function appLauncher() { $appsList = $this->getAppsList(); if (!$appsList) { - return new JSONResponse(array(), Http::STATUS_NOT_FOUND); + return new JSONResponse(array(), Http::STATUS_NOT_FOUND); } $appLauncherData = array( "appsListJson" => json_encode($appsList), "webId" => json_encode($this->getProfilePage()), "storageUrl" => json_encode($this->getStorageUrl($this->userId)), - 'solidNavigation' => array( + 'solidNavigation' => array( "profile" => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.page.profile", array("userId" => $this->userId))), "launcher" => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.app.appLauncher", array())), ) diff --git a/solid/lib/Controller/CalendarController.php b/solid/lib/Controller/CalendarController.php index c06bab0..3bd7f10 100644 --- a/solid/lib/Controller/CalendarController.php +++ b/solid/lib/Controller/CalendarController.php @@ -1,10 +1,10 @@ config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; $this->session = $session; @@ -50,7 +49,7 @@ public function __construct( private function getFileSystem($userId) { // Make sure the root folder has an acl file, as is required by the spec; - // Generate a default file granting the owner full access. + // Generate a default file granting the owner full access. $defaultAcl = $this->generateDefaultAcl($userId); // Create the Nextcloud Calendar Adapter @@ -74,7 +73,7 @@ private function getFileSystem($userId) { $filesystem = new \League\Flysystem\Filesystem($rdfAdapter); $filesystem->addPlugin(new \Pdsinterop\Rdf\Flysystem\Plugin\AsMime($formats)); - + $plugin = new \Pdsinterop\Rdf\Flysystem\Plugin\ReadRdf($graph); $filesystem->addPlugin($plugin); @@ -122,18 +121,18 @@ private function getCalendarUrl($userId) { * @NoCSRFRequired */ public function handleRequest($userId, $path) { - $this->calendarUserId = $userId; - + $this->calendarUserId = $userId; + $this->rawRequest = \Laminas\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); $this->response = new \Laminas\Diactoros\Response(); $this->filesystem = $this->getFileSystem($userId); - $this->resourceServer = new ResourceServer($this->filesystem, $this->response); + $this->resourceServer = new ResourceServer($this->filesystem, $this->response); $this->WAC = new WAC($this->filesystem); $request = $this->rawRequest; - $baseUrl = $this->getCalendarUrl($userId); + $baseUrl = $this->getCalendarUrl($userId); $this->resourceServer->setBaseUrl($baseUrl); $this->WAC->setBaseUrl($baseUrl); $notifications = new SolidNotifications(); @@ -143,31 +142,31 @@ public function handleRequest($userId, $path) { try { $webId = $dpop->getWebId($request); - } catch(\Pdsinterop\Solid\Auth\Exception\Exception $e) { + } catch (\Pdsinterop\Solid\Auth\Exception\Exception $e) { $response = $this->resourceServer->getResponse() ->withStatus(Http::STATUS_CONFLICT, "Invalid token " . $e->getMessage()); return $this->respond($response); } - + if (!$this->WAC->isAllowed($request, $webId)) { $response = $this->resourceServer->getResponse()->withStatus(403, "Access denied"); return $this->respond($response); } - $response = $this->resourceServer->respondToRequest($request); + $response = $this->resourceServer->respondToRequest($request); $response = $this->WAC->addWACHeaders($request, $response, $webId); return $this->respond($response); } - + /** * @PublicPage * @NoAdminRequired * @NoCSRFRequired */ - public function handleGet($userId, $path) { + public function handleGet($userId, $path) { return $this->handleRequest($userId, $path); } - + /** * @PublicPage * @NoAdminRequired @@ -181,10 +180,11 @@ public function handlePost($userId, $path) { * @NoAdminRequired * @NoCSRFRequired */ - public function handlePut() { // $userId, $path) { + public function handlePut() { + // $userId, $path) { // FIXME: Adding the correct variables in the function name will make nextcloud // throw an error about accessing put twice, so we will find out the userId and path from $_SERVER instead; - + // because we got here, the request uri should look like: // /index.php/apps/solid/@{userId}/storage{path} $pathInfo = explode("@", $_SERVER['REQUEST_URI']); @@ -192,7 +192,7 @@ public function handlePut() { // $userId, $path) { $userId = $pathInfo[0]; $path = $pathInfo[1]; $path = preg_replace("/^calendar/", "", $path); - + return $this->handleRequest($userId, $path); } /** @@ -239,7 +239,7 @@ private function respond($response) { $result->addHeader($header, $value); } } - + $result->setStatus($statusCode); return $result; } diff --git a/solid/lib/Controller/ContactsController.php b/solid/lib/Controller/ContactsController.php index ff07c42..1e6367b 100644 --- a/solid/lib/Controller/ContactsController.php +++ b/solid/lib/Controller/ContactsController.php @@ -1,10 +1,10 @@ config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; $this->session = $session; @@ -51,7 +49,7 @@ public function __construct( private function getFileSystem($userId) { // Make sure the root folder has an acl file, as is required by the spec; - // Generate a default file granting the owner full access. + // Generate a default file granting the owner full access. $defaultAcl = $this->generateDefaultAcl($userId); // Create the Nextcloud Contacts Adapter @@ -75,7 +73,7 @@ private function getFileSystem($userId) { $filesystem = new \League\Flysystem\Filesystem($rdfAdapter); $filesystem->addPlugin(new \Pdsinterop\Rdf\Flysystem\Plugin\AsMime($formats)); - + $plugin = new \Pdsinterop\Rdf\Flysystem\Plugin\ReadRdf($graph); $filesystem->addPlugin($plugin); @@ -123,18 +121,18 @@ private function getContactsUrl($userId) { * @NoCSRFRequired */ public function handleRequest($userId, $path) { - $this->contactsUserId = $userId; - + $this->contactsUserId = $userId; + $this->rawRequest = \Laminas\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); $this->response = new \Laminas\Diactoros\Response(); $this->filesystem = $this->getFileSystem($userId); - $this->resourceServer = new ResourceServer($this->filesystem, $this->response); + $this->resourceServer = new ResourceServer($this->filesystem, $this->response); $this->WAC = new WAC($this->filesystem); $request = $this->rawRequest; - $baseUrl = $this->getContactsUrl($userId); + $baseUrl = $this->getContactsUrl($userId); $this->resourceServer->setBaseUrl($baseUrl); $this->WAC->setBaseUrl($baseUrl); $notifications = new SolidNotifications(); @@ -144,31 +142,31 @@ public function handleRequest($userId, $path) { try { $webId = $dpop->getWebId($request); - } catch(\Pdsinterop\Solid\Auth\Exception\Exception $e) { + } catch (\Pdsinterop\Solid\Auth\Exception\Exception $e) { $response = $this->resourceServer->getResponse() ->withStatus(Http::STATUS_CONFLICT, "Invalid token " . $e->getMessage()); return $this->respond($response); } - + if (!$this->WAC->isAllowed($request, $webId)) { $response = $this->resourceServer->getResponse()->withStatus(403, "Access denied"); return $this->respond($response); } - $response = $this->resourceServer->respondToRequest($request); + $response = $this->resourceServer->respondToRequest($request); $response = $this->WAC->addWACHeaders($request, $response, $webId); return $this->respond($response); } - + /** * @PublicPage * @NoAdminRequired * @NoCSRFRequired */ - public function handleGet($userId, $path) { + public function handleGet($userId, $path) { return $this->handleRequest($userId, $path); } - + /** * @PublicPage * @NoAdminRequired @@ -182,10 +180,11 @@ public function handlePost($userId, $path) { * @NoAdminRequired * @NoCSRFRequired */ - public function handlePut() { // $userId, $path) { + public function handlePut() { + // $userId, $path) { // FIXME: Adding the correct variables in the function name will make nextcloud // throw an error about accessing put twice, so we will find out the userId and path from $_SERVER instead; - + // because we got here, the request uri should look like: // /index.php/apps/solid/@{userId}/storage{path} $pathInfo = explode("@", $_SERVER['REQUEST_URI']); @@ -193,7 +192,7 @@ public function handlePut() { // $userId, $path) { $userId = $pathInfo[0]; $path = $pathInfo[1]; $path = preg_replace("/^contacts/", "", $path); - + return $this->handleRequest($userId, $path); } /** diff --git a/solid/lib/Controller/PageController.php b/solid/lib/Controller/PageController.php index c8bbd8f..24c99f2 100644 --- a/solid/lib/Controller/PageController.php +++ b/solid/lib/Controller/PageController.php @@ -1,17 +1,16 @@ userId = $userId; $this->userManager = $userManager; - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; $this->config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); } @@ -50,8 +49,8 @@ private function getUserProfile($userId) { $profile = array( 'id' => $userId, 'displayName' => $user->getDisplayName(), - 'profileUri' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.profile.handleGet", array("userId" => $userId, "path" => "/card"))) . "#me", - 'solidNavigation' => array( + 'profileUri' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.profile.handleGet", array("userId" => $userId, "path" => "/card"))) . "#me", + 'solidNavigation' => array( "profile" => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.page.profile", array("userId" => $userId))), "launcher" => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.app.appLauncher", array())), ) @@ -72,7 +71,7 @@ public function profile($userId) { // header("Access-Control-Allow-Credentials: true"); $profile = $this->getUserProfile($userId); if (!$profile) { - return new JSONResponse(array(), Http::STATUS_NOT_FOUND); + return new JSONResponse(array(), Http::STATUS_NOT_FOUND); } $templateResponse = new TemplateResponse('solid', 'profile', $profile); $policy = new ContentSecurityPolicy(); diff --git a/solid/lib/Controller/ProfileController.php b/solid/lib/Controller/ProfileController.php index 67cfc7d..9dde7cd 100644 --- a/solid/lib/Controller/ProfileController.php +++ b/solid/lib/Controller/ProfileController.php @@ -1,10 +1,10 @@ config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; $this->userManager = $userManager; $this->contactsManager = $contactsManager; @@ -54,7 +53,7 @@ public function __construct( private function getFileSystem($userId) { // Make sure the root folder has an acl file, as is required by the spec; - // Generate a default file granting the owner full access. + // Generate a default file granting the owner full access. $defaultAcl = $this->generateDefaultAcl($userId); $profile = $this->generateTurtleProfile($userId); @@ -78,7 +77,7 @@ private function getFileSystem($userId) { $filesystem = new \League\Flysystem\Filesystem($rdfAdapter); $filesystem->addPlugin(new \Pdsinterop\Rdf\Flysystem\Plugin\AsMime($formats)); - + $plugin = new \Pdsinterop\Rdf\Flysystem\Plugin\ReadRdf($graph); $filesystem->addPlugin($plugin); @@ -139,18 +138,18 @@ private function getStorageUrl($userId) { * @NoCSRFRequired */ public function handleRequest($userId, $path) { - $this->userId = $userId; - + $this->userId = $userId; + $this->rawRequest = \Laminas\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); $this->response = new \Laminas\Diactoros\Response(); $this->filesystem = $this->getFileSystem($userId); - $this->resourceServer = new ResourceServer($this->filesystem, $this->response); + $this->resourceServer = new ResourceServer($this->filesystem, $this->response); $this->WAC = new WAC($this->filesystem); $request = $this->rawRequest; - $baseUrl = $this->getProfileUrl($userId); + $baseUrl = $this->getProfileUrl($userId); $this->resourceServer->setBaseUrl($baseUrl); $this->WAC->setBaseUrl($baseUrl); $notifications = new SolidNotifications(); @@ -161,7 +160,7 @@ public function handleRequest($userId, $path) { if ($request->getHeaderLine("DPop")) { try { $webId = $dpop->getWebId($request); - } catch(\Pdsinterop\Solid\Auth\Exception\Exception $e) { + } catch (\Pdsinterop\Solid\Auth\Exception\Exception $e) { $response = $this->resourceServer->getResponse() ->withStatus(Http::STATUS_CONFLICT, "Invalid token " . $e->getMessage()); return $this->respond($response); @@ -175,20 +174,20 @@ public function handleRequest($userId, $path) { return $this->respond($response); } - $response = $this->resourceServer->respondToRequest($request); + $response = $this->resourceServer->respondToRequest($request); $response = $this->WAC->addWACHeaders($request, $response, $webId); return $this->respond($response); } - + /** * @PublicPage * @NoAdminRequired * @NoCSRFRequired */ - public function handleGet($userId, $path) { + public function handleGet($userId, $path) { return $this->handleRequest($userId, $path); } - + /** * @PublicPage * @NoAdminRequired @@ -202,10 +201,11 @@ public function handlePost($userId, $path) { * @NoAdminRequired * @NoCSRFRequired */ - public function handlePut() { // $userId, $path) { + public function handlePut() { + // $userId, $path) { // FIXME: Adding the correct variables in the function name will make nextcloud // throw an error about accessing put twice, so we will find out the userId and path from $_SERVER instead; - + // because we got here, the request uri should look like: // /index.php/apps/solid/@{userId}/storage{path} $pathInfo = explode("@", $_SERVER['REQUEST_URI']); @@ -213,7 +213,7 @@ public function handlePut() { // $userId, $path) { $userId = $pathInfo[0]; $path = $pathInfo[1]; $path = preg_replace("/^profile/", "", $path); - + return $this->handleRequest($userId, $path); } /** @@ -260,10 +260,10 @@ private function respond($response) { $result->addHeader($header, $value); } } -// $origin = $_SERVER['HTTP_ORIGIN'] ?? "*"; -// $result->addHeader('Access-Control-Allow-Credentials', 'true'); -// $result->addHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); -// $result->addHeader('Access-Control-Allow-Origin', $origin); + // $origin = $_SERVER['HTTP_ORIGIN'] ?? "*"; + // $result->addHeader('Access-Control-Allow-Credentials', 'true'); + // $result->addHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); + // $result->addHeader('Access-Control-Allow-Origin', $origin); $result->setStatus($statusCode); return $result; } @@ -273,11 +273,11 @@ private function getUserProfile($userId) { $user = $this->userManager->get($userId); $addressBooks = $this->contactsManager->getUserAddressBooks(); $friends = []; - foreach($addressBooks as $k => $v) { + foreach ($addressBooks as $k => $v) { $results = $addressBooks[$k]->search('', ['FN'], ['types' => true]); - foreach($results as $found) { + foreach ($results as $found) { if (isset($found['URL']) && is_array($found['URL'])) { - foreach($found['URL'] as $i => $obj) { + foreach ($found['URL'] as $i => $obj) { array_push($friends, $obj['value']); } } @@ -287,7 +287,7 @@ private function getUserProfile($userId) { $profile = array( 'id' => $userId, 'displayName' => $user->getDisplayName(), - 'profileUri' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.profile.handleGet", array("userId" => $userId, "path" => "/card"))) . "#me", + 'profileUri' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.profile.handleGet", array("userId" => $userId, "path" => "/card"))) . "#me", 'friends' => $friends, 'inbox' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.storage.handleGet", array("userId" => $userId, "path" => "/inbox/"))), 'preferences' => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.storage.handleGet", array("userId" => $userId, "path" => "/settings/preferences.ttl"))), @@ -308,38 +308,38 @@ private function generateTurtleProfile($userId) { return ""; } ob_start(); - ?>@prefix : <#>. - @prefix solid: . - @prefix pro: <./>. - @prefix foaf: . - @prefix schem: . - @prefix acl: . - @prefix ldp: . - @prefix inbox: <>. - @prefix sp: . - @prefix ser: <>. - - pro:card a foaf:PersonalProfileDocument; foaf:maker :me; foaf:primaryTopic :me. - - :me - a schem:Person, foaf:Person; - ldp:inbox inbox:; - sp:preferencesFile <>; - sp:storage ser:; - solid:account ser:; - solid:privateTypeIndex <>; - solid:publicTypeIndex <>; - solid:oidcIssuer <>; - $friend) { - ?> - foaf:knows <>; - - foaf:name ""; - "". - @prefix : <#>. + @prefix solid: . + @prefix pro: <./>. + @prefix foaf: . + @prefix schem: . + @prefix acl: . + @prefix ldp: . + @prefix inbox: <>. + @prefix sp: . + @prefix ser: <>. + + pro:card a foaf:PersonalProfileDocument; foaf:maker :me; foaf:primaryTopic :me. + + :me + a schem:Person, foaf:Person; + ldp:inbox inbox:; + sp:preferencesFile <>; + sp:storage ser:; + solid:account ser:; + solid:privateTypeIndex <>; + solid:publicTypeIndex <>; + solid:oidcIssuer <>; + $friend) { + ?> + foaf:knows <>; + + foaf:name ""; + "". + config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); $this->userId = $userId; $this->userManager = $userManager; - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; $this->session = $session; @@ -93,13 +90,13 @@ private function getOpenIdEndpoints() { private function getKeys() { $encryptionKey = $this->config->getEncryptionKey(); - $privateKey = $this->config->getPrivateKey(); - $key = openssl_pkey_get_private($privateKey); - $publicKey = openssl_pkey_get_details($key)['key']; + $privateKey = $this->config->getPrivateKey(); + $key = openssl_pkey_get_private($privateKey); + $publicKey = openssl_pkey_get_details($key)['key']; return [ "encryptionKey" => $encryptionKey, - "privateKey" => $privateKey, - "publicKey" => $publicKey + "privateKey" => $privateKey, + "publicKey" => $publicKey ]; } @@ -115,7 +112,7 @@ private function createAuthServerConfig() { $keys['publicKey'], $this->getOpenIdEndpoints() ))->create(); - } catch(\Throwable $e) { + } catch (\Throwable $e) { // var_dump($e); return null; } @@ -129,10 +126,10 @@ private function createAuthServerConfig() { public function cors($path) { $origin = $_SERVER['HTTP_ORIGIN']; return (new DataResponse('OK')); -// ->addHeader('Access-Control-Allow-Origin', $origin) -// ->addHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') -// ->addHeader('Access-Control-Allow-Methods', 'POST') -// ->addHeader('Access-Control-Allow-Credentials', 'true'); + // ->addHeader('Access-Control-Allow-Origin', $origin) + // ->addHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') + // ->addHeader('Access-Control-Allow-Methods', 'POST') + // ->addHeader('Access-Control-Allow-Credentials', 'true'); } /** @@ -145,7 +142,7 @@ public function authorize() { $result = new JSONResponse('Authorization required'); $result->setStatus(401); return $result; -// return $result->addHeader('Access-Control-Allow-Origin', '*'); + // return $result->addHeader('Access-Control-Allow-Origin', '*'); } if (isset($_GET['request'])) { @@ -153,7 +150,7 @@ public function authorize() { try { $token = $jwtConfig->parser()->parse($_GET['request']); $this->session->set("nonce", $token->claims()->get('nonce')); - } catch(\Exception $e) { + } catch (\Exception $e) { $this->session->set("nonce", $_GET['nonce']); } } @@ -170,22 +167,22 @@ public function authorize() { $result = new JSONResponse('Bad request, does not contain valid token'); $result->setStatus(400); return $result; -// return $result->addHeader('Access-Control-Allow-Origin', '*'); + // return $result->addHeader('Access-Control-Allow-Origin', '*'); } try { $getVars['redirect_uri'] = $token->claims()->get("redirect_uri"); - } catch(\Exception $e) { + } catch (\Exception $e) { $result = new JSONResponse('Bad request, missing redirect uri'); $result->setStatus(400); return $result; -// return $result->addHeader('Access-Control-Allow-Origin', '*'); + // return $result->addHeader('Access-Control-Allow-Origin', '*'); } } if (preg_match("/^http(s)?:/", $getVars['client_id'])) { $parsedOrigin = parse_url($getVars['redirect_uri']); $origin = 'https://' . $parsedOrigin['host']; - + $clientData = array( "client_id_issued_at" => time(), "client_name" => $getVars['client_id'], @@ -216,7 +213,7 @@ public function authorize() { $request = \Laminas\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $getVars, $_POST, $_COOKIE, $_FILES); $response = new \Laminas\Diactoros\Response(); - $server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response); + $server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response); $response = $server->respondToAuthorizationRequest($request, $user, $approval); $response = $this->tokenGenerator->addIdTokenToResponse( @@ -255,10 +252,10 @@ private function getResponseType() { switch ($responseType) { case "token": return "token"; - break; + break; case "code": return "code"; - break; + break; } } return "token"; // default to token response type; @@ -286,14 +283,14 @@ public function token() { $httpDpop = $request->getServerParams()['HTTP_DPOP']; $response = new \Laminas\Diactoros\Response(); - $server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response); + $server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response); $response = $server->respondToAccessTokenRequest($request); // FIXME: not sure if decoding this here is the way to go. // FIXME: because this is a public page, the nonce from the session is not available here. $codeInfo = $this->tokenGenerator->getCodeInfo($code); $response = $this->tokenGenerator->addIdTokenToResponse( - $response, + $response, $clientId, $codeInfo['user_id'], ($_SESSION['nonce'] ?? ''), @@ -347,8 +344,8 @@ public function register() { ); $registration = $this->tokenGenerator->respondToRegistration($registration, $this->config->getPrivateKey()); return (new JSONResponse($registration)); -// ->addHeader('Access-Control-Allow-Origin', $origin) -// ->addHeader('Access-Control-Allow-Methods', 'POST'); + // ->addHeader('Access-Control-Allow-Origin', $origin) + // ->addHeader('Access-Control-Allow-Methods', 'POST'); } /** @@ -369,7 +366,7 @@ public function registeredClient($clientId) { */ public function jwks() { $response = new \Laminas\Diactoros\Response(); - $server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response); + $server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response); $response = $server->respondToJwksMetadataRequest(); return $this->respond($response); } @@ -398,7 +395,7 @@ private function respond($response) { } } $result->setStatus($statusCode); -// $result->addHeader('Access-Control-Allow-Origin', '*'); + // $result->addHeader('Access-Control-Allow-Origin', '*'); return $result; } @@ -413,7 +410,7 @@ private function getClient($clientId) { $clientRegistration['client_name'] ); } else { - return new \Pdsinterop\Solid\Auth\Config\Client('','',array(),''); + return new \Pdsinterop\Solid\Auth\Config\Client('', '', array(), ''); } } } diff --git a/solid/lib/Controller/SolidWebhookController.php b/solid/lib/Controller/SolidWebhookController.php index 6a88a81..3b6c368 100644 --- a/solid/lib/Controller/SolidWebhookController.php +++ b/solid/lib/Controller/SolidWebhookController.php @@ -3,31 +3,19 @@ namespace OCA\Solid\Controller; use Closure; -use OCA\Solid\AppInfo\Application; -use OCA\Solid\Service\SolidWebhookService; -use OCA\Solid\ServerConfig; -use OCA\Solid\PlainResponse; -use OCA\Solid\Notifications\SolidNotifications; use OCA\Solid\DpopFactoryTrait; - +use OCA\Solid\PlainResponse; +use OCA\Solid\Service\SolidWebhookService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; +use OCP\Files\IRootFolder; +use OCP\IConfig; +use OCP\IDBConnection; use OCP\IRequest; -use OCP\IUserManager; -use OCP\IURLGenerator; use OCP\ISession; -use OCP\IDBConnection; -use OCP\IConfig; -use OCP\Files\IRootFolder; -use OCP\Files\IHomeStorage; -use OCP\Files\SimpleFS\ISimpleRoot; -use OCP\AppFramework\Http; -use OCP\AppFramework\Http\Response; -use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\Http\ContentSecurityPolicy; - -use Pdsinterop\Solid\Resources\Server as ResourceServer; -use Pdsinterop\Solid\Auth\Utils\DPop as DPop; +use OCP\IURLGenerator; +use OCP\IUserManager; use Pdsinterop\Solid\Auth\WAC as WAC; class SolidWebhookController extends Controller { @@ -38,7 +26,7 @@ class SolidWebhookController extends Controller { /* @var ISession */ private $session; - + /** @var SolidWebhookService */ private $webhookService; @@ -55,10 +43,10 @@ public function __construct( IDBConnection $connection, ) { parent::__construct($AppName, $request); - require_once(__DIR__.'/../../vendor/autoload.php'); + require_once(__DIR__ . '/../../vendor/autoload.php'); $this->config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); $this->rootFolder = $rootFolder; - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; $this->session = $session; $this->webhookService = $webhookService; @@ -69,7 +57,7 @@ public function __construct( $this->rawRequest = \Laminas\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); $this->webId = $this->DPop->getWebId($this->rawRequest); // FIXME: Should we handle webhooks for 'public'? - } catch(\Exception $e) { + } catch (\Exception $e) { return new PlainResponse("Invalid token", 409); } } @@ -139,7 +127,7 @@ private function getFileSystem() { $filesystem = new \League\Flysystem\Filesystem($rdfAdapter); $filesystem->addPlugin(new \Pdsinterop\Rdf\Flysystem\Plugin\AsMime($formats)); - + $plugin = new \Pdsinterop\Rdf\Flysystem\Plugin\ReadRdf($graph); $filesystem->addPlugin($plugin); @@ -168,14 +156,14 @@ private function parseTopic($topic) { $pathicles = explode("/", $internalUrl); $userId = $pathicles[0]; // @alice $userId = preg_replace("/^@/", "", $userId); // alice - $storageUrl = $this->getStorageUrl($userId); // https://nextcloud.server/solid/@alice/storage/ + $storageUrl = $this->getStorageUrl($userId); // https://nextcloud.server/solid/@alice/storage/ $storagePath = str_replace($storageUrl, '/', $topic); // /foo/bar return array( "userId" => $userId, "path" => $storagePath ); } - + private function createGetRequest($topic) { $serverParams = []; $fileParams = []; @@ -192,7 +180,7 @@ private function createGetRequest($topic) { $headers ); } - + private function checkReadAccess($topic) { // split out $topic into $userId and $path https://nextcloud.server/solid/@alice/storage/foo/bar // - userId in this case is the pod owner (not the one doing the request). (alice) @@ -200,7 +188,7 @@ private function checkReadAccess($topic) { $target = $this->parseTopic($topic); $userId = $target["userId"]; $path = $target["path"]; - + $this->initializeStorage($userId); $this->WAC = new WAC($this->filesystem); diff --git a/solid/lib/Controller/SolidWebsocketController.php b/solid/lib/Controller/SolidWebsocketController.php index 713e178..8adf1e9 100644 --- a/solid/lib/Controller/SolidWebsocketController.php +++ b/solid/lib/Controller/SolidWebsocketController.php @@ -5,22 +5,20 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; -class SolidWebsocketController extends Controller -{ - /** - * @PublicPage - * @NoAdminRequired - * @NoCSRFRequired - */ - public function register(): DataResponse - { - // @FIXME: If there is no PUBSUB_URL what should be returned? 404? 500? Yo'mamma? 2023/01/27/BMP - $pubsub = getenv("PUBSUB_URL") ?: "http://pubsub:8080"; +class SolidWebsocketController extends Controller { + /** + * @PublicPage + * @NoAdminRequired + * @NoCSRFRequired + */ + public function register(): DataResponse { + // @FIXME: If there is no PUBSUB_URL what should be returned? 404? 500? Yo'mamma? 2023/01/27/BMP + $pubsub = getenv("PUBSUB_URL") ?: "http://pubsub:8080"; - return new DataResponse([ - "@context" => "https://www.w3.org/ns/solid/notification/v1", - "type" => "WebSocketSubscription2021", - "source" => $pubsub . "/?type=WebSocketSubscription2021" - ]); - } + return new DataResponse([ + "@context" => "https://www.w3.org/ns/solid/notification/v1", + "type" => "WebSocketSubscription2021", + "source" => $pubsub . "/?type=WebSocketSubscription2021" + ]); + } } diff --git a/solid/lib/Controller/StorageController.php b/solid/lib/Controller/StorageController.php index b424896..9ef1d50 100644 --- a/solid/lib/Controller/StorageController.php +++ b/solid/lib/Controller/StorageController.php @@ -1,13 +1,14 @@ config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); $this->rootFolder = $rootFolder; - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; $this->session = $session; @@ -76,7 +73,7 @@ private function getFileSystem() { $filesystem = new \League\Flysystem\Filesystem($rdfAdapter); $filesystem->addPlugin(new \Pdsinterop\Rdf\Flysystem\Plugin\AsMime($formats)); - + $plugin = new \Pdsinterop\Rdf\Flysystem\Plugin\ReadRdf($graph); $filesystem->addPlugin($plugin); @@ -299,7 +296,7 @@ public function handleRequest($userId, $path) { $this->WAC = new WAC($this->filesystem); $request = $this->rawRequest; - $baseUrl = $this->getStorageUrl($userId); + $baseUrl = $this->getStorageUrl($userId); $this->resourceServer->setBaseUrl($baseUrl); $this->WAC->setBaseUrl($baseUrl); @@ -311,7 +308,7 @@ public function handleRequest($userId, $path) { $error = false; try { $webId = $dpop->getWebId($request); - } catch(\Pdsinterop\Solid\Auth\Exception\Exception $e) { + } catch (\Pdsinterop\Solid\Auth\Exception\Exception $e) { $error = $e; } @@ -319,7 +316,7 @@ public function handleRequest($userId, $path) { $bearer = $this->getBearer(); try { $webId = $bearer->getWebId($request); - } catch(\Pdsinterop\Solid\Auth\Exception\Exception $e) { + } catch (\Pdsinterop\Solid\Auth\Exception\Exception $e) { $error = $e; } } @@ -347,20 +344,20 @@ public function handleRequest($userId, $path) { ->withStatus(403, "Access denied"); return $this->respond($response); } - $response = $this->resourceServer->respondToRequest($request); + $response = $this->resourceServer->respondToRequest($request); $response = $this->WAC->addWACHeaders($request, $response, $webId); return $this->respond($response); } - + /** * @PublicPage * @NoAdminRequired * @NoCSRFRequired */ - public function handleGet($userId, $path) { + public function handleGet($userId, $path) { return $this->handleRequest($userId, $path); } - + /** * @PublicPage * @NoAdminRequired @@ -374,10 +371,11 @@ public function handlePost($userId, $path) { * @NoAdminRequired * @NoCSRFRequired */ - public function handlePut() { // $userId, $path) { + public function handlePut() { + // $userId, $path) { // FIXME: Adding the correct variables in the function name will make nextcloud // throw an error about accessing put twice, so we will find out the userId and path from $_SERVER instead; - + // because we got here, the request uri should look like: // /index.php/apps/solid/@{userId}/storage{path} $pathInfo = explode("@", $_SERVER['REQUEST_URI']); @@ -385,7 +383,7 @@ public function handlePut() { // $userId, $path) { $userId = $pathInfo[0]; $path = $pathInfo[1]; $path = preg_replace("/^storage/", "", $path); - + return $this->handleRequest($userId, $path); } /** @@ -426,24 +424,24 @@ private function respond($response) { $result->addHeader($header, implode(", ", $values)); } -// $origin = $_SERVER['HTTP_ORIGIN']; -// $result->addHeader('Access-Control-Allow-Credentials', 'true'); -// $result->addHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); -// $result->addHeader('Access-Control-Allow-Origin', $origin); - - $policy = new EmptyContentSecurityPolicy(); - $policy->addAllowedStyleDomain("*"); - $policy->addAllowedStyleDomain("data:"); - $policy->addAllowedScriptDomain("*"); - $policy->addAllowedImageDomain("*"); - $policy->addAllowedFontDomain("*"); - $policy->addAllowedConnectDomain("*"); - $policy->allowInlineStyle(true); - $policy->allowInlineScript(true); - $policy->allowEvalScript(true); - $result->setContentSecurityPolicy($policy); - - $result->setStatus($statusCode); + // $origin = $_SERVER['HTTP_ORIGIN']; + // $result->addHeader('Access-Control-Allow-Credentials', 'true'); + // $result->addHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); + // $result->addHeader('Access-Control-Allow-Origin', $origin); + + $policy = new EmptyContentSecurityPolicy(); + $policy->addAllowedStyleDomain("*"); + $policy->addAllowedStyleDomain("data:"); + $policy->addAllowedScriptDomain("*"); + $policy->addAllowedImageDomain("*"); + $policy->addAllowedFontDomain("*"); + $policy->addAllowedConnectDomain("*"); + $policy->allowInlineStyle(true); + $policy->allowInlineScript(true); + $policy->allowEvalScript(true); + $result->setContentSecurityPolicy($policy); + + $result->setStatus($statusCode); return $result; } } diff --git a/solid/lib/Db/SolidWebhook.php b/solid/lib/Db/SolidWebhook.php index 824fb1b..ba9bd22 100644 --- a/solid/lib/Db/SolidWebhook.php +++ b/solid/lib/Db/SolidWebhook.php @@ -3,7 +3,6 @@ namespace OCA\Solid\Db; use JsonSerializable; - use OCP\AppFramework\Db\Entity; class SolidWebhook extends Entity implements JsonSerializable { diff --git a/solid/lib/DpopFactoryTrait.php b/solid/lib/DpopFactoryTrait.php index 55f00de..66f3bc4 100644 --- a/solid/lib/DpopFactoryTrait.php +++ b/solid/lib/DpopFactoryTrait.php @@ -7,41 +7,37 @@ use Pdsinterop\Solid\Auth\Utils\DPop; use Pdsinterop\Solid\Auth\Utils\JtiValidator; -trait DpopFactoryTrait -{ - ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ +trait DpopFactoryTrait { + ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ - private IDBConnection $connection; - private DateInterval $validFor; + private IDBConnection $connection; + private DateInterval $validFor; - //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ - final public function getDpop(): DPop - { - $interval = $this->getDpopValidFor(); + final public function getDpop(): DPop { + $interval = $this->getDpopValidFor(); - $replayDetector = new JtiReplayDetector($interval, $this->connection); + $replayDetector = new JtiReplayDetector($interval, $this->connection); - $jtiValidator = new JtiValidator($replayDetector); + $jtiValidator = new JtiValidator($replayDetector); - return new DPop($jtiValidator); - } + return new DPop($jtiValidator); + } - final public function getDpopValidFor(): DateInterval - { - static $validFor; + final public function getDpopValidFor(): DateInterval { + static $validFor; - if ($validFor === null) { - $validFor = new DateInterval('PT10M'); - } + if ($validFor === null) { + $validFor = new DateInterval('PT10M'); + } - return $validFor; - } + return $validFor; + } - final public function setJtiStorage(IDBConnection $connection): void - { - $this->connection = $connection; - } + final public function setJtiStorage(IDBConnection $connection): void { + $this->connection = $connection; + } - ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ } diff --git a/solid/lib/JtiReplayDetector.php b/solid/lib/JtiReplayDetector.php index bb449f5..798b0e4 100644 --- a/solid/lib/JtiReplayDetector.php +++ b/solid/lib/JtiReplayDetector.php @@ -8,71 +8,66 @@ use OCP\IDBConnection; use Pdsinterop\Solid\Auth\ReplayDetectorInterface; -class JtiReplayDetector implements ReplayDetectorInterface -{ - ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ +class JtiReplayDetector implements ReplayDetectorInterface { + ////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\ - private string $table = 'solid_jti'; + private string $table = 'solid_jti'; - //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ - public function __construct(private DateInterval $interval, private IDBConnection $connection) - { - } + public function __construct(private DateInterval $interval, private IDBConnection $connection) { + } - public function detect(string $jti, string $targetUri): bool - { - $hash = sha1($targetUri); + public function detect(string $jti, string $targetUri): bool { + $hash = sha1($targetUri); - // @TODO: $this->rotateBuckets(); - $has = $this->has($jti, $hash); + // @TODO: $this->rotateBuckets(); + $has = $this->has($jti, $hash); - if ($has === false) { - $this->store($jti, $hash); - } + if ($has === false) { + $this->store($jti, $hash); + } - return $has; - } + return $has; + } - ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + ////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\ - private function has(string $jti, string $uri): bool - { - $queryBuilder = $this->connection->getQueryBuilder(); + private function has(string $jti, string $uri): bool { + $queryBuilder = $this->connection->getQueryBuilder(); - $notOlderThan = (new DateTime())->sub($this->interval); + $notOlderThan = (new DateTime())->sub($this->interval); - $cursor = $queryBuilder->select('*') - ->from($this->table) - ->where( - $queryBuilder->expr()->eq('jti', $queryBuilder->createNamedParameter($jti,IQueryBuilder::PARAM_STR)) - ) - ->andWhere( - $queryBuilder->expr()->eq('uri', $queryBuilder->createNamedParameter($uri, IQueryBuilder::PARAM_STR)) - ) - ->andWhere( - $queryBuilder->expr()->gt('request_time', $queryBuilder->createParameter('notOlderThan')) - )->setParameter('notOlderThan', $notOlderThan, 'datetime') - ->execute() - ; + $cursor = $queryBuilder->select('*') + ->from($this->table) + ->where( + $queryBuilder->expr()->eq('jti', $queryBuilder->createNamedParameter($jti, IQueryBuilder::PARAM_STR)) + ) + ->andWhere( + $queryBuilder->expr()->eq('uri', $queryBuilder->createNamedParameter($uri, IQueryBuilder::PARAM_STR)) + ) + ->andWhere( + $queryBuilder->expr()->gt('request_time', $queryBuilder->createParameter('notOlderThan')) + )->setParameter('notOlderThan', $notOlderThan, 'datetime') + ->execute() + ; - $row = $cursor->fetch(); + $row = $cursor->fetch(); - $cursor->closeCursor(); + $cursor->closeCursor(); - return ! empty($row); - } + return ! empty($row); + } - private function store(string $jti, string $uri): void - { - $queryBuilder = $this->connection->getQueryBuilder(); + private function store(string $jti, string $uri): void { + $queryBuilder = $this->connection->getQueryBuilder(); - $queryBuilder->insert($this->table) - ->values([ - 'jti' => $queryBuilder->createNamedParameter($jti), - 'uri' => $queryBuilder->createNamedParameter($uri), - ]) - ->executeStatement() - ; - } + $queryBuilder->insert($this->table) + ->values([ + 'jti' => $queryBuilder->createNamedParameter($jti), + 'uri' => $queryBuilder->createNamedParameter($uri), + ]) + ->executeStatement() + ; + } } diff --git a/solid/lib/Middleware/SolidCorsMiddleware.php b/solid/lib/Middleware/SolidCorsMiddleware.php index f0af686..e011690 100644 --- a/solid/lib/Middleware/SolidCorsMiddleware.php +++ b/solid/lib/Middleware/SolidCorsMiddleware.php @@ -1,58 +1,58 @@ request = $request; - } - - public function afterController($controller, $methodName, Response $response) { - $corsMethods="GET, PUT, POST, OPTIONS, DELETE, PATCH"; - $corsAllowedHeaders="*, allow, accept, authorization, content-type, dpop, slug, link"; - $corsMaxAge="1728000"; - $corsExposeHeaders="Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via"; - $corsAllowCredentials="true"; - - if (isset($this->request->server['HTTP_ORIGIN'])) { - $corsAllowOrigin = $this->request->server['HTTP_ORIGIN']; - } else { - $corsAllowOrigin = '*'; - } - - $response->addHeader('Access-Control-Allow-Origin', $corsAllowOrigin); - $response->addHeader('Access-Control-Allow-Methods', $corsMethods); - $response->addHeader('Access-Control-Allow-Headers', $corsAllowedHeaders); - $response->addHeader('Access-Control-Max-Age', $corsMaxAge); - $response->addHeader('Access-Control-Allow-Credentials', $corsAllowCredentials); - $response->addHeader('Access-Control-Expose-Headers', $corsExposeHeaders); - $response->addHeader('Accept-Patch', 'text/n3'); - - $pubsub = getenv("PUBSUB_URL"); - if ($pubsub) { - $response->addHeader('updates-via', $pubsub); - } - $linkHeaders = '; rel="http://www.w3.org/ns/solid#storageDescription"'; - $existingHeaders = $response->getHeaders(); - if (isset($existingHeaders['Link'])) { // careful - this dictionary key is case sensitive here - $linkHeaders .= ', ' . $existingHeaders['Link']; - } - $response->addHeader('Link', $linkHeaders); - - /** - * Please note that the Link header with rel='acl' and the WAC-Allow - * header have already been added by pdsinterop/solid-auth, and Link - * headers with rel='type' by pdsinterop/php-solid-crud. - * - * @see https://github.com/pdsinterop/php-solid-auth/blob/v0.10.1/src/WAC.php#L39-L40 - * @see https://github.com/pdsinterop/php-solid-crud/blob/v0.7.1/src/Server.php#L679-L683 - */ - return $response; - } - } + +namespace OCA\Solid\Middleware; + +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Middleware; +use OCP\IRequest; + +class SolidCorsMiddleware extends Middleware { + private $request; + + public function __construct(IRequest $request) { + $this->request = $request; + } + + public function afterController($controller, $methodName, Response $response) { + $corsMethods = "GET, PUT, POST, OPTIONS, DELETE, PATCH"; + $corsAllowedHeaders = "*, allow, accept, authorization, content-type, dpop, slug, link"; + $corsMaxAge = "1728000"; + $corsExposeHeaders = "Authorization, User, Location, Link, Vary, Last-Modified, ETag, Accept-Patch, Accept-Post, Updates-Via, Allow, WAC-Allow, Content-Length, WWW-Authenticate, MS-Author-Via"; + $corsAllowCredentials = "true"; + + if (isset($this->request->server['HTTP_ORIGIN'])) { + $corsAllowOrigin = $this->request->server['HTTP_ORIGIN']; + } else { + $corsAllowOrigin = '*'; + } + + $response->addHeader('Access-Control-Allow-Origin', $corsAllowOrigin); + $response->addHeader('Access-Control-Allow-Methods', $corsMethods); + $response->addHeader('Access-Control-Allow-Headers', $corsAllowedHeaders); + $response->addHeader('Access-Control-Max-Age', $corsMaxAge); + $response->addHeader('Access-Control-Allow-Credentials', $corsAllowCredentials); + $response->addHeader('Access-Control-Expose-Headers', $corsExposeHeaders); + $response->addHeader('Accept-Patch', 'text/n3'); + + $pubsub = getenv("PUBSUB_URL"); + if ($pubsub) { + $response->addHeader('updates-via', $pubsub); + } + $linkHeaders = '; rel="http://www.w3.org/ns/solid#storageDescription"'; + $existingHeaders = $response->getHeaders(); + if (isset($existingHeaders['Link'])) { // careful - this dictionary key is case sensitive here + $linkHeaders .= ', ' . $existingHeaders['Link']; + } + $response->addHeader('Link', $linkHeaders); + + /** + * Please note that the Link header with rel='acl' and the WAC-Allow + * header have already been added by pdsinterop/solid-auth, and Link + * headers with rel='type' by pdsinterop/php-solid-crud. + * + * @see https://github.com/pdsinterop/php-solid-auth/blob/v0.10.1/src/WAC.php#L39-L40 + * @see https://github.com/pdsinterop/php-solid-crud/blob/v0.7.1/src/Server.php#L679-L683 + */ + return $response; + } +} diff --git a/solid/lib/Migration/Version000000Date20220919084800.php b/solid/lib/Migration/Version000000Date20220919084800.php index 22a6fa0..3e749e7 100644 --- a/solid/lib/Migration/Version000000Date20220919084800.php +++ b/solid/lib/Migration/Version000000Date20220919084800.php @@ -6,8 +6,8 @@ use Closure; use OCP\DB\ISchemaWrapper; -use OCP\Migration\SimpleMigrationStep; use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; class Version000000Date20220919084800 extends SimpleMigrationStep { /** diff --git a/solid/lib/Migration/Version000000Date20221020145600.php b/solid/lib/Migration/Version000000Date20221020145600.php index 90b3eb1..7fd978a 100644 --- a/solid/lib/Migration/Version000000Date20221020145600.php +++ b/solid/lib/Migration/Version000000Date20221020145600.php @@ -7,52 +7,51 @@ use Closure; use OCP\DB\ISchemaWrapper; use OCP\DB\Types; -use OCP\Migration\SimpleMigrationStep; use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; class Version000000Date20221020145600 extends SimpleMigrationStep { - /** - * @param IOutput $output - * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options - * - * @return null|ISchemaWrapper - */ - public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper - { - /** @var ISchemaWrapper $schema */ - $schema = $schemaClosure(); - - // As the JTI table should only contain short-lived data anyway, it can safely be dropped - if ($schema->hasTable('solid_jti') === true) { - $schema->dropTable('solid_jti'); - } - - $table = $schema->createTable('solid_jti'); - - $table->addColumn('id', Types::INTEGER, [ - 'autoincrement' => true, - 'notnull' => true, - ]); - - $table->addColumn('jti', Types::STRING, [ - 'length' => 255, - 'notnull' => true, - ]); - - $table->addColumn('uri', Types::STRING, [ - 'length' => 40, - 'notnull' => true, - ]); - - $table->addColumn('request_time', Types::DATETIME, [ - 'default' => 'CURRENT_TIMESTAMP', - 'notnull' => true, - ]); - - $table->setPrimaryKey(['id']); - $table->addIndex(['jti', 'uri']); - - return $schema; - } + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + // As the JTI table should only contain short-lived data anyway, it can safely be dropped + if ($schema->hasTable('solid_jti') === true) { + $schema->dropTable('solid_jti'); + } + + $table = $schema->createTable('solid_jti'); + + $table->addColumn('id', Types::INTEGER, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + + $table->addColumn('jti', Types::STRING, [ + 'length' => 255, + 'notnull' => true, + ]); + + $table->addColumn('uri', Types::STRING, [ + 'length' => 40, + 'notnull' => true, + ]); + + $table->addColumn('request_time', Types::DATETIME, [ + 'default' => 'CURRENT_TIMESTAMP', + 'notnull' => true, + ]); + + $table->setPrimaryKey(['id']); + $table->addIndex(['jti', 'uri']); + + return $schema; + } } diff --git a/solid/lib/Notifications/SolidNotifications.php b/solid/lib/Notifications/SolidNotifications.php index ff5e399..66efd96 100644 --- a/solid/lib/Notifications/SolidNotifications.php +++ b/solid/lib/Notifications/SolidNotifications.php @@ -1,31 +1,29 @@ notifications = $notifications; - } + if ($pubsub) { + $notifications[] = new SolidPubSub($pubsub); + } - public function send($path, $type) { - foreach ($this->notifications as $notification) { - $notification->send($path, $type); - } - } - } + $this->notifications = $notifications; + } + + public function send($path, $type) { + foreach ($this->notifications as $notification) { + $notification->send($path, $type); + } + } +} diff --git a/solid/lib/Notifications/SolidPubSub.php b/solid/lib/Notifications/SolidPubSub.php index e0acaf4..a339b1f 100644 --- a/solid/lib/Notifications/SolidPubSub.php +++ b/solid/lib/Notifications/SolidPubSub.php @@ -1,29 +1,29 @@ pubsub = $pubsubUrl; - } +use Pdsinterop\Solid\SolidNotifications\SolidNotificationsInterface; +use WebSocket\Client; - public function send($path, $type) { - $pubsub = str_replace(["https://", "http://"], "ws://", $this->pubsub); +class SolidPubSub implements SolidNotificationsInterface { + private $pubsub; + public function __construct($pubsubUrl) { + $this->pubsub = $pubsubUrl; + } - $client = new Client($pubsub, array( - 'headers' => array( - 'Sec-WebSocket-Protocol' => 'solid-0.1' - ) - )); + public function send($path, $type) { + $pubsub = str_replace(["https://", "http://"], "ws://", $this->pubsub); - try { - $client->send("pub $path\n"); - } catch (\WebSocket\Exception $exception) { - throw new Exception('Could not write to pubsub server', 502, $exception); - } - } - } + $client = new Client($pubsub, array( + 'headers' => array( + 'Sec-WebSocket-Protocol' => 'solid-0.1' + ) + )); + + try { + $client->send("pub $path\n"); + } catch (\WebSocket\Exception $exception) { + throw new Exception('Could not write to pubsub server', 502, $exception); + } + } +} diff --git a/solid/lib/Notifications/SolidWebhook.php b/solid/lib/Notifications/SolidWebhook.php index 00df1ac..d7af115 100644 --- a/solid/lib/Notifications/SolidWebhook.php +++ b/solid/lib/Notifications/SolidWebhook.php @@ -1,74 +1,74 @@ webhookService = $app->getContainer()->get(SolidWebhookService::class); - } - public function send($topic, $type) { - $webhooks = $this->getWebhooks($topic); - foreach ($webhooks as $webhook) { - try { - $this->postUpdate($webhook->{'target'}, $topic, $type); - } catch(\Exception $e) { - // FIXME: add retry code here? - } - } - } - private function getWebhooks($topic) { - $urls = $this->webhookService->findByTopic($topic); - return $urls; - } - private function postUpdate($webhookUrl, $topic, $type) { - try { - $postData = $this->createUpdatePayload($topic, $type); - $opts = array( - 'http' => array( - 'method' => 'POST', - 'header' => 'Content-Type: application/ld+json', - 'content' => $postData - ), - 'ssl' => array( - 'verify_peer' => false, // FIXME: Do we need to be more strict here? - 'verify_peer_name' => false - ) - ); - $context = stream_context_create($opts); - $result = file_get_contents($webhookUrl, false, $context); - } catch (\Exception $exception) { - throw new Exception('Could not write to webhook server', 502, $exception); - } - } - private function createUpdatePayload($topic, $type) { - //$updateId = "urn:uuid:"; - //$actor = ""; - $object = [ - "id" => $topic, - "type" => [ - "http://www.w3.org/ns/ldp#Resource" - ] - ]; - $state = "1234-5678-90ab-cdef-12345678"; - $published = date(DATE_ISO8601); - $payload = [ - "@context" => [ - "https://www.w3.org/ns/activitystreams", - "https://www.w3.org/ns/solid/notification/v1" - ], - // "id":$updateId, - // "actor" : [$actor], - "type" => [$type], - "object" => $object, - // "state" : $state - "published" => $published - ]; - - return json_encode($payload); - } - } +namespace OCA\Solid\Notifications; + +use OCA\Solid\Service\SolidWebhookService; +use Pdsinterop\Solid\SolidNotifications\SolidNotificationsInterface; + +class SolidWebhook implements SolidNotificationsInterface { + public function __construct() { + $app = new \OCP\AppFramework\App('solid'); + $this->webhookService = $app->getContainer()->get(SolidWebhookService::class); + } + + public function send($topic, $type) { + $webhooks = $this->getWebhooks($topic); + foreach ($webhooks as $webhook) { + try { + $this->postUpdate($webhook->{'target'}, $topic, $type); + } catch (\Exception $e) { + // FIXME: add retry code here? + } + } + } + private function getWebhooks($topic) { + $urls = $this->webhookService->findByTopic($topic); + return $urls; + } + private function postUpdate($webhookUrl, $topic, $type) { + try { + $postData = $this->createUpdatePayload($topic, $type); + $opts = array( + 'http' => array( + 'method' => 'POST', + 'header' => 'Content-Type: application/ld+json', + 'content' => $postData + ), + 'ssl' => array( + 'verify_peer' => false, // FIXME: Do we need to be more strict here? + 'verify_peer_name' => false + ) + ); + $context = stream_context_create($opts); + $result = file_get_contents($webhookUrl, false, $context); + } catch (\Exception $exception) { + throw new Exception('Could not write to webhook server', 502, $exception); + } + } + private function createUpdatePayload($topic, $type) { + //$updateId = "urn:uuid:"; + //$actor = ""; + $object = [ + "id" => $topic, + "type" => [ + "http://www.w3.org/ns/ldp#Resource" + ] + ]; + $state = "1234-5678-90ab-cdef-12345678"; + $published = date(DATE_ISO8601); + $payload = [ + "@context" => [ + "https://www.w3.org/ns/activitystreams", + "https://www.w3.org/ns/solid/notification/v1" + ], + // "id":$updateId, + // "actor" : [$actor], + "type" => [$type], + "object" => $object, + // "state" : $state + "published" => $published + ]; + + return json_encode($payload); + } +} diff --git a/solid/lib/PlainResponse.php b/solid/lib/PlainResponse.php index 41cd03b..0454cc7 100644 --- a/solid/lib/PlainResponse.php +++ b/solid/lib/PlainResponse.php @@ -1,53 +1,54 @@ data = $data; - $this->setStatus($statusCode); - $this->addHeader('Content-Type', 'text/html; charset=utf-8'); - } - - /** - * Returns the data unchanged - * @return string the data (unchanged) - */ - public function render() { - $response = $this->data; - return $response; - } - - /** - * Sets the data for the response - * @return PlainResponse Reference to this object - */ - public function setData($data) { - $this->data = $data; - return $this; - } - - /** - * Used to get the set parameters - * @return response data - */ - public function getData() { - return $this->data; - } + // FIXME: We might as well add a PSRResponse class to handle those; + + /** + * response data + * @var array|object + */ + protected $data; + + /** + * constructor of PlainResponse + * @param array|object $data the object or array that should be transformed + * @param int $statusCode the Http status code, defaults to 200 + */ + public function __construct($data = '', $statusCode = Http::STATUS_OK) { + parent::__construct(); + $this->data = $data; + $this->setStatus($statusCode); + $this->addHeader('Content-Type', 'text/html; charset=utf-8'); + } + + /** + * Returns the data unchanged + * @return string the data (unchanged) + */ + public function render() { + $response = $this->data; + return $response; + } + + /** + * Sets the data for the response + * @return PlainResponse Reference to this object + */ + public function setData($data) { + $this->data = $data; + return $this; + } + + /** + * Used to get the set parameters + * @return response data + */ + public function getData() { + return $this->data; + } } diff --git a/solid/lib/ServerConfig.php b/solid/lib/ServerConfig.php index 590a371..244f5af 100644 --- a/solid/lib/ServerConfig.php +++ b/solid/lib/ServerConfig.php @@ -1,215 +1,216 @@ config = $config; - $this->userManager = $userManager; - $this->urlGenerator = $urlGenerator; - } + public function __construct(IConfig $config, IUrlGenerator $urlGenerator, IUserManager $userManager) { + $this->config = $config; + $this->userManager = $userManager; + $this->urlGenerator = $urlGenerator; + } - /** - * @return string - */ - public function getPrivateKey() { - $result = $this->config->getAppValue('solid','privateKey'); - if (!$result) { - // generate and save a new set if we don't have a private key; - $keys = $this->generateKeySet(); - $this->config->setAppValue('solid','privateKey',$keys['privateKey']); - $this->config->setAppValue('solid','encryptionKey',$keys['encryptionKey']); - } - return $this->config->getAppValue('solid','privateKey'); - } + /** + * @return string + */ + public function getPrivateKey() { + $result = $this->config->getAppValue('solid', 'privateKey'); + if (!$result) { + // generate and save a new set if we don't have a private key; + $keys = $this->generateKeySet(); + $this->config->setAppValue('solid', 'privateKey', $keys['privateKey']); + $this->config->setAppValue('solid', 'encryptionKey', $keys['encryptionKey']); + } + return $this->config->getAppValue('solid', 'privateKey'); + } - /** - * @param string $privateKey - */ - public function setPrivateKey($privateKey) { - $this->config->setAppValue('solid','privateKey',$privateKey); - } + /** + * @param string $privateKey + */ + public function setPrivateKey($privateKey) { + $this->config->setAppValue('solid', 'privateKey', $privateKey); + } - /** - * @return string - */ - public function getEncryptionKey() { - return $this->config->getAppValue('solid','encryptionKey'); - } + /** + * @return string + */ + public function getEncryptionKey() { + return $this->config->getAppValue('solid', 'encryptionKey'); + } - /** - * @param string $publicKey - */ - public function setEncryptionKey($publicKey) { - $this->config->setAppValue('solid','encryptionKey',$publicKey); - } + /** + * @param string $publicKey + */ + public function setEncryptionKey($publicKey) { + $this->config->setAppValue('solid', 'encryptionKey', $publicKey); + } - /** - * @param string $clientId - * @return array|null - */ - public function getClientConfigById($clientId) { - $clients = (array)$this->config->getAppValue('solid','clients'); - if (array_key_exists($clientId, $clients)) { - return $clients[$clientId]; - } - return null; + /** + * @param string $clientId + * @return array|null + */ + public function getClientConfigById($clientId) { + $clients = (array)$this->config->getAppValue('solid', 'clients'); + if (array_key_exists($clientId, $clients)) { + return $clients[$clientId]; } + return null; + } - /** - * @return array|null - */ - public function getClients() { - $clients = (array)$this->config->getAppKeys('solid'); - return $clients; - } + /** + * @return array|null + */ + public function getClients() { + $clients = (array)$this->config->getAppKeys('solid'); + return $clients; + } - /** - * @param array $clientConfig - * @return string - */ - public function saveClientConfig($clientConfig) { - $clients = (array)$this->config->getAppValue('solid', 'clients'); - $clientId = uuidv4(); - $clients[$clientId] = $clientConfig; - $this->config->setAppValue('solid','clients', $clients); - return $clientId; - } + /** + * @param array $clientConfig + * @return string + */ + public function saveClientConfig($clientConfig) { + $clients = (array)$this->config->getAppValue('solid', 'clients'); + $clientId = uuidv4(); + $clients[$clientId] = $clientConfig; + $this->config->setAppValue('solid', 'clients', $clients); + return $clientId; + } - /** - * @param string $clientId - * @param array $scopes - */ - public function addScopesToClient($clientId, $scopes) { - $clientScopes = $this->getClientScopes($clientId); - $clientScopes = array_unique(array_merge($clientScopes, $scopes)); - $this->setClientScopes($clientId, $clientScopes); - } + /** + * @param string $clientId + * @param array $scopes + */ + public function addScopesToClient($clientId, $scopes) { + $clientScopes = $this->getClientScopes($clientId); + $clientScopes = array_unique(array_merge($clientScopes, $scopes)); + $this->setClientScopes($clientId, $clientScopes); + } - /** - * @param string $clientId - * @param array $scopes - */ - public function setClientScopes($clientId, $scopes) { - $clientScopes = (array)$this->config->getAppValue('solid', 'clientScopes'); - $clientScopes[$clientId] = $scopes; - $this->config->setAppValue('solid', 'clientScopes', $clientScopes); - } + /** + * @param string $clientId + * @param array $scopes + */ + public function setClientScopes($clientId, $scopes) { + $clientScopes = (array)$this->config->getAppValue('solid', 'clientScopes'); + $clientScopes[$clientId] = $scopes; + $this->config->setAppValue('solid', 'clientScopes', $clientScopes); + } - /** - * @param string $clientId - * @return array - */ - public function getClientScopes($clientId) { - $clientScopes = (array)$this->config->getAppValue('solid', 'clientScopes'); - if (array_key_exists($clientId, $clientScopes)) { - return $clientScopes[$clientId]; - } - return []; + /** + * @param string $clientId + * @return array + */ + public function getClientScopes($clientId) { + $clientScopes = (array)$this->config->getAppValue('solid', 'clientScopes'); + if (array_key_exists($clientId, $clientScopes)) { + return $clientScopes[$clientId]; } + return []; + } - /** - * @param string $clientId - */ - public function removeClientConfig($clientId) { - $clients = (array)$this->config->getAppValue('solid', 'clients'); - unset($clients[$clientId]); - $this->config->setAppValue('solid','clients', $clients); - $scopes = (array)$this->config->getAppValue('solid', 'clientScopes'); - unset($scopes[$clientId]); - $this->config->setAppValue('solid', 'clientScopes', $scopes); - } - public function getAllowedClients($userId) { - return json_decode($this->config->getUserValue($userId, 'solid', "allowedClients", "[]"), true); - } + /** + * @param string $clientId + */ + public function removeClientConfig($clientId) { + $clients = (array)$this->config->getAppValue('solid', 'clients'); + unset($clients[$clientId]); + $this->config->setAppValue('solid', 'clients', $clients); + $scopes = (array)$this->config->getAppValue('solid', 'clientScopes'); + unset($scopes[$clientId]); + $this->config->setAppValue('solid', 'clientScopes', $scopes); + } + public function getAllowedClients($userId) { + return json_decode($this->config->getUserValue($userId, 'solid', "allowedClients", "[]"), true); + } - public function addAllowedClient($userId, $clientId) { - $allowedClients = $this->getAllowedClients($userId); - $allowedClients[] = $clientId; - $this->config->setUserValue($userId, "solid", "allowedClients", json_encode($allowedClients)); - } - public function removeAllowedClient($userId, $clientId) { - $allowedClients = $this->getAllowedClients($userId); - $allowedClients = array_diff($allowedClients, array($clientId)); - $this->config->setUserValue($userId, "solid", "allowedClients", json_encode($allowedClients)); - } + public function addAllowedClient($userId, $clientId) { + $allowedClients = $this->getAllowedClients($userId); + $allowedClients[] = $clientId; + $this->config->setUserValue($userId, "solid", "allowedClients", json_encode($allowedClients)); + } + public function removeAllowedClient($userId, $clientId) { + $allowedClients = $this->getAllowedClients($userId); + $allowedClients = array_diff($allowedClients, array($clientId)); + $this->config->setUserValue($userId, "solid", "allowedClients", json_encode($allowedClients)); + } - public function saveClientRegistration($origin, $clientData) { - $originHash = md5($origin); - $existingRegistration = $this->getClientRegistration($originHash); - if ($existingRegistration && isset($existingRegistration['redirect_uris'])) { - foreach ($existingRegistration['redirect_uris'] as $uri) { - $clientData['redirect_uris'][] = $uri; - } - $clientData['redirect_uris'] = array_unique($clientData['redirect_uris']); + public function saveClientRegistration($origin, $clientData) { + $originHash = md5($origin); + $existingRegistration = $this->getClientRegistration($originHash); + if ($existingRegistration && isset($existingRegistration['redirect_uris'])) { + foreach ($existingRegistration['redirect_uris'] as $uri) { + $clientData['redirect_uris'][] = $uri; } + $clientData['redirect_uris'] = array_unique($clientData['redirect_uris']); + } - $clientData['client_name'] = $origin; - $clientData['client_secret'] = md5(random_bytes(32)); - $this->config->setAppValue('solid', "client-" . $originHash, json_encode($clientData)); + $clientData['client_name'] = $origin; + $clientData['client_secret'] = md5(random_bytes(32)); + $this->config->setAppValue('solid', "client-" . $originHash, json_encode($clientData)); - $this->config->setAppValue('solid', "client-" . $origin, json_encode($clientData)); - return $originHash; - } + $this->config->setAppValue('solid', "client-" . $origin, json_encode($clientData)); + return $originHash; + } - public function removeClientRegistration($clientId) { - $this->config->deleteAppValue('solid', "client-" . $clientId); - } + public function removeClientRegistration($clientId) { + $this->config->deleteAppValue('solid', "client-" . $clientId); + } - public function getClientRegistration($clientId) { - $data = $this->config->getAppValue('solid', "client-" . $clientId, "{}"); - return json_decode($data, true); - } + public function getClientRegistration($clientId) { + $data = $this->config->getAppValue('solid', "client-" . $clientId, "{}"); + return json_decode($data, true); + } - public function getProfileData($userId) { - return $this->config->getUserValue($userId, "solid", "profileData", ""); - } - public function setProfileData($userId, $profileData) { - $this->config->setUserValue($userId, "solid", "profileData", $profileData); - - if ($this->userManager->userExists($userId)) { - $graph = new \EasyRdf\Graph(); - $graph->parse($profileData, 'turtle'); - $data = $graph->toRdfPhp(); - $subject = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.profile.handleGet", array("userId" => $userId, "path" => "/card"))) . "#me"; - $subjectData = $data[$subject]; - $fields = array( - "name" => $subjectData['http://xmlns.com/foaf/0.1/name'][0]['value'] - ); - - // and write them to the user; - $user = $this->userManager->get($userId); - $user->setDisplayName($fields['name']); - } - } - private function generateKeySet() { - $config = array( - "digest_alg" => "sha256", - "private_key_bits" => 2048, - "private_key_type" => OPENSSL_KEYTYPE_RSA, - ); - // Create the private and public key - $key = openssl_pkey_new($config); - - // Extract the private key from $key to $privateKey - openssl_pkey_export($key, $privateKey); - $encryptionKey = base64_encode(random_bytes(32)); - $result = array( - "privateKey" => $privateKey, - "encryptionKey" => $encryptionKey + public function getProfileData($userId) { + return $this->config->getUserValue($userId, "solid", "profileData", ""); + } + public function setProfileData($userId, $profileData) { + $this->config->setUserValue($userId, "solid", "profileData", $profileData); + + if ($this->userManager->userExists($userId)) { + $graph = new \EasyRdf\Graph(); + $graph->parse($profileData, 'turtle'); + $data = $graph->toRdfPhp(); + $subject = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.profile.handleGet", array("userId" => $userId, "path" => "/card"))) . "#me"; + $subjectData = $data[$subject]; + $fields = array( + "name" => $subjectData['http://xmlns.com/foaf/0.1/name'][0]['value'] ); - return $result; + + // and write them to the user; + $user = $this->userManager->get($userId); + $user->setDisplayName($fields['name']); } } + private function generateKeySet() { + $config = array( + "digest_alg" => "sha256", + "private_key_bits" => 2048, + "private_key_type" => OPENSSL_KEYTYPE_RSA, + ); + // Create the private and public key + $key = openssl_pkey_new($config); + + // Extract the private key from $key to $privateKey + openssl_pkey_export($key, $privateKey); + $encryptionKey = base64_encode(random_bytes(32)); + $result = array( + "privateKey" => $privateKey, + "encryptionKey" => $encryptionKey + ); + return $result; + } +} diff --git a/solid/lib/Service/SolidWebhookService.php b/solid/lib/Service/SolidWebhookService.php index 32469a9..cf5eab2 100644 --- a/solid/lib/Service/SolidWebhookService.php +++ b/solid/lib/Service/SolidWebhookService.php @@ -3,12 +3,10 @@ namespace OCA\Solid\Service; use Exception; - -use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\MultipleObjectsReturnedException; - use OCA\Solid\Db\SolidWebhook; use OCA\Solid\Db\SolidWebhookMapper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; class SolidWebhookService { /** @var SolidWebhookMapper */ diff --git a/solid/lib/Service/UserService.php b/solid/lib/Service/UserService.php index f831ca2..6766b05 100644 --- a/solid/lib/Service/UserService.php +++ b/solid/lib/Service/UserService.php @@ -1,18 +1,19 @@ userSession = $userSession; - } + public function __construct($userSession) { + $this->userSession = $userSession; + } - public function login($userId, $password) { - return $this->userSession->login($userId, $password); - } + public function login($userId, $password) { + return $this->userSession->login($userId, $password); + } - public function logout() { - $this->userSession->logout(); - } + public function logout() { + $this->userSession->logout(); + } } diff --git a/solid/lib/Settings.php b/solid/lib/Settings.php index 7ab6dfb..9ddcc23 100644 --- a/solid/lib/Settings.php +++ b/solid/lib/Settings.php @@ -2,7 +2,6 @@ namespace OCA\Solid; -use OCA\Solid\ServerConfig; use OcP\AppFramework\Http\TemplateResponse; use OCP\Settings\ISettings; diff --git a/solid/lib/WellKnown/OpenIdConfigurationHandler.php b/solid/lib/WellKnown/OpenIdConfigurationHandler.php index a408f84..057b695 100644 --- a/solid/lib/WellKnown/OpenIdConfigurationHandler.php +++ b/solid/lib/WellKnown/OpenIdConfigurationHandler.php @@ -1,18 +1,17 @@ config = new \OCA\Solid\ServerConfig($config, $urlGenerator, $userManager); - $this->request = $request; + $this->request = $request; $this->urlGenerator = $urlGenerator; - $this->authServerConfig = $this->createAuthServerConfig(); - $this->authServerFactory = (new \Pdsinterop\Solid\Auth\Factory\AuthorizationServerFactory($this->authServerConfig))->create(); + $this->authServerConfig = $this->createAuthServerConfig(); + $this->authServerFactory = (new \Pdsinterop\Solid\Auth\Factory\AuthorizationServerFactory($this->authServerConfig))->create(); } private function getOpenIdEndpoints() { @@ -53,45 +51,45 @@ private function getOpenIdEndpoints() { "registration_endpoint" => $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("solid.server.register")) ]; } - + private function getKeys() { $encryptionKey = $this->config->getEncryptionKey(); - $privateKey = $this->config->getPrivateKey(); - $key = openssl_pkey_get_private($privateKey); - $publicKey = openssl_pkey_get_details($key)['key']; + $privateKey = $this->config->getPrivateKey(); + $key = openssl_pkey_get_private($privateKey); + $publicKey = openssl_pkey_get_details($key)['key']; return [ "encryptionKey" => $encryptionKey, - "privateKey" => $privateKey, - "publicKey" => $publicKey + "privateKey" => $privateKey, + "publicKey" => $publicKey ]; } - + private function createAuthServerConfig() { $keys = $this->getKeys(); try { return (new \Pdsinterop\Solid\Auth\Factory\ConfigFactory( - new \Pdsinterop\Solid\Auth\Config\Client('','',array(),''), + new \Pdsinterop\Solid\Auth\Config\Client('', '', array(), ''), $keys['encryptionKey'], $keys['privateKey'], $keys['publicKey'], $this->getOpenIdEndpoints() ))->create(); - } catch(\Throwable $e) { + } catch (\Throwable $e) { return null; } } - public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse { - if ($service !== 'openid-configuration') { - return $previousResponse; - } + public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse { + if ($service !== 'openid-configuration') { + return $previousResponse; + } $response = new \Laminas\Diactoros\Response(); - $server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response); + $server = new \Pdsinterop\Solid\Auth\Server($this->authServerFactory, $this->authServerConfig, $response); $response = $server->respondToOpenIdMetadataRequest(); return new GenericResponse($this->respond($response)); - } + } private function respond($response) { $statusCode = $response->getStatusCode(); @@ -110,7 +108,7 @@ private function respond($response) { $body = 'ok'; } $result = new JSONResponse($body); - + foreach ($headers as $header => $values) { foreach ($values as $value) { $result->addHeader($header, $value); diff --git a/solid/lib/WellKnown/SolidHandler.php b/solid/lib/WellKnown/SolidHandler.php index 5026525..95b027e 100644 --- a/solid/lib/WellKnown/SolidHandler.php +++ b/solid/lib/WellKnown/SolidHandler.php @@ -1,31 +1,30 @@ urlGenerator = $urlGenerator; } - public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse { - if ($service !== 'solid') { - return $previousResponse; - } + public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse { + if ($service !== 'solid') { + return $previousResponse; + } $webhooksRegisterEndpoint = $this->urlGenerator->linkToRoute('solid.solidWebhook.register'); // FIXME: this shouldn't happen: if (strlen($webhooksRegisterEndpoint) == 0) { diff --git a/solid/templates/admin.php b/solid/templates/admin.php index b892e13..60897bf 100644 --- a/solid/templates/admin.php +++ b/solid/templates/admin.php @@ -1,19 +1,19 @@
-

t('Solid OpenID Connect Settings')); ?>

-

- +

t('Solid OpenID Connect Settings')); ?>

+

+ - -

+ +

\ No newline at end of file diff --git a/solid/templates/applauncher.php b/solid/templates/applauncher.php index 3e4077d..63125e4 100644 --- a/solid/templates/applauncher.php +++ b/solid/templates/applauncher.php @@ -4,11 +4,11 @@ ?>
- inc('navigation/index')); ?> + inc('navigation/index')); ?>
-
- inc('applauncher/index')); ?> -
+
+ inc('applauncher/index')); ?> +
diff --git a/solid/templates/applauncher/index.php b/solid/templates/applauncher/index.php index e4516af..c50d797 100644 --- a/solid/templates/applauncher/index.php +++ b/solid/templates/applauncher/index.php @@ -1,10 +1,10 @@
- +
diff --git a/solid/templates/profile.php b/solid/templates/profile.php index c4a05d8..3251f8c 100644 --- a/solid/templates/profile.php +++ b/solid/templates/profile.php @@ -4,11 +4,11 @@ ?>
- inc('navigation/index')); ?> + inc('navigation/index')); ?>
-
- inc('profile/index')); ?> -
+
+ inc('profile/index')); ?> +
diff --git a/solid/templates/profile/index.php b/solid/templates/profile/index.php index 1c4c7b3..e8eb102 100644 --- a/solid/templates/profile/index.php +++ b/solid/templates/profile/index.php @@ -1,14 +1,14 @@
-
-
-

Your Solid profile

-

WebID URI:

-

Your display name:

-
-
+
+
+

Your Solid profile

+

WebID URI:

+

Your display name:

+
+
\ No newline at end of file diff --git a/solid/templates/profile/turtle.php b/solid/templates/profile/turtle.php index c1d8b95..0bd2e59 100644 --- a/solid/templates/profile/turtle.php +++ b/solid/templates/profile/turtle.php @@ -1,6 +1,6 @@ \ No newline at end of file + +header("Content-type: text/turtle"); +p($_['turtleProfile']); +// phpcs:disable PSR2.Files.ClosingTag.NotAllowed +// phpcs:disable PSR2.Files.EndFileNewline.NoneFound diff --git a/solid/templates/sharing.php b/solid/templates/sharing.php index f5b460c..6310876 100644 --- a/solid/templates/sharing.php +++ b/solid/templates/sharing.php @@ -3,18 +3,18 @@ style('solid', 'style'); ?>
- inc('navigation/index')); ?> + inc('navigation/index')); ?>
-
-
-
-

is requesting your consent to use as an identity provider

- - - -
-
-
+
+
+
+

is requesting your consent to use as an identity provider

+ + + +
+
+
diff --git a/solid/templates/turtle-profile.php b/solid/templates/turtle-profile.php index 478cb7c..f052d40 100644 --- a/solid/templates/turtle-profile.php +++ b/solid/templates/turtle-profile.php @@ -1,4 +1,4 @@ inc('profile/turtle')); - // phpcs:disable PSR2.Files.ClosingTag.NotAllowed -?> + +print_unescaped($this->inc('profile/turtle')); +// phpcs:disable PSR2.Files.ClosingTag.NotAllowed diff --git a/solid/tests/Integration/AppTest.php b/solid/tests/Integration/AppTest.php index 6347f96..2f50ce0 100644 --- a/solid/tests/Integration/AppTest.php +++ b/solid/tests/Integration/AppTest.php @@ -5,23 +5,22 @@ use OCP\AppFramework\App; use Test\TestCase; - /** * This test shows how to make a small Integration Test. Query your class * directly from the container, only pass in mocks if needed and run your tests * against the database */ class AppTest extends TestCase { - private $container; + private $container; - public function setUp() { - parent::setUp(); - $app = new App('solid'); - $this->container = $app->getContainer(); - } + public function setUp() { + parent::setUp(); + $app = new App('solid'); + $this->container = $app->getContainer(); + } - public function testAppInstalled() { - $appManager = $this->container->query('OCP\App\IAppManager'); - $this->assertTrue($appManager->isInstalled('solid')); - } + public function testAppInstalled() { + $appManager = $this->container->query('OCP\App\IAppManager'); + $this->assertTrue($appManager->isInstalled('solid')); + } } diff --git a/solid/tests/Unit/Controller/PageControllerTest.php b/solid/tests/Unit/Controller/PageControllerTest.php index 2b488ab..7ecdc8f 100644 --- a/solid/tests/Unit/Controller/PageControllerTest.php +++ b/solid/tests/Unit/Controller/PageControllerTest.php @@ -2,12 +2,9 @@ namespace OCA\Solid\Tests\Unit\Controller; -use PHPUnit_Framework_TestCase; - -use OCP\AppFramework\Http\TemplateResponse; - use OCA\Solid\Controller\PageController; - +use OCP\AppFramework\Http\TemplateResponse; +use PHPUnit_Framework_TestCase; class PageControllerTest extends PHPUnit_Framework_TestCase { private $controller; @@ -25,5 +22,4 @@ public function testIndex() { $this->assertEquals('index', $result->getTemplateName()); $this->assertTrue($result instanceof TemplateResponse); } - } diff --git a/solid/tests/Unit/JtiReplayDetectorTest.php b/solid/tests/Unit/JtiReplayDetectorTest.php index 5395a60..03bf0db 100644 --- a/solid/tests/Unit/JtiReplayDetectorTest.php +++ b/solid/tests/Unit/JtiReplayDetectorTest.php @@ -3,26 +3,23 @@ namespace OCA\Solid; use DateInterval; -use OCP\IDBConnection; +use OCP\DB\IResult; use OCP\DB\QueryBuilder\IExpressionBuilder; use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\DB\IResult; +use OCP\IDBConnection; use PHPUnit\Framework\TestCase; -class JtiReplayDetectorTest extends TestCase -{ - public static function setUpBeforeClass(): void - { - require_once __DIR__.'/../../lib/JtiReplayDetector.php'; +class JtiReplayDetectorTest extends TestCase { + public static function setUpBeforeClass(): void { + require_once __DIR__ . '/../../lib/JtiReplayDetector.php'; } - private function createMocks($result) - { + private function createMocks($result) { $mockIDBConnection = $this->createMock(IDBConnection::class); $mockQueryBuilder = $this->createMock(IQueryBuilder::class); $mockExpr = $this->createMock(IExpressionBuilder::class); $mockResult = $this->createMock(IResult::class); - + $mockIDBConnection->expects($this->any()) ->method('getQueryBuilder') ->willReturn($mockQueryBuilder); @@ -67,13 +64,12 @@ private function createMocks($result) return $mockIDBConnection; } - public function testJtiDetected(): void - { + public function testJtiDetected(): void { $dateInterval = new DateInterval('PT90S'); $mockIDBConnection = $this->createMocks(true); - + $detector = new JtiReplayDetector($dateInterval, $mockIDBConnection); - + $mockUUID = 'mockUUID-with-some-more-text'; $mockURI = 'mockURI'; $result = $detector->detect($mockUUID, $mockURI); @@ -81,18 +77,16 @@ public function testJtiDetected(): void $this->assertTrue($result); } - public function testJtiNotDetected(): void - { + public function testJtiNotDetected(): void { $dateInterval = new DateInterval('PT90S'); $mockIDBConnection = $this->createMocks(false); - + $detector = new JtiReplayDetector($dateInterval, $mockIDBConnection); - + $mockUUID = 'mockUUID-with-some-more-text'; $mockURI = 'mockURI'; $result = $detector->detect($mockUUID, $mockURI); $this->assertFalse($result); } - } diff --git a/solid/tests/bootstrap.php b/solid/tests/bootstrap.php index 91a9a5b..b3ead28 100644 --- a/solid/tests/bootstrap.php +++ b/solid/tests/bootstrap.php @@ -1,12 +1,12 @@ addValidRoot(OC::$SERVERROOT . '/tests'); @@ -14,8 +14,8 @@ // Fix for "Autoload path not allowed: .../solid/tests/testcase.php" \OC_App::loadApp('solid'); -if(!class_exists('PHPUnit_Framework_TestCase')) { - require_once('PHPUnit/Autoload.php'); +if (!class_exists('PHPUnit_Framework_TestCase')) { + require_once('PHPUnit/Autoload.php'); } OC_Hook::clear();