Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ATTRIBUTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9202,12 +9202,12 @@ SOFTWARE.

```

## pytest (9.0.2) - UNKNOWN
## pytest (9.0.3) - UNKNOWN

pytest: simple powerful testing with Python

* URL: https://docs.pytest.org/en/latest/
* Author(s): Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin, Others (See AUTHORS)
* Author(s): Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Freya Bruhin, Others (See AUTHORS)

### License Text

Expand Down
4 changes: 2 additions & 2 deletions src/aignostics_foundry_core/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This file provides an overview of all modules in `aignostics_foundry_core`, thei
| **models** | Shared output format enum | `OutputFormat` StrEnum with `YAML` and `JSON` values for use in CLI and API responses |
| **process** | Current process introspection | `ProcessInfo`, `ParentProcessInfo` Pydantic models and `get_process_info()` for runtime process metadata; `SUBPROCESS_CREATION_FLAGS` for subprocess creation |
| **api.exceptions** | API exception hierarchy and FastAPI handlers | `ApiException` (500), `NotFoundException` (404), `AccessDeniedException` (401); `api_exception_handler`, `unhandled_exception_handler`, `validation_exception_handler` for FastAPI registration |
| **api.auth** | Auth0 authentication FastAPI dependencies | `AuthSettings` (env-prefix configurable), `UnauthenticatedError`, `ForbiddenError` (403); `get_auth_client`, `get_user`, `require_authenticated`, `require_admin`, `require_internal`, `require_internal_admin` FastAPI dependencies; Auth0 cookie security schemes |
| **api.auth** | Auth0 authentication FastAPI dependencies | `AuthSettings` (env-prefix and env files derived from `ctx.env_prefix`/`ctx.env_file`), `UnauthenticatedError`, `ForbiddenError` (403); `get_auth_client`, `get_user`, `require_authenticated`, `require_admin`, `require_internal`, `require_internal_admin` FastAPI dependencies; Auth0 cookie security schemes |
| **api.core** | Versioned API router and FastAPI factory | `VersionedAPIRouter` (tracks all created instances), `API_TAG_*` constants, `create_public/authenticated/admin/internal/internal_admin_router` factories, `build_api_metadata`, `build_versioned_api_tags`, `build_root_api_tags`, `get_versioned_api_instances(versions, build_metadata=None, *, context=None)`, `init_api()` |
| **api** | Consolidated API sub-package | Re-exports all public symbols from `api.exceptions`, `api.auth`, and `api.core`; import any API symbol directly from `aignostics_foundry_core.api` |
| **log** | Configurable loguru logging initialisation | `logging_initialize(filter_func=None, *, context=None)`, `LogSettings` (env-prefix configurable), `InterceptHandler` for stdlib-to-loguru bridging |
Expand Down Expand Up @@ -112,7 +112,7 @@ This file provides an overview of all modules in `aignostics_foundry_core`, thei

- **Purpose**: Provides Auth0 cookie-based session authentication dependencies for FastAPI routes. All project-specific settings (org ID, role claim) are loaded from `AuthSettings` whose env prefix is configurable at instantiation.
- **Key Features**:
- `AuthSettings(OpaqueSettings)` — uses the active FoundryContext.env_prefix to derive the env prefix (`{ctx.env_prefix}AUTH_`). Fields: `internal_org_id` (required `str`; identifies the internal organization), `auth0_role_claim` (required `str`; JWT claim name for role). Both fields are mandatory — no defaults are provided.
- `AuthSettings(OpaqueSettings)` — uses the active FoundryContext to derive both the env prefix (`{ctx.env_prefix}AUTH_`) and the env file list (`ctx.env_file`). Fields: `internal_org_id` (required `str`; identifies the internal organization), `auth0_role_claim` (required `str`; JWT claim name for role). Both fields are mandatory — no defaults are provided.
- `UnauthenticatedError(Exception)` — raised when a user session is missing or invalid
- `ForbiddenError(ApiException)` — `status_code = 403`; raised when user lacks required role or org membership
- `get_auth_client(request)` — retrieves `AuthClient` from `request.app.state.auth_client`; raises `RuntimeError` if not configured
Expand Down
14 changes: 8 additions & 6 deletions src/aignostics_foundry_core/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@


class AuthSettings(OpaqueSettings):
"""Auth settings whose env prefix is derived from the active FoundryContext.
"""Auth settings whose env prefix and env files are derived from the active FoundryContext.

The effective prefix is ``{FoundryContext.env_prefix}AUTH_``, resolved at
instantiation time via :func:`aignostics_foundry_core.foundry.get_context`.
The effective prefix is ``{FoundryContext.env_prefix}AUTH_`` and the env files are
``FoundryContext.env_file``, both resolved at instantiation time via
:func:`aignostics_foundry_core.foundry.get_context`.

Both ``internal_org_id`` and ``auth0_role_claim`` are required — they must be
provided via the corresponding environment variables (no defaults).
provided via environment variables or ``.env`` files (no defaults).
"""

model_config = SettingsConfigDict(extra="ignore")
Expand All @@ -46,8 +47,9 @@ class AuthSettings(OpaqueSettings):
auth0_role_claim: str

def __init__(self, **kwargs: Any) -> None: # noqa: ANN401
"""Initialise settings, deriving env_prefix from the active FoundryContext."""
super().__init__(_env_prefix=f"{get_context().env_prefix}AUTH_", **kwargs) # pyright: ignore[reportCallIssue]
"""Initialise settings, deriving env_prefix and env files from the active FoundryContext."""
ctx = get_context()
super().__init__(_env_prefix=f"{ctx.env_prefix}AUTH_", _env_file=ctx.env_file, **kwargs) # pyright: ignore[reportCallIssue]


class UnauthenticatedError(Exception):
Expand Down
27 changes: 27 additions & 0 deletions tests/aignostics_foundry_core/api/auth_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import time
from collections.abc import Generator
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock

import pytest
Expand All @@ -19,7 +20,9 @@
require_internal,
require_internal_admin,
)
from aignostics_foundry_core.foundry import set_context
from tests.aignostics_foundry_core.api import AUTH0_ROLE_CLAIM_VAR_NAME, INTERNAL_ORG_ID_VAR_NAME
from tests.conftest import make_context

_INTERNAL_ORG_ID = "org_internal_123"
_OTHER_ORG_ID = "org_other_456"
Expand Down Expand Up @@ -112,6 +115,30 @@ def test_auth_settings_raises_when_required_fields_absent(self, monkeypatch: pyt
AuthSettings()


@pytest.mark.integration
class TestAuthSettingsEnvFile:
"""Tests for AuthSettings env file loading."""

def test_auth_settings_reads_fields_from_env_file_via_context(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""AuthSettings reads required fields from a .env file listed in the active FoundryContext."""
env_file = tmp_path / ".env"
env_file.write_text(
f"{INTERNAL_ORG_ID_VAR_NAME}=org_from_env_file\n{AUTH0_ROLE_CLAIM_VAR_NAME}=claim_from_env_file\n"
)

set_context(make_context(env_file=[env_file]))

monkeypatch.delenv(INTERNAL_ORG_ID_VAR_NAME, raising=False)
monkeypatch.delenv(AUTH0_ROLE_CLAIM_VAR_NAME, raising=False)

settings = AuthSettings()

assert settings.internal_org_id == "org_from_env_file"
assert settings.auth0_role_claim == "claim_from_env_file"


@pytest.mark.integration
class TestGetUser:
"""Tests for get_user FastAPI dependency."""
Expand Down
Loading