Approve access requests from the dashboard with an emailed temporary password
Each open access request on /dashboard/users gains an Approve form with a suggested username. Approving creates a user-role account with a 20-character random temporary password, emails it to the requester with the sign-in link, and marks the request done; if the email fails the account is deleted so the admin can retry. Approval refuses when mail is not configured. Migration 0008 adds users.must_change_password. A middleware on the signed-in routers sends flagged users to /account?change=1 until they set a new password; login honours the flag regardless of `next`, and the CLI's `users passwd` clears it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVPagF6jfDv78CC5Jv2wp4
This commit is contained in:
+16
@@ -26,6 +26,8 @@ enum Transport {
|
||||
Smtp(AsyncSmtpTransport<Tokio1Executor>),
|
||||
#[cfg(test)]
|
||||
Recording(Arc<std::sync::Mutex<Vec<Message>>>),
|
||||
#[cfg(test)]
|
||||
Failing,
|
||||
}
|
||||
|
||||
/// A reusable SMTP sender built from the startup configuration.
|
||||
@@ -78,6 +80,10 @@ impl Mailer {
|
||||
.push(message);
|
||||
return Ok(());
|
||||
}
|
||||
#[cfg(test)]
|
||||
if let Transport::Failing = &self.transport {
|
||||
anyhow::bail!("test mail delivery failed");
|
||||
}
|
||||
|
||||
let to = parse_mailbox(&message.to, "message recipient")?;
|
||||
let email = lettre::Message::builder()
|
||||
@@ -96,6 +102,8 @@ impl Mailer {
|
||||
}
|
||||
#[cfg(test)]
|
||||
Transport::Recording(_) => unreachable!("recording transport returned above"),
|
||||
#[cfg(test)]
|
||||
Transport::Failing => unreachable!("failing transport returned above"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -111,6 +119,14 @@ impl Mailer {
|
||||
messages,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn failing() -> Self {
|
||||
Self {
|
||||
from: "daily@example.com".parse().expect("valid test mailbox"),
|
||||
transport: Transport::Failing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_mailbox(value: &str, field: &str) -> anyhow::Result<Mailbox> {
|
||||
|
||||
Reference in New Issue
Block a user