mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-07-29 13:49:57 +00:00
OAuth2 (#32)
* make auth method asyncs * oauth2 demo support * full coverage * update docs
This commit is contained in:
@ -2,10 +2,9 @@ import pytest
|
||||
|
||||
from aiohttp import web
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from ahriman.core.auth.auth import Auth
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.models.user import User
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.middlewares.auth_handler import auth_handler, AuthorizationPolicy, setup_auth
|
||||
@ -23,7 +22,7 @@ async def test_permits(authorization_policy: AuthorizationPolicy, user: User) ->
|
||||
"""
|
||||
must call validator check
|
||||
"""
|
||||
authorization_policy.validator = MagicMock()
|
||||
authorization_policy.validator = AsyncMock()
|
||||
authorization_policy.validator.verify_access.return_value = True
|
||||
|
||||
assert await authorization_policy.permits(user.username, user.access, "/endpoint")
|
||||
|
@ -9,7 +9,7 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None:
|
||||
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
|
||||
response = await client.post("/service-api/v1/add", json={"packages": ["ahriman"]})
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
add_mock.assert_called_with(["ahriman"], True)
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ async def test_post_now(client: TestClient, mocker: MockerFixture) -> None:
|
||||
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
|
||||
response = await client.post("/service-api/v1/add", json={"packages": ["ahriman"], "build_now": False})
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
add_mock.assert_called_with(["ahriman"], False)
|
||||
|
||||
|
||||
@ -42,5 +42,5 @@ async def test_post_update(client: TestClient, mocker: MockerFixture) -> None:
|
||||
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
|
||||
response = await client.post("/service-api/v1/update", json={"packages": ["ahriman"]})
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
add_mock.assert_called_with(["ahriman"], True)
|
||||
|
@ -9,7 +9,7 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None:
|
||||
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_remove")
|
||||
response = await client.post("/service-api/v1/remove", json={"packages": ["ahriman"]})
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
add_mock.assert_called_with(["ahriman"])
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ async def test_get(client: TestClient, aur_package_ahriman: aur.Package, mocker:
|
||||
mocker.patch("aur.search", return_value=[aur_package_ahriman])
|
||||
response = await client.get("/service-api/v1/search", params={"for": "ahriman"})
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
assert await response.json() == ["ahriman"]
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ async def test_get_join(client: TestClient, mocker: MockerFixture) -> None:
|
||||
search_mock = mocker.patch("aur.search")
|
||||
response = await client.get("/service-api/v1/search", params=[("for", "ahriman"), ("for", "maybe")])
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
search_mock.assert_called_with("ahriman maybe")
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ async def test_get_join_filter(client: TestClient, mocker: MockerFixture) -> Non
|
||||
search_mock = mocker.patch("aur.search")
|
||||
response = await client.get("/service-api/v1/search", params=[("for", "ah"), ("for", "maybe")])
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
search_mock.assert_called_with("maybe")
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ async def test_get(client: TestClient) -> None:
|
||||
response = await client.get("/status-api/v1/ahriman")
|
||||
status = BuildStatus.from_json(await response.json())
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
assert status.status == BuildStatusEnum.Unknown
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ async def test_post(client: TestClient) -> None:
|
||||
response = await client.get("/status-api/v1/ahriman")
|
||||
status = BuildStatus.from_json(await response.json())
|
||||
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
assert status.status == BuildStatusEnum.Success
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ async def test_get(client: TestClient, package_ahriman: Package, package_python_
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
|
||||
|
||||
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
|
||||
packages = [Package.from_json(item["package"]) for item in await response.json()]
|
||||
assert packages
|
||||
@ -45,7 +45,7 @@ async def test_delete(client: TestClient, package_ahriman: Package, package_pyth
|
||||
assert response.status == 404
|
||||
|
||||
response = await client.get(f"/status-api/v1/packages/{package_python_schedule.base}")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
|
||||
|
||||
async def test_delete_unknown(client: TestClient, package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||
@ -62,7 +62,7 @@ async def test_delete_unknown(client: TestClient, package_ahriman: Package, pack
|
||||
assert response.status == 404
|
||||
|
||||
response = await client.get(f"/status-api/v1/packages/{package_python_schedule.base}")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
|
||||
|
||||
async def test_post(client: TestClient, package_ahriman: Package) -> None:
|
||||
@ -75,7 +75,7 @@ async def test_post(client: TestClient, package_ahriman: Package) -> None:
|
||||
assert post_response.status == 204
|
||||
|
||||
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
|
||||
|
||||
async def test_post_exception(client: TestClient, package_ahriman: Package) -> None:
|
||||
@ -100,7 +100,7 @@ async def test_post_light(client: TestClient, package_ahriman: Package) -> None:
|
||||
assert post_response.status == 204
|
||||
|
||||
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
statuses = {
|
||||
Package.from_json(item["package"]).base: BuildStatus.from_json(item["status"])
|
||||
for item in await response.json()
|
||||
|
@ -15,7 +15,7 @@ async def test_get(client: TestClient, package_ahriman: Package, package_python_
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
|
||||
|
||||
response = await client.get("/status-api/v1/packages")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
|
||||
packages = [Package.from_json(item["package"]) for item in await response.json()]
|
||||
assert packages
|
||||
|
@ -14,7 +14,7 @@ async def test_get(client: TestClient, package_ahriman: Package) -> None:
|
||||
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||
|
||||
response = await client.get("/status-api/v1/status")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
|
||||
json = await response.json()
|
||||
assert json["version"] == version.__version__
|
||||
|
@ -6,7 +6,7 @@ async def test_get(client_with_auth: TestClient) -> None:
|
||||
must generate status page correctly (/)
|
||||
"""
|
||||
response = await client_with_auth.get("/")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
assert await response.text()
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ async def test_get_index(client_with_auth: TestClient) -> None:
|
||||
must generate status page correctly (/index.html)
|
||||
"""
|
||||
response = await client_with_auth.get("/index.html")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
assert await response.text()
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ async def test_get_without_auth(client: TestClient) -> None:
|
||||
must use dummy authorized_userid function in case if no security library installed
|
||||
"""
|
||||
response = await client.get("/")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
assert await response.text()
|
||||
|
||||
|
||||
@ -33,4 +33,4 @@ async def test_get_static(client: TestClient) -> None:
|
||||
must return static files
|
||||
"""
|
||||
response = await client.get("/static/favicon.ico")
|
||||
assert response.status == 200
|
||||
assert response.ok
|
||||
|
@ -1,9 +1,75 @@
|
||||
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
|
||||
|
||||
|
||||
async def test_get_default_validator(client_with_auth: TestClient) -> None:
|
||||
"""
|
||||
must return 405 in case if no OAuth enabled
|
||||
"""
|
||||
get_response = await client_with_auth.get("/user-api/v1/login")
|
||||
assert get_response.status == 405
|
||||
|
||||
|
||||
async def test_get_redirect_to_oauth(client_with_auth: TestClient) -> None:
|
||||
"""
|
||||
must redirect to OAuth service provider in case if no code is supplied
|
||||
"""
|
||||
oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth)
|
||||
oauth.get_oauth_url.return_value = "https://example.com"
|
||||
|
||||
get_response = await client_with_auth.get("/user-api/v1/login")
|
||||
assert get_response.ok
|
||||
oauth.get_oauth_url.assert_called_once()
|
||||
|
||||
|
||||
async def test_get_redirect_to_oauth_empty_code(client_with_auth: TestClient) -> None:
|
||||
"""
|
||||
must redirect to OAuth service provider in case if empty code is supplied
|
||||
"""
|
||||
oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth)
|
||||
oauth.get_oauth_url.return_value = "https://example.com"
|
||||
|
||||
get_response = await client_with_auth.get("/user-api/v1/login", params={"code": ""})
|
||||
assert get_response.ok
|
||||
oauth.get_oauth_url.assert_called_once()
|
||||
|
||||
|
||||
async def test_get(client_with_auth: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must login user correctly from OAuth
|
||||
"""
|
||||
oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth)
|
||||
oauth.get_oauth_username.return_value = "user"
|
||||
oauth.known_username.return_value = True
|
||||
oauth.enabled = False # lol
|
||||
remember_mock = mocker.patch("aiohttp_security.remember")
|
||||
|
||||
get_response = await client_with_auth.get("/user-api/v1/login", params={"code": "code"})
|
||||
|
||||
assert get_response.ok
|
||||
oauth.get_oauth_username.assert_called_with("code")
|
||||
oauth.known_username.assert_called_with("user")
|
||||
remember_mock.assert_called_once()
|
||||
|
||||
|
||||
async def test_get_unauthorized(client_with_auth: TestClient, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return unauthorized from OAuth
|
||||
"""
|
||||
oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth)
|
||||
oauth.known_username.return_value = False
|
||||
remember_mock = mocker.patch("aiohttp_security.remember")
|
||||
|
||||
get_response = await client_with_auth.get("/user-api/v1/login", params={"code": "code"})
|
||||
|
||||
assert get_response.status == 401
|
||||
remember_mock.assert_not_called()
|
||||
|
||||
|
||||
async def test_post(client_with_auth: TestClient, user: User, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must login user correctly
|
||||
@ -12,10 +78,10 @@ async def test_post(client_with_auth: TestClient, user: User, mocker: MockerFixt
|
||||
remember_mock = mocker.patch("aiohttp_security.remember")
|
||||
|
||||
post_response = await client_with_auth.post("/user-api/v1/login", json=payload)
|
||||
assert post_response.status == 200
|
||||
assert post_response.ok
|
||||
|
||||
post_response = await client_with_auth.post("/user-api/v1/login", data=payload)
|
||||
assert post_response.status == 200
|
||||
assert post_response.ok
|
||||
|
||||
remember_mock.assert_called()
|
||||
|
||||
@ -26,7 +92,7 @@ async def test_post_skip(client: TestClient, user: User) -> None:
|
||||
"""
|
||||
payload = {"username": user.username, "password": user.password}
|
||||
post_response = await client.post("/user-api/v1/login", json=payload)
|
||||
assert post_response.status == 200
|
||||
assert post_response.ok
|
||||
|
||||
|
||||
async def test_post_unauthorized(client_with_auth: TestClient, user: User, mocker: MockerFixture) -> None:
|
||||
|
@ -11,7 +11,7 @@ async def test_post(client_with_auth: TestClient, mocker: MockerFixture) -> None
|
||||
forget_mock = mocker.patch("aiohttp_security.forget")
|
||||
|
||||
post_response = await client_with_auth.post("/user-api/v1/logout")
|
||||
assert post_response.status == 200
|
||||
assert post_response.ok
|
||||
forget_mock.assert_called_once()
|
||||
|
||||
|
||||
@ -32,4 +32,4 @@ async def test_post_disabled(client: TestClient) -> None:
|
||||
must raise exception if auth is disabled
|
||||
"""
|
||||
post_response = await client.post("/user-api/v1/logout")
|
||||
assert post_response.status == 200
|
||||
assert post_response.ok
|
||||
|
Reference in New Issue
Block a user