All checks were successful
continuous-integration/drone/push Build is passing
164 lines
5.0 KiB
Rust
164 lines
5.0 KiB
Rust
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);
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
pub enum AuthenticationFlow {
|
|
AuthorizationCode,
|
|
Implicit,
|
|
}
|
|
|
|
pub type AdditionalClaims = HashMap<String, Value>;
|
|
|
|
#[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<String>,
|
|
|
|
/// 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<AdditionalClaims>,
|
|
|
|
/// Additional claims to return through the user info endpoint
|
|
claims_user_info: Option<AdditionalClaims>,
|
|
}
|
|
|
|
impl PartialEq for Client {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.id.eq(&other.id)
|
|
}
|
|
}
|
|
|
|
impl Eq for Client {}
|
|
|
|
impl Client {
|
|
/// Get the client authentication flow
|
|
pub fn auth_flow(&self) -> AuthenticationFlow {
|
|
match self.secret {
|
|
None => AuthenticationFlow::Implicit,
|
|
Some(_) => AuthenticationFlow::AuthorizationCode,
|
|
}
|
|
}
|
|
|
|
/// 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<AdditionalClaims>,
|
|
) -> Option<AdditionalClaims> {
|
|
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<AdditionalClaims> {
|
|
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<AdditionalClaims> {
|
|
self.process_additional_claims(user, &self.claims_user_info)
|
|
}
|
|
}
|
|
|
|
pub type ClientManager = EntityManager<Client>;
|
|
|
|
impl EntityManager<Client> {
|
|
pub fn find_by_id(&self, u: &ClientID) -> Option<Client> {
|
|
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);
|
|
}
|
|
}
|
|
}
|