mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-05-26 00:26:16 +00:00
feat: allow readonly events with read permission
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
#
|
#
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, StreamResponse
|
from aiohttp.web import HTTPBadRequest, Request, StreamResponse
|
||||||
from aiohttp_sse import EventSourceResponse, sse_response
|
from aiohttp_sse import EventSourceResponse, sse_response
|
||||||
from asyncio import Queue, QueueShutDown, wait_for
|
from asyncio import Queue, QueueShutDown, wait_for
|
||||||
from typing import ClassVar
|
from typing import ClassVar
|
||||||
@@ -35,14 +35,47 @@ from ahriman.web.views.base import BaseView
|
|||||||
class EventBusView(BaseView):
|
class EventBusView(BaseView):
|
||||||
"""
|
"""
|
||||||
event bus SSE view
|
event bus SSE view
|
||||||
|
|
||||||
Attributes:
|
|
||||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
GET_PERMISSION: ClassVar[UserAccess] = UserAccess.Full
|
READ_EVENTS: ClassVar[set[EventType]] = {
|
||||||
|
EventType.PackageHeld,
|
||||||
|
EventType.PackageOutdated,
|
||||||
|
EventType.PackageRemoved,
|
||||||
|
EventType.PackageStatusChanged,
|
||||||
|
EventType.PackageUpdateFailed,
|
||||||
|
EventType.PackageUpdated,
|
||||||
|
EventType.ServiceStatusChanged,
|
||||||
|
}
|
||||||
ROUTES = ["/api/v1/events/stream"]
|
ROUTES = ["/api/v1/events/stream"]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def get_permission(cls, request: Request) -> UserAccess:
|
||||||
|
"""
|
||||||
|
retrieve user permission from the request
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request(Request): request object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
UserAccess: extracted permission
|
||||||
|
"""
|
||||||
|
if request.method.upper() not in ("GET", "HEAD"):
|
||||||
|
return await BaseView.get_permission(request)
|
||||||
|
|
||||||
|
permission = UserAccess.Full
|
||||||
|
event_filter = request.query.getall("event", []) if request.query is not None else []
|
||||||
|
|
||||||
|
if event_filter:
|
||||||
|
try:
|
||||||
|
topics = {EventType(event) for event in event_filter}
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if topics.issubset(cls.READ_EVENTS):
|
||||||
|
permission = UserAccess.Read
|
||||||
|
|
||||||
|
return permission
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def _run(response: EventSourceResponse, queue: Queue[SSEvent]) -> None:
|
async def _run(response: EventSourceResponse, queue: Queue[SSEvent]) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -66,7 +99,7 @@ class EventBusView(BaseView):
|
|||||||
tags=["Audit log"],
|
tags=["Audit log"],
|
||||||
summary="Live updates",
|
summary="Live updates",
|
||||||
description="Stream live updates via SSE",
|
description="Stream live updates via SSE",
|
||||||
permission=GET_PERMISSION,
|
permission=UserAccess.Full,
|
||||||
error_400_enabled=True,
|
error_400_enabled=True,
|
||||||
error_404_description="Repository is unknown",
|
error_404_description="Repository is unknown",
|
||||||
schema=SSESchema(many=True),
|
schema=SSESchema(many=True),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import pytest
|
|||||||
|
|
||||||
from aiohttp.test_utils import TestClient
|
from aiohttp.test_utils import TestClient
|
||||||
from asyncio import Queue
|
from asyncio import Queue
|
||||||
|
from multidict import MultiDict
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ from ahriman.models.event import EventType
|
|||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.keys import WatcherKey
|
from ahriman.web.keys import WatcherKey
|
||||||
|
from ahriman.web.views.base import BaseView
|
||||||
from ahriman.web.views.v1.auditlog.event_bus import EventBusView
|
from ahriman.web.views.v1.auditlog.event_bus import EventBusView
|
||||||
|
|
||||||
|
|
||||||
@@ -38,6 +40,51 @@ async def test_get_permission() -> None:
|
|||||||
assert await EventBusView.get_permission(request) == UserAccess.Full
|
assert await EventBusView.get_permission(request) == UserAccess.Full
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_permission_build_log() -> None:
|
||||||
|
"""
|
||||||
|
must return full permission for build log stream
|
||||||
|
"""
|
||||||
|
request = pytest.helpers.request("", "", "GET", params=MultiDict(event=EventType.BuildLog))
|
||||||
|
assert await EventBusView.get_permission(request) == UserAccess.Full
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_permission_build_log_with_read_events() -> None:
|
||||||
|
"""
|
||||||
|
must return full permission for mixed build log and read event stream
|
||||||
|
"""
|
||||||
|
request = pytest.helpers.request("", "", "GET", params=MultiDict([
|
||||||
|
("event", EventType.BuildLog),
|
||||||
|
("event", EventType.PackageUpdated),
|
||||||
|
]))
|
||||||
|
assert await EventBusView.get_permission(request) == UserAccess.Full
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_permission_invalid_event() -> None:
|
||||||
|
"""
|
||||||
|
must return full permission for invalid event type
|
||||||
|
"""
|
||||||
|
request = pytest.helpers.request("", "", "GET", params=MultiDict(event="invalid"))
|
||||||
|
assert await EventBusView.get_permission(request) == UserAccess.Full
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_permission_post() -> None:
|
||||||
|
"""
|
||||||
|
must use default permission for non-get requests
|
||||||
|
"""
|
||||||
|
request = pytest.helpers.request("", "", "POST", params=MultiDict(event=EventType.PackageUpdated))
|
||||||
|
assert await EventBusView.get_permission(request) == await BaseView.get_permission(request)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_permission_read_events() -> None:
|
||||||
|
"""
|
||||||
|
must return read permission for package and status streams
|
||||||
|
"""
|
||||||
|
request = pytest.helpers.request("", "", "GET", params=MultiDict(
|
||||||
|
("event", event_type) for event_type in EventBusView.READ_EVENTS
|
||||||
|
))
|
||||||
|
assert await EventBusView.get_permission(request) == UserAccess.Read
|
||||||
|
|
||||||
|
|
||||||
def test_routes() -> None:
|
def test_routes() -> None:
|
||||||
"""
|
"""
|
||||||
must return correct routes
|
must return correct routes
|
||||||
|
|||||||
Reference in New Issue
Block a user