use crate::data::entity_manager::EntityManager; use crate::data::user::User; use crate::utils::string_utils::apply_env_vars; use serde_json::Value; use std::collections::HashMap; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)] pub struct ClientID(pub String); pub type AdditionalClaims = HashMap; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct Client { /// The ID of the client pub id: ClientID, /// The human-readable name of the client pub name: String, /// A short description of the service provided by the client pub description: String, /// The secret used by the client to retrieve authenticated users information /// This value is absent if implicit authentication flow is used pub secret: Option, /// The URI where the users should be redirected once authenticated pub redirect_uri: String, /// Specify if the client must be allowed by default for new account #[serde(default = "bool::default")] pub default: bool, /// Specify whether a client is granted to all users #[serde(default = "bool::default")] pub granted_to_all_users: bool, /// Specify whether recent Second Factor Authentication is required to access this client #[serde(default = "bool::default")] pub enforce_2fa_auth: bool, /// Additional claims to return with the id token claims_id_token: Option, /// Additional claims to return through the user info endpoint claims_user_info: Option, } impl PartialEq for Client { fn eq(&self, other: &Self) -> bool { self.id.eq(&other.id) } } impl Eq for Client {} impl Client { /// Check if the client has a secret defined pub fn has_secret(&self) -> bool { self.secret.is_some() } /// Process a single claim value fn process_claim_string(&self, user: &User, str: &str) -> String { str.replace("{username}", &user.username) .replace("{mail}", &user.email) .replace("{first_name}", &user.first_name) .replace("{last_name}", &user.last_name) .replace("{uid}", &user.uid.0) } /// Recurse claims processing fn recurse_claims_processing(&self, user: &User, value: &Value) -> Value { match value { Value::String(s) => Value::String(self.process_claim_string(user, s)), Value::Array(arr) => Value::Array( arr.iter() .map(|v| self.recurse_claims_processing(user, v)) .collect(), ), Value::Object(obj) => obj .iter() .map(|(k, v)| { ( self.process_claim_string(user, k), self.recurse_claims_processing(user, v), ) }) .collect(), v => v.clone(), } } /// Process additional claims, processing placeholders fn process_additional_claims( &self, user: &User, claims: &Option, ) -> Option { let claims = claims.as_ref()?; let res = claims .iter() .map(|(k, v)| { ( self.process_claim_string(user, k), self.recurse_claims_processing(user, v), ) }) .collect(); Some(res) } /// Get additional claims for id_token for a successful authentication pub fn claims_id_token(&self, user: &User) -> Option { self.process_additional_claims(user, &self.claims_id_token) } /// Get additional claims for user info for a successful authentication pub fn claims_user_info(&self, user: &User) -> Option { self.process_additional_claims(user, &self.claims_user_info) } } pub type ClientManager = EntityManager; impl EntityManager { pub fn find_by_id(&self, u: &ClientID) -> Option { for entry in self.iter() { if entry.id.eq(u) { return Some(entry.clone()); } } None } /// Get the list of default clients. /// /// i.e. the clients that are granted to new accounts by default pub fn get_default_clients(&self) -> Vec<&Client> { self.iter().filter(|u| u.default).collect() } pub fn apply_environment_variables(&mut self) { for c in self.iter_mut() { c.id = ClientID(apply_env_vars(&c.id.0)); c.name = apply_env_vars(&c.name); c.description = apply_env_vars(&c.description); c.secret = c.secret.as_deref().map(apply_env_vars); c.redirect_uri = apply_env_vars(&c.redirect_uri); } } }