implement support of unix socket for server

This feature can be used for unauthorized access to apis - e.g. for
reporting service if it is run on the same machine. Since now it becomes
recommended way for the interprocess communication, thus some options
(e.g. creating user with as-service flag) are no longer available now
This commit is contained in:
2022-11-29 01:18:01 +02:00
parent 4811dec759
commit 0161617e36
24 changed files with 247 additions and 134 deletions

View File

@ -3,9 +3,8 @@ import pytest
from asyncio import BaseEventLoop
from aiohttp import web
from aiohttp.test_utils import TestClient
from collections import namedtuple
from pytest_mock import MockerFixture
from typing import Any
from typing import Any, Dict, Optional
from unittest.mock import MagicMock
import ahriman.core.auth.helpers
@ -18,11 +17,9 @@ from ahriman.models.user import User
from ahriman.web.web import setup_service
_request = namedtuple("_request", ["app", "path", "method", "json", "post"])
@pytest.helpers.register
def request(app: web.Application, path: str, method: str, json: Any = None, data: Any = None) -> _request:
def request(app: web.Application, path: str, method: str, json: Any = None, data: Any = None,
extra: Optional[Dict[str, Any]] = None) -> MagicMock:
"""
request generator helper
@ -32,11 +29,22 @@ def request(app: web.Application, path: str, method: str, json: Any = None, data
method(str): method for the request
json(Any, optional): json payload of the request (Default value = None)
data(Any, optional): form data payload of the request (Default value = None)
extra(Optional[Dict[str, Any]], optional): extra info which will be injected for ``get_extra_info`` command
Returns:
_request: dummy request object
MagicMock: dummy request mock
"""
return _request(app, path, method, json, data)
request_mock = MagicMock()
request_mock.app = app
request_mock.path = path
request_mock.method = method
request_mock.json = json
request_mock.post = data
extra = extra or {}
request_mock.get_extra_info.side_effect = lambda key: extra.get(key)
return request_mock
@pytest.fixture

View File

@ -1,4 +1,5 @@
import pytest
import socket
from aiohttp import web
from aiohttp.test_utils import TestClient
@ -55,6 +56,21 @@ 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_unix_socket(client_with_auth: TestClient, mocker: MockerFixture) -> None:
"""
must allow calls via unix sockets
"""
aiohttp_request = pytest.helpers.request(
"", "/api/v1/status", "GET", extra={"socket": socket.socket(socket.AF_UNIX)})
request_handler = AsyncMock()
request_handler.get_permission.return_value = UserAccess.Full
check_permission_mock = mocker.patch("aiohttp_security.check_permission")
handler = auth_handler(allow_read_only=False)
await handler(aiohttp_request, request_handler)
check_permission_mock.assert_not_called()
async def test_auth_handler_api(mocker: MockerFixture) -> None:
"""
must ask for status permission for api calls

View File

@ -50,7 +50,7 @@ def test_run(application: web.Application, mocker: MockerFixture) -> None:
run_server(application)
run_application_mock.assert_called_once_with(
application, host="127.0.0.1", port=port, handle_signals=False,
application, host="127.0.0.1", port=port, path=None, handle_signals=False,
access_log=pytest.helpers.anyvar(int), access_log_class=FilteredAccessLogger
)
@ -65,7 +65,7 @@ def test_run_with_auth(application_with_auth: web.Application, mocker: MockerFix
run_server(application_with_auth)
run_application_mock.assert_called_once_with(
application_with_auth, host="127.0.0.1", port=port, handle_signals=False,
application_with_auth, host="127.0.0.1", port=port, path=None, handle_signals=False,
access_log=pytest.helpers.anyvar(int), access_log_class=FilteredAccessLogger
)
@ -80,6 +80,23 @@ def test_run_with_debug(application_with_debug: web.Application, mocker: MockerF
run_server(application_with_debug)
run_application_mock.assert_called_once_with(
application_with_debug, host="127.0.0.1", port=port, handle_signals=False,
application_with_debug, host="127.0.0.1", port=port, path=None, handle_signals=False,
access_log=pytest.helpers.anyvar(int), access_log_class=FilteredAccessLogger
)
def test_run_with_socket(application: web.Application, mocker: MockerFixture) -> None:
"""
must run application
"""
port = 8080
socket = "/run/ahriman.sock"
application["configuration"].set_option("web", "port", str(port))
application["configuration"].set_option("web", "unix_socket", socket)
run_application_mock = mocker.patch("aiohttp.web.run_app")
run_server(application)
run_application_mock.assert_called_once_with(
application, host="127.0.0.1", port=port, path=socket, handle_signals=False,
access_log=pytest.helpers.anyvar(int), access_log_class=FilteredAccessLogger
)