Skip to content

Commit

Permalink
Move routers under /api
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisburr committed Oct 6, 2023
1 parent f81ab5f commit 90cb06e
Show file tree
Hide file tree
Showing 14 changed files with 109 additions and 107 deletions.
2 changes: 1 addition & 1 deletion src/diracx/client/aio/operations/_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def build_token_request(**kwargs: Any) -> HttpRequest:
accept = _headers.pop("Accept", "application/json")

# Construct URL
_url = "/auth/token"
_url = "/api/auth/token"
_url: str = _format_url_section(_url)

_headers["Accept"] = _SERIALIZER.header("accept", accept, "str")
Expand Down
2 changes: 1 addition & 1 deletion src/diracx/client/operations/_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def build_token_request(vo: str, **kwargs: Any) -> HttpRequest:
accept = _headers.pop("Accept", "application/json")

# Construct URL
_url = "/auth/token"
_url = "/api/auth/token"
_url: str = _format_url_section(_url)

_headers["Accept"] = _SERIALIZER.header("accept", accept, "str")
Expand Down
4 changes: 3 additions & 1 deletion src/diracx/routers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,11 @@ def create_app_inner(
dependencies = []
if isinstance(router, DiracxRouter) and router.diracx_require_auth:
dependencies.append(Depends(verify_dirac_access_token))
# Most routers are mounted under /api/<system_name>
path_root = getattr(router, "diracx_path_root", "/api")
app.include_router(
router,
prefix=f"/{system_name}",
prefix=f"{path_root}/{system_name}",
tags=[system_name],
dependencies=dependencies,
)
Expand Down
2 changes: 1 addition & 1 deletion src/diracx/routers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
)
from .fastapi_classes import DiracxRouter

oidc_scheme = OpenIdConnect(openIdConnectUrl="/api/.well-known/openid-configuration")
oidc_scheme = OpenIdConnect(openIdConnectUrl="/.well-known/openid-configuration")


@add_settings_annotation
Expand Down
5 changes: 4 additions & 1 deletion src/diracx/routers/fastapi_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ async def lifespan(app: DiracFastAPI):
generate_unique_id_function=lambda route: f"{route.tags[0]}_{route.name}",
title="Dirac",
lifespan=lifespan,
root_path="/api",
openapi_url="/api/openapi.json",
docs_url="/api/docs",
)
# FIXME: when autorest will support 3.1.0
# From 0.99.0, FastAPI is using openapi 3.1.0 by default
Expand All @@ -60,6 +61,8 @@ def __init__(
*,
dependencies=None,
require_auth: bool = True,
path_root: str = "/api",
):
super().__init__(dependencies=dependencies)
self.diracx_require_auth = require_auth
self.diracx_path_root = path_root
2 changes: 1 addition & 1 deletion src/diracx/routers/well_known.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .dependencies import Config
from .fastapi_classes import DiracxRouter

router = DiracxRouter(require_auth=False)
router = DiracxRouter(require_auth=False, path_root="")


@router.get("/openid-configuration")
Expand Down
2 changes: 1 addition & 1 deletion tests/client/test_regenerate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def test_regenerate_client(test_client, tmp_path):
WARNING: This test will modify the source code of the client!
"""
r = test_client.get("/openapi.json")
r = test_client.get("/api/openapi.json")
r.raise_for_status()

openapi_spec = tmp_path / "openapi.json"
Expand Down
7 changes: 1 addition & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,6 @@ async def create_db_schemas(app=app):
# already been ran
app.lifetime_functions.append(create_db_schemas)

# For test purpose, we remove root_path as it can generate issues.
# TestClient sends HTTP requests directly to the DiracFastAPI,
# bypassing the proxy server.
app.root_path = ""

yield app


Expand Down Expand Up @@ -301,7 +296,7 @@ def cli_env(monkeypatch, tmp_path, demo_urls):
diracx_url = demo_urls["diracx"]

# Ensure the demo is working
r = requests.get(f"{diracx_url}/openapi.json")
r = requests.get(f"{diracx_url}/api/openapi.json")
r.raise_for_status()
assert r.json()["info"]["title"] == "Dirac"

Expand Down
18 changes: 9 additions & 9 deletions tests/routers/auth/test_legacy_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ def legacy_credentials(monkeypatch):

async def test_valid(test_client, legacy_credentials):
r = test_client.get(
"/auth/legacy-exchange",
"/api/auth/legacy-exchange",
params={"preferred_username": "chaen", "scope": "vo:lhcb group:lhcb_user"},
headers=legacy_credentials,
)
assert r.status_code == 200
access_token = r.json()["access_token"]

r = test_client.get(
"/auth/userinfo", headers={"Authorization": f"Bearer {access_token}"}
"/api/auth/userinfo", headers={"Authorization": f"Bearer {access_token}"}
)
assert r.status_code == 200
user_info = r.json()
Expand All @@ -37,7 +37,7 @@ async def test_valid(test_client, legacy_credentials):

async def test_disabled(test_client):
r = test_client.get(
"/auth/legacy-exchange",
"/api/auth/legacy-exchange",
params={"preferred_username": "chaen", "scope": "vo:lhcb group:lhcb_user"},
headers={"Authorization": "Bearer diracx:legacy:ChangeME"},
)
Expand All @@ -46,7 +46,7 @@ async def test_disabled(test_client):

async def test_no_credentials(test_client, legacy_credentials):
r = test_client.get(
"/auth/legacy-exchange",
"/api/auth/legacy-exchange",
params={"preferred_username": "chaen", "scope": "vo:lhcb group:lhcb_user"},
headers={"Authorization": "Bearer invalid"},
)
Expand All @@ -56,7 +56,7 @@ async def test_no_credentials(test_client, legacy_credentials):

async def test_invalid_credentials(test_client, legacy_credentials):
r = test_client.get(
"/auth/legacy-exchange",
"/api/auth/legacy-exchange",
params={"preferred_username": "chaen", "scope": "vo:lhcb group:lhcb_user"},
headers={"Authorization": "Bearer invalid"},
)
Expand All @@ -66,7 +66,7 @@ async def test_invalid_credentials(test_client, legacy_credentials):

async def test_wrong_credentials(test_client, legacy_credentials):
r = test_client.get(
"/auth/legacy-exchange",
"/api/auth/legacy-exchange",
params={"preferred_username": "chaen", "scope": "vo:lhcb group:lhcb_user"},
headers={"Authorization": "Bearer diracx:legacy:ChangeME"},
)
Expand All @@ -76,7 +76,7 @@ async def test_wrong_credentials(test_client, legacy_credentials):

async def test_unknown_vo(test_client, legacy_credentials):
r = test_client.get(
"/auth/legacy-exchange",
"/api/auth/legacy-exchange",
params={"preferred_username": "chaen", "scope": "vo:unknown group:lhcb_user"},
headers=legacy_credentials,
)
Expand All @@ -86,7 +86,7 @@ async def test_unknown_vo(test_client, legacy_credentials):

async def test_unknown_group(test_client, legacy_credentials):
r = test_client.get(
"/auth/legacy-exchange",
"/api/auth/legacy-exchange",
params={"preferred_username": "chaen", "scope": "vo:lhcb group:unknown"},
headers=legacy_credentials,
)
Expand All @@ -96,7 +96,7 @@ async def test_unknown_group(test_client, legacy_credentials):

async def test_unknown_user(test_client, legacy_credentials):
r = test_client.get(
"/auth/legacy-exchange",
"/api/auth/legacy-exchange",
params={"preferred_username": "unknown", "scope": "vo:lhcb group:lhcb_user"},
headers=legacy_credentials,
)
Expand Down
44 changes: 22 additions & 22 deletions tests/routers/auth/test_standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ async def test_authorization_flow(test_client, auth_httpx_mock: HTTPXMock):
)

r = test_client.get(
"/auth/authorize",
"/api/auth/authorize",
params={
"response_type": "code",
"code_challenge": code_challenge,
Expand Down Expand Up @@ -158,15 +158,15 @@ async def test_authorization_flow(test_client, auth_httpx_mock: HTTPXMock):
)

# Ensure the token request doesn't work a second time
r = test_client.post("/auth/token", data=request_data)
r = test_client.post("/api/auth/token", data=request_data)
assert r.status_code == 400, r.json()
assert r.json()["detail"] == "Code was already used"


async def test_device_flow(test_client, auth_httpx_mock: HTTPXMock):
# Initiate the device flow (would normally be done from CLI)
r = test_client.post(
"/auth/device",
"/api/auth/device",
params={
"client_id": DIRAC_CLIENT_ID,
"audience": "Dirac server",
Expand All @@ -183,7 +183,7 @@ async def test_device_flow(test_client, auth_httpx_mock: HTTPXMock):

# Check that token requests return "authorization_pending"
r = test_client.post(
"/auth/token",
"/api/auth/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": data["device_code"],
Expand Down Expand Up @@ -234,7 +234,7 @@ async def test_device_flow(test_client, auth_httpx_mock: HTTPXMock):
)

# Ensure the token request doesn't work a second time
r = test_client.post("/auth/token", data=request_data)
r = test_client.post("/api/auth/token", data=request_data)
assert r.status_code == 400, r.json()
assert r.json()["detail"] == "Code was already used"

Expand Down Expand Up @@ -275,7 +275,7 @@ async def test_refresh_token_rotation(test_client, auth_httpx_mock: HTTPXMock):

# User uses the initial refresh token to get a new one
# The server should detect the breach and revoke every token bound to User
r = test_client.post("/auth/token", data=request_data)
r = test_client.post("/api/auth/token", data=request_data)
data = r.json()
assert r.status_code == 400, data
assert (
Expand All @@ -287,7 +287,7 @@ async def test_refresh_token_rotation(test_client, auth_httpx_mock: HTTPXMock):
# In theory, new_refresh_token has not been revoked since it is the latest one
# But because a breach was detected, it should also be revoked
request_data["refresh_token"] = new_refresh_token
r = test_client.post("/auth/token", data=request_data)
r = test_client.post("/api/auth/token", data=request_data)
data = r.json()
assert r.status_code == 400, data
assert (
Expand Down Expand Up @@ -326,7 +326,7 @@ async def test_refresh_token_expired(

# Try to get a new access token using the invalid refresh token
# The server should detect that it is not encoded properly
r = test_client.post("/auth/token", data=request_data)
r = test_client.post("/api/auth/token", data=request_data)
data = r.json()
assert r.status_code == 401, data
assert data["detail"] == "Invalid JWT: expired_token: The token is expired"
Expand Down Expand Up @@ -372,7 +372,7 @@ async def test_refresh_token_invalid(test_client, auth_httpx_mock: HTTPXMock):

# Try to get a new access token using the invalid refresh token
# The server should detect that it is not encoded properly
r = test_client.post("/auth/token", data=request_data)
r = test_client.post("/api/auth/token", data=request_data)
data = r.json()
assert r.status_code == 401, data
assert data["detail"] == "Invalid JWT: bad_signature: "
Expand All @@ -392,7 +392,7 @@ async def test_list_refresh_tokens(test_client, auth_httpx_mock: HTTPXMock):

# Normal user lists his/her refresh tokens
r = test_client.get(
"/auth/refresh-tokens",
"/api/auth/refresh-tokens",
headers={"Authorization": f"Bearer {normal_user_access_token}"},
)
data = r.json()
Expand All @@ -406,7 +406,7 @@ async def test_list_refresh_tokens(test_client, auth_httpx_mock: HTTPXMock):

# Token manager lists refresh tokens: should get his/her own and the normal user's one
r = test_client.get(
"/auth/refresh-tokens",
"/api/auth/refresh-tokens",
headers={"Authorization": f"Bearer {token_manager_access_token}"},
)
data = r.json()
Expand All @@ -425,7 +425,7 @@ async def test_list_refresh_tokens(test_client, auth_httpx_mock: HTTPXMock):

# Normal user lists his/her refresh tokens again
r = test_client.get(
"/auth/refresh-tokens",
"/api/auth/refresh-tokens",
headers={"Authorization": f"Bearer {response_data['access_token']}"},
)
data = r.json()
Expand All @@ -434,7 +434,7 @@ async def test_list_refresh_tokens(test_client, auth_httpx_mock: HTTPXMock):

# Token manager lists refresh tokens: should get his/her own and the normal user's one
r = test_client.get(
"/auth/refresh-tokens",
"/api/auth/refresh-tokens",
headers={"Authorization": f"Bearer {token_manager_access_token}"},
)
data = r.json()
Expand Down Expand Up @@ -471,31 +471,31 @@ async def test_revoke_refresh_tokens_normal_user(

# Normal user tries to delete a random and non-existing RT: should raise an error
r = test_client.delete(
"/auth/refresh-tokens/does-not-exists",
"/api/auth/refresh-tokens/does-not-exists",
headers={"Authorization": f"Bearer {normal_user_access_token}"},
)
data = r.json()
assert r.status_code == 400, data

# Normal user tries to delete token manager's RT: should not work
r = test_client.delete(
f"/auth/refresh-tokens/{token_manager_refresh_payload['jti']}",
f"/api/auth/refresh-tokens/{token_manager_refresh_payload['jti']}",
headers={"Authorization": f"Bearer {normal_user_access_token}"},
)
data = r.json()
assert r.status_code == 401, data

# Normal user tries to delete his/her RT: should work
r = test_client.delete(
f"/auth/refresh-tokens/{normal_user_refresh_payload['jti']}",
f"/api/auth/refresh-tokens/{normal_user_refresh_payload['jti']}",
headers={"Authorization": f"Bearer {normal_user_access_token}"},
)
data = r.json()
assert r.status_code == 200, data

# Normal user tries to delete his/her RT again: should work
r = test_client.delete(
f"/auth/refresh-tokens/{normal_user_refresh_payload['jti']}",
f"/api/auth/refresh-tokens/{normal_user_refresh_payload['jti']}",
headers={"Authorization": f"Bearer {normal_user_access_token}"},
)
data = r.json()
Expand Down Expand Up @@ -530,15 +530,15 @@ async def test_revoke_refresh_tokens_token_manager(

# Token manager tries to delete token manager's RT: should work
r = test_client.delete(
f"/auth/refresh-tokens/{normal_user_refresh_payload['jti']}",
f"/api/auth/refresh-tokens/{normal_user_refresh_payload['jti']}",
headers={"Authorization": f"Bearer {token_manager_access_token}"},
)
data = r.json()
assert r.status_code == 200, data

# Token manager tries to delete his/her RT: should work
r = test_client.delete(
f"/auth/refresh-tokens/{token_manager_refresh_payload['jti']}",
f"/api/auth/refresh-tokens/{token_manager_refresh_payload['jti']}",
headers={"Authorization": f"Bearer {token_manager_access_token}"},
)
data = r.json()
Expand All @@ -551,7 +551,7 @@ def _get_tokens(
"""Get a pair of tokens (access, refresh) through a device flow code"""
# User Initiates a device flow (would normally be done from CLI)
r = test_client.post(
"/auth/device",
"/api/auth/device",
params={
"client_id": DIRAC_CLIENT_ID,
"audience": "Dirac server",
Expand All @@ -572,7 +572,7 @@ def _get_tokens(

# User gets a TokenResponse: should contain an access and a refresh tokens
r = test_client.post(
"/auth/token",
"/api/auth/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": data["device_code"],
Expand All @@ -585,7 +585,7 @@ def _get_tokens(
def _get_and_check_token_response(test_client, request_data):
"""Get a token and check that mandatory fields are present"""
# Check that token request now works
r = test_client.post("/auth/token", data=request_data)
r = test_client.post("/api/auth/token", data=request_data)
assert r.status_code == 200, r.json()
response_data = r.json()
assert response_data["access_token"]
Expand Down
Loading

0 comments on commit 90cb06e

Please sign in to comment.