Find identity providers by brand name when unique.
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
@@ -2525,8 +2525,13 @@ pub struct IdentityProvider {
|
|||||||
/// for your convenience. For certain brands we apply essential internal
|
/// for your convenience. For certain brands we apply essential internal
|
||||||
/// workarounds specific to that provider; it is important to configure this
|
/// workarounds specific to that provider; it is important to configure this
|
||||||
/// field properly when a provider needs to be recognized (like GitHub for
|
/// field properly when a provider needs to be recognized (like GitHub for
|
||||||
/// example). Several configured providers can share the same brand name. It
|
/// example).
|
||||||
/// is not case-sensitive.
|
///
|
||||||
|
/// Several configured providers can share the same brand name. It is not
|
||||||
|
/// case-sensitive. As a convenience for common simple deployments we can
|
||||||
|
/// identify this provider by brand in addition to the unique `client_id` if
|
||||||
|
/// and only if there is a single provider for the brand; see notes for
|
||||||
|
/// `client_id`.
|
||||||
#[serde(deserialize_with = "utils::string::de::to_lowercase")]
|
#[serde(deserialize_with = "utils::string::de::to_lowercase")]
|
||||||
pub brand: String,
|
pub brand: String,
|
||||||
|
|
||||||
@@ -2534,6 +2539,11 @@ pub struct IdentityProvider {
|
|||||||
/// registration. This ID then uniquely identifies this configuration
|
/// registration. This ID then uniquely identifies this configuration
|
||||||
/// instance itself, becoming the identity provider's ID and must be unique
|
/// instance itself, becoming the identity provider's ID and must be unique
|
||||||
/// and remain unchanged.
|
/// and remain unchanged.
|
||||||
|
///
|
||||||
|
/// As a convenience we also identify this config by `brand` if and only if
|
||||||
|
/// there is a single provider configured for a `brand`. Note carefully that
|
||||||
|
/// multiple providers configured with the same `brand` is not an error and
|
||||||
|
/// this provider will simply not be found when querying by `brand`.
|
||||||
pub client_id: String,
|
pub client_id: String,
|
||||||
|
|
||||||
/// Secret key the provider generated for you along with the `client_id`
|
/// Secret key the provider generated for you along with the `client_id`
|
||||||
|
|||||||
@@ -28,33 +28,80 @@ pub(super) fn build(args: &crate::Args<'_>) -> Self {
|
|||||||
#[implement(Providers)]
|
#[implement(Providers)]
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
pub async fn get(&self, id: &str) -> Result<Provider> {
|
pub async fn get(&self, id: &str) -> Result<Provider> {
|
||||||
if let Some(provider) = self.providers.read().await.get(id).cloned() {
|
if let Some(provider) = self.get_cached(id).await {
|
||||||
return Ok(provider);
|
return Ok(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = self.get_config(id)?;
|
let config = self.get_config(id)?;
|
||||||
|
let id = config.id().to_owned();
|
||||||
let mut map = self.providers.write().await;
|
let mut map = self.providers.write().await;
|
||||||
let config = self.configure(config).await?;
|
let provider = self.configure(config).await?;
|
||||||
|
|
||||||
debug!(?id, ?config);
|
debug!(?id, ?provider);
|
||||||
_ = map.insert(id.into(), config.clone());
|
_ = map.insert(id, provider.clone());
|
||||||
|
|
||||||
Ok(config)
|
Ok(provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the admin-configured Provider which exists prior to any
|
/// Get the admin-configured Provider which exists prior to any
|
||||||
/// reconciliation with the well-known discovery (the server's config is
|
/// reconciliation with the well-known discovery (the server's config is
|
||||||
/// immutable); though it is important to note the server config can be
|
/// immutable); though it is important to note the server config can be
|
||||||
/// reloaded. This will Err NotFound for a non-existent idp.
|
/// reloaded. This will Err NotFound for a non-existent idp.
|
||||||
|
///
|
||||||
|
/// When no provider is found with a matching client_id, providers are then
|
||||||
|
/// searched by brand. Brand matching will be invalidated when more than one
|
||||||
|
/// provider matches the brand.
|
||||||
#[implement(Providers)]
|
#[implement(Providers)]
|
||||||
pub fn get_config(&self, id: &str) -> Result<Provider> {
|
pub fn get_config(&self, id: &str) -> Result<Provider> {
|
||||||
self.services
|
let providers = &self.services.config.identity_provider;
|
||||||
.config
|
|
||||||
.identity_provider
|
if let Some(provider) = providers
|
||||||
.iter()
|
.iter()
|
||||||
.find(|config| config.id() == id)
|
.find(|config| config.id() == id)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| err!(Request(NotFound("Unrecognized Identity Provider"))))
|
{
|
||||||
|
return Ok(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(provider) = providers
|
||||||
|
.iter()
|
||||||
|
.find(|config| config.brand == id.to_lowercase())
|
||||||
|
.filter(|_| {
|
||||||
|
providers
|
||||||
|
.iter()
|
||||||
|
.filter(|config| config.brand == id.to_lowercase())
|
||||||
|
.count()
|
||||||
|
.eq(&1)
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
|
return Ok(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err!(Request(NotFound("Unrecognized Identity Provider")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the discovered provider from the runtime cache. ID may be client_id or
|
||||||
|
/// brand if brand is unique among provider configurations.
|
||||||
|
#[implement(Providers)]
|
||||||
|
async fn get_cached(&self, id: &str) -> Option<Provider> {
|
||||||
|
let providers = self.providers.read().await;
|
||||||
|
|
||||||
|
if let Some(provider) = providers.get(id).cloned() {
|
||||||
|
return Some(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
providers
|
||||||
|
.values()
|
||||||
|
.find(|provider| provider.brand == id.to_lowercase())
|
||||||
|
.filter(|_| {
|
||||||
|
providers
|
||||||
|
.values()
|
||||||
|
.filter(|provider| provider.brand == id.to_lowercase())
|
||||||
|
.count()
|
||||||
|
.eq(&1)
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure an identity provider; takes the admin-configured instance from the
|
/// Configure an identity provider; takes the admin-configured instance from the
|
||||||
|
|||||||
@@ -2141,8 +2141,13 @@
|
|||||||
# for your convenience. For certain brands we apply essential internal
|
# for your convenience. For certain brands we apply essential internal
|
||||||
# workarounds specific to that provider; it is important to configure this
|
# workarounds specific to that provider; it is important to configure this
|
||||||
# field properly when a provider needs to be recognized (like GitHub for
|
# field properly when a provider needs to be recognized (like GitHub for
|
||||||
# example). Several configured providers can share the same brand name. It
|
# example).
|
||||||
# is not case-sensitive.
|
#
|
||||||
|
# Several configured providers can share the same brand name. It is not
|
||||||
|
# case-sensitive. As a convenience for common simple deployments we can
|
||||||
|
# identify this provider by brand in addition to the unique `client_id` if
|
||||||
|
# and only if there is a single provider for the brand; see notes for
|
||||||
|
# `client_id`.
|
||||||
#
|
#
|
||||||
#brand =
|
#brand =
|
||||||
|
|
||||||
@@ -2151,6 +2156,11 @@
|
|||||||
# instance itself, becoming the identity provider's ID and must be unique
|
# instance itself, becoming the identity provider's ID and must be unique
|
||||||
# and remain unchanged.
|
# and remain unchanged.
|
||||||
#
|
#
|
||||||
|
# As a convenience we also identify this config by `brand` if and only if
|
||||||
|
# there is a single provider configured for a `brand`. Note carefully that
|
||||||
|
# multiple providers configured with the same `brand` is not an error and
|
||||||
|
# this provider will simply not be found when querying by `brand`.
|
||||||
|
#
|
||||||
#client_id =
|
#client_id =
|
||||||
|
|
||||||
# Secret key the provider generated for you along with the `client_id`
|
# Secret key the provider generated for you along with the `client_id`
|
||||||
|
|||||||
Reference in New Issue
Block a user