diff --git a/backend/infrahub/config.py b/backend/infrahub/config.py index 5a714c8bc6..74f9cb90dc 100644 --- a/backend/infrahub/config.py +++ b/backend/infrahub/config.py @@ -451,6 +451,14 @@ class SecurityOIDCProvider2(SecurityOIDCSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_OIDC_PROVIDER2_") +class SecurityOIDCProviderSettings(BaseModel): + """This class is meant to facilitate configuration of OIDC providers when loading configuration from a infrahub.toml file.""" + + google: Optional[SecurityOIDCGoogle] = Field(default=None) + provider1: Optional[SecurityOIDCProvider1] = Field(default=None) + provider2: Optional[SecurityOIDCProvider2] = Field(default=None) + + class SecurityOAuth2BaseSettings(BaseSettings): """Baseclass for typing""" @@ -490,6 +498,14 @@ class SecurityOAuth2Google(SecurityOAuth2Settings): display_label: str = Field(default="Google") +class SecurityOAuth2ProviderSettings(BaseModel): + """This class is meant to facilitate configuration of OAuth2 providers when loading configuration from a infrahub.toml file.""" + + google: Optional[SecurityOAuth2Google] = Field(default=None) + provider1: Optional[SecurityOAuth2Provider1] = Field(default=None) + provider2: Optional[SecurityOAuth2Provider2] = Field(default=None) + + class MiscellaneousSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_MISC_") print_query_details: bool = False @@ -535,7 +551,9 @@ class SecuritySettings(BaseSettings): default_factory=generate_uuid, description="The secret key used to validate authentication tokens" ) oauth2_providers: list[Oauth2Provider] = Field(default_factory=list, description="The selected OAuth2 providers") + oauth2_provider_settings: SecurityOAuth2ProviderSettings = Field(default_factory=SecurityOAuth2ProviderSettings) oidc_providers: list[OIDCProvider] = Field(default_factory=list, description="The selected OIDC providers") + oidc_provider_settings: SecurityOIDCProviderSettings = Field(default_factory=SecurityOIDCProviderSettings) _oauth2_settings: dict[str, SecurityOAuth2Settings] = PrivateAttr(default_factory=dict) _oidc_settings: dict[str, SecurityOIDCSettings] = PrivateAttr(default_factory=dict) @@ -547,9 +565,21 @@ def check_oauth2_provider_settings(self) -> Self: Oauth2Provider.GOOGLE: SecurityOAuth2Google, } for oauth2_provider in self.oauth2_providers: - provider = mapped_providers[oauth2_provider]() - if isinstance(provider, SecurityOAuth2Settings): - self._oauth2_settings[oauth2_provider.value] = provider + match oauth2_provider: + case Oauth2Provider.GOOGLE: + if self.oauth2_provider_settings.google: + self._oauth2_settings[oauth2_provider.value] = self.oauth2_provider_settings.google + case Oauth2Provider.PROVIDER1: + if self.oauth2_provider_settings.provider1: + self._oauth2_settings[oauth2_provider.value] = self.oauth2_provider_settings.provider1 + case Oauth2Provider.PROVIDER2: + if self.oauth2_provider_settings.provider2: + self._oauth2_settings[oauth2_provider.value] = self.oauth2_provider_settings.provider2 + + if oauth2_provider.value not in self._oauth2_settings: + provider = mapped_providers[oauth2_provider]() + if isinstance(provider, SecurityOAuth2Settings): + self._oauth2_settings[oauth2_provider.value] = provider return self @@ -561,9 +591,21 @@ def check_oidc_provider_settings(self) -> Self: OIDCProvider.PROVIDER2: SecurityOIDCProvider2, } for oidc_provider in self.oidc_providers: - provider = mapped_providers[oidc_provider]() - if isinstance(provider, SecurityOIDCSettings): - self._oidc_settings[oidc_provider.value] = provider + match oidc_provider: + case OIDCProvider.GOOGLE: + if self.oidc_provider_settings.google: + self._oidc_settings[oidc_provider.value] = self.oidc_provider_settings.google + case OIDCProvider.PROVIDER1: + if self.oidc_provider_settings.provider1: + self._oidc_settings[oidc_provider.value] = self.oidc_provider_settings.provider1 + case OIDCProvider.PROVIDER2: + if self.oidc_provider_settings.provider2: + self._oidc_settings[oidc_provider.value] = self.oidc_provider_settings.provider2 + + if oidc_provider.value not in self._oidc_settings: + provider = mapped_providers[oidc_provider]() + if isinstance(provider, SecurityOIDCSettings): + self._oidc_settings[oidc_provider.value] = provider return self diff --git a/docs/docs/guides/sso.mdx b/docs/docs/guides/sso.mdx index 6ac4c558ad..d7c50cf45f 100644 --- a/docs/docs/guides/sso.mdx +++ b/docs/docs/guides/sso.mdx @@ -1,6 +1,9 @@ --- title: Configuring Single sign-on --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Configuring Single sign-on In Infrahub you can configure SSO using either Open ID Connect (OIDC) or can use OAuth2. @@ -40,15 +43,36 @@ Aside from the display label and icon all the other entries will be provided by An example of what the configuration could look like: -```bash -export INFRAHUB_OAUTH2_PROVIDER1_CLIENT_ID=infrahub-sso -export INFRAHUB_OAUTH2_PROVIDER1_CLIENT_SECRET=edPf4IaquQaqns7t3s95mLhKKYdwL1up -export INFRAHUB_OAUTH2_PROVIDER1_AUTHORIZATION_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/auth -export INFRAHUB_OAUTH2_PROVIDER1_TOKEN_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/token -export INFRAHUB_OAUTH2_PROVIDER1_USERINFO_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/userinfo -export INFRAHUB_OAUTH2_PROVIDER1_DISPLAY_LABEL="Internal Server (Keycloak)" -export INFRAHUB_OAUTH2_PROVIDER1_ICON="mdi:security-lock-outline" -``` + + + + ```bash + export INFRAHUB_OAUTH2_PROVIDER1_CLIENT_ID=infrahub-sso + export INFRAHUB_OAUTH2_PROVIDER1_CLIENT_SECRET=edPf4IaquQaqns7t3s95mLhKKYdwL1up + export INFRAHUB_OAUTH2_PROVIDER1_AUTHORIZATION_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/auth + export INFRAHUB_OAUTH2_PROVIDER1_TOKEN_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/token + export INFRAHUB_OAUTH2_PROVIDER1_USERINFO_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/userinfo + export INFRAHUB_OAUTH2_PROVIDER1_DISPLAY_LABEL="Internal Server (Keycloak)" + export INFRAHUB_OAUTH2_PROVIDER1_ICON="mdi:security-lock-outline" + ``` + + + + + ```toml + [security.oauth2_provider_settings.provider1] + client_id = "infrahub-sso" + client_secret = "edPf4IaquQaqns7t3s95mLhKKYdwL1up" + authorization_url = "http://localhost:8180/realms/infrahub/protocol/openid-connect/auth" + token_url = "http://localhost:8180/realms/infrahub/protocol/openid-connect/token" + userinfo_url = "http://localhost:8180/realms/infrahub/protocol/openid-connect/userinfo" + scopes = ["openid", "profile", "email"] + display_label = "Internal Server (Keycloak)" + icon = "mdi:security-lock-outline" + ``` + + + This could be the configuration of a Keycloak provider, please refer to the documentation of your intended provider for guides on how to create a client and access the required information. @@ -56,15 +80,43 @@ This could be the configuration of a Keycloak provider, please refer to the docu In order to activate the above provider we need to add it to the list of active OAuth2 providers. -```bash -export INFRAHUB_SECURITY_OAUTH2_PROVIDERS='["provider1"]' -``` + + + + ```bash + export INFRAHUB_SECURITY_OAUTH2_PROVIDERS='["provider1"]' + ``` + + + + + ```toml + [security] + oauth2_providers = ["provider1"] + ``` + + + Alternatively if you are setting up multiple providers each with their different settings: -```bash -export INFRAHUB_SECURITY_OAUTH2_PROVIDERS='["provider1","provider2"]' -``` + + + + ```bash + export INFRAHUB_SECURITY_OAUTH2_PROVIDERS='["provider1","provider2"]' + ``` + + + + + ```toml + [security] + oauth2_providers = ["provider1", "provider2"] + ``` + + + ## Setting up OIDC in Infrahub @@ -89,13 +141,31 @@ Aside from the display label and icon all the other entries will be provided by An example of what the configuration could look like: -```bash -export INFRAHUB_OIDC_PROVIDER1_CLIENT_ID=infrahub-sso -export INFRAHUB_OIDC_PROVIDER1_CLIENT_SECRET=edPf4IaquQaqns7t3s95mLhKKYdwL1up -export INFRAHUB_OIDC_PROVIDER1_DISCOVERY_URL=http://localhost:8180/realms/infrahub/.well-known/openid-configuration -export INFRAHUB_OIDC_PROVIDER1_DISPLAY_LABEL="Internal Server (Keycloak)" -export INFRAHUB_OIDC_PROVIDER1_ICON="mdi:security-lock-outline" -``` + + + + ```bash + export INFRAHUB_OIDC_PROVIDER1_CLIENT_ID=infrahub-sso + export INFRAHUB_OIDC_PROVIDER1_CLIENT_SECRET=edPf4IaquQaqns7t3s95mLhKKYdwL1up + export INFRAHUB_OIDC_PROVIDER1_DISCOVERY_URL=http://localhost:8180/realms/infrahub/.well-known/openid-configuration + export INFRAHUB_OIDC_PROVIDER1_DISPLAY_LABEL="Internal Server (Keycloak)" + export INFRAHUB_OIDC_PROVIDER1_ICON="mdi:security-lock-outline" + ``` + + + + + ```toml + [security.oidc_provider_settings.provider1] + client_id = "infrahub-sso" + client_secret = "edPf4IaquQaqns7t3s95mLhKKYdwL1up" + discovery_url = "http://localhost:8180/realms/infrahub/.well-known/openid-configuration" + display_label = "Internal Server (Keycloak)" + icon = "mdi:security-lock-outline" + ``` + + + This could be the configuration of a Keycloak provider, please refer to the documentation of your intended provider for guides on how to create a client and access the required information. @@ -103,15 +173,43 @@ This could be the configuration of a Keycloak provider, please refer to the docu In order to activate the above provider we need to add it to the list of active OIDC providers. -```bash -export INFRAHUB_SECURITY_OIDC_PROVIDERS='["provider1"]' -``` + + + + ```bash + export INFRAHUB_SECURITY_OIDC_PROVIDERS='["provider1"]' + ``` + + + + + ```toml + [security] + oidc_providers = ["provider1"] + ``` + + + Alternatively if you are setting up multiple providers each with their different settings: -```bash -export INFRAHUB_SECURITY_OIDC_PROVIDERS='["provider1","provider2"]' -``` + + + + ```bash + export INFRAHUB_SECURITY_OIDC_PROVIDERS='["provider1","provider2"]' + ``` + + + + + ```toml + [security] + oidc_providers = ["provider1", "provider2"] + ``` + + + ## Configuring the redirect URI in the identity provider