Skip to content

Access request workflow backend (v0.13.0)#109

Merged
rbardaji merged 5 commits intomainfrom
feature/108-access-requests-backend
Apr 23, 2026
Merged

Access request workflow backend (v0.13.0)#109
rbardaji merged 5 commits intomainfrom
feature/108-access-requests-backend

Conversation

@rbardaji
Copy link
Copy Markdown
Collaborator

Summary

  • Add a MongoDB-backed access-request workflow so denied users can ask for entry and administrators can approve or reject from ep-api.
  • Four new REST endpoints under /user/access-requests for create / list / approve / reject.
  • New require_admin dependency that admits users with either the global ndp_admin role or the endpoint-specific {AFFINITIES_EP_UUID}_admin role.
  • New small client for the NDP AAI API (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.
  • Persistence lives in the access_requests collection on the existing MongoDB instance. The connection string and database name are reused from CatalogSettings — no new env vars beyond the ENABLE_ACCESS_REQUESTS feature flag (off by default).
  • Bump version to 0.13.0.

Closes #108

Test plan

  • 52 new unit tests across tests/test_require_admin.py, tests/test_aai_client.py, tests/test_access_request_service.py, tests/test_access_request_routes.py
  • Full test suite passes (1101 tests)
  • black --check --diff . passes
  • flake8 api/ tests/ --max-line-length=88 --extend-ignore=E203,W503,E501,F401 passes
  • Manual end-to-end run with Keycloak + MongoDB profiles: yutian submits request → 201; yutian submits again → 409; yutian tries to list → 403; raul lists → sees pending; raul approves with grant_type=member → user gets added to the endpoint group in Keycloak; yutian's subsequent /user/info returns 200; reject path also verified; ?status=all returns both historical records.

Followups

  • UI is deliberately out of scope for this PR (tracked in issue 2). The existing 403 screen will keep its current wording until the UI PR lands.
  • The endpoint group {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.

Raul Bardaji 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
@rbardaji rbardaji merged commit 3af975c into main Apr 23, 2026
1 check passed
@rbardaji rbardaji deleted the feature/108-access-requests-backend branch April 23, 2026 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Backend: access request workflow (create, list, approve, reject)

1 participant