Commit 963a6ebf authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

feat: Update QoD session service specification and introduce platform roles

parent 27bdccd7
Loading
Loading
Loading
Loading
+1 −1
Changes for decisions/0006-oeg-db-schema-consolidation.md: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ status: "accepted"

# ADR-0006: `oeg_db` schema — the flow docs' schema is authoritative; `app_registrations` is the CAMARA app record

**Status:** accepted
**Status:** accepted — "client identity is the JWT `client_id` (= `tenant_id`)" is amended by [ADR-0048](/decisions/0048-platform-roles.md): `tenant_id` is the token's `organization` claim, else its `sub`, and `app_provider_id` is the `sub`; `api_subscriptions` is reserved for per-product quota and validity. Everything else stands
**Date:** 2026-07-04
**Affects:** [oeg/persistence-model.md](/oeg/persistence-model.md), [oeg/app-registration-flow.md](/oeg/app-registration-flow.md), [oeg/app-instance-flow.md](/oeg/app-instance-flow.md), [oeg/app-deployment-flow.md](/oeg/app-deployment-flow.md)

+1 −1
Changes for decisions/0035-qod-session-service-specification.md: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ status: "accepted"

# ADR-0035: A QoD session references a single platform-seeded service specification

**Status:** accepted
**Status:** accepted — "per-tenant entitlement layers on through `api_subscriptions`" is amended by [ADR-0048](/decisions/0048-platform-roles.md): access to QoD is the `quality-on-demand` product grant, enforced through token scopes; `api_subscriptions` is reserved for per-product quota and validity. Everything else stands
**Date:** 2026-08-05
**Affects:** [oeg/persistence-model.md](/oeg/persistence-model.md), [srm/architecture.md](/srm/architecture.md), [srm/end-to-end-examples.md](/srm/end-to-end-examples.md), [srm/interface-contract.md](/srm/interface-contract.md)

+109 −0
Changes for decisions/0048-platform-roles.md: 109 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
type: Decision Record
title: "ADR-0048: Three platform roles, API products granted per user and client, tenancy from the organization claim"
description: "Realm oop has exactly three realm roles. Every new user gets app-provider on registration but, in normal mode, calls only the API products granted to them: each CAMARA scope is issued only to holders of its product role. User administration runs through a dedicated Admin API client. Tenancy is the token's organization claim, else its subject. A development mode switches login off for local use."
tags: [adr, security, keycloak, portal, oeg]
timestamp: 2026-10-04T00:00:00Z
status: "proposed"
---

# ADR-0048: Three platform roles, API product grants, and tenancy

**Status:** proposed
**Date:** 2026-10-04
**Affects:** [platform/architecture.md](/platform/architecture.md) §Security Architecture → Roles, [oeg/architecture.md](/oeg/architecture.md) (role and scope checks, F4), [oeg/persistence-model.md](/oeg/persistence-model.md) (`tenant_id`, `app_provider_id`, `api_subscriptions`), [platform/glossary.md](/platform/glossary.md), [ADR-0006](/decisions/0006-oeg-db-schema-consolidation.md), [ADR-0035](/decisions/0035-qod-session-service-specification.md), portal backend `Role` enum, Helm `charts/keycloak` (realm `oop`)

## Context

The Roles table in [platform/architecture.md](/platform/architecture.md) lists two roles,
`app-provider` and `operator`. The portal backend additionally checks `platform-admin`, which the
realm did not define, so any route guarded only by it refused every user.

The platform has no mail server: nothing is sent by email, so there are no verification links and
no self-service password reset. A person has to reset passwords and fix accounts. That work needs
an owner, and it should not fall to operators by default: an operator who can manage users can
grant themselves roles or take over another account, which mixes running the platform with
controlling who may use it.

Helm attaches every CAMARA scope as a default scope of the portal client, so every user's token
carries all of them. Anyone who registers can call every API, including Location Retrieval, which
locates any device whose number the caller sends. Local development needs that openness; a
deployment that real developers use should grant access deliberately.

Tenancy was left open. OEG reads the token's `organization` claim and falls back to `sub`, while
ADR-0006 and the persistence model still say `tenant_id` is the JWT `client_id`, which no
component does. Two places also claim to decide who may call what: token scopes, which OEG checks
per operation, and `api_subscriptions`, a table OEG never populates.

## Decision

1. **Realm `oop` has exactly three realm roles.**

   | Role | Who | Can do |
   |---|---|---|
   | `app-provider` | Developers (application providers; "Developer" in the UI) | Register and deploy apps, call CAMARA/TMF APIs through OEG for the products granted to them, see their own resources |
   | `operator` | People who run the platform | Manage catalog, zones, domains and federation; see all operations |
   | `platform-admin` | A few trusted people | Manage users through the portal: reset passwords, grant and revoke roles and products |

2. **Every new user, including self-registered ones, gets `app-provider` immediately** (the realm's
   default role). There is no approval step for the role.
3. **`platform-admin` does not imply `operator`** and vice versa (no composite roles). One person
   may hold both.
4. **User administration goes through Keycloak's Admin API using a dedicated confidential client,
   `oop-portal-admin`,** whose service account holds only `realm-management` `manage-users`,
   `view-users`, `query-users` and `view-realm` in realm `oop`. The portal authorizes its user
   pages with `platform-admin`; people never receive Keycloak administration rights through the
   portal. `manage-users` can map any realm role, so the portal must restrict which roles its
   admin pages offer.
5. **API access is granted per API product.**
   - The API products are `edge-application-management`, `quality-on-demand`,
     `location-retrieval` and `traffic-influence`. Each is a client role of the same name on the
     `oeg` client. Client roles, so decision 1 still holds.
   - Each CAMARA client scope has a role scope mapping to its product's role. Keycloak puts a scope
     in a token only when the user, or the service account behind a client, holds that role. OEG
     keeps checking scopes per operation and knows nothing about products.
   - In normal mode a new user holds no product role. An administrator grants a product by adding
     the user to that product's group.
   - A deployment may list products every new user receives (a realm default group). The list is
     empty by default, and `location-retrieval` may not be on it.
   - A machine client holds `app-provider` and the product roles of the products it is entitled to.
6. **Tenancy is the token's `organization` claim, or its `sub` when there is none.** A
   `platform-admin` sets `organization` on the user. `app_provider_id` is the token's `sub`.
   Neither is a role.
7. **Token scopes are the only access check in Release 2.** `api_subscriptions` stays in OEG's
   schema for per-product quota and validity, unpopulated until a release needs them.
8. **Development mode switches login off for the whole platform.** Helm's `global.auth.enabled:
   false` sets the portal backend to `AUTH__SECURITY_MODE=none`, where every request acts as a
   development user holding every role, and OEG to `KEYCLOAK_SETTINGS__ENFORCEMENT_ENABLED=false`,
   where failed checks are logged instead of rejected. It is allowed only when the public host is
   `localhost` or `127.0.0.1`. Normal mode always applies decisions 1–7.
9. **These are not roles:**
   - *Federation partners* — realm `federation`, identified by client (`azp`) and the `fed-mgmt`
     scope ([ADR-0042](/decisions/0042-ewbi-oauth2-client-credentials-mtls-deferred.md)).
   - *Future personas* (`channel-partner`, `ai-agent` in
     [platform/future-architecture.md](/platform/future-architecture.md)) are added when their
     features are built, not before.

## Consequences

- OEG and the portal check the same three role names; OEG's `operator`-gated routes need no change.
- A new user can call no CAMARA API until granted a product, unless the deployment lists default
  products. The portal shows a no-access state for products the user lacks instead of passing on
  OEG's 403.
- OEG must check scopes on QoD sessions. It skips them today (CAMARA's client-credentials
  alternative), so a user without `quality-on-demand` could still create sessions. Traffic
  Influence needs role and scope checks when it reaches `develop`.
- Role scope mappings must be verified on Keycloak 26.7 before the chart relies on them.
- Realm import only creates a realm that does not exist yet. On an existing realm, the roles,
  groups, scope mappings and the `oop-portal-admin` client are added through the Admin API (or
  keycloak-config-cli once adopted).
- The `oop-portal-admin` secret can reset any password in realm `oop`: it belongs to the portal
  backend alone and is stored like the other client secrets.
- The Helm chart does not yet pass development mode to the portal or OEG. Until it does, both
  variables are set by hand.
- Development mode gives anyone who reaches the platform every role and every operation, which is
  why it is limited to `localhost`.
- Grants are per user in Release 2. When organizations own applications (the TMF931
  ApplicationOwner), grants move to the organization; the product roles and scope mappings stay.
- ADR-0006's "`tenant_id` is the JWT `client_id`" and ADR-0035's "per-tenant entitlement through
  `api_subscriptions`" are amended by decisions 6 and 7.
+1 −0
Changes for decisions/index.md: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -49,4 +49,5 @@ the reasoning lives here. Never renumber; supersede instead (see the record-deci
* [ADR-0044](0044-srm-owns-federated-zone-locality-and-hand-off.md) - SRM resolves Edge Cloud Zone locality for deployment targets and forwards non-local deploys to FM; interim layer pending a future runtime-coordinator component
* [ADR-0045](0045-pin-narrows-candidates-not-probing.md) - a zone/domain pin narrows the device-targeted candidate set rather than bypassing target probing; probing is triggered by how many candidates survive the pin (amends ADR-0038's unpinned scoping)
* [ADR-0047](0047-terminal-record-read-follows-camara-schema.md) - terminal-record read behaviour follows each family's own vendored CAMARA schema: Traffic Influence returns 200 with `state: deleted`, QoD stays 404 (amends ADR-0034's blanket 404 rule and its 204 delete code)
* [ADR-0048](0048-platform-roles.md) - three realm roles (app-provider, operator, platform-admin); new users get app-provider but call only the API products granted to them (each CAMARA scope gated by a product role); tenancy is the `organization` claim, else `sub`; development mode switches login off on localhost (amends ADR-0006's tenant identity and ADR-0035's entitlement note)
* [ADR-0062](0062-zone-state-uses-camara-inactive.md) - `zone.state` uses CAMARA's `active`/`inactive`/`unknown` (was `offline`), so OEG passes the zone status filter and value through unmapped; domain, capability and control-path-binding states keep `offline`
+30 −17

File changed.

Preview size limit exceeded, changes collapsed.

Loading