From eea14dc504484f7fc63cebddbcb048aa8eb38c37 Mon Sep 17 00:00:00 2001 From: Andrey Borysenko Date: Thu, 11 Jul 2024 20:08:52 +0300 Subject: [PATCH] optimization: system flag and ex_app_users removal (#323) The system flag and extra table for that `ex_app_users` is removed to optimize database usage and simplify the system logic. --------- Signed-off-by: Andrey Borysenko --- .github/workflows/tests-special.yml | 19 +-- CHANGELOG.md | 2 + appinfo/info.xml | 1 - docs/Concepts.rst | 9 -- docs/ManagingExternalApplications.rst | 14 +- docs/tech_details/Authentication.rst | 14 +- lib/AppInfo/Application.php | 3 - lib/Command/ExApp/Notify.php | 2 +- lib/Command/ExApp/Users/ListUsers.php | 52 -------- lib/Controller/ExAppProxyController.php | 8 +- lib/Controller/ExAppsPageController.php | 6 - lib/Controller/OCSExAppController.php | 7 +- lib/Controller/TopMenuController.php | 3 - lib/Db/ExApp.php | 8 -- lib/Db/ExAppMapper.php | 2 - lib/Db/ExAppUser.php | 49 ------- lib/Db/ExAppUserMapper.php | 91 ------------- lib/Listener/UserDeletedListener.php | 41 ------ .../Version2800Date20240710220000.php | 39 ++++++ lib/PublicFunctions.php | 8 +- lib/Service/AppAPIService.php | 84 +----------- lib/Service/ExAppApiScopeService.php | 80 ++++++------ lib/Service/ExAppService.php | 17 +-- lib/Service/ExAppUsersService.php | 120 ------------------ .../ProvidersAI/SpeechToTextService.php | 2 +- .../ProvidersAI/TextProcessingService.php | 2 +- .../ProvidersAI/TranslationService.php | 4 +- src/components/Apps/DaemonDetails.vue | 6 - src/store/apps.js | 4 - 29 files changed, 117 insertions(+), 580 deletions(-) delete mode 100644 lib/Command/ExApp/Users/ListUsers.php delete mode 100644 lib/Db/ExAppUser.php delete mode 100644 lib/Db/ExAppUserMapper.php delete mode 100644 lib/Listener/UserDeletedListener.php create mode 100644 lib/Migration/Version2800Date20240710220000.php delete mode 100644 lib/Service/ExAppUsersService.php diff --git a/.github/workflows/tests-special.yml b/.github/workflows/tests-special.yml index 2253348b..b8c7d319 100644 --- a/.github/workflows/tests-special.yml +++ b/.github/workflows/tests-special.yml @@ -228,7 +228,7 @@ jobs: - name: Check logs run: grep -q 'Hello from ' data/nextcloud.log || error - - name: Test ALL Scope, System App + - name: Test ALL Scope run: python3 apps/${{ env.APP_NAME }}/tests/auth_scopes_system.py 0 - name: Re-Register App @@ -243,24 +243,9 @@ jobs: kill -15 $(cat /tmp/_install.pid) timeout 3m tail --pid=$(cat /tmp/_install.pid) -f /dev/null - - name: Test NO ALL Scope, System App + - name: Test NO ALL Scope run: python3 apps/${{ env.APP_NAME }}/tests/auth_scopes_system.py 1 - - name: Re-Register App - run: | - php occ app_api:app:unregister $APP_ID --silent --force || true - python3 apps/${{ env.APP_NAME }}/tests/install_no_init.py & - echo $! > /tmp/_install.pid - sleep 5s - php occ app_api:app:register $APP_ID manual_install --json-info \ - "{\"appid\":\"$APP_ID\",\"name\":\"$APP_ID\",\"daemon_config_name\":\"manual_install\",\"version\":\"$APP_VERSION\",\"secret\":\"$APP_SECRET\",\"port\":$APP_PORT,\"scopes\":[\"ALL\"],\"system_app\":0}" \ - --force-scopes --wait-finish - kill -15 $(cat /tmp/_install.pid) - timeout 3m tail --pid=$(cat /tmp/_install.pid) -f /dev/null - - - name: Test NO ALL Scope, NO System App - run: python3 apps/${{ env.APP_NAME }}/tests/auth_scopes_system.py 2 - - name: Upload NC logs if: always() uses: actions/upload-artifact@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 99d7a1a2..0d4abed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ Note: Nextcloud 27 is no longer supported since this version. ### Changed - Dropped support of Nextcloud 27. #322 +- ExApp system flag is now deprecated and removed to optimize performance and simplicity. #323 +- PublicFunctions changes: `exAppRequestWithUserInit` and `asyncExAppRequestWithUserInit` are now deprecated. #323 ### Fixed diff --git a/appinfo/info.xml b/appinfo/info.xml index ec6f16ca..78226de2 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -89,7 +89,6 @@ to join us in shaping a more versatile, stable, and secure app landscape. OCA\AppAPI\Command\ExApp\ListExApps OCA\AppAPI\Command\ExApp\Notify OCA\AppAPI\Command\ExApp\Scopes\ListScopes - OCA\AppAPI\Command\ExApp\Users\ListUsers OCA\AppAPI\Command\ExAppConfig\GetConfig OCA\AppAPI\Command\ExAppConfig\SetConfig OCA\AppAPI\Command\ExAppConfig\DeleteConfig diff --git a/docs/Concepts.rst b/docs/Concepts.rst index ae731907..c944cc69 100644 --- a/docs/Concepts.rst +++ b/docs/Concepts.rst @@ -9,15 +9,6 @@ Each application defines list of API groups it intends to access. This system easily allows you to increase the level of trust in applications. Even prior to installation, it's possible to ascertain the API groups to which an application will gain access. -System & Non System apps ------------------------- - -The main difference between a System app and a regular app is that a System app can impersonate a user who hasn't interacted with the app before. -And normal applications cannot impersonate a user and call any APIs in the context of the user if the user has never used this application before. - -Given that numerous applications do not require system-level status, -this approach significantly heightens security and fosters greater trust in new and lesser-known applications. - Extensible Deployment --------------------- diff --git a/docs/ManagingExternalApplications.rst b/docs/ManagingExternalApplications.rst index 2eb34bae..6d9379dc 100644 --- a/docs/ManagingExternalApplications.rst +++ b/docs/ManagingExternalApplications.rst @@ -103,18 +103,8 @@ ListExApps command will show all ExApps: ExApps: appid (Display Name): version [enabled/disabled] - to_gif (ToGif): 1.0.0 [enabled] - upscaler_demo (Upscaler Demo): 1.0.0 [enabled] - -List ExApp users ----------------- - -Command: ``app_api:app:users:list `` - -System user -*********** - -System user (``[system user]``) in the list means that this ExApp was setup as a system ExApp. + to_gif_example (To Gif Example): 1.0.0 [enabled] + upscaler_example (Upscaler Example): 1.0.0 [enabled] Using the ExApp Management UI ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/tech_details/Authentication.rst b/docs/tech_details/Authentication.rst index 8e99daba..7fbed18f 100644 --- a/docs/tech_details/Authentication.rst +++ b/docs/tech_details/Authentication.rst @@ -65,8 +65,6 @@ Authentication flow in details AppAPI-->>Nextcloud: Reject if secret does not match AppAPI-->>AppAPI: Check API scope AppAPI-->>Nextcloud: Reject if API scope not match - AppAPI-->>AppAPI: Check if user interacted with ExApp - AppAPI-->>Nextcloud: Reject if user has not interacted with ExApp (attempt to bypass user) AppAPI-->>AppAPI: Check if user is not empty and active AppAPI-->>Nextcloud: Set active user AppAPI->>-Nextcloud: Request accepted/rejected @@ -87,13 +85,9 @@ After successful authentication AppAPI sets `app_api` session key to ``true``. .. code-block:: php $this->session->set('app_api', true); + $this->session->set('app_api_system', true); // deprecated since AppAPI 2.8.0 -.. note:: The Nextcloud server verifies this session key and allows **CORS protection** and **Two-Factor authentication** to be bypassed for requests coming from ExApps. +.. note:: -For ``System`` applications additional flag is set: - -.. code-block:: php - - $this->session->set('app_api_system', true); - -.. note:: The Nextcloud Server skips rate limiting for requests coming from ``System`` ExApps. + The Nextcloud server verifies this session key and allows **CORS protection** and **Two-Factor authentication** to be bypassed for requests coming from ExApps. + Also the rate limit is not applied to requests coming from ExApps. diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f16aff31..ad4a051a 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -12,7 +12,6 @@ use OCA\AppAPI\Listener\FileEventsListener; use OCA\AppAPI\Listener\LoadFilesPluginListener; use OCA\AppAPI\Listener\SabrePluginAuthInitListener; -use OCA\AppAPI\Listener\UserDeletedListener; use OCA\AppAPI\Middleware\AppAPIAuthMiddleware; use OCA\AppAPI\Middleware\ExAppUIL10NMiddleware; use OCA\AppAPI\Middleware\ExAppUiMiddleware; @@ -47,7 +46,6 @@ use OCP\Settings\Events\DeclarativeSettingsGetValueEvent; use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent; use OCP\Settings\Events\DeclarativeSettingsSetValueEvent; -use OCP\User\Events\UserDeletedEvent; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Throwable; @@ -74,7 +72,6 @@ public function register(IRegistrationContext $context): void { $context->registerMiddleware(ExAppUiMiddleware::class); $context->registerMiddleware(ExAppUIL10NMiddleware::class, true); $context->registerEventListener(SabrePluginAuthInitEvent::class, SabrePluginAuthInitListener::class); - $context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class); $context->registerNotifierService(ExAppNotifier::class); $context->registerNotifierService(ExAppAdminNotifier::class); diff --git a/lib/Command/ExApp/Notify.php b/lib/Command/ExApp/Notify.php index 315b40ac..77ab2b97 100644 --- a/lib/Command/ExApp/Notify.php +++ b/lib/Command/ExApp/Notify.php @@ -53,7 +53,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $route = $input->getArgument('route'); $userId = $input->getOption('user-id'); - $response = $this->service->aeRequestToExApp($exApp, $route, $userId, params: $eventJsonData, + $response = $this->service->requestToExApp($exApp, $route, $userId, params: $eventJsonData, options: ['timeout' => 2]); if (is_array($response) && isset($response['error'])) { $output->writeln(sprintf('Failed to notify ExApp %s: %s', $appId, $response['error'])); diff --git a/lib/Command/ExApp/Users/ListUsers.php b/lib/Command/ExApp/Users/ListUsers.php deleted file mode 100644 index aa1c87a4..00000000 --- a/lib/Command/ExApp/Users/ListUsers.php +++ /dev/null @@ -1,52 +0,0 @@ -setName('app_api: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($appId); - 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/Controller/ExAppProxyController.php b/lib/Controller/ExAppProxyController.php index d2b992b1..4a61249c 100644 --- a/lib/Controller/ExAppProxyController.php +++ b/lib/Controller/ExAppProxyController.php @@ -81,7 +81,7 @@ public function ExAppGet(string $appId, string $other): Response { return new NotFoundResponse(); } - $response = $this->service->aeRequestToExApp2( + $response = $this->service->requestToExApp2( $exApp, '/' . $other, $this->userId, 'GET', queryParams: $_GET, options: [ RequestOptions::COOKIES => $this->buildProxyCookiesJar($_COOKIE, $this->service->getExAppDomain($exApp)), ], @@ -110,7 +110,7 @@ public function ExAppPost(string $appId, string $other): Response { } $bodyParams = $this->prepareBodyParams($this->request->getParams()); - $response = $this->service->aeRequestToExApp2( + $response = $this->service->requestToExApp2( $exApp, '/' . $other, $this->userId, queryParams: $_GET, bodyParams: $bodyParams, options: $options, request: $this->request, @@ -138,7 +138,7 @@ public function ExAppPut(string $appId, string $other): Response { } $bodyParams = $this->prepareBodyParams($this->request->getParams()); - $response = $this->service->aeRequestToExApp2( + $response = $this->service->requestToExApp2( $exApp, '/' . $other, $this->userId, 'PUT', queryParams: $_GET, bodyParams: $bodyParams, options: $options, request: $this->request, @@ -158,7 +158,7 @@ public function ExAppDelete(string $appId, string $other): Response { } $bodyParams = $this->prepareBodyParams($this->request->getParams()); - $response = $this->service->aeRequestToExApp2( + $response = $this->service->requestToExApp2( $exApp, '/' . $other, $this->userId, 'DELETE', queryParams: $_GET, bodyParams: $bodyParams, options: [ RequestOptions::COOKIES => $this->buildProxyCookiesJar($_COOKIE, $this->service->getExAppDomain($exApp)), diff --git a/lib/Controller/ExAppsPageController.php b/lib/Controller/ExAppsPageController.php index d2682977..1dade990 100644 --- a/lib/Controller/ExAppsPageController.php +++ b/lib/Controller/ExAppsPageController.php @@ -18,7 +18,6 @@ use OCA\AppAPI\Service\DaemonConfigService; use OCA\AppAPI\Service\ExAppApiScopeService; use OCA\AppAPI\Service\ExAppService; -use OCA\AppAPI\Service\ExAppUsersService; use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; @@ -51,7 +50,6 @@ class ExAppsPageController extends Controller { private IL10N $l10n; private LoggerInterface $logger; private IAppManager $appManager; - private ExAppUsersService $exAppUsersService; public function __construct( IRequest $request, @@ -60,7 +58,6 @@ public function __construct( AppAPIService $service, DaemonConfigService $daemonConfigService, ExAppApiScopeService $exAppApiScopeService, - ExAppUsersService $exAppUsersService, DockerActions $dockerActions, CategoryFetcher $categoryFetcher, IFactory $l10nFactory, @@ -84,7 +81,6 @@ public function __construct( $this->exAppFetcher = $exAppFetcher; $this->logger = $logger; $this->appManager = $appManager; - $this->exAppUsersService = $exAppUsersService; } #[NoCSRFRequired] @@ -243,7 +239,6 @@ private function getAppsForCategory(string $requestedCategory = ''): array { 'appstoreData' => $app, 'scopes' => $scopes, 'daemon' => $daemon, - 'systemApp' => $exApp !== null && $this->exAppUsersService->exAppUserExists($exApp->getAppid(), ''), 'status' => $exApp !== null ? $exApp->getStatus() : [], 'error' => $exApp !== null ? $exApp->getStatus()['error'] ?? '' : '', ]; @@ -375,7 +370,6 @@ private function buildLocalAppsList(array $apps, array $exApps): array { 'appstoreData' => $app, 'scopes' => $scopes, 'daemon' => $daemon, - 'systemApp' => $this->exAppUsersService->exAppUserExists($exApp->getAppid(), ''), 'exAppUrl' => $this->service->getExAppUrl($exApp, $exApp->getPort(), $auth), 'releases' => [], 'update' => null, diff --git a/lib/Controller/OCSExAppController.php b/lib/Controller/OCSExAppController.php index de7730e4..b9bcaf41 100644 --- a/lib/Controller/OCSExAppController.php +++ b/lib/Controller/OCSExAppController.php @@ -98,6 +98,11 @@ public function requestToExApp( ]); } + /** + * TODO: remove later + * + * @deprecated since AppAPI 2.8.0 + */ #[NoCSRFRequired] public function aeRequestToExApp( string $appId, @@ -111,7 +116,7 @@ public function aeRequestToExApp( if ($exApp === null) { return new DataResponse(['error' => sprintf('ExApp `%s` not found', $appId)]); } - $response = $this->service->aeRequestToExApp($exApp, $route, $userId, $method, $params, $options, $this->request); + $response = $this->service->requestToExApp($exApp, $route, $userId, $method, $params, $options, $this->request); if (is_array($response) && isset($response['error'])) { return new DataResponse($response, Http::STATUS_BAD_REQUEST); } diff --git a/lib/Controller/TopMenuController.php b/lib/Controller/TopMenuController.php index 9a47a5fe..e5b500e5 100644 --- a/lib/Controller/TopMenuController.php +++ b/lib/Controller/TopMenuController.php @@ -6,7 +6,6 @@ use OCA\AppAPI\AppInfo\Application; use OCA\AppAPI\Service\ExAppService; -use OCA\AppAPI\Service\ExAppUsersService; use OCA\AppAPI\Service\UI\InitialStateService; use OCA\AppAPI\Service\UI\ScriptsService; use OCA\AppAPI\Service\UI\StylesService; @@ -34,7 +33,6 @@ public function __construct( private readonly InitialStateService $initialStateService, private readonly ScriptsService $scriptsService, private readonly StylesService $stylesService, - private readonly ExAppUsersService $exAppUsersService, private readonly ExAppService $service, private readonly ?string $userId, private readonly IGroupManager $groupManager, @@ -70,7 +68,6 @@ public function viewExAppPage(string $appId, string $name, string $other): Templ $this->stylesService->applyExAppStyles($appId, 'top_menu', $menuEntry->getName()); $this->postprocess = true; - $this->exAppUsersService->setupExAppUser($exApp->getAppid(), $this->userId); $response = new TemplateResponse(Application::APP_ID, 'embedded'); $csp = new ContentSecurityPolicy(); $csp->addAllowedScriptDomain($this->request->getServerHost()); diff --git a/lib/Db/ExApp.php b/lib/Db/ExApp.php index 054c2fe1..d7ed9248 100644 --- a/lib/Db/ExApp.php +++ b/lib/Db/ExApp.php @@ -24,7 +24,6 @@ * @method int getEnabled() * @method int getCreatedTime() * @method int getLastCheckTime() - * @method int getIsSystem() * @method array getApiScopes() * @method array getDeployConfig() * @method string getAcceptsDeployId() @@ -40,7 +39,6 @@ * @method void setEnabled(int $enabled) * @method void setCreatedTime(int $createdTime) * @method void setLastCheckTime(int $lastCheckTime) - * @method void setIsSystem(int $system) * @method void setApiScopes(array $apiScopes) * @method void setDeployConfig(array $deployConfig) * @method void setAcceptsDeployId(string $acceptsDeployId) @@ -58,7 +56,6 @@ class ExApp extends Entity implements JsonSerializable { protected $enabled; protected $createdTime; protected $lastCheckTime; - protected $isSystem; protected $apiScopes; protected $deployConfig; protected $acceptsDeployId; @@ -79,7 +76,6 @@ public function __construct(array $params = []) { $this->addType('enabled', 'int'); $this->addType('createdTime', 'int'); $this->addType('lastCheckTime', 'int'); - $this->addType('isSystem', 'int'); $this->addType('apiScopes', 'json'); $this->addType('deployConfig', 'json'); $this->addType('acceptsDeployId', 'string'); @@ -123,9 +119,6 @@ public function __construct(array $params = []) { if (isset($params['last_check_time'])) { $this->setLastCheckTime($params['last_check_time']); } - if (isset($params['is_system'])) { - $this->setIsSystem($params['is_system']); - } if (isset($params['api_scopes'])) { $this->setApiScopes($params['api_scopes']); } else { @@ -154,7 +147,6 @@ public function jsonSerialize(): array { 'enabled' => $this->getEnabled(), 'created_time' => $this->getCreatedTime(), 'last_check_time' => $this->getLastCheckTime(), - 'is_system' => $this->getIsSystem(), 'api_scopes' => $this->getApiScopes(), 'deploy_config' => $this->getDeployConfig(), 'accepts_deploy_id' => $this->getAcceptsDeployId(), diff --git a/lib/Db/ExAppMapper.php b/lib/Db/ExAppMapper.php index 18cf3e4d..e4b8eb81 100644 --- a/lib/Db/ExAppMapper.php +++ b/lib/Db/ExAppMapper.php @@ -114,8 +114,6 @@ public function updateExApp(ExApp $exApp, array $fields): int { $qb = $qb->set('enabled', $qb->createNamedParameter($exApp->getEnabled(), IQueryBuilder::PARAM_INT)); } elseif ($field === 'last_check_time') { $qb = $qb->set('last_check_time', $qb->createNamedParameter($exApp->getLastCheckTime(), IQueryBuilder::PARAM_INT)); - } elseif ($field === 'is_system') { - $qb = $qb->set('is_system', $qb->createNamedParameter($exApp->getIsSystem(), IQueryBuilder::PARAM_INT)); } elseif ($field === 'api_scopes') { $qb = $qb->set('api_scopes', $qb->createNamedParameter($exApp->getApiScopes(), IQueryBuilder::PARAM_JSON)); } diff --git a/lib/Db/ExAppUser.php b/lib/Db/ExAppUser.php deleted file mode 100644 index 04859757..00000000 --- a/lib/Db/ExAppUser.php +++ /dev/null @@ -1,49 +0,0 @@ -addType('appid', 'string'); - $this->addType('userid', 'string'); - - if (isset($params['id'])) { - $this->setId($params['id']); - } - if (isset($params['appid'])) { - $this->setAppid($params['appid']); - } - if (isset($params['userid'])) { - $this->setUserid($params['userid']); - } - } - - public function jsonSerialize(): array { - return [ - 'id' => $this->getId(), - 'appid' => $this->getAppid(), - 'userid' => $this->getUserid(), - ]; - } -} diff --git a/lib/Db/ExAppUserMapper.php b/lib/Db/ExAppUserMapper.php deleted file mode 100644 index 70a06e1b..00000000 --- a/lib/Db/ExAppUserMapper.php +++ /dev/null @@ -1,91 +0,0 @@ - - */ -class ExAppUserMapper extends QBMapper { - public function __construct(IDBConnection $db) { - parent::__construct($db, 'ex_apps_users'); - } - - /** - * @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 - * - * @throws Exception - * - * @return ExAppUser[] - */ - public function findByAppidUserid(string $appId, string $userId): array { - $qb = $this->db->getQueryBuilder(); - return $this->findEntities($qb->select('*') - ->from($this->tableName) - ->where( - $qb->expr()->eq('appid', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR)), - $qb->expr()->eq('userid', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))) - ->orWhere( - $qb->expr()->eq('appid', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR)), - $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(); - } - - /** - * @param string $appId - * @param string $userId - * - * @throws Exception - * @return int - */ - public function deleteByAppidUserid(string $appId, string $userId): int { - $qb = $this->db->getQueryBuilder(); - return $qb->delete($this->tableName) - ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appId, IQueryBuilder::PARAM_STR))) - ->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))) - ->executeStatement(); - } - - /** - * @param string $userId - * - * @throws Exception - * @return int - */ - public function deleteByUserid(string $userId): int { - $qb = $this->db->getQueryBuilder(); - return $qb->delete($this->tableName) - ->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))) - ->executeStatement(); - } -} diff --git a/lib/Listener/UserDeletedListener.php b/lib/Listener/UserDeletedListener.php deleted file mode 100644 index d8995cad..00000000 --- a/lib/Listener/UserDeletedListener.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -class UserDeletedListener implements IEventListener { - - public function __construct( - private LoggerInterface $logger, - private ExAppUsersService $exAppUsersService) { - } - - public function handle(Event $event): void { - if (!($event instanceof UserDeletedEvent)) { - return; - } - - // Delete ExApp user record on user deletion - try { - $this->exAppUsersService->removeDeletedUser($event->getUser()->getUID()); - } catch (Exception $e) { - // Ignore exceptions - $this->logger->info('Could not delete ExApp user ' . $event->getUser()->getUID(), [ - 'exception' => $e, - ]); - } - } -} diff --git a/lib/Migration/Version2800Date20240710220000.php b/lib/Migration/Version2800Date20240710220000.php new file mode 100644 index 00000000..b72995d6 --- /dev/null +++ b/lib/Migration/Version2800Date20240710220000.php @@ -0,0 +1,39 @@ +hasTable('ex_apps')) { + $table = $schema->getTable('ex_apps'); + if ($table->hasColumn('is_system')) { + $table->dropColumn('is_system'); + } + } + + if ($schema->hasTable('ex_apps_users')) { + $schema->dropTable('ex_apps_users'); + } + + return $schema; + } +} diff --git a/lib/PublicFunctions.php b/lib/PublicFunctions.php index 2b336fb8..5f158b4f 100644 --- a/lib/PublicFunctions.php +++ b/lib/PublicFunctions.php @@ -39,6 +39,8 @@ public function exAppRequest( /** * Request to ExApp with AppAPI auth headers and ExApp user initialization + * + * @depreacted since AppAPI 2.8.0, use `exAppRequest` instead */ public function exAppRequestWithUserInit( string $appId, @@ -53,7 +55,7 @@ public function exAppRequestWithUserInit( if ($exApp === null) { return ['error' => sprintf('ExApp `%s` not found', $appId)]; } - return $this->service->aeRequestToExApp($exApp, $route, $userId, $method, $params, $options, $request); + return $this->service->requestToExApp($exApp, $route, $userId, $method, $params, $options, $request); } /** @@ -81,6 +83,8 @@ public function asyncExAppRequest( * Async request to ExApp with AppAPI auth headers and ExApp user initialization * * @throws \Exception if ExApp not found or failed to setup ExApp user + * + * @depreacted since AppAPI 2.8.0, use `asyncExAppRequest` instead */ public function asyncExAppRequestWithUserInit( string $appId, @@ -95,6 +99,6 @@ public function asyncExAppRequestWithUserInit( if ($exApp === null) { throw new \Exception(sprintf('ExApp `%s` not found', $appId)); } - return $this->service->aeRequestToExAppAsync($exApp, $route, $userId, $method, $params, $options, $request); + return $this->service->requestToExAppAsync($exApp, $route, $userId, $method, $params, $options, $request); } } diff --git a/lib/Service/AppAPIService.php b/lib/Service/AppAPIService.php index 8b54ab70..4dc16261 100644 --- a/lib/Service/AppAPIService.php +++ b/lib/Service/AppAPIService.php @@ -38,7 +38,6 @@ public function __construct( private readonly IFactory $l10nFactory, private readonly ExNotificationsManager $exNotificationsManager, private readonly ExAppService $exAppService, - private readonly ExAppUsersService $exAppUsersService, private readonly ExAppApiScopeService $exAppApiScopeService, private readonly DockerActions $dockerActions, private readonly ManualActions $manualActions, @@ -47,50 +46,6 @@ public function __construct( $this->client = $clientService->newClient(); } - /** - * Request to ExApp with AppAPI auth headers and ExApp user initialization - */ - public function aeRequestToExApp( - ExApp $exApp, - string $route, - ?string $userId = null, - string $method = 'POST', - array $params = [], - array $options = [], - ?IRequest $request = null, - ): array|IResponse { - try { - $this->exAppUsersService->setupExAppUser($exApp->getAppid(), $userId); - } catch (\Exception $e) { - $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($exApp, $route, $userId, $method, $params, $options, $request); - } - - /** - * Request to ExApp with AppAPI auth headers and ExApp user initialization - * with more correct query/body params handling - */ - public function aeRequestToExApp2( - ExApp $exApp, - string $route, - ?string $userId = null, - string $method = 'POST', - array $queryParams = [], - array $bodyParams = [], - array $options = [], - ?IRequest $request = null, - ): array|IResponse { - try { - $this->exAppUsersService->setupExAppUser($exApp->getAppid(), $userId); - } catch (\Exception $e) { - $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->requestToExApp2($exApp, $route, $userId, $method, $queryParams, $bodyParams, $options, $request); - } - /** * Request to ExApp with AppAPI auth headers */ @@ -145,23 +100,6 @@ private function requestToExAppInternal( } } - /** - * @throws \Exception - */ - public function aeRequestToExAppAsync( - ExApp $exApp, - string $route, - ?string $userId = null, - string $method = 'POST', - array $params = [], - array $options = [], - ?IRequest $request = null, - ): IPromise { - $this->exAppUsersService->setupExAppUser($exApp->getAppid(), $userId); - $requestData = $this->prepareRequestToExApp($exApp, $route, $userId, $method, $params, $options, $request); - return $this->requestToExAppInternalAsync($exApp, $method, $requestData['url'], $requestData['options']); - } - /** * @throws \Exception */ @@ -320,7 +258,6 @@ private function getUriEncodedParams(array $params): string { * - checks ExApp scopes <-> ExApp API copes * * More info in docs: https://cloud-py-api.github.io/app_api/authentication.html - * */ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false): bool { $this->throttler->sleepDelayOrThrowOnMax($request->getRemoteAddress(), Application::APP_ID); @@ -387,19 +324,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) } } - // For APIs that not assuming work under user context we do not check ExApp users - if ((!$exApp->getIsSystem()) && (($apiScope === null) or ($apiScope['user_check']))) { - 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 false; - } - } - return $this->finalizeRequestToNC($userId, $request, $exApp->getIsSystem()); + return $this->finalizeRequestToNC($userId, $request); } else { $this->logger->error(sprintf('Invalid signature for ExApp: %s and user: %s.', $exApp->getAppid(), $userId !== '' ? $userId : 'null')); $this->throttler->registerAttempt(Application::APP_ID, $request->getRemoteAddress(), [ @@ -416,9 +341,8 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false) * Final step of AppAPI authentication request validation for Nextcloud: * - sets active user (null if not a user context) * - updates ExApp last response time - * */ - private function finalizeRequestToNC(string $userId, IRequest $request, int $isSystemApp): bool { + private function finalizeRequestToNC(string $userId, IRequest $request): bool { if ($userId !== '') { $activeUser = $this->userManager->get($userId); if ($activeUser === null) { @@ -430,9 +354,7 @@ private function finalizeRequestToNC(string $userId, IRequest $request, int $isS $this->userSession->setUser(null); } $this->session->set('app_api', true); - if ($isSystemApp === 1) { - $this->session->set('app_api_system', true); - } + $this->session->set('app_api_system', true); // TODO: Remove after drop support NC29 $this->throttler->resetDelay($request->getRemoteAddress(), Application::APP_ID, [ 'appid' => $request->getHeader('EX-APP-ID'), diff --git a/lib/Service/ExAppApiScopeService.php b/lib/Service/ExAppApiScopeService.php index 65b5d70f..c221ce69 100644 --- a/lib/Service/ExAppApiScopeService.php +++ b/lib/Service/ExAppApiScopeService.php @@ -18,52 +18,52 @@ public function __construct( $aeApiV2Prefix = '/apps/' . Application::APP_ID . '/api/v2'; $this->apiScopes = [ // AppAPI scopes - ['api_route' => $aeApiV1Prefix . '/ui/files-actions-menu', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV2Prefix . '/ui/files-actions-menu', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/ui/top-menu', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/ui/initial-state', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/ui/script', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/ui/style', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/ui/settings', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/log', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/ex-app/config', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/ex-app/preference', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/users', 'scope_group' => 2, 'name' => 'SYSTEM', 'user_check' => 1], - ['api_route' => $aeApiV1Prefix . '/ex-app/all', 'scope_group' => 2, 'name' => 'SYSTEM', 'user_check' => 1], - ['api_route' => $aeApiV1Prefix . '/ex-app/enabled', 'scope_group' => 2, 'name' => 'SYSTEM', 'user_check' => 1], - ['api_route' => $aeApiV1Prefix . '/notification', 'scope_group' => 32, 'name' => 'NOTIFICATIONS', 'user_check' => 1], - ['api_route' => $aeApiV1Prefix . '/talk_bot', 'scope_group' => 60, 'name' => 'TALK_BOT', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/ai_provider/', 'scope_group' => 61, 'name' => 'AI_PROVIDERS', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/events_listener', 'scope_group' => 62, 'name' => 'EVENTS_LISTENER', 'user_check' => 0], - ['api_route' => $aeApiV1Prefix . '/occ_command', 'scope_group' => 63, 'name' => 'OCC_COMMAND', 'user_check' => 0], + ['api_route' => $aeApiV1Prefix . '/ui/files-actions-menu', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV2Prefix . '/ui/files-actions-menu', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/ui/top-menu', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/ui/initial-state', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/ui/script', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/ui/style', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => $aeApiV1Prefix . '/ui/settings', '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 . '/ex-app/enabled', 'scope_group' => 2, 'name' => 'SYSTEM'], + ['api_route' => $aeApiV1Prefix . '/notification', 'scope_group' => 32, 'name' => 'NOTIFICATIONS'], + ['api_route' => $aeApiV1Prefix . '/talk_bot', 'scope_group' => 60, 'name' => 'TALK_BOT'], + ['api_route' => $aeApiV1Prefix . '/ai_provider/', 'scope_group' => 61, 'name' => 'AI_PROVIDERS'], + ['api_route' => $aeApiV1Prefix . '/events_listener', 'scope_group' => 62, 'name' => 'EVENTS_LISTENER'], + ['api_route' => $aeApiV1Prefix . '/occ_command', 'scope_group' => 63, 'name' => 'OCC_COMMAND'], // AppAPI internal scopes - ['api_route' => '/apps/app_api/apps/status', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => '/apps/app_api/ex-app/status', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => '/apps/app_api/ex-app/state', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], + ['api_route' => '/apps/app_api/apps/status', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => '/apps/app_api/ex-app/status', 'scope_group' => 1, 'name' => 'BASIC'], + ['api_route' => '/apps/app_api/ex-app/state', 'scope_group' => 1, 'name' => 'BASIC'], // Cloud scopes - ['api_route' => '/cloud/capabilities', 'scope_group' => 1, 'name' => 'BASIC', 'user_check' => 0], - ['api_route' => '/cloud/apps', 'scope_group' => 2, 'name' => 'SYSTEM', 'user_check' => 1], - ['api_route' => '/apps/provisioning_api/api/', 'scope_group' => 2, 'name' => 'SYSTEM', 'user_check' => 1], - ['api_route' => '/settings/admin/log/', 'scope_group' => 2, 'name' => 'SYSTEM', 'user_check' => 0], - ['api_route' => '/dav/', 'scope_group' => 10, 'name' => 'FILES', 'user_check' => 1], - ['api_route' => '/apps/files/ajax/', 'scope_group' => 10, 'name' => 'FILES', 'user_check' => 1], - ['api_route' => '/apps/files_sharing/api/', 'scope_group' => 11, 'name' => 'FILES_SHARING', 'user_check' => 1], - ['api_route' => '/cloud/user', 'scope_group' => 30, 'name' => 'USER_INFO', 'user_check' => 1], - ['api_route' => '/cloud/groups', 'scope_group' => 30, 'name' => 'USER_INFO', 'user_check' => 1], - ['api_route' => '/apps/user_status/api/', 'scope_group' => 31, 'name' => 'USER_STATUS', 'user_check' => 1], - ['api_route' => '/apps/notifications/api/', 'scope_group' => 32, 'name' => 'NOTIFICATIONS', 'user_check' => 1], - ['api_route' => '/apps/weather_status/api/', 'scope_group' => 33, 'name' => 'WEATHER_STATUS', 'user_check' => 1], - ['api_route' => '/apps/spreed/api/', 'scope_group' => 50, 'name' => 'TALK', 'user_check' => 1], - ['api_route' => '/taskprocessing/', 'scope_group' => 61, 'name' => 'AI_PROVIDERS', 'user_check' => 0], - ['api_route' => '/apps/activity/api/', 'scope_group' => 110, 'name' => 'ACTIVITIES', 'user_check' => 1], - ['api_route' => '/apps/notes/api/', 'scope_group' => 120, 'name' => 'NOTES', 'user_check' => 1], - ['api_route' => '/textprocessing/', 'scope_group' => 200, 'name' => 'TEXT_PROCESSING', 'user_check' => 1], - ['api_route' => '/translation/', 'scope_group' => 210, 'name' => 'MACHINE_TRANSLATION', 'user_check' => 1], + ['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' => '/settings/admin/log/', 'scope_group' => 2, 'name' => 'SYSTEM'], + ['api_route' => '/dav/', 'scope_group' => 10, 'name' => 'FILES'], + ['api_route' => '/apps/files/ajax/', 'scope_group' => 10, 'name' => 'FILES'], + ['api_route' => '/apps/files_sharing/api/', 'scope_group' => 11, 'name' => 'FILES_SHARING'], + ['api_route' => '/cloud/user', 'scope_group' => 30, 'name' => 'USER_INFO'], + ['api_route' => '/cloud/groups', 'scope_group' => 30, 'name' => 'USER_INFO'], + ['api_route' => '/apps/user_status/api/', 'scope_group' => 31, 'name' => 'USER_STATUS'], + ['api_route' => '/apps/notifications/api/', 'scope_group' => 32, 'name' => 'NOTIFICATIONS'], + ['api_route' => '/apps/weather_status/api/', 'scope_group' => 33, 'name' => 'WEATHER_STATUS'], + ['api_route' => '/apps/spreed/api/', 'scope_group' => 50, 'name' => 'TALK'], + ['api_route' => '/taskprocessing/', 'scope_group' => 61, 'name' => 'AI_PROVIDERS'], + ['api_route' => '/apps/activity/api/', 'scope_group' => 110, 'name' => 'ACTIVITIES'], + ['api_route' => '/apps/notes/api/', 'scope_group' => 120, 'name' => 'NOTES'], + ['api_route' => '/textprocessing/', 'scope_group' => 200, 'name' => 'TEXT_PROCESSING'], + ['api_route' => '/translation/', 'scope_group' => 210, 'name' => 'MACHINE_TRANSLATION'], //ALL Scope - ['api_route' => 'non-exist-all-api-route', 'scope_group' => self::ALL_API_SCOPE, 'name' => 'ALL', 'user_check' => 1], + ['api_route' => 'non-exist-all-api-route', 'scope_group' => self::ALL_API_SCOPE, 'name' => 'ALL'], ]; } diff --git a/lib/Service/ExAppService.php b/lib/Service/ExAppService.php index 636629a9..9283948b 100644 --- a/lib/Service/ExAppService.php +++ b/lib/Service/ExAppService.php @@ -39,7 +39,6 @@ public function __construct( private readonly ExAppFetcher $exAppFetcher, private readonly ExAppArchiveFetcher $exAppArchiveFetcher, private readonly ExAppMapper $exAppMapper, - private readonly ExAppUsersService $exAppUsersService, private readonly ExAppApiScopeService $exAppApiScopeService, private readonly TopMenuService $topMenuService, private readonly InitialStateService $initialStateService, @@ -79,7 +78,6 @@ public function registerExApp(array $appInfo): ?ExApp { 'status' => json_encode(['deploy' => 0, 'init' => 0, 'action' => '', 'type' => 'install', 'error' => '']), 'created_time' => time(), 'last_check_time' => time(), - 'is_system' => (int)filter_var($appInfo['external-app']['system'], FILTER_VALIDATE_BOOLEAN), 'api_scopes' => $appInfo['api_scopes'], ]); try { @@ -98,7 +96,6 @@ public function unregisterExApp(string $appId): bool { if ($exApp === null) { return false; } - $this->exAppUsersService->removeExAppUsers($appId); $this->talkBotsService->unregisterExAppTalkBots($exApp); // TODO: Think about internal Events for clean and flexible unregister ExApp callbacks $this->filesActionsMenuService->unregisterExAppFileActions($appId); $this->topMenuService->unregisterExAppMenuEntries($appId); @@ -179,7 +176,7 @@ public function formatExAppInfo(ExApp $exApp): array { 'version' => $exApp->getVersion(), 'enabled' => filter_var($exApp->getEnabled(), FILTER_VALIDATE_BOOLEAN), 'last_check_time' => $exApp->getLastCheckTime(), - 'system' => $exApp->getIsSystem(), + 'system' => true, // TODO: Remove later 'status' => $exApp->getStatus(), 'scopes' => $this->exAppApiScopeService->mapScopeGroupsToNames($exApp->getApiScopes()), ]; @@ -194,15 +191,14 @@ public function getNCUsersList(): ?array { public function updateExAppInfo(ExApp $exApp, array $appInfo): bool { $exApp->setVersion($appInfo['version']); $exApp->setName($appInfo['name']); - $exApp->setIsSystem((int)filter_var($appInfo['external-app']['system'], FILTER_VALIDATE_BOOLEAN)); $exApp->setApiScopes($appInfo['api_scopes']); - if (!$this->updateExApp($exApp, ['version', 'name', 'is_system', 'api_scopes'])) { + if (!$this->updateExApp($exApp, ['version', 'name', 'api_scopes'])) { return false; } return true; } - public function updateExApp(ExApp $exApp, array $fields = ['version', 'name', 'port', 'status', 'enabled', 'last_check_time', 'is_system', 'api_scopes']): bool { + public function updateExApp(ExApp $exApp, array $fields = ['version', 'name', 'port', 'status', 'enabled', 'last_check_time', 'api_scopes']): bool { try { $this->exAppMapper->updateExApp($exApp, $fields); $this->cache->remove('/ex_apps'); @@ -252,17 +248,12 @@ public function getAppInfo(string $appId, ?string $infoXml, ?string $jsonInfo): # fill 'id' if it is missing(this field was called `appid` in previous versions in json) $appInfo['id'] = $appInfo['id'] ?? $appId; # during manual install JSON can have all values at root level - foreach (['docker-install', 'scopes', 'system', 'system_app', 'translations_folder'] as $key) { + foreach (['docker-install', 'scopes', 'system_app', 'translations_folder'] as $key) { if (isset($appInfo[$key])) { $appInfo['external-app'][$key] = $appInfo[$key]; unset($appInfo[$key]); } } - # TO-DO: remove this in AppAPI 2.4.0 - if (isset($appInfo['external-app']['system_app'])) { - $appInfo['external-app']['system'] = $appInfo['external-app']['system_app']; - unset($appInfo['external-app']['system_app']); - } } else { if ($infoXml !== null) { $xmlAppInfo = simplexml_load_string(file_get_contents($infoXml)); diff --git a/lib/Service/ExAppUsersService.php b/lib/Service/ExAppUsersService.php deleted file mode 100644 index cd4e094c..00000000 --- a/lib/Service/ExAppUsersService.php +++ /dev/null @@ -1,120 +0,0 @@ -cache = $cacheFactory->createDistributed(Application::APP_ID . '/ex_apps_users'); - } - - /** - * @throws Exception - */ - public function setupExAppUser(string $appId, ?string $userId): void { - if (!empty($userId)) { - if (!$this->exAppUserExists($appId, $userId)) { - $this->mapper->insert(new ExAppUser([ - 'appid' => $appId, - 'userid' => $userId, - ])); - } - } - } - - public function getExAppUsers(string $appId): array { - try { - $cacheKey = '/ex_apps_users_' . $appId; - $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($appId); - $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', $appId, $e->getMessage()), ['exception' => $e]); - return []; - } - } - - public function removeExAppUsers(string $appId): bool { - try { - $result = $this->mapper->deleteByAppid($appId) !== 0; - if ($result) { - $this->cache->clear('/ex_apps_users_' . $appId); - } - return $result; - } catch (Exception $e) { - $this->logger->error(sprintf('Failed to remove ex_app_users for ExApp %s. Error: %s', $appId, $e->getMessage()), ['exception' => $e]); - return false; - } - } - - public function removeExAppUser(string $appId, string $userId): bool { - try { - $result = $this->mapper->deleteByAppid($appId) !== 0; - if ($result) { - $this->cache->clear('/ex_apps_users_' . $appId); - } - return $result; - } catch (Exception $e) { - $this->logger->error(sprintf('Failed to remove ex_app_user %s for ExApp %s. Error: %s', $userId, $appId, $e->getMessage()), ['exception' => $e]); - return false; - } - } - - public function removeDeletedUser(string $userId): bool { - try { - $result = $this->mapper->deleteByUserId($userId) !== 0; - if ($result) { - $this->cache->clear('/ex_apps_users_'); - } - return $result; - } catch (Exception $e) { - $this->logger->error(sprintf('Failed to remove ex_app_user %s after User deletion. Error: %s', $userId, $e->getMessage()), ['exception' => $e]); - return false; - } - } - - /** - * @throws Exception - */ - 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) { - 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; - } - return false; - } -} diff --git a/lib/Service/ProvidersAI/SpeechToTextService.php b/lib/Service/ProvidersAI/SpeechToTextService.php index 49b14981..be4d80e1 100644 --- a/lib/Service/ProvidersAI/SpeechToTextService.php +++ b/lib/Service/ProvidersAI/SpeechToTextService.php @@ -173,7 +173,7 @@ public function transcribeFile(File $file, float $maxExecutionTime = 0): string } catch (Exception $e) { throw new \Exception(sprintf('Failed to open file: %s. Error: %s', $file->getName(), $e->getMessage())); } - $response = $service->exAppRequestWithUserInit($this->provider->getAppid(), + $response = $service->exAppRequest($this->provider->getAppid(), $route, $this->userId, options: [ diff --git a/lib/Service/ProvidersAI/TextProcessingService.php b/lib/Service/ProvidersAI/TextProcessingService.php index 71ce02c8..ffeccf8d 100644 --- a/lib/Service/ProvidersAI/TextProcessingService.php +++ b/lib/Service/ProvidersAI/TextProcessingService.php @@ -188,7 +188,7 @@ public function process(string $prompt, float $maxExecutionTime = 0): string { $queueRecord = $mapper->insert(new TextProcessingProviderQueue(['created_time' => time()])); $taskId = $queueRecord->getId(); - $response = $service->exAppRequestWithUserInit($this->provider->getAppid(), + $response = $service->exAppRequest($this->provider->getAppid(), $route, $this->userId, params: [ diff --git a/lib/Service/ProvidersAI/TranslationService.php b/lib/Service/ProvidersAI/TranslationService.php index 6a43fecb..4cf481da 100644 --- a/lib/Service/ProvidersAI/TranslationService.php +++ b/lib/Service/ProvidersAI/TranslationService.php @@ -201,7 +201,7 @@ public function detectLanguage(string $text): ?string { return null; // ExApp does not support language detection } - $response = $service->exAppRequestWithUserInit($this->provider->getAppid(), + $response = $service->exAppRequest($this->provider->getAppid(), $route, $this->userId, params: [ @@ -268,7 +268,7 @@ public function translate(?string $fromLanguage, string $toLanguage, string $tex $queueRecord = $mapper->insert(new TranslationQueue(['created_time' => time()])); $taskId = $queueRecord->getId(); - $response = $service->exAppRequestWithUserInit($this->provider->getAppid(), + $response = $service->exAppRequest($this->provider->getAppid(), $route, $this->userId, params: [ diff --git a/src/components/Apps/DaemonDetails.vue b/src/components/Apps/DaemonDetails.vue index 06ca1095..3fbc50e0 100644 --- a/src/components/Apps/DaemonDetails.vue +++ b/src/components/Apps/DaemonDetails.vue @@ -5,12 +5,6 @@

{{ t('app_api', 'Name') }}: {{ daemon.name }}

{{ t('app_api', 'Display Name') }}: {{ daemon.display_name }}

{{ t('app_api', 'GPUs support') }}: {{ daemon.deploy_config?.gpu || 'false' }}

-
-
- {{ t('app_api', 'System app: ') }} - {{ app.systemApp }} -
-
diff --git a/src/store/apps.js b/src/store/apps.js index 5223c67c..3d0ca70d 100644 --- a/src/store/apps.js +++ b/src/store/apps.js @@ -63,7 +63,6 @@ const mutations = { if (!app.installed) { app.installed = true app.needsDownload = false - app.systemApp = false app.daemon = state.defaultDaemon app.status = { type: 'install', @@ -159,9 +158,6 @@ const mutations = { if (exAppInfo.scopes) { app.scopes = exAppInfo.scopes } - if (exAppInfo.system) { - app.systemApp = exAppInfo.system - } }, setIntervalUpdater(state, updater) {