diff --git a/docs/configuration.md b/docs/configuration.md
index 19f155b1..b03837ea 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -24,8 +24,6 @@ Base authorization settings. `OAuth` provider requires `aioauth-client` library
* `target` - specifies authorization provider, string, optional, default `disabled`. Allowed values are `disabled`, `configuration`, `oauth`.
* `allow_read_only` - allow requesting read only pages without authorization, boolean, required.
-* `allowed_paths` - URI paths (exact match) which can be accessed without authorization, space separated list of strings, optional.
-* `allowed_paths_groups` - URI paths prefixes which can be accessed without authorization, space separated list of strings, optional.
* `client_id` - OAuth2 application client ID, string, required in case if `oauth` is used.
* `client_secret` - OAuth2 application client secret key, string, required in case if `oauth` is used.
* `max_age` - parameter which controls both cookie expiration and token expiration inside the service, integer, optional, default is 7 days.
diff --git a/src/ahriman/core/auth/auth.py b/src/ahriman/core/auth/auth.py
index f1b94d7f..a953d538 100644
--- a/src/ahriman/core/auth/auth.py
+++ b/src/ahriman/core/auth/auth.py
@@ -33,16 +33,11 @@ from ahriman.models.user_access import UserAccess
class Auth:
"""
helper to deal with user authorization
- :ivar allowed_paths: URI paths which can be accessed without authorization
- :ivar allowed_paths_groups: URI paths prefixes which can be accessed without authorization
+ :ivar allow_read_only: allow read only access to the index page
:ivar enabled: indicates if authorization is enabled
- :cvar ALLOWED_PATHS: URI paths which can be accessed without authorization, predefined
- :cvar ALLOWED_PATHS_GROUPS: URI paths prefixes which can be accessed without authorization, predefined
+ :ivar max_age: session age in seconds. It will be used for both client side and server side checks
"""
- ALLOWED_PATHS = {"/", "/index.html"}
- ALLOWED_PATHS_GROUPS = {"/static", "/user-api"}
-
def __init__(self, configuration: Configuration, provider: AuthSettings = AuthSettings.Disabled) -> None:
"""
default constructor
@@ -52,10 +47,7 @@ class Auth:
self.logger = logging.getLogger("http")
self.allow_read_only = configuration.getboolean("auth", "allow_read_only")
- self.allowed_paths = set(configuration.getlist("auth", "allowed_paths", fallback=[]))
- self.allowed_paths.update(self.ALLOWED_PATHS)
- self.allowed_paths_groups = set(configuration.getlist("auth", "allowed_paths_groups", fallback=[]))
- self.allowed_paths_groups.update(self.ALLOWED_PATHS_GROUPS)
+
self.enabled = provider.is_enabled
self.max_age = configuration.getint("auth", "max_age", fallback=7 * 24 * 3600)
@@ -115,19 +107,6 @@ class Auth:
del username, password
return True
- async def is_safe_request(self, uri: Optional[str], required: UserAccess) -> bool:
- """
- check if requested path are allowed without authorization
- :param uri: request uri
- :param required: required access level
- :return: True in case if this URI can be requested without authorization and False otherwise
- """
- if required == UserAccess.Read and self.allow_read_only:
- return True # in case if read right requested and allowed in options
- if not uri:
- return False # request without context is not allowed
- return uri in self.allowed_paths or any(uri.startswith(path) for path in self.allowed_paths_groups)
-
async def known_username(self, username: Optional[str]) -> bool: # pylint: disable=no-self-use
"""
check if user is known
diff --git a/src/ahriman/models/user_access.py b/src/ahriman/models/user_access.py
index 4eb78272..0cc25c99 100644
--- a/src/ahriman/models/user_access.py
+++ b/src/ahriman/models/user_access.py
@@ -23,9 +23,11 @@ from enum import Enum
class UserAccess(Enum):
"""
web user access enumeration
- :cvar Read: user can read status page
+ :cvar Safe: user can access the page without authorization, should not be user for user configuration
+ :cvar Read: user can read the page
:cvar Write: user can modify task and package list
"""
+ Safe = "safe"
Read = "read"
Write = "write"
diff --git a/src/ahriman/web/middlewares/auth_handler.py b/src/ahriman/web/middlewares/auth_handler.py
index 2822ead7..da01c9b7 100644
--- a/src/ahriman/web/middlewares/auth_handler.py
+++ b/src/ahriman/web/middlewares/auth_handler.py
@@ -19,10 +19,12 @@
#
import aiohttp_security # type: ignore
import base64
+import types
from aiohttp import web
from aiohttp.web import middleware, Request
from aiohttp.web_response import StreamResponse
+from aiohttp.web_urldispatcher import StaticResource
from aiohttp_session import setup as setup_session # type: ignore
from aiohttp_session.cookie_storage import EncryptedCookieStorage # type: ignore
from cryptography import fernet
@@ -72,20 +74,22 @@ class AuthorizationPolicy(aiohttp_security.AbstractAuthorizationPolicy): # type
return await self.validator.verify_access(user.username, permission, context)
-def auth_handler(validator: Auth) -> MiddlewareType:
+def auth_handler() -> MiddlewareType:
"""
authorization and authentication middleware
- :param validator: authorization module instance
:return: built middleware
"""
@middleware
async def handle(request: Request, handler: HandlerType) -> StreamResponse:
- if request.method in ("GET", "HEAD", "OPTIONS"):
- permission = UserAccess.Read
+ permission_method = getattr(handler, "get_permission", None)
+ if permission_method is not None:
+ permission = await permission_method(request)
+ elif isinstance(handler, types.MethodType): # additional wrapper for static resources
+ handler_instance = getattr(handler, "__self__", None)
+ permission = UserAccess.Safe if isinstance(handler_instance, StaticResource) else UserAccess.Write
else:
permission = UserAccess.Write
-
- if not await validator.is_safe_request(request.path, permission):
+ if permission != UserAccess.Safe:
await aiohttp_security.check_permission(request, permission, request.path)
return await handler(request)
@@ -109,6 +113,6 @@ def setup_auth(application: web.Application, validator: Auth) -> web.Application
identity_policy = aiohttp_security.SessionIdentityPolicy()
aiohttp_security.setup(application, identity_policy, authorization_policy)
- application.middlewares.append(auth_handler(validator))
+ application.middlewares.append(auth_handler())
return application
diff --git a/src/ahriman/web/views/base.py b/src/ahriman/web/views/base.py
index 1d43694e..3c54106f 100644
--- a/src/ahriman/web/views/base.py
+++ b/src/ahriman/web/views/base.py
@@ -17,13 +17,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-from aiohttp.web import View
-from typing import Any, Dict, List, Optional
+from __future__ import annotations
+
+from aiohttp.web import Request, View
+from typing import Any, Dict, List, Optional, Type
from ahriman.core.auth.auth import Auth
from ahriman.core.configuration import Configuration
from ahriman.core.spawn import Spawn
from ahriman.core.status.watcher import Watcher
+from ahriman.models.user_access import UserAccess
class BaseView(View):
@@ -63,6 +66,16 @@ class BaseView(View):
validator: Auth = self.request.app["validator"]
return validator
+ @classmethod
+ async def get_permission(cls: Type[BaseView], request: Request) -> UserAccess:
+ """
+ retrieve user permission from the request
+ :param request: request object
+ :return: extracted permission
+ """
+ permission: UserAccess = getattr(cls, f"{request.method.upper()}_PERMISSION", UserAccess.Write)
+ return permission
+
async def extract_data(self, list_keys: Optional[List[str]] = None) -> Dict[str, Any]:
"""
extract json data from either json or form data
diff --git a/src/ahriman/web/views/index.py b/src/ahriman/web/views/index.py
index f617fa97..5c1d58f8 100644
--- a/src/ahriman/web/views/index.py
+++ b/src/ahriman/web/views/index.py
@@ -24,12 +24,15 @@ from typing import Any, Dict
from ahriman import version
from ahriman.core.auth.helpers import authorized_userid
from ahriman.core.util import pretty_datetime
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class IndexView(BaseView):
"""
root view
+ :cvar GET_PERMISSION: get permissions of self
+ :cvar HEAD_PERMISSION: head permissions of self
It uses jinja2 templates for report generation, the following variables are allowed:
@@ -58,6 +61,8 @@ class IndexView(BaseView):
version - ahriman version, string, required
"""
+ GET_PERMISSION = HEAD_PERMISSION = UserAccess.Safe
+
@aiohttp_jinja2.template("build-status.jinja2")
async def get(self) -> Dict[str, Any]:
"""
diff --git a/src/ahriman/web/views/service/add.py b/src/ahriman/web/views/service/add.py
index ed507faa..0e7a571c 100644
--- a/src/ahriman/web/views/service/add.py
+++ b/src/ahriman/web/views/service/add.py
@@ -19,14 +19,18 @@
#
from aiohttp.web import HTTPFound, Response, json_response
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class AddView(BaseView):
"""
add package web view
+ :cvar POST_PERMISSION: post permissions of self
"""
+ POST_PERMISSION = UserAccess.Write
+
async def post(self) -> Response:
"""
add new package
diff --git a/src/ahriman/web/views/service/reload_auth.py b/src/ahriman/web/views/service/reload_auth.py
index 36fcab40..8d9121f9 100644
--- a/src/ahriman/web/views/service/reload_auth.py
+++ b/src/ahriman/web/views/service/reload_auth.py
@@ -17,18 +17,21 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-from aiohttp.web import Response
-from aiohttp.web_exceptions import HTTPNoContent
+from aiohttp.web import HTTPNoContent, Response
from ahriman.core.auth.auth import Auth
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class ReloadAuthView(BaseView):
"""
reload authentication module web view
+ :cvar POST_PERMISSION: post permissions of self
"""
+ POST_PERMISSION = UserAccess.Write
+
async def post(self) -> Response:
"""
reload authentication module. No parameters supported here
diff --git a/src/ahriman/web/views/service/remove.py b/src/ahriman/web/views/service/remove.py
index bbb3da16..cbbf2ed6 100644
--- a/src/ahriman/web/views/service/remove.py
+++ b/src/ahriman/web/views/service/remove.py
@@ -19,14 +19,18 @@
#
from aiohttp.web import HTTPFound, Response, json_response
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class RemoveView(BaseView):
"""
remove package web view
+ :cvar POST_PERMISSION: post permissions of self
"""
+ POST_PERMISSION = UserAccess.Write
+
async def post(self) -> Response:
"""
remove existing packages
diff --git a/src/ahriman/web/views/service/search.py b/src/ahriman/web/views/service/search.py
index c5018a30..d53c84e6 100644
--- a/src/ahriman/web/views/service/search.py
+++ b/src/ahriman/web/views/service/search.py
@@ -22,14 +22,19 @@ import aur # type: ignore
from aiohttp.web import Response, json_response
from typing import Callable, Iterator
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class SearchView(BaseView):
"""
AUR search web view
+ :cvar GET_PERMISSION: get permissions of self
+ :cvar HEAD_PERMISSION: head permissions of self
"""
+ GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
+
async def get(self) -> Response:
"""
search packages in AUR
diff --git a/src/ahriman/web/views/status/ahriman.py b/src/ahriman/web/views/status/ahriman.py
index c04e14c5..40e0d101 100644
--- a/src/ahriman/web/views/status/ahriman.py
+++ b/src/ahriman/web/views/status/ahriman.py
@@ -20,14 +20,21 @@
from aiohttp.web import HTTPNoContent, Response, json_response
from ahriman.models.build_status import BuildStatusEnum
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class AhrimanView(BaseView):
"""
service status web view
+ :cvar GET_PERMISSION: get permissions of self
+ :cvar HEAD_PERMISSION: head permissions of self
+ :cvar POST_PERMISSION: post permissions of self
"""
+ GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
+ POST_PERMISSION = UserAccess.Write
+
async def get(self) -> Response:
"""
get current service status
diff --git a/src/ahriman/web/views/status/package.py b/src/ahriman/web/views/status/package.py
index 22672bba..ad814481 100644
--- a/src/ahriman/web/views/status/package.py
+++ b/src/ahriman/web/views/status/package.py
@@ -22,14 +22,22 @@ from aiohttp.web import HTTPNoContent, HTTPNotFound, Response, json_response
from ahriman.core.exceptions import UnknownPackage
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class PackageView(BaseView):
"""
package base specific web view
+ :cvar DELETE_PERMISSION: delete permissions of self
+ :cvar GET_PERMISSION: get permissions of self
+ :cvar HEAD_PERMISSION: head permissions of self
+ :cvar POST_PERMISSION: post permissions of self
"""
+ DELETE_PERMISSION = POST_PERMISSION = UserAccess.Write
+ GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
+
async def get(self) -> Response:
"""
get current package base status
diff --git a/src/ahriman/web/views/status/packages.py b/src/ahriman/web/views/status/packages.py
index 57d0768e..193adf15 100644
--- a/src/ahriman/web/views/status/packages.py
+++ b/src/ahriman/web/views/status/packages.py
@@ -19,14 +19,21 @@
#
from aiohttp.web import HTTPNoContent, Response, json_response
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class PackagesView(BaseView):
"""
global watcher view
+ :cvar GET_PERMISSION: get permissions of self
+ :cvar HEAD_PERMISSION: head permissions of self
+ :cvar POST_PERMISSION: post permissions of self
"""
+ GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
+ POST_PERMISSION = UserAccess.Write
+
async def get(self) -> Response:
"""
get current packages status
diff --git a/src/ahriman/web/views/status/status.py b/src/ahriman/web/views/status/status.py
index 84aaf937..a6e25373 100644
--- a/src/ahriman/web/views/status/status.py
+++ b/src/ahriman/web/views/status/status.py
@@ -22,14 +22,19 @@ from aiohttp.web import Response, json_response
from ahriman import version
from ahriman.models.counters import Counters
from ahriman.models.internal_status import InternalStatus
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class StatusView(BaseView):
"""
web service status web view
+ :cvar GET_PERMISSION: get permissions of self
+ :cvar HEAD_PERMISSION: head permissions of self
"""
+ GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
+
async def get(self) -> Response:
"""
get current service status
diff --git a/src/ahriman/web/views/user/login.py b/src/ahriman/web/views/user/login.py
index de32ef01..76593488 100644
--- a/src/ahriman/web/views/user/login.py
+++ b/src/ahriman/web/views/user/login.py
@@ -20,6 +20,7 @@
from aiohttp.web import HTTPFound, HTTPMethodNotAllowed, HTTPUnauthorized, Response
from ahriman.core.auth.helpers import remember
+from ahriman.models.user_access import UserAccess
from ahriman.models.user_identity import UserIdentity
from ahriman.web.views.base import BaseView
@@ -27,8 +28,12 @@ from ahriman.web.views.base import BaseView
class LoginView(BaseView):
"""
login endpoint view
+ :cvar GET_PERMISSION: get permissions of self
+ :cvar POST_PERMISSION: post permissions of self
"""
+ GET_PERMISSION = POST_PERMISSION = UserAccess.Safe
+
async def get(self) -> Response:
"""
OAuth2 response handler
diff --git a/src/ahriman/web/views/user/logout.py b/src/ahriman/web/views/user/logout.py
index 5e0ecde0..d107a8c0 100644
--- a/src/ahriman/web/views/user/logout.py
+++ b/src/ahriman/web/views/user/logout.py
@@ -20,14 +20,18 @@
from aiohttp.web import HTTPFound, Response
from ahriman.core.auth.helpers import check_authorized, forget
+from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView
class LogoutView(BaseView):
"""
logout endpoint view
+ :cvar POST_PERMISSION: post permissions of self
"""
+ POST_PERMISSION = UserAccess.Safe
+
async def post(self) -> Response:
"""
logout user from the service. No parameters supported here
diff --git a/tests/ahriman/core/auth/test_auth.py b/tests/ahriman/core/auth/test_auth.py
index 0a359066..8ec51f84 100644
--- a/tests/ahriman/core/auth/test_auth.py
+++ b/tests/ahriman/core/auth/test_auth.py
@@ -109,40 +109,6 @@ async def test_check_credentials(auth: Auth, user: User) -> None:
assert await auth.check_credentials(None, None)
-async def test_is_safe_request(auth: Auth) -> None:
- """
- must validate safe request
- """
- # login and logout are always safe
- assert await auth.is_safe_request("/user-api/v1/login", UserAccess.Write)
- assert await auth.is_safe_request("/user-api/v1/logout", UserAccess.Write)
-
- auth.allowed_paths.add("/safe")
- auth.allowed_paths_groups.add("/unsafe/safe")
-
- assert await auth.is_safe_request("/safe", UserAccess.Write)
- assert not await auth.is_safe_request("/unsafe", UserAccess.Write)
- assert await auth.is_safe_request("/unsafe/safe", UserAccess.Write)
- assert await auth.is_safe_request("/unsafe/safe/suffix", UserAccess.Write)
-
-
-async def test_is_safe_request_empty(auth: Auth) -> None:
- """
- must not allow requests without path
- """
- assert not await auth.is_safe_request(None, UserAccess.Read)
- assert not await auth.is_safe_request("", UserAccess.Read)
-
-
-async def test_is_safe_request_read_only(auth: Auth) -> None:
- """
- must allow read-only requests if it is set in settings
- """
- assert await auth.is_safe_request("/", UserAccess.Read)
- auth.allow_read_only = True
- assert await auth.is_safe_request("/unsafe", UserAccess.Read)
-
-
async def test_known_username(auth: Auth, user: User) -> None:
"""
must allow any username
diff --git a/tests/ahriman/web/middlewares/test_auth_handler.py b/tests/ahriman/web/middlewares/test_auth_handler.py
index d3d65004..40498b8e 100644
--- a/tests/ahriman/web/middlewares/test_auth_handler.py
+++ b/tests/ahriman/web/middlewares/test_auth_handler.py
@@ -43,60 +43,74 @@ async def test_permits(authorization_policy: AuthorizationPolicy, user: User) ->
assert not await authorization_policy.permits(user.username, user.access, "/endpoint")
-async def test_auth_handler_api(auth: Auth, mocker: MockerFixture) -> None:
+async def test_auth_handler_api(mocker: MockerFixture) -> None:
"""
must ask for status permission for api calls
"""
aiohttp_request = pytest.helpers.request("", "/status-api", "GET")
request_handler = AsyncMock()
- mocker.patch("ahriman.core.auth.auth.Auth.is_safe_request", return_value=False)
+ request_handler.get_permission.return_value = UserAccess.Read
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
- handler = auth_handler(auth)
+ handler = auth_handler()
await handler(aiohttp_request, request_handler)
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Read, aiohttp_request.path)
-async def test_auth_handler_api_post(auth: Auth, mocker: MockerFixture) -> None:
+async def test_auth_handler_api_no_method(mocker: MockerFixture) -> None:
+ """
+ must ask for write permission if handler does not have get_permission method
+ """
+ aiohttp_request = pytest.helpers.request("", "/status-api", "GET")
+ request_handler = AsyncMock()
+ request_handler.get_permission = None
+ check_permission_mock = mocker.patch("aiohttp_security.check_permission")
+
+ handler = auth_handler()
+ await handler(aiohttp_request, request_handler)
+ check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Write, aiohttp_request.path)
+
+
+async def test_auth_handler_api_post(mocker: MockerFixture) -> None:
"""
must ask for status permission for api calls with POST
"""
aiohttp_request = pytest.helpers.request("", "/status-api", "POST")
request_handler = AsyncMock()
- mocker.patch("ahriman.core.auth.auth.Auth.is_safe_request", return_value=False)
+ request_handler.get_permission.return_value = UserAccess.Write
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
- handler = auth_handler(auth)
+ handler = auth_handler()
await handler(aiohttp_request, request_handler)
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Write, aiohttp_request.path)
-async def test_auth_handler_read(auth: Auth, mocker: MockerFixture) -> None:
+async def test_auth_handler_read(mocker: MockerFixture) -> None:
"""
must ask for read permission for api calls with GET
"""
for method in ("GET", "HEAD", "OPTIONS"):
aiohttp_request = pytest.helpers.request("", "", method)
request_handler = AsyncMock()
- mocker.patch("ahriman.core.auth.auth.Auth.is_safe_request", return_value=False)
+ request_handler.get_permission.return_value = UserAccess.Read
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
- handler = auth_handler(auth)
+ handler = auth_handler()
await handler(aiohttp_request, request_handler)
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Read, aiohttp_request.path)
-async def test_auth_handler_write(auth: Auth, mocker: MockerFixture) -> None:
+async def test_auth_handler_write(mocker: MockerFixture) -> None:
"""
must ask for read permission for api calls with POST
"""
for method in ("CONNECT", "DELETE", "PATCH", "POST", "PUT", "TRACE"):
aiohttp_request = pytest.helpers.request("", "", method)
request_handler = AsyncMock()
- mocker.patch("ahriman.core.auth.auth.Auth.is_safe_request", return_value=False)
+ request_handler.get_permission.return_value = UserAccess.Write
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
- handler = auth_handler(auth)
+ handler = auth_handler()
await handler(aiohttp_request, request_handler)
check_permission_mock.assert_called_with(aiohttp_request, UserAccess.Write, aiohttp_request.path)
diff --git a/tests/ahriman/web/views/service/test_views_service_add.py b/tests/ahriman/web/views/service/test_views_service_add.py
index 4442dcab..8f03b3b4 100644
--- a/tests/ahriman/web/views/service/test_views_service_add.py
+++ b/tests/ahriman/web/views/service/test_views_service_add.py
@@ -1,6 +1,20 @@
+import pytest
+
from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.service.add import AddView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("POST",):
+ request = pytest.helpers.request("", "", method)
+ assert await AddView.get_permission(request) == UserAccess.Write
+
async def test_post(client: TestClient, mocker: MockerFixture) -> None:
"""
diff --git a/tests/ahriman/web/views/service/test_views_service_reload_auth.py b/tests/ahriman/web/views/service/test_views_service_reload_auth.py
index 793570e3..5594c366 100644
--- a/tests/ahriman/web/views/service/test_views_service_reload_auth.py
+++ b/tests/ahriman/web/views/service/test_views_service_reload_auth.py
@@ -1,6 +1,20 @@
+import pytest
+
from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.service.reload_auth import ReloadAuthView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("POST",):
+ request = pytest.helpers.request("", "", method)
+ assert await ReloadAuthView.get_permission(request) == UserAccess.Write
+
async def test_post(client_with_auth: TestClient, mocker: MockerFixture) -> None:
"""
diff --git a/tests/ahriman/web/views/service/test_views_service_remove.py b/tests/ahriman/web/views/service/test_views_service_remove.py
index df162079..3f60aa1a 100644
--- a/tests/ahriman/web/views/service/test_views_service_remove.py
+++ b/tests/ahriman/web/views/service/test_views_service_remove.py
@@ -1,6 +1,20 @@
+import pytest
+
from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.service.remove import RemoveView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("POST",):
+ request = pytest.helpers.request("", "", method)
+ assert await RemoveView.get_permission(request) == UserAccess.Write
+
async def test_post(client: TestClient, mocker: MockerFixture) -> None:
"""
diff --git a/tests/ahriman/web/views/service/test_views_service_search.py b/tests/ahriman/web/views/service/test_views_service_search.py
index 944b9fc8..fca7c088 100644
--- a/tests/ahriman/web/views/service/test_views_service_search.py
+++ b/tests/ahriman/web/views/service/test_views_service_search.py
@@ -1,8 +1,21 @@
import aur
+import pytest
from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.service.search import SearchView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("GET", "HEAD"):
+ request = pytest.helpers.request("", "", method)
+ assert await SearchView.get_permission(request) == UserAccess.Read
+
async def test_get(client: TestClient, aur_package_ahriman: aur.Package, mocker: MockerFixture) -> None:
"""
diff --git a/tests/ahriman/web/views/status/test_views_status_ahriman.py b/tests/ahriman/web/views/status/test_views_status_ahriman.py
index 7108701b..b251d750 100644
--- a/tests/ahriman/web/views/status/test_views_status_ahriman.py
+++ b/tests/ahriman/web/views/status/test_views_status_ahriman.py
@@ -1,7 +1,23 @@
+import pytest
+
from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.status.ahriman import AhrimanView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("GET", "HEAD"):
+ request = pytest.helpers.request("", "", method)
+ assert await AhrimanView.get_permission(request) == UserAccess.Read
+ for method in ("POST",):
+ request = pytest.helpers.request("", "", method)
+ assert await AhrimanView.get_permission(request) == UserAccess.Write
async def test_get(client: TestClient) -> None:
diff --git a/tests/ahriman/web/views/status/test_views_status_package.py b/tests/ahriman/web/views/status/test_views_status_package.py
index cdbe447e..dcbc91b2 100644
--- a/tests/ahriman/web/views/status/test_views_status_package.py
+++ b/tests/ahriman/web/views/status/test_views_status_package.py
@@ -1,7 +1,23 @@
+import pytest
+
from pytest_aiohttp import TestClient
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.status.package import PackageView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("GET", "HEAD"):
+ request = pytest.helpers.request("", "", method)
+ assert await PackageView.get_permission(request) == UserAccess.Read
+ for method in ("DELETE", "POST"):
+ request = pytest.helpers.request("", "", method)
+ assert await PackageView.get_permission(request) == UserAccess.Write
async def test_get(client: TestClient, package_ahriman: Package, package_python_schedule: Package) -> None:
diff --git a/tests/ahriman/web/views/status/test_views_status_packages.py b/tests/ahriman/web/views/status/test_views_status_packages.py
index 30d4a962..2533dc64 100644
--- a/tests/ahriman/web/views/status/test_views_status_packages.py
+++ b/tests/ahriman/web/views/status/test_views_status_packages.py
@@ -1,8 +1,24 @@
+import pytest
+
from pytest_aiohttp import TestClient
from pytest_mock import MockerFixture
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.status.packages import PackagesView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("GET", "HEAD"):
+ request = pytest.helpers.request("", "", method)
+ assert await PackagesView.get_permission(request) == UserAccess.Read
+ for method in ("POST",):
+ request = pytest.helpers.request("", "", method)
+ assert await PackagesView.get_permission(request) == UserAccess.Write
async def test_get(client: TestClient, package_ahriman: Package, package_python_schedule: Package) -> None:
diff --git a/tests/ahriman/web/views/status/test_views_status_status.py b/tests/ahriman/web/views/status/test_views_status_status.py
index 776f3775..4f9ac081 100644
--- a/tests/ahriman/web/views/status/test_views_status_status.py
+++ b/tests/ahriman/web/views/status/test_views_status_status.py
@@ -1,9 +1,22 @@
+import pytest
+
from pytest_aiohttp import TestClient
import ahriman.version as version
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.status.status import StatusView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("GET", "HEAD"):
+ request = pytest.helpers.request("", "", method)
+ assert await StatusView.get_permission(request) == UserAccess.Read
async def test_get(client: TestClient, package_ahriman: Package) -> None:
diff --git a/tests/ahriman/web/views/test_views_base.py b/tests/ahriman/web/views/test_views_base.py
index 907ef776..78eb56b9 100644
--- a/tests/ahriman/web/views/test_views_base.py
+++ b/tests/ahriman/web/views/test_views_base.py
@@ -33,6 +33,16 @@ def test_validator(base: BaseView) -> None:
assert base.validator
+async def test_get_permission(base: BaseView) -> None:
+ """
+ must search for permission attribute in class
+ """
+ for method in ("DELETE", "GET", "HEAD", "POST"):
+ request = pytest.helpers.request(base.request.app, "", method)
+ setattr(BaseView, f"{method.upper()}_PERMISSION", "permission")
+ assert await base.get_permission(request) == "permission"
+
+
async def test_extract_data_json(base: BaseView) -> None:
"""
must parse and return json
diff --git a/tests/ahriman/web/views/test_views_index.py b/tests/ahriman/web/views/test_views_index.py
index 0c9ff926..ede356f6 100644
--- a/tests/ahriman/web/views/test_views_index.py
+++ b/tests/ahriman/web/views/test_views_index.py
@@ -1,5 +1,19 @@
+import pytest
+
from pytest_aiohttp import TestClient
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.index import IndexView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("GET", "HEAD"):
+ request = pytest.helpers.request("", "", method)
+ assert await IndexView.get_permission(request) == UserAccess.Safe
+
async def test_get(client_with_auth: TestClient) -> None:
"""
@@ -34,3 +48,11 @@ async def test_get_static(client: TestClient) -> None:
"""
response = await client.get("/static/favicon.ico")
assert response.ok
+
+
+async def test_get_static_with_auth(client_with_auth: TestClient) -> None:
+ """
+ must return static files
+ """
+ response = await client_with_auth.get("/static/favicon.ico")
+ assert response.ok
diff --git a/tests/ahriman/web/views/user/test_views_user_login.py b/tests/ahriman/web/views/user/test_views_user_login.py
index e524f6dc..6e4608d1 100644
--- a/tests/ahriman/web/views/user/test_views_user_login.py
+++ b/tests/ahriman/web/views/user/test_views_user_login.py
@@ -1,9 +1,21 @@
+import pytest
from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.core.auth.oauth import OAuth
from ahriman.models.user import User
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.user.login import LoginView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("GET", "POST"):
+ request = pytest.helpers.request("", "", method)
+ assert await LoginView.get_permission(request) == UserAccess.Safe
async def test_get_default_validator(client_with_auth: TestClient) -> None:
diff --git a/tests/ahriman/web/views/user/test_views_user_logout.py b/tests/ahriman/web/views/user/test_views_user_logout.py
index 7e316204..0403898f 100644
--- a/tests/ahriman/web/views/user/test_views_user_logout.py
+++ b/tests/ahriman/web/views/user/test_views_user_logout.py
@@ -1,7 +1,21 @@
+import pytest
+
from aiohttp.test_utils import TestClient
from aiohttp.web import HTTPUnauthorized
from pytest_mock import MockerFixture
+from ahriman.models.user_access import UserAccess
+from ahriman.web.views.user.logout import LogoutView
+
+
+async def test_get_permission() -> None:
+ """
+ must return correct permission for the request
+ """
+ for method in ("POST",):
+ request = pytest.helpers.request("", "", method)
+ assert await LogoutView.get_permission(request) == UserAccess.Safe
+
async def test_post(client_with_auth: TestClient, mocker: MockerFixture) -> None:
"""