Documentation / Authentication / Full Example & AdminPanel
Full Example & AdminPanel
Full Example — Login / Logout
use runique::prelude::*;
// LoginForm — declared separately, .no_hash() is required on the password field
#[derive(Serialize, Debug, Clone)]
#[serde(transparent)]
pub struct LoginForm {
pub form: Forms,
}
impl FormAccess for LoginForm {
fn register_fields(form: &mut Forms) {
form.field(&TextField::text("username").label("Username").required());
form.field(&TextField::password("password").label("Password").no_hash().required());
}
impl_form_access!();
}
impl RuniqueForm for LoginForm {}
pub async fn login_post(mut request: Request) -> AppResult<Response> {
let form: LoginForm = request.form("my_form");
let validated = match ValidationForm::try_form(form, &request).await {
Ok(validated) => validated,
Err(form) => {
context_update!(request => { "login_form" => &form });
return request.render("login.html");
}
};
let db = request.engine.db.clone();
let username = validated.cleaned::<String>("username").unwrap_or_default();
let password = validated.cleaned::<String>("password").unwrap_or_default();
// 1. Check the credentials: the password is verified even for an unknown name
// (no leak through response time), account active and activated.
// 2. Open the session — session id and CSRF token renewed.
if let Some(user) = authenticate_user(&db, username.trim(), &password).await
&& login(&request.session, &user, None, false).await.is_ok()
{
return Ok(Redirect::to("/dashboard").into_response());
}
// Invalid credentials (generic message — don't distinguish unknown user / wrong password)
let form = validated.into_form();
context_update!(request => {
"login_form" => &form,
"messages" => flash_now!(error => "Invalid credentials"),
});
request.render("login.html")
}
// POST-only route: "/logout" => post(views::logout_view), name = "logout"
pub async fn logout_view(request: Request) -> AppResult<Response> {
// This view reads no form: it checks the CSRF token itself.
if !request.prisme.csrf_valid {
return Ok(StatusCode::FORBIDDEN.into_response());
}
// An error here means the session couldn't be closed: it is still
// alive, so the user gets an error page.
logout(&request.session, None).await?;
Ok(Redirect::to("/login").into_response())
}
The sign-out form, in the template:
<form method="post" action='{% link "logout" %}'>
{% csrf %}
<button type="submit">Sign out</button>
</form>
A sign-out link (GET) could be triggered by another site (an image, a link): it must go through a POST with its CSRF token.
Authentication for the AdminPanel
Nothing to configure: the admin signs in the active, activated accounts of eihwaz_users of
rank admin, dirigeant or superuser, or members of a group that opens the admin. To add fields to the user model, use extend!{ table: "eihwaz_users", ... }.
To connect authentication to the admin panel, see also 11-Admin.md.