From 42663ad50cce4b8009d0b286c420d80311af4e0b Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Mon, 20 Jul 2026 10:30:09 -0700 Subject: [PATCH 1/7] Remove client secret file from runner Remove client secret file from runner --- .dockerignore | 1 - client_secrets.json | 6 ------ 2 files changed, 7 deletions(-) delete mode 100644 client_secrets.json diff --git a/.dockerignore b/.dockerignore index cf1f6d39..d19c1058 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,4 +8,3 @@ !pyproject.toml !LICENSE !README.md -!client_secrets.json diff --git a/client_secrets.json b/client_secrets.json deleted file mode 100644 index b21ccfc0..00000000 --- a/client_secrets.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "server_metadata_url": "https://auth.dataone.org/realms/dataone/.well-known/openid-configuration", - "client_id": "d1-confidential", - "client_secret": "", - "redirect_uris": ["https://ogdc.test.dataone.org/api/authorize"] -} From 8fcb6eb91ad157d3ad8ac0f974ad240f52d57108 Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Mon, 20 Jul 2026 10:30:46 -0700 Subject: [PATCH 2/7] Load client secret file from mount path Load client secret file from mount path --- src/ogdc_runner/service/auth.py | 31 ++++++++++++++++++++++---- src/ogdc_runner/service/auth_routes.py | 2 +- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/ogdc_runner/service/auth.py b/src/ogdc_runner/service/auth.py index 0f8e0a0b..aa1a2466 100644 --- a/src/ogdc_runner/service/auth.py +++ b/src/ogdc_runner/service/auth.py @@ -3,12 +3,35 @@ from __future__ import annotations import os +from typing import Any -from dataone.auth import AuthFactory, load_client_secrets +from dataone.auth import ( + ACCESS_MODE_AUTHENTICATED, + AuthFactory, + ConfigurationError, + get_access_mode, + load_client_secrets, +) -SCOPE_ADMIN = os.getenv("OGDC_SCOPE_ADMIN", "odgc:admin") +SCOPE_ADMIN = os.getenv("OGDC_SCOPE_ADMIN", "ogdc:admin") scopes = [SCOPE_ADMIN] -# load secrets and instantiate the client -secrets = load_client_secrets() + +def load_oidc_client_secrets() -> dict[str, Any]: + """Load mounted OIDC credentials when authentication is enabled.""" + if get_access_mode() != ACCESS_MODE_AUTHENTICATED: + return {} + + secrets_file = os.getenv("OIDC_CLIENT_SECRETS_FILE") + if not secrets_file: + msg = ( + "OIDC_CLIENT_SECRETS_FILE must point to a client secrets JSON file " + "when ACCESS_MODE=authenticated" + ) + raise ConfigurationError(msg) + + return load_client_secrets(secrets_file) + + +secrets = load_oidc_client_secrets() auth_client = AuthFactory.create_client("fastapi", secrets, scopes) diff --git a/src/ogdc_runner/service/auth_routes.py b/src/ogdc_runner/service/auth_routes.py index 84701df5..70960578 100644 --- a/src/ogdc_runner/service/auth_routes.py +++ b/src/ogdc_runner/service/auth_routes.py @@ -18,7 +18,7 @@ bearer_schema = HTTPBearer(auto_error=False) -SCOPE_ADMIN = os.getenv("OGDC_SCOPE_ADMIN", "odgc:admin") +SCOPE_ADMIN = os.getenv("OGDC_SCOPE_ADMIN", "ogdc:admin") router = APIRouter( # Require that all routes in this module be authenticated via an access From fdd35bf67a966a3233a425bd10393d1bd69b553f Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Mon, 20 Jul 2026 10:31:52 -0700 Subject: [PATCH 3/7] Update test config and add tests for loading client secrets Update test config and add tests for loading client secrets --- tests/conftest.py | 4 ++++ tests/unit/test_auth_config.py | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/unit/test_auth_config.py diff --git a/tests/conftest.py b/tests/conftest.py index 60c594e7..6e1322c3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,15 @@ from __future__ import annotations +import os from functools import cache from pathlib import Path import pytest from sqlmodel import StaticPool, create_engine +# OIDC-specific tests provide their own temporary credentials. +os.environ.setdefault("ACCESS_MODE", "open") + from ogdc_runner.service import db SHELL_RECIPE_TEST_PATH = Path(__file__).parent / "test_shell_workflow_recipe_dir" diff --git a/tests/unit/test_auth_config.py b/tests/unit/test_auth_config.py new file mode 100644 index 00000000..ea061d67 --- /dev/null +++ b/tests/unit/test_auth_config.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import json + +import pytest +from dataone.auth import ConfigurationError + +from ogdc_runner.service import auth + + +def test_authenticated_mode_loads_explicit_secrets_file(tmp_path, monkeypatch): + secrets_path = tmp_path / "client_secrets.json" + expected = { + "server_metadata_url": "https://auth.example/realms/test/.well-known/openid-configuration", + "client_id": "ogdc-test", + "client_secret": "not-a-real-secret", + "redirect_uris": ["https://ogdc.example/api/authorize"], + } + secrets_path.write_text(json.dumps(expected)) + monkeypatch.setenv("ACCESS_MODE", "authenticated") + monkeypatch.setenv("OIDC_CLIENT_SECRETS_FILE", str(secrets_path)) + + assert auth.load_oidc_client_secrets() == expected + + +def test_authenticated_mode_requires_explicit_secrets_file(monkeypatch): + monkeypatch.setenv("ACCESS_MODE", "authenticated") + monkeypatch.delenv("OIDC_CLIENT_SECRETS_FILE", raising=False) + + with pytest.raises(ConfigurationError, match="OIDC_CLIENT_SECRETS_FILE"): + auth.load_oidc_client_secrets() + + +@pytest.mark.parametrize("access_mode", ["open", "read_only"]) +def test_unauthenticated_modes_do_not_require_oidc_secret(access_mode, monkeypatch): + monkeypatch.setenv("ACCESS_MODE", access_mode) + monkeypatch.delenv("OIDC_CLIENT_SECRETS_FILE", raising=False) + + assert auth.load_oidc_client_secrets() == {} From b59a9b36f3960ade6c643e35c69bbb09c4b95245 Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Mon, 20 Jul 2026 10:32:04 -0700 Subject: [PATCH 4/7] Update docs Update docs --- CHANGELOG.md | 4 ++++ docs/architecture/service.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 862699d5..8d61d0cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT_VERSION + +- Load OIDC client configuration from a mounted file instead of the image. + ## v0.4.0 (2026-07-14) - Add parallel execution for `viz-workflow` recipes. diff --git a/docs/architecture/service.md b/docs/architecture/service.md index 29d5c63f..0166a5f6 100644 --- a/docs/architecture/service.md +++ b/docs/architecture/service.md @@ -63,6 +63,10 @@ New users can be given access via the keycloak administrator user interface. - `OGDC_DB_NAME` (optional): database name, defaults to `ogdc`. - `OGDC_SECRET_KEY` (optional): value of the secret key used to encode/decode cookies used for authentication. +- `ACCESS_MODE` (optional): one of `authenticated`, `read_only`, or `open`; + defaults to `authenticated`. +- `OIDC_CLIENT_SECRETS_FILE`: mounted OIDC client JSON path. Required in + authenticated mode; changes require a service restart. - `OGDC_WORKFLOW_PVC_NAME` (optional): name of the PersistentVolumeClaim mounted into workflow pods, defaults to `cephfs-qgnet-ogdc-workflow-pvc`. The ogdc-helm chart sets this to `cephfs-${RELEASE_NAME}-workflow-pvc`. From 616865de508be476e5552ed3ec4e08f7955f5581 Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Mon, 20 Jul 2026 10:32:27 -0700 Subject: [PATCH 5/7] Bump my version patch Bump my version patch --- CHANGELOG.md | 2 +- pyproject.toml | 4 ++-- src/ogdc_runner/__init__.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d61d0cc..19878ebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT_VERSION +## v0.4.1 (2026-07-20) - Load OIDC client configuration from a mounted file instead of the image. diff --git a/pyproject.toml b/pyproject.toml index a844fb3c..f7c7ba1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ authors = [ { name = "Robyn Marowitz", email = "robyn.marowitz@colorado.edu" }, { name = "Jeanette Clark", email = "clark@nceas.ucsb.edu" }, ] -version = "0.4.0" +version = "0.4.1" description = "Defines OGDC recipe API(s) and submits recipes to OGDC for execution" readme = "README.md" license.file = "LICENSE" @@ -94,7 +94,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [tool.bumpversion] -current_version = "0.4.0" +current_version = "0.4.1" commit = false tag = false diff --git a/src/ogdc_runner/__init__.py b/src/ogdc_runner/__init__.py index 38311b6e..5c32524b 100644 --- a/src/ogdc_runner/__init__.py +++ b/src/ogdc_runner/__init__.py @@ -14,6 +14,6 @@ ) # Use your package's name as registered on PyPI except PackageNotFoundError: # This block handles cases where the package is imported but not yet installed (e.g., development mode) - __version__ = "0.4.0" + __version__ = "0.4.1" __all__ = ["__version__"] From f7e15d9bde45f5dee606a62c7a69429ee1078bd6 Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Mon, 20 Jul 2026 10:33:50 -0700 Subject: [PATCH 6/7] Update version in the citation README.md Update version in the citation README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d83d50cc..220ce306 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Cite this software as: > Trey Stafford, Robyn Marowitz, Rushiraj Nenuji, Matthew B. Jones, Twila > Moon, Jeanette Clark. 2026. Open Geospatial Data Cloud (OGDC) Runner: Client tool and API for > scalable and repeatable geospatial data transformation recipes. (version -> 0.4.0). Arctic Data Center. +> 0.4.1). Arctic Data Center. > [doi:10.18739/A2P26Q57N](https://doi.org/doi:10.18739/A2P26Q57N) From 495d8e3b1d825a1d26473f41e2d69c8aab1a650b Mon Sep 17 00:00:00 2001 From: Rushiraj Nenuji Date: Mon, 20 Jul 2026 11:25:07 -0700 Subject: [PATCH 7/7] Update pointers to viz workflow image Update pointers to viz workflow image --- src/ogdc_runner/workflow/viz_workflow.py | 2 +- tests/integration/test_ogdc_viz_recipe.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ogdc_runner/workflow/viz_workflow.py b/src/ogdc_runner/workflow/viz_workflow.py index f5be6381..21beba63 100644 --- a/src/ogdc_runner/workflow/viz_workflow.py +++ b/src/ogdc_runner/workflow/viz_workflow.py @@ -58,7 +58,7 @@ # Viz worker container image. Override via VIZ_WORKFLOW_IMAGE env var. VIZ_WORKFLOW_IMAGE: str = os.environ.get( "VIZ_WORKFLOW_IMAGE", - "ghcr.io/permafrostdiscoverygateway/viz-workflow:latest", + "ghcr.io/permafrostdiscoverygateway/pdgworkflow:latest", ) VIZ_WORKFLOW_IMAGE_PULL_POLICY: str = os.environ.get( "VIZ_WORKFLOW_IMAGE_PULL_POLICY", diff --git a/tests/integration/test_ogdc_viz_recipe.py b/tests/integration/test_ogdc_viz_recipe.py index 1a5a3d8e..625cb77b 100644 --- a/tests/integration/test_ogdc_viz_recipe.py +++ b/tests/integration/test_ogdc_viz_recipe.py @@ -16,7 +16,7 @@ from ogdc_runner.workflow import viz_workflow DEFAULT_VIZ_WORKFLOW_TEST_IMAGE = ( - "ghcr.io/permafrostdiscoverygateway/viz-workflow:1.1.0-dev-2" + "ghcr.io/permafrostdiscoverygateway/pdgworkflow:latest" )