Skip to content

Commit

Permalink
appapi-2.0.3-api-scopes (#216)
Browse files Browse the repository at this point in the history
Ref: cloud-py-api/app_api#220

No more "optional API Scopes", lets make all simpler :)

Signed-off-by: Alexander Piskun <bigcat88@icloud.com>
  • Loading branch information
bigcat88 committed Feb 1, 2024
1 parent 0a183e7 commit 561d309
Show file tree
Hide file tree
Showing 19 changed files with 31 additions and 122 deletions.
8 changes: 2 additions & 6 deletions docs/NextcloudApp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,8 @@ First of all, we modernize info.ixml, add the API groups we need for this to wor
.. code-block:: xml
<scopes>
<required>
<value>FILES</value>
<value>NOTIFICATIONS</value>
</required>
<optional>
</optional>
<value>FILES</value>
<value>NOTIFICATIONS</value>
</scopes>
.. note:: Full list of avalaible API scopes can be found `here <https://cloud-py-api.github.io/app_api/tech_details/ApiScopes.html>`_.
Expand Down
8 changes: 2 additions & 6 deletions docs/NextcloudTalkBot.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,8 @@ The first step is to add the **TALK_BOT** and **TALK** scopes to your `info.xml`
.. code-block:: xml
<scopes>
<required>
<value>TALK</value>
<value>TALK_BOT</value>
</required>
<optional>
</optional>
<value>TALK</value>
<value>TALK_BOT</value>
</scopes>
The TALK_BOT scope enables your application to register the bot within the Nextcloud system, while the TALK scope permits access to Talk's endpoints.
Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
nitpick_ignore_regex = [
(r"py:class", r"starlette\.requests\.Request"),
(r"py:class", r"starlette\.requests\.HTTPConnection"),
(r"py:class", r"ComputedFieldInfo"),
(r"py:.*", r"httpx.*"),
]

Expand Down
6 changes: 3 additions & 3 deletions examples/as_app/skeleton/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,19 @@ run:
register27:
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:unregister skeleton --silent --force || true
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:register skeleton manual_install --json-info \
"{\"appid\":\"skeleton\",\"name\":\"App Skeleton\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9030,\"scopes\":{\"required\":[],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"skeleton\",\"name\":\"App Skeleton\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9030,\"scopes\":[],\"system_app\":0}" \
--force-scopes --wait-finish

.PHONY: register28
register28:
docker exec master-stable28-1 sudo -u www-data php occ app_api:app:unregister skeleton --silent --force || true
docker exec master-stable28-1 sudo -u www-data php occ app_api:app:register skeleton manual_install --json-info \
"{\"appid\":\"skeleton\",\"name\":\"App Skeleton\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9030,\"scopes\":{\"required\":[],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"skeleton\",\"name\":\"App Skeleton\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9030,\"scopes\":[],\"system_app\":0}" \
--force-scopes --wait-finish

.PHONY: register
register:
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:unregister skeleton --silent --force || true
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:register skeleton manual_install --json-info \
"{\"appid\":\"skeleton\",\"name\":\"App Skeleton\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9030,\"scopes\":{\"required\":[],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"skeleton\",\"name\":\"App Skeleton\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9030,\"scopes\":[],\"system_app\":0}" \
--force-scopes --wait-finish
6 changes: 1 addition & 5 deletions examples/as_app/skeleton/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<bugs>https://github.com/cloud-py-api/nc_py_api/issues</bugs>
<repository type="git">https://github.com/cloud-py-api/nc_py_api</repository>
<dependencies>
<nextcloud min-version="27" max-version="29"/>
<nextcloud min-version="27" max-version="30"/>
</dependencies>
<external-app>
<docker-install>
Expand All @@ -25,10 +25,6 @@
<image-tag>latest</image-tag>
</docker-install>
<scopes>
<required>
</required>
<optional>
</optional>
</scopes>
<system>false</system>
</external-app>
Expand Down
4 changes: 2 additions & 2 deletions examples/as_app/talk_bot/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ run27:
register:
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:unregister talk_bot --silent --force || true
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:register talk_bot manual_install --json-info \
"{\"appid\":\"talk_bot\",\"name\":\"TalkBot\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9032,\"scopes\":{\"required\":[\"TALK\", \"TALK_BOT\"],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"talk_bot\",\"name\":\"TalkBot\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9032,\"scopes\":[\"TALK\", \"TALK_BOT\"],\"system_app\":0}" \
--force-scopes --wait-finish

.PHONY: register27
register27:
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:unregister talk_bot --force || true
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:register talk_bot manual_install --json-info \
"{\"appid\":\"talk_bot\",\"name\":\"TalkBot\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9032,\"scopes\":{\"required\":[\"TALK\", \"TALK_BOT\"],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"talk_bot\",\"name\":\"TalkBot\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9032,\"scopes\":[\"TALK\", \"TALK_BOT\"],\"system_app\":0}" \
--force-scopes --wait-finish
8 changes: 2 additions & 6 deletions examples/as_app/talk_bot/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,8 @@
<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>
<system>false</system>
</external-app>
Expand Down
4 changes: 2 additions & 2 deletions examples/as_app/talk_bot_ai/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ run27:
register:
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:unregister talk_bot_ai --silent --force || true
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:register talk_bot_ai manual_install --json-info \
"{\"appid\":\"talk_bot_ai\",\"name\":\"TalkBotAI\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9034,\"scopes\":{\"required\":[\"TALK\", \"TALK_BOT\"],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"talk_bot_ai\",\"name\":\"TalkBotAI\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9034,\"scopes\":[\"TALK\", \"TALK_BOT\"],\"system_app\":0}" \
--force-scopes --wait-finish

.PHONY: register27
register27:
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:unregister talk_bot_ai --silent --force || true
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:register talk_bot_ai manual_install --json-info \
"{\"appid\":\"talk_bot_ai\",\"name\":\"TalkBotAI\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9034,\"scopes\":{\"required\":[\"TALK\", \"TALK_BOT\"],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"talk_bot_ai\",\"name\":\"TalkBotAI\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9034,\"scopes\":[\"TALK\", \"TALK_BOT\"],\"system_app\":0}" \
--force-scopes --wait-finish
8 changes: 2 additions & 6 deletions examples/as_app/talk_bot_ai/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,8 @@
<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>
<system>false</system>
</external-app>
Expand Down
6 changes: 3 additions & 3 deletions examples/as_app/to_gif/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,19 @@ run:
register27:
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:unregister to_gif --silent --force || true
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:register to_gif manual_install --json-info \
"{\"appid\":\"to_gif\",\"name\":\"to_gif\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9031,\"scopes\":{\"required\":[\"FILES\", \"NOTIFICATIONS\"],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"to_gif\",\"name\":\"to_gif\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9031,\"scopes\":[\"FILES\", \"NOTIFICATIONS\"],\"system_app\":0}" \
--force-scopes --wait-finish

.PHONY: register28
register28:
docker exec master-stable28-1 sudo -u www-data php occ app_api:app:unregister to_gif --silent --force || true
docker exec master-stable28-1 sudo -u www-data php occ app_api:app:register to_gif manual_install --json-info \
"{\"appid\":\"to_gif\",\"name\":\"to_gif\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9031,\"scopes\":{\"required\":[\"FILES\", \"NOTIFICATIONS\"],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"to_gif\",\"name\":\"to_gif\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9031,\"scopes\":[\"FILES\", \"NOTIFICATIONS\"],\"system_app\":0}" \
--force-scopes --wait-finish

.PHONY: register
register:
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:unregister to_gif --silent --force || true
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:register to_gif manual_install --json-info \
"{\"appid\":\"to_gif\",\"name\":\"to_gif\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9031,\"scopes\":{\"required\":[\"FILES\", \"NOTIFICATIONS\"],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"to_gif\",\"name\":\"to_gif\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9031,\"scopes\":[\"FILES\", \"NOTIFICATIONS\"],\"system_app\":0}" \
--force-scopes --wait-finish
8 changes: 2 additions & 6 deletions examples/as_app/to_gif/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,8 @@
<image-tag>latest</image-tag>
</docker-install>
<scopes>
<required>
<value>FILES</value>
<value>NOTIFICATIONS</value>
</required>
<optional>
</optional>
<value>FILES</value>
<value>NOTIFICATIONS</value>
</scopes>
<system>false</system>
</external-app>
Expand Down
6 changes: 3 additions & 3 deletions examples/as_app/ui_example/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,19 @@ run:
register27:
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:unregister ui_example --silent --force || true
docker exec master-stable27-1 sudo -u www-data php occ app_api:app:register ui_example manual_install --json-info \
"{\"appid\":\"ui_example\",\"name\":\"UI Example\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9035,\"scopes\":{\"required\":[],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"ui_example\",\"name\":\"UI Example\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9035,\"scopes\":[],\"system_app\":0}" \
--force-scopes --wait-finish

.PHONY: register28
register28:
docker exec master-stable28-1 sudo -u www-data php occ app_api:app:unregister ui_example --silent --force || true
docker exec master-stable28-1 sudo -u www-data php occ app_api:app:register ui_example manual_install --json-info \
"{\"appid\":\"ui_example\",\"name\":\"UI Example\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9035,\"scopes\":{\"required\":[],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"ui_example\",\"name\":\"UI Example\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9035,\"scopes\":[],\"system_app\":0}" \
--force-scopes --wait-finish

.PHONY: register
register:
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:unregister ui_example --silent --force || true
docker exec master-nextcloud-1 sudo -u www-data php occ app_api:app:register ui_example manual_install --json-info \
"{\"appid\":\"ui_example\",\"name\":\"UI Example\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9035,\"scopes\":{\"required\":[],\"optional\":[]},\"system_app\":0}" \
"{\"appid\":\"ui_example\",\"name\":\"UI Example\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"port\":9035,\"scopes\":[],\"system_app\":0}" \
--force-scopes --wait-finish
4 changes: 0 additions & 4 deletions examples/as_app/ui_example/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@
<image-tag>latest</image-tag>
</docker-install>
<scopes>
<required>
</required>
<optional>
</optional>
</scopes>
<system>false</system>
</external-app>
Expand Down
2 changes: 1 addition & 1 deletion nc_py_api/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version of nc_py_api."""

__version__ = "0.9.0"
__version__ = "0.10.0.dev0"
2 changes: 1 addition & 1 deletion nc_py_api/ex_app/defs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class LogLvl(enum.IntEnum):


class ApiScope(enum.IntEnum):
"""Default API scopes. Should be used as a parameter to the :py:meth:`~.NextcloudApp.scope_allowed` method."""
"""Defined API scopes."""

SYSTEM = 2
"""Allows access to the System APIs."""
Expand Down
20 changes: 1 addition & 19 deletions nc_py_api/nextcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from .activity import _ActivityAPI, _AsyncActivityAPI
from .apps import _AppsAPI, _AsyncAppsAPI
from .calendar import _CalendarAPI
from .ex_app.defs import ApiScope, LogLvl
from .ex_app.defs import LogLvl
from .ex_app.providers.providers import AsyncProvidersApi, ProvidersApi
from .ex_app.ui.ui import AsyncUiApi, UiApi
from .files.files import AsyncFilesAPI, FilesAPI
Expand Down Expand Up @@ -330,15 +330,6 @@ def users_list(self) -> list[str]:
"""Returns list of users on the Nextcloud instance. **Available** only for ``System`` applications."""
return self._session.ocs("GET", f"{self._session.ae_url}/users")

def scope_allowed(self, scope: ApiScope) -> bool:
"""Check if API scope is avalaible for application.
Useful for applications that declare optional scopes to check if they are allowed.
"""
if self.check_capabilities("app_api"):
return False
return scope in self.capabilities["app_api"]["scopes"]

@property
def user(self) -> str:
"""Property containing the current user ID.
Expand Down Expand Up @@ -470,15 +461,6 @@ async def users_list(self) -> list[str]:
"""Returns list of users on the Nextcloud instance. **Available** only for ``System`` applications."""
return await self._session.ocs("GET", f"{self._session.ae_url}/users")

async def scope_allowed(self, scope: ApiScope) -> bool:
"""Check if API scope is avalaible for application.
Useful for applications that declare optional scopes to check if they are allowed.
"""
if await self.check_capabilities("app_api"):
return False
return scope in (await self.capabilities)["app_api"]["scopes"]

@property
async def user(self) -> str:
"""Property containing the current user ID.
Expand Down
2 changes: 1 addition & 1 deletion scripts/ci_register.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@

php occ app_api:daemon:register manual_install "Manual Install" manual-install http "$4" 0
php occ app_api:app:register "$1" manual_install --json-info \
"{\"appid\":\"$1\",\"name\":\"$1\",\"daemon_config_name\":\"manual_install\",\"version\":\"$2\",\"secret\":\"$3\",\"scopes\":{\"required\":[\"ALL\"],\"optional\":[]},\"port\":$5,\"system_app\":1}" \
"{\"appid\":\"$1\",\"name\":\"$1\",\"daemon_config_name\":\"manual_install\",\"version\":\"$2\",\"secret\":\"$3\",\"scopes\":[\"ALL\"],\"port\":$5,\"system_app\":1}" \
--force-scopes --wait-finish
2 changes: 1 addition & 1 deletion scripts/dev_register.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ echo "unregistering nc_py_api as an app for $1 container"
docker exec "$1" sudo -u www-data php occ app_api:app:unregister nc_py_api --silent --force || true
echo "registering nc_py_api as an app for $1 container"
docker exec "$1" sudo -u www-data 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\",\"scopes\":{\"required\":[\"ALL\"],\"optional\":[]},\"port\":9009,\"system_app\":1}" \
"{\"appid\":\"nc_py_api\",\"name\":\"nc_py_api\",\"daemon_config_name\":\"manual_install\",\"version\":\"1.0.0\",\"secret\":\"12345\",\"scopes\":[\"ALL\"],\"port\":9009,\"system_app\":1}" \
--force-scopes --wait-finish
echo "nc_py_api for $1 is ready to use"
48 changes: 1 addition & 47 deletions tests/actual_tests/nc_app_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import pytest

from nc_py_api.ex_app import ApiScope, set_handlers
from nc_py_api.ex_app import set_handlers


def test_get_users_list(nc_app):
Expand All @@ -19,27 +19,6 @@ async def test_get_users_list_async(anc_app):
assert await anc_app.user in users


def test_scope_allowed(nc_app):
for i in ApiScope:
if i == ApiScope.ALL.value:
assert nc_app.scope_allowed(i)
else:
assert not nc_app.scope_allowed(i)
assert not nc_app.scope_allowed(0) # noqa
assert not nc_app.scope_allowed(999999999) # noqa


@pytest.mark.asyncio(scope="session")
async def test_scope_allowed_async(anc_app):
for i in ApiScope:
if i == ApiScope.ALL.value:
assert await anc_app.scope_allowed(i)
else:
assert not await anc_app.scope_allowed(i)
assert not await anc_app.scope_allowed(0) # noqa
assert not await anc_app.scope_allowed(999999999) # noqa


def test_app_cfg(nc_app):
app_cfg = nc_app.app_cfg
assert app_cfg.app_name == environ["APP_ID"]
Expand All @@ -55,31 +34,6 @@ async def test_app_cfg_async(anc_app):
assert app_cfg.app_secret == environ["APP_SECRET"]


def test_scope_allow_app_ecosystem_disabled(nc_client, nc_app):
assert nc_app.scope_allowed(ApiScope.ALL)
nc_client.apps.disable("app_api")
try:
assert nc_app.scope_allowed(ApiScope.ALL)
nc_app.update_server_info()
assert not nc_app.scope_allowed(ApiScope.ALL)
finally:
nc_client.apps.enable("app_api")
nc_app.update_server_info()


@pytest.mark.asyncio(scope="session")
async def test_scope_allow_app_ecosystem_disabled_async(anc_client, anc_app):
assert await anc_app.scope_allowed(ApiScope.ALL)
await anc_client.apps.disable("app_api")
try:
assert await anc_app.scope_allowed(ApiScope.ALL)
await anc_app.update_server_info()
assert not await anc_app.scope_allowed(ApiScope.ALL)
finally:
await anc_client.apps.enable("app_api")
await anc_app.update_server_info()


def test_change_user(nc_app):
orig_user = nc_app.user
try:
Expand Down

0 comments on commit 561d309

Please sign in to comment.