From f08aa4d07ca2560e8f433f24cd5dfaf75f4f6d90 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 18 Jul 2023 00:04:49 +0300 Subject: [PATCH 01/29] Caching adjustments. Refactoring, use service layer with cache for other entities --- Makefile | 2 +- README.md | 77 +------- appinfo/info.xml | 3 + docs/authentication.rst | 116 +++++++++++ docs/conf.py | 4 +- docs/definitions.rst | 1 + docs/index.rst | 1 + docs/requirements.txt | 1 + lib/AppInfo/Application.php | 2 +- lib/Capabilities.php | 6 +- lib/Command/ExApp/Register.php | 24 ++- lib/Command/ExApp/Scopes/ListScopes.php | 86 ++++++++ lib/Command/ExApp/Unregister.php | 7 +- lib/Command/ExApp/Users/ListUsers.php | 84 ++++++++ lib/Command/Scopes/ListApiScopes.php | 67 +++++++ lib/Db/ExApp.php | 2 +- lib/Db/ExAppPreference.php | 2 +- lib/Db/ExAppScope.php | 2 +- lib/Db/ExAppScopeMapper.php | 13 ++ lib/Db/ExAppUser.php | 4 +- lib/Db/ExAppUserMapper.php | 23 +++ lib/Service/AppEcosystemV2Service.php | 230 +++++++--------------- lib/Service/DaemonConfigService.php | 48 ++--- lib/Service/ExAppApiScopeService.php | 34 +++- lib/Service/ExAppConfigService.php | 47 +++-- lib/Service/ExAppPreferenceService.php | 14 +- lib/Service/ExAppScopesService.php | 128 ++++++++++++ lib/Service/ExAppUsersService.php | 143 ++++++++++++++ lib/Service/ExFilesActionsMenuService.php | 4 +- 29 files changed, 854 insertions(+), 321 deletions(-) create mode 100644 docs/authentication.rst create mode 100644 lib/Command/ExApp/Scopes/ListScopes.php create mode 100644 lib/Command/ExApp/Users/ListUsers.php create mode 100644 lib/Command/Scopes/ListApiScopes.php create mode 100644 lib/Service/ExAppScopesService.php create mode 100644 lib/Service/ExAppUsersService.php diff --git a/Makefile b/Makefile index dc2cff7a..7f6724a6 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ dock-sock27: dock-sock26: @echo "creating daemon for nextcloud 'stable26' container" docker exec master-nextcloud-1 sudo -u www-data php occ app_ecosystem_v2:daemon:unregister docker_dev || true - docker exec master-stable6-1 sudo -u www-data php occ app_ecosystem_v2:daemon:register \ + docker exec master-stable26-1 sudo -u www-data php occ app_ecosystem_v2:daemon:register \ docker_dev Docker docker-install unix-socket /var/run/docker.sock http://stable26/index.php --net=master_default .PHONY: dock2port diff --git a/README.md b/README.md index 8f788908..b4167398 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ This authentication is based on a shared secret between Nextcloud and the extern 1. ExApp sends a request to Nextcloud 2. Nextcloud passes request to AppEcosystemV2 -3. AppEcosystemV2 validates request (see [authentication](#AppEcosystemV2-authentication) section) +3. AppEcosystemV2 validates request 4. Request is accepted/rejected ```mermaid @@ -61,80 +61,7 @@ sequenceDiagram Nextcloud-->>-ExApp: Response (200/401) ``` -### AppEcosystemV2 authentication - -Each ExApp request to secured with AEAuth must contain the following headers (order is important): - -1. `AE-VERSION` - `[required]` minimal version of the AppEcosystemV2 -2. `EX-APP-ID` - `[required]` id of the ExApp -3. `EX-APP-VERSION` - `[required]` version of the ExApp -4. `NC-USER-ID` - `[optional]` the user under which the request is made, can be empty in case of system apps (more details in [scopes](#AppEcosystemV2-scopes) section) -5. `AE-DATA-HASH` - `[required]` hash of the request body (see details in [signature](#AE-SIGNATURE) section) -6. `AE-SIGN-TIME` - `[required]` unix timestamp of the request -7. `AE-SIGNATURE` - `[required]` signature of the request (see details [signature](#AE-SIGNATURE) section) - -### AE-SIGNATURE - -AppEcosystemV2 signature (AE-SIGNATURE) is a HMAC-SHA256 hash of the request signed with the shared secret. - -Depending on request method signing body is different: - -* `GET` - * method - * uri (with urlencoded query params) - * headers (`AE-VERSION`, `EX-APP-ID`, `EX-APP-VERSION`, `NC-USER-ID`, `AE-DATA-HASH`, `AE-SIGN-TIME`) -* Others - * method - * uri (with urlencoded query params) - * headers (`AE-VERSION`, `EX-APP-ID`, `EX-APP-VERSION`, `NC-USER-ID`, `AE-DATA-HASH`, `AE-SIGN-TIME`) - * xxh64 hash from request body (post data, json, files, etc) - -### AE-DATA-HASH signature - -`AE-DATA-HASH` header must contain a xxh64 hash of the request body. -It's calculated even if the request body is empty (e.g. empty hash: `ef46db3751d8e999`). - -### AppEcosystemV2 scopes - -AppEcosystemV2 supports the following default scopes: - -* `BASIC_API_SCOPE` - init scope, used when ExApp is on initialization step and has no user context -* `SYSTEM_API_SCOPE` - configured for system apps, mostly has no user context -* `DAV_API_SCOPE` - scope for dav requests, has user context - -### AppEcosystemV2 authentication diagram - -```mermaid -sequenceDiagram - autonumber - participant ExApp - box Nextcloud - participant Nextcloud - participant AppEcosystemV2 - end - ExApp->>+Nextcloud: Request to API - Nextcloud->>Nextcloud: Check if AE-SIGNATURE header exists - Nextcloud-->>ExApp: Reject if AE-SIGNATURE header not exists - Nextcloud->>Nextcloud: Check if AppEcosystemV2 enabled - Nextcloud-->>ExApp: Reject if AppEcosystemV2 not enabled - Nextcloud->>+AppEcosystemV2: Validate request - AppEcosystemV2-->>AppEcosystemV2: Check if ExApp exists and enabled - AppEcosystemV2-->>Nextcloud: Reject if ExApp not exists or disabled - AppEcosystemV2-->>AppEcosystemV2: Validate AE-SIGN-TIME - AppEcosystemV2-->>Nextcloud: Reject if sign time diff > 5 min - AppEcosystemV2-->>AppEcosystemV2: Generate and validate AE-SIGNATURE - AppEcosystemV2-->>Nextcloud: Reject if signature not match - AppEcosystemV2-->>AppEcosystemV2: Validate AE-DATA-HASH - AppEcosystemV2-->>Nextcloud: Reject if data hash not match - AppEcosystemV2-->>AppEcosystemV2: Check API scope - AppEcosystemV2-->>Nextcloud: Reject if API scope not match - AppEcosystemV2-->>AppEcosystemV2: Check if user interacted with ExApp - AppEcosystemV2-->>Nextcloud: Reject if user has not interacted with ExApp (attempt to bypass user) - AppEcosystemV2-->>AppEcosystemV2: Check if user is not empty and active - AppEcosystemV2-->>Nextcloud: Set active user - AppEcosystemV2->>-Nextcloud: Request accepted/rejected - Nextcloud->>-ExApp: Response (200/401) -``` +More details in [docs](https://cloud-py-api.github.io/app_ecosystem_v2/authentication.html) ## 🔧 Configuration diff --git a/appinfo/info.xml b/appinfo/info.xml index 38fd6f16..f76213bd 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -34,6 +34,8 @@ OCA\AppEcosystemV2\Command\ExApp\Enable OCA\AppEcosystemV2\Command\ExApp\Disable OCA\AppEcosystemV2\Command\ExApp\ListExApps + OCA\AppEcosystemV2\Command\ExApp\Scopes\ListScopes + OCA\AppEcosystemV2\Command\ExApp\Users\ListUsers OCA\AppEcosystemV2\Command\ExAppConfig\GetConfig OCA\AppEcosystemV2\Command\ExAppConfig\SetConfig OCA\AppEcosystemV2\Command\ExAppConfig\DeleteConfig @@ -41,6 +43,7 @@ OCA\AppEcosystemV2\Command\Daemon\RegisterDaemon OCA\AppEcosystemV2\Command\Daemon\UnregisterDaemon OCA\AppEcosystemV2\Command\Daemon\ListDaemons + OCA\AppEcosystemV2\Command\Scopes\ListApiScopes OCA\AppEcosystemV2\Settings\Admin diff --git a/docs/authentication.rst b/docs/authentication.rst new file mode 100644 index 00000000..61d16fee --- /dev/null +++ b/docs/authentication.rst @@ -0,0 +1,116 @@ +============== +Authentication +============== + +AppEcosystemV2 adds separate authentication for external apps. +This authentication is based on a shared secret between Nextcloud and the external app. + + +Authentication flow +^^^^^^^^^^^^^^^^^^^ + +1. ExApp sends a request to Nextcloud +2. Nextcloud passes request to AppEcosystemV2 +3. AppEcosystemV2 validates request (see [authentication](#AppEcosystemV2-authentication) section) +4. Request is accepted/rejected + +.. mermaid:: + + sequenceDiagram + participant ExApp + box Nextcloud + participant Nextcloud + participant AppEcosystemV2 + end + ExApp->>+Nextcloud: Request to API + Nextcloud->>+AppEcosystemV2: Validate request + AppEcosystemV2-->>-Nextcloud: Request accepted/rejected + Nextcloud-->>-ExApp: Response (200/401) + + +Authentication headers +^^^^^^^^^^^^^^^^^^^^^^ + +Each ExApp request to secured API with AppEcosystemAuth must contain the following headers (order is important): + +1. `AE-VERSION` - minimal version of the AppEcosystemV2 +2. `EX-APP-ID` - id of the ExApp +3. `EX-APP-VERSION` - version of the ExApp +4. `NC-USER-ID` - the user under which the request is made, can be empty in case of system apps (more details in [scopes](#AppEcosystemV2-scopes) section) +5. `AE-DATA-HASH` - hash of the request body (see details in [signature](#AE-SIGNATURE) section) +6. `AE-SIGN-TIME` - unix timestamp of the request +7. `AE-SIGNATURE` - signature of the request (see details [signature](#AE-SIGNATURE) section) + + +AE_SIGNATURE +************ + +AppEcosystemV2 signature (AE-SIGNATURE) is a HMAC-SHA256 hash of the request signed with the shared secret. + +The signature is calculated from the following data: + +* method +* uri (with urlencoded query parameters) +* headers (`AE-VERSION`, `EX-APP-ID`, `EX-APP-VERSION`, `NC-USER-ID`, `AE-DATA-HASH`, `AE-SIGN-TIME`) +* xxh64 hash from request body (post data, json, files, etc.) + +AE_DATA_HASH +************ + +`AE-DATA-HASH` header must contain a xxh64 hash of the request body. +It's calculated even if the request body is empty (e.g. empty hash: `ef46db3751d8e999`). + + +ExApp scopes +************ + +AppEcosystemV2 will support extensible scopes (with interfaces to register own ones). +Currently, the following scopes are available: + +* `BASIC` +* `SYSTEM` +* `USER_INFO` +* `USER_STATUS` +* `NOTIFICATIONS` +* `WEATHER_STATUS` +* `DAV` + +There is a CLI command to list registered scopes: `occ app_ecosystem_v2:scopes:list`. + +Authentication flow in details +****************************** + +.. mermaid:: + :zoom: + + sequenceDiagram + autonumber + participant ExApp + box Nextcloud + participant Nextcloud + participant AppEcosystemV2 + end + ExApp->>+Nextcloud: Request to API + Nextcloud->>Nextcloud: Check if AE-SIGNATURE header exists + Nextcloud-->>ExApp: Reject if AE-SIGNATURE header not exists + Nextcloud->>Nextcloud: Check if AppEcosystemV2 enabled + Nextcloud-->>ExApp: Reject if AppEcosystemV2 not enabled + Nextcloud->>+AppEcosystemV2: Validate request + AppEcosystemV2-->>AppEcosystemV2: Check if ExApp exists and enabled + AppEcosystemV2-->>Nextcloud: Reject if ExApp not exists or disabled + AppEcosystemV2-->>AppEcosystemV2: Validate AE-SIGN-TIME + AppEcosystemV2-->>Nextcloud: Reject if sign time diff > 5 min + AppEcosystemV2-->>AppEcosystemV2: Generate and validate AE-SIGNATURE + AppEcosystemV2-->>Nextcloud: Reject if signature not match + AppEcosystemV2-->>AppEcosystemV2: Validate AE-DATA-HASH + AppEcosystemV2-->>Nextcloud: Reject if data hash not match + AppEcosystemV2-->>AppEcosystemV2: Check API scope + AppEcosystemV2-->>Nextcloud: Reject if API scope not match + AppEcosystemV2-->>AppEcosystemV2: Check if user interacted with ExApp + AppEcosystemV2-->>Nextcloud: Reject if user has not interacted with ExApp (attempt to bypass user) + AppEcosystemV2-->>AppEcosystemV2: Check if user is not empty and active + AppEcosystemV2-->>Nextcloud: Set active user + AppEcosystemV2->>-Nextcloud: Request accepted/rejected + Nextcloud->>-ExApp: Response (200/401) + + diff --git a/docs/conf.py b/docs/conf.py index f62e2c6f..5a13c9df 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,7 +15,9 @@ "sphinx_copybutton", "sphinx_inline_tabs", "sphinx_issues", - "sphinx_rtd_theme"] + "sphinx_rtd_theme", + "sphinxcontrib.mermaid", +] # General information about the project. project = "AppEcosystemV2" diff --git a/docs/definitions.rst b/docs/definitions.rst index 88aabf56..f90382dd 100644 --- a/docs/definitions.rst +++ b/docs/definitions.rst @@ -7,3 +7,4 @@ AppEcosystemV2 brings out the following terms: * ExApp (External App) - the app on another (from PHP) programming language, which can be hosted separately or inside container * DaemonConfig - configuration of orchestration daemon (e.g. Docker) where ExApps are deployed * ExAppConfig - similar to Nextcloud `app_config`, but for ExApps configuration +* AppEcosystemAuth - AppEcosystemV2 authentication diff --git a/docs/index.rst b/docs/index.rst index ca128bc2..f8279d9f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,3 +11,4 @@ Welcome to the amazing docs that we will write for AppEcosystemV2! definitions.rst development/index.rst deploy/index.rst + ./authentication diff --git a/docs/requirements.txt b/docs/requirements.txt index 0200b9ec..c8a1a654 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,3 +3,4 @@ sphinx-rtd-theme>=1.2.2 sphinx-copybutton>=0.5.2 sphinx_issues>=3.0.1 sphinx-inline-tabs +sphinxcontrib-mermaid>=0.9.2 diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 754771dd..d6312ed4 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -54,7 +54,7 @@ class Application extends App implements IBootstrap { public const APP_ID = 'app_ecosystem_v2'; - public const CACHE_TTL = 3600; + public const CACHE_TTL = 60 * 60; public const ICON_CACHE_TTL = 60 * 60 *24; public function __construct(array $urlParams = []) { diff --git a/lib/Capabilities.php b/lib/Capabilities.php index c9385e0a..a053fa75 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -34,6 +34,7 @@ use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Db\ExAppScope; use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; +use OCA\AppEcosystemV2\Service\ExAppScopesService; use OCP\App\IAppManager; use OCP\Capabilities\ICapability; use OCP\IConfig; @@ -43,18 +44,21 @@ class Capabilities implements ICapability { private IConfig $config; private IAppManager $appManager; private AppEcosystemV2Service $service; + private ExAppScopesService $exAppScopesService; private IRequest $request; public function __construct( IConfig $config, IAppManager $appManager, AppEcosystemV2Service $service, + ExAppScopesService $exAppScopesService, IRequest $request, ) { $this->config = $config; $this->appManager = $appManager; $this->service = $service; $this->request = $request; + $this->exAppScopesService = $exAppScopesService; } public function getCapabilities(): array { @@ -75,7 +79,7 @@ private function attachExAppScopes(&$capabilities): void { if ($exApp !== null) { $capabilities['scopes'] = array_map(function (ExAppScope $scope) { return intval($scope->getScopeGroup()); - }, $this->service->getExAppScopeGroups($exApp)); + }, $this->exAppScopesService->getExAppScopes($exApp)); } } } diff --git a/lib/Command/ExApp/Register.php b/lib/Command/ExApp/Register.php index 7cf1a36f..2e6c39bb 100644 --- a/lib/Command/ExApp/Register.php +++ b/lib/Command/ExApp/Register.php @@ -31,11 +31,6 @@ namespace OCA\AppEcosystemV2\Command\ExApp; -use OCA\AppEcosystemV2\Db\ExApp; -use OCA\AppEcosystemV2\DeployActions\DockerActions; -use OCA\AppEcosystemV2\DeployActions\ManualActions; -use OCA\AppEcosystemV2\Service\DaemonConfigService; -use OCA\AppEcosystemV2\Service\ExAppApiScopeService; use OCP\DB\Exception; use OCP\Http\Client\IResponse; use Symfony\Component\Console\Command\Command; @@ -44,14 +39,23 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use OCA\AppEcosystemV2\Db\ExApp; +use OCA\AppEcosystemV2\DeployActions\DockerActions; +use OCA\AppEcosystemV2\DeployActions\ManualActions; use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; -use Symfony\Component\Console\Question\ConfirmationQuestion; +use OCA\AppEcosystemV2\Service\DaemonConfigService; +use OCA\AppEcosystemV2\Service\ExAppApiScopeService; +use OCA\AppEcosystemV2\Service\ExAppScopesService; +use OCA\AppEcosystemV2\Service\ExAppUsersService; class Register extends Command { private AppEcosystemV2Service $service; private DaemonConfigService $daemonConfigService; private ExAppApiScopeService $exAppApiScopeService; + private ExAppScopesService $exAppScopesService; + private ExAppUsersService $exAppUsersService; private DockerActions $dockerActions; private ManualActions $manualActions; @@ -59,6 +63,8 @@ public function __construct( AppEcosystemV2Service $service, DaemonConfigService $daemonConfigService, ExAppApiScopeService $exAppApiScopeService, + ExAppScopesService $exAppScopesService, + ExAppUsersService $exAppUsersService, DockerActions $dockerActions, ManualActions $manualActions, ) { @@ -67,6 +73,8 @@ public function __construct( $this->service = $service; $this->daemonConfigService = $daemonConfigService; $this->exAppApiScopeService = $exAppApiScopeService; + $this->exAppScopesService = $exAppScopesService; + $this->exAppUsersService = $exAppUsersService; // TODO: Change to dynamic DeployActions resolving $this->dockerActions = $dockerActions; @@ -141,7 +149,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (filter_var($exAppInfo['system_app'], FILTER_VALIDATE_BOOLEAN)) { try { - $this->service->setupSystemAppFlag($exApp); + $this->exAppUsersService->setupSystemAppFlag($exApp); } catch (Exception $e) { $output->writeln(sprintf('Error while setting app system flag: %s', $e->getMessage())); @@ -219,7 +227,7 @@ private function registerExAppScopes($output, ExApp $exApp, array $requestedExAp $scopeType = $required ? 'required' : 'optional'; $registeredScopeGroups = []; foreach ($requestedExAppScopeGroups as $scopeGroup) { - if ($this->service->setExAppScopeGroup($exApp, $scopeGroup)) { + if ($this->exAppScopesService->setExAppScopeGroup($exApp, $scopeGroup)) { $registeredScopeGroups[] = $scopeGroup; } else { $output->writeln(sprintf('Failed to set %s ExApp scope group: %s', $scopeType, $scopeGroup)); diff --git a/lib/Command/ExApp/Scopes/ListScopes.php b/lib/Command/ExApp/Scopes/ListScopes.php new file mode 100644 index 00000000..7d282bad --- /dev/null +++ b/lib/Command/ExApp/Scopes/ListScopes.php @@ -0,0 +1,86 @@ + + * + * @copyright Copyright (c) 2023 Alexander Piskun + * + * @author 2023 Andrey Borysenko + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\AppEcosystemV2\Command\ExApp\Scopes; + +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; +use OCA\AppEcosystemV2\Service\ExAppApiScopeService; +use OCA\AppEcosystemV2\Service\ExAppScopesService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + + +class ListScopes extends Command { + private AppEcosystemV2Service $service; + private ExAppScopesService $exAppScopeService; + private ExAppApiScopeService $exAppApiScopeService; + + public function __construct( + AppEcosystemV2Service $service, + ExAppScopesService $exAppScopeService, + ExAppApiScopeService $exAppApiScopeService, + ) { + parent::__construct(); + + $this->service = $service; + $this->exAppScopeService = $exAppScopeService; + $this->exAppApiScopeService = $exAppApiScopeService; + } + + protected function configure() { + $this->setName('app_ecosystem_v2:app:scopes:list'); + $this->setDescription('List ExApp granted scopes'); + + $this->addArgument('appid', InputArgument::REQUIRED); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $appId = $input->getArgument('appid'); + $exApp = $this->service->getExApp($appId); + if ($exApp === null) { + $output->writeln(sprintf('ExApp %s not found.', $appId)); + return 2; + } + + $scopes = $this->exAppScopeService->getExAppScopes($exApp); + if (empty($scopes)) { + $output->writeln(sprintf('No scopes granted for ExApp %s', $appId)); + return 0; + } + + $output->writeln(sprintf('ExApp %s scopes:', $exApp->getAppid())); + $mappedScopes = array_unique($this->exAppApiScopeService->mapScopeGroupsToNames($scopes)); + $output->writeln(join(', ', $mappedScopes)); + return 0; + } +} diff --git a/lib/Command/ExApp/Unregister.php b/lib/Command/ExApp/Unregister.php index 22649b27..7e0ddd45 100644 --- a/lib/Command/ExApp/Unregister.php +++ b/lib/Command/ExApp/Unregister.php @@ -38,6 +38,7 @@ use Symfony\Component\Console\Output\OutputInterface; use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; +use OCA\AppEcosystemV2\Service\ExAppScopesService; class Unregister extends Command { private AppEcosystemV2Service $service; @@ -85,12 +86,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln(sprintf('Failed to unregister ExApp %s.', $appId)); return 1; } - if ($exApp->getAppid() === $appId) { - $appScopes = $this->service->getExAppScopeGroups($exApp); - foreach ($appScopes as $appScope) { - $this->service->removeExAppScopeGroup($exApp, intval($appScope->getScopeGroup())); - } - } $output->writeln(sprintf('ExApp %s successfully unregistered.', $appId)); return 0; diff --git a/lib/Command/ExApp/Users/ListUsers.php b/lib/Command/ExApp/Users/ListUsers.php new file mode 100644 index 00000000..2e6ffc77 --- /dev/null +++ b/lib/Command/ExApp/Users/ListUsers.php @@ -0,0 +1,84 @@ + + * + * @copyright Copyright (c) 2023 Alexander Piskun + * + * @author 2023 Andrey Borysenko + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\AppEcosystemV2\Command\ExApp\Users; + +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; +use OCA\AppEcosystemV2\Service\ExAppUsersService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + + +class ListUsers extends Command { + private AppEcosystemV2Service $service; + private ExAppUsersService $exAppUserService; + + public function __construct( + AppEcosystemV2Service $service, + ExAppUsersService $exAppUserService, + ) { + parent::__construct(); + + $this->service = $service; + $this->exAppUserService = $exAppUserService; + } + + protected function configure() { + $this->setName('app_ecosystem_v2:app:users:list'); + $this->setDescription('List ExApp authorized users'); + + $this->addArgument('appid', InputArgument::REQUIRED); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $appId = $input->getArgument('appid'); + $exApp = $this->service->getExApp($appId); + if ($exApp === null) { + $output->writeln(sprintf('ExApp %s not found.', $appId)); + return 2; + } + + $exAppUsers = $this->exAppUserService->getExAppUsers($exApp); + if (empty($exAppUsers)) { + $output->writeln(sprintf('ExApp %s has no authorized users.', $appId)); + return 0; + } + + $output->writeln(sprintf('ExApp %s authorized users:', $appId)); + foreach ($exAppUsers as $exAppUser) { + $output->writeln($exAppUser->getUserid() !== '' ? $exAppUser->getUserid() : '[system user]'); + } + + return 0; + } +} diff --git a/lib/Command/Scopes/ListApiScopes.php b/lib/Command/Scopes/ListApiScopes.php new file mode 100644 index 00000000..fcaea33f --- /dev/null +++ b/lib/Command/Scopes/ListApiScopes.php @@ -0,0 +1,67 @@ + + * + * @copyright Copyright (c) 2023 Alexander Piskun + * + * @author 2023 Andrey Borysenko + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\AppEcosystemV2\Command\Scopes; + +use OCA\AppEcosystemV2\Service\ExAppApiScopeService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + + +class ListApiScopes extends Command { + private ExAppApiScopeService $service; + + public function __construct(ExAppApiScopeService $service) { + parent::__construct(); + + $this->service = $service; + } + + protected function configure() { + $this->setName('app_ecosystem_v2:scopes:list'); + $this->setDescription('List registered API scopes'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $scopes = $this->service->getExAppApiScopes(); + if (empty($scopes)) { + $output->writeln('No API scopes registered'); + return 0; + } + + $output->writeln('Registered API scopes:'); + foreach ($scopes as $scope) { + $output->writeln(sprintf(' %s. %s [%s]', $scope->getScopeGroup(), $scope->getApiRoute(), $scope->getName())); + } + return 0; + } +} diff --git a/lib/Db/ExApp.php b/lib/Db/ExApp.php index c2b74928..02e31fdf 100644 --- a/lib/Db/ExApp.php +++ b/lib/Db/ExApp.php @@ -139,7 +139,7 @@ public function __construct(array $params = []) { public function jsonSerialize(): array { return [ 'id' => $this->getId(), - 'app_id' => $this->getAppid(), + 'appid' => $this->getAppid(), 'version' => $this->getVersion(), 'name'=> $this->getName(), 'daemon_config_name' => $this->getDaemonConfigName(), diff --git a/lib/Db/ExAppPreference.php b/lib/Db/ExAppPreference.php index 2e367a08..9ffd76bd 100644 --- a/lib/Db/ExAppPreference.php +++ b/lib/Db/ExAppPreference.php @@ -84,7 +84,7 @@ public function jsonSerialize(): array { return [ 'id' => $this->getId(), 'user_id' => $this->getUserid(), - 'app_id' => $this->getAppid(), + 'appid' => $this->getAppid(), 'configkey' => $this->getConfigkey(), 'configvalue' => $this->getConfigvalue(), ]; diff --git a/lib/Db/ExAppScope.php b/lib/Db/ExAppScope.php index 633eda2e..f6e79f31 100644 --- a/lib/Db/ExAppScope.php +++ b/lib/Db/ExAppScope.php @@ -69,7 +69,7 @@ public function __construct(array $params = []) { public function jsonSerialize(): array { return [ 'id' => $this->getId(), - 'app_id' => $this->getAppid(), + 'appid' => $this->getAppid(), 'scope_group' => $this->getScopeGroup(), ]; } diff --git a/lib/Db/ExAppScopeMapper.php b/lib/Db/ExAppScopeMapper.php index 6785b97e..32befd39 100644 --- a/lib/Db/ExAppScopeMapper.php +++ b/lib/Db/ExAppScopeMapper.php @@ -86,4 +86,17 @@ public function findByAppidScope(string $appId, int $scopeGroup): ?ExAppScope { ->andWhere($qb->expr()->eq('scope_group', $qb->createNamedParameter($scopeGroup), IQueryBuilder::PARAM_INT)) ); } + + /** + * @param string $appId + * + * @throws Exception + * @return int + */ + public function deleteByAppid(string $appId): int { + $qb = $this->db->getQueryBuilder(); + $qb->delete()->from($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)); + return $qb->executeStatement(); + } } diff --git a/lib/Db/ExAppUser.php b/lib/Db/ExAppUser.php index 97739985..b78ddbe4 100644 --- a/lib/Db/ExAppUser.php +++ b/lib/Db/ExAppUser.php @@ -69,8 +69,8 @@ public function __construct(array $params = []) { public function jsonSerialize(): array { return [ 'id' => $this->getId(), - 'app_id' => $this->getAppid(), - 'user_id' => $this->getUserid(), + 'appid' => $this->getAppid(), + 'userid' => $this->getUserid(), ]; } } diff --git a/lib/Db/ExAppUserMapper.php b/lib/Db/ExAppUserMapper.php index 6bbe57f8..6ff620cf 100644 --- a/lib/Db/ExAppUserMapper.php +++ b/lib/Db/ExAppUserMapper.php @@ -53,6 +53,16 @@ public function findAll(int $limit = null, int $offset = null): array { return $this->findEntities($qb); } + /** + * @throws Exception + */ + public function findByAppid(string $appId): array { + $qb = $this->db->getQueryBuilder(); + return $this->findEntities($qb->select('*') + ->from($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR)))); + } + /** * @param string $appId * @param string $userId @@ -73,4 +83,17 @@ public function findByAppidUserid(string $appId, string $userId): array { $qb->expr()->eq('userid', $qb->createNamedParameter(null, IQueryBuilder::PARAM_NULL)) )); } + + /** + * @param string $appId + * + * @throws Exception + * @return int + */ + public function deleteByAppid(string $appId): int { + $qb = $this->db->getQueryBuilder(); + return $qb->delete($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR))) + ->executeStatement(); + } } diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index 4734c669..19061647 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -32,8 +32,6 @@ namespace OCA\AppEcosystemV2\Service; use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Db\ExAppScope; -use OCA\AppEcosystemV2\Db\ExAppScopeMapper; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; use OCP\Http\Client\IResponse; @@ -49,8 +47,6 @@ use OCA\AppEcosystemV2\Db\ExApp; use OCA\AppEcosystemV2\Db\ExAppMapper; -use OCA\AppEcosystemV2\Db\ExAppUser; -use OCA\AppEcosystemV2\Db\ExAppUserMapper; use OCP\App\IAppManager; use OCP\AppFramework\Db\DoesNotExistException; use OCP\IRequest; @@ -61,6 +57,7 @@ class AppEcosystemV2Service { public const BASIC_API_SCOPE = 1; const MAX_SIGN_TIME_X_MIN_DIFF = 60 * 5; + const CACHE_TTL = 60 * 60; // 1 hour private LoggerInterface $logger; private ILogFactory $logFactory; @@ -69,31 +66,29 @@ class AppEcosystemV2Service { private IClient $client; private ExAppMapper $exAppMapper; private IAppManager $appManager; - private ExAppUserMapper $exAppUserMapper; private ISecureRandom $random; private IUserSession $userSession; private IUserManager $userManager; private ExAppApiScopeService $exAppApiScopeService; - private ExAppScopeMapper $exAppScopeMapper; + private ExAppUsersService $exAppUsersService; + private ExAppScopesService $exAppScopesService; private ExAppConfigService $exAppConfigService; - private DaemonConfigService $daemonConfigService; public function __construct( - LoggerInterface $logger, - ILogFactory $logFactory, - ICacheFactory $cacheFactory, - IConfig $config, - IClientService $clientService, - ExAppMapper $exAppMapper, - IAppManager $appManager, - ExAppUserMapper $exAppUserMapper, + LoggerInterface $logger, + ILogFactory $logFactory, + ICacheFactory $cacheFactory, + IConfig $config, + IClientService $clientService, + ExAppMapper $exAppMapper, + IAppManager $appManager, + ExAppUsersService $exAppUserService, ExAppApiScopeService $exAppApiScopeService, - ExAppScopeMapper $exAppScopeMapper, - ISecureRandom $random, - IUserSession $userSession, - IUserManager $userManager, - ExAppConfigService $exAppConfigService, - DaemonConfigService $daemonConfigService, + ExAppScopesService $exAppScopesService, + ISecureRandom $random, + IUserSession $userSession, + IUserManager $userManager, + ExAppConfigService $exAppConfigService, ) { $this->logger = $logger; $this->logFactory = $logFactory; @@ -102,28 +97,28 @@ public function __construct( $this->client = $clientService->newClient(); $this->exAppMapper = $exAppMapper; $this->appManager = $appManager; - $this->exAppUserMapper = $exAppUserMapper; $this->random = $random; $this->userSession = $userSession; $this->userManager = $userManager; + $this->exAppUsersService = $exAppUserService; $this->exAppApiScopeService = $exAppApiScopeService; - $this->exAppScopeMapper = $exAppScopeMapper; + $this->exAppScopesService = $exAppScopesService; $this->exAppConfigService = $exAppConfigService; - $this->daemonConfigService = $daemonConfigService; } - public function getExApp(string $exAppId): ?ExApp { + public function getExApp(string $appId): ?ExApp { try { - $cacheKey = 'exApp_' . $exAppId; -// $cached = $this->cache->get($cacheKey); -// if ($cached !== null) { -// return $cached instanceof ExApp ? $cached : new ExApp($cached); -// } + $cacheKey = '/exApp_' . $appId; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached instanceof ExApp ? $cached : new ExApp($cached); + } - $exApp = $this->exAppMapper->findByAppId($exAppId); + $exApp = $this->exAppMapper->findByAppId($appId); $this->cache->set($cacheKey, $exApp); return $exApp; - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { + } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { + $this->logger->debug(sprintf('ExApp %s not found.', $appId), ['exception' => $e]); return null; } } @@ -154,7 +149,10 @@ public function registerExApp(string $appId, array $appData): ?ExApp { $exApp->setStatus(json_encode(['active' => true])); // TODO: Add status request to ExApp $exApp->setLastResponseTime(time()); try { - return $this->exAppMapper->update($exApp); + $cacheKey = '/exApp_' . $appId; + $exApp = $this->exAppMapper->update($exApp); + $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); + return $exApp; } catch (Exception $e) { $this->logger->error(sprintf('Error while updating already registered ExApp: %s', $e->getMessage())); return null; @@ -174,7 +172,10 @@ public function registerExApp(string $appId, array $appData): ?ExApp { 'last_response_time' => time(), ]); try { - return $this->exAppMapper->insert($exApp); + $cacheKey = '/exApp_' . $appId; + $exApp = $this->exAppMapper->insert($exApp); + $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); + return $exApp; } catch (Exception $e) { $this->logger->error(sprintf('Error while registering ExApp: %s', $e->getMessage())); return null; @@ -191,17 +192,22 @@ public function registerExApp(string $appId, array $appData): ?ExApp { * @return ExApp|null */ public function unregisterExApp(string $appId): ?ExApp { + $exApp = $this->getExApp($appId); + if ($exApp === null) { + return null; + } try { - $exApp = $this->exAppMapper->findByAppId($appId); if ($this->exAppMapper->deleteExApp($exApp) !== 1) { $this->logger->error(sprintf('Error while unregistering ExApp: %s', $appId)); return null; } -// TODO: Remove app scopes, app users, app configs, app preferences - $this->cache->remove('exApp_' . $appId); +// TODO: Remove ?app configs, ?app preferences + $this->exAppScopesService->removeExAppScopes($exApp); + $this->exAppUsersService->removeExAppUsers($exApp); + $this->cache->remove('/exApp_' . $appId); return $exApp; - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { - $this->logger->error(sprintf('Error while unregistering ExApp: %s', $e->getMessage())); + } catch (Exception $e) { + $this->logger->error(sprintf('Error while unregistering ExApp: %s', $e->getMessage()), ['exception' => $e]); return null; } } @@ -214,41 +220,6 @@ public function getExAppsByPort(int $port): array { } } - public function getExAppScopeGroups(ExApp $exApp): array { - try { - return $this->exAppScopeMapper->findByAppid($exApp->getAppid()); - } catch (Exception) { - return []; - } - } - - public function setExAppScopeGroup(ExApp $exApp, int $scopeGroup): ?ExAppScope { - $appId = $exApp->getAppid(); - try { - return $this->exAppScopeMapper->findByAppidScope($appId, $scopeGroup); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - $exAppScope = new ExAppScope([ - 'appid' => $appId, - 'scope_group' => $scopeGroup, - ]); - try { - return $this->exAppScopeMapper->insert($exAppScope); - } catch (\Exception $e) { - $this->logger->error(sprintf('Error while setting ExApp scope group: %s', $e->getMessage())); - return null; - } - } - } - - public function removeExAppScopeGroup(ExApp $exApp, int $scopeGroup): ?ExAppScope { - try { - $exAppScope = $this->exAppScopeMapper->findByAppidScope($exApp->getAppid(), $scopeGroup); - return $this->exAppScopeMapper->delete($exAppScope); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - return null; - } - } - /** * Enable ExApp. Sends request to ExApp to update enabled state. * If request fails, ExApp will be disabled. @@ -277,8 +248,8 @@ public function enableExApp(ExApp $exApp): bool { return false; } - $cacheKey = 'exApp_' . $exApp->getAppid(); - $this->cache->remove($cacheKey); + $cacheKey = '/exApp_' . $exApp->getAppid(); + $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); return true; } } catch (Exception $e) { @@ -312,8 +283,8 @@ public function disableExApp(ExApp $exApp): bool { return false; } $this->updateExAppLastResponseTime($exApp); - $cacheKey = 'exApp_' . $exApp->getAppid(); - $this->cache->remove($cacheKey); + $cacheKey = '/exApp_' . $exApp->getAppid(); + $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); return true; } catch (Exception $e) { $this->logger->error(sprintf('Error while disabling ExApp: %s', $e->getMessage())); @@ -343,35 +314,6 @@ public function getAppStatus(string $appId): ?array { } } - /** - * @param ExApp $exApp - * @param string|null $userId - * - * @throws Exception - */ - public function setupExAppUser(ExApp $exApp, ?string $userId): void { - if (!empty($userId)) { - if (!$this->exAppUserExists($exApp->getAppid(), $userId)) { - $this->exAppUserMapper->insert(new ExAppUser([ - 'appid' => $exApp->getAppid(), - 'userid' => $userId, - ])); - } - } - } - - /** - * @param ExApp $exApp - * - * @throws Exception - */ - public function setupSystemAppFlag(ExApp $exApp): void { - $this->exAppUserMapper->insert(new ExAppUser([ - 'appid' => $exApp->getAppid(), - 'userid' => '', - ])); - } - /** * Request to ExApp with AppEcosystem auth headers and ExApp user initialization * @@ -393,9 +335,9 @@ public function aeRequestToExApp( array $params = [] ): array|IResponse { try { - $this->setupExAppUser($exApp, $userId); + $this->exAppUsersService->setupExAppUser($exApp, $userId); } catch (\Exception $e) { - $this->logger->error('Error while inserting ExApp user: ' . $e->getMessage()); + $this->logger->error(sprintf('Error while inserting ExApp %s user. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); return ['error' => 'Error while inserting ExApp user: ' . $e->getMessage()]; } return $this->requestToExApp($request, $userId, $exApp, $route, $method, $params); @@ -471,7 +413,7 @@ public function requestToExApp( // TODO: Add files support return $response; } catch (\Exception $e) { - $this->logger->error(sprintf('Error during request to ExApp %s: %s', $exApp->getAppid(), $e->getMessage())); + $this->logger->error(sprintf('Error during request to ExApp %s: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); return ['error' => $e->getMessage()]; } } @@ -562,7 +504,7 @@ private function generateDataHash(string $data): string { * - checks if request data hash is valid * - checks ExApp scopes <-> ExApp API copes * - * More info in docs: https://github.com/cloud-py-api/app_ecosystem_v2#authentication-diagram (temporal url, TODO: update link to docs) + * More info in docs: https://cloud-py-api.github.io/app_ecosystem_v2/authentication.html * * @param IRequest $request * @param bool $isDav @@ -570,20 +512,18 @@ private function generateDataHash(string $data): string { * @return bool */ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false): bool { - try { - $exApp = $this->exAppMapper->findByAppId($request->getHeader('EX-APP-ID')); - $enabled = $exApp->getEnabled(); - if (!$enabled) { - $this->logger->error(sprintf('ExApp with appId %s is disabled (%s)', $request->getHeader('EX-APP-ID'), $enabled)); - return false; - } - $secret = $exApp->getSecret(); + $exApp = $this->getExApp($request->getHeader('EX-APP-ID')); + if ($exApp === null) { + $this->logger->error(sprintf('ExApp with appId %s not found.', $request->getHeader('EX-APP-ID'))); + return false; + } - $this->handleExAppDebug($exApp, $request, false); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - $this->logger->error(sprintf('ExApp with appId %s not found', $request->getHeader('EX-APP-ID'))); + $enabled = $exApp->getEnabled(); + if (!$enabled) { + $this->logger->error(sprintf('ExApp with appId %s is disabled (%s)', $request->getHeader('EX-APP-ID'), $enabled)); return false; } + $this->handleExAppDebug($exApp, $request, false); $headers = [ 'AE-VERSION' => $request->getHeader('AE-VERSION'), @@ -605,7 +545,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) $headers['AE-SIGN-TIME'] = $signTime; $body = $request->getMethod() . $request->getRequestUri() . json_encode($headers, JSON_UNESCAPED_SLASHES); - $signature = hash_hmac('sha256', $body, $secret); + $signature = hash_hmac('sha256', $body, $exApp->getSecret()); $signatureValid = $signature === $requestSignature; if ($signatureValid) { @@ -617,7 +557,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) try { $path = $request->getPathInfo(); } catch (\Exception $e) { - $this->logger->error(sprintf('Error getting path info. Error: %s', $e->getMessage())); + $this->logger->error(sprintf('Error getting path info. Error: %s', $e->getMessage()), ['exception' => $e]); return false; } } else { @@ -631,18 +571,21 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) } // If it is not an initialization scope group - check if this endpoint is allowed to be called if ($apiScope->getScopeGroup() !== self::BASIC_API_SCOPE) { - if (!$this->passesScopeCheck($exApp, $apiScope->getScopeGroup())) { + if (!$this->exAppScopesService->passesScopeCheck($exApp, $apiScope->getScopeGroup())) { $this->logger->error(sprintf('ExApp %s not passed scope group check %s', $exApp->getAppid(), $path)); return false; } - if (!$this->exAppUserExists($exApp->getAppid(), $userId)) { + if (!$this->exAppUsersService->exAppUserExists($exApp->getAppid(), $userId)) { $this->logger->error(sprintf('ExApp %s user %s does not exist', $exApp->getAppid(), $userId)); return false; } } - return $this->finalizeRequestToNC($userId, $exApp); + return $this->finalizeRequestToNC($userId); + } else { + $this->logger->error(sprintf('Invalid signature for ExApp: %s and user: %s.', $exApp->getAppid(), $userId !== '' ? $userId : 'null')); } - $this->logger->error(sprintf('Invalid signature for ExApp: %s and user: %s.', $exApp->getAppid(), $userId !== '' ? $userId : 'null')); + + $this->logger->error(sprintf('ExApp %s request to NC validation failed.', $exApp->getAppid())); return false; } @@ -652,11 +595,10 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) * - updates ExApp last response time * * @param $userId - * @param $exApp * * @return bool */ - private function finalizeRequestToNC($userId, $exApp): bool { + private function finalizeRequestToNC($userId): bool { if ($userId !== '') { $activeUser = $this->userManager->get($userId); if ($activeUser === null) { @@ -667,7 +609,6 @@ private function finalizeRequestToNC($userId, $exApp): bool { } else { $this->userSession->setUser(null); } - $this->updateExAppLastResponseTime($exApp); return true; } @@ -706,7 +647,7 @@ public function updateExAppLastResponseTime(&$exApp): void { try { $this->exAppMapper->updateLastResponseTime($exApp); } catch (Exception $e) { - $this->logger->error(sprintf('Error while updating ExApp last response time for ExApp: %s. Error: %s', $exApp->getAppid(), $e->getMessage())); + $this->logger->error(sprintf('Error while updating ExApp last response time for ExApp: %s. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); } } @@ -721,7 +662,7 @@ public function getExAppsList(bool $extended = false): array { 'version' => $exApp->getVersion(), 'enabled' => $exApp->getEnabled(), 'last_response_time' => $exApp->getLastResponseTime(), - 'system' => $this->exAppUserExists($exApp->getAppid(), ''), + 'system' => $this->exAppUsersService->exAppUserExists($exApp->getAppid(), ''), ]; }, $exApps); } else { @@ -730,7 +671,7 @@ public function getExAppsList(bool $extended = false): array { }, $exApps); } } catch (Exception $e) { - $this->logger->error(sprintf('Error while getting ExApps list. Error: %s', $e->getMessage())); + $this->logger->error(sprintf('Error while getting ExApps list. Error: %s', $e->getMessage()), ['exception' => $e]); $exApps = []; } return $exApps; @@ -742,27 +683,6 @@ public function getNCUsersList(): ?array { }, $this->userManager->searchDisplayName('')); } - private function exAppUserExists(string $appId, string $userId): bool { - try { - $exAppUsers = $this->exAppUserMapper->findByAppidUserid($appId, $userId); - if (!empty($exAppUsers) && $exAppUsers[0] instanceof ExAppUser) { - return true; - } - return false; - } catch (Exception) { - return false; - } - } - - public function passesScopeCheck(ExApp $exApp, int $apiScope): bool { - try { - $exAppScope = $this->exAppScopeMapper->findByAppidScope($exApp->getAppid(), $apiScope); - return $exAppScope instanceof ExAppScope; - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - return false; - } - } - private function getCustomLogger(string $name): LoggerInterface { $path = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $name; return $this->logFactory->getCustomPsrLogger($path); diff --git a/lib/Service/DaemonConfigService.php b/lib/Service/DaemonConfigService.php index 2d65fd83..c36e4a2e 100644 --- a/lib/Service/DaemonConfigService.php +++ b/lib/Service/DaemonConfigService.php @@ -46,6 +46,7 @@ * Daemon configuration (daemons) */ class DaemonConfigService { + const CACHE_TTL = 60 * 60 * 2; // 2 hours private LoggerInterface $logger; private ICache $cache; private DaemonConfigMapper $mapper; @@ -70,10 +71,10 @@ public function registerDaemonConfig(array $params): ?DaemonConfig { 'host' => $params['host'], 'deploy_config' => $params['deploy_config'], ])); - $this->cache->remove('daemon_configs'); + $this->cache->remove('/daemon_configs'); return $daemonConfig; } catch (Exception $e) { - $this->logger->error('Failed to register daemon config. Error: ' . $e->getMessage()); + $this->logger->error('Failed to register daemon config. Error: ' . $e->getMessage(), ['exception' => $e]); return null; } } @@ -81,35 +82,18 @@ public function registerDaemonConfig(array $params): ?DaemonConfig { public function unregisterDaemonConfig(DaemonConfig $daemonConfig): ?DaemonConfig { try { $daemonConfig = $this->mapper->delete($daemonConfig); - $this->cache->remove('daemon_configs'); - $this->cache->remove('daemon_config_' . $daemonConfig->getId()); + $this->cache->remove('/daemon_configs'); + $this->cache->remove('/daemon_config_' . $daemonConfig->getName()); return $daemonConfig; } catch (Exception $e) { - $this->logger->error('Failed to unregister daemon config. Error: ' . $e->getMessage()); - return null; - } - } - - public function getDaemonConfig(int $daemonConfigId): ?DaemonConfig { - try { - $cacheKey = 'daemon_config_' . $daemonConfigId; -// $cached = $this->cache->get($cacheKey); -// if ($cached !== null) { -// return $cached instanceof DaemonConfig ? $cached : new DaemonConfig($cached); -// } - - $daemonConfig = $this->mapper->findById($daemonConfigId); - $this->cache->set($cacheKey, $daemonConfig, Application::CACHE_TTL); - return $daemonConfig; - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { - $this->logger->error('Failed to get daemon config. Error: ' . $e->getMessage()); + $this->logger->error('Failed to unregister daemon config. Error: ' . $e->getMessage(), ['exception' => $e]); return null; } } public function getRegisteredDaemonConfigs(): ?array { try { - $cacheKey = 'daemon_configs'; + $cacheKey = '/daemon_configs'; $cached = $this->cache->get($cacheKey); if ($cached !== null) { return array_map(function($cachedEntry) { @@ -118,27 +102,27 @@ public function getRegisteredDaemonConfigs(): ?array { } $daemonConfigs = $this->mapper->findAll(); - $this->cache->set($cacheKey, $daemonConfigs); + $this->cache->set($cacheKey, $daemonConfigs, self::CACHE_TTL); return $daemonConfigs; } catch (Exception $e) { - $this->logger->error('Failed to get registered daemon configs. Error: ' . $e->getMessage()); + $this->logger->error('Failed to get registered daemon configs. Error: ' . $e->getMessage(), ['exception' => $e]); return null; } } public function getDaemonConfigByName(string $name): ?DaemonConfig { try { - $cacheKey = 'daemon_config_' . $name; -// $cached = $this->cache->get($cacheKey); -// if ($cached !== null) { -// return $cached instanceof DaemonConfig ? $cached : new DaemonConfig($cached); -// } + $cacheKey = '/daemon_config_' . $name; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached instanceof DaemonConfig ? $cached : new DaemonConfig($cached); + } $daemonConfig = $this->mapper->findByName($name); - $this->cache->set($cacheKey, $daemonConfig, Application::CACHE_TTL); + $this->cache->set($cacheKey, $daemonConfig, self::CACHE_TTL); return $daemonConfig; } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { - $this->logger->error('Failed to get daemon config by name. Error: ' . $e->getMessage()); + $this->logger->error('Failed to get daemon config by name. Error: ' . $e->getMessage(), ['exception' => $e]); return null; } } diff --git a/lib/Service/ExAppApiScopeService.php b/lib/Service/ExAppApiScopeService.php index 4618a344..48d05f4e 100644 --- a/lib/Service/ExAppApiScopeService.php +++ b/lib/Service/ExAppApiScopeService.php @@ -43,6 +43,7 @@ use Psr\Log\LoggerInterface; class ExAppApiScopeService { + const CACHE_TTL = 60 * 60 * 24 * 7; // 1 week private LoggerInterface $logger; private ExAppApiScopeMapper $mapper; private ICache $cache; @@ -59,25 +60,35 @@ public function __construct( public function getExAppApiScopes(): array { try { - return $this->mapper->findAll(); + $cacheKey = '/all_api_scopes'; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return array_map(function ($cachedEntry) { + return $cachedEntry instanceof ExAppApiScope ? $cachedEntry : new ExAppApiScope($cachedEntry); + }, $cached); + } + + $apiScopes = $this->mapper->findAll(); + $this->cache->set($cacheKey, $apiScopes, self::CACHE_TTL); + return $apiScopes; } catch (Exception $e) { - $this->logger->error(sprintf('Failed to get all api scopes. Error: %s', $e->getMessage())); + $this->logger->error(sprintf('Failed to get all api scopes. Error: %s', $e->getMessage()), ['exception' => $e]); return []; } } public function getApiScopeByRoute(string $apiRoute): ?ExAppApiScope { try { - $cacheKey = 'api_scope_' . $apiRoute; -// $cached = $this->cache->get($cacheKey); -// if ($cached !== null) { -// return $cached instanceof ExAppApiScope ? $cached : new ExAppApiScope($cached); -// } + $cacheKey = '/api_scope_' . $apiRoute; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached instanceof ExAppApiScope ? $cached : new ExAppApiScope($cached); + } $apiScopes = $this->getExAppApiScopes(); foreach ($apiScopes as $apiScope) { if (str_starts_with($apiRoute, $apiScope->getApiRoute())) { - $this->cache->set($cacheKey, $apiScope, Application::CACHE_TTL); + $this->cache->set($cacheKey, $apiScope, self::CACHE_TTL); return $apiScope; } } @@ -111,6 +122,7 @@ public function registerInitScopes(): bool { ['api_route' => '/dav/', 'scope_group' => 3, 'name' => 'DAV'], ]; + $this->cache->clear('/all_api_scopes'); $registeredApiScopes = $this->getExAppApiScopes(); $registeredApiScopesRoutes = []; foreach ($registeredApiScopes as $registeredApiScope) { @@ -125,7 +137,7 @@ public function registerInitScopes(): bool { } return true; } catch (Exception $e) { - $this->logger->error('Failed to fill init API scopes: ' . $e->getMessage()); + $this->logger->error('Failed to fill init API scopes: ' . $e->getMessage(), ['exception' => $e]); return false; } } @@ -146,9 +158,11 @@ public function registerApiScope(string $apiRoute, int $scopeGroup, string $name $exAppApiScope = null; } $this->mapper->insertOrUpdate($apiScope); + $this->cache->remove('/api_scope_' . $apiRoute); + $this->cache->remove('/all_api_scopes'); return $apiScope; } catch (Exception $e) { - $this->logger->error('Failed to register API scope: ' . $e->getMessage()); + $this->logger->error('Failed to register API scope: ' . $e->getMessage(), ['exception' => $e]); return null; } } diff --git a/lib/Service/ExAppConfigService.php b/lib/Service/ExAppConfigService.php index 079f6223..2cceea0b 100644 --- a/lib/Service/ExAppConfigService.php +++ b/lib/Service/ExAppConfigService.php @@ -46,6 +46,7 @@ * App configuration (appconfig_ex) */ class ExAppConfigService { + const CACHE_TTL = 60 * 60; // 1 hour private LoggerInterface $logger; private ICache $cache; private ExAppConfigMapper $mapper; @@ -69,11 +70,11 @@ public function __construct( * @return array|null */ public function getAppConfigValues(string $appId, array $configKeys): ?array { - $cacheKey = $appId . ':' . json_encode($configKeys); -// $cached = $this->cache->get($cacheKey); -// if ($value !== null) { -// return $cached; -// } + $cacheKey = sprintf('/%s:%s', $appId, json_encode($configKeys)); + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached; + } try { $exAppConfigs = array_map(function (ExAppConfig $exAppConfig) { @@ -82,7 +83,7 @@ public function getAppConfigValues(string $appId, array $configKeys): ?array { 'configvalue' => $exAppConfig->getConfigvalue() ?? '', ]; }, $this->mapper->findByAppConfigKeys($appId, $configKeys)); - $this->cache->set($cacheKey, $exAppConfigs, Application::CACHE_TTL); + $this->cache->set($cacheKey, $exAppConfigs, self::CACHE_TTL); return $exAppConfigs; } catch (Exception) { return null; @@ -101,8 +102,10 @@ public function getAppConfigValues(string $appId, array $configKeys): ?array { */ public function setAppConfigValue(string $appId, string $configKey, mixed $configValue, int $sensitive = 0): ?ExAppConfig { try { - $appConfigEx = $this->mapper->findByAppConfigKey($appId, $configKey); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { +// $appConfigEx = $this->mapper->findByAppConfigKey($appId, $configKey); + $appConfigEx = $this->getAppConfig($appId, $configKey); + } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { + $this->logger->error('Error while getting app_config_ex value: ' . $e->getMessage(), ['exception' => $e]); $appConfigEx = null; } if ($appConfigEx === null) { @@ -113,19 +116,17 @@ public function setAppConfigValue(string $appId, string $configKey, mixed $confi 'configvalue' => $configValue ?? '', 'sensitive' => $sensitive, ])); + $cacheKey = sprintf('/%s:%s', $appId, $configKey); + $this->cache->set($cacheKey, $appConfigEx, self::CACHE_TTL); } catch (\Exception $e) { - $this->logger->error('Error while inserting app_config_ex value: ' . $e->getMessage()); + $this->logger->error('Error while inserting app_config_ex value: ' . $e->getMessage(), ['exception' => $e]); return null; } } else { $appConfigEx->setConfigvalue($configValue); $appConfigEx->setSensitive($sensitive); - try { - if ($this->updateAppConfigValue($appConfigEx) !== 1) { - $this->logger->error('Error while updating app_config_ex value'); - return null; - } - } catch (Exception) { + if ($this->updateAppConfigValue($appConfigEx) !== 1) { + $this->logger->error('Error while updating app_config_ex value'); return null; } } @@ -169,7 +170,15 @@ public function getAllAppConfig(string $appId): array { */ public function getAppConfig(mixed $appId, mixed $configKey): ?ExAppConfig { try { - return $this->mapper->findByAppConfigKey($appId, $configKey); + $cacheKey = sprintf('/%s:%s', $appId, $configKey); + $cashed= $this->cache->get($cacheKey); + if ($cashed !== null) { + return $cashed instanceof ExAppConfig ? $cashed : new ExAppConfig($cashed); + } + + $exAppConfig = $this->mapper->findByAppConfigKey($appId, $configKey); + $this->cache->set($cacheKey, $exAppConfig, self::CACHE_TTL); + return $exAppConfig; } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { return null; } @@ -182,7 +191,11 @@ public function getAppConfig(mixed $appId, mixed $configKey): ?ExAppConfig { */ public function updateAppConfigValue(ExAppConfig $exAppConfig): ?int { try { - return $this->mapper->updateAppConfigValue($exAppConfig); + $result = $this->mapper->updateAppConfigValue($exAppConfig); + if ($result === 1) { + $cacheKey = sprintf('/%s:%s', $exAppConfig->getAppid(), $exAppConfig->getConfigkey()); + $this->cache->set($cacheKey, $exAppConfig, self::CACHE_TTL); + } } catch (Exception) { return null; } diff --git a/lib/Service/ExAppPreferenceService.php b/lib/Service/ExAppPreferenceService.php index 7359f79e..c38481c6 100644 --- a/lib/Service/ExAppPreferenceService.php +++ b/lib/Service/ExAppPreferenceService.php @@ -74,7 +74,7 @@ public function setUserConfigValue(string $userId, string $appId, string $config 'configvalue' => $configValue ?? '', ])); } catch (Exception $e) { - $this->logger->error('Error while inserting new config value: ' . $e->getMessage()); + $this->logger->error('Error while inserting new config value: ' . $e->getMessage(), ['exception' => $e]); return null; } } else { @@ -86,7 +86,7 @@ public function setUserConfigValue(string $userId, string $appId, string $config } return $exAppPreference; } catch (Exception $e) { - $this->logger->error('Error while updating config value: ' . $e->getMessage()); + $this->logger->error('Error while updating config value: ' . $e->getMessage(), ['exception' => $e]); return null; } } @@ -100,11 +100,11 @@ public function setUserConfigValue(string $userId, string $appId, string $config */ public function getUserConfigValues(string $userId, string $appId, array $configKeys): ?array { try { - $cacheKey = $userId . $appId . implode('', $configKeys); -// $cached = $this->cache->get($cacheKey); -// if ($cached !== null) { -// return $cached; -// } + $cacheKey = sprintf('/%s/%s:%s', $userId, $appId, implode('', $configKeys)); + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached; + } return array_map(function (ExAppPreference $exAppPreference) { return [ diff --git a/lib/Service/ExAppScopesService.php b/lib/Service/ExAppScopesService.php new file mode 100644 index 00000000..87bcdbdd --- /dev/null +++ b/lib/Service/ExAppScopesService.php @@ -0,0 +1,128 @@ + + * + * @copyright Copyright (c) 2023 Alexander Piskun + * + * @author 2023 Andrey Borysenko + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\AppEcosystemV2\Service; + + +use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Db\ExApp; +use OCA\AppEcosystemV2\Db\ExAppScope; +use OCA\AppEcosystemV2\Db\ExAppScopeMapper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\DB\Exception; +use OCP\ICache; +use OCP\ICacheFactory; +use Psr\Log\LoggerInterface; + +class ExAppScopesService { + const CACHE_TTL = 60 * 60; // 1 hour + private LoggerInterface $logger; + private ExAppScopeMapper $mapper; + private ICache $cache; + + public function __construct( + LoggerInterface $logger, + ExAppScopeMapper $mapper, + ICacheFactory $cacheFactory, + ) { + $this->logger = $logger; + $this->mapper = $mapper; + $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_apps_scopes'); + } + + public function getExAppScopes(ExApp $exApp): array { + try { + $cacheKey = '/ex_app_scopes_' . $exApp->getAppid(); + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return array_map(function($cachedEntry) { + return $cachedEntry instanceof ExAppScope ? $cachedEntry : new ExAppScope($cachedEntry); + }, $cached); + } + + $exAppScopes = $this->mapper->findByAppid($exApp->getAppid()); + $this->cache->set($cacheKey, $exAppScopes, self::CACHE_TTL); + return $exAppScopes; + } catch (Exception $e) { + $this->logger->error(sprintf('Failed to get all api scopes. Error: %s', $e->getMessage()), ['exception' => $e]); + return []; + } + } + + public function setExAppScopeGroup(ExApp $exApp, int $scopeGroup) { + try { + return $this->mapper->findByAppidScope($exApp->getAppid(), $scopeGroup); + } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { + $exAppScope = new ExAppScope([ + 'appid' => $exApp->getAppid(), + 'scope_group' => $scopeGroup, + ]); + try { + return $this->mapper->insert($exAppScope); + } catch (\Exception $e) { + $this->logger->error(sprintf('Error while setting ExApp scope group: %s', $e->getMessage())); + return null; + } + } + } + + public function getByScope(ExApp $exApp, int $apiScope): ?ExAppScope { + try { + $cacheKey = '/ex_app_scopes_' . $exApp->getAppid() . '_' . $apiScope; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return $cached instanceof ExAppScope ? $cached : new ExAppScope($cached); + } + + $exAppScope = $this->mapper->findByAppidScope($exApp->getAppid(), $apiScope); + $this->cache->set($cacheKey, $exAppScope, self::CACHE_TTL); + return $exAppScope; + } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { + return null; + } + } + + public function passesScopeCheck(ExApp $exApp, int $apiScope): bool { + $exAppScope = $this->getByScope($exApp, $apiScope); + return $exAppScope instanceof ExAppScope; + } + + public function removeExAppScopes(ExApp $exApp): bool { + try { + $result = $this->mapper->deleteByAppid($exApp->getAppid()); + return $result > 0; + } catch (Exception $e) { + $this->logger->error(sprintf('Failed to delete all ExApp %s scopes. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); + return false; + } + } +} diff --git a/lib/Service/ExAppUsersService.php b/lib/Service/ExAppUsersService.php new file mode 100644 index 00000000..ba7d06c0 --- /dev/null +++ b/lib/Service/ExAppUsersService.php @@ -0,0 +1,143 @@ + + * + * @copyright Copyright (c) 2023 Alexander Piskun + * + * @author 2023 Andrey Borysenko + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\AppEcosystemV2\Service; + + +use OCA\AppEcosystemV2\Db\ExApp; +use OCA\AppEcosystemV2\Db\ExAppUser; +use OCP\DB\Exception; +use OCP\ICache; +use OCP\ICacheFactory; +use Psr\Log\LoggerInterface; + +use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Db\ExAppUserMapper; + +class ExAppUsersService { + const CACHE_TLL = 60 * 60; // 1 hour + private LoggerInterface $logger; + private ICache $cache; + private ExAppUserMapper $mapper; + + public function __construct( + LoggerInterface $logger, + ICacheFactory $cacheFactory, + ExAppUserMapper $mapper, + ) { + $this->logger = $logger; + $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_apps_users'); + $this->mapper = $mapper; + } + + /** + * @param ExApp $exApp + * + * @throws Exception + */ + public function setupSystemAppFlag(ExApp $exApp): void { + $this->mapper->insert(new ExAppUser([ + 'appid' => $exApp->getAppid(), + 'userid' => '', + ])); + } + + /** + * @param ExApp $exApp + * @param string|null $userId + * + * @throws Exception + */ + public function setupExAppUser(ExApp $exApp, ?string $userId): void { + if (!empty($userId)) { + if (!$this->exAppUserExists($exApp->getAppid(), $userId)) { + $this->mapper->insert(new ExAppUser([ + 'appid' => $exApp->getAppid(), + 'userid' => $userId, + ])); + } + } + } + + public function getExAppUsers(ExApp $exApp): array { + try { + $cacheKey = '/ex_apps_users_' . $exApp->getAppid(); + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + array_map(function($cashedEntry) { + return $cashedEntry instanceof ExAppUser ? $cashedEntry : new ExAppUser($cashedEntry); + }, $cached); + } + + $exAppUser = $this->mapper->findByAppid($exApp->getAppid()); + $this->cache->set($cacheKey, $exAppUser, self::CACHE_TLL); + return $exAppUser; + } catch (Exception $e) { + $this->logger->error(sprintf('Failed to get ex_app_users for ExApp %s. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); + return []; + } + } + + public function removeExAppUsers(ExApp $exApp): bool { + try { + $result = $this->mapper->deleteByAppid($exApp->getAppid()) !== 0; + if ($result) { + $this->cache->remove('/ex_apps_users_' . $exApp->getAppid()); + } + return $result; + } catch (Exception $e) { + $this->logger->error(sprintf('Failed to remove ex_app_users for appid %s. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); + return false; + } + } + + public function exAppUserExists(string $appId, string $userId): bool { + try { + $cacheKey = '/ex_apps_users_' . $appId . '_' . $userId; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + $exAppUsers = array_map(function($cashedEntry) { + return $cashedEntry instanceof ExAppUser ? $cashedEntry : new ExAppUser($cashedEntry); + }, $cached); + return !empty($exAppUsers) && $exAppUsers[0] instanceof ExAppUser; + } + + $exAppUsers = $this->mapper->findByAppidUserid($appId, $userId); + if (!empty($exAppUsers) && $exAppUsers[0] instanceof ExAppUser) { + $this->cache->set($cacheKey, $exAppUsers, self::CACHE_TLL); + return true; + } + } catch (Exception) { + return false; + } + return false; + } +} diff --git a/lib/Service/ExFilesActionsMenuService.php b/lib/Service/ExFilesActionsMenuService.php index 6129103d..0b007505 100644 --- a/lib/Service/ExFilesActionsMenuService.php +++ b/lib/Service/ExFilesActionsMenuService.php @@ -177,7 +177,7 @@ public function getExAppFileAction(string $appId, string $fileActionName): ?ExFi $fileAction = $this->mapper->findByAppIdName($appId, $fileActionName); $this->cache->set($cacheKey, $fileAction, Application::CACHE_TTL); } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { - $this->logger->error(sprintf('Failed to get file action %s for app: %s', $fileActionName, $appId)); + $this->logger->error(sprintf('Failed to get file action %s for app: %s. Error: %s', $fileActionName, $appId, $e->getMessage()), ['exception' => $e]); $fileAction = null; } return $fileAction; @@ -240,7 +240,7 @@ public function loadFileActionIcon(string $appId, string $exFileActionName): ?ar ]; } } catch (\Exception $e) { - $this->logger->error(sprintf('Failed to load file action icon %s for ExApp: %s with error: %s', $exFileActionName, $appId, $e->getMessage())); + $this->logger->error(sprintf('Failed to load file action icon %s for ExApp: %s with error: %s', $exFileActionName, $appId, $e->getMessage()), ['exception' => $e]); return null; } return null; From aea71cd46cc22ce23b0c3865d7ae10cbc21bfc5d Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 18 Jul 2023 13:41:19 +0300 Subject: [PATCH 02/29] fix missing return --- lib/Service/ExAppConfigService.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/Service/ExAppConfigService.php b/lib/Service/ExAppConfigService.php index 2cceea0b..45353a9d 100644 --- a/lib/Service/ExAppConfigService.php +++ b/lib/Service/ExAppConfigService.php @@ -101,13 +101,7 @@ public function getAppConfigValues(string $appId, array $configKeys): ?array { * @return ExAppConfig|null */ public function setAppConfigValue(string $appId, string $configKey, mixed $configValue, int $sensitive = 0): ?ExAppConfig { - try { -// $appConfigEx = $this->mapper->findByAppConfigKey($appId, $configKey); - $appConfigEx = $this->getAppConfig($appId, $configKey); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { - $this->logger->error('Error while getting app_config_ex value: ' . $e->getMessage(), ['exception' => $e]); - $appConfigEx = null; - } + $appConfigEx = $this->getAppConfig($appId, $configKey); if ($appConfigEx === null) { try { $appConfigEx = $this->mapper->insert(new ExAppConfig([ @@ -196,6 +190,7 @@ public function updateAppConfigValue(ExAppConfig $exAppConfig): ?int { $cacheKey = sprintf('/%s:%s', $exAppConfig->getAppid(), $exAppConfig->getConfigkey()); $this->cache->set($cacheKey, $exAppConfig, self::CACHE_TTL); } + return $result; } catch (Exception) { return null; } From 79adf39d8e0288c9832036ad2ff7e961d1f7c029 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 18 Jul 2023 21:34:42 +0300 Subject: [PATCH 03/29] Added lint, psalm, fixes. last_response_time->last_check_time --- .github/workflows/lint.yml | 143 ++ .gitignore | 1 + .php-cs-fixer.dist.php | 18 + composer.json | 11 +- composer.lock | 2280 +++++++++++++++-- lib/AppInfo/Application.php | 3 + lib/Command/ExApp/Deploy.php | 14 +- lib/Command/ExApp/Register.php | 2 +- lib/Command/ExAppConfig/GetConfig.php | 2 +- lib/DavPlugin.php | 3 + lib/Db/DaemonConfigMapper.php | 3 + lib/Db/ExApp.php | 14 +- lib/Db/ExAppApiScopeMapper.php | 3 + lib/Db/ExAppConfigMapper.php | 3 + lib/Db/ExAppMapper.php | 7 +- lib/Db/ExAppPreferenceMapper.php | 3 + lib/Db/ExAppScopeMapper.php | 3 + lib/Db/ExAppUserMapper.php | 3 + lib/Db/ExFilesActionsMenuMapper.php | 3 + ...yActions.php => AbstractDeployActions.php} | 2 +- lib/DeployActions/DockerActions.php | 4 +- lib/DeployActions/ManualActions.php | 4 +- lib/Listener/LoadFilesPluginListener.php | 3 + lib/Listener/SabrePluginAuthInitListener.php | 3 + lib/Migration/Version1000Date202305221555.php | 5 +- lib/Profiler/AEDataCollector.php | 6 +- lib/Service/AppEcosystemV2Service.php | 18 +- lib/Service/ExAppPreferenceService.php | 2 +- psalm.xml | 55 + tests/psalm-baseline.xml | 52 + 30 files changed, 2466 insertions(+), 207 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .php-cs-fixer.dist.php rename lib/Deploy/{DeployActions.php => AbstractDeployActions.php} (98%) create mode 100644 psalm.xml create mode 100644 tests/psalm-baseline.xml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..bb06f1dc --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,143 @@ +name: Lint + +on: + pull_request: + paths: + - 'appinfo/*.*' + - 'lib/**' +# - 'src/**' + - 'templates/*.*' + push: + paths: + - 'appinfo/*.*' + - 'lib/*.*' +# - 'src/*.*' + - 'templates/*.*' + workflow_dispatch: + +jobs: + xml-lint: + runs-on: ubuntu-latest + name: info.xml lint + + steps: + - uses: actions/checkout@v3 + - name: Download xml appinfo schema + run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd + + - name: Lint appinfo/info.xml + uses: ChristophWurst/xmllint-action@v1.1 + with: + xml-file: ./appinfo/info.xml + xml-schema-file: ./info.xsd + + php-lint: + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: ["8.1", "8.2"] + + name: php-lint + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up php ${{ matrix.php-versions }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + coverage: none + + - name: Lint + run: composer run lint + + php-cs: + runs-on: ubuntu-latest + name: php-cs + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up php ${{ matrix.php-versions }} + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + + - name: Install dependencies + run: composer i + + - name: Lint + run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 ) + + php-psalm-analysis: + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: ["8.1", "8.2"] + ocp-version: ['v26.0.3', 'v27.0.0', 'dev-master'] + name: php-psalm-analysis + + steps: + - uses: actions/checkout@v3 + - name: Set up php ${{ matrix.php-versions }} + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + + - name: Install dependencies + run: composer i + + - name: Install nextcloud/ocp + run: composer require --dev nextcloud/ocp:${{ matrix.ocp-version }} + + - name: Run coding standards check + run: composer run psalm + + php-security-analysis: + runs-on: ubuntu-latest + name: security analysis + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Psalm + uses: docker://vimeo/psalm-github-actions:4.30.0 + with: + security_analysis: true + composer_ignore_platform_reqs: false + report_file: results.sarif + + - name: Upload Security Analysis results to GitHub + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: results.sarif + +# js-eslint: +# runs-on: ubuntu-latest +# name: eslint +# +# steps: +# - uses: actions/checkout@v3 +# - name: Install dependencies +# run: npm ci +# +# - name: ESLint +# run: npm run lint + +# stylelint: +# runs-on: ubuntu-latest +# +# name: stylelint +# +# steps: +# - uses: actions/checkout@v3 +# - name: Install dependencies +# run: npm ci +# +# - name: Lint +# run: npm run stylelint diff --git a/.gitignore b/.gitignore index 0c15a561..5c0fac10 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ node_modules vendor _build certs +.php-cs-fixer.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 00000000..f7bbdd81 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,18 @@ +getFinder() + ->ignoreVCSIgnored(true) + ->notPath('build') + ->notPath('l10n') + ->notPath('src') + ->notPath('vendor') + ->in(__DIR__); +return $config; diff --git a/composer.json b/composer.json index ad92c28f..925bb59f 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,10 @@ ], "require-dev": { "nextcloud/ocp": "dev-master", - "roave/security-advisories": "dev-latest" + "roave/security-advisories": "dev-latest", + "psalm/phar": "^5.13", + "nextcloud/coding-standard": "^1.1", + "friendsofphp/php-cs-fixer": "^3.22" }, "scripts": { "lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l", @@ -27,11 +30,7 @@ "psalm:update-baseline": "psalm.phar --threads=1 --update-baseline", "psalm:update-baseline:force": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml", "psalm:clear": "psalm.phar --clear-cache && psalm.phar --clear-global-cache", - "psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType", - "test:unit": "phpunit -c tests/phpunit.unit.xml --fail-on-warning", - "test:unit:coverage": "XDEBUG_MODE=coverage phpunit -c tests/phpunit.unit.xml --fail-on-warning --coverage-html=tests/coverage/php/unit --coverage-clover=tests/coverage/php/unit/clover.xml", - "test:integration": "phpunit -c tests/phpunit.integration.xml --fail-on-warning", - "test:integration:coverage": "XDEBUG_MODE=coverage phpunit -c tests/phpunit.integration.xml --fail-on-warning --coverage-html=tests/coverage/php/integration --coverage-clover=tests/coverage/php/integration/clover.xml" + "psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType" }, "config": { "optimize-autoloader": true, diff --git a/composer.lock b/composer.lock index b688faf5..1a71b169 100644 --- a/composer.lock +++ b/composer.lock @@ -4,75 +4,110 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e5c78b28d347a8149cd455c7ab68db78", + "content-hash": "9df186851ce8e8327f39df7a904a7531", "packages": [], "packages-dev": [ { - "name": "nextcloud/ocp", - "version": "dev-master", + "name": "composer/pcre", + "version": "3.1.0", "source": { "type": "git", - "url": "https://github.com/nextcloud-deps/ocp.git", - "reference": "86dc48abdb8116bc4da048ac46b8094316d02ab5" + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/86dc48abdb8116bc4da048ac46b8094316d02ab5", - "reference": "86dc48abdb8116bc4da048ac46b8094316d02ab5", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", "shasum": "" }, "require": { - "php": "~8.0 || ~8.1 || ~8.2", - "psr/clock": "^1.0", - "psr/container": "^2.0.2", - "psr/event-dispatcher": "^1.0", - "psr/log": "^1.1.4" + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-master": "28.0.0-dev" + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "AGPL-3.0-or-later" + "MIT" ], "authors": [ { - "name": "Christoph Wurst", - "email": "christoph@winzerhof-wurst.at" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Composer package containing Nextcloud's public API (classes, interfaces)", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], "support": { - "issues": "https://github.com/nextcloud-deps/ocp/issues", - "source": "https://github.com/nextcloud-deps/ocp/tree/master" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.0" }, - "time": "2023-07-07T00:44:43+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-17T09:50:14+00:00" }, { - "name": "psr/clock", - "version": "1.0.0", + "name": "composer/semver", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/php-fig/clock.git", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0" + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Psr\\Clock\\": "src/" + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -81,51 +116,77 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "Common interface for reading the clock.", - "homepage": "https://github.com/php-fig/clock", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "clock", - "now", - "psr", - "psr-20", - "time" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "issues": "https://github.com/php-fig/clock/issues", - "source": "https://github.com/php-fig/clock/tree/1.0.0" + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" }, - "time": "2022-11-25T14:36:26+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "composer/xdebug-handler", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", "shasum": "" }, "require": { - "php": ">=7.4.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Composer\\XdebugHandler\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -134,51 +195,71 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Restarts a process without Xdebug.", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" }, - "time": "2021-11-05T16:47:00+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "doctrine/annotations", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/doctrine/annotations.git", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", "shasum": "" }, "require": { - "php": ">=7.2.0" + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", @@ -187,48 +268,178 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "Standard interfaces for event handling.", + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ - "events", - "psr", - "psr-14" + "annotations", + "docblock", + "parser" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.1" }, - "time": "2019-01-08T18:20:26+00:00" + "time": "2023-02-02T22:02:53+00:00" }, { - "name": "psr/log", - "version": "1.1.4", + "name": "doctrine/lexer", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "url": "https://github.com/doctrine/lexer.git", + "reference": "84a527db05647743d50373e0ec53a152f2cde568" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568", + "reference": "84a527db05647743d50373e0ec53a152f2cde568", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" } + ], + "time": "2022-12-15T16:57:16+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.22.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/92b019f6c8d79aa26349d0db7671d37440dc0ff3", + "reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3", + "shasum": "" }, + "require": { + "composer/semver": "^3.3", + "composer/xdebug-handler": "^3.0.3", + "doctrine/annotations": "^2", + "doctrine/lexer": "^2 || ^3", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "sebastian/diff": "^4.0 || ^5.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/options-resolver": "^5.4 || ^6.0", + "symfony/polyfill-mbstring": "^1.27", + "symfony/polyfill-php80": "^1.27", + "symfony/polyfill-php81": "^1.27", + "symfony/process": "^5.4 || ^6.0", + "symfony/stopwatch": "^5.4 || ^6.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3 || ^2.0", + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.0", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.5.3", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.16", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "phpunitgoodpractices/polyfill": "^1.6", + "phpunitgoodpractices/traits": "^1.9.2", + "symfony/phpunit-bridge": "^6.2.3", + "symfony/yaml": "^5.4 || ^6.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "PhpCsFixer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -237,72 +448,454 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "A tool to automatically fix PHP code style", "keywords": [ - "log", - "psr", - "psr-3" + "Static code analysis", + "fixer", + "standards", + "static analysis" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.22.0" }, - "time": "2021-05-03T11:20:27+00:00" + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2023-07-16T23:08:06+00:00" }, { - "name": "roave/security-advisories", - "version": "dev-latest", + "name": "nextcloud/coding-standard", + "version": "v1.1.0", "source": { "type": "git", - "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "a4feff1acc004e86181b0297fb5b1de5adb98f08" + "url": "https://github.com/nextcloud/coding-standard.git", + "reference": "20efa30db5240a5f078e03b04c685735a89dfc9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a4feff1acc004e86181b0297fb5b1de5adb98f08", - "reference": "a4feff1acc004e86181b0297fb5b1de5adb98f08", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/20efa30db5240a5f078e03b04c685735a89dfc9e", + "reference": "20efa30db5240a5f078e03b04c685735a89dfc9e", "shasum": "" }, - "conflict": { - "3f/pygmentize": "<1.2", - "admidio/admidio": "<4.2.9", - "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", - "aheinze/cockpit": "<=2.2.1", - "akaunting/akaunting": "<2.1.13", - "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", - "alextselegidis/easyappointments": "<1.5", - "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", - "amazing/media2click": ">=1,<1.3.3", - "amphp/artax": "<1.0.6|>=2,<2.0.6", - "amphp/http": "<1.0.1", - "amphp/http-client": ">=4,<4.4", - "anchorcms/anchor-cms": "<=0.12.7", - "andreapollastri/cipi": "<=3.1.15", - "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", - "apereo/phpcas": "<1.6", - "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6|>=2.6,<2.7.10|>=3,<3.0.12|>=3.1,<3.1.3", - "appwrite/server-ce": "<=1.2.1", - "arc/web": "<3", - "area17/twill": "<1.2.5|>=2,<2.5.3", - "artesaos/seotools": "<0.17.2", - "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", - "athlon1600/php-proxy": "<=5.1", - "athlon1600/php-proxy-app": "<=3", - "automad/automad": "<1.8", - "awesome-support/awesome-support": "<=6.0.7", - "aws/aws-sdk-php": ">=3,<3.2.1", - "azuracast/azuracast": "<0.18.3", - "backdrop/backdrop": "<1.24.2", - "badaso/core": "<2.7", - "bagisto/bagisto": "<0.1.5", - "barrelstrength/sprout-base-email": "<1.2.7", - "barrelstrength/sprout-forms": "<3.9", - "barryvdh/laravel-translation-manager": "<0.6.2", - "barzahlen/barzahlen-php": "<2.0.1", + "require": { + "friendsofphp/php-cs-fixer": "^3.9", + "php": "^7.3|^8.0" + }, + "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.0" + }, + "time": "2023-04-13T10:52:46+00:00" + }, + { + "name": "nextcloud/ocp", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/nextcloud-deps/ocp.git", + "reference": "86dc48abdb8116bc4da048ac46b8094316d02ab5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/86dc48abdb8116bc4da048ac46b8094316d02ab5", + "reference": "86dc48abdb8116bc4da048ac46b8094316d02ab5", + "shasum": "" + }, + "require": { + "php": "~8.0 || ~8.1 || ~8.2", + "psr/clock": "^1.0", + "psr/container": "^2.0.2", + "psr/event-dispatcher": "^1.0", + "psr/log": "^1.1.4" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "28.0.0-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "AGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Christoph Wurst", + "email": "christoph@winzerhof-wurst.at" + } + ], + "description": "Composer package containing Nextcloud's public API (classes, interfaces)", + "support": { + "issues": "https://github.com/nextcloud-deps/ocp/issues", + "source": "https://github.com/nextcloud-deps/ocp/tree/master" + }, + "time": "2023-07-07T00:44:43+00:00" + }, + { + "name": "psalm/phar", + "version": "5.13.1", + "source": { + "type": "git", + "url": "https://github.com/psalm/phar.git", + "reference": "2107e3e98b25b93cb366dc601d1190af102431a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/psalm/phar/zipball/2107e3e98b25b93cb366dc601d1190af102431a5", + "reference": "2107e3e98b25b93cb366dc601d1190af102431a5", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "vimeo/psalm": "*" + }, + "bin": [ + "psalm.phar" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer-based Psalm Phar", + "support": { + "issues": "https://github.com/psalm/phar/issues", + "source": "https://github.com/psalm/phar/tree/5.13.1" + }, + "time": "2023-06-27T17:20:37+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "roave/security-advisories", + "version": "dev-latest", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "a4feff1acc004e86181b0297fb5b1de5adb98f08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a4feff1acc004e86181b0297fb5b1de5adb98f08", + "reference": "a4feff1acc004e86181b0297fb5b1de5adb98f08", + "shasum": "" + }, + "conflict": { + "3f/pygmentize": "<1.2", + "admidio/admidio": "<4.2.9", + "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", + "aheinze/cockpit": "<=2.2.1", + "akaunting/akaunting": "<2.1.13", + "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", + "alextselegidis/easyappointments": "<1.5", + "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", + "amazing/media2click": ">=1,<1.3.3", + "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<1.0.1", + "amphp/http-client": ">=4,<4.4", + "anchorcms/anchor-cms": "<=0.12.7", + "andreapollastri/cipi": "<=3.1.15", + "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", + "apereo/phpcas": "<1.6", + "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6|>=2.6,<2.7.10|>=3,<3.0.12|>=3.1,<3.1.3", + "appwrite/server-ce": "<=1.2.1", + "arc/web": "<3", + "area17/twill": "<1.2.5|>=2,<2.5.3", + "artesaos/seotools": "<0.17.2", + "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", + "athlon1600/php-proxy": "<=5.1", + "athlon1600/php-proxy-app": "<=3", + "automad/automad": "<1.8", + "awesome-support/awesome-support": "<=6.0.7", + "aws/aws-sdk-php": ">=3,<3.2.1", + "azuracast/azuracast": "<0.18.3", + "backdrop/backdrop": "<1.24.2", + "badaso/core": "<2.7", + "bagisto/bagisto": "<0.1.5", + "barrelstrength/sprout-base-email": "<1.2.7", + "barrelstrength/sprout-forms": "<3.9", + "barryvdh/laravel-translation-manager": "<0.6.2", + "barzahlen/barzahlen-php": "<2.0.1", "baserproject/basercms": "<4.7.5", "bassjobsen/bootstrap-3-typeahead": ">4.0.2", "bigfork/silverstripe-form-capture": ">=3,<3.1.1", @@ -845,43 +1438,1400 @@ "zfr/zfr-oauth2-server-module": "<0.1.2", "zoujingli/thinkadmin": "<6.0.22" }, - "default-branch": true, - "type": "metapackage", + "default-branch": true, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + }, + { + "name": "Ilya Tribusean", + "email": "slash3b@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "keywords": [ + "dev" + ], + "support": { + "issues": "https://github.com/Roave/SecurityAdvisories/issues", + "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", + "type": "tidelift" + } + ], + "time": "2023-07-10T18:04:37+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-01T07:48:21+00:00" + }, + { + "name": "symfony/console", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", + "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-29T12:49:39+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", + "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-21T14:41:17+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-06-01T08:30:39+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2", + "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-02T01:25:41+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-12T14:21:09+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "role": "maintainer" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" }, { - "name": "Ilya Tribusean", - "email": "slash3b@gmail.com", - "role": "maintainer" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "dev" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/Roave/SecurityAdvisories/issues", - "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { - "url": "https://github.com/Ocramius", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-07-10T18:04:37+00:00" + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/process", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628", + "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-19T08:06:44+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-02-16T10:14:28+00:00" + }, + { + "name": "symfony/string", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f", + "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-21T21:06:29+00:00" } ], "aliases": [], diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index d6312ed4..6962ffae 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -63,6 +63,9 @@ public function __construct(array $urlParams = []) { $this->registerDavAuth(); } + /** + * @psalm-suppress UndefinedClass + */ public function register(IRegistrationContext $context): void { $context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadFilesPluginListener::class); $context->registerCapability(Capabilities::class); diff --git a/lib/Command/ExApp/Deploy.php b/lib/Command/ExApp/Deploy.php index f227b3bc..c4c27f3c 100644 --- a/lib/Command/ExApp/Deploy.php +++ b/lib/Command/ExApp/Deploy.php @@ -116,9 +116,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $deployConfig = $daemonConfig->getDeployConfig(); $imageParams = [ - 'image_src' => (string) $infoXml->xpath('ex-app/docker-install/registry')[0] ?? 'docker.io', - 'image_name' => (string) $infoXml->xpath('ex-app/docker-install/image')[0] ?? $appId, - 'image_tag' => (string) $infoXml->xpath('ex-app/docker-install/image-tag')[0] ?? 'latest', + 'image_src' => (string) ($infoXml->xpath('ex-app/docker-install/registry')[0] ?? 'docker.io'), + 'image_name' => (string) ($infoXml->xpath('ex-app/docker-install/image')[0] ?? $appId), + 'image_tag' => (string) ($infoXml->xpath('ex-app/docker-install/image-tag')[0] ?? 'latest'), ]; $containerParams = [ 'name' => $appId, @@ -132,10 +132,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'appid' => $appId, 'name' => (string) $infoXml->name, 'version' => (string) $infoXml->version, - 'protocol' => (string) $infoXml->xpath('ex-app/protocol')[0] ?? 'http', + 'protocol' => (string) ($infoXml->xpath('ex-app/protocol')[0] ?? 'http'), 'host' => $this->buildExAppHost($deployConfig), 'port' => $containerParams['port'], - 'system_app' => (bool) $infoXml->xpath('ex-app/system')[0] ?? false, + 'system_app' => (bool) ($infoXml->xpath('ex-app/system')[0] ?? false), ], $envParams, $deployConfig); $containerParams['env'] = $envs; @@ -159,8 +159,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'secret' => explode('=', $envs[1])[1], 'host' => $this->dockerActions->resolveDeployExAppHost($appId, $daemonConfig), 'port' => explode('=', $envs[7])[1], - 'protocol' => (string) $infoXml->xpath('ex-app/protocol')[0] ?? 'http', - 'system_app' => (bool) $infoXml->xpath('ex-app/system')[0] ?? false, + 'protocol' => (string) ($infoXml->xpath('ex-app/protocol')[0] ?? 'http'), + 'system_app' => (bool) ($infoXml->xpath('ex-app/system')[0] ?? false), ]; if ($this->heartbeatExApp($resultOutput)) { $output->writeln(json_encode($resultOutput, JSON_UNESCAPED_SLASHES)); diff --git a/lib/Command/ExApp/Register.php b/lib/Command/ExApp/Register.php index 2e6c39bb..1ab54a00 100644 --- a/lib/Command/ExApp/Register.php +++ b/lib/Command/ExApp/Register.php @@ -246,7 +246,7 @@ private function getRequestedExAppScopeGroups(OutputInterface $output, ExApp $ex return null; } if ($response->getStatusCode() === 200) { - $this->service->updateExAppLastResponseTime($exApp); + $this->service->updateExAppLastCheckTime($exApp); return json_decode($response->getBody(), true); } return null; diff --git a/lib/Command/ExAppConfig/GetConfig.php b/lib/Command/ExAppConfig/GetConfig.php index 6dffa5bf..efeb2206 100644 --- a/lib/Command/ExAppConfig/GetConfig.php +++ b/lib/Command/ExAppConfig/GetConfig.php @@ -65,7 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $appId = $input->getArgument('appid'); $exApp = $this->service->getExApp($appId); if ($exApp === null) { - $output->writeln(sprintf('ExApp % not found.', $appId)); + $output->writeln(sprintf('ExApp %s not found.', $appId)); return 1; } diff --git a/lib/DavPlugin.php b/lib/DavPlugin.php index 01cc0a6e..d3b24674 100644 --- a/lib/DavPlugin.php +++ b/lib/DavPlugin.php @@ -41,6 +41,9 @@ use OCA\DAV\Connector\Sabre\Auth; use OCP\ISession; +/** + * @psalm-suppress UndefinedClass, MissingDependency + */ class DavPlugin extends ServerPlugin { private IRequest $request; private ISession $session; diff --git a/lib/Db/DaemonConfigMapper.php b/lib/Db/DaemonConfigMapper.php index a0daf74a..38c9691d 100644 --- a/lib/Db/DaemonConfigMapper.php +++ b/lib/Db/DaemonConfigMapper.php @@ -39,6 +39,9 @@ use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +/** + * @template-extends QBMapper + */ class DaemonConfigMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_apps_daemons'); diff --git a/lib/Db/ExApp.php b/lib/Db/ExApp.php index 02e31fdf..f0a3b67a 100644 --- a/lib/Db/ExApp.php +++ b/lib/Db/ExApp.php @@ -50,7 +50,7 @@ * @method string getStatus() * @method int getEnabled() * @method int getCreatedTime() - * @method int getLastResponseTime() + * @method int getLastCheckTime() * @method void setAppid(string $appid) * @method void setVersion(string $version) * @method void setName(string $name) @@ -62,7 +62,7 @@ * @method void setStatus(string $status) * @method void setEnabled(int $enabled) * @method void setCreatedTime(int $createdTime) - * @method void setLastResponseTime(int $lastResponseTime) + * @method void setLastCheckTime(int $lastCheckTime) */ class ExApp extends Entity implements JsonSerializable { protected $appid; @@ -76,7 +76,7 @@ class ExApp extends Entity implements JsonSerializable { protected $status; protected $enabled; protected $createdTime; - protected $lastResponseTime; + protected $lastCheckTime; /** * @param array $params @@ -93,7 +93,7 @@ public function __construct(array $params = []) { $this->addType('status', 'string'); $this->addType('enabled', 'int'); $this->addType('createdTime', 'int'); - $this->addType('lastResponseTime', 'int'); + $this->addType('lastCheckTime', 'int'); if (isset($params['id'])) { $this->setId($params['id']); @@ -131,8 +131,8 @@ public function __construct(array $params = []) { if (isset($params['created_time'])) { $this->setCreatedTime($params['created_time']); } - if (isset($params['last_response_time'])) { - $this->setLastResponseTime($params['last_response_time']); + if (isset($params['last_check_time'])) { + $this->setLastCheckTime($params['last_check_time']); } } @@ -150,7 +150,7 @@ public function jsonSerialize(): array { 'status' => $this->getStatus(), 'enabled' => $this->getEnabled(), 'created_time' => $this->getCreatedTime(), - 'last_response_time' => $this->getLastResponseTime(), + 'last_check_time' => $this->getLastCheckTime(), ]; } } diff --git a/lib/Db/ExAppApiScopeMapper.php b/lib/Db/ExAppApiScopeMapper.php index fccdb7f5..74602e9c 100644 --- a/lib/Db/ExAppApiScopeMapper.php +++ b/lib/Db/ExAppApiScopeMapper.php @@ -37,6 +37,9 @@ use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +/** + * @template-extends QBMapper + */ class ExAppApiScopeMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_apps_api_scopes'); diff --git a/lib/Db/ExAppConfigMapper.php b/lib/Db/ExAppConfigMapper.php index 4b19f12d..a65b07fd 100644 --- a/lib/Db/ExAppConfigMapper.php +++ b/lib/Db/ExAppConfigMapper.php @@ -39,6 +39,9 @@ use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +/** + * @template-extends QBMapper + */ class ExAppConfigMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'appconfig_ex'); diff --git a/lib/Db/ExAppMapper.php b/lib/Db/ExAppMapper.php index 285bee2d..845eaa49 100644 --- a/lib/Db/ExAppMapper.php +++ b/lib/Db/ExAppMapper.php @@ -39,6 +39,9 @@ use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +/** + * @template-extends QBMapper + */ class ExAppMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_apps'); @@ -154,10 +157,10 @@ public function updateExAppEnabled(string $appId, bool $enabled): int { /** * @throws Exception */ - public function updateLastResponseTime(ExApp $exApp): int { + public function updateLastCheckTime(ExApp $exApp): int { $qb = $this->db->getQueryBuilder(); return $qb->update($this->tableName) - ->set('last_response_time', $qb->createNamedParameter($exApp->getLastResponseTime(), IQueryBuilder::PARAM_INT)) + ->set('last_check_time', $qb->createNamedParameter($exApp->getLastCheckTime(), IQueryBuilder::PARAM_INT)) ->where( $qb->expr()->eq('appid', $qb->createNamedParameter($exApp->getAppid())) )->executeStatement(); diff --git a/lib/Db/ExAppPreferenceMapper.php b/lib/Db/ExAppPreferenceMapper.php index 7894c402..791d9b81 100644 --- a/lib/Db/ExAppPreferenceMapper.php +++ b/lib/Db/ExAppPreferenceMapper.php @@ -38,6 +38,9 @@ use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +/** + * @template-extends QBMapper + */ class ExAppPreferenceMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'preferences_ex'); diff --git a/lib/Db/ExAppScopeMapper.php b/lib/Db/ExAppScopeMapper.php index 32befd39..9c2a9fd7 100644 --- a/lib/Db/ExAppScopeMapper.php +++ b/lib/Db/ExAppScopeMapper.php @@ -38,6 +38,9 @@ use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +/** + * @template-extends QBMapper + */ class ExAppScopeMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_apps_scopes'); diff --git a/lib/Db/ExAppUserMapper.php b/lib/Db/ExAppUserMapper.php index 6ff620cf..1774eeef 100644 --- a/lib/Db/ExAppUserMapper.php +++ b/lib/Db/ExAppUserMapper.php @@ -36,6 +36,9 @@ use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +/** + * @template-extends QBMapper + */ class ExAppUserMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_apps_users'); diff --git a/lib/Db/ExFilesActionsMenuMapper.php b/lib/Db/ExFilesActionsMenuMapper.php index 9cc78ee3..823b5c6b 100644 --- a/lib/Db/ExFilesActionsMenuMapper.php +++ b/lib/Db/ExFilesActionsMenuMapper.php @@ -38,6 +38,9 @@ use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +/** + * @template-extends QBMapper + */ class ExFilesActionsMenuMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_files_actions_menu'); diff --git a/lib/Deploy/DeployActions.php b/lib/Deploy/AbstractDeployActions.php similarity index 98% rename from lib/Deploy/DeployActions.php rename to lib/Deploy/AbstractDeployActions.php index 27285746..9d7f3dce 100644 --- a/lib/Deploy/DeployActions.php +++ b/lib/Deploy/AbstractDeployActions.php @@ -36,7 +36,7 @@ /** * Base class for AppEcosystemV2 ExApp deploy actions */ -abstract class DeployActions { +abstract class AbstractDeployActions { /** * Deploy type (action) id name * diff --git a/lib/DeployActions/DockerActions.php b/lib/DeployActions/DockerActions.php index 1b6e3461..6b12ee7b 100644 --- a/lib/DeployActions/DockerActions.php +++ b/lib/DeployActions/DockerActions.php @@ -39,9 +39,9 @@ use Psr\Log\LoggerInterface; use OCA\AppEcosystemV2\Db\DaemonConfig; -use OCA\AppEcosystemV2\Deploy\DeployActions; +use OCA\AppEcosystemV2\Deploy\AbstractDeployActions; -class DockerActions extends DeployActions { +class DockerActions extends AbstractDeployActions { public const DOCKER_API_VERSION = 'v1.41'; public const AE_REQUIRED_ENVS = [ 'AE_VERSION', diff --git a/lib/DeployActions/ManualActions.php b/lib/DeployActions/ManualActions.php index ab52c440..2627bfc3 100644 --- a/lib/DeployActions/ManualActions.php +++ b/lib/DeployActions/ManualActions.php @@ -32,12 +32,12 @@ namespace OCA\AppEcosystemV2\DeployActions; use OCA\AppEcosystemV2\Db\DaemonConfig; -use OCA\AppEcosystemV2\Deploy\DeployActions; +use OCA\AppEcosystemV2\Deploy\AbstractDeployActions; /** * Manual deploy actions for development. */ -class ManualActions extends DeployActions { +class ManualActions extends AbstractDeployActions { public function getAcceptsDeployId(): string { return 'manual-install'; } diff --git a/lib/Listener/LoadFilesPluginListener.php b/lib/Listener/LoadFilesPluginListener.php index c9b55c8e..58c6c99b 100644 --- a/lib/Listener/LoadFilesPluginListener.php +++ b/lib/Listener/LoadFilesPluginListener.php @@ -40,6 +40,9 @@ use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Service\ExFilesActionsMenuService; +/** + * @template-extends IEventListener + */ class LoadFilesPluginListener implements IEventListener { private IInitialState $initialState; private ExFilesActionsMenuService $service; diff --git a/lib/Listener/SabrePluginAuthInitListener.php b/lib/Listener/SabrePluginAuthInitListener.php index f8471ada..2c5fd279 100644 --- a/lib/Listener/SabrePluginAuthInitListener.php +++ b/lib/Listener/SabrePluginAuthInitListener.php @@ -37,6 +37,9 @@ use OCA\DAV\Events\SabrePluginAuthInitEvent; +/** + * @template-extends IEventListener + */ class SabrePluginAuthInitListener implements IEventListener { private AEAuthBackend $aeAuth; diff --git a/lib/Migration/Version1000Date202305221555.php b/lib/Migration/Version1000Date202305221555.php index 3d4a9ced..29d17dfa 100644 --- a/lib/Migration/Version1000Date202305221555.php +++ b/lib/Migration/Version1000Date202305221555.php @@ -31,6 +31,7 @@ namespace OCA\AppEcosystemV2\Migration; +use Closure; use OCP\DB\ISchemaWrapper; use OCP\DB\Types; use OCP\Migration\SimpleMigrationStep; @@ -44,7 +45,7 @@ class Version1000Date202305221555 extends SimpleMigrationStep { * * @return null|ISchemaWrapper */ - public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); @@ -127,7 +128,7 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op 'notnull' => true, 'unsigned' => true, ]); - $table->addColumn('last_response_time', Types::BIGINT, [ + $table->addColumn('last_check_time', Types::BIGINT, [ 'notnull' => true, 'unsigned' => true, ]); diff --git a/lib/Profiler/AEDataCollector.php b/lib/Profiler/AEDataCollector.php index 492df90a..ca4a3cc6 100644 --- a/lib/Profiler/AEDataCollector.php +++ b/lib/Profiler/AEDataCollector.php @@ -32,10 +32,14 @@ namespace OCA\AppEcosystemV2\Profiler; use OC\AppFramework\Http\Request; -use OCA\AppEcosystemV2\AppInfo\Application; use OCP\AppFramework\Http\Response; use OCP\DataCollector\AbstractDataCollector; +use OCA\AppEcosystemV2\AppInfo\Application; + +/** + * @psalm-suppress UndefinedClass + */ class AEDataCollector extends AbstractDataCollector { public function getName(): string { return Application::APP_ID; diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index 19061647..6c7ac788 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -147,7 +147,7 @@ public function registerExApp(string $appId, array $appData): ?ExApp { $exApp->setSecret($secret); } $exApp->setStatus(json_encode(['active' => true])); // TODO: Add status request to ExApp - $exApp->setLastResponseTime(time()); + $exApp->setLastCheckTime(time()); try { $cacheKey = '/exApp_' . $appId; $exApp = $this->exAppMapper->update($exApp); @@ -169,7 +169,7 @@ public function registerExApp(string $appId, array $appData): ?ExApp { 'secret' => $appData['secret'] !== '' ? $appData['secret'] : $this->random->generate(128), 'status' => json_encode(['active' => true]), // TODO: Add status request to ExApp 'created_time' => time(), - 'last_response_time' => time(), + 'last_check_time' => time(), ]); try { $cacheKey = '/exApp_' . $appId; @@ -236,7 +236,7 @@ public function enableExApp(ExApp $exApp): bool { if ($exAppEnabled instanceof IResponse) { $response = json_decode($exAppEnabled->getBody(), true); if (isset($response['error']) && strlen($response['error']) === 0) { - $this->updateExAppLastResponseTime($exApp); + $this->updateExAppLastCheckTime($exApp); } else { $this->logger->error(sprintf('Failed to enable ExApp %s. Error: %s', $exApp->getAppid(), $response['error'])); $this->disableExApp($exApp); @@ -282,7 +282,7 @@ public function disableExApp(ExApp $exApp): bool { if ($this->exAppMapper->updateExAppEnabled($exApp->getAppid(), false) !== 1) { return false; } - $this->updateExAppLastResponseTime($exApp); + $this->updateExAppLastCheckTime($exApp); $cacheKey = '/exApp_' . $exApp->getAppid(); $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); return true; @@ -306,7 +306,7 @@ public function getAppStatus(string $appId): ?array { if ($response instanceof IResponse && $response->getStatusCode() === 200) { $status = json_decode($response->getBody(), true); $exApp->setStatus($status); - $this->updateExAppLastResponseTime($exApp); + $this->updateExAppLastCheckTime($exApp); } return json_decode($exApp->getStatus(), true); } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { @@ -642,12 +642,12 @@ private function verifyDataHash(string $dataHash): bool { return $dataHash === $phpInputHash; } - public function updateExAppLastResponseTime(&$exApp): void { - $exApp->setLastResponseTime(time()); + public function updateExAppLastCheckTime(ExApp &$exApp): void { + $exApp->setLastCheckTime(time()); try { - $this->exAppMapper->updateLastResponseTime($exApp); + $this->exAppMapper->updateLastCheckTime($exApp); } catch (Exception $e) { - $this->logger->error(sprintf('Error while updating ExApp last response time for ExApp: %s. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); + $this->logger->error(sprintf('Error while updating ExApp last check time for ExApp: %s. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); } } diff --git a/lib/Service/ExAppPreferenceService.php b/lib/Service/ExAppPreferenceService.php index c38481c6..0f02db5d 100644 --- a/lib/Service/ExAppPreferenceService.php +++ b/lib/Service/ExAppPreferenceService.php @@ -96,7 +96,7 @@ public function setUserConfigValue(string $userId, string $appId, string $config * @param string $userId * @param string $appId * @param array $configKeys - * @return ExAppPreference[]|null + * @return array|null */ public function getUserConfigValues(string $userId, string $appId, array $configKeys): ?array { try { diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000..9ea6e4d5 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml new file mode 100644 index 00000000..0d4e8798 --- /dev/null +++ b/tests/psalm-baseline.xml @@ -0,0 +1,52 @@ + + + + + LoadFilesPluginListener::class + SabrePluginAuthInitListener::class + + + DavPlugin + + + + + $event + + + class LoadFilesPluginListener implements IEventListener { + + + IEventListener + + + IEventListener + + + LoadAdditionalScriptsEvent + + + + + $event + + + class SabrePluginAuthInitListener implements IEventListener { + + + IEventListener + + + AEAuthBackend + AEAuthBackend + + + IEventListener + + + + + Request + + + From 2bf9acc7cf01932a43cc517a6b1ba986623fcd5d Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 18 Jul 2023 22:43:21 +0300 Subject: [PATCH 04/29] updated jobs matrix --- .github/workflows/lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bb06f1dc..19b18818 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: ["8.1", "8.2"] + php-versions: ["8.1"] name: php-lint @@ -76,7 +76,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: ["8.1", "8.2"] + php-versions: ["8.1"] ocp-version: ['v26.0.3', 'v27.0.0', 'dev-master'] name: php-psalm-analysis From d73fac68266b11daa88714f061e86dceda4e346b Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 18 Jul 2023 22:54:32 +0300 Subject: [PATCH 05/29] ci fixes --- appinfo/info.xml | 2 +- lib/Service/AppEcosystemV2Service.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index f76213bd..a9291542 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -19,8 +19,8 @@ https://github.com/nextcloud/app_ecosystem_v2/issues https://github.com/nextcloud/app_ecosystem_v2.git - + diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index 6c7ac788..f4dd170f 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -661,7 +661,7 @@ public function getExAppsList(bool $extended = false): array { 'name' => $exApp->getName(), 'version' => $exApp->getVersion(), 'enabled' => $exApp->getEnabled(), - 'last_response_time' => $exApp->getLastResponseTime(), + 'last_response_time' => $exApp->getLastCheckTime(), 'system' => $this->exAppUsersService->exAppUserExists($exApp->getAppid(), ''), ]; }, $exApps); From e044ad0e9f7fe3c91afb3335ea42b475768b8f3d Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 18 Jul 2023 23:02:03 +0300 Subject: [PATCH 06/29] ci fixes --- .github/workflows/lint.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 19b18818..a320615b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -77,7 +77,6 @@ jobs: strategy: matrix: php-versions: ["8.1"] - ocp-version: ['v26.0.3', 'v27.0.0', 'dev-master'] name: php-psalm-analysis steps: @@ -85,15 +84,12 @@ jobs: - name: Set up php ${{ matrix.php-versions }} uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: ${{ matrix.php-versions }} coverage: none - name: Install dependencies run: composer i - - name: Install nextcloud/ocp - run: composer require --dev nextcloud/ocp:${{ matrix.ocp-version }} - - name: Run coding standards check run: composer run psalm From a0f176a3a58961822221c3c064ae613f4833594a Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Tue, 18 Jul 2023 23:19:55 +0300 Subject: [PATCH 07/29] ci fixes --- .github/workflows/lint.yml | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a320615b..48cc6560 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,11 +17,12 @@ on: jobs: xml-lint: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 name: info.xml lint steps: - uses: actions/checkout@v3 + - name: Download xml appinfo schema run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd @@ -32,16 +33,14 @@ jobs: xml-schema-file: ./info.xsd php-lint: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 + name: php-lint strategy: matrix: php-versions: ["8.1"] - name: php-lint - steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - name: Set up php ${{ matrix.php-versions }} uses: shivammathur/setup-php@v2 @@ -53,17 +52,19 @@ jobs: run: composer run lint php-cs: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 name: php-cs + strategy: + matrix: + php-versions: [ "8.1" ] steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - name: Set up php ${{ matrix.php-versions }} uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: ${{ matrix.php-versions }} coverage: none - name: Install dependencies @@ -73,7 +74,7 @@ jobs: run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 ) php-psalm-analysis: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: php-versions: ["8.1"] @@ -94,7 +95,7 @@ jobs: run: composer run psalm php-security-analysis: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 name: security analysis steps: - uses: actions/checkout@v3 @@ -114,7 +115,7 @@ jobs: sarif_file: results.sarif # js-eslint: -# runs-on: ubuntu-latest +# runs-on: ubuntu-22.04 # name: eslint # # steps: @@ -126,7 +127,7 @@ jobs: # run: npm run lint # stylelint: -# runs-on: ubuntu-latest +# runs-on: ubuntu-22.04 # # name: stylelint # From 3c7a94103830e66798b35010f574c4bafa65760c Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 01:10:24 +0300 Subject: [PATCH 08/29] exApp caching fixes --- .github/workflows/lint.yml | 2 +- composer.json | 10 ++++++++++ lib/Service/AppEcosystemV2Service.php | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 48cc6560..69e8e332 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -56,7 +56,7 @@ jobs: name: php-cs strategy: matrix: - php-versions: [ "8.1" ] + php-versions: ["8.1"] steps: - uses: actions/checkout@v3 diff --git a/composer.json b/composer.json index 925bb59f..59639443 100644 --- a/composer.json +++ b/composer.json @@ -38,5 +38,15 @@ "platform": { "php": "8.1" } + }, + "autoload": { + "psr-4": { + "OCA\\AppEcosystemV2\\": "lib/" + } + }, + "autoload-dev": { + "psr-4": { + "OCP\\": "vendor/nextcloud/ocp/OCP" + } } } diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index f4dd170f..5120d66c 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -249,6 +249,7 @@ public function enableExApp(ExApp $exApp): bool { } $cacheKey = '/exApp_' . $exApp->getAppid(); + $exApp->setEnabled(1); $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); return true; } @@ -284,6 +285,7 @@ public function disableExApp(ExApp $exApp): bool { } $this->updateExAppLastCheckTime($exApp); $cacheKey = '/exApp_' . $exApp->getAppid(); + $exApp->setEnabled(0); $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); return true; } catch (Exception $e) { From f502e362a340230d9c8977183c41ebe72979ecb7 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 01:36:50 +0300 Subject: [PATCH 09/29] delete query fixes --- lib/Db/ExAppScopeMapper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Db/ExAppScopeMapper.php b/lib/Db/ExAppScopeMapper.php index 9c2a9fd7..f71fbe21 100644 --- a/lib/Db/ExAppScopeMapper.php +++ b/lib/Db/ExAppScopeMapper.php @@ -98,8 +98,8 @@ public function findByAppidScope(string $appId, int $scopeGroup): ?ExAppScope { */ public function deleteByAppid(string $appId): int { $qb = $this->db->getQueryBuilder(); - $qb->delete()->from($this->tableName) - ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)); - return $qb->executeStatement(); + return $qb->delete($this->tableName) + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId), IQueryBuilder::PARAM_STR)) + ->executeStatement(); } } From 9d747799ea5c3c73c9673598336ba579972606c7 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 03:02:25 +0300 Subject: [PATCH 10/29] Added container healthcheck. Updated daemon list command --- lib/Command/Daemon/ListDaemons.php | 2 +- lib/Command/ExApp/Deploy.php | 5 +++++ lib/DeployActions/DockerActions.php | 18 ++++++++++++++++++ lib/Service/DaemonConfigService.php | 3 +++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/Command/Daemon/ListDaemons.php b/lib/Command/Daemon/ListDaemons.php index ffa9e558..4f01b886 100644 --- a/lib/Command/Daemon/ListDaemons.php +++ b/lib/Command/Daemon/ListDaemons.php @@ -69,7 +69,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln('Registered ExApp daemon configs:'); foreach ($daemonConfigs as $daemon) { - $output->writeln(sprintf('%s. %s [%s]: %s://%s', $daemon->getId(), $daemon->getDisplayName(), $daemon->getAcceptsDeployId(), $daemon->getProtocol(), $daemon->getHost())); + $output->writeln(sprintf('%s. %s - %s [%s]: %s://%s', $daemon->getId(), $daemon->getName(), $daemon->getDisplayName(), $daemon->getAcceptsDeployId(), $daemon->getProtocol(), $daemon->getHost())); } return 0; diff --git a/lib/Command/ExApp/Deploy.php b/lib/Command/ExApp/Deploy.php index c4c27f3c..0c79715b 100644 --- a/lib/Command/ExApp/Deploy.php +++ b/lib/Command/ExApp/Deploy.php @@ -150,6 +150,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if (!isset($startResult['error']) && isset($createResult['Id'])) { + if (!$this->dockerActions->healthcheckContainer($createResult['Id'], $daemonConfig)) { + $output->writeln(sprintf('ExApp %s deployment failed. Error: %s', $appId, 'Container healthcheck failed.')); + return 1; + } + // TODO: Remove resultOutput $resultOutput = [ 'appid' => $appId, diff --git a/lib/DeployActions/DockerActions.php b/lib/DeployActions/DockerActions.php index 6b12ee7b..e31003e9 100644 --- a/lib/DeployActions/DockerActions.php +++ b/lib/DeployActions/DockerActions.php @@ -251,6 +251,24 @@ public function resolveDeployExAppHost(string $appId, DaemonConfig $daemonConfig return $host; } + public function containerStateHealthy(array $containerInfo): bool { + return $containerInfo['State']['Health']['Status'] === 'healthy' && $containerInfo['State']['Status'] === 'running'; + } + + public function healthcheckContainer(string $containerId, DaemonConfig $daemonConfig): bool { + $attempts = 0; + $totalAttempts = 60; // ~60 seconds for container to initialize + while ($attempts < $totalAttempts) { + $containerInfo = $this->inspectContainer($this->buildDockerUrl($daemonConfig), $containerId); + if ($this->containerStateHealthy($containerInfo)) { + return true; + } + $attempts++; + sleep(1); + } + return false; + } + public function buildDockerUrl(DaemonConfig $daemonConfig): string { $dockerUrl = 'http://localhost'; if (in_array($daemonConfig->getProtocol(), ['http', 'https'])) { diff --git a/lib/Service/DaemonConfigService.php b/lib/Service/DaemonConfigService.php index c36e4a2e..cf93b2c1 100644 --- a/lib/Service/DaemonConfigService.php +++ b/lib/Service/DaemonConfigService.php @@ -91,6 +91,9 @@ public function unregisterDaemonConfig(DaemonConfig $daemonConfig): ?DaemonConfi } } + /** + * @return DaemonConfig[]|null + */ public function getRegisteredDaemonConfigs(): ?array { try { $cacheKey = '/daemon_configs'; From b66aa931555e31114fae1c3eb27c0a330dc51a85 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 13:54:09 +0300 Subject: [PATCH 11/29] Minor caches fixes --- lib/DeployActions/DockerActions.php | 2 +- lib/Service/AppEcosystemV2Service.php | 7 ++++--- lib/Service/ExAppConfigService.php | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/DeployActions/DockerActions.php b/lib/DeployActions/DockerActions.php index e31003e9..75efdaf0 100644 --- a/lib/DeployActions/DockerActions.php +++ b/lib/DeployActions/DockerActions.php @@ -252,7 +252,7 @@ public function resolveDeployExAppHost(string $appId, DaemonConfig $daemonConfig } public function containerStateHealthy(array $containerInfo): bool { - return $containerInfo['State']['Health']['Status'] === 'healthy' && $containerInfo['State']['Status'] === 'running'; + return $containerInfo['State']['Status'] === 'running'; } public function healthcheckContainer(string $containerId, DaemonConfig $daemonConfig): bool { diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index 5120d66c..f1ce6fd2 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -232,6 +232,10 @@ public function getExAppsByPort(int $port): array { public function enableExApp(ExApp $exApp): bool { try { if ($this->exAppMapper->updateExAppEnabled($exApp->getAppid(), true) === 1) { + $cacheKey = '/exApp_' . $exApp->getAppid(); + $exApp->setEnabled(1); + $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); + $exAppEnabled = $this->requestToExApp(null, null, $exApp, '/enabled?enabled=1', 'PUT'); if ($exAppEnabled instanceof IResponse) { $response = json_decode($exAppEnabled->getBody(), true); @@ -248,9 +252,6 @@ public function enableExApp(ExApp $exApp): bool { return false; } - $cacheKey = '/exApp_' . $exApp->getAppid(); - $exApp->setEnabled(1); - $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); return true; } } catch (Exception $e) { diff --git a/lib/Service/ExAppConfigService.php b/lib/Service/ExAppConfigService.php index 45353a9d..18f659e0 100644 --- a/lib/Service/ExAppConfigService.php +++ b/lib/Service/ExAppConfigService.php @@ -70,7 +70,7 @@ public function __construct( * @return array|null */ public function getAppConfigValues(string $appId, array $configKeys): ?array { - $cacheKey = sprintf('/%s:%s', $appId, json_encode($configKeys)); + $cacheKey = sprintf('/%s:%s', $appId, join(':', array_values($configKeys))); $cached = $this->cache->get($cacheKey); if ($cached !== null) { return $cached; From 5be6b59d5aa5a40789c4d2a285d8310b5cd66235 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 16:12:39 +0300 Subject: [PATCH 12/29] Added job with redis --- .github/workflows/tests-deploy.yml | 143 +++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/.github/workflows/tests-deploy.yml b/.github/workflows/tests-deploy.yml index 15c05890..a1f0c841 100644 --- a/.github/workflows/tests-deploy.yml +++ b/.github/workflows/tests-deploy.yml @@ -361,3 +361,146 @@ jobs: name: nc_docker_app_host_by_hostname_nextcloud.log path: nextcloud.log if-no-files-found: warn + + nc-host-app-docker-redis: + runs-on: ubuntu-22.04 + name: NC In Host (Redis) • ${{ matrix.server-version }} • 🐘${{ matrix.php-version }} + strategy: + fail-fast: false + matrix: + php-version: [ '8.1' ] + server-version: [ 'stable27' ] + include: + - server-version: "master" + php-version: "8.2" + + services: + postgres: + image: ghcr.io/nextcloud/continuous-integration-postgres-14:latest + ports: + - 4444:5432/tcp + env: + POSTGRES_USER: root + POSTGRES_PASSWORD: rootpassword + POSTGRES_DB: nextcloud + options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5 + redis: + image: redis + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 + + steps: + - name: Set app env + run: echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV + + - name: Checkout server + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + with: + submodules: true + repository: nextcloud/server + ref: ${{ matrix.server-version }} + + - name: Checkout AppEcosystemV2 + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + with: + path: apps/${{ env.APP_NAME }} + + - name: Set up php ${{ matrix.php-versions }} + uses: shivammathur/setup-php@4bd44f22a98a19e0950cbad5f31095157cc9621b # v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql + coverage: none + ini-file: development + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check composer file existence + id: check_composer + uses: andstor/file-existence-action@20b4d2e596410855db8f9ca21e96fbe18e12930b # v2 + with: + files: apps/${{ env.APP_NAME }}/composer.json + + - name: Set up dependencies + if: steps.check_composer.outputs.files_exists == 'true' + working-directory: apps/${{ env.APP_NAME }} + run: composer i + + - name: Set up Nextcloud + env: + DB_PORT: 4444 + REDIS_HOST: redis + REDIS_PORT: 6379 + run: | + mkdir data + ./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 \ + --database-port=$DB_PORT --database-user=root --database-pass=rootpassword \ + --admin-user admin --admin-pass admin + ./occ config:system:set loglevel --value=0 --type=integer + ./occ config:system:set debug --value=true --type=boolean + ./occ config:system:set allow_local_remote_servers --value true + + ./occ config:system:set memcache.local --value '\\OC\\Memcache\\Redis' + ./occ config:system:set memcache.distributed --value '\\OC\\Memcache\\Redis' + ./occ config:system:set memcache.locking --value '\\OC\\Memcache\\Redis' + ./occ config:system:set redis host --value ${{ env.REDIS_HOST }} + ./occ config:system:set redis port --value ${{ env.REDIS_PORT }} + + ./occ app:enable --force ${{ env.APP_NAME }} + patch -p 1 -i apps/${{ env.APP_NAME }}/base_php.patch + + - name: Test deploy + run: | + php -S 127.0.0.1:8080 & + ./occ app_ecosystem_v2:daemon:register docker_local_sock Docker docker-install unix-socket /var/run/docker.sock http://127.0.0.1:8080/index.php + ./occ app_ecosystem_v2:app:deploy app_python_skeleton docker_local_sock \ + --info-xml https://raw.githubusercontent.com/cloud-py-api/py_app_v2-skeleton/main/appinfo/info.xml + ./occ app_ecosystem_v2:app:register app_python_skeleton docker_local_sock + ./occ app_ecosystem_v2:app:enable app_python_skeleton + ./occ app_ecosystem_v2:app:disable app_python_skeleton + ./occ app_ecosystem_v2:app:unregister app_python_skeleton --silent + ./occ app_ecosystem_v2:daemon:unregister docker_local_sock + + - name: Check logs + run: | + grep -q 'Hello from app_python_skeleton :)' data/nextcloud.log || error + grep -q 'Bye bye from app_python_skeleton :(' data/nextcloud.log || error + + - name: Save container ingo & logs + if: always() + run: | + docker inspect app_python_skeleton | json_pp > container.json + docker logs app_python_skeleton > container.log 2>&1 + + - name: Check redis keys + run: | + redis-cli keys '*app_ecosystem_v2*' || error + + - name: Upload Container info + if: always() + uses: actions/upload-artifact@v3 + with: + name: nc_host_app_docker_redis_${{ matrix.server-version }}_${{ matrix.php-version }}_container.json + path: container.json + if-no-files-found: warn + + - name: Upload Container logs + if: always() + uses: actions/upload-artifact@v3 + with: + name: nc_host_app_docker_redis_${{ matrix.server-version }}_${{ matrix.php-version }}_container.log + path: container.log + if-no-files-found: warn + + - name: Upload NC logs + if: always() + uses: actions/upload-artifact@v3 + with: + name: nc_host_app_docker_redis_${{ matrix.server-version }}_${{ matrix.php-version }}_nextcloud.log + path: data/nextcloud.log + if-no-files-found: warn From 62bf6f5b4951a0440408f15134ea5b8dfe8e64c7 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 16:16:20 +0300 Subject: [PATCH 13/29] Added php redis module --- .github/workflows/tests-deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests-deploy.yml b/.github/workflows/tests-deploy.yml index a1f0c841..1bfc8fc8 100644 --- a/.github/workflows/tests-deploy.yml +++ b/.github/workflows/tests-deploy.yml @@ -364,7 +364,7 @@ jobs: nc-host-app-docker-redis: runs-on: ubuntu-22.04 - name: NC In Host (Redis) • ${{ matrix.server-version }} • 🐘${{ matrix.php-version }} + name: NC In Host(Redis) • ${{ matrix.server-version }} • 🐘${{ matrix.php-version }} strategy: fail-fast: false matrix: @@ -414,7 +414,7 @@ jobs: uses: shivammathur/setup-php@4bd44f22a98a19e0950cbad5f31095157cc9621b # v2 with: php-version: ${{ matrix.php-versions }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql + extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql, redis coverage: none ini-file: development env: From c708477f13f0f43460f028f8c89b44b5cf5bb4f2 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 16:26:10 +0300 Subject: [PATCH 14/29] fix redis config setup --- .github/workflows/tests-deploy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests-deploy.yml b/.github/workflows/tests-deploy.yml index 1bfc8fc8..a1c3204a 100644 --- a/.github/workflows/tests-deploy.yml +++ b/.github/workflows/tests-deploy.yml @@ -445,9 +445,9 @@ jobs: ./occ config:system:set debug --value=true --type=boolean ./occ config:system:set allow_local_remote_servers --value true - ./occ config:system:set memcache.local --value '\\OC\\Memcache\\Redis' - ./occ config:system:set memcache.distributed --value '\\OC\\Memcache\\Redis' - ./occ config:system:set memcache.locking --value '\\OC\\Memcache\\Redis' + ./occ config:system:set memcache.local --value "\\OC\\Memcache\\Redis" + ./occ config:system:set memcache.distributed --value "\\OC\\Memcache\\Redis" + ./occ config:system:set memcache.locking --value "\\OC\\Memcache\\Redis" ./occ config:system:set redis host --value ${{ env.REDIS_HOST }} ./occ config:system:set redis port --value ${{ env.REDIS_PORT }} From e64b61ecef9aaca9d0f0586c11d1134e6adcb5bc Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 16:28:27 +0300 Subject: [PATCH 15/29] fix redis service host --- .github/workflows/tests-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests-deploy.yml b/.github/workflows/tests-deploy.yml index a1c3204a..994aeee7 100644 --- a/.github/workflows/tests-deploy.yml +++ b/.github/workflows/tests-deploy.yml @@ -434,7 +434,7 @@ jobs: - name: Set up Nextcloud env: DB_PORT: 4444 - REDIS_HOST: redis + REDIS_HOST: localhost REDIS_PORT: 6379 run: | mkdir data From 8dc479339719bfc19e6dda3bc37973b8117b4a80 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 16:34:00 +0300 Subject: [PATCH 16/29] fix check of redis keys --- .github/workflows/tests-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests-deploy.yml b/.github/workflows/tests-deploy.yml index 994aeee7..b9345c39 100644 --- a/.github/workflows/tests-deploy.yml +++ b/.github/workflows/tests-deploy.yml @@ -479,7 +479,7 @@ jobs: - name: Check redis keys run: | - redis-cli keys '*app_ecosystem_v2*' || error + docker exec redis redis-cli keys '*app_ecosystem_v2*' || error - name: Upload Container info if: always() From af8e5d8b47497a99551c1fb8c620170d82f6a2ed Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Wed, 19 Jul 2023 18:56:29 +0300 Subject: [PATCH 17/29] php-cs fixes --- appinfo/routes.php | 20 +++++----- composer.json | 6 +-- composer.lock | 15 ++++---- lib/AEAuthBackend.php | 3 +- lib/AppInfo/Application.php | 23 ++++++------ lib/Capabilities.php | 1 + lib/Command/Daemon/ListDaemons.php | 10 ++--- lib/Command/Daemon/RegisterDaemon.php | 4 +- lib/Command/Daemon/UnregisterDaemon.php | 4 +- lib/Command/ExApp/Deploy.php | 4 +- lib/Command/ExApp/Disable.php | 4 +- lib/Command/ExApp/Enable.php | 4 +- lib/Command/ExApp/ListExApps.php | 3 +- lib/Command/ExApp/Register.php | 27 +++++++------- lib/Command/ExApp/Scopes/ListScopes.php | 2 +- lib/Command/ExApp/Unregister.php | 5 +-- lib/Command/ExApp/Users/ListUsers.php | 2 +- lib/Command/ExAppConfig/DeleteConfig.php | 4 +- lib/Command/ExAppConfig/GetConfig.php | 4 +- lib/Command/ExAppConfig/ListConfig.php | 4 +- lib/Command/ExAppConfig/SetConfig.php | 4 +- lib/Command/Scopes/ListApiScopes.php | 2 +- lib/Controller/AppConfigController.php | 15 ++++---- lib/Controller/ConfigController.php | 8 ++-- lib/Controller/ExAppController.php | 12 +++--- ...ApiController.php => OCSApiController.php} | 27 ++++++-------- lib/Controller/PreferencesController.php | 10 ++--- lib/DavPlugin.php | 10 ++--- lib/Db/DaemonConfigMapper.php | 5 +-- lib/Db/ExApp.php | 2 +- lib/Db/ExAppApiScopeMapper.php | 2 +- lib/Db/ExAppConfigMapper.php | 4 +- lib/Db/ExAppMapper.php | 4 +- lib/Db/ExAppPreferenceMapper.php | 4 +- lib/Db/ExAppScopeMapper.php | 2 +- lib/Db/ExAppUserMapper.php | 4 +- lib/Db/ExFilesActionsMenuMapper.php | 4 +- lib/Deploy/AbstractDeployActions.php | 8 ++-- lib/DeployActions/DockerActions.php | 12 +++--- lib/Exceptions/AEAuthNotValidException.php | 2 +- lib/Listener/LoadFilesPluginListener.php | 12 +++--- lib/Listener/SabrePluginAuthInitListener.php | 4 +- lib/Middleware/AppEcosystemAuthMiddleware.php | 11 +++--- lib/Migration/DataInitializationStep.php | 4 +- lib/Migration/Version1000Date202305221555.php | 2 +- lib/Profiler/AEDataCollector.php | 7 ++-- lib/PublicCapabilities.php | 1 + lib/Service/AppEcosystemV2Service.php | 37 +++++++++---------- lib/Service/DaemonConfigService.php | 16 ++++---- lib/Service/ExAppApiScopeService.php | 34 ++++++++--------- lib/Service/ExAppConfigService.php | 14 +++---- lib/Service/ExAppPreferenceService.php | 1 + lib/Service/ExAppScopesService.php | 6 +-- lib/Service/ExAppUsersService.php | 13 +++---- lib/Service/ExFilesActionsMenuService.php | 35 +++++++++--------- lib/Settings/Admin.php | 7 +++- lib/Settings/AdminSection.php | 6 ++- 57 files changed, 248 insertions(+), 252 deletions(-) rename lib/Controller/{OcsApiController.php => OCSApiController.php} (98%) diff --git a/appinfo/routes.php b/appinfo/routes.php index a96e643e..08b6ce8e 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -36,24 +36,24 @@ ], 'ocs' => [ // Logging - ['name' => 'ocsApi#log', 'url' => '/api/v1/log', 'verb' => 'POST'], + ['name' => 'OCSApi#log', 'url' => '/api/v1/log', 'verb' => 'POST'], // ExAppUsers - ['name' => 'ocsApi#getExAppUsers', 'url' => '/api/v1/users', 'verb' => 'GET'], + ['name' => 'OCSApi#getExAppUsers', 'url' => '/api/v1/users', 'verb' => 'GET'], // ExApps ['name' => 'exApp#getExApps', 'url' => '/api/v1/ex-app/all', 'verb' => 'GET'], // Ex Apps registration - ['name' => 'ocsApi#registerExternalApp', 'url' => '/api/v1/ex-app', 'verb' => 'POST'], - ['name' => 'ocsApi#unregisterExternalApp', 'url' => '/api/v1/ex-app', 'verb' => 'DELETE'], - ['name' => 'ocsApi#getAppStatus', 'url' => '/api/v1/ex-app/{appId}/status', 'verb' => 'GET'], + ['name' => 'OCSApi#registerExternalApp', 'url' => '/api/v1/ex-app', 'verb' => 'POST'], + ['name' => 'OCSApi#unregisterExternalApp', 'url' => '/api/v1/ex-app', 'verb' => 'DELETE'], + ['name' => 'OCSApi#getAppStatus', 'url' => '/api/v1/ex-app/{appId}/status', 'verb' => 'GET'], // File Actions Menu - ['name' => 'ocsApi#registerFileActionMenu', 'url' => '/api/v1/files/actions/menu', 'verb' => 'POST'], - ['name' => 'ocsApi#unregisterFileActionMenu', 'url' => '/api/v1/files/actions/menu', 'verb' => 'DELETE'], - ['name' => 'ocsApi#handleFileAction', 'url' => '/api/v1/files/action', 'verb' => 'POST'], - ['name' => 'ocsApi#loadFileActionIcon', 'url' => '/api/v1/files/action/icon', 'verb' => 'GET'], + ['name' => 'OCSApi#registerFileActionMenu', 'url' => '/api/v1/files/actions/menu', 'verb' => 'POST'], + ['name' => 'OCSApi#unregisterFileActionMenu', 'url' => '/api/v1/files/actions/menu', 'verb' => 'DELETE'], + ['name' => 'OCSApi#handleFileAction', 'url' => '/api/v1/files/action', 'verb' => 'POST'], + ['name' => 'OCSApi#loadFileActionIcon', 'url' => '/api/v1/files/action/icon', 'verb' => 'GET'], // appconfig_ex (app configuration) ['name' => 'appConfig#setAppConfigValue', 'url' => '/api/v1/ex-app/config', 'verb' => 'POST'], @@ -66,7 +66,7 @@ ['name' => 'preferences#deleteUserConfigValues', 'url' => '/api/v1/ex-app/preference', 'verb' => 'DELETE'], // api_scopes - ['name' => 'ocsApi#registerApiScope', 'url' => '/api/v1/ex-app/scopes', 'verb' => 'POST'], + ['name' => 'OCSApi#registerApiScope', 'url' => '/api/v1/ex-app/scopes', 'verb' => 'POST'], // TODO: Implement Notifications, SearchProvider, BackgroundJob, SettingsPage, SettingsSection, EventListener, DashboardWidget, Capabilities diff --git a/composer.json b/composer.json index 59639443..36c1de0b 100644 --- a/composer.json +++ b/composer.json @@ -20,12 +20,12 @@ "roave/security-advisories": "dev-latest", "psalm/phar": "^5.13", "nextcloud/coding-standard": "^1.1", - "friendsofphp/php-cs-fixer": "^3.22" + "friendsofphp/php-cs-fixer": "3.16" }, "scripts": { "lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l", - "cs:check": "php-cs-fixer fix --dry-run --diff", - "cs:fix": "php-cs-fixer fix", + "cs:check": "php-cs-fixer fix ./lib --dry-run --diff", + "cs:fix": "php-cs-fixer fix ./lib", "psalm": "psalm.phar --threads=1", "psalm:update-baseline": "psalm.phar --threads=1 --update-baseline", "psalm:update-baseline:force": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml", diff --git a/composer.lock b/composer.lock index 1a71b169..cc048153 100644 --- a/composer.lock +++ b/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": "9df186851ce8e8327f39df7a904a7531", + "content-hash": "6ce3c08a6611be06a4362ac506b919e3", "packages": [], "packages-dev": [ { @@ -380,16 +380,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.22.0", + "version": "v3.16.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3" + "reference": "d40f9436e1c448d309fa995ab9c14c5c7a96f2dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/92b019f6c8d79aa26349d0db7671d37440dc0ff3", - "reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/d40f9436e1c448d309fa995ab9c14c5c7a96f2dc", + "reference": "d40f9436e1c448d309fa995ab9c14c5c7a96f2dc", "shasum": "" }, "require": { @@ -413,7 +413,6 @@ "symfony/stopwatch": "^5.4 || ^6.0" }, "require-dev": { - "facile-it/paraunit": "^1.3 || ^2.0", "justinrainbow/json-schema": "^5.2", "keradus/cli-executor": "^2.0", "mikey179/vfsstream": "^1.6.11", @@ -465,7 +464,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.22.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.16.0" }, "funding": [ { @@ -473,7 +472,7 @@ "type": "github" } ], - "time": "2023-07-16T23:08:06+00:00" + "time": "2023-04-02T19:30:06+00:00" }, { "name": "nextcloud/coding-standard", diff --git a/lib/AEAuthBackend.php b/lib/AEAuthBackend.php index 46dc7e08..c4c939d2 100644 --- a/lib/AEAuthBackend.php +++ b/lib/AEAuthBackend.php @@ -32,9 +32,10 @@ namespace OCA\AppEcosystemV2; use OCA\AppEcosystemV2\AppInfo\Application; + use OCA\DAV\Connector\Sabre\Auth; -use OCP\ISession; use OCP\IRequest; +use OCP\ISession; use Sabre\DAV\Auth\Backend\BackendInterface; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 6962ffae..6f349174 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -31,31 +31,31 @@ namespace OCA\AppEcosystemV2\AppInfo; +use OCA\AppEcosystemV2\Capabilities; +use OCA\AppEcosystemV2\DavPlugin; +use OCA\AppEcosystemV2\Listener\LoadFilesPluginListener; +use OCA\AppEcosystemV2\Listener\SabrePluginAuthInitListener; +use OCA\AppEcosystemV2\Middleware\AppEcosystemAuthMiddleware; use OCA\AppEcosystemV2\Profiler\AEDataCollector; use OCA\AppEcosystemV2\PublicCapabilities; + +use OCA\DAV\Events\SabrePluginAuthInitEvent; +use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\AppFramework\App; -use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; +use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\Profiler\IProfiler; use OCP\SabrePluginEvent; -use OCA\Files\Event\LoadAdditionalScriptsEvent; - -use OCA\AppEcosystemV2\Capabilities; -use OCA\AppEcosystemV2\DavPlugin; -use OCA\AppEcosystemV2\Listener\LoadFilesPluginListener; -use OCA\AppEcosystemV2\Listener\SabrePluginAuthInitListener; -use OCA\AppEcosystemV2\Middleware\AppEcosystemAuthMiddleware; -use OCA\DAV\Events\SabrePluginAuthInitEvent; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; class Application extends App implements IBootstrap { public const APP_ID = 'app_ecosystem_v2'; - public const CACHE_TTL = 60 * 60; - public const ICON_CACHE_TTL = 60 * 60 *24; + public const CACHE_TTL = 60 * 60; // 1 hour + public const ICON_CACHE_TTL = 60 * 60 * 24; // 1 day public function __construct(array $urlParams = []) { parent::__construct(self::APP_ID, $urlParams); @@ -94,4 +94,3 @@ public function registerDavAuth(): void { }); } } - diff --git a/lib/Capabilities.php b/lib/Capabilities.php index a053fa75..fed963f0 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -35,6 +35,7 @@ use OCA\AppEcosystemV2\Db\ExAppScope; use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\ExAppScopesService; + use OCP\App\IAppManager; use OCP\Capabilities\ICapability; use OCP\IConfig; diff --git a/lib/Command/Daemon/ListDaemons.php b/lib/Command/Daemon/ListDaemons.php index 4f01b886..a0ecc1cb 100644 --- a/lib/Command/Daemon/ListDaemons.php +++ b/lib/Command/Daemon/ListDaemons.php @@ -31,22 +31,18 @@ namespace OCA\AppEcosystemV2\Command\Daemon; +use OCA\AppEcosystemV2\Service\DaemonConfigService; + use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; -use OCA\AppEcosystemV2\Service\DaemonConfigService; - class ListDaemons extends Command { - private AppEcosystemV2Service $service; private DaemonConfigService $daemonConfigService; - public function __construct(AppEcosystemV2Service $service, DaemonConfigService $daemonConfigService) { + public function __construct(DaemonConfigService $daemonConfigService) { parent::__construct(); - $this->service = $service; $this->daemonConfigService = $daemonConfigService; } diff --git a/lib/Command/Daemon/RegisterDaemon.php b/lib/Command/Daemon/RegisterDaemon.php index 6a076bd9..3a1907f6 100644 --- a/lib/Command/Daemon/RegisterDaemon.php +++ b/lib/Command/Daemon/RegisterDaemon.php @@ -31,14 +31,14 @@ namespace OCA\AppEcosystemV2\Command\Daemon; +use OCA\AppEcosystemV2\Service\DaemonConfigService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\DaemonConfigService; - class RegisterDaemon extends Command { private DaemonConfigService $daemonConfigService; diff --git a/lib/Command/Daemon/UnregisterDaemon.php b/lib/Command/Daemon/UnregisterDaemon.php index ad940f39..946fba77 100644 --- a/lib/Command/Daemon/UnregisterDaemon.php +++ b/lib/Command/Daemon/UnregisterDaemon.php @@ -31,13 +31,13 @@ namespace OCA\AppEcosystemV2\Command\Daemon; +use OCA\AppEcosystemV2\Service\DaemonConfigService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\DaemonConfigService; - class UnregisterDaemon extends Command { private DaemonConfigService $daemonConfigService; diff --git a/lib/Command/ExApp/Deploy.php b/lib/Command/ExApp/Deploy.php index 0c79715b..2dd3a9da 100644 --- a/lib/Command/ExApp/Deploy.php +++ b/lib/Command/ExApp/Deploy.php @@ -34,6 +34,8 @@ use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\DeployActions\DockerActions; use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; +use OCA\AppEcosystemV2\Service\DaemonConfigService; + use OCP\App\IAppManager; use OCP\IURLGenerator; use OCP\Security\ISecureRandom; @@ -43,8 +45,6 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\DaemonConfigService; - class Deploy extends Command { private AppEcosystemV2Service $service; private DaemonConfigService $daemonConfigService; diff --git a/lib/Command/ExApp/Disable.php b/lib/Command/ExApp/Disable.php index fa690ad5..17db8936 100644 --- a/lib/Command/ExApp/Disable.php +++ b/lib/Command/ExApp/Disable.php @@ -31,13 +31,13 @@ namespace OCA\AppEcosystemV2\Command\ExApp; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; - class Disable extends Command { private AppEcosystemV2Service $service; diff --git a/lib/Command/ExApp/Enable.php b/lib/Command/ExApp/Enable.php index 49f15f89..786bda0d 100644 --- a/lib/Command/ExApp/Enable.php +++ b/lib/Command/ExApp/Enable.php @@ -31,13 +31,13 @@ namespace OCA\AppEcosystemV2\Command\ExApp; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; - class Enable extends Command { private AppEcosystemV2Service $service; diff --git a/lib/Command/ExApp/ListExApps.php b/lib/Command/ExApp/ListExApps.php index 7ddf386b..a8192542 100644 --- a/lib/Command/ExApp/ListExApps.php +++ b/lib/Command/ExApp/ListExApps.php @@ -31,11 +31,12 @@ namespace OCA\AppEcosystemV2\Command\ExApp; +use OCA\AppEcosystemV2\Db\ExAppMapper; + use OCP\DB\Exception; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Db\ExAppMapper; class ListExApps extends Command { private ExAppMapper $mapper; diff --git a/lib/Command/ExApp/Register.php b/lib/Command/ExApp/Register.php index 1ab54a00..5eb7dea9 100644 --- a/lib/Command/ExApp/Register.php +++ b/lib/Command/ExApp/Register.php @@ -31,6 +31,15 @@ namespace OCA\AppEcosystemV2\Command\ExApp; +use OCA\AppEcosystemV2\Db\ExApp; +use OCA\AppEcosystemV2\DeployActions\DockerActions; +use OCA\AppEcosystemV2\DeployActions\ManualActions; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; +use OCA\AppEcosystemV2\Service\DaemonConfigService; +use OCA\AppEcosystemV2\Service\ExAppApiScopeService; +use OCA\AppEcosystemV2\Service\ExAppScopesService; +use OCA\AppEcosystemV2\Service\ExAppUsersService; + use OCP\DB\Exception; use OCP\Http\Client\IResponse; use Symfony\Component\Console\Command\Command; @@ -41,15 +50,6 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ConfirmationQuestion; -use OCA\AppEcosystemV2\Db\ExApp; -use OCA\AppEcosystemV2\DeployActions\DockerActions; -use OCA\AppEcosystemV2\DeployActions\ManualActions; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; -use OCA\AppEcosystemV2\Service\DaemonConfigService; -use OCA\AppEcosystemV2\Service\ExAppApiScopeService; -use OCA\AppEcosystemV2\Service\ExAppScopesService; -use OCA\AppEcosystemV2\Service\ExAppUsersService; - class Register extends Command { private AppEcosystemV2Service $service; private DaemonConfigService $daemonConfigService; @@ -111,7 +111,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // TODO: Make this dynamic if ($daemonConfig->getAcceptsDeployId() == $this->dockerActions->getAcceptsDeployId()) { $exAppInfo = $this->dockerActions->loadExAppInfo($appId, $daemonConfig); - } else if ($daemonConfig->getAcceptsDeployId() == $this->manualActions->getAcceptsDeployId()) { + } elseif ($daemonConfig->getAcceptsDeployId() == $this->manualActions->getAcceptsDeployId()) { $exAppJson = $input->getOption('json-info'); if ($exAppJson === null) { $output->writeln('ExApp JSON is required for manual deploy.'); @@ -150,8 +150,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (filter_var($exAppInfo['system_app'], FILTER_VALIDATE_BOOLEAN)) { try { $this->exAppUsersService->setupSystemAppFlag($exApp); - } - catch (Exception $e) { + } catch (Exception $e) { $output->writeln(sprintf('Error while setting app system flag: %s', $e->getMessage())); return 1; } @@ -184,7 +183,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // Prompt to approve optional ExApp scopes if ($confirmRequiredScopes && count($requestedExAppScopeGroups['optional']) > 0) { $output->writeln(sprintf('ExApp %s requested optional scopes: %s', $appId, implode(', ', - $this->exAppApiScopeService->mapScopeGroupsToNames($requestedExAppScopeGroups['optional'])))); + $this->exAppApiScopeService->mapScopeGroupsToNames($requestedExAppScopeGroups['optional'])))); $question = new ConfirmationQuestion('Do you want to approve it? [y/N] ', false); $confirmOptionalScopes = $helper->ask($input, $output, $question); } @@ -235,7 +234,7 @@ private function registerExAppScopes($output, ExApp $exApp, array $requestedExAp } if (count($registeredScopeGroups) > 0) { $output->writeln(sprintf('ExApp %s %s scope groups successfully set: %s', $exApp->getAppid(), $scopeType, implode(', ', - $this->exAppApiScopeService->mapScopeGroupsToNames($registeredScopeGroups)))); + $this->exAppApiScopeService->mapScopeGroupsToNames($registeredScopeGroups)))); } } diff --git a/lib/Command/ExApp/Scopes/ListScopes.php b/lib/Command/ExApp/Scopes/ListScopes.php index 7d282bad..01ad5274 100644 --- a/lib/Command/ExApp/Scopes/ListScopes.php +++ b/lib/Command/ExApp/Scopes/ListScopes.php @@ -34,12 +34,12 @@ use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\ExAppApiScopeService; use OCA\AppEcosystemV2\Service\ExAppScopesService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - class ListScopes extends Command { private AppEcosystemV2Service $service; private ExAppScopesService $exAppScopeService; diff --git a/lib/Command/ExApp/Unregister.php b/lib/Command/ExApp/Unregister.php index 7e0ddd45..72eac4d5 100644 --- a/lib/Command/ExApp/Unregister.php +++ b/lib/Command/ExApp/Unregister.php @@ -31,15 +31,14 @@ namespace OCA\AppEcosystemV2\Command\ExApp; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; -use OCA\AppEcosystemV2\Service\ExAppScopesService; - class Unregister extends Command { private AppEcosystemV2Service $service; diff --git a/lib/Command/ExApp/Users/ListUsers.php b/lib/Command/ExApp/Users/ListUsers.php index 2e6ffc77..6db97bae 100644 --- a/lib/Command/ExApp/Users/ListUsers.php +++ b/lib/Command/ExApp/Users/ListUsers.php @@ -33,12 +33,12 @@ use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\ExAppUsersService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - class ListUsers extends Command { private AppEcosystemV2Service $service; private ExAppUsersService $exAppUserService; diff --git a/lib/Command/ExAppConfig/DeleteConfig.php b/lib/Command/ExAppConfig/DeleteConfig.php index 9c6c9491..1c0bb5ad 100644 --- a/lib/Command/ExAppConfig/DeleteConfig.php +++ b/lib/Command/ExAppConfig/DeleteConfig.php @@ -31,14 +31,14 @@ namespace OCA\AppEcosystemV2\Command\ExAppConfig; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\ExAppConfigService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; - class DeleteConfig extends Command { private AppEcosystemV2Service $service; private ExAppConfigService $exAppConfigService; diff --git a/lib/Command/ExAppConfig/GetConfig.php b/lib/Command/ExAppConfig/GetConfig.php index efeb2206..8764323b 100644 --- a/lib/Command/ExAppConfig/GetConfig.php +++ b/lib/Command/ExAppConfig/GetConfig.php @@ -31,15 +31,15 @@ namespace OCA\AppEcosystemV2\Command\ExAppConfig; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\ExAppConfigService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; - class GetConfig extends Command { private AppEcosystemV2Service $service; private ExAppConfigService $exAppConfigService; diff --git a/lib/Command/ExAppConfig/ListConfig.php b/lib/Command/ExAppConfig/ListConfig.php index 595c90a4..787e73a9 100644 --- a/lib/Command/ExAppConfig/ListConfig.php +++ b/lib/Command/ExAppConfig/ListConfig.php @@ -31,15 +31,15 @@ namespace OCA\AppEcosystemV2\Command\ExAppConfig; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\ExAppConfigService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; - class ListConfig extends Command { private AppEcosystemV2Service $service; private ExAppConfigService $appConfigService; diff --git a/lib/Command/ExAppConfig/SetConfig.php b/lib/Command/ExAppConfig/SetConfig.php index c0b15372..550b4a02 100644 --- a/lib/Command/ExAppConfig/SetConfig.php +++ b/lib/Command/ExAppConfig/SetConfig.php @@ -31,15 +31,15 @@ namespace OCA\AppEcosystemV2\Command\ExAppConfig; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\ExAppConfigService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; - class SetConfig extends Command { private AppEcosystemV2Service $service; private ExAppConfigService $exAppConfigService; diff --git a/lib/Command/Scopes/ListApiScopes.php b/lib/Command/Scopes/ListApiScopes.php index fcaea33f..956e4b69 100644 --- a/lib/Command/Scopes/ListApiScopes.php +++ b/lib/Command/Scopes/ListApiScopes.php @@ -32,11 +32,11 @@ namespace OCA\AppEcosystemV2\Command\Scopes; use OCA\AppEcosystemV2\Service\ExAppApiScopeService; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - class ListApiScopes extends Command { private ExAppApiScopeService $service; diff --git a/lib/Controller/AppConfigController.php b/lib/Controller/AppConfigController.php index 58732f4b..e31f564a 100644 --- a/lib/Controller/AppConfigController.php +++ b/lib/Controller/AppConfigController.php @@ -31,20 +31,19 @@ namespace OCA\AppEcosystemV2\Controller; - +use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Attribute\AppEcosystemAuth; use OCA\AppEcosystemV2\Db\ExAppConfig; +use OCA\AppEcosystemV2\Service\ExAppConfigService; + +use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; -use OCP\AppFramework\OCS\OCSBadRequestException; -use OCP\IRequest; -use OCP\AppFramework\OCSController; -use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; - -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Service\ExAppConfigService; +use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCSController; +use OCP\IRequest; class AppConfigController extends OCSController { private ExAppConfigService $exAppConfigService; diff --git a/lib/Controller/ConfigController.php b/lib/Controller/ConfigController.php index b4fea80c..88b78ea0 100644 --- a/lib/Controller/ConfigController.php +++ b/lib/Controller/ConfigController.php @@ -31,12 +31,12 @@ namespace OCA\AppEcosystemV2\Controller; +use OCA\AppEcosystemV2\AppInfo\Application; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\DataResponse; use OCP\IConfig; use OCP\IRequest; -use OCP\AppFramework\Http\DataResponse; -use OCP\AppFramework\Controller; - -use OCA\AppEcosystemV2\AppInfo\Application; class ConfigController extends Controller { private IConfig $config; diff --git a/lib/Controller/ExAppController.php b/lib/Controller/ExAppController.php index 4e70b11d..15ab360b 100644 --- a/lib/Controller/ExAppController.php +++ b/lib/Controller/ExAppController.php @@ -31,15 +31,15 @@ namespace OCA\AppEcosystemV2\Controller; -use OCP\AppFramework\Http\Attribute\NoCSRFRequired; -use OCP\IRequest; -use OCP\AppFramework\OCSController; +use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; + use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; - -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; +use OCP\AppFramework\OCSController; +use OCP\IRequest; class ExAppController extends OCSController { private AppEcosystemV2Service $service; diff --git a/lib/Controller/OcsApiController.php b/lib/Controller/OCSApiController.php similarity index 98% rename from lib/Controller/OcsApiController.php rename to lib/Controller/OCSApiController.php index b2333fb1..3b96806f 100644 --- a/lib/Controller/OcsApiController.php +++ b/lib/Controller/OCSApiController.php @@ -31,27 +31,24 @@ namespace OCA\AppEcosystemV2\Controller; +use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Attribute\AppEcosystemAuth; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; use OCA\AppEcosystemV2\Service\ExAppApiScopeService; +use OCA\AppEcosystemV2\Service\ExFilesActionsMenuService; + +use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; -use OCP\EventDispatcher\IEventDispatcher; -use Psr\Log\LoggerInterface; - -use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCSController; - use OCP\IL10N; use OCP\IRequest; -use OCP\AppFramework\Http\DataDisplayResponse; -use OCP\AppFramework\Http\Response; -use OCP\AppFramework\OCS\OCSBadRequestException; - -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; -use OCA\AppEcosystemV2\Service\ExFilesActionsMenuService; +use Psr\Log\LoggerInterface; class OCSApiController extends OCSController { private LoggerInterface $logger; @@ -136,7 +133,7 @@ public function log( */ #[NoCSRFRequired] public function registerExternalApp(string $appId, array $appData, string $format = 'json'): Response { -// TODO: Sync logic with OCC command + // TODO: Sync logic with OCC command $result = $this->service->registerExApp($appId, $appData); return $this->buildResponse(new DataResponse([ 'success' => $result !== null, @@ -154,7 +151,7 @@ public function registerExternalApp(string $appId, array $appData, string $forma */ #[NoCSRFRequired] public function unregisterExternalApp(string $appId, string $format = 'json'): Response { -// TODO: Sync logic with OCC command + // TODO: Sync logic with OCC command $deletedExApp = $this->service->unregisterExApp($appId); if ($deletedExApp === null) { return $this->buildResponse(new DataResponse([ @@ -184,7 +181,7 @@ public function getAppStatus(string $appId, string $format = 'json'): Response { return $this->buildResponse(new DataResponse([ 'success' => $appStatus !== null, 'appStatus' => [ - 'appId'=> $appId, + 'appId' => $appId, 'status' => $appStatus, ], ], Http::STATUS_OK), $format); diff --git a/lib/Controller/PreferencesController.php b/lib/Controller/PreferencesController.php index bd33b6a3..0dda75c6 100644 --- a/lib/Controller/PreferencesController.php +++ b/lib/Controller/PreferencesController.php @@ -31,18 +31,18 @@ namespace OCA\AppEcosystemV2\Controller; +use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Attribute\AppEcosystemAuth; use OCA\AppEcosystemV2\Db\ExAppPreference; +use OCA\AppEcosystemV2\Service\ExAppPreferenceService; + use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; -use OCP\AppFramework\OCS\OCSBadRequestException; -use OCP\AppFramework\OCSController; - -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Service\ExAppPreferenceService; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; +use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCSController; use OCP\IRequest; use OCP\IUserSession; diff --git a/lib/DavPlugin.php b/lib/DavPlugin.php index d3b24674..18475597 100644 --- a/lib/DavPlugin.php +++ b/lib/DavPlugin.php @@ -31,15 +31,15 @@ namespace OCA\AppEcosystemV2; +use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; + +use OCA\DAV\Connector\Sabre\Auth; +use OCP\IRequest; +use OCP\ISession; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; -use OCP\IRequest; - -use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; -use OCA\DAV\Connector\Sabre\Auth; -use OCP\ISession; /** * @psalm-suppress UndefinedClass, MissingDependency diff --git a/lib/Db/DaemonConfigMapper.php b/lib/Db/DaemonConfigMapper.php index 38c9691d..d1e2fdcd 100644 --- a/lib/Db/DaemonConfigMapper.php +++ b/lib/Db/DaemonConfigMapper.php @@ -32,12 +32,11 @@ namespace OCA\AppEcosystemV2\Db; use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\DB\Exception; -use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; /** * @template-extends QBMapper diff --git a/lib/Db/ExApp.php b/lib/Db/ExApp.php index f0a3b67a..0c0ed658 100644 --- a/lib/Db/ExApp.php +++ b/lib/Db/ExApp.php @@ -141,7 +141,7 @@ public function jsonSerialize(): array { 'id' => $this->getId(), 'appid' => $this->getAppid(), 'version' => $this->getVersion(), - 'name'=> $this->getName(), + 'name' => $this->getName(), 'daemon_config_name' => $this->getDaemonConfigName(), 'protocol' => $this->getProtocol(), 'host' => $this->getHost(), diff --git a/lib/Db/ExAppApiScopeMapper.php b/lib/Db/ExAppApiScopeMapper.php index 74602e9c..eaa2eca0 100644 --- a/lib/Db/ExAppApiScopeMapper.php +++ b/lib/Db/ExAppApiScopeMapper.php @@ -33,9 +33,9 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\AppFramework\Db\QBMapper; use OCP\DB\Exception; use OCP\IDBConnection; -use OCP\AppFramework\Db\QBMapper; /** * @template-extends QBMapper diff --git a/lib/Db/ExAppConfigMapper.php b/lib/Db/ExAppConfigMapper.php index a65b07fd..260a3434 100644 --- a/lib/Db/ExAppConfigMapper.php +++ b/lib/Db/ExAppConfigMapper.php @@ -34,10 +34,10 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\DB\Exception; -use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; /** * @template-extends QBMapper diff --git a/lib/Db/ExAppMapper.php b/lib/Db/ExAppMapper.php index 845eaa49..fc29bfdc 100644 --- a/lib/Db/ExAppMapper.php +++ b/lib/Db/ExAppMapper.php @@ -34,10 +34,10 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\DB\Exception; -use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; /** * @template-extends QBMapper diff --git a/lib/Db/ExAppPreferenceMapper.php b/lib/Db/ExAppPreferenceMapper.php index 791d9b81..472adf45 100644 --- a/lib/Db/ExAppPreferenceMapper.php +++ b/lib/Db/ExAppPreferenceMapper.php @@ -33,10 +33,10 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\DB\Exception; -use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; /** * @template-extends QBMapper diff --git a/lib/Db/ExAppScopeMapper.php b/lib/Db/ExAppScopeMapper.php index f71fbe21..26f8ae88 100644 --- a/lib/Db/ExAppScopeMapper.php +++ b/lib/Db/ExAppScopeMapper.php @@ -33,10 +33,10 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\AppFramework\Db\QBMapper; use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; -use OCP\AppFramework\Db\QBMapper; /** * @template-extends QBMapper diff --git a/lib/Db/ExAppUserMapper.php b/lib/Db/ExAppUserMapper.php index 1774eeef..aeab341c 100644 --- a/lib/Db/ExAppUserMapper.php +++ b/lib/Db/ExAppUserMapper.php @@ -31,10 +31,10 @@ namespace OCA\AppEcosystemV2\Db; -use OCP\DB\Exception; -use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; /** * @template-extends QBMapper diff --git a/lib/Db/ExFilesActionsMenuMapper.php b/lib/Db/ExFilesActionsMenuMapper.php index 823b5c6b..46c80dcf 100644 --- a/lib/Db/ExFilesActionsMenuMapper.php +++ b/lib/Db/ExFilesActionsMenuMapper.php @@ -33,10 +33,10 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\DB\Exception; -use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; /** * @template-extends QBMapper diff --git a/lib/Deploy/AbstractDeployActions.php b/lib/Deploy/AbstractDeployActions.php index 9d7f3dce..0785a4bd 100644 --- a/lib/Deploy/AbstractDeployActions.php +++ b/lib/Deploy/AbstractDeployActions.php @@ -42,7 +42,7 @@ abstract class AbstractDeployActions { * * @return string */ - public abstract function getAcceptsDeployId(): string; + abstract public function getAcceptsDeployId(): string; /** * Deploy ExApp to the target daemon @@ -52,7 +52,7 @@ public abstract function getAcceptsDeployId(): string; * * @return mixed */ - public abstract function deployExApp(DaemonConfig $daemonConfig, array $params = []): mixed; + abstract public function deployExApp(DaemonConfig $daemonConfig, array $params = []): mixed; /** * Load ExApp information from the target daemon. @@ -63,7 +63,7 @@ public abstract function deployExApp(DaemonConfig $daemonConfig, array $params = * * @return array required data for ExApp registration */ - public abstract function loadExAppInfo(string $appId, DaemonConfig $daemonConfig, array $params = []): array; + abstract public function loadExAppInfo(string $appId, DaemonConfig $daemonConfig, array $params = []): array; /** * Resolve ExApp host depending on daemon configuration. @@ -75,5 +75,5 @@ public abstract function loadExAppInfo(string $appId, DaemonConfig $daemonConfig * * @return string */ - public abstract function resolveDeployExAppHost(string $appId, DaemonConfig $daemonConfig, array $params = []): string; + abstract public function resolveDeployExAppHost(string $appId, DaemonConfig $daemonConfig, array $params = []): string; } diff --git a/lib/DeployActions/DockerActions.php b/lib/DeployActions/DockerActions.php index 75efdaf0..01564e77 100644 --- a/lib/DeployActions/DockerActions.php +++ b/lib/DeployActions/DockerActions.php @@ -31,16 +31,16 @@ namespace OCA\AppEcosystemV2\DeployActions; - use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; -use OCP\ICertificateManager; -use OCP\IConfig; -use Psr\Log\LoggerInterface; use OCA\AppEcosystemV2\Db\DaemonConfig; use OCA\AppEcosystemV2\Deploy\AbstractDeployActions; +use OCP\ICertificateManager; +use OCP\IConfig; +use Psr\Log\LoggerInterface; + class DockerActions extends AbstractDeployActions { public const DOCKER_API_VERSION = 'v1.41'; public const AE_REQUIRED_ENVS = [ @@ -285,7 +285,7 @@ public function initGuzzleClient(DaemonConfig $daemonConfig): void { CURLOPT_UNIX_SOCKET_PATH => $daemonConfig->getHost(), ], ]; - } else if (in_array($daemonConfig->getProtocol(), ['http', 'https'])) { + } elseif (in_array($daemonConfig->getProtocol(), ['http', 'https'])) { $guzzleParams = $this->setupCerts($guzzleParams, $daemonConfig->getDeployConfig()); } $this->guzzleClient = new Client($guzzleParams); @@ -299,7 +299,7 @@ public function initGuzzleClient(DaemonConfig $daemonConfig): void { */ private function setupCerts(array $guzzleParams, array $deployConfig): array { if (!$this->config->getSystemValueBool('installed', false)) { - $certs = \OC::$SERVERROOT . '/resources/config/ca-bundle.crt'; + $certs = \OC::$SERVERROOT . '/resources/config/ca-bundle.crt'; } else { $certs = $this->certificateManager->getAbsoluteBundlePath(); } diff --git a/lib/Exceptions/AEAuthNotValidException.php b/lib/Exceptions/AEAuthNotValidException.php index f46ea6cd..83fc5b1b 100644 --- a/lib/Exceptions/AEAuthNotValidException.php +++ b/lib/Exceptions/AEAuthNotValidException.php @@ -40,4 +40,4 @@ class AEAuthNotValidException extends \Exception { public function __construct($message = 'AEAuth failed', $code = Http::STATUS_UNAUTHORIZED) { parent::__construct($message, $code); } -} \ No newline at end of file +} diff --git a/lib/Listener/LoadFilesPluginListener.php b/lib/Listener/LoadFilesPluginListener.php index 58c6c99b..dd8e783e 100644 --- a/lib/Listener/LoadFilesPluginListener.php +++ b/lib/Listener/LoadFilesPluginListener.php @@ -31,15 +31,15 @@ namespace OCA\AppEcosystemV2\Listener; -use OCP\Util; -use OCP\EventDispatcher\Event; -use OCP\EventDispatcher\IEventListener; -use OCA\Files\Event\LoadAdditionalScriptsEvent; -use OCP\AppFramework\Services\IInitialState; - use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Service\ExFilesActionsMenuService; +use OCA\Files\Event\LoadAdditionalScriptsEvent; +use OCP\AppFramework\Services\IInitialState; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Util; + /** * @template-extends IEventListener */ diff --git a/lib/Listener/SabrePluginAuthInitListener.php b/lib/Listener/SabrePluginAuthInitListener.php index 2c5fd279..ee5a4f8a 100644 --- a/lib/Listener/SabrePluginAuthInitListener.php +++ b/lib/Listener/SabrePluginAuthInitListener.php @@ -32,10 +32,10 @@ namespace OCA\AppEcosystemV2\Listener; use OCA\AppEcosystemV2\AEAuthBackend; -use OCP\EventDispatcher\Event; -use OCP\EventDispatcher\IEventListener; use OCA\DAV\Events\SabrePluginAuthInitEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; /** * @template-extends IEventListener diff --git a/lib/Middleware/AppEcosystemAuthMiddleware.php b/lib/Middleware/AppEcosystemAuthMiddleware.php index ff5c3853..fe133877 100644 --- a/lib/Middleware/AppEcosystemAuthMiddleware.php +++ b/lib/Middleware/AppEcosystemAuthMiddleware.php @@ -31,21 +31,20 @@ namespace OCA\AppEcosystemV2\Middleware; -use ReflectionMethod; - -use OCP\AppFramework\Controller; -use OCP\AppFramework\Middleware; -use OCP\AppFramework\Utility\IControllerMethodReflector; - use OCA\AppEcosystemV2\Attribute\AppEcosystemAuth; use OCA\AppEcosystemV2\Exceptions\AEAuthNotValidException; use OCA\AppEcosystemV2\Service\AppEcosystemV2Service; + +use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Middleware; +use OCP\AppFramework\Utility\IControllerMethodReflector; use OCP\IL10N; use OCP\IRequest; use Psr\Log\LoggerInterface; +use ReflectionMethod; class AppEcosystemAuthMiddleware extends Middleware { private IControllerMethodReflector $reflector; diff --git a/lib/Migration/DataInitializationStep.php b/lib/Migration/DataInitializationStep.php index ddc5308e..41327a02 100644 --- a/lib/Migration/DataInitializationStep.php +++ b/lib/Migration/DataInitializationStep.php @@ -31,11 +31,11 @@ namespace OCA\AppEcosystemV2\Migration; +use OCA\AppEcosystemV2\Service\ExAppApiScopeService; + use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; -use OCA\AppEcosystemV2\Service\ExAppApiScopeService; - class DataInitializationStep implements IRepairStep { private ExAppApiScopeService $service; diff --git a/lib/Migration/Version1000Date202305221555.php b/lib/Migration/Version1000Date202305221555.php index 29d17dfa..d4c0696a 100644 --- a/lib/Migration/Version1000Date202305221555.php +++ b/lib/Migration/Version1000Date202305221555.php @@ -34,8 +34,8 @@ use Closure; use OCP\DB\ISchemaWrapper; use OCP\DB\Types; -use OCP\Migration\SimpleMigrationStep; use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; class Version1000Date202305221555 extends SimpleMigrationStep { /** diff --git a/lib/Profiler/AEDataCollector.php b/lib/Profiler/AEDataCollector.php index ca4a3cc6..bc1798dd 100644 --- a/lib/Profiler/AEDataCollector.php +++ b/lib/Profiler/AEDataCollector.php @@ -32,11 +32,12 @@ namespace OCA\AppEcosystemV2\Profiler; use OC\AppFramework\Http\Request; -use OCP\AppFramework\Http\Response; -use OCP\DataCollector\AbstractDataCollector; use OCA\AppEcosystemV2\AppInfo\Application; +use OCP\AppFramework\Http\Response; +use OCP\DataCollector\AbstractDataCollector; + /** * @psalm-suppress UndefinedClass */ @@ -46,7 +47,7 @@ public function getName(): string { } public function collect(Request $request, Response $response, \Throwable $exception = null): void { -// TODO: Check why DAV requests missing AE headers data + // TODO: Check why DAV requests missing AE headers data $headers = []; $aeHeadersList = [ 'AE-VERSION', diff --git a/lib/PublicCapabilities.php b/lib/PublicCapabilities.php index 31f59152..f47f9c1d 100644 --- a/lib/PublicCapabilities.php +++ b/lib/PublicCapabilities.php @@ -32,6 +32,7 @@ namespace OCA\AppEcosystemV2; use OCA\AppEcosystemV2\AppInfo\Application; + use OCP\App\IAppManager; use OCP\Capabilities\IPublicCapability; diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index f1ce6fd2..a98b3311 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -32,32 +32,31 @@ namespace OCA\AppEcosystemV2\Service; use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Db\ExApp; +use OCA\AppEcosystemV2\Db\ExAppMapper; + +use OCP\App\IAppManager; +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; +use OCP\Http\Client\IClient; +use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; use OCP\ICache; use OCP\ICacheFactory; use OCP\IConfig; -use OCP\IUser; -use OCP\Log\ILogFactory; -use Psr\Log\LoggerInterface; - -use OCP\Http\Client\IClientService; -use OCP\Http\Client\IClient; - -use OCA\AppEcosystemV2\Db\ExApp; -use OCA\AppEcosystemV2\Db\ExAppMapper; -use OCP\App\IAppManager; -use OCP\AppFramework\Db\DoesNotExistException; use OCP\IRequest; +use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; +use OCP\Log\ILogFactory; use OCP\Security\ISecureRandom; +use Psr\Log\LoggerInterface; class AppEcosystemV2Service { public const BASIC_API_SCOPE = 1; - const MAX_SIGN_TIME_X_MIN_DIFF = 60 * 5; - const CACHE_TTL = 60 * 60; // 1 hour + public const MAX_SIGN_TIME_X_MIN_DIFF = 60 * 5; + public const CACHE_TTL = 60 * 60; // 1 hour private LoggerInterface $logger; private ILogFactory $logFactory; @@ -166,7 +165,7 @@ public function registerExApp(string $appId, array $appData): ?ExApp { 'protocol' => $appData['protocol'], 'host' => $appData['host'], 'port' => $appData['port'], - 'secret' => $appData['secret'] !== '' ? $appData['secret'] : $this->random->generate(128), + 'secret' => $appData['secret'] !== '' ? $appData['secret'] : $this->random->generate(128), 'status' => json_encode(['active' => true]), // TODO: Add status request to ExApp 'created_time' => time(), 'last_check_time' => time(), @@ -201,7 +200,7 @@ public function unregisterExApp(string $appId): ?ExApp { $this->logger->error(sprintf('Error while unregistering ExApp: %s', $appId)); return null; } -// TODO: Remove ?app configs, ?app preferences + // TODO: Remove ?app configs, ?app preferences $this->exAppScopesService->removeExAppScopes($exApp); $this->exAppUsersService->removeExAppUsers($exApp); $this->cache->remove('/exApp_' . $appId); @@ -246,7 +245,7 @@ public function enableExApp(ExApp $exApp): bool { $this->disableExApp($exApp); return false; } - } else if (isset($exAppEnabled['error'])) { + } elseif (isset($exAppEnabled['error'])) { $this->logger->error(sprintf('Failed to enable ExApp %s. Error: %s', $exApp->getAppid(), $exAppEnabled['error'])); $this->disableExApp($exApp); return false; @@ -278,7 +277,7 @@ public function disableExApp(ExApp $exApp): bool { if (isset($response['error']) && strlen($response['error']) !== 0) { $this->logger->error(sprintf('Failed to disable ExApp %s. Error: %s', $exApp->getAppid(), $response['error'])); } - } else if (isset($exAppDisabled['error'])) { + } elseif (isset($exAppDisabled['error'])) { $this->logger->error(sprintf('Failed to enable ExApp %s. Error: %s', $exApp->getAppid(), $exAppDisabled['error'])); } if ($this->exAppMapper->updateExAppEnabled($exApp->getAppid(), false) !== 1) { @@ -413,7 +412,7 @@ public function requestToExApp( default: return ['error' => 'Bad HTTP method']; } -// TODO: Add files support + // TODO: Add files support return $response; } catch (\Exception $e) { $this->logger->error(sprintf('Error during request to ExApp %s: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); @@ -547,7 +546,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) } $headers['AE-SIGN-TIME'] = $signTime; - $body = $request->getMethod() . $request->getRequestUri() . json_encode($headers, JSON_UNESCAPED_SLASHES); + $body = $request->getMethod() . $request->getRequestUri() . json_encode($headers, JSON_UNESCAPED_SLASHES); $signature = hash_hmac('sha256', $body, $exApp->getSecret()); $signatureValid = $signature === $requestSignature; diff --git a/lib/Service/DaemonConfigService.php b/lib/Service/DaemonConfigService.php index cf93b2c1..0c61987d 100644 --- a/lib/Service/DaemonConfigService.php +++ b/lib/Service/DaemonConfigService.php @@ -32,21 +32,21 @@ namespace OCA\AppEcosystemV2\Service; use OCA\AppEcosystemV2\AppInfo\Application; -use Psr\Log\LoggerInterface; +use OCA\AppEcosystemV2\Db\DaemonConfig; +use OCA\AppEcosystemV2\Db\DaemonConfigMapper; + +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; use OCP\ICache; use OCP\ICacheFactory; -use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\MultipleObjectsReturnedException; - -use OCA\AppEcosystemV2\Db\DaemonConfig; -use OCA\AppEcosystemV2\Db\DaemonConfigMapper; +use Psr\Log\LoggerInterface; /** * Daemon configuration (daemons) */ class DaemonConfigService { - const CACHE_TTL = 60 * 60 * 2; // 2 hours + public const CACHE_TTL = 60 * 60 * 2; // 2 hours private LoggerInterface $logger; private ICache $cache; private DaemonConfigMapper $mapper; @@ -99,7 +99,7 @@ public function getRegisteredDaemonConfigs(): ?array { $cacheKey = '/daemon_configs'; $cached = $this->cache->get($cacheKey); if ($cached !== null) { - return array_map(function($cachedEntry) { + return array_map(function ($cachedEntry) { return $cachedEntry instanceof DaemonConfig ? $cachedEntry : new DaemonConfig($cachedEntry); }, $cached); } diff --git a/lib/Service/ExAppApiScopeService.php b/lib/Service/ExAppApiScopeService.php index 48d05f4e..b4274ae4 100644 --- a/lib/Service/ExAppApiScopeService.php +++ b/lib/Service/ExAppApiScopeService.php @@ -31,10 +31,10 @@ namespace OCA\AppEcosystemV2\Service; - use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Db\ExAppApiScope; use OCA\AppEcosystemV2\Db\ExAppApiScopeMapper; + use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; @@ -43,7 +43,7 @@ use Psr\Log\LoggerInterface; class ExAppApiScopeService { - const CACHE_TTL = 60 * 60 * 24 * 7; // 1 week + public const CACHE_TTL = 60 * 60 * 24 * 7; // 1 week private LoggerInterface $logger; private ExAppApiScopeMapper $mapper; private ICache $cache; @@ -103,23 +103,23 @@ public function registerInitScopes(): bool { $initApiScopes = [ // AppEcosystemV2 scopes - ['api_route' => $aeApiV1Prefix . '/files/actions/menu', 'scope_group' => 1, 'name' => 'BASIC'], - ['api_route' => $aeApiV1Prefix . '/log', 'scope_group' => 1, 'name' => 'BASIC'], - ['api_route' => $aeApiV1Prefix . '/ex-app/config', 'scope_group' => 1, 'name' => 'BASIC'], - ['api_route' => $aeApiV1Prefix . '/ex-app/preference', 'scope_group' => 1, 'name' => 'BASIC'], - ['api_route' => $aeApiV1Prefix . '/users', 'scope_group' => 2, 'name' => 'SYSTEM'], - ['api_route' => $aeApiV1Prefix . '/ex-app/all', 'scope_group' => 2, 'name' => 'SYSTEM'], + ['api_route' => $aeApiV1Prefix . '/files/actions/menu', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/log', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/ex-app/config', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/ex-app/preference', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/users', 'scope_group' => 2, 'name' => 'SYSTEM'], + ['api_route' => $aeApiV1Prefix . '/ex-app/all', 'scope_group' => 2, 'name' => 'SYSTEM'], // Cloud scopes - ['api_route' => '/cloud/capabilities', 'scope_group' => 1, 'name' => 'BASIC'], - ['api_route' => '/cloud/apps', 'scope_group' => 2, 'name' => 'SYSTEM'], - ['api_route' => '/apps/provisioning_api/api/', 'scope_group' => 2, 'name' => 'SYSTEM'], - ['api_route' => '/cloud/users', 'scope_group' => 10, 'name' => 'USER_INFO'], - ['api_route' => '/cloud/groups', 'scope_group' => 10, 'name' => 'USER_INFO'], - ['api_route' => '/apps/user_status/api/', 'scope_group' => 11, 'name' => 'USER_STATUS'], - ['api_route' => '/apps/notifications/api/', 'scope_group' => 12, 'name' => 'NOTIFICATIONS'], - ['api_route' => '/apps/weather_status/api/', 'scope_group' => 13, 'name' => 'WEATHER_STATUS'], - ['api_route' => '/dav/', 'scope_group' => 3, 'name' => 'DAV'], + ['api_route' => '/cloud/capabilities', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => '/cloud/apps', 'scope_group' => 2, 'name' => 'SYSTEM'], + ['api_route' => '/apps/provisioning_api/api/', 'scope_group' => 2, 'name' => 'SYSTEM'], + ['api_route' => '/cloud/users', 'scope_group' => 10, 'name' => 'USER_INFO'], + ['api_route' => '/cloud/groups', 'scope_group' => 10, 'name' => 'USER_INFO'], + ['api_route' => '/apps/user_status/api/', 'scope_group' => 11, 'name' => 'USER_STATUS'], + ['api_route' => '/apps/notifications/api/', 'scope_group' => 12, 'name' => 'NOTIFICATIONS'], + ['api_route' => '/apps/weather_status/api/', 'scope_group' => 13, 'name' => 'WEATHER_STATUS'], + ['api_route' => '/dav/', 'scope_group' => 3, 'name' => 'DAV'], ]; $this->cache->clear('/all_api_scopes'); diff --git a/lib/Service/ExAppConfigService.php b/lib/Service/ExAppConfigService.php index 18f659e0..df5e8d3a 100644 --- a/lib/Service/ExAppConfigService.php +++ b/lib/Service/ExAppConfigService.php @@ -31,22 +31,22 @@ namespace OCA\AppEcosystemV2\Service; +use OCA\AppEcosystemV2\AppInfo\Application; +use OCA\AppEcosystemV2\Db\ExAppConfig; +use OCA\AppEcosystemV2\Db\ExAppConfigMapper; + +use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; use OCP\ICache; use OCP\ICacheFactory; use Psr\Log\LoggerInterface; -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Db\ExAppConfig; -use OCA\AppEcosystemV2\Db\ExAppConfigMapper; -use OCP\AppFramework\Db\DoesNotExistException; - /** * App configuration (appconfig_ex) */ class ExAppConfigService { - const CACHE_TTL = 60 * 60; // 1 hour + public const CACHE_TTL = 60 * 60; // 1 hour private LoggerInterface $logger; private ICache $cache; private ExAppConfigMapper $mapper; @@ -165,7 +165,7 @@ public function getAllAppConfig(string $appId): array { public function getAppConfig(mixed $appId, mixed $configKey): ?ExAppConfig { try { $cacheKey = sprintf('/%s:%s', $appId, $configKey); - $cashed= $this->cache->get($cacheKey); + $cashed = $this->cache->get($cacheKey); if ($cashed !== null) { return $cashed instanceof ExAppConfig ? $cashed : new ExAppConfig($cashed); } diff --git a/lib/Service/ExAppPreferenceService.php b/lib/Service/ExAppPreferenceService.php index 0f02db5d..975ed3b8 100644 --- a/lib/Service/ExAppPreferenceService.php +++ b/lib/Service/ExAppPreferenceService.php @@ -34,6 +34,7 @@ use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Db\ExAppPreference; use OCA\AppEcosystemV2\Db\ExAppPreferenceMapper; + use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; diff --git a/lib/Service/ExAppScopesService.php b/lib/Service/ExAppScopesService.php index 87bcdbdd..714afb99 100644 --- a/lib/Service/ExAppScopesService.php +++ b/lib/Service/ExAppScopesService.php @@ -31,11 +31,11 @@ namespace OCA\AppEcosystemV2\Service; - use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Db\ExApp; use OCA\AppEcosystemV2\Db\ExAppScope; use OCA\AppEcosystemV2\Db\ExAppScopeMapper; + use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; @@ -44,7 +44,7 @@ use Psr\Log\LoggerInterface; class ExAppScopesService { - const CACHE_TTL = 60 * 60; // 1 hour + public const CACHE_TTL = 60 * 60; // 1 hour private LoggerInterface $logger; private ExAppScopeMapper $mapper; private ICache $cache; @@ -64,7 +64,7 @@ public function getExAppScopes(ExApp $exApp): array { $cacheKey = '/ex_app_scopes_' . $exApp->getAppid(); $cached = $this->cache->get($cacheKey); if ($cached !== null) { - return array_map(function($cachedEntry) { + return array_map(function ($cachedEntry) { return $cachedEntry instanceof ExAppScope ? $cachedEntry : new ExAppScope($cachedEntry); }, $cached); } diff --git a/lib/Service/ExAppUsersService.php b/lib/Service/ExAppUsersService.php index ba7d06c0..b4d03026 100644 --- a/lib/Service/ExAppUsersService.php +++ b/lib/Service/ExAppUsersService.php @@ -31,19 +31,18 @@ namespace OCA\AppEcosystemV2\Service; - +use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Db\ExApp; use OCA\AppEcosystemV2\Db\ExAppUser; +use OCA\AppEcosystemV2\Db\ExAppUserMapper; + use OCP\DB\Exception; use OCP\ICache; use OCP\ICacheFactory; use Psr\Log\LoggerInterface; -use OCA\AppEcosystemV2\AppInfo\Application; -use OCA\AppEcosystemV2\Db\ExAppUserMapper; - class ExAppUsersService { - const CACHE_TLL = 60 * 60; // 1 hour + public const CACHE_TLL = 60 * 60; // 1 hour private LoggerInterface $logger; private ICache $cache; private ExAppUserMapper $mapper; @@ -92,7 +91,7 @@ public function getExAppUsers(ExApp $exApp): array { $cacheKey = '/ex_apps_users_' . $exApp->getAppid(); $cached = $this->cache->get($cacheKey); if ($cached !== null) { - array_map(function($cashedEntry) { + array_map(function ($cashedEntry) { return $cashedEntry instanceof ExAppUser ? $cashedEntry : new ExAppUser($cashedEntry); }, $cached); } @@ -124,7 +123,7 @@ public function exAppUserExists(string $appId, string $userId): bool { $cacheKey = '/ex_apps_users_' . $appId . '_' . $userId; $cached = $this->cache->get($cacheKey); if ($cached !== null) { - $exAppUsers = array_map(function($cashedEntry) { + $exAppUsers = array_map(function ($cashedEntry) { return $cashedEntry instanceof ExAppUser ? $cashedEntry : new ExAppUser($cashedEntry); }, $cached); return !empty($exAppUsers) && $exAppUsers[0] instanceof ExAppUser; diff --git a/lib/Service/ExFilesActionsMenuService.php b/lib/Service/ExFilesActionsMenuService.php index 0b007505..8359c7db 100644 --- a/lib/Service/ExFilesActionsMenuService.php +++ b/lib/Service/ExFilesActionsMenuService.php @@ -31,23 +31,22 @@ namespace OCA\AppEcosystemV2\Service; -use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\DB\Exception; -use OCP\ICache; -use OCP\ICacheFactory; -use OCP\IRequest; -use Psr\Log\LoggerInterface; use OCA\AppEcosystemV2\AppInfo\Application; -use OCP\Cache\CappedMemoryCache; - use OCA\AppEcosystemV2\Db\ExFilesActionsMenu; use OCA\AppEcosystemV2\Db\ExFilesActionsMenuMapper; use OCP\AppFramework\Db\DoesNotExistException; + use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Http; +use OCP\DB\Exception; use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IRequest; +use Psr\Log\LoggerInterface; class ExFilesActionsMenuService { private ICache $cache; @@ -100,7 +99,7 @@ public function registerFileActionMenu(string $appId, array $params): ?Entity { $fileActionMenu->setActionHandler($params['action_handler']); try { if ($this->mapper->updateFileActionMenu($fileActionMenu) !== 1) { - $this->logger->error(sprintf('Failed to update file action menu %s for app: %s',$params['name'], $appId)); + $this->logger->error(sprintf('Failed to update file action menu %s for app: %s', $params['name'], $appId)); return null; } } catch (Exception) { @@ -120,7 +119,7 @@ public function registerFileActionMenu(string $appId, array $params): ?Entity { 'action_handler' => $params['action_handler'], ])); } catch (Exception) { - $this->logger->error(sprintf('Failed to insert file action menu %s for app: %s', $params['name'],$appId)); + $this->logger->error(sprintf('Failed to insert file action menu %s for app: %s', $params['name'], $appId)); return null; } } @@ -152,10 +151,10 @@ public function unregisterFileActionMenu(string $appId, string $fileActionMenuNa */ public function getRegisteredFileActions(): ?array { $cacheKey = 'ex_files_actions_menus'; -// $cache = $this->cache->get($cacheKey); -// if ($cache !== null) { -// return $cache; -// } + // $cache = $this->cache->get($cacheKey); + // if ($cache !== null) { + // return $cache; + // } try { $fileActions = $this->mapper->findAllEnabled(); @@ -168,10 +167,10 @@ public function getRegisteredFileActions(): ?array { public function getExAppFileAction(string $appId, string $fileActionName): ?ExFilesActionsMenu { $cacheKey = 'ex_files_actions_menu_' . $appId . '_' . $fileActionName; -// $cache = $this->cache->get($cacheKey); -// if ($cache !== null) { -// return $cache; -// } + // $cache = $this->cache->get($cacheKey); + // if ($cache !== null) { + // return $cache; + // } try { $fileAction = $this->mapper->findByAppIdName($appId, $fileActionName); diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 6bc958d3..5eadf38a 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -1,4 +1,7 @@ Date: Wed, 19 Jul 2023 19:01:06 +0300 Subject: [PATCH 18/29] fix redis service name --- .github/workflows/tests-deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests-deploy.yml b/.github/workflows/tests-deploy.yml index b9345c39..347281a4 100644 --- a/.github/workflows/tests-deploy.yml +++ b/.github/workflows/tests-deploy.yml @@ -391,6 +391,7 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 + --name redis ports: - 6379:6379 From ac6d4a52cd907b8d35a60c775da64d3c09bedd09 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Thu, 20 Jul 2023 14:51:35 +0300 Subject: [PATCH 19/29] last_response_time -> last_check_time (nc_py_api getExApps) --- lib/Service/AppEcosystemV2Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index a98b3311..4be18b99 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -663,7 +663,7 @@ public function getExAppsList(bool $extended = false): array { 'name' => $exApp->getName(), 'version' => $exApp->getVersion(), 'enabled' => $exApp->getEnabled(), - 'last_response_time' => $exApp->getLastCheckTime(), + 'last_check_time' => $exApp->getLastCheckTime(), 'system' => $this->exAppUsersService->exAppUserExists($exApp->getAppid(), ''), ]; }, $exApps); From b0f3adbfb0b97e6c0d8dad46add8841612331b77 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Thu, 20 Jul 2023 15:22:31 +0300 Subject: [PATCH 20/29] fix xml file loading --- lib/Command/ExApp/Deploy.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Command/ExApp/Deploy.php b/lib/Command/ExApp/Deploy.php index 2dd3a9da..3efa7db4 100644 --- a/lib/Command/ExApp/Deploy.php +++ b/lib/Command/ExApp/Deploy.php @@ -91,7 +91,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 2; } - $infoXml = simplexml_load_file($pathToInfoXml); + $infoXml = simplexml_load_string(file_get_contents($pathToInfoXml)); if ($infoXml === false) { $output->writeln(sprintf('Failed to load info.xml from %s', $pathToInfoXml)); return 2; From 8bf9d4db5242f6481b7f4727b931ce396d174441 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Thu, 20 Jul 2023 21:18:24 +0300 Subject: [PATCH 21/29] Refactoring. Removed unused parts --- appinfo/info.xml | 2 +- docs/authentication.rst | 6 +- docs/index.rst | 2 +- .../{Scopes => ApiScopes}/ListApiScopes.php | 0 lib/Db/DaemonConfigMapper.php | 19 --- lib/Db/ExAppMapper.php | 36 ----- lib/Db/ExAppPreferenceMapper.php | 13 -- lib/Db/ExAppScopeMapper.php | 12 -- lib/Db/ExAppUserMapper.php | 12 -- lib/Db/ExFilesActionsMenuMapper.php | 75 +---------- lib/Service/AppEcosystemV2Service.php | 106 ++++++--------- lib/Service/ExAppApiScopeService.php | 13 +- lib/Service/ExAppConfigService.php | 36 +---- lib/Service/ExAppPreferenceService.php | 12 -- lib/Service/ExAppScopesService.php | 39 +++--- lib/Service/ExAppUsersService.php | 35 +++-- lib/Service/ExFilesActionsMenuService.php | 125 +++++++----------- 17 files changed, 148 insertions(+), 395 deletions(-) rename lib/Command/{Scopes => ApiScopes}/ListApiScopes.php (100%) diff --git a/appinfo/info.xml b/appinfo/info.xml index a9291542..381394fb 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -43,7 +43,7 @@ OCA\AppEcosystemV2\Command\Daemon\RegisterDaemon OCA\AppEcosystemV2\Command\Daemon\UnregisterDaemon OCA\AppEcosystemV2\Command\Daemon\ListDaemons - OCA\AppEcosystemV2\Command\Scopes\ListApiScopes + OCA\AppEcosystemV2\Command\ApiScopes\ListApiScopes OCA\AppEcosystemV2\Settings\Admin diff --git a/docs/authentication.rst b/docs/authentication.rst index 61d16fee..4a686453 100644 --- a/docs/authentication.rst +++ b/docs/authentication.rst @@ -11,7 +11,7 @@ Authentication flow 1. ExApp sends a request to Nextcloud 2. Nextcloud passes request to AppEcosystemV2 -3. AppEcosystemV2 validates request (see [authentication](#AppEcosystemV2-authentication) section) +3. AppEcosystemV2 validates request (see `authentication flow in details`_) 4. Request is accepted/rejected .. mermaid:: @@ -37,9 +37,9 @@ Each ExApp request to secured API with AppEcosystemAuth must contain the followi 2. `EX-APP-ID` - id of the ExApp 3. `EX-APP-VERSION` - version of the ExApp 4. `NC-USER-ID` - the user under which the request is made, can be empty in case of system apps (more details in [scopes](#AppEcosystemV2-scopes) section) -5. `AE-DATA-HASH` - hash of the request body (see details in [signature](#AE-SIGNATURE) section) +5. `AE-DATA-HASH` - hash of the request body (see details in `ae_signature`_ section) 6. `AE-SIGN-TIME` - unix timestamp of the request -7. `AE-SIGNATURE` - signature of the request (see details [signature](#AE-SIGNATURE) section) +7. `AE-SIGNATURE` - signature of the request (see details `ae_signature`_ section) AE_SIGNATURE diff --git a/docs/index.rst b/docs/index.rst index f8279d9f..af9d06ae 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,4 +11,4 @@ Welcome to the amazing docs that we will write for AppEcosystemV2! definitions.rst development/index.rst deploy/index.rst - ./authentication + authentication diff --git a/lib/Command/Scopes/ListApiScopes.php b/lib/Command/ApiScopes/ListApiScopes.php similarity index 100% rename from lib/Command/Scopes/ListApiScopes.php rename to lib/Command/ApiScopes/ListApiScopes.php diff --git a/lib/Db/DaemonConfigMapper.php b/lib/Db/DaemonConfigMapper.php index d1e2fdcd..d2f7e2ef 100644 --- a/lib/Db/DaemonConfigMapper.php +++ b/lib/Db/DaemonConfigMapper.php @@ -58,25 +58,6 @@ public function findAll(int $limit = null, int $offset = null): array { return $this->findEntities($qb); } - /** - * @param int $id - * - * @throws DoesNotExistException - * @throws Exception - * @throws MultipleObjectsReturnedException - * - * @return DaemonConfig - */ - public function findById(int $id): DaemonConfig { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->tableName) - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); - return $this->findEntity($qb); - } - /** * @param string $name * diff --git a/lib/Db/ExAppMapper.php b/lib/Db/ExAppMapper.php index fc29bfdc..84c55309 100644 --- a/lib/Db/ExAppMapper.php +++ b/lib/Db/ExAppMapper.php @@ -95,25 +95,6 @@ public function findByPort(int $port): array { return $this->findEntities($qb); } - /** - * @param string $name - * - * @throws DoesNotExistException if not found - * @throws MultipleObjectsReturnedException if more than one result - * @throws Exception - * - * @return ExApp - */ - public function findByName(string $name): Entity { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->tableName) - ->where( - $qb->expr()->eq('name', $qb->createNamedParameter($name, IQueryBuilder::PARAM_STR)) - ); - return $this->findEntity($qb); - } - /** * @throws Exception */ @@ -125,23 +106,6 @@ public function deleteExApp(ExApp $exApp): int { )->executeStatement(); } - /** - * @throws Exception - */ - public function findExAppEnabled(string $appId): array { - $qb = $this->db->getQueryBuilder(); - $qb->select('enabled') - ->from($this->tableName) - ->where( - $qb->expr()->eq('appid', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR)) - ); - $result = $qb->executeQuery(); - return [ - 'success' => $result->rowCount() === 1, - 'enabled' => $result->fetchOne(), - ]; - } - /** * @throws Exception */ diff --git a/lib/Db/ExAppPreferenceMapper.php b/lib/Db/ExAppPreferenceMapper.php index 472adf45..0dbd1682 100644 --- a/lib/Db/ExAppPreferenceMapper.php +++ b/lib/Db/ExAppPreferenceMapper.php @@ -46,19 +46,6 @@ public function __construct(IDBConnection $db) { parent::__construct($db, 'preferences_ex'); } - /** - * @throws Exception - * @return ExAppPreference[] - */ - public function findAll(int $limit = null, int $offset = null): array { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->tableName) - ->setMaxResults($limit) - ->setFirstResult($offset); - return $this->findEntities($qb); - } - /** * @param string $userId * @param string $appId diff --git a/lib/Db/ExAppScopeMapper.php b/lib/Db/ExAppScopeMapper.php index 26f8ae88..da56befc 100644 --- a/lib/Db/ExAppScopeMapper.php +++ b/lib/Db/ExAppScopeMapper.php @@ -46,18 +46,6 @@ public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_apps_scopes'); } - /** - * @throws Exception - */ - public function findAll(int $limit = null, int $offset = null): array { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->tableName) - ->setMaxResults($limit) - ->setFirstResult($offset); - return $this->findEntities($qb); - } - /** * @param string $appId * diff --git a/lib/Db/ExAppUserMapper.php b/lib/Db/ExAppUserMapper.php index aeab341c..1b5a8696 100644 --- a/lib/Db/ExAppUserMapper.php +++ b/lib/Db/ExAppUserMapper.php @@ -44,18 +44,6 @@ public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_apps_users'); } - /** - * @throws Exception - */ - public function findAll(int $limit = null, int $offset = null): array { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->tableName) - ->setMaxResults($limit) - ->setFirstResult($offset); - return $this->findEntities($qb); - } - /** * @throws Exception */ diff --git a/lib/Db/ExFilesActionsMenuMapper.php b/lib/Db/ExFilesActionsMenuMapper.php index 46c80dcf..2c4b250f 100644 --- a/lib/Db/ExFilesActionsMenuMapper.php +++ b/lib/Db/ExFilesActionsMenuMapper.php @@ -46,18 +46,6 @@ public function __construct(IDBConnection $db) { parent::__construct($db, 'ex_files_actions_menu'); } - /** - * @throws Exception - */ - public function findAll(int $limit = null, int $offset = null): array { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->tableName) - ->setMaxResults($limit) - ->setFirstResult($offset); - return $this->findEntities($qb); - } - /** * @throws Exception */ @@ -83,22 +71,6 @@ public function findAllEnabled(): array { return $result->fetchAll(); } - /** - * @param string $appId - * - * @throws Exception - * @return ExFilesActionsMenu[] - */ - public function findAllByAppId(string $appId): array { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->tableName) - ->where( - $qb->expr()->eq('appId', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR)) - ); - return $this->findEntities($qb); - } - /** * @param string $appId * @param string $name @@ -109,12 +81,12 @@ public function findAllByAppId(string $appId): array { * * @return ExFilesActionsMenu */ - public function findByAppIdName(string $appId, string $name): ExFilesActionsMenu { + public function findByAppidName(string $appId, string $name): ExFilesActionsMenu { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->tableName) ->where( - $qb->expr()->eq('appId', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR)), + $qb->expr()->eq('appid', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR)), $qb->expr()->eq('name', $qb->createNamedParameter($name, IQueryBuilder::PARAM_STR)), ); return $this->findEntity($qb); @@ -139,47 +111,4 @@ public function findByName(string $name): ExFilesActionsMenu { ); return $this->findEntity($qb); } - - /** - * @param ExFilesActionsMenu $exFilesActionsMenu - * - * @throws Exception - * - * @return int Number of updated rows - */ - public function updateFileActionMenu(ExFilesActionsMenu $exFilesActionsMenu): int { - $qb = $this->db->getQueryBuilder(); - return $qb->update($this->tableName) - ->set('display_name', $qb->createNamedParameter($exFilesActionsMenu->getDisplayName(), IQueryBuilder::PARAM_STR)) - ->set('mime', $qb->createNamedParameter($exFilesActionsMenu->getMime(), IQueryBuilder::PARAM_STR)) - ->set('permissions', $qb->createNamedParameter($exFilesActionsMenu->getPermissions(), IQueryBuilder::PARAM_STR)) - ->set('order', $qb->createNamedParameter($exFilesActionsMenu->getOrder(), IQueryBuilder::PARAM_INT)) - ->set('icon', $qb->createNamedParameter($exFilesActionsMenu->getIcon() ?? '', IQueryBuilder::PARAM_STR)) - ->set('icon_class', $qb->createNamedParameter($exFilesActionsMenu->getIconClass(), IQueryBuilder::PARAM_STR)) - ->set('action_handler', $qb->createNamedParameter($exFilesActionsMenu->getActionHandler(), IQueryBuilder::PARAM_STR)) - ->where( - $qb->expr()->eq('appId', $qb->createNamedParameter($exFilesActionsMenu->getAppid(), IQueryBuilder::PARAM_STR)), - $qb->expr()->eq('name', $qb->createNamedParameter($exFilesActionsMenu->getName(), IQueryBuilder::PARAM_STR)) - ) - ->executeStatement(); - } - - /** - * @param ExFilesActionsMenu $exFilesActionsMenu - * - * @throws Exception - * - * @return int Number of deleted rows - */ - public function deleteByAppidName(ExFilesActionsMenu $exFilesActionsMenu): int { - $qb = $this->db->getQueryBuilder(); - return $qb->delete($this->tableName) - ->where( - $qb->expr()->eq('appId', $qb->createNamedParameter($exFilesActionsMenu->getAppid(), IQueryBuilder::PARAM_STR)) - ) - ->andWhere( - $qb->expr()->eq('name', $qb->createNamedParameter($exFilesActionsMenu->getName(), IQueryBuilder::PARAM_STR)) - ) - ->executeStatement(); - } } diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index 4be18b99..e550d075 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -116,10 +116,11 @@ public function getExApp(string $appId): ?ExApp { $exApp = $this->exAppMapper->findByAppId($appId); $this->cache->set($cacheKey, $exApp); return $exApp; - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { - $this->logger->debug(sprintf('ExApp %s not found.', $appId), ['exception' => $e]); - return null; + } catch (DoesNotExistException) { + } catch (MultipleObjectsReturnedException|Exception $e) { + $this->logger->debug(sprintf('Failed to get ExApp %s. Error: %s', $appId, $e->getMessage()), ['exception' => $e]); } + return null; } /** @@ -131,54 +132,27 @@ public function getExApp(string $appId): ?ExApp { * @return ExApp|null */ public function registerExApp(string $appId, array $appData): ?ExApp { + $exApp = new ExApp([ + 'appid' => $appId, + 'version' => $appData['version'], + 'name' => $appData['name'], + 'daemon_config_name' => $appData['daemon_config_name'], + 'protocol' => $appData['protocol'], + 'host' => $appData['host'], + 'port' => $appData['port'], + 'secret' => $appData['secret'] !== '' ? $appData['secret'] : $this->random->generate(128), + 'status' => json_encode(['active' => true]), // TODO: Add status request to ExApp + 'created_time' => time(), + 'last_check_time' => time(), + ]); try { - $exApp = $this->exAppMapper->findByAppId($appId); - $exApp->setVersion($appData['version']); - $exApp->setName($appData['name']); - $exApp->setDaemonConfigName($appData['daemon_config_name']); - $exApp->setProtocol($appData['protocol']); - $exApp->setHost($appData['host']); - $exApp->setPort($appData['port']); - if ($appData['secret'] !== '') { - $exApp->setSecret($appData['secret']); - } else { - $secret = $this->random->generate(128); - $exApp->setSecret($secret); - } - $exApp->setStatus(json_encode(['active' => true])); // TODO: Add status request to ExApp - $exApp->setLastCheckTime(time()); - try { - $cacheKey = '/exApp_' . $appId; - $exApp = $this->exAppMapper->update($exApp); - $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); - return $exApp; - } catch (Exception $e) { - $this->logger->error(sprintf('Error while updating already registered ExApp: %s', $e->getMessage())); - return null; - } - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - $exApp = new ExApp([ - 'appid' => $appId, - 'version' => $appData['version'], - 'name' => $appData['name'], - 'daemon_config_name' => $appData['daemon_config_name'], - 'protocol' => $appData['protocol'], - 'host' => $appData['host'], - 'port' => $appData['port'], - 'secret' => $appData['secret'] !== '' ? $appData['secret'] : $this->random->generate(128), - 'status' => json_encode(['active' => true]), // TODO: Add status request to ExApp - 'created_time' => time(), - 'last_check_time' => time(), - ]); - try { - $cacheKey = '/exApp_' . $appId; - $exApp = $this->exAppMapper->insert($exApp); - $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); - return $exApp; - } catch (Exception $e) { - $this->logger->error(sprintf('Error while registering ExApp: %s', $e->getMessage())); - return null; - } + $cacheKey = '/exApp_' . $appId; + $exApp = $this->exAppMapper->insert($exApp); + $this->cache->set($cacheKey, $exApp, self::CACHE_TTL); + return $exApp; + } catch (Exception $e) { + $this->logger->error(sprintf('Error while registering ExApp %s: %s', $appId, $e->getMessage())); + return null; } } @@ -200,7 +174,7 @@ public function unregisterExApp(string $appId): ?ExApp { $this->logger->error(sprintf('Error while unregistering ExApp: %s', $appId)); return null; } - // TODO: Remove ?app configs, ?app preferences + // TODO: Do we need to remove app_config_ex, app_preferences_ex too $this->exAppScopesService->removeExAppScopes($exApp); $this->exAppUsersService->removeExAppUsers($exApp); $this->cache->remove('/exApp_' . $appId); @@ -302,18 +276,18 @@ public function disableExApp(ExApp $exApp): bool { * @return array|null */ public function getAppStatus(string $appId): ?array { - try { - $exApp = $this->exAppMapper->findByAppId($appId); - $response = $this->requestToExApp(null, '', $exApp, '/status', 'GET'); - if ($response instanceof IResponse && $response->getStatusCode() === 200) { - $status = json_decode($response->getBody(), true); - $exApp->setStatus($status); - $this->updateExAppLastCheckTime($exApp); - } - return json_decode($exApp->getStatus(), true); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { + $exApp = $this->getExApp($appId); + if ($exApp === null) { return null; } + + $response = $this->requestToExApp(null, '', $exApp, '/status', 'GET'); + if ($response instanceof IResponse && $response->getStatusCode() === 200) { + $status = json_decode($response->getBody(), true); + $exApp->setStatus($status); + $this->updateExAppLastCheckTime($exApp); + } + return json_decode($exApp->getStatus(), true); } /** @@ -577,9 +551,13 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) $this->logger->error(sprintf('ExApp %s not passed scope group check %s', $exApp->getAppid(), $path)); return false; } - if (!$this->exAppUsersService->exAppUserExists($exApp->getAppid(), $userId)) { - $this->logger->error(sprintf('ExApp %s user %s does not exist', $exApp->getAppid(), $userId)); - return false; + try { + if (!$this->exAppUsersService->exAppUserExists($exApp->getAppid(), $userId)) { + $this->logger->error(sprintf('ExApp %s user %s does not exist', $exApp->getAppid(), $userId)); + return false; + } + } catch (Exception $e) { + $this->logger->error(sprintf('Failed to get ExApp %s user %s. Error: %s', $exApp->getAppid(), $userId, $e->getMessage()), ['exception' => $e]); } } return $this->finalizeRequestToNC($userId); diff --git a/lib/Service/ExAppApiScopeService.php b/lib/Service/ExAppApiScopeService.php index b4274ae4..5d120f7d 100644 --- a/lib/Service/ExAppApiScopeService.php +++ b/lib/Service/ExAppApiScopeService.php @@ -43,7 +43,6 @@ use Psr\Log\LoggerInterface; class ExAppApiScopeService { - public const CACHE_TTL = 60 * 60 * 24 * 7; // 1 week private LoggerInterface $logger; private ExAppApiScopeMapper $mapper; private ICache $cache; @@ -69,10 +68,10 @@ public function getExAppApiScopes(): array { } $apiScopes = $this->mapper->findAll(); - $this->cache->set($cacheKey, $apiScopes, self::CACHE_TTL); + $this->cache->set($cacheKey, $apiScopes); return $apiScopes; } catch (Exception $e) { - $this->logger->error(sprintf('Failed to get all api scopes. Error: %s', $e->getMessage()), ['exception' => $e]); + $this->logger->error(sprintf('Failed to get all ApiScopes. Error: %s', $e->getMessage()), ['exception' => $e]); return []; } } @@ -88,7 +87,7 @@ public function getApiScopeByRoute(string $apiRoute): ?ExAppApiScope { $apiScopes = $this->getExAppApiScopes(); foreach ($apiScopes as $apiScope) { if (str_starts_with($apiRoute, $apiScope->getApiRoute())) { - $this->cache->set($cacheKey, $apiScope, self::CACHE_TTL); + $this->cache->set($cacheKey, $apiScope); return $apiScope; } } @@ -133,11 +132,13 @@ public function registerInitScopes(): bool { if (in_array($apiScope['api_route'], array_keys($registeredApiScopesRoutes))) { $apiScope['id'] = $registeredApiScopesRoutes[$apiScope['api_route']]; } - $this->mapper->insertOrUpdate(new ExAppApiScope($apiScope)); + $registeredApiScope = $this->mapper->insertOrUpdate(new ExAppApiScope($apiScope)); + $cacheKey = '/api_scope_' . $apiScope['api_route']; + $this->cache->set($cacheKey, $registeredApiScope); } return true; } catch (Exception $e) { - $this->logger->error('Failed to fill init API scopes: ' . $e->getMessage(), ['exception' => $e]); + $this->logger->error('Failed to fill init ApiScopes: ' . $e->getMessage(), ['exception' => $e]); return false; } } diff --git a/lib/Service/ExAppConfigService.php b/lib/Service/ExAppConfigService.php index df5e8d3a..265cc139 100644 --- a/lib/Service/ExAppConfigService.php +++ b/lib/Service/ExAppConfigService.php @@ -31,32 +31,25 @@ namespace OCA\AppEcosystemV2\Service; -use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Db\ExAppConfig; use OCA\AppEcosystemV2\Db\ExAppConfigMapper; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; -use OCP\ICache; -use OCP\ICacheFactory; use Psr\Log\LoggerInterface; /** * App configuration (appconfig_ex) */ class ExAppConfigService { - public const CACHE_TTL = 60 * 60; // 1 hour private LoggerInterface $logger; - private ICache $cache; private ExAppConfigMapper $mapper; public function __construct( - ICacheFactory $cacheFactory, ExAppConfigMapper $mapper, LoggerInterface $logger, ) { - $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/appconfig_ex'); $this->mapper = $mapper; $this->logger = $logger; } @@ -70,21 +63,13 @@ public function __construct( * @return array|null */ public function getAppConfigValues(string $appId, array $configKeys): ?array { - $cacheKey = sprintf('/%s:%s', $appId, join(':', array_values($configKeys))); - $cached = $this->cache->get($cacheKey); - if ($cached !== null) { - return $cached; - } - try { - $exAppConfigs = array_map(function (ExAppConfig $exAppConfig) { + return array_map(function (ExAppConfig $exAppConfig) { return [ 'configkey' => $exAppConfig->getConfigkey(), 'configvalue' => $exAppConfig->getConfigvalue() ?? '', ]; }, $this->mapper->findByAppConfigKeys($appId, $configKeys)); - $this->cache->set($cacheKey, $exAppConfigs, self::CACHE_TTL); - return $exAppConfigs; } catch (Exception) { return null; } @@ -110,8 +95,6 @@ public function setAppConfigValue(string $appId, string $configKey, mixed $confi 'configvalue' => $configValue ?? '', 'sensitive' => $sensitive, ])); - $cacheKey = sprintf('/%s:%s', $appId, $configKey); - $this->cache->set($cacheKey, $appConfigEx, self::CACHE_TTL); } catch (\Exception $e) { $this->logger->error('Error while inserting app_config_ex value: ' . $e->getMessage(), ['exception' => $e]); return null; @@ -164,15 +147,7 @@ public function getAllAppConfig(string $appId): array { */ public function getAppConfig(mixed $appId, mixed $configKey): ?ExAppConfig { try { - $cacheKey = sprintf('/%s:%s', $appId, $configKey); - $cashed = $this->cache->get($cacheKey); - if ($cashed !== null) { - return $cashed instanceof ExAppConfig ? $cashed : new ExAppConfig($cashed); - } - - $exAppConfig = $this->mapper->findByAppConfigKey($appId, $configKey); - $this->cache->set($cacheKey, $exAppConfig, self::CACHE_TTL); - return $exAppConfig; + return $this->mapper->findByAppConfigKey($appId, $configKey); } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { return null; } @@ -185,12 +160,7 @@ public function getAppConfig(mixed $appId, mixed $configKey): ?ExAppConfig { */ public function updateAppConfigValue(ExAppConfig $exAppConfig): ?int { try { - $result = $this->mapper->updateAppConfigValue($exAppConfig); - if ($result === 1) { - $cacheKey = sprintf('/%s:%s', $exAppConfig->getAppid(), $exAppConfig->getConfigkey()); - $this->cache->set($cacheKey, $exAppConfig, self::CACHE_TTL); - } - return $result; + return $this->mapper->updateAppConfigValue($exAppConfig); } catch (Exception) { return null; } diff --git a/lib/Service/ExAppPreferenceService.php b/lib/Service/ExAppPreferenceService.php index 975ed3b8..ce47d987 100644 --- a/lib/Service/ExAppPreferenceService.php +++ b/lib/Service/ExAppPreferenceService.php @@ -31,15 +31,12 @@ namespace OCA\AppEcosystemV2\Service; -use OCA\AppEcosystemV2\AppInfo\Application; use OCA\AppEcosystemV2\Db\ExAppPreference; use OCA\AppEcosystemV2\Db\ExAppPreferenceMapper; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\DB\Exception; -use OCP\ICache; -use OCP\ICacheFactory; use Psr\Log\LoggerInterface; /** @@ -48,16 +45,13 @@ class ExAppPreferenceService { private ExAppPreferenceMapper $mapper; private LoggerInterface $logger; - private ICache $cache; public function __construct( ExAppPreferenceMapper $mapper, LoggerInterface $logger, - ICacheFactory $cacheFactory, ) { $this->mapper = $mapper; $this->logger = $logger; - $this->cache = $cacheFactory->createDistributed(Application::APP_ID . '/preferences_ex'); } public function setUserConfigValue(string $userId, string $appId, string $configKey, mixed $configValue) { @@ -101,12 +95,6 @@ public function setUserConfigValue(string $userId, string $appId, string $config */ public function getUserConfigValues(string $userId, string $appId, array $configKeys): ?array { try { - $cacheKey = sprintf('/%s/%s:%s', $userId, $appId, implode('', $configKeys)); - $cached = $this->cache->get($cacheKey); - if ($cached !== null) { - return $cached; - } - return array_map(function (ExAppPreference $exAppPreference) { return [ 'configkey' => $exAppPreference->getConfigkey(), diff --git a/lib/Service/ExAppScopesService.php b/lib/Service/ExAppScopesService.php index 714afb99..4c0746ed 100644 --- a/lib/Service/ExAppScopesService.php +++ b/lib/Service/ExAppScopesService.php @@ -44,7 +44,6 @@ use Psr\Log\LoggerInterface; class ExAppScopesService { - public const CACHE_TTL = 60 * 60; // 1 hour private LoggerInterface $logger; private ExAppScopeMapper $mapper; private ICache $cache; @@ -70,7 +69,7 @@ public function getExAppScopes(ExApp $exApp): array { } $exAppScopes = $this->mapper->findByAppid($exApp->getAppid()); - $this->cache->set($cacheKey, $exAppScopes, self::CACHE_TTL); + $this->cache->set($cacheKey, $exAppScopes); return $exAppScopes; } catch (Exception $e) { $this->logger->error(sprintf('Failed to get all api scopes. Error: %s', $e->getMessage()), ['exception' => $e]); @@ -78,20 +77,21 @@ public function getExAppScopes(ExApp $exApp): array { } } - public function setExAppScopeGroup(ExApp $exApp, int $scopeGroup) { + public function setExAppScopeGroup(ExApp $exApp, int $scopeGroup): ?ExAppScope { + $exAppScope = $this->getByScope($exApp, $scopeGroup); + if ($exAppScope instanceof ExAppScope) { + return $exAppScope; + } + + $exAppScope = new ExAppScope([ + 'appid' => $exApp->getAppid(), + 'scope_group' => $scopeGroup, + ]); try { - return $this->mapper->findByAppidScope($exApp->getAppid(), $scopeGroup); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - $exAppScope = new ExAppScope([ - 'appid' => $exApp->getAppid(), - 'scope_group' => $scopeGroup, - ]); - try { - return $this->mapper->insert($exAppScope); - } catch (\Exception $e) { - $this->logger->error(sprintf('Error while setting ExApp scope group: %s', $e->getMessage())); - return null; - } + return $this->mapper->insert($exAppScope); + } catch (Exception $e) { + $this->logger->error(sprintf('Error while setting ExApp scope group: %s', $e->getMessage()), ['exception' => $e]); + return null; } } @@ -104,7 +104,7 @@ public function getByScope(ExApp $exApp, int $apiScope): ?ExAppScope { } $exAppScope = $this->mapper->findByAppidScope($exApp->getAppid(), $apiScope); - $this->cache->set($cacheKey, $exAppScope, self::CACHE_TTL); + $this->cache->set($cacheKey, $exAppScope); return $exAppScope; } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { return null; @@ -118,8 +118,11 @@ public function passesScopeCheck(ExApp $exApp, int $apiScope): bool { public function removeExAppScopes(ExApp $exApp): bool { try { - $result = $this->mapper->deleteByAppid($exApp->getAppid()); - return $result > 0; + $result = $this->mapper->deleteByAppid($exApp->getAppid()) > 0; + if ($result) { + $this->cache->clear('/ex_app_scopes_' . $exApp->getAppid()); + } + return $result; } catch (Exception $e) { $this->logger->error(sprintf('Failed to delete all ExApp %s scopes. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); return false; diff --git a/lib/Service/ExAppUsersService.php b/lib/Service/ExAppUsersService.php index b4d03026..38fee88f 100644 --- a/lib/Service/ExAppUsersService.php +++ b/lib/Service/ExAppUsersService.php @@ -109,33 +109,32 @@ public function removeExAppUsers(ExApp $exApp): bool { try { $result = $this->mapper->deleteByAppid($exApp->getAppid()) !== 0; if ($result) { - $this->cache->remove('/ex_apps_users_' . $exApp->getAppid()); + $this->cache->clear('/ex_apps_users_' . $exApp->getAppid()); } return $result; } catch (Exception $e) { - $this->logger->error(sprintf('Failed to remove ex_app_users for appid %s. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); + $this->logger->error(sprintf('Failed to remove ex_app_users for ExApp %s. Error: %s', $exApp->getAppid(), $e->getMessage()), ['exception' => $e]); return false; } } + /** + * @throws Exception + */ public function exAppUserExists(string $appId, string $userId): bool { - try { - $cacheKey = '/ex_apps_users_' . $appId . '_' . $userId; - $cached = $this->cache->get($cacheKey); - if ($cached !== null) { - $exAppUsers = array_map(function ($cashedEntry) { - return $cashedEntry instanceof ExAppUser ? $cashedEntry : new ExAppUser($cashedEntry); - }, $cached); - return !empty($exAppUsers) && $exAppUsers[0] instanceof ExAppUser; - } + $cacheKey = '/ex_apps_users_' . $appId . '_' . $userId; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + $exAppUsers = array_map(function ($cashedEntry) { + return $cashedEntry instanceof ExAppUser ? $cashedEntry : new ExAppUser($cashedEntry); + }, $cached); + return !empty($exAppUsers) && $exAppUsers[0] instanceof ExAppUser; + } - $exAppUsers = $this->mapper->findByAppidUserid($appId, $userId); - if (!empty($exAppUsers) && $exAppUsers[0] instanceof ExAppUser) { - $this->cache->set($cacheKey, $exAppUsers, self::CACHE_TLL); - return true; - } - } catch (Exception) { - return false; + $exAppUsers = $this->mapper->findByAppidUserid($appId, $userId); + if (!empty($exAppUsers) && $exAppUsers[0] instanceof ExAppUser) { + $this->cache->set($cacheKey, $exAppUsers, self::CACHE_TLL); + return true; } return false; } diff --git a/lib/Service/ExFilesActionsMenuService.php b/lib/Service/ExFilesActionsMenuService.php index 8359c7db..bfcbcda2 100644 --- a/lib/Service/ExFilesActionsMenuService.php +++ b/lib/Service/ExFilesActionsMenuService.php @@ -73,7 +73,7 @@ public function __construct( } /** - * Register file action menu from ex app + * Register file action menu from ExApp * * @param string $appId * @param array $params @@ -86,79 +86,61 @@ public function registerFileActionMenu(string $appId, array $params): ?Entity { } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { $fileActionMenu = null; } - // If exists - update, else - create - if ($fileActionMenu instanceof ExFilesActionsMenu) { - $fileActionMenu->setAppid($appId); - $fileActionMenu->setName($params['name']); - $fileActionMenu->setDisplayName($params['display_name']); - $fileActionMenu->setMime($params['mime']); - $fileActionMenu->setPermissions($params['permissions']); - $fileActionMenu->setOrder($params['order']); - $fileActionMenu->setIcon($params['icon']); - $fileActionMenu->setIconClass($params['icon_class']); - $fileActionMenu->setActionHandler($params['action_handler']); - try { - if ($this->mapper->updateFileActionMenu($fileActionMenu) !== 1) { - $this->logger->error(sprintf('Failed to update file action menu %s for app: %s', $params['name'], $appId)); - return null; - } - } catch (Exception) { - return null; - } - } else { - try { - $fileActionMenu = $this->mapper->insert(new ExFilesActionsMenu([ - 'appid' => $appId, - 'name' => $params['name'], - 'display_name' => $params['display_name'], - 'mime' => $params['mime'], - 'permissions' => $params['permissions'], - 'order' => $params['order'], - 'icon' => $params['icon'], - 'icon_class' => $params['icon_class'], - 'action_handler' => $params['action_handler'], - ])); - } catch (Exception) { - $this->logger->error(sprintf('Failed to insert file action menu %s for app: %s', $params['name'], $appId)); - return null; + try { + $newFileActionMenu = new ExFilesActionsMenu([ + 'appid' => $appId, + 'name' => $params['name'], + 'display_name' => $params['display_name'], + 'mime' => $params['mime'], + 'permissions' => $params['permissions'], + 'order' => $params['order'], + 'icon' => $params['icon'], + 'icon_class' => $params['icon_class'], + 'action_handler' => $params['action_handler'], + ]); + if ($fileActionMenu !== null) { + $newFileActionMenu->setId($fileActionMenu->getId()); } + $fileActionMenu = $this->mapper->insertOrUpdate($newFileActionMenu); + $cacheKey = '/ex_files_actions_menu_' . $appId . '_' . $params['name']; + $this->cache->remove('/ex_files_actions_menus'); + $this->cache->set($cacheKey, $fileActionMenu); + } catch (Exception $e) { + $this->logger->error(sprintf('Failed to register ExApp %s FileActionMenu %s. Error: %s', $appId, $params['name'], $e->getMessage()), ['exception' => $e]); + return null; } return $fileActionMenu; } public function unregisterFileActionMenu(string $appId, string $fileActionMenuName): ?ExFilesActionsMenu { try { - $fileActionMenu = $this->mapper->findByName($fileActionMenuName); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - $fileActionMenu = null; - } - if ($fileActionMenu !== null) { - try { - if ($this->mapper->deleteByAppidName($fileActionMenu) !== 1) { - $this->logger->error('Failed to delete file action menu ' . $fileActionMenuName . ' for app: ' . $appId); - return null; - } - } catch (Exception) { - $this->logger->error('Failed to delete file action menu ' . $fileActionMenuName . ' for app: ' . $appId); - return null; - } + $fileActionMenu = $this->getExAppFileAction($appId, $fileActionMenuName); + $this->mapper->delete($fileActionMenu); + $this->cache->remove('/ex_files_actions_menu_' . $appId . '_' . $fileActionMenuName); + return $fileActionMenu; + } catch (Exception $e) { + $this->logger->error(sprintf('Failed to unregister ExApp %s FileActionMenu %s. Error: %s', $appId, $fileActionMenuName, $e->getMessage()), ['exception' => $e]); + return null; } - return $fileActionMenu; } /** + * Get list of registered file actions (only for enabled ExApps) + * * @return ExFilesActionsMenu[]|null */ public function getRegisteredFileActions(): ?array { - $cacheKey = 'ex_files_actions_menus'; - // $cache = $this->cache->get($cacheKey); - // if ($cache !== null) { - // return $cache; - // } - try { + $cacheKey = '/ex_files_actions_menus'; + $cached = $this->cache->get($cacheKey); + if ($cached !== null) { + return array_map(function ($cacheEntry) { + return $cacheEntry instanceof ExFilesActionsMenu ? $cacheEntry : new ExFilesActionsMenu($cacheEntry); + }, $cached); + } + $fileActions = $this->mapper->findAllEnabled(); - $this->cache->set($cacheKey, $fileActions, Application::CACHE_TTL); + $this->cache->set($cacheKey, $fileActions); return $fileActions; } catch (Exception) { return null; @@ -166,28 +148,24 @@ public function getRegisteredFileActions(): ?array { } public function getExAppFileAction(string $appId, string $fileActionName): ?ExFilesActionsMenu { - $cacheKey = 'ex_files_actions_menu_' . $appId . '_' . $fileActionName; - // $cache = $this->cache->get($cacheKey); - // if ($cache !== null) { - // return $cache; - // } + $cacheKey = '/ex_files_actions_menu_' . $appId . '_' . $fileActionName; + $cache = $this->cache->get($cacheKey); + if ($cache !== null) { + return $cache; + } try { - $fileAction = $this->mapper->findByAppIdName($appId, $fileActionName); - $this->cache->set($cacheKey, $fileAction, Application::CACHE_TTL); + $fileAction = $this->mapper->findByAppidName($appId, $fileActionName); + $this->cache->set($cacheKey, $fileAction); } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception $e) { - $this->logger->error(sprintf('Failed to get file action %s for app: %s. Error: %s', $fileActionName, $appId, $e->getMessage()), ['exception' => $e]); + $this->logger->error(sprintf('ExApp %s FileAction %s not found. Error: %s', $appId, $fileActionName, $e->getMessage()), ['exception' => $e]); $fileAction = null; } return $fileAction; } public function handleFileAction(string $userId, string $appId, string $fileActionName, string $actionHandler, array $actionFile): bool { - try { - $exFileAction = $this->mapper->findByName($fileActionName); - } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { - $exFileAction = null; - } + $exFileAction = $this->getExAppFileAction($appId, $fileActionName); if ($exFileAction !== null) { $handler = $exFileAction->getActionHandler(); // route on ex app $params = [ @@ -206,12 +184,11 @@ public function handleFileAction(string $userId, string $appId, string $fileActi return $result->getStatusCode() === 200; } if (isset($result['error'])) { - $this->logger->error(sprintf('Failed to handle file action %s for EXApp: %s with error: %s', $fileActionName, $appId, $result['error'])); + $this->logger->error(sprintf('Failed to handle ExApp %s FileAction %s. Error: %s', $appId, $fileActionName, $result['error'])); return false; } } } - $this->logger->error(sprintf('Failed to find file action menu %s for ExApp: %s', $fileActionName, $appId)); return false; } @@ -239,7 +216,7 @@ public function loadFileActionIcon(string $appId, string $exFileActionName): ?ar ]; } } catch (\Exception $e) { - $this->logger->error(sprintf('Failed to load file action icon %s for ExApp: %s with error: %s', $exFileActionName, $appId, $e->getMessage()), ['exception' => $e]); + $this->logger->error(sprintf('Failed to load ExApp %s FileAction icon %s. Error: %s', $appId, $exFileActionName, $e->getMessage()), ['exception' => $e]); return null; } return null; From cb64c63e92f2ded82e3eb901885dd2538dbf66c9 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Thu, 20 Jul 2023 21:31:00 +0300 Subject: [PATCH 22/29] minor fixes --- lib/Command/ApiScopes/ListApiScopes.php | 2 +- lib/Service/AppEcosystemV2Service.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Command/ApiScopes/ListApiScopes.php b/lib/Command/ApiScopes/ListApiScopes.php index 956e4b69..68bd6f2d 100644 --- a/lib/Command/ApiScopes/ListApiScopes.php +++ b/lib/Command/ApiScopes/ListApiScopes.php @@ -29,7 +29,7 @@ * */ -namespace OCA\AppEcosystemV2\Command\Scopes; +namespace OCA\AppEcosystemV2\Command\ApiScopes; use OCA\AppEcosystemV2\Service\ExAppApiScopeService; diff --git a/lib/Service/AppEcosystemV2Service.php b/lib/Service/AppEcosystemV2Service.php index e550d075..b65154e4 100644 --- a/lib/Service/AppEcosystemV2Service.php +++ b/lib/Service/AppEcosystemV2Service.php @@ -558,6 +558,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) } } catch (Exception $e) { $this->logger->error(sprintf('Failed to get ExApp %s user %s. Error: %s', $exApp->getAppid(), $userId, $e->getMessage()), ['exception' => $e]); + return false; } } return $this->finalizeRequestToNC($userId); From bf3a311c982e4d97e49106935cc573b4560986f4 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Fri, 21 Jul 2023 15:27:42 +0300 Subject: [PATCH 23/29] file action fix --- lib/Service/ExFilesActionsMenuService.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Service/ExFilesActionsMenuService.php b/lib/Service/ExFilesActionsMenuService.php index bfcbcda2..5d1a4b8b 100644 --- a/lib/Service/ExFilesActionsMenuService.php +++ b/lib/Service/ExFilesActionsMenuService.php @@ -78,9 +78,9 @@ public function __construct( * @param string $appId * @param array $params * - * @return Entity|null + * @return ExFilesActionsMenu|null */ - public function registerFileActionMenu(string $appId, array $params): ?Entity { + public function registerFileActionMenu(string $appId, array $params): ?ExFilesActionsMenu { try { $fileActionMenu = $this->mapper->findByName($params['name']); } catch (DoesNotExistException|MultipleObjectsReturnedException|Exception) { @@ -151,7 +151,7 @@ public function getExAppFileAction(string $appId, string $fileActionName): ?ExFi $cacheKey = '/ex_files_actions_menu_' . $appId . '_' . $fileActionName; $cache = $this->cache->get($cacheKey); if ($cache !== null) { - return $cache; + return $cache instanceof ExFilesActionsMenu ? $cache : new ExFilesActionsMenu($cache); } try { From 0233af646330bd4b10075718eeda81a777949a0a Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Fri, 21 Jul 2023 15:29:45 +0300 Subject: [PATCH 24/29] cs fix --- lib/Service/ExFilesActionsMenuService.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Service/ExFilesActionsMenuService.php b/lib/Service/ExFilesActionsMenuService.php index 5d1a4b8b..507f8030 100644 --- a/lib/Service/ExFilesActionsMenuService.php +++ b/lib/Service/ExFilesActionsMenuService.php @@ -36,7 +36,6 @@ use OCA\AppEcosystemV2\Db\ExFilesActionsMenuMapper; use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Http; use OCP\DB\Exception; From 125acee7386721d5393e4f20377790bf0013d53e Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Fri, 21 Jul 2023 15:40:03 +0300 Subject: [PATCH 25/29] fix unregister file action --- lib/Service/ExFilesActionsMenuService.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Service/ExFilesActionsMenuService.php b/lib/Service/ExFilesActionsMenuService.php index 507f8030..055c125b 100644 --- a/lib/Service/ExFilesActionsMenuService.php +++ b/lib/Service/ExFilesActionsMenuService.php @@ -114,6 +114,9 @@ public function registerFileActionMenu(string $appId, array $params): ?ExFilesAc public function unregisterFileActionMenu(string $appId, string $fileActionMenuName): ?ExFilesActionsMenu { try { $fileActionMenu = $this->getExAppFileAction($appId, $fileActionMenuName); + if ($fileActionMenu === null) { + return null; + } $this->mapper->delete($fileActionMenu); $this->cache->remove('/ex_files_actions_menu_' . $appId . '_' . $fileActionMenuName); return $fileActionMenu; From cd0cdf82cd581437cffd102abf440e96b1801193 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Fri, 21 Jul 2023 17:22:08 +0300 Subject: [PATCH 26/29] added return not_found status --- lib/Controller/AppConfigController.php | 8 ++++---- lib/Controller/OCSApiController.php | 5 +++++ lib/Controller/PreferencesController.php | 8 ++++---- lib/Service/ExAppConfigService.php | 8 ++++---- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/Controller/AppConfigController.php b/lib/Controller/AppConfigController.php index e31f564a..601d1a5d 100644 --- a/lib/Controller/AppConfigController.php +++ b/lib/Controller/AppConfigController.php @@ -100,7 +100,7 @@ public function setAppConfigValue(string $configKey, mixed $configValue, string public function getAppConfigValues(array $configKeys, string $format = 'json'): Response { $appId = $this->request->getHeader('EX-APP-ID'); $result = $this->exAppConfigService->getAppConfigValues($appId, $configKeys); - return $this->buildResponse(new DataResponse($result, Http::STATUS_OK), $format); + return $this->buildResponse(new DataResponse($result, !empty($result) ? Http::STATUS_OK : Http::STATUS_NOT_FOUND), $format); } /** @@ -119,9 +119,9 @@ public function getAppConfigValues(array $configKeys, string $format = 'json'): public function deleteAppConfigValues(array $configKeys, string $format = 'json'): Response { $appId = $this->request->getHeader('EX-APP-ID'); $result = $this->exAppConfigService->deleteAppConfigValues($configKeys, $appId); - if ($result !== -1) { - return $this->buildResponse(new DataResponse($result, Http::STATUS_OK), $format); + if ($result === -1) { + throw new OCSBadRequestException('Error deleting app config values'); } - throw new OCSBadRequestException('Error deleting app config values'); + return $this->buildResponse(new DataResponse($result, $result !== 0 ? Http::STATUS_OK : Http::STATUS_NOT_FOUND), $format); } } diff --git a/lib/Controller/OCSApiController.php b/lib/Controller/OCSApiController.php index 3b96806f..2db289e5 100644 --- a/lib/Controller/OCSApiController.php +++ b/lib/Controller/OCSApiController.php @@ -223,6 +223,11 @@ public function registerFileActionMenu(array $fileActionMenuParams, string $form public function unregisterFileActionMenu(string $fileActionMenuName, string $format = 'json'): Response { $appId = $this->request->getHeader('EX-APP-ID'); $unregisteredFileActionMenu = $this->exFilesActionsMenuService->unregisterFileActionMenu($appId, $fileActionMenuName); + if ($unregisteredFileActionMenu === null) { + return $this->buildResponse(new DataResponse([ + 'error' => $this->l->t('FileActionMenu not found.'), + ], Http::STATUS_NOT_FOUND), $format); + } return $this->buildResponse(new DataResponse([ 'success' => $unregisteredFileActionMenu !== null, 'unregisteredFileActionMenu' => $unregisteredFileActionMenu, diff --git a/lib/Controller/PreferencesController.php b/lib/Controller/PreferencesController.php index 0dda75c6..59bfba85 100644 --- a/lib/Controller/PreferencesController.php +++ b/lib/Controller/PreferencesController.php @@ -106,7 +106,7 @@ public function getUserConfigValues(array $configKeys, string $format = 'json'): $userId = $this->userSession->getUser()->getUID(); $appId = $this->request->getHeader('EX-APP-ID'); $result = $this->exAppPreferenceService->getUserConfigValues($userId, $appId, $configKeys); - return $this->buildResponse(new DataResponse($result, Http::STATUS_OK), $format); + return $this->buildResponse(new DataResponse($result, !empty($result) ? Http::STATUS_OK : Http::STATUS_NOT_FOUND), $format); } /** @@ -126,9 +126,9 @@ public function deleteUserConfigValues(array $configKeys, string $format = 'json $userId = $this->userSession->getUser()->getUID(); $appId = $this->request->getHeader('EX-APP-ID'); $result = $this->exAppPreferenceService->deleteUserConfigValues($configKeys, $userId, $appId); - if ($result !== -1) { - return $this->buildResponse(new DataResponse($result, Http::STATUS_OK), $format); + if ($result === -1) { + throw new OCSBadRequestException('Failed to delete user config values'); } - throw new OCSBadRequestException('Failed to delete user config values'); + return $this->buildResponse(new DataResponse($result, $result !== 0 ? Http::STATUS_OK : Http::STATUS_NOT_FOUND), $format); } } diff --git a/lib/Service/ExAppConfigService.php b/lib/Service/ExAppConfigService.php index 265cc139..81e42210 100644 --- a/lib/Service/ExAppConfigService.php +++ b/lib/Service/ExAppConfigService.php @@ -76,7 +76,7 @@ public function getAppConfigValues(string $appId, array $configKeys): ?array { } /** - * Set app_config_ex value + * Set appconfig_ex value * * @param string $appId * @param string $configKey @@ -95,15 +95,15 @@ public function setAppConfigValue(string $appId, string $configKey, mixed $confi 'configvalue' => $configValue ?? '', 'sensitive' => $sensitive, ])); - } catch (\Exception $e) { - $this->logger->error('Error while inserting app_config_ex value: ' . $e->getMessage(), ['exception' => $e]); + } catch (Exception $e) { + $this->logger->error(sprintf('Failed to insert appconfig_ex value. Error: %s', $e->getMessage()), ['exception' => $e]); return null; } } else { $appConfigEx->setConfigvalue($configValue); $appConfigEx->setSensitive($sensitive); if ($this->updateAppConfigValue($appConfigEx) !== 1) { - $this->logger->error('Error while updating app_config_ex value'); + $this->logger->error(sprintf('Error while updating appconfig_ex %s value.', $configKey)); return null; } } From f2ef0fda71e26afd0b8b02d348c96f0aec845ca4 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Fri, 21 Jul 2023 17:25:09 +0300 Subject: [PATCH 27/29] fix psalm --- lib/Controller/OCSApiController.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/Controller/OCSApiController.php b/lib/Controller/OCSApiController.php index 2db289e5..38235a6f 100644 --- a/lib/Controller/OCSApiController.php +++ b/lib/Controller/OCSApiController.php @@ -228,10 +228,7 @@ public function unregisterFileActionMenu(string $fileActionMenuName, string $for 'error' => $this->l->t('FileActionMenu not found.'), ], Http::STATUS_NOT_FOUND), $format); } - return $this->buildResponse(new DataResponse([ - 'success' => $unregisteredFileActionMenu !== null, - 'unregisteredFileActionMenu' => $unregisteredFileActionMenu, - ], Http::STATUS_OK), $format); + return $this->buildResponse(new DataResponse($unregisteredFileActionMenu, Http::STATUS_OK), $format); } /** From 723f08d28017e1612cf905c11a646a66138365c5 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Fri, 21 Jul 2023 17:53:30 +0300 Subject: [PATCH 28/29] fix ocs not_found --- lib/Controller/AppConfigController.php | 7 ++++++- lib/Controller/OCSApiController.php | 6 +++--- lib/Controller/PreferencesController.php | 6 +++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/Controller/AppConfigController.php b/lib/Controller/AppConfigController.php index 601d1a5d..b5a77e5d 100644 --- a/lib/Controller/AppConfigController.php +++ b/lib/Controller/AppConfigController.php @@ -42,6 +42,7 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; use OCP\IRequest; @@ -111,6 +112,7 @@ public function getAppConfigValues(array $configKeys, string $format = 'json'): * @param string $format * * @throws OCSBadRequestException + * @throws OCSNotFoundException * @return Response */ #[AppEcosystemAuth] @@ -122,6 +124,9 @@ public function deleteAppConfigValues(array $configKeys, string $format = 'json' if ($result === -1) { throw new OCSBadRequestException('Error deleting app config values'); } - return $this->buildResponse(new DataResponse($result, $result !== 0 ? Http::STATUS_OK : Http::STATUS_NOT_FOUND), $format); + if ($result === 0) { + throw new OCSNotFoundException('No appconfig_ex values deleted'); + } + return $this->buildResponse(new DataResponse($result, Http::STATUS_OK), $format); } } diff --git a/lib/Controller/OCSApiController.php b/lib/Controller/OCSApiController.php index 38235a6f..e7674455 100644 --- a/lib/Controller/OCSApiController.php +++ b/lib/Controller/OCSApiController.php @@ -45,6 +45,7 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; use OCP\IL10N; use OCP\IRequest; @@ -215,6 +216,7 @@ public function registerFileActionMenu(array $fileActionMenuParams, string $form * @param string $fileActionMenuName * @param string $format * + * @throws OCSNotFoundException * @return Response */ #[AppEcosystemAuth] @@ -224,9 +226,7 @@ public function unregisterFileActionMenu(string $fileActionMenuName, string $for $appId = $this->request->getHeader('EX-APP-ID'); $unregisteredFileActionMenu = $this->exFilesActionsMenuService->unregisterFileActionMenu($appId, $fileActionMenuName); if ($unregisteredFileActionMenu === null) { - return $this->buildResponse(new DataResponse([ - 'error' => $this->l->t('FileActionMenu not found.'), - ], Http::STATUS_NOT_FOUND), $format); + throw new OCSNotFoundException('FileActionMenu not found'); } return $this->buildResponse(new DataResponse($unregisteredFileActionMenu, Http::STATUS_OK), $format); } diff --git a/lib/Controller/PreferencesController.php b/lib/Controller/PreferencesController.php index 59bfba85..7b6cf26d 100644 --- a/lib/Controller/PreferencesController.php +++ b/lib/Controller/PreferencesController.php @@ -42,6 +42,7 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; use OCP\IRequest; use OCP\IUserSession; @@ -129,6 +130,9 @@ public function deleteUserConfigValues(array $configKeys, string $format = 'json if ($result === -1) { throw new OCSBadRequestException('Failed to delete user config values'); } - return $this->buildResponse(new DataResponse($result, $result !== 0 ? Http::STATUS_OK : Http::STATUS_NOT_FOUND), $format); + if ($result === 0) { + throw new OCSNotFoundException('No preferences_ex values deleted'); + } + return $this->buildResponse(new DataResponse($result, Http::STATUS_OK), $format); } } From 1337107614b8cc93d3825c26d337d95fd57d707b Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Sat, 22 Jul 2023 14:02:06 +0300 Subject: [PATCH 29/29] to return `OCSNotFoundException` in `unregisterFileActionMenu` Signed-off-by: Alexander Piskun --- lib/Controller/OCSApiController.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/Controller/OCSApiController.php b/lib/Controller/OCSApiController.php index e7674455..95bc6555 100644 --- a/lib/Controller/OCSApiController.php +++ b/lib/Controller/OCSApiController.php @@ -214,21 +214,20 @@ public function registerFileActionMenu(array $fileActionMenuParams, string $form * @NoCSRFRequired * * @param string $fileActionMenuName - * @param string $format * * @throws OCSNotFoundException - * @return Response + * @return DataResponse */ #[AppEcosystemAuth] #[PublicPage] #[NoCSRFRequired] - public function unregisterFileActionMenu(string $fileActionMenuName, string $format = 'json'): Response { + public function unregisterFileActionMenu(string $fileActionMenuName): DataResponse { $appId = $this->request->getHeader('EX-APP-ID'); $unregisteredFileActionMenu = $this->exFilesActionsMenuService->unregisterFileActionMenu($appId, $fileActionMenuName); if ($unregisteredFileActionMenu === null) { throw new OCSNotFoundException('FileActionMenu not found'); } - return $this->buildResponse(new DataResponse($unregisteredFileActionMenu, Http::STATUS_OK), $format); + return new DataResponse(); } /**