Skip to content

Commit 3ff48b1

Browse files
author
Olivier Gintrand
committed
feat(sso): add SSO_ENTRA_TEAM_MAPPING env var for declarative team mapping
Add a new SSO_ENTRA_TEAM_MAPPING setting (JSON dict) to configure Entra ID group-to-team mapping declaratively via environment variable, enabling GitOps workflows for Kubernetes/Helm deployments. Previously, team_mapping for the Entra provider was hard-coded to {} in get_predefined_sso_providers(), making it impossible to set via env. The existing preservation logic only kept DB mappings when env was empty — it could not merge env + DB mappings. Changes: - config.py: Add sso_entra_team_mapping field (Dict[str, Any], default={}) - sso_bootstrap.py: Use settings.sso_entra_team_mapping instead of {} for the Entra provider definition - sso_bootstrap.py: Replace simple preservation with smart merge (env base + DB override) — same strategy already used for provider_metadata. Admin API changes survive restarts while env provides the base mapping. This brings Entra ID to parity with Okta, which already supports SSO_OKTA_TEAM_MAPPING. Fixes #3997 Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
1 parent a2aa82a commit 3ff48b1

2 files changed

Lines changed: 8 additions & 4 deletions

File tree

mcpgateway/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ class Settings(BaseSettings):
385385
sso_entra_graph_api_enabled: bool = Field(default=True, description="Enable Microsoft Graph fallback for EntraID groups overage claims")
386386
sso_entra_graph_api_timeout: int = Field(default=10, ge=1, le=120, description="Timeout in seconds for Microsoft Graph group fallback requests")
387387
sso_entra_graph_api_max_groups: int = Field(default=0, ge=0, description="Maximum groups to keep from Graph fallback (0 = no limit)")
388+
sso_entra_team_mapping: Dict[str, Any] = Field(default_factory=dict, description="Map EntraID groups to ContextForge teams (JSON: {group_id: {team_id: ..., role: member|owner}})")
388389

389390
sso_adfs_enabled: bool = Field(default=False, description="Enable ADFS OIDC authentication")
390391
sso_adfs_client_id: Optional[str] = Field(default=None, description="ADFS OAuth client ID")

mcpgateway/utils/sso_bootstrap.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def get_predefined_sso_providers() -> List[Dict]:
225225
"scope": "openid profile email User.Read",
226226
"trusted_domains": settings.sso_trusted_domains,
227227
"auto_create_users": settings.sso_auto_create_users,
228-
"team_mapping": {},
228+
"team_mapping": settings.sso_entra_team_mapping,
229229
"provider_metadata": {
230230
"groups_claim": settings.sso_entra_groups_claim,
231231
"role_mappings": settings.sso_entra_role_mappings,
@@ -414,9 +414,12 @@ async def bootstrap_sso_providers() -> None:
414414
if existing_provider.scope and existing_provider.scope != "openid profile email" and provider_config.get("scope") == "openid profile email":
415415
provider_config["scope"] = existing_provider.scope
416416

417-
# Preserve DB team_mapping if env provides empty mapping
418-
if existing_provider.team_mapping and not provider_config.get("team_mapping"):
419-
provider_config["team_mapping"] = existing_provider.team_mapping
417+
# Smart merge for team_mapping: same strategy as provider_metadata.
418+
# Env provides base mapping, DB values override (Admin API changes survive restarts).
419+
if "team_mapping" in provider_config:
420+
env_mapping = provider_config["team_mapping"] or {}
421+
db_mapping = existing_provider.team_mapping or {}
422+
provider_config["team_mapping"] = {**env_mapping, **db_mapping}
420423

421424
updated = await sso_service.update_provider(existing_provider.id, provider_config)
422425
if updated:

0 commit comments

Comments
 (0)