Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

removed "optional API Scope" support #220

Merged
merged 19 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/tests-special.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ jobs:
sleep 5s
php occ app_api:daemon:register manual_install "Manual Install" manual-install http localhost 0
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\":{\"required\":[\"SYSTEM\", \"NOTIFICATIONS\"],\"optional\":[\"USER_INFO\"]},\"system_app\":1}" \
"{\"appid\":\"$APP_ID\",\"name\":\"$APP_ID\",\"daemon_config_name\":\"manual_install\",\"version\":\"$APP_VERSION\",\"secret\":\"$APP_SECRET\",\"port\":$APP_PORT,\"scopes\":[\"SYSTEM\", \"NOTIFICATIONS\", \"USER_INFO\"],\"system_app\":1}" \
--force-scopes --wait-finish
kill -15 $(cat /tmp/_install.pid)
timeout 3m tail --pid=$(cat /tmp/_install.pid) -f /dev/null
Expand Down Expand Up @@ -220,7 +220,7 @@ jobs:
sleep 5s
php occ app_api:daemon:register manual_install "Manual Install" manual-install http localhost 0
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\":{\"required\":[\"ALL\"],\"optional\":[]},\"system_app\":1}" \
"{\"appid\":\"$APP_ID\",\"name\":\"$APP_ID\",\"daemon_config_name\":\"manual_install\",\"version\":\"$APP_VERSION\",\"secret\":\"$APP_SECRET\",\"port\":$APP_PORT,\"scopes\":[\"ALL\"],\"system_app\":1}" \
--force-scopes --wait-finish
kill -15 $(cat /tmp/_install.pid)
timeout 3m tail --pid=$(cat /tmp/_install.pid) -f /dev/null
Expand All @@ -238,7 +238,7 @@ jobs:
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\":{\"required\":[\"SYSTEM\"],\"optional\":[]},\"system_app\":1}" \
"{\"appid\":\"$APP_ID\",\"name\":\"$APP_ID\",\"daemon_config_name\":\"manual_install\",\"version\":\"$APP_VERSION\",\"secret\":\"$APP_SECRET\",\"port\":$APP_PORT,\"scopes\":[\"SYSTEM\"],\"system_app\":1}" \
--force-scopes --wait-finish
kill -15 $(cat /tmp/_install.pid)
timeout 3m tail --pid=$(cat /tmp/_install.pid) -f /dev/null
Expand All @@ -253,7 +253,7 @@ jobs:
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\":{\"required\":[\"ALL\"],\"optional\":[]},\"system_app\":0}" \
"{\"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
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [2.0.3 - 2024-02-01]

### Added

- Added RestartPolicy option (Admin settings) #220
- Added ExApp init timeout option (Admin settings) #220

### Changed

- Removed support of `Optional` API scopes. #220

## [2.0.2 - 2024-01-28]

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ to join us in shaping a more versatile, stable, and secure app landscape.
*Your insights, suggestions, and contributions are invaluable to us.*

]]></description>
<version>2.0.2</version>
<version>2.0.3</version>
<licence>agpl</licence>
<author mail="[email protected]" homepage="https://github.com/andrey18106">Andrey Borysenko</author>
<author mail="[email protected]" homepage="https://github.com/bigcat88">Alexander Piskun</author>
Expand Down
4 changes: 1 addition & 3 deletions docs/Concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ Concepts
API Access Control Mechanism
----------------------------

Each application defines required and an optional list of API groups it intends to access.

Administrators can permit or deny an application's access to any API from the **optional** list.
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.
Expand Down
2 changes: 1 addition & 1 deletion docs/DeployConfigurations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ Suggested way to communicate with Docker: via ``docker-socket-proxy``.
class ExApp3 python

Suggested config values(template *Docker Socket Proxy*):
1. Daemon host: aa-docker-socket-proxy:2375
1. Daemon host: nextcloud-appapi-dsp:2375
2. HTTPS checkbox: ``disabled``
3. Network: `user defined network <https://docs.docker.com/network/#user-defined-networks>`_
4. HaProxy password: ``optional``
Expand Down
72 changes: 9 additions & 63 deletions docs/tech_details/Deployment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,59 +27,24 @@ This can be done by ``occ`` CLI command **app_api:daemon:register**:

.. code-block:: bash

app_api:daemon:register <name> <display-name> <accepts-deploy-id> <protocol> <host> <nextcloud_url> [--net NET] [--host HOST] [--ssl_key SSL_KEY] [--ssl_key_password SSL_KEY_PASSWORD] [--ssl_cert SSL_CERT] [--ssl_cert_password SSL_CERT_PASSWORD] [--]
app_api:daemon:register <name> <display-name> <accepts-deploy-id> <protocol> <host> <nextcloud_url> [--net NET] [--haproxy_password PASSWORD] [--]

Arguments
*********

* ``name`` - unique name of the daemon (e.g. ``docker_local_sock``)
* ``display-name`` - name of the daemon (e.g. ``My Local Docker``, will be displayed in the UI)
* ``accepts-deploy-id`` - type of deployment (``docker-install`` or ``manual-install``)
* ``protocol`` - protocol used to connect to the daemon (``unix-socket``, ``http`` or ``https``)
* ``host`` - host of the daemon (e.g. ``/var/run/docker.sock`` for ``unix-socket`` protocol or ``host:port`` for ``http(s)`` protocol)
* ``protocol`` - protocol used to connect to the daemon (``http`` or ``https``)
* ``host`` - host of the daemon (e.g. ``/var/run/docker.sock`` or ``host:port``)
* ``nextcloud_url`` - Nextcloud URL, Daemon config required option (e.g. ``https://nextcloud.local``)
* ``--gpu`` - ``[optional]`` GPU device to expose to the daemon (e.g. ``/dev/dri``)

Options
*******

* ``--net [network-name]`` - ``[required]`` network name to bind docker container to (default: ``host``)
* ``--hostname HOST`` - ``[required]`` host to expose daemon to (defaults to ExApp appid)
* ``--ssl_key SSL_KEY`` - ``[optional]`` path to SSL key file (local absolute path)
* ``--ssl_password SSL_PASSWORD`` - ``[optional]`` SSL key password
* ``--ssl_cert SSL_CERT`` - ``[optional]`` path to SSL cert file (local absolute path)
* ``--ssl_cert_password SSL_CERT_PASSWORD`` - ``[optional]`` SSL cert password

DeployConfig
************

DeployConfig is a set of additional options in Daemon config, which are used in deployment algorithms to configure
ExApp container.

.. code-block:: json

{
"net": "nextcloud",
"host": null,
"nextcloud_url": "https://nextcloud.local",
"ssl_key": "/path/to/ssl/key.pem",
"ssl_key_password": "ssl_key_password",
"ssl_cert": "/path/to/ssl/cert.pem",
"ssl_cert_password": "ssl_cert_password",
"gpus": ["/dev/dri"],
}


DeployConfig options
""""""""""""""""""""

* ``net`` **[required]** - network name to bind docker container to (default: ``host``)
* ``host`` *[optional]* - in case Docker is on remote host, this should be a hostname of remote machine
* ``nextcloud_url`` **[required]** - Nextcloud URL (e.g. ``https://nextcloud.local``)
* ``ssl_key`` *[optional]* - path to SSL key file (local absolute path)
* ``ssl_key_password`` *[optional]* - SSL key password
* ``ssl_cert`` *[optional]* - path to SSL cert file (local absolute path)
* ``ssl_cert_password`` *[optional]* - SSL cert password
* ``--haproxy_password PASSWORD`` - ``[optional]`` password if ``AppAPI Docker Socket Proxy`` is used
* ``--gpu`` - ``[optional]`` GPU device to expose to the daemon (e.g. ``/dev/dri``)

.. note::
Common configurations are tested by CI in our repository, see `workflows on github <https://github.com/cloud-py-api/app_api/blob/main/.github/workflows/tests-deploy.yml>`_.
Expand All @@ -91,7 +56,7 @@ Example of ``occ`` **app_api:daemon:register** command:

.. code-block:: bash

sudo -u www-data php occ app_api:daemon:register docker_local_sock "My Local Docker" docker-install unix-socket /var/run/docker.sock "https://nextcloud.local" --net nextcloud
sudo -u www-data php occ app_api:daemon:register docker_local_sock "My Local Docker" docker-install http /var/run/docker.sock "https://nextcloud.local" --net nextcloud


ExApp deployment
Expand Down Expand Up @@ -156,7 +121,7 @@ For all examples and applications we release we usually add manual_install comma
.. code-block::

php occ app_api:app:register nc_py_api manual_install --json-info \
"{\"appid\":\"nc_py_api\",\"name\":\"nc_py_api\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"host\":\"localhost\",\"port\":$APP_PORT,\"scopes\":{\"required\":[\"SYSTEM\", \"FILES\", \"FILES_SHARING\"],\"optional\":[\"USER_INFO\", \"USER_STATUS\", \"NOTIFICATIONS\", \"WEATHER_STATUS\", \"TALK\"]},\"protocol\":\"http\",\"system_app\":1}" \
"{\"appid\":\"nc_py_api\",\"name\":\"nc_py_api\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":$APP_PORT,\"scopes\":[\"SYSTEM\", \"FILES\", \"FILES_SHARING\", \"USER_INFO\", \"USER_STATUS\", \"NOTIFICATIONS\", \"WEATHER_STATUS\", \"TALK\"],\"system_app\":1}" \
--force-scopes

.. note:: **Deployment/Startup of App should be done by developer when manual_install DeployConfig type is used.**
Expand All @@ -172,7 +137,6 @@ The following env variables are required and built automatically:
* ``APP_ID`` - ExApp appid
* ``APP_DISPLAY_NAME`` - ExApp display name
* ``APP_VERSION`` - ExApp version
* ``APP_PROTOCOL`` - protocol ExApp is listening on (http|https)
* ``APP_HOST`` - host ExApp is listening on
* ``APP_PORT`` - port ExApp is listening on (randomly selected by AppAPI)
* ``APP_PERSISTENT_STORAGE`` - path to mounted volume for persistent data storage between ExApp updates
Expand All @@ -182,19 +146,6 @@ The following env variables are required and built automatically:
.. note::
Additional envs can be passed using multiple ``--env ENV_NAME=ENV_VAL`` options

Docker daemon remote
********************

If you want to connect to remote docker daemon with TLS enabled, you need to provide SSL key and cert by provided options.
Important: before deploy you need to import ca.pem file using `occ security <https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/occ_command.html#security>`_ command:

``php occ security:certificates:import /path/to/ca.pem``

The daemon must be configured with ``protocol=http|https``, ``host=https://dockerapihost``, ``port=8443``.
DaemonConfig deploy options ``ssl_key`` and ``ssl_cert`` must be provided with local absolute paths to SSL key and cert files.
In case of password protected key or cert, you can provide ``ssl_key_password`` and ``ssl_cert_password`` options.
More info about how to configure daemon will be added soon.

ExApp registration
------------------

Expand Down Expand Up @@ -250,14 +201,9 @@ It has the same structure as Nextcloud appinfo/info.xml file, but with some addi
<image-tag>latest</image-tag>
</docker-install>
<scopes>
<required>
<value>TALK</value>
<value>TALK_BOT</value>
</required>
<optional>
</optional>
<value>TALK</value>
<value>TALK_BOT</value>
</scopes>
<protocol>http</protocol>
<system>0</system>
</ex-app>
...
4 changes: 2 additions & 2 deletions lib/BackgroundJob/ExAppInitStatusCheckJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ public function __construct(
}

protected function run($argument): void {
// Iterate over all ExApp and check for status.init_start_time if it is older than ex_app_init_timeout minutes
// Iterate over all ExApp and check for status.init_start_time if it is older than init_timeout minutes
// set status.progress=0 and status.error message with timeout error
try {
$exApps = $this->mapper->findAll();
$initTimeoutMinutes = intval($this->config->getAppValue(Application::APP_ID, 'ex_app_init_timeout', '40'));
$initTimeoutMinutes = intval($this->config->getAppValue(Application::APP_ID, 'init_timeout', '40'));
foreach ($exApps as $exApp) {
$status = $exApp->getStatus();
if (!isset($status['init_start_time'])) {
Expand Down
24 changes: 2 additions & 22 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,16 @@
namespace OCA\AppAPI;

use OCA\AppAPI\AppInfo\Application;
use OCA\AppAPI\Db\ExAppScope;
use OCA\AppAPI\Service\ExAppScopesService;
use OCA\AppAPI\Service\ExAppService;
use OCA\AppAPI\Service\ProvidersAI\TextProcessingService;
use OCP\App\IAppManager;
use OCP\Capabilities\ICapability;
use OCP\IConfig;
use OCP\IRequest;

class Capabilities implements ICapability {

public function __construct(
private IConfig $config,
private IAppManager $appManager,
private ExAppService $service,
private ExAppScopesService $exAppScopesService,
private IRequest $request,
private readonly IConfig $config,
private readonly IAppManager $appManager,
) {
}

Expand All @@ -33,21 +26,8 @@ public function getCapabilities(): array {
'task_types' => array_keys(TextProcessingService::TASK_TYPES),
]
];
$this->attachExAppScopes($capabilities);
return [
'app_api' => $capabilities,
];
}

private function attachExAppScopes(&$capabilities): void {
$appId = $this->request->getHeader('EX-APP-ID');
if ($appId !== '') {
$exApp = $this->service->getExApp($appId);
if ($exApp !== null) {
$capabilities['scopes'] = array_map(function (ExAppScope $scope) {
return intval($scope->getScopeGroup());
}, $this->exAppScopesService->getExAppScopes($exApp));
}
}
}
}
2 changes: 1 addition & 1 deletion lib/Command/ExApp/Deploy.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$daemonConfig->getProtocol(),
$daemonConfig->getHost(),
$daemonConfig->getDeployConfig(),
(int)explode('=', $deployParams['container_params']['env'][7])[1],
(int)explode('=', $deployParams['container_params']['env'][6])[1],
$auth,
);
if (!$this->service->heartbeatExApp($exAppUrl, $auth)) {
Expand Down
29 changes: 9 additions & 20 deletions lib/Command/ExApp/Register.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
}

$requestedExAppScopeGroups = $this->exAppService->getExAppRequestedScopes($exApp, $infoXml, $exAppInfo);
$requestedExAppScopeGroups = $this->exAppService->getExAppScopes($exApp, $infoXml, $exAppInfo);
if (isset($requestedExAppScopeGroups['error'])) {
$output->writeln($requestedExAppScopeGroups['error']);
$this->exAppService->unregisterExApp($exApp->getAppid());
Expand All @@ -154,40 +154,29 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$forceScopes = (bool) $input->getOption('force-scopes');
$confirmRequiredScopes = $forceScopes;
$confirmOptionalScopes = $forceScopes;

if (!$forceScopes && $input->isInteractive()) {
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');

// Prompt to approve required ExApp scopes
if (count($requestedExAppScopeGroups['required']) > 0) {
$output->writeln(sprintf('ExApp %s requested required scopes: %s', $appId, implode(', ', $requestedExAppScopeGroups['required'])));
if (count($requestedExAppScopeGroups) > 0) {
$output->writeln(sprintf('ExApp %s requested required scopes: %s', $appId, implode(', ', $requestedExAppScopeGroups)));
$question = new ConfirmationQuestion('Do you want to approve it? [y/N] ', false);
$confirmRequiredScopes = $helper->ask($input, $output, $question);
} else {
$confirmRequiredScopes = true;
}

// Prompt to approve optional ExApp scopes
if ($confirmRequiredScopes && count($requestedExAppScopeGroups['optional']) > 0) {
$output->writeln(sprintf('ExApp %s requested optional scopes: %s', $appId, implode(', ', $requestedExAppScopeGroups['optional'])));
$question = new ConfirmationQuestion('Do you want to approve it? [y/N] ', false);
$confirmOptionalScopes = $helper->ask($input, $output, $question);
}
}

if (!$confirmRequiredScopes && count($requestedExAppScopeGroups['required']) > 0) {
if (!$confirmRequiredScopes && count($requestedExAppScopeGroups) > 0) {
$output->writeln(sprintf('ExApp %s required scopes not approved.', $appId));
$this->exAppService->unregisterExApp($exApp->getAppid());
return 1;
}

if (count($requestedExAppScopeGroups['required']) > 0) {
$this->registerExAppScopes($output, $exApp, $requestedExAppScopeGroups['required'], 'required');
}
if ($confirmOptionalScopes && count($requestedExAppScopeGroups['optional']) > 0) {
$this->registerExAppScopes($output, $exApp, $requestedExAppScopeGroups['optional'], 'optional');
if (count($requestedExAppScopeGroups) > 0) {
$this->registerExAppScopes($output, $exApp, $requestedExAppScopeGroups);
}

if (!$this->service->dispatchExAppInit($exApp->getAppid())) {
Expand All @@ -212,17 +201,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 0;
}

private function registerExAppScopes($output, ExApp $exApp, array $requestedExAppScopeGroups, string $scopeType): void {
private function registerExAppScopes($output, ExApp $exApp, array $requestedExAppScopeGroups): void {
$registeredScopeGroups = [];
foreach ($this->exAppApiScopeService->mapScopeNamesToNumbers($requestedExAppScopeGroups) as $scopeGroup) {
if ($this->exAppScopesService->setExAppScopeGroup($exApp, $scopeGroup)) {
$registeredScopeGroups[] = $scopeGroup;
} else {
$output->writeln(sprintf('Failed to set %s ExApp scope group: %s', $scopeType, $scopeGroup));
$output->writeln(sprintf('Failed to set %s ExApp scope group: %s', $exApp->getAppid(), $scopeGroup));
}
}
if (count($registeredScopeGroups) > 0) {
$output->writeln(sprintf('ExApp %s %s scope groups successfully set: %s', $exApp->getAppid(), $scopeType, implode(', ',
$output->writeln(sprintf('ExApp %s scope groups successfully set: %s', $exApp->getAppid(), implode(', ',
$this->exAppApiScopeService->mapScopeGroupsToNames($registeredScopeGroups))));
}
}
Expand Down
Loading
Loading