Skip to content

Commit

Permalink
rewire api calls
Browse files Browse the repository at this point in the history
  • Loading branch information
witlox committed Dec 10, 2024
1 parent bc4714d commit d450e93
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 26 deletions.
6 changes: 5 additions & 1 deletion horao/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,18 @@ def init(authorization: Optional[AuthenticationBackend] = None) -> Starlette:
if cors == "*":
logger.warning("CORS is set to *")
routes = [
Route("/ping", endpoint=horao.api.alive_controller.is_alive, methods=["GET"]),
Route("/login", endpoint=horao.api.authenticate.login, methods=["POST"]),
Route("/logout", endpoint=horao.api.authenticate.logout, methods=["POST"]),
Route(
"/synchronize",
endpoint=horao.api.synchronization.synchronize,
methods=["POST"],
),
Route(
"/reservations",
endpoint=horao.api.user_actions.get_reservations,
methods=["GET"],
),
Route("/openapi.json", endpoint=openapi_schema, include_in_schema=False),
]
if os.getenv("UI", "False") == "True":
Expand Down
2 changes: 1 addition & 1 deletion horao/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-#
"""API definitions."""
from .alive_controller import is_alive
from .authenticate import login, logout
from .synchronization import synchronize
from .user_actions import get_reservations
2 changes: 2 additions & 0 deletions horao/api/authenticate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os

from authlib.integrations.starlette_client import OAuth # type: ignore
from starlette.authentication import requires
from starlette.requests import Request
from starlette.responses import RedirectResponse

Expand Down Expand Up @@ -38,6 +39,7 @@ async def login(request: Request):
return await client.authorize_redirect(request, redirect_uri)


@requires("authenticated")
async def logout(request: Request):
"""
responses:
Expand Down
18 changes: 1 addition & 17 deletions horao/api/synchronization.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,7 @@ async def synchronize(request: Request) -> JSONResponse:
return JSONResponse(status_code=400, content={"error": "Error parsing request"})
try:
session = init_session()
for k, v in logical_infrastructure.infrastructure.items():
local_dc = await session.async_load(k.name)
if not local_dc:
await session.async_save(k.name, k)
else:
local_dc.merge(k)
local_dc_content = await session.async_load(f"{k.name}.content")
if not local_dc_content:
await session.async_save(f"{k.name}.content", v)
else:
local_dc_content.merge(v)
if logical_infrastructure.claims:
for k, v in logical_infrastructure.claims.items():
await session.async_save(k.name, k)
if logical_infrastructure.constraints:
for k, v in logical_infrastructure.contraints.items():
await session.async_save(k.name, k)
session.save_logical_infrastructure(logical_infrastructure)
except Exception as e:
logging.error(f"Error synchronizing: {e}")
if os.getenv("DEBUG", "False") == "True":
Expand Down
54 changes: 53 additions & 1 deletion horao/persistance/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import logging
from typing import Any, Dict, Optional

from redis.asyncio import Redis as RedisAIO
from redis import Redis as Redis
from redis.asyncio import Redis as RedisAIO

from horao.conceptual.decorators import instrument_class_function
from horao.logical.infrastructure import LogicalInfrastructure
from horao.persistance.serialize import HoraoDecoder, HoraoEncoder


Expand Down Expand Up @@ -52,6 +53,57 @@ async def items(self) -> Dict[str, Any] | Any:
return await self.redis_aio.items()
return self.memory.items()

async def load_logical_infrastructure(self) -> LogicalInfrastructure:
"""
Load the logical infrastructure from the store
:return: LogicalInfrastructure
"""
infrastructure = {}
claims = {}
constraints = {}
for key in await self.keys():
if key.startswith("datacenter-"):
dc = await self.async_load(key)
content = await self.async_load(f"datacenter-{key}.content")
infrastructure[dc] = content
elif key.startswith("claim-"):
claim = await self.async_load(key)
content = await self.async_load(f"claim-{key}.content")
claims[claim] = content
elif key.startswith("constraint-"):
constraint = await self.async_load(key)
content = await self.async_load(f"constraint-{key}.content")
constraints[constraint] = content
return LogicalInfrastructure(infrastructure, constraints, claims)

async def save_logical_infrastructure(
self, logical_infrastructure: LogicalInfrastructure
) -> None:
"""
Save the logical infrastructure to the store
:param logical_infrastructure: infrastructure to save
:return: None
"""
for k, v in logical_infrastructure.infrastructure.items():
local_dc = await self.async_load(k.name)
if not local_dc:
await self.async_save(f"datacenter-{k.name}", k)
else:
local_dc.merge(k)
local_dc_content = await self.async_load(f"datacenter-{k.name}.content")
if not local_dc_content:
await self.async_save(f"datacenter-{k.name}.content", v)
else:
local_dc_content.merge(v)
if logical_infrastructure.claims:
for k, v in logical_infrastructure.claims.items(): # type: ignore
await self.async_save(f"claim-{k.name}", k)
await self.async_save(f"claim-{k.name}.content", v)
if logical_infrastructure.constraints:
for k, v in logical_infrastructure.constraints.items(): # type: ignore
await self.async_save(f"constraint-{k.name}", k)
await self.async_save(f"constraint-{k.name}.content", v)

@instrument_class_function(name="async_load", level=logging.DEBUG)
async def async_load(self, key: str) -> Any | None:
"""
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ redis = "^5.1.1"
authlib = "^1.3.2"

[tool.poetry.scripts]
horao = 'horao.__main__:main'
horao = 'horao.main:main'

[tool.poetry.group.dev.dependencies]
black = "^24.10.0"
Expand Down
16 changes: 13 additions & 3 deletions tests/basic_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@
from starlette.requests import HTTPException, Request # type: ignore
from starlette.responses import JSONResponse # type: ignore

from horao.auth.roles import Administrator, TenantController, User
from horao.conceptual.tenant import Tenant

basic_auth_structure = {
"netadm": {"password": "secret", "role": "network.admin"},
"sysadm": {"password": "secret", "role": "system.admin"},
"read_usr": {"password": "secret1", "role": User("read_usr")},
"tenant": {
"password": "secret2",
"role": TenantController("tenant", [Tenant("test", "owner")]),
},
"admin": {"password": "secret3", "role": Administrator("admin")},
}


Expand All @@ -45,7 +52,10 @@ async def authenticate(self, conn):
or basic_auth_structure[username]["password"] != password
):
raise AuthenticationError(f"access not allowed for {username}")
return AuthCredentials(["authenticated"]), SimpleUser(username)
return (
AuthCredentials(["authenticated"]),
basic_auth_structure[username]["role"],
)


def basic_auth(username, password) -> str:
Expand Down
4 changes: 2 additions & 2 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def test_ping_service_unauthorized():
os.environ["TELEMETRY"] = "OFF"
ia = init(BasicAuthBackend())
with TestClient(ia) as client:
lg = client.get("/ping")
lg = client.get("/reservations")
assert 403 == lg.status_code


Expand All @@ -25,7 +25,7 @@ def test_ping_service_authorized():
ia = init(BasicAuthBackend())
with TestClient(ia) as client:
lg = client.get(
"/ping", headers={"Authorization": basic_auth("netadm", "secret")}
"/reservations", headers={"Authorization": basic_auth("tenant", "secret2")}
)
assert 200 == lg.status_code

Expand Down

0 comments on commit d450e93

Please sign in to comment.