rustauth_plugins/admin/
options.rs1use 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}