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}