Access request workflow backend (v0.13.0)#109
Merged
Conversation
added 5 commits
April 23, 2026 13:00
Introduce the first building blocks of the access-request workflow: - A new swagger_settings.enable_access_requests flag (off by default) and an access_requests_collection name. MongoDB connection string and database name are reused from the existing CatalogSettings so we do not add duplicate configuration. - Pydantic models covering the request creation payload, the approve/reject decision payloads, and a read model rehydrated from MongoDB documents. - A small AccessRequestRepository that wraps pymongo and exposes create_pending, find_pending_by_user_sub, find_by_id, list and mark_decided. The collection carries indexes on (status, created_at) and on user_sub to keep the admin list and the duplicate guard fast. Refs #108
Introduce two reusable pieces for the upcoming access-request endpoints:
- A small client for the NDP AAI API (add_user_to_group, assign_role,
list_group_members). Every call forwards the caller's bearer token so
the AAI enforces its existing role model — no service account or new
secret is introduced on the ep-api side. Transport errors become 502,
authentication errors become 401/403, and 4xx details from the AAI
are surfaced when available.
- An is_admin helper and a FastAPI dependency require_admin that admit
users holding either the global ndp_admin role or the endpoint-scoped
{AFFINITIES_EP_UUID}_admin role, and reject every other caller with
403. The role name is derived from the existing AFFINITIES_EP_UUID
configuration so no new settings are needed.
Refs #108
Wire the moving parts introduced in the previous commits into four
new endpoints:
- POST /user/access-requests (any authenticated user): create a
pending request, rejecting duplicates with 409.
- GET /user/access-requests (admin only): list pending requests by
default, with ?status=all|approved|rejected filters.
- POST /user/access-requests/{id}/approve (admin only): perform the
IDP grant (add to endpoint group and, when requested, assign the
endpoint admin role) using the admin's own bearer token, then
persist the decision.
- POST /user/access-requests/{id}/reject (admin only): mark the
request as rejected without touching the IDP.
All endpoints return 503 when ENABLE_ACCESS_REQUESTS is off so the
surface area is explicit on deployments without MongoDB.
A thin service module owns lifecycle rules (one pending per user,
AAI grant before marking as approved, find-then-update race guard)
and keeps the route handlers declarative.
Refs #108
…routes
Add four new suites that exercise every layer of the access-request
backend:
- test_require_admin: endpoint_admin_role_name derivation, is_admin
(ndp_admin or {uuid}_admin, case-insensitive, malformed roles)
and the require_admin dependency.
- test_aai_client: URL derivation, happy-path POST/GET, auth-error
translation (401/403/404), 5xx and transport error fallbacks.
- test_access_request_service: feature-flag guard, create-request
duplicate guard and user identification, list filtering semantics,
approve with member vs admin grant (the latter also assigns the
endpoint admin role), 404/409 lifecycle errors, IDP failure does
not mark the request approved, and missing endpoint UUID returns
503. Reject service path covered as well.
- test_access_request_routes: request/response shapes for all four
endpoints, 422 validation for invalid grant_type and status filter,
admin-only gating, and that the caller's bearer token is forwarded
to the service layer on approve.
Refs #108
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/user/access-requestsfor create / list / approve / reject.require_admindependency that admits users with either the globalndp_adminrole or the endpoint-specific{AFFINITIES_EP_UUID}_adminrole.add_user_to_group,assign_role,list_group_members). The IDP write on approve uses the administrator's own bearer token, so no service account or new secret is introduced on the ep-api side.access_requestscollection on the existing MongoDB instance. The connection string and database name are reused fromCatalogSettings— no new env vars beyond theENABLE_ACCESS_REQUESTSfeature flag (off by default).Closes #108
Test plan
tests/test_require_admin.py,tests/test_aai_client.py,tests/test_access_request_service.py,tests/test_access_request_routes.pyblack --check --diff .passesflake8 api/ tests/ --max-line-length=88 --extend-ignore=E203,W503,E501,F401passesgrant_type=member→ user gets added to the endpoint group in Keycloak; yutian's subsequent/user/inforeturns 200; reject path also verified;?status=allreturns both historical records.Followups
{AFFINITIES_EP_UUID}must exist in Keycloak before approving the first request. This is a one-time setup today; we can add lazy creation in a follow-up if needed.