diff --git a/docs/architecture.md b/docs/architecture.md index f066fa9c..5fa7e6b2 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -148,6 +148,7 @@ Some features require optional dependencies to be installed: Web application requires the following python packages to be installed: * Core part requires `aiohttp` (application itself), `aiohttp_jinja2` and `Jinja2` (HTML generation from templates). +* In addition, `aiohttp_debugtoolbar` is required for debug panel. Please note that this option does not work together with authorization and basically must not be used in production. * In addition, authorization feature requires `aiohttp_security`, `aiohttp_session` and `cryptography`. * In addition to base authorization dependencies, OAuth2 also requires `aioauth-client` library. @@ -173,9 +174,9 @@ Package provides base jinja templates which can be overridden by settings. Vanil ## Requests and scopes -Service provides optional authorization which can be turned on in settings. In order to control user access there are two levels of authorization - read-only (only GET-like requests) and write (anything). +Service provides optional authorization which can be turned on in settings. In order to control user access there are two levels of authorization - read-only (only GET-like requests) and write (anything) which are provided by each web view directly. -If this feature is configured any request except for whitelisted will be prohibited without authentication. In addition, configuration flag `auth.allow_read_only` can be used in order to allow seeing main page without authorization (this page is in default white list). +If this feature is configured any request will be prohibited without authentication. In addition, configuration flag `auth.safe_build_status` can be used in order to allow seeing main page without authorization. For authenticated users it uses encrypted session cookies to store tokens; encryption key is generated each time at the start of the application. It also stores expiration time of the session inside. diff --git a/docs/configuration.md b/docs/configuration.md index d7ffa15c..2299b8fa 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -23,12 +23,12 @@ libalpm and AUR related configuration. Base authorization settings. `OAuth` provider requires `aioauth-client` library to be installed. * `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. * `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. * `oauth_provider` - OAuth2 provider class name as is in `aioauth-client` (e.g. `GoogleClient`, `GithubClient` etc), string, required in case if `oauth` is used. * `oauth_scopes` - scopes list for OAuth2 provider, which will allow retrieving user email (which is used for checking user permissions), e.g. `https://www.googleapis.com/auth/userinfo.email` for `GoogleClient` or `user:email` for `GithubClient`, space separated list of strings, required in case if `oauth` is used. +* `safe_build_status` - allow requesting status page without authorization, boolean, required. * `salt` - password hash salt, string, required in case if authorization enabled (automatically generated by `create-user` subcommand). ## `auth:*` groups diff --git a/package/etc/ahriman.ini b/package/etc/ahriman.ini index ec1b3237..cb143ba6 100644 --- a/package/etc/ahriman.ini +++ b/package/etc/ahriman.ini @@ -10,10 +10,10 @@ root = / [auth] target = disabled -allow_read_only = yes max_age = 604800 oauth_provider = GoogleClient oauth_scopes = https://www.googleapis.com/auth/userinfo.email +safe_build_status = yes [build] archbuild_flags = diff --git a/package/share/ahriman/build-status/package-actions-modals.jinja2 b/package/share/ahriman/build-status/package-actions-modals.jinja2 index fb72034d..8dcd1ae8 100644 --- a/package/share/ahriman/build-status/package-actions-modals.jinja2 +++ b/package/share/ahriman/build-status/package-actions-modals.jinja2 @@ -16,6 +16,7 @@
diff --git a/package/share/ahriman/build-status/package-actions-script.jinja2 b/package/share/ahriman/build-status/package-actions-script.jinja2 index 3c6d41c3..80c26d48 100644 --- a/package/share/ahriman/build-status/package-actions-script.jinja2 +++ b/package/share/ahriman/build-status/package-actions-script.jinja2 @@ -80,6 +80,11 @@ doPackageAction("/service-api/v1/add", $packages); } + function requestPackages() { + const $packages = [$package.val()] + doPackageAction("/service-api/v1/request", $packages); + } + function removePackages() { doPackageAction("/service-api/v1/remove", getSelection()); } function updatePackages() { doPackageAction("/service-api/v1/add", getSelection()); } diff --git a/src/ahriman/core/auth/auth.py b/src/ahriman/core/auth/auth.py index a953d538..e20ba508 100644 --- a/src/ahriman/core/auth/auth.py +++ b/src/ahriman/core/auth/auth.py @@ -33,9 +33,9 @@ from ahriman.models.user_access import UserAccess class Auth: """ helper to deal with user authorization - :ivar allow_read_only: allow read only access to the index page :ivar enabled: indicates if authorization is enabled :ivar max_age: session age in seconds. It will be used for both client side and server side checks + :ivar safe_build_status: allow read only access to the index page """ def __init__(self, configuration: Configuration, provider: AuthSettings = AuthSettings.Disabled) -> None: @@ -46,7 +46,7 @@ class Auth: """ self.logger = logging.getLogger("http") - self.allow_read_only = configuration.getboolean("auth", "allow_read_only") + self.safe_build_status = configuration.getboolean("auth", "safe_build_status") self.enabled = provider.is_enabled self.max_age = configuration.getint("auth", "max_age", fallback=7 * 24 * 3600) diff --git a/src/ahriman/web/routes.py b/src/ahriman/web/routes.py index cbdad29e..b00096cf 100644 --- a/src/ahriman/web/routes.py +++ b/src/ahriman/web/routes.py @@ -24,6 +24,7 @@ from ahriman.web.views.index import IndexView from ahriman.web.views.service.add import AddView from ahriman.web.views.service.reload_auth import ReloadAuthView from ahriman.web.views.service.remove import RemoveView +from ahriman.web.views.service.request import RequestView from ahriman.web.views.service.search import SearchView from ahriman.web.views.status.ahriman import AhrimanView from ahriman.web.views.status.package import PackageView @@ -48,6 +49,8 @@ def setup_routes(application: Application, static_path: Path) -> None: POST /service-api/v1/remove remove existing package from repository + POST /service-api/v1/request request to add new packages to repository + GET /service-api/v1/search search for substring in AUR POST /service-api/v1/update update packages in repository, actually it is just alias for add @@ -82,6 +85,8 @@ def setup_routes(application: Application, static_path: Path) -> None: application.router.add_post("/service-api/v1/remove", RemoveView) + application.router.add_post("/service-api/v1/request", RequestView) + application.router.add_get("/service-api/v1/search", SearchView, allow_head=False) application.router.add_post("/service-api/v1/update", AddView) diff --git a/src/ahriman/web/views/index.py b/src/ahriman/web/views/index.py index 23e4021a..46877ab7 100644 --- a/src/ahriman/web/views/index.py +++ b/src/ahriman/web/views/index.py @@ -93,8 +93,9 @@ class IndexView(BaseView): # auth block auth_username = await authorized_userid(self.request) + authenticated = not self.validator.enabled or self.validator.safe_build_status or auth_username is not None auth = { - "authenticated": not self.validator.enabled or self.validator.allow_read_only or auth_username is not None, + "authenticated": authenticated, "control": self.validator.auth_control, "enabled": self.validator.enabled, "username": auth_username, diff --git a/src/ahriman/web/views/service/add.py b/src/ahriman/web/views/service/add.py index 0e7a571c..63a52426 100644 --- a/src/ahriman/web/views/service/add.py +++ b/src/ahriman/web/views/service/add.py @@ -37,8 +37,7 @@ class AddView(BaseView): JSON body must be supplied, the following model is used: { - "packages": "ahriman", # either list of packages or package name as in AUR - "build_now": true # optional flag which runs build + "packages": "ahriman" # either list of packages or package name as in AUR } :return: redirect to main page on success @@ -46,11 +45,10 @@ class AddView(BaseView): data = await self.extract_data(["packages"]) try: - now = data.get("build_now", True) packages = data["packages"] except Exception as e: return json_response(data=str(e), status=400) - self.spawner.packages_add(packages, now) + self.spawner.packages_add(packages, now=True) raise HTTPFound("/") diff --git a/src/ahriman/web/views/service/request.py b/src/ahriman/web/views/service/request.py new file mode 100644 index 00000000..611ca7fa --- /dev/null +++ b/src/ahriman/web/views/service/request.py @@ -0,0 +1,54 @@ +# +# Copyright (c) 2021 ahriman team. +# +# This file is part of ahriman +# (see https://github.com/arcan1s/ahriman). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see