Skip to main content

auth_password/
admin.rs

1use crate::config::AuthPasswordConfig;
2use crate::module::RESET_PASSWORD_ACTION;
3use crate::repositories::PasswordAuthRepository;
4use auth::public::AuthUserId;
5use platform_core::{AppContext, AppError, AppResult, ErrorCode};
6use platform_module::AdminActionSource;
7use serde_json::Value;
8
9#[derive(Debug, Clone)]
10pub struct AuthPasswordAdminActions {
11    ctx: AppContext,
12    repository: PasswordAuthRepository,
13}
14
15impl AuthPasswordAdminActions {
16    #[must_use]
17    pub fn new(ctx: AppContext) -> Self {
18        Self {
19            repository: PasswordAuthRepository::new(ctx.db.clone()),
20            ctx,
21        }
22    }
23}
24
25#[async_trait::async_trait]
26impl AdminActionSource for AuthPasswordAdminActions {
27    async fn invoke(&self, action: &str, input: Value) -> AppResult<Value> {
28        match action {
29            RESET_PASSWORD_ACTION => {
30                let user_id = AuthUserId(required_string(&input, "user_id")?.to_owned());
31                let new_password = required_string(&input, "new_password")?;
32                let config = AuthPasswordConfig::from_context(&self.ctx)?;
33                let updated = self
34                    .repository
35                    .reset_password(&user_id, new_password, self.ctx.clock.now(), &config)
36                    .await?;
37                if !updated {
38                    return Err(AppError::new(
39                        ErrorCode::NotFound,
40                        "password credential not found for user",
41                    ));
42                }
43                Ok(serde_json::json!({
44                    "reset": true,
45                    "user_id": user_id.0,
46                }))
47            }
48            other => Err(AppError::new(
49                ErrorCode::NotFound,
50                format!("Unknown auth-password admin action `{other}`"),
51            )),
52        }
53    }
54}
55
56fn required_string<'a>(input: &'a Value, name: &str) -> AppResult<&'a str> {
57    input
58        .get(name)
59        .and_then(Value::as_str)
60        .map(str::trim)
61        .filter(|value| !value.is_empty())
62        .ok_or_else(|| AppError::new(ErrorCode::Validation, format!("{name} is required")))
63}