Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add robot credentials auth flow #55

Merged
merged 3 commits into from
Aug 14, 2024
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
12 changes: 7 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Hub endpoints
HUB__CORE_BASE_URL=https://core.privateaim.net
HUB__STORAGE_BASE_URL=https://storage.privateaim.net
HUB__AUTH_BASE_URL=https://auth.privateaim.net
HUB__AUTH_USERNAME=foobar
HUB__AUTH_PASSWORD=sup3r_s3cr3t
HUB__CORE_BASE_URL=https://core.privateaim.dev
HUB__STORAGE_BASE_URL=https://storage.privateaim.dev
HUB__AUTH_BASE_URL=https://auth.privateaim.dev
# Auth credentials
HUB__AUTH_METHOD=robot
HUB__ROBOT_AUTH__ID=beep_boop
HUB__ROBOT_AUTH__SECRET=sup3r_s3cr3t
# MinIO instance
MINIO__ENDPOINT=localhost:9000
MINIO__ACCESS_KEY=admin
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ jobs:
MINIO__SECRET_KEY: ${{env.MINIO_ROOT_PASSWORD}}
MINIO__BUCKET: ${{env.MINIO_LOCAL_BUCKET_NAME}}
MINIO__ENDPOINT: localhost:9000
HUB__AUTH_USERNAME: ${{secrets.HUB_AUTH_USERNAME}}
HUB__AUTH_PASSWORD: ${{secrets.HUB_AUTH_PASSWORD}}
HUB__AUTH_METHOD: robot
HUB__ROBOT_AUTH__ID: ${{secrets.HUB_ROBOT_ID}}
HUB__ROBOT_AUTH__SECRET: ${{secrets.HUB_ROBOT_SECRET}}
# No tests against live infra are run here but these envs are set anyway to future-proof in case
# we ever end up testing against live infra. python-dotenv will not override existing envs so the prod
# URLs in the .env.example file shouldn't carry over here.
Expand Down
41 changes: 23 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ Docker, [pull a recent image from the GitHub container registry](https://github.
Pass in the configuration options using `-e` flags and forward port 8080 from your host to the container.

```
$ docker run --rm -p 8080:8080 -e HUB__AUTH_USERNAME=admin \
-e HUB__AUTH_PASSWORD=super_secret \
$ docker run --rm -p 8080:8080 -e HUB__ROBOT_AUTH__ID=beepboop \
-e HUB__ROBOT_AUTH__SECRET=super_secret \
-e HUB__AUTH_METHOD=robot \
-e MINIO__ENDPOINT=localhost:9000 \
-e MINIO__ACCESS_KEY=admin \
-e MINIO__SECRET_KEY=super_secret \
Expand All @@ -40,21 +41,24 @@ $ docker run --rm -p 8080:8080 -e HUB__AUTH_USERNAME=admin \

The following table shows all available configuration options.

| **Environment variable** | **Description** | **Default** | **Required** |
|----------------------------|----------------------------------------------------------|--------------------------------|:------------:|
| HUB__CORE_BASE_URL | Base URL for the FLAME Core API | https://core.privateaim.net | |
| HUB__STORAGE_BASE_URL | Base URL for the FLAME Storage API | https://storage.privateaim.net | |
| HUB__AUTH_BASE_URL | Base URL for the FLAME Auth API | https://auth.privateaim.net | |
| HUB__AUTH_USERNAME | Username to use for obtaining access tokens | | x |
| HUB__AUTH_PASSWORD | Password to use for obtaining access tokens | | x |
| MINIO__ENDPOINT | MinIO S3 API endpoint (without scheme) | | x |
| MINIO__ACCESS_KEY | Access key for interacting with MinIO S3 API | | x |
| MINIO__SECRET_KEY | Secret key for interacting with MinIO S3 API | | x |
| MINIO__BUCKET | Name of S3 bucket to store result files in | | x |
| MINIO__REGION | Region of S3 bucket to store result files in | us-east-1 | |
| MINIO__USE_SSL | Flag for en-/disabling encrypted traffic to MinIO S3 API | 0 | |
| OIDC__CERTS_URL | URL to OIDC-complaint JWKS endpoint for validating JWTs | | x |
| OIDC__CLIENT_ID_CLAIM_NAME | JWT claim to identify authenticated requests with | client_id | |
| **Environment variable** | **Description** | **Default** | **Required** |
|------------------------------|-------------------------------------------------------------------------------------|--------------------------------|:------------:|
| HUB__CORE_BASE_URL | Base URL for the FLAME Core API | https://core.privateaim.net | |
| HUB__STORAGE_BASE_URL | Base URL for the FLAME Storage API | https://storage.privateaim.net | |
| HUB__AUTH_BASE_URL | Base URL for the FLAME Auth API | https://auth.privateaim.net | |
| HUB__AUTH_METHOD | Authentication method to use for central FLAME services (`password` or `robot`) | | x |
| HUB__PASSWORD_AUTH__USERNAME | Username to use for obtaining access tokens using password auth scheme | | |
| HUB__PASSWORD_AUTH__PASSWORD | Password to use for obtaining access tokens using password auth scheme | | |
| HUB__ROBOT_AUTH__ID | Robot ID to use for obtaining access tokens using robot credentials auth scheme | | |
| HUB__ROBOT_AUTH__SECRET | Robot secret to use for obtaining access tokens using robot credentials auth scheme | | |
| MINIO__ENDPOINT | MinIO S3 API endpoint (without scheme) | | x |
| MINIO__ACCESS_KEY | Access key for interacting with MinIO S3 API | | x |
| MINIO__SECRET_KEY | Secret key for interacting with MinIO S3 API | | x |
| MINIO__BUCKET | Name of S3 bucket to store result files in | | x |
| MINIO__REGION | Region of S3 bucket to store result files in | us-east-1 | |
| MINIO__USE_SSL | Flag for en-/disabling encrypted traffic to MinIO S3 API | 0 | |
| OIDC__CERTS_URL | URL to OIDC-complaint JWKS endpoint for validating JWTs | | x |
| OIDC__CLIENT_ID_CLAIM_NAME | JWT claim to identify authenticated requests with | client_id | |

## Note on running tests

Expand All @@ -72,7 +76,8 @@ Some tests need to be run against live infrastructure.
Since a proper test instance is not available yet, these tests are hidden behind a flag and are not explicitly run in
CI.
To run these tests, append `-m live` to the command above.
Make sure to configure `HUB__AUTH_USERNAME` and `HUB__AUTH_PASSWORD` in your `.env.test` file before running tests.
Make sure to configure `HUB__ROBOT_AUTH__ID` and `HUB__ROBOT_AUTH__SECRET` in your `.env.test` file before running
tests.

# License

Expand Down
37 changes: 34 additions & 3 deletions project/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from pydantic import BaseModel, HttpUrl, ConfigDict
from enum import Enum

from pydantic import BaseModel, HttpUrl, ConfigDict, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing_extensions import Self


class MinioConnection(BaseModel):
Expand All @@ -24,15 +27,43 @@ class OIDCConfig(BaseModel):
model_config = ConfigDict(frozen=True)


class PasswordAuthConfig(BaseModel):
username: str
password: str


class RobotAuthConfig(BaseModel):
id: str
secret: str


class AuthMethod(str, Enum):
password = "password"
robot = "robot"


class HubConfig(BaseModel):
core_base_url: HttpUrl = "https://core.privateaim.net"
auth_base_url: HttpUrl = "https://auth.privateaim.net"
storage_base_url: HttpUrl = "https://storage.privateaim.net"
auth_username: str
auth_password: str

auth_method: AuthMethod

password_auth: PasswordAuthConfig | None = None
robot_auth: RobotAuthConfig | None = None

model_config = ConfigDict(frozen=True)

@model_validator(mode="after")
def check_auth_credentials_provided(self) -> Self:
if self.auth_method == AuthMethod.password and self.password_auth is None:
raise ValueError("password auth specified but no credentials provided")

if self.auth_method == AuthMethod.robot and self.robot_auth is None:
raise ValueError("robot auth specified but no credentials provided")

return self


class Settings(BaseSettings):
hub: HubConfig
Expand Down
34 changes: 25 additions & 9 deletions project/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@
from minio import Minio
from starlette import status

from project.config import Settings, MinioBucketConfig
from project.hub import FlamePasswordAuthClient, FlameCoreClient, FlameStorageClient
from project.config import Settings, MinioBucketConfig, AuthMethod
from project.hub import (
FlamePasswordAuthClient,
FlameCoreClient,
FlameStorageClient,
FlameRobotAuthClient,
BaseAuthClient,
)

security = HTTPBearer()
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -111,16 +117,26 @@ def get_client_id(


def get_auth_client(settings: Annotated[Settings, Depends(get_settings)]):
return FlamePasswordAuthClient(
settings.hub.auth_username,
settings.hub.auth_password,
base_url=str(settings.hub.auth_base_url),
)
if settings.hub.auth_method == AuthMethod.password:
return FlamePasswordAuthClient(
settings.hub.password_auth.username,
settings.hub.password_auth.password,
base_url=str(settings.hub.auth_base_url),
)

if settings.hub.auth_method == AuthMethod.robot:
return FlameRobotAuthClient(
settings.hub.robot_auth.id,
settings.hub.robot_auth.secret,
base_url=str(settings.hub.auth_base_url),
)

raise NotImplementedError(f"unknown auth method {settings.hub.auth_method}")


def get_core_client(
settings: Annotated[Settings, Depends(get_settings)],
auth_client: Annotated[FlamePasswordAuthClient, Depends(get_auth_client)],
auth_client: Annotated[BaseAuthClient, Depends(get_auth_client)],
):
return FlameCoreClient(
auth_client,
Expand All @@ -130,6 +146,6 @@ def get_core_client(

def get_storage_client(
settings: Annotated[Settings, Depends(get_settings)],
auth_client: Annotated[FlamePasswordAuthClient, Depends(get_auth_client)],
auth_client: Annotated[BaseAuthClient, Depends(get_auth_client)],
):
return FlameStorageClient(auth_client, base_url=str(settings.hub.storage_base_url))
Loading