Skip to main content

rustauth_plugins/admin/
options.rs

1use std::collections::BTreeMap;
2
3use serde_json::json;
4use time::Duration;
5
6use super::access::{default_roles, AdminRole};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct AdminOptions {
10    pub default_role: String,
11    pub admin_roles: Vec<String>,
12    pub default_ban_reason: Option<String>,
13    pub default_ban_expires_in: Option<Duration>,
14    pub impersonation_session_duration: Duration,
15    pub roles: BTreeMap<String, AdminRole>,
16    pub admin_user_ids: Vec<String>,
17    pub banned_user_message: String,
18    pub allow_impersonating_admins: bool,
19    pub schema: AdminSchemaOptions,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct AdminSchemaOptions {
24    pub user_role_field: String,
25    pub user_banned_field: String,
26    pub user_ban_reason_field: String,
27    pub user_ban_expires_field: String,
28    pub session_impersonated_by_field: String,
29}
30
31impl Default for AdminOptions {
32    fn default() -> Self {
33        Self {
34            default_role: "user".to_owned(),
35            admin_roles: vec!["admin".to_owned()],
36            default_ban_reason: None,
37            default_ban_expires_in: None,
38            impersonation_session_duration: Duration::hours(1),
39            roles: default_roles(),
40            admin_user_ids: Vec::new(),
41            banned_user_message: "You have been banned from this application. Please contact support if you believe this is an error.".to_owned(),
42            allow_impersonating_admins: false,
43            schema: AdminSchemaOptions::default(),
44        }
45    }
46}
47
48impl Default for AdminSchemaOptions {
49    fn default() -> Self {
50        Self {
51            user_role_field: "role".to_owned(),
52            user_banned_field: "banned".to_owned(),
53            user_ban_reason_field: "ban_reason".to_owned(),
54            user_ban_expires_field: "ban_expires".to_owned(),
55            session_impersonated_by_field: "impersonated_by".to_owned(),
56        }
57    }
58}
59
60impl AdminOptions {
61    #[must_use]
62    pub fn builder() -> AdminOptionsBuilder {
63        AdminOptionsBuilder::default()
64    }
65
66    pub fn with_defaults(mut self) -> Self {
67        if self.default_role.trim().is_empty() {
68            self.default_role = "user".to_owned();
69        }
70        if self.admin_roles.is_empty() {
71            self.admin_roles = vec!["admin".to_owned()];
72        }
73        if self.impersonation_session_duration.is_zero() {
74            self.impersonation_session_duration = Duration::hours(1);
75        }
76        if self.roles.is_empty() {
77            self.roles = default_roles();
78        }
79        if self.banned_user_message.trim().is_empty() {
80            self.banned_user_message = Self::default().banned_user_message;
81        }
82        self.schema = self.schema.with_defaults();
83        self
84    }
85
86    pub fn validate(&self) -> Result<(), String> {
87        for role in &self.admin_roles {
88            if !self
89                .roles
90                .keys()
91                .any(|candidate: &String| candidate.eq_ignore_ascii_case(role))
92            {
93                return Err(format!(
94                    "Invalid admin role `{role}`. Admin roles must be defined in roles."
95                ));
96            }
97        }
98        Ok(())
99    }
100
101    pub fn to_json(&self) -> serde_json::Value {
102        json!({
103            "defaultRole": self.default_role,
104            "adminRoles": self.admin_roles,
105            "adminUserIds": self.admin_user_ids,
106            "bannedUserMessage": self.banned_user_message,
107            "allowImpersonatingAdmins": self.allow_impersonating_admins,
108        })
109    }
110}
111
112#[derive(Clone, Default)]
113pub struct AdminOptionsBuilder {
114    default_role: Option<String>,
115    admin_roles: Option<Vec<String>>,
116    default_ban_reason: Option<Option<String>>,
117    default_ban_expires_in: Option<Option<Duration>>,
118    impersonation_session_duration: Option<Duration>,
119    roles: Option<BTreeMap<String, AdminRole>>,
120    admin_user_ids: Option<Vec<String>>,
121    banned_user_message: Option<String>,
122    allow_impersonating_admins: Option<bool>,
123    schema: Option<AdminSchemaOptions>,
124}
125
126impl AdminOptionsBuilder {
127    #[must_use]
128    pub fn default_role(mut self, default_role: impl Into<String>) -> Self {
129        self.default_role = Some(default_role.into());
130        self
131    }
132
133    #[must_use]
134    pub fn admin_roles(mut self, admin_roles: Vec<String>) -> Self {
135        self.admin_roles = Some(admin_roles);
136        self
137    }
138
139    #[must_use]
140    pub fn admin_role(mut self, admin_role: impl Into<String>) -> Self {
141        self.admin_roles = Some(vec![admin_role.into()]);
142        self
143    }
144
145    pub fn build(self) -> Result<AdminOptions, rustauth_core::error::RustAuthError> {
146        let defaults = AdminOptions::default();
147        let options = AdminOptions {
148            default_role: self.default_role.unwrap_or(defaults.default_role),
149            admin_roles: self.admin_roles.unwrap_or(defaults.admin_roles),
150            default_ban_reason: self
151                .default_ban_reason
152                .unwrap_or(defaults.default_ban_reason),
153            default_ban_expires_in: self
154                .default_ban_expires_in
155                .unwrap_or(defaults.default_ban_expires_in),
156            impersonation_session_duration: self
157                .impersonation_session_duration
158                .unwrap_or(defaults.impersonation_session_duration),
159            roles: self.roles.unwrap_or(defaults.roles),
160            admin_user_ids: self.admin_user_ids.unwrap_or(defaults.admin_user_ids),
161            banned_user_message: self
162                .banned_user_message
163                .unwrap_or(defaults.banned_user_message),
164            allow_impersonating_admins: self
165                .allow_impersonating_admins
166                .unwrap_or(defaults.allow_impersonating_admins),
167            schema: self.schema.unwrap_or(defaults.schema),
168        }
169        .with_defaults();
170        options
171            .validate()
172            .map_err(rustauth_core::error::RustAuthError::InvalidConfig)?;
173        Ok(options)
174    }
175}
176
177impl AdminSchemaOptions {
178    fn with_defaults(mut self) -> Self {
179        let defaults = Self::default();
180        if self.user_role_field.trim().is_empty() {
181            self.user_role_field = defaults.user_role_field;
182        }
183        if self.user_banned_field.trim().is_empty() {
184            self.user_banned_field = defaults.user_banned_field;
185        }
186        if self.user_ban_reason_field.trim().is_empty() {
187            self.user_ban_reason_field = defaults.user_ban_reason_field;
188        }
189        if self.user_ban_expires_field.trim().is_empty() {
190            self.user_ban_expires_field = defaults.user_ban_expires_field;
191        }
192        if self.session_impersonated_by_field.trim().is_empty() {
193            self.session_impersonated_by_field = defaults.session_impersonated_by_field;
194        }
195        self
196    }
197}