From 4939e5d2fb39a34570457bd8ccd74ae97ae44c82 Mon Sep 17 00:00:00 2001 From: Mohammad Torkashvand <mohammad.torkashvand@geant.org> Date: Wed, 24 Jan 2024 15:26:21 +0100 Subject: [PATCH] Add API key authentication for routes --- gso/api/v1/subscriptions.py | 4 ++-- gso/auth/api_key_auth.py | 20 ++++++++++++++++++++ gso/oss-params-example.json | 4 ++++ gso/settings.py | 1 + test/api/test_subscriptions.py | 18 ++++++++++++++++-- test/conftest.py | 4 ++++ 6 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 gso/auth/api_key_auth.py diff --git a/gso/api/v1/subscriptions.py b/gso/api/v1/subscriptions.py index 24c9307f..9cc6075a 100644 --- a/gso/api/v1/subscriptions.py +++ b/gso/api/v1/subscriptions.py @@ -8,13 +8,13 @@ from orchestrator.domain import SubscriptionModel from orchestrator.schemas import SubscriptionDomainModelSchema from orchestrator.services.subscriptions import build_extended_domain_model -from gso.auth.security import opa_security_default +from gso.auth.api_key_auth import get_api_key from gso.services.subscriptions import get_active_router_subscriptions router = APIRouter( prefix="/subscriptions", tags=["Subscriptions"], - dependencies=[Depends(opa_security_default)], + dependencies=[Depends(get_api_key)], ) diff --git a/gso/auth/api_key_auth.py b/gso/auth/api_key_auth.py new file mode 100644 index 00000000..c3fb3629 --- /dev/null +++ b/gso/auth/api_key_auth.py @@ -0,0 +1,20 @@ +"""Manage API key validation for FastAPI routes.""" + +from fastapi import Depends, HTTPException, status +from fastapi.security.api_key import APIKeyHeader + +from gso.settings import load_oss_params + +API_KEY_NAME = "access_token" +api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=True) + + +async def get_api_key(api_key: str = Depends(api_key_header)) -> str: + """Validate the provided API key against known third-party keys and returns it if valid, else raises HTTP 403.""" + settings = load_oss_params() + + # TODO: This is a simulated database of API keys which should be replace with a real one + if api_key in settings.THIRD_PARTY_API_KEYS: + return api_key + + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid API Key") diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json index fa8616e4..7af29f35 100644 --- a/gso/oss-params-example.json +++ b/gso/oss-params-example.json @@ -73,5 +73,9 @@ "broker_url": "redis://localhost:6379/0", "result_backend": "rpc://localhost:6379/0", "result_expires": 3600 + }, + "THIRD_PARTY_API_KEYS": { + "REALLY_random_AND_secure_T0keN": "Ansible Dynamic Inventory Generator", + "Another_REALLY_random_AND_secure_T0keN": "Application 2" } } diff --git a/gso/settings.py b/gso/settings.py index f05632ed..ca0da591 100644 --- a/gso/settings.py +++ b/gso/settings.py @@ -161,6 +161,7 @@ class OSSParams(BaseSettings): MONITORING: MonitoringParams PROVISIONING_PROXY: ProvisioningProxyParams CELERY: CeleryParams + THIRD_PARTY_API_KEYS: dict[str, str] def load_oss_params() -> OSSParams: diff --git a/test/api/test_subscriptions.py b/test/api/test_subscriptions.py index d56d2d58..ebdeefef 100644 --- a/test/api/test_subscriptions.py +++ b/test/api/test_subscriptions.py @@ -3,14 +3,28 @@ from orchestrator.types import SubscriptionLifecycle ROUTER_SUBSCRIPTION_ENDPOINT = "/api/v1/subscriptions/routers" -def test_router_subscriptions_endpoint(test_client, nokia_router_subscription_factory): +def test_router_subscriptions_endpoint_with_valid_api_key(test_client, nokia_router_subscription_factory): nokia_router_subscription_factory() nokia_router_subscription_factory() nokia_router_subscription_factory() nokia_router_subscription_factory(status=SubscriptionLifecycle.TERMINATED) nokia_router_subscription_factory(status=SubscriptionLifecycle.INITIAL) - response = test_client.get(ROUTER_SUBSCRIPTION_ENDPOINT) + response = test_client.get(ROUTER_SUBSCRIPTION_ENDPOINT, headers={"access_token": "REALY_random_AND_3cure_T0keN"}) assert response.status_code == 200 assert len(response.json()) == 3 + + +def test_router_subscriptions_endpoint_with_invalid_api_key(test_client, nokia_router_subscription_factory): + response = test_client.get(ROUTER_SUBSCRIPTION_ENDPOINT, headers={"access_token": "fake_invalid_api_key"}) + + assert response.status_code == 403 + assert response.json() == {"detail": "Invalid API Key"} + + +def test_router_subscriptions_endpoint_without_api_key(test_client, nokia_router_subscription_factory): + response = test_client.get(ROUTER_SUBSCRIPTION_ENDPOINT) + + assert response.status_code == 403 + assert response.json() == {"detail": "Not authenticated"} diff --git a/test/conftest.py b/test/conftest.py index edb1dffb..fac1c168 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -204,6 +204,10 @@ def configuration_data() -> dict: "result_backend": "rpc://localhost:6379/0", "result_expires": 3600, }, + "THIRD_PARTY_API_KEYS": { + "REALY_random_AND_3cure_T0keN": "LSO", + "another_REALY_random_AND_3cure_T0keN": "Application 2", + }, } -- GitLab