diff --git a/helm-charts/bifrost/templates/_helpers.tpl b/helm-charts/bifrost/templates/_helpers.tpl index 65056996d7..6612ba3348 100644 --- a/helm-charts/bifrost/templates/_helpers.tpl +++ b/helm-charts/bifrost/templates/_helpers.tpl @@ -1277,6 +1277,80 @@ false {{- if hasKey .Values.bifrost.plugins.bigquery "version" }}{{- $_ := set $plugin "version" (.Values.bifrost.plugins.bigquery.version | int) }}{{- end }} {{- $plugins = append $plugins $plugin }} {{- end }} +{{- if (.Values.bifrost.plugins.kafka).enabled }} +{{- $kafkaConfig := dict }} +{{- $inputConfig := .Values.bifrost.plugins.kafka.config | default dict }} +{{- if $inputConfig.brokers }} +{{- $_ := set $kafkaConfig "brokers" $inputConfig.brokers }} +{{- end }} +{{- if $inputConfig.topic }} +{{- $_ := set $kafkaConfig "topic" $inputConfig.topic }} +{{- end }} +{{- if hasKey $inputConfig "sasl_enabled" }} +{{- $_ := set $kafkaConfig "sasl_enabled" $inputConfig.sasl_enabled }} +{{- end }} +{{- if $inputConfig.sasl }} +{{- $_ := set $kafkaConfig "sasl" $inputConfig.sasl }} +{{- end }} +{{- if hasKey $inputConfig "tls_enabled" }} +{{- $_ := set $kafkaConfig "tls_enabled" $inputConfig.tls_enabled }} +{{- end }} +{{- if $inputConfig.ca_cert }} +{{- $_ := set $kafkaConfig "ca_cert" $inputConfig.ca_cert }} +{{- end }} +{{- if $inputConfig.compression }} +{{- $_ := set $kafkaConfig "compression" $inputConfig.compression }} +{{- end }} +{{- if hasKey $inputConfig "batch_size" }} +{{- $_ := set $kafkaConfig "batch_size" $inputConfig.batch_size }} +{{- end }} +{{- if hasKey $inputConfig "flush_interval_ms" }} +{{- $_ := set $kafkaConfig "flush_interval_ms" $inputConfig.flush_interval_ms }} +{{- end }} +{{- if hasKey $inputConfig "auto_create_topic" }} +{{- $_ := set $kafkaConfig "auto_create_topic" $inputConfig.auto_create_topic }} +{{- end }} +{{- if hasKey $inputConfig "disable_content_logging" }} +{{- $_ := set $kafkaConfig "disable_content_logging" $inputConfig.disable_content_logging }} +{{- end }} +{{- if $inputConfig.request_headers }} +{{- $_ := set $kafkaConfig "request_headers" $inputConfig.request_headers }} +{{- end }} +{{- if $inputConfig.plugin_span_filter }} +{{- $_ := set $kafkaConfig "plugin_span_filter" $inputConfig.plugin_span_filter }} +{{- end }} +{{- $plugin := dict "enabled" true "name" "kafka" "config" $kafkaConfig }} +{{- if hasKey .Values.bifrost.plugins.kafka "version" }}{{- $_ := set $plugin "version" (.Values.bifrost.plugins.kafka.version | int) }}{{- end }} +{{- $plugins = append $plugins $plugin }} +{{- end }} +{{- if (.Values.bifrost.plugins.pubsub).enabled }} +{{- $pubsubConfig := dict }} +{{- $inputConfig := .Values.bifrost.plugins.pubsub.config | default dict }} +{{- if $inputConfig.project_id }} +{{- $_ := set $pubsubConfig "project_id" $inputConfig.project_id }} +{{- end }} +{{- if $inputConfig.topic_id }} +{{- $_ := set $pubsubConfig "topic_id" $inputConfig.topic_id }} +{{- end }} +{{- if $inputConfig.service_account_key }} +{{- $_ := set $pubsubConfig "service_account_key" $inputConfig.service_account_key }} +{{- end }} +{{- if hasKey $inputConfig "auto_create_topic" }} +{{- $_ := set $pubsubConfig "auto_create_topic" $inputConfig.auto_create_topic }} +{{- end }} +{{- if hasKey $inputConfig "disable_content_logging" }} +{{- $_ := set $pubsubConfig "disable_content_logging" $inputConfig.disable_content_logging }} +{{- end }} +{{- if $inputConfig.request_headers }} +{{- $_ := set $pubsubConfig "request_headers" $inputConfig.request_headers }} +{{- end }} +{{- if $inputConfig.plugin_span_filter }} +{{- $_ := set $pubsubConfig "plugin_span_filter" $inputConfig.plugin_span_filter }} +{{- end }} +{{- $plugin := dict "enabled" true "name" "pubsub" "config" $pubsubConfig }} +{{- if hasKey .Values.bifrost.plugins.pubsub "version" }}{{- $_ := set $plugin "version" (.Values.bifrost.plugins.pubsub.version | int) }}{{- end }} +{{- $plugins = append $plugins $plugin }} +{{- end }} {{- /* Custom plugins */ -}} {{- if .Values.bifrost.plugins.custom }} {{- range .Values.bifrost.plugins.custom }} @@ -1448,6 +1522,36 @@ Call this template at the beginning of deployment/stateful templates {{- if and .Values.bifrost.plugins.bigquery.enabled (not (.Values.bifrost.plugins.bigquery.config | default dict).project_id) }} {{- fail "ERROR: bifrost.plugins.bigquery.config.project_id is required when the BigQuery plugin is enabled." }} {{- end }} +{{- if and (.Values.bifrost.plugins.kafka).enabled (hasKey .Values.bifrost.plugins.kafka "version") (lt (int .Values.bifrost.plugins.kafka.version) 1) }} +{{- fail "ERROR: bifrost.plugins.kafka.version must be >= 1. Bump to >1 to force DB-backed plugin config updates." }} +{{- end }} +{{- if and (.Values.bifrost.plugins.kafka).enabled (hasKey .Values.bifrost.plugins.kafka "version") (gt (int .Values.bifrost.plugins.kafka.version) 32767) }} +{{- fail "ERROR: bifrost.plugins.kafka.version must be <= 32767." }} +{{- end }} +{{- if (.Values.bifrost.plugins.kafka).enabled }} +{{- $kafkaInputConfig := .Values.bifrost.plugins.kafka.config | default dict }} +{{- if not $kafkaInputConfig.brokers }} +{{- fail "ERROR: bifrost.plugins.kafka.config.brokers is required when the Kafka plugin is enabled." }} +{{- end }} +{{- if not $kafkaInputConfig.topic }} +{{- fail "ERROR: bifrost.plugins.kafka.config.topic is required when the Kafka plugin is enabled." }} +{{- end }} +{{- end }} +{{- if and (.Values.bifrost.plugins.pubsub).enabled (hasKey .Values.bifrost.plugins.pubsub "version") (lt (int .Values.bifrost.plugins.pubsub.version) 1) }} +{{- fail "ERROR: bifrost.plugins.pubsub.version must be >= 1. Bump to >1 to force DB-backed plugin config updates." }} +{{- end }} +{{- if and (.Values.bifrost.plugins.pubsub).enabled (hasKey .Values.bifrost.plugins.pubsub "version") (gt (int .Values.bifrost.plugins.pubsub.version) 32767) }} +{{- fail "ERROR: bifrost.plugins.pubsub.version must be <= 32767." }} +{{- end }} +{{- if (.Values.bifrost.plugins.pubsub).enabled }} +{{- $pubsubInputConfig := .Values.bifrost.plugins.pubsub.config | default dict }} +{{- if not $pubsubInputConfig.project_id }} +{{- fail "ERROR: bifrost.plugins.pubsub.config.project_id is required when the Pub/Sub plugin is enabled." }} +{{- end }} +{{- if not $pubsubInputConfig.topic_id }} +{{- fail "ERROR: bifrost.plugins.pubsub.config.topic_id is required when the Pub/Sub plugin is enabled." }} +{{- end }} +{{- end }} {{/* Validate semantic cache plugin when enabled */}} {{- if .Values.bifrost.plugins.semanticCache.enabled }} diff --git a/helm-charts/bifrost/values.schema.json b/helm-charts/bifrost/values.schema.json index 99d6545a4f..ddb7d97f10 100644 --- a/helm-charts/bifrost/values.schema.json +++ b/helm-charts/bifrost/values.schema.json @@ -1015,6 +1015,211 @@ } } }, + "kafka": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "version": { + "type": "integer", + "minimum": 1 + }, + "config": { + "type": "object", + "properties": { + "brokers": { + "type": "array", + "items": { "type": "string" }, + "description": "Kafka broker addresses (required when enabled)" + }, + "topic": { + "type": "string", + "description": "Kafka topic to publish traces to (required when enabled)" + }, + "sasl_enabled": { + "type": "boolean", + "description": "Enable SASL authentication. SASL is only activated when both sasl_enabled is true and sasl is set" + }, + "sasl": { + "type": "object", + "properties": { + "mechanism": { + "type": "string", + "enum": ["PLAIN", "SCRAM-SHA-256", "SCRAM-SHA-512"] + }, + "username": { + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "value": { "type": "string" }, + "env_var": { "type": "string" }, + "from_env": { "type": "boolean" } + }, + "additionalProperties": false + } + ], + "description": "SASL username. Supports env.VAR_NAME prefix for environment variable substitution" + }, + "password": { + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "value": { "type": "string" }, + "env_var": { "type": "string" }, + "from_env": { "type": "boolean" } + }, + "additionalProperties": false + } + ], + "description": "SASL password. Supports env.VAR_NAME prefix for environment variable substitution" + } + }, + "additionalProperties": false + }, + "tls_enabled": { + "type": "boolean", + "description": "Enable TLS for broker connections" + }, + "ca_cert": { + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "value": { "type": "string" }, + "env_var": { "type": "string" }, + "from_env": { "type": "boolean" } + }, + "additionalProperties": false + } + ], + "description": "PEM-encoded CA certificate used to verify the broker's TLS certificate. Omit to use the system CA pool. Supports env.VAR_NAME prefix" + }, + "compression": { + "type": "string", + "enum": ["none", "gzip", "snappy", "lz4", "zstd"], + "description": "Compression codec for published messages" + }, + "batch_size": { + "type": "integer", + "description": "Maximum number of messages batched per write (default: 100)" + }, + "flush_interval_ms": { + "type": "integer", + "description": "Maximum time (ms) to wait before flushing a batch (default: 1000)" + }, + "auto_create_topic": { + "type": "boolean", + "description": "Create the topic at startup if it does not exist" + }, + "disable_content_logging": { + "type": "boolean", + "description": "Strip input/output message content from traces before publishing" + }, + "request_headers": { + "type": "array", + "items": { "type": "string" }, + "description": "Request-header name patterns (exact or wildcard like \"x-custom-*\") whose captured values are embedded in published traces" + }, + "plugin_span_filter": { + "$ref": "#/$defs/pluginSpanFilter" + } + } + } + }, + "if": { + "properties": { + "enabled": { "const": true } + }, + "required": ["enabled"] + }, + "then": { + "required": ["config"], + "properties": { + "config": { + "required": ["brokers", "topic"], + "properties": { + "brokers": { "minItems": 1 } + } + } + } + } + }, + "pubsub": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "version": { + "type": "integer", + "minimum": 1 + }, + "config": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "GCP project ID (required when enabled)" + }, + "topic_id": { + "type": "string", + "description": "Pub/Sub topic ID — just the ID, not the full resource name (required when enabled)" + }, + "service_account_key": { + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "value": { "type": "string" }, + "env_var": { "type": "string" }, + "from_env": { "type": "boolean" } + }, + "additionalProperties": false + } + ], + "description": "Service account key JSON for authentication. Omit to use Application Default Credentials (ADC). Supports env.VAR_NAME prefix" + }, + "auto_create_topic": { + "type": "boolean", + "description": "Create the topic at startup if it does not exist" + }, + "disable_content_logging": { + "type": "boolean", + "description": "Strip request/response content from traces before publishing" + }, + "request_headers": { + "type": "array", + "items": { "type": "string" }, + "description": "Request-header name patterns (exact or wildcard like \"x-custom-*\") whose captured values are included in published traces" + }, + "plugin_span_filter": { + "$ref": "#/$defs/pluginSpanFilter" + } + } + } + }, + "if": { + "properties": { + "enabled": { "const": true } + }, + "required": ["enabled"] + }, + "then": { + "required": ["config"], + "properties": { + "config": { + "required": ["project_id", "topic_id"] + } + } + } + }, "custom": { "type": "array", "items": { diff --git a/helm-charts/bifrost/values.yaml b/helm-charts/bifrost/values.yaml index a437b82944..4f3100e0c5 100644 --- a/helm-charts/bifrost/values.yaml +++ b/helm-charts/bifrost/values.yaml @@ -555,6 +555,43 @@ bifrost: # mode: "exclude" # "include" or "exclude" # plugins: ["logging"] + kafka: + enabled: false + version: 1 + config: + brokers: [] # Kafka broker addresses (required when enabled) + topic: "" # Topic to publish traces to (required when enabled) + sasl_enabled: false + # sasl: + # mechanism: "PLAIN" # PLAIN | SCRAM-SHA-256 | SCRAM-SHA-512 + # username: "env.KAFKA_USERNAME" + # password: "env.KAFKA_PASSWORD" + tls_enabled: false + # ca_cert: "env.KAFKA_CA_CERT" # PEM CA certificate to verify the broker. Omit to use the system CA pool. + compression: "none" # none | gzip | snappy | lz4 | zstd + batch_size: 100 + flush_interval_ms: 1000 + auto_create_topic: false + disable_content_logging: false + # request_headers: [] # Header name patterns (exact or wildcard like "x-custom-*") + # plugin_span_filter: # Optional: filter which plugin hook spans are exported + # mode: "exclude" # "include" or "exclude" + # plugins: ["logging"] + + pubsub: + enabled: false + version: 1 + config: + project_id: "" # GCP project ID (required when enabled) + topic_id: "" # Pub/Sub topic ID (required when enabled) + # service_account_key: "" # Service account key JSON, or "env.VAR". Omit to use ADC. + auto_create_topic: false + disable_content_logging: false + # request_headers: [] # Header name patterns (exact or wildcard like "x-custom-*") + # plugin_span_filter: # Optional: filter which plugin hook spans are exported + # mode: "exclude" # "include" or "exclude" + # plugins: ["logging"] + # Custom/dynamic plugins custom: [] # - name: "my-custom-plugin" diff --git a/transports/config.schema.json b/transports/config.schema.json index 0f88aeb60a..893b8bbcb8 100644 --- a/transports/config.schema.json +++ b/transports/config.schema.json @@ -1877,6 +1877,201 @@ } } } + }, + { + "if": { + "properties": { + "name": { + "const": "kafka" + } + } + }, + "then": { + "required": ["config"], + "properties": { + "config": { + "type": "object", + "description": "Configuration for the Kafka traces plugin (Enterprise). Publishes completed traces as JSON messages to a Kafka topic, keyed by trace ID.", + "properties": { + "brokers": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "Kafka broker addresses (required)" + }, + "topic": { + "type": "string", + "description": "Kafka topic to publish traces to (required)" + }, + "sasl_enabled": { + "type": "boolean", + "description": "Enable SASL authentication. SASL is only activated when both sasl_enabled is true and sasl is set.", + "default": false + }, + "sasl": { + "type": "object", + "description": "SASL credentials, used when sasl_enabled is true", + "properties": { + "mechanism": { + "type": "string", + "enum": ["PLAIN", "SCRAM-SHA-256", "SCRAM-SHA-512"] + }, + "username": { + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "value": { "type": "string" }, + "env_var": { "type": "string" }, + "from_env": { "type": "boolean" } + }, + "additionalProperties": false + } + ], + "description": "SASL username. Supports env var syntax: \"env.MY_VAR\"." + }, + "password": { + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "value": { "type": "string" }, + "env_var": { "type": "string" }, + "from_env": { "type": "boolean" } + }, + "additionalProperties": false + } + ], + "description": "SASL password. Supports env var syntax: \"env.MY_VAR\"." + } + }, + "additionalProperties": false + }, + "tls_enabled": { + "type": "boolean", + "description": "Enable TLS for broker connections", + "default": false + }, + "ca_cert": { + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "value": { "type": "string" }, + "env_var": { "type": "string" }, + "from_env": { "type": "boolean" } + }, + "additionalProperties": false + } + ], + "description": "PEM-encoded CA certificate used to verify the broker's TLS certificate. Omit to use the system CA pool. Supports env var syntax: \"env.KAFKA_CA_CERT\"." + }, + "compression": { + "type": "string", + "enum": ["none", "gzip", "snappy", "lz4", "zstd"], + "description": "Compression codec for published messages", + "default": "none" + }, + "batch_size": { + "type": "integer", + "description": "Maximum number of messages batched per write", + "default": 100 + }, + "flush_interval_ms": { + "type": "integer", + "description": "Maximum time (ms) to wait before flushing a batch", + "default": 1000 + }, + "auto_create_topic": { + "type": "boolean", + "description": "Create the topic at startup if it does not exist", + "default": false + }, + "disable_content_logging": { + "type": "boolean", + "description": "Strip input/output message content from traces before publishing", + "default": false + }, + "request_headers": { + "type": "array", + "items": { "type": "string" }, + "description": "Request-header name patterns (exact or wildcard like \"x-custom-*\") whose captured values are embedded in published traces" + }, + "plugin_span_filter": { + "$ref": "#/$defs/plugin_span_filter" + } + }, + "required": ["brokers", "topic"], + "additionalProperties": false + } + } + } + }, + { + "if": { + "properties": { + "name": { + "const": "pubsub" + } + } + }, + "then": { + "required": ["config"], + "properties": { + "config": { + "type": "object", + "description": "Configuration for the Google Cloud Pub/Sub traces plugin (Enterprise). Publishes completed traces as JSON messages to a Pub/Sub topic.", + "properties": { + "project_id": { + "type": "string", + "description": "GCP project ID (required)" + }, + "topic_id": { + "type": "string", + "description": "Pub/Sub topic ID — just the ID, not the full resource name (required)" + }, + "service_account_key": { + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "value": { "type": "string" }, + "env_var": { "type": "string" }, + "from_env": { "type": "boolean" } + }, + "additionalProperties": false + } + ], + "description": "Service account key JSON for authentication. Omit to use Application Default Credentials (ADC). Supports env var syntax: \"env.MY_VAR\"." + }, + "auto_create_topic": { + "type": "boolean", + "description": "Create the topic at startup if it does not exist", + "default": false + }, + "disable_content_logging": { + "type": "boolean", + "description": "Strip request/response content from traces before publishing", + "default": false + }, + "request_headers": { + "type": "array", + "items": { "type": "string" }, + "description": "Request-header name patterns (exact or wildcard like \"x-custom-*\") whose captured values are included in published traces" + }, + "plugin_span_filter": { + "$ref": "#/$defs/plugin_span_filter" + } + }, + "required": ["project_id", "topic_id"], + "additionalProperties": false + } + } + } } ], "additionalProperties": false @@ -2379,7 +2574,12 @@ } } }, - "required": ["code_keywords", "reasoning_keywords", "technical_keywords", "simple_keywords"], + "required": [ + "code_keywords", + "reasoning_keywords", + "technical_keywords", + "simple_keywords" + ], "additionalProperties": false }, "complexity_analyzer_config": { @@ -2680,7 +2880,15 @@ }, "model_family": { "type": "string", - "enum": ["anthropic", "openai", "mistral", "cohere", "gemini", "nova", "titan"], + "enum": [ + "anthropic", + "openai", + "mistral", + "cohere", + "gemini", + "nova", + "titan" + ], "description": "Underlying model family. Used by provider routing without substring-sniffing the wire model ID." }, "description": { @@ -4205,6 +4413,15 @@ "role": { "type": "string", "description": "Bifrost role to assign on match" + }, + "attributeType": { + "type": "string", + "enum": ["user", "group"], + "description": "SCIM provisioning type: 'user' matches SCIM User attributes, 'group' matches SCIM Group displayName" + }, + "attributeValue": { + "type": "string", + "description": "SCIM attribute value to match (for attributeType 'user': the SCIM user attribute value; for 'group': the SCIM group displayName, auto-set to 'displayName')" } }, "required": ["attribute", "value", "role"], @@ -4227,7 +4444,7 @@ }, "team": { "type": "string", - "description": "Bifrost team slug to assign" + "description": "Bifrost team slug to assign. In case of '*' value, leave this empty" }, "attributeType": { "type": "string", @@ -4255,11 +4472,11 @@ }, "value": { "type": "string", - "description": "Claim value to match" + "description": "Claim value to match. Wildcard '*' will use the claim as is the name of the business unit to assign." }, "business_unit": { "type": "string", - "description": "Bifrost business unit slug to assign" + "description": "Bifrost business unit slug to assign. In case of '*' value, leave this empty" }, "attributeType": { "type": "string", @@ -4271,7 +4488,7 @@ "description": "SCIM attribute value to match (for attributeType 'user': the SCIM user attribute value; for 'group': the SCIM group displayName, auto-set to 'displayName')" } }, - "required": ["attribute", "value", "business_unit"], + "required": ["attribute", "value"], "additionalProperties": false } } @@ -4324,6 +4541,102 @@ "type": "string", "description": "JWT claim field for roles (default: 'roles')", "default": "roles" + }, + "attributeRoleMappings": { + "type": "array", + "description": "Ordered list of attribute -> role mappings (first match wins).", + "items": { + "type": "object", + "properties": { + "attribute": { + "type": "string", + "description": "JWT claim name (supports dot paths)" + }, + "value": { + "type": "string", + "description": "Claim value to match (case-insensitive)" + }, + "role": { + "type": "string", + "description": "Bifrost role to assign on match" + }, + "attributeType": { + "type": "string", + "enum": ["user", "group"], + "description": "SCIM provisioning type: 'user' matches SCIM User attributes, 'group' matches SCIM Group displayName" + }, + "attributeValue": { + "type": "string", + "description": "SCIM attribute value to match (for attributeType 'user': the SCIM user attribute value; for 'group': the SCIM group displayName, auto-set to 'displayName')" + } + }, + "required": ["attribute", "value", "role"], + "additionalProperties": false + } + }, + "attributeTeamMappings": { + "type": "array", + "description": "Attribute -> team mappings (all matches apply). Use value '*' for pass-through. Add attributeType/attributeValue to enable SCIM provisioning for a mapping.", + "items": { + "type": "object", + "properties": { + "attribute": { + "type": "string", + "description": "JWT claim name" + }, + "value": { + "type": "string", + "description": "Claim value to match, or '*' for pass-through" + }, + "team": { + "type": "string", + "description": "Bifrost team slug to assign. In case of '*' value, leave this empty" + }, + "attributeType": { + "type": "string", + "enum": ["user", "group"], + "description": "SCIM provisioning type: 'user' matches SCIM User attributes, 'group' matches SCIM Group displayName" + }, + "attributeValue": { + "type": "string", + "description": "SCIM attribute value to match (for attributeType 'user': the SCIM user attribute value; for 'group': the SCIM group displayName, auto-set to 'displayName')" + } + }, + "required": ["attribute", "value"], + "additionalProperties": false + } + }, + "attributeBusinessUnitMappings": { + "type": "array", + "description": "Attribute -> business-unit mappings (all matches apply). Add attributeType/attributeValue to enable SCIM provisioning for a mapping.", + "items": { + "type": "object", + "properties": { + "attribute": { + "type": "string", + "description": "JWT claim name" + }, + "value": { + "type": "string", + "description": "Claim value to match. Wildcard '*' will use the claim as is the name of the business unit to assign." + }, + "business_unit": { + "type": "string", + "description": "Bifrost business unit slug to assign. In case of '*' value, leave this empty" + }, + "attributeType": { + "type": "string", + "enum": ["user", "group"], + "description": "SCIM provisioning type: 'user' matches SCIM User attributes, 'group' matches SCIM Group displayName" + }, + "attributeValue": { + "type": "string", + "description": "SCIM attribute value to match (for attributeType 'user': the SCIM user attribute value; for 'group': the SCIM group displayName, auto-set to 'displayName')" + } + }, + "required": ["attribute", "value"], + "additionalProperties": false + } } }, "required": ["tenantId", "clientId"], @@ -4371,23 +4684,71 @@ }, "attributeRoleMappings": { "type": "array", - "description": "Ordered list of attribute -> role mappings", + "description": "Ordered list of attribute -> role mappings (first match wins).", "items": { - "type": "object" + "type": "object", + "properties": { + "attribute": { + "type": "string", + "description": "JWT claim name (supports dot paths, e.g. 'realm_access.roles')" + }, + "value": { + "type": "string", + "description": "Claim value to match (case-insensitive)" + }, + "role": { + "type": "string", + "description": "Bifrost role to assign on match" + } + }, + "required": ["attribute", "value", "role"], + "additionalProperties": false } }, "attributeTeamMappings": { "type": "array", - "description": "Attribute -> team mappings (all matches apply)", + "description": "Attribute -> team mappings (all matches apply). Use value '*' for pass-through.", "items": { - "type": "object" + "type": "object", + "properties": { + "attribute": { + "type": "string", + "description": "JWT claim name" + }, + "value": { + "type": "string", + "description": "Claim value to match, or '*' for pass-through" + }, + "team": { + "type": "string", + "description": "Bifrost team slug to assign. In case of '*' value, leave this empty" + } + }, + "required": ["attribute", "value"], + "additionalProperties": false } }, "attributeBusinessUnitMappings": { "type": "array", - "description": "Attribute -> business-unit mappings (all matches apply)", + "description": "Attribute -> business-unit mappings (all matches apply).", "items": { - "type": "object" + "type": "object", + "properties": { + "attribute": { + "type": "string", + "description": "JWT claim name" + }, + "value": { + "type": "string", + "description": "Claim value to match. Wildcard '*' will use the claim as is the name of the business unit to assign." + }, + "business_unit": { + "type": "string", + "description": "Bifrost business unit slug to assign. In case of '*' value, leave this empty" + } + }, + "required": ["attribute", "value"], + "additionalProperties": false } } },