Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions distribution/all-in-one/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,9 @@ services:
- "9243:9243"
volumes:
- ../../platform-api/config/config.toml:/etc/platform-api/config.toml:ro
- ../../platform-api/resources/role-to-scope-mapping.yaml:/etc/platform-api/role-to-scope-mapping.yaml:ro
- platform-api-data:/api-platform/data
- platform-api-certs:/app/data/certs
# RS256 JWT signing/verification keys — on the Platform API's {{ file }}
# allowlist (/etc/platform-api).
- platform-api-jwt-keys:/etc/platform-api/keys:ro
environment:
- APIP_CP_DEFAULT_DEVPORTAL_API_URL=http://devportal:${APIP_DP_SERVER_PORT:-9543}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ data:

[platform_api.auth]
mode = {{ $auth.mode | quote }}
scope_validation = {{ $auth.scopeValidation }}

[platform_api.auth.authorization]
enabled = {{ $auth.authorization.enabled }}
mode = {{ $auth.authorization.mode | quote }}
role_to_scope_mapping = {{ $auth.authorization.roleToScopeMapping | quote }}

[platform_api.auth.claim_mappings]
organization = {{ $auth.claimMappings.organization | quote }}
Expand All @@ -90,8 +94,6 @@ data:
jwks_url = {{ required "config.auth.idp.jwksUrl is required when auth.mode is \"idp\"" $auth.idp.jwksUrl | quote }}
issuer = {{ toJson $auth.idp.issuer }}
audience = {{ toJson $auth.idp.audience }}
validation_mode = {{ $auth.idp.validationMode | quote }}
role_mappings = {{ $auth.idp.roleMappings | quote }}
{{- end }}

{{- if eq $auth.mode "file" }}
Expand All @@ -109,7 +111,7 @@ data:
# provisions a generated username and a bcrypt password hash.
username = {{ `'{{ env "APIP_CP_ADMIN_USERNAME" }}'` }}
password_hash = {{ `'{{ env "APIP_CP_ADMIN_PASSWORD_HASH" }}'` }}
scopes = {{ $auth.file.admin.scopes | quote }}
roles = {{ required "config.auth.file.admin.roles is required when auth.mode is \"file\"" $auth.file.admin.roles | toJson }}
{{- end }}

[platform_api.server.http]
Expand Down Expand Up @@ -166,4 +168,12 @@ data:

{{ . | nindent 4 | trim }}
{{- end }}
{{- with $auth.authorization.roles }}
# Role→scope mapping named by auth.authorization.role_to_scope_mapping. Mounted as a
# file rather than folded into the TOML above because the Platform API reads it
# separately and re-reads it only on restart.
role-to-scope-mapping.yaml: |
roles:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ spec:
- name: config
mountPath: /etc/platform-api/config-platform-api.toml
subPath: config-platform-api.toml
{{- if $pa.config.auth.authorization.roles }}
- name: config
mountPath: {{ $pa.config.auth.authorization.roleToScopeMapping }}
subPath: role-to-scope-mapping.yaml
{{- end }}
- name: jwt-keys
mountPath: {{ $jwtKeyDir }}
readOnly: true
Expand All @@ -184,6 +189,10 @@ spec:
items:
- key: config-platform-api.toml
path: config-platform-api.toml
{{- if $pa.config.auth.authorization.roles }}
- key: role-to-scope-mapping.yaml
path: role-to-scope-mapping.yaml
{{- end }}
# RS256 JWT keys mounted as PEM files from the external Secret. The
# public key verifies tokens (every mode); the private key signs
# file-mode login tokens (mounted only in file mode).
Expand Down
59 changes: 50 additions & 9 deletions kubernetes/helm/platform-api-helm-chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,48 @@ config:
# --- Authentication ---
auth:
# Exactly one mode:
# external_token — verify externally-minted RS256 JWTs with the public key.
# file — external_token + local username/password login (issues
# internal_token — verify RS256 JWTs minted by another trusted platform
# component, using the public key.
# file — internal_token + local username/password login (issues
# RS256 tokens signed with the private key).
# idp — validate tokens against an external IDP's JWKS.
mode: file
# Enforce per-endpoint OAuth2 scopes on validated tokens.
scopeValidation: true
# --- Authorization --- applies in every auth mode above: an enterprise-IDP
# token carries the same roles claim whether it is verified against a JWKS
# endpoint or with a local public key.
authorization:
# Enforce per-endpoint OAuth2 scopes on validated tokens.
enabled: true
mode: scope # scope | role
# Path to the role→scope mapping YAML. Required when mode=role, and in file
# mode (auth.file.admin.roles is a user's whole grant). The chart renders
# `roles` below into its config ConfigMap and mounts it here; point this
# elsewhere only if you supply your own file via extraVolumes/extraVolumeMounts.
roleToScopeMapping: /etc/platform-api/role-to-scope-mapping.yaml
# Roles the mapping file defines, each a name and the scopes it grants.
# Only ap_admin is shipped here — the file-mode admin below names it.
# platform-api/resources/role-to-scope-mapping.yaml is the full sample set (ap_admin,
# ap_operator, ap_publisher, ap_subscriber, ap_viewer); copy the entries you
# need from it. An ap: scope the Platform API's OpenAPI spec does not declare
# fails startup; dp: scopes (Developer Portal) are checked for shape only.
roles:
- name: ap_admin
scopes:
- ap:organization:manage
- ap:project:manage
- ap:gateway:manage
- ap:gateway_custom_policy:manage
- ap:rest_api:manage
- ap:llm_provider:manage
- ap:llm_proxy:manage
- ap:llm_template:manage
- ap:mcp_proxy:manage
- ap:application:manage
- ap:subscription:manage
- ap:subscription_plan:manage
- ap:secret:manage
- ap:api_key:read
- ap:api_key:all:manage
# Claim-name mappings shared by all modes.
claimMappings:
organization: organization
Expand All @@ -122,7 +157,9 @@ config:
username: username
email: email
scope: scope
roles: "" # e.g. "realm_access.roles" (Keycloak)
# Claim carrying the user's roles — read in role authorization mode, and the
# claim the file-mode login endpoint signs auth.file.admin.roles into.
roles: roles # Keycloak nests it: "realm_access.roles"
# Local RS256 JWT keys. public_key_file verifies tokens (every mode);
# private_key_file signs login tokens (file mode only). Both are mounted as
# PEM files from the Secret (secrets.keys.jwtPublicKey / jwtPrivateKey).
Expand All @@ -145,18 +182,22 @@ config:
# APIP_CP_ADMIN_PASSWORD_HASH (secrets.keys.adminUsername / adminPasswordHash),
# which generate-secrets.sh provisions with a generated username and a bcrypt
# hash. There is no admin/admin default: startup fails closed if unset. Only
# the granted scopes are configured here (add more users via configToml).
# the granted roles are configured here (add more users via configToml).
admin:
scopes: "ap:organization:manage ap:gateway:manage ap:gateway_custom_policy:manage ap:rest_api:manage ap:llm_provider:manage ap:llm_proxy:manage ap:mcp_proxy:manage ap:application:manage ap:subscription:manage ap:subscription_plan:manage ap:project:manage ap:llm_template:manage ap:devportal:manage ap:api_key:read ap:api_key:all:manage ap:secret:manage"
# REQUIRED in file mode — one or more roles from the roleToScopeMapping
# file, expanded into the token's scopes at login (the union of what each
# grants), and this user's entire grant (there is no per-user scope list).
# Requires that file to be mounted and roleToScopeMapping above to point
# at it; a role absent from the file fails startup.
roles:
- ap_admin
# idp mode — external OIDC provider (rendered only when mode=idp). jwksUrl is
# required in that mode.
idp:
name: ""
jwksUrl: ""
issuer: [] # accepted token issuers
audience: [] # accepted audiences; empty = don't check
validationMode: scope # scope | role
roleMappings: "" # path to a role→scope mapping YAML

# --- Server listeners ---
server:
Expand Down
Loading
Loading