mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-28 06:41:43 +00:00
@ -52,8 +52,8 @@ class Status(Handler):
|
||||
# we are using reporter here
|
||||
client = Application(architecture, configuration, no_report=False, unsafe=unsafe).repository.reporter
|
||||
if args.ahriman:
|
||||
ahriman = client.get_self()
|
||||
StatusPrinter(ahriman).print(args.info)
|
||||
service_status = client.get_internal()
|
||||
StatusPrinter(service_status.status).print(args.info)
|
||||
if args.package:
|
||||
packages: Iterable[Tuple[Package, BuildStatus]] = sum(
|
||||
[client.get(base) for base in args.package],
|
||||
|
@ -37,7 +37,7 @@ class Auth:
|
||||
enabled(bool): indicates if authorization is enabled
|
||||
logger(logging.Logger): class logger
|
||||
max_age(int): session age in seconds. It will be used for both client side and server side checks
|
||||
safe_build_status(bool): allow read only access to the index page
|
||||
allow_read_only(bool): allow read only access to APIs
|
||||
"""
|
||||
|
||||
def __init__(self, configuration: Configuration, provider: AuthSettings = AuthSettings.Disabled) -> None:
|
||||
@ -50,7 +50,7 @@ class Auth:
|
||||
"""
|
||||
self.logger = logging.getLogger("http")
|
||||
|
||||
self.safe_build_status = configuration.getboolean("auth", "safe_build_status")
|
||||
self.allow_read_only = configuration.getboolean("auth", "allow_read_only")
|
||||
|
||||
self.enabled = provider.is_enabled
|
||||
self.max_age = configuration.getint("auth", "max_age", fallback=7 * 24 * 3600)
|
||||
|
33
src/ahriman/core/database/migrations/m002_user_access.py
Normal file
33
src/ahriman/core/database/migrations/m002_user_access.py
Normal file
@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
__all__ = ["steps"]
|
||||
|
||||
|
||||
steps = [
|
||||
"""
|
||||
update users set access = 'read' where access = 'safe'
|
||||
""",
|
||||
"""
|
||||
update users set access = 'reporter' where access = 'read'
|
||||
""",
|
||||
"""
|
||||
update users set access = 'full' where access = 'write'
|
||||
""",
|
||||
]
|
@ -80,16 +80,7 @@ class Client:
|
||||
Returns:
|
||||
InternalStatus: current internal (web) service status
|
||||
"""
|
||||
return InternalStatus()
|
||||
|
||||
def get_self(self) -> BuildStatus: # pylint: disable=no-self-use
|
||||
"""
|
||||
get ahriman status itself
|
||||
|
||||
Returns:
|
||||
BuildStatus: current ahriman status
|
||||
"""
|
||||
return BuildStatus()
|
||||
return InternalStatus(BuildStatus())
|
||||
|
||||
def remove(self, base: str) -> None:
|
||||
"""
|
||||
|
@ -57,16 +57,6 @@ class WebClient(Client):
|
||||
self.__session = requests.session()
|
||||
self._login()
|
||||
|
||||
@property
|
||||
def _ahriman_url(self) -> str:
|
||||
"""
|
||||
get url for the service status api
|
||||
|
||||
Returns:
|
||||
str: full url for web service for ahriman service itself
|
||||
"""
|
||||
return f"{self.address}/status-api/v1/ahriman"
|
||||
|
||||
@property
|
||||
def _login_url(self) -> str:
|
||||
"""
|
||||
@ -201,26 +191,7 @@ class WebClient(Client):
|
||||
self.logger.exception("could not get web service status: %s", exception_response_text(e))
|
||||
except Exception:
|
||||
self.logger.exception("could not get web service status")
|
||||
return InternalStatus()
|
||||
|
||||
def get_self(self) -> BuildStatus:
|
||||
"""
|
||||
get ahriman status itself
|
||||
|
||||
Returns:
|
||||
BuildStatus: current ahriman status
|
||||
"""
|
||||
try:
|
||||
response = self.__session.get(self._ahriman_url)
|
||||
response.raise_for_status()
|
||||
|
||||
status_json = response.json()
|
||||
return BuildStatus.from_json(status_json)
|
||||
except requests.HTTPError as e:
|
||||
self.logger.exception("could not get service status: %s", exception_response_text(e))
|
||||
except Exception:
|
||||
self.logger.exception("could not get service status")
|
||||
return BuildStatus()
|
||||
return InternalStatus(BuildStatus())
|
||||
|
||||
def remove(self, base: str) -> None:
|
||||
"""
|
||||
@ -265,7 +236,7 @@ class WebClient(Client):
|
||||
payload = {"status": status.value}
|
||||
|
||||
try:
|
||||
response = self.__session.post(self._ahriman_url, json=payload)
|
||||
response = self.__session.post(self._status_url, json=payload)
|
||||
response.raise_for_status()
|
||||
except requests.HTTPError as e:
|
||||
self.logger.exception("could not update service status: %s", exception_response_text(e))
|
||||
|
@ -46,40 +46,6 @@ class BuildStatusEnum(str, Enum):
|
||||
Failed = "failed"
|
||||
Success = "success"
|
||||
|
||||
def badges_color(self) -> str:
|
||||
"""
|
||||
convert itself to shield.io badges color
|
||||
|
||||
Returns:
|
||||
str: shields.io color
|
||||
"""
|
||||
if self == BuildStatusEnum.Pending:
|
||||
return "yellow"
|
||||
if self == BuildStatusEnum.Building:
|
||||
return "yellow"
|
||||
if self == BuildStatusEnum.Failed:
|
||||
return "critical"
|
||||
if self == BuildStatusEnum.Success:
|
||||
return "success"
|
||||
return "inactive"
|
||||
|
||||
def bootstrap_color(self) -> str:
|
||||
"""
|
||||
converts itself to bootstrap color
|
||||
|
||||
Returns:
|
||||
str: bootstrap color
|
||||
"""
|
||||
if self == BuildStatusEnum.Pending:
|
||||
return "warning"
|
||||
if self == BuildStatusEnum.Building:
|
||||
return "warning"
|
||||
if self == BuildStatusEnum.Failed:
|
||||
return "danger"
|
||||
if self == BuildStatusEnum.Success:
|
||||
return "success"
|
||||
return "secondary"
|
||||
|
||||
|
||||
@dataclass
|
||||
class BuildStatus:
|
||||
|
@ -22,6 +22,7 @@ from __future__ import annotations
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from typing import Any, Dict, Optional, Type
|
||||
|
||||
from ahriman.models.build_status import BuildStatus
|
||||
from ahriman.models.counters import Counters
|
||||
|
||||
|
||||
@ -31,12 +32,14 @@ class InternalStatus:
|
||||
internal server status
|
||||
|
||||
Attributes:
|
||||
status(BuildStatus): service status
|
||||
architecture(Optional[str]): repository architecture
|
||||
packages(Counters): packages statuses counter object
|
||||
repository(Optional[str]): repository name
|
||||
version(Optional[str]): service version
|
||||
"""
|
||||
|
||||
status: BuildStatus
|
||||
architecture: Optional[str] = None
|
||||
packages: Counters = field(default=Counters(total=0))
|
||||
repository: Optional[str] = None
|
||||
@ -54,7 +57,8 @@ class InternalStatus:
|
||||
InternalStatus: internal status
|
||||
"""
|
||||
counters = Counters.from_json(dump["packages"]) if "packages" in dump else Counters(total=0)
|
||||
return cls(architecture=dump.get("architecture"),
|
||||
return cls(status=BuildStatus.from_json(dump.get("status", {})),
|
||||
architecture=dump.get("architecture"),
|
||||
packages=counters,
|
||||
repository=dump.get("repository"),
|
||||
version=dump.get("version"))
|
||||
|
@ -142,9 +142,7 @@ class User:
|
||||
Returns:
|
||||
bool: True in case if user is allowed to do this request and False otherwise
|
||||
"""
|
||||
if self.access == UserAccess.Write:
|
||||
return True # everything is allowed
|
||||
return self.access == required
|
||||
return self.access.permits(required)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
|
@ -17,6 +17,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
@ -25,12 +27,31 @@ class UserAccess(str, Enum):
|
||||
web user access enumeration
|
||||
|
||||
Attributes:
|
||||
Safe(UserAccess): (class attribute) user can access the page without authorization,
|
||||
should not be used for user configuration
|
||||
Unauthorized(UserAccess): (class attribute) user can access specific resources which are marked as available
|
||||
without authorization (e.g. login, logout, static)
|
||||
Read(UserAccess): (class attribute) user can read the page
|
||||
Write(UserAccess): (class attribute) user can modify task and package list
|
||||
Reporter(UserAccess): (class attribute) user can read everything and is able to perform some modifications
|
||||
Full(UserAccess): (class attribute) user has full access
|
||||
"""
|
||||
|
||||
Safe = "safe"
|
||||
Unauthorized = "unauthorized"
|
||||
Read = "read"
|
||||
Write = "write"
|
||||
Reporter = "reporter"
|
||||
Full = "full"
|
||||
|
||||
def permits(self, other: UserAccess) -> bool:
|
||||
"""
|
||||
compare enumeration between each other and check if current permission allows the ``other``
|
||||
|
||||
Args:
|
||||
other(UserAccess): other permission to compare
|
||||
|
||||
Returns:
|
||||
bool: True in case if current permission allows the operation and False otherwise
|
||||
"""
|
||||
for member in UserAccess:
|
||||
if member == other:
|
||||
return True
|
||||
if member == self:
|
||||
return False
|
||||
return False # must never happen
|
||||
|
@ -89,10 +89,13 @@ class AuthorizationPolicy(aiohttp_security.AbstractAuthorizationPolicy): # type
|
||||
return await self.validator.verify_access(user.username, permission, context)
|
||||
|
||||
|
||||
def auth_handler() -> MiddlewareType:
|
||||
def auth_handler(allow_read_only: bool) -> MiddlewareType:
|
||||
"""
|
||||
authorization and authentication middleware
|
||||
|
||||
Args:
|
||||
allow_read_only: allow
|
||||
|
||||
Returns:
|
||||
MiddlewareType: built middleware
|
||||
"""
|
||||
@ -102,10 +105,14 @@ def auth_handler() -> MiddlewareType:
|
||||
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
|
||||
permission = UserAccess.Unauthorized if isinstance(handler_instance, StaticResource) else UserAccess.Full
|
||||
else:
|
||||
permission = UserAccess.Full
|
||||
if permission == UserAccess.Unauthorized: # explicit if elif else for better code coverage
|
||||
pass
|
||||
elif allow_read_only and UserAccess.Read.permits(permission):
|
||||
pass
|
||||
else:
|
||||
permission = UserAccess.Write
|
||||
if permission != UserAccess.Safe:
|
||||
await aiohttp_security.check_permission(request, permission, request.path)
|
||||
|
||||
return await handler(request)
|
||||
@ -133,6 +140,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())
|
||||
application.middlewares.append(auth_handler(validator.allow_read_only))
|
||||
|
||||
return application
|
||||
|
@ -25,7 +25,6 @@ from ahriman.web.views.service.add import AddView
|
||||
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
|
||||
from ahriman.web.views.status.packages import PackagesView
|
||||
from ahriman.web.views.status.status import StatusView
|
||||
@ -55,9 +54,6 @@ def setup_routes(application: Application, static_path: Path) -> None:
|
||||
|
||||
* POST /service-api/v1/update update packages in repository, actually it is just alias for add
|
||||
|
||||
* GET /status-api/v1/ahriman get current service status
|
||||
* POST /status-api/v1/ahriman update service status
|
||||
|
||||
* GET /status-api/v1/packages get all known packages
|
||||
* POST /status-api/v1/packages force update every package from repository
|
||||
|
||||
@ -65,7 +61,8 @@ def setup_routes(application: Application, static_path: Path) -> None:
|
||||
* GET /status-api/v1/package/:base get package base status
|
||||
* POST /status-api/v1/package/:base update package base status
|
||||
|
||||
* GET /status-api/v1/status get web service status itself
|
||||
* GET /status-api/v1/status get service status itself
|
||||
* POST /status-api/v1/status update service status itself
|
||||
|
||||
* GET /user-api/v1/login OAuth2 handler for login
|
||||
* POST /user-api/v1/login login to service
|
||||
@ -90,9 +87,6 @@ def setup_routes(application: Application, static_path: Path) -> None:
|
||||
|
||||
application.router.add_post("/service-api/v1/update", AddView)
|
||||
|
||||
application.router.add_get("/status-api/v1/ahriman", AhrimanView, allow_head=True)
|
||||
application.router.add_post("/status-api/v1/ahriman", AhrimanView)
|
||||
|
||||
application.router.add_get("/status-api/v1/packages", PackagesView, allow_head=True)
|
||||
application.router.add_post("/status-api/v1/packages", PackagesView)
|
||||
|
||||
@ -101,6 +95,7 @@ def setup_routes(application: Application, static_path: Path) -> None:
|
||||
application.router.add_post("/status-api/v1/packages/{package}", PackageView)
|
||||
|
||||
application.router.add_get("/status-api/v1/status", StatusView, allow_head=True)
|
||||
application.router.add_post("/status-api/v1/status", StatusView)
|
||||
|
||||
application.router.add_get("/user-api/v1/login", LoginView)
|
||||
application.router.add_post("/user-api/v1/login", LoginView)
|
||||
|
@ -101,7 +101,7 @@ class BaseView(View):
|
||||
Returns:
|
||||
UserAccess: extracted permission
|
||||
"""
|
||||
permission: UserAccess = getattr(cls, f"{request.method.upper()}_PERMISSION", UserAccess.Write)
|
||||
permission: UserAccess = getattr(cls, f"{request.method.upper()}_PERMISSION", UserAccess.Full)
|
||||
return permission
|
||||
|
||||
async def extract_data(self, list_keys: Optional[List[str]] = None) -> Dict[str, Any]:
|
||||
|
@ -21,9 +21,7 @@ import aiohttp_jinja2
|
||||
|
||||
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
|
||||
|
||||
@ -34,37 +32,19 @@ class IndexView(BaseView):
|
||||
|
||||
It uses jinja2 templates for report generation, the following variables are allowed:
|
||||
|
||||
* architecture - repository architecture, string, required
|
||||
* auth - authorization descriptor, required
|
||||
* authenticated - alias to check if user can see the page, boolean, required
|
||||
* control - HTML to insert for login control, HTML string, required
|
||||
* enabled - whether authorization is enabled by configuration or not, boolean, required
|
||||
* username - authenticated username if any, string, null means not authenticated
|
||||
* index_url - url to the repository index, string, optional
|
||||
* packages - sorted list of packages properties, required
|
||||
* base, string
|
||||
* depends, sorted list of strings
|
||||
* groups, sorted list of strings
|
||||
* licenses, sorted list of strings
|
||||
* packages, sorted list of strings
|
||||
* status, string based on enum value
|
||||
* status_color, string based on enum value
|
||||
* timestamp, pretty printed datetime, string
|
||||
* version, string
|
||||
* web_url, string
|
||||
* repository - repository name, string, required
|
||||
* service - service status properties, required
|
||||
* status, string based on enum value
|
||||
* status_color, string based on enum value
|
||||
* timestamp, pretty printed datetime, string
|
||||
* version - ahriman version, string, required
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Safe
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
@aiohttp_jinja2.template("build-status.jinja2")
|
||||
async def get(self) -> Dict[str, Any]:
|
||||
@ -74,43 +54,15 @@ class IndexView(BaseView):
|
||||
Returns:
|
||||
Dict[str, Any]: parameters for jinja template
|
||||
"""
|
||||
# some magic to make it jinja-friendly
|
||||
packages = [
|
||||
{
|
||||
"base": package.base,
|
||||
"depends": package.depends,
|
||||
"groups": package.groups,
|
||||
"licenses": package.licenses,
|
||||
"packages": list(sorted(package.packages)),
|
||||
"status": status.status.value,
|
||||
"status_color": status.status.bootstrap_color(),
|
||||
"timestamp": pretty_datetime(status.timestamp),
|
||||
"version": package.version,
|
||||
"web_url": package.remote.web_url if package.remote is not None else None,
|
||||
} for package, status in sorted(self.service.packages, key=lambda item: item[0].base)
|
||||
]
|
||||
service = {
|
||||
"status": self.service.status.status.value,
|
||||
"status_color": self.service.status.status.badges_color(),
|
||||
"timestamp": pretty_datetime(self.service.status.timestamp),
|
||||
}
|
||||
|
||||
# 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": authenticated,
|
||||
"control": self.validator.auth_control,
|
||||
"enabled": self.validator.enabled,
|
||||
"username": auth_username,
|
||||
}
|
||||
|
||||
return {
|
||||
"architecture": self.service.architecture,
|
||||
"auth": auth,
|
||||
"index_url": self.configuration.get("web", "index_url", fallback=None),
|
||||
"packages": packages,
|
||||
"repository": self.service.repository.name,
|
||||
"service": service,
|
||||
"version": version.__version__,
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class AddView(BaseView):
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
POST_PERMISSION = UserAccess.Write
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
|
@ -31,7 +31,7 @@ class RemoveView(BaseView):
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
POST_PERMISSION = UserAccess.Write
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
|
@ -31,7 +31,7 @@ class RequestView(BaseView):
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
POST_PERMISSION = UserAccess.Read
|
||||
POST_PERMISSION = UserAccess.Reporter
|
||||
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
|
@ -35,7 +35,7 @@ class SearchView(BaseView):
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Reporter
|
||||
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
|
@ -1,71 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2021-2022 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import HTTPBadRequest, 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
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
|
||||
POST_PERMISSION = UserAccess.Write
|
||||
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get current service status
|
||||
|
||||
Returns:
|
||||
Response: 200 with service status object
|
||||
"""
|
||||
return json_response(self.service.status.view())
|
||||
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
update service status
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"status": "unknown", # service status string, must be valid ``BuildStatusEnum``
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
"""
|
||||
try:
|
||||
data = await self.extract_data()
|
||||
status = BuildStatusEnum(data["status"])
|
||||
except Exception as e:
|
||||
raise HTTPBadRequest(reason=str(e))
|
||||
|
||||
self.service.update_self(status)
|
||||
|
||||
raise HTTPNoContent()
|
@ -37,7 +37,7 @@ class PackageView(BaseView):
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
DELETE_PERMISSION = POST_PERMISSION = UserAccess.Write
|
||||
DELETE_PERMISSION = POST_PERMISSION = UserAccess.Full
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
|
||||
|
||||
async def get(self) -> Response:
|
||||
|
@ -34,7 +34,7 @@ class PackagesView(BaseView):
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
|
||||
POST_PERMISSION = UserAccess.Write
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
|
@ -17,9 +17,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from aiohttp.web import Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
|
||||
from ahriman import version
|
||||
from ahriman.models.build_status import BuildStatusEnum
|
||||
from ahriman.models.counters import Counters
|
||||
from ahriman.models.internal_status import InternalStatus
|
||||
from ahriman.models.user_access import UserAccess
|
||||
@ -33,9 +34,11 @@ class StatusView(BaseView):
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
@ -46,9 +49,34 @@ class StatusView(BaseView):
|
||||
"""
|
||||
counters = Counters.from_packages(self.service.packages)
|
||||
status = InternalStatus(
|
||||
status=self.service.status,
|
||||
architecture=self.service.architecture,
|
||||
packages=counters,
|
||||
repository=self.service.repository.name,
|
||||
version=version.__version__)
|
||||
|
||||
return json_response(status.view())
|
||||
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
update service status
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"status": "unknown", # service status string, must be valid ``BuildStatusEnum``
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
"""
|
||||
try:
|
||||
data = await self.extract_data()
|
||||
status = BuildStatusEnum(data["status"])
|
||||
except Exception as e:
|
||||
raise HTTPBadRequest(reason=str(e))
|
||||
|
||||
self.service.update_self(status)
|
||||
|
||||
raise HTTPNoContent()
|
||||
|
@ -34,7 +34,7 @@ class LoginView(BaseView):
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = POST_PERMISSION = UserAccess.Safe
|
||||
GET_PERMISSION = POST_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
async def get(self) -> None:
|
||||
"""
|
||||
|
@ -32,7 +32,7 @@ class LogoutView(BaseView):
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
POST_PERMISSION = UserAccess.Safe
|
||||
POST_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
|
Reference in New Issue
Block a user