openauth-plugins 0.0.4

Official OpenAuth plugin modules.
Documentation
use std::collections::BTreeMap;

use serde_json::json;

use super::access::{default_roles, Role};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdminOptions {
    pub default_role: String,
    pub admin_roles: Vec<String>,
    pub default_ban_reason: Option<String>,
    pub default_ban_expires_in: Option<i64>,
    pub impersonation_session_duration: i64,
    pub roles: BTreeMap<String, Role>,
    pub admin_user_ids: Vec<String>,
    pub banned_user_message: String,
    pub allow_impersonating_admins: bool,
    pub schema: AdminSchemaOptions,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdminSchemaOptions {
    pub user_role_field: String,
    pub user_banned_field: String,
    pub user_ban_reason_field: String,
    pub user_ban_expires_field: String,
    pub session_impersonated_by_field: String,
}

impl Default for AdminOptions {
    fn default() -> Self {
        Self {
            default_role: "user".to_owned(),
            admin_roles: vec!["admin".to_owned()],
            default_ban_reason: None,
            default_ban_expires_in: None,
            impersonation_session_duration: 60 * 60,
            roles: default_roles(),
            admin_user_ids: Vec::new(),
            banned_user_message: "You have been banned from this application. Please contact support if you believe this is an error.".to_owned(),
            allow_impersonating_admins: false,
            schema: AdminSchemaOptions::default(),
        }
    }
}

impl Default for AdminSchemaOptions {
    fn default() -> Self {
        Self {
            user_role_field: "role".to_owned(),
            user_banned_field: "banned".to_owned(),
            user_ban_reason_field: "ban_reason".to_owned(),
            user_ban_expires_field: "ban_expires".to_owned(),
            session_impersonated_by_field: "impersonated_by".to_owned(),
        }
    }
}

impl AdminOptions {
    pub fn with_defaults(mut self) -> Self {
        if self.default_role.trim().is_empty() {
            self.default_role = "user".to_owned();
        }
        if self.admin_roles.is_empty() {
            self.admin_roles = vec!["admin".to_owned()];
        }
        if self.impersonation_session_duration <= 0 {
            self.impersonation_session_duration = 60 * 60;
        }
        if self.roles.is_empty() {
            self.roles = default_roles();
        }
        if self.banned_user_message.trim().is_empty() {
            self.banned_user_message = Self::default().banned_user_message;
        }
        self.schema = self.schema.with_defaults();
        self
    }

    pub fn validate(&self) -> Result<(), String> {
        for role in &self.admin_roles {
            if !self
                .roles
                .keys()
                .any(|candidate: &String| candidate.eq_ignore_ascii_case(role))
            {
                return Err(format!(
                    "Invalid admin role `{role}`. Admin roles must be defined in roles."
                ));
            }
        }
        Ok(())
    }

    pub fn to_json(&self) -> serde_json::Value {
        json!({
            "defaultRole": self.default_role,
            "adminRoles": self.admin_roles,
            "adminUserIds": self.admin_user_ids,
            "bannedUserMessage": self.banned_user_message,
            "allowImpersonatingAdmins": self.allow_impersonating_admins,
        })
    }
}

impl AdminSchemaOptions {
    fn with_defaults(mut self) -> Self {
        let defaults = Self::default();
        if self.user_role_field.trim().is_empty() {
            self.user_role_field = defaults.user_role_field;
        }
        if self.user_banned_field.trim().is_empty() {
            self.user_banned_field = defaults.user_banned_field;
        }
        if self.user_ban_reason_field.trim().is_empty() {
            self.user_ban_reason_field = defaults.user_ban_reason_field;
        }
        if self.user_ban_expires_field.trim().is_empty() {
            self.user_ban_expires_field = defaults.user_ban_expires_field;
        }
        if self.session_impersonated_by_field.trim().is_empty() {
            self.session_impersonated_by_field = defaults.session_impersonated_by_field;
        }
        self
    }
}