admin/security: show flash, confirm action with modal

This commit is contained in:
Philippe Loctaux 2023-04-07 17:46:39 +02:00
parent 19840b31e6
commit 11e2894621
2 changed files with 137 additions and 71 deletions

View file

@ -72,10 +72,15 @@ pub async fn settings_update_branding(
} }
#[get("/admin/settings/security")] #[get("/admin/settings/security")]
pub async fn settings_security(admin: JwtAdmin) -> Result<Page> { pub async fn settings_security(
Ok(Page::AdminSettingsSecurity( admin: JwtAdmin,
super::content::AdminSettingsSecurity { user: admin.0 }, flash: Option<FlashMessage<'_>>,
)) ) -> Result<Template> {
let page = Page::AdminSettingsSecurity(super::content::AdminSettingsSecurity { user: admin.0 });
Ok(flash
.map(|flash| Page::with_flash(page.clone(), flash))
.unwrap_or_else(|| page.into()))
} }
#[derive(Debug, FromForm)] #[derive(Debug, FromForm)]
@ -90,81 +95,93 @@ pub async fn settings_security_form(
form: Form<SecurityForm>, form: Form<SecurityForm>,
ip_address: IpAddr, ip_address: IpAddr,
cookie_jar: &CookieJar<'_>, cookie_jar: &CookieJar<'_>,
) -> Result<Redirect> { ) -> Result<Flash<Redirect>> {
let mut transaction = db.begin().await?; let mut transaction = db.begin().await?;
if let Some(logout_everyone) = form.logout_everyone { let (flash_kind, flash_message) = match form.logout_everyone {
if logout_everyone { Some(logout_everyone) => {
// Generate key id if logout_everyone {
let key_id = task::spawn_blocking(id::KeyID::default).await?; // Generate key id
let key_id = task::spawn_blocking(id::KeyID::default).await?;
// Generate key // Generate key
let key_id_for_generation = key_id.clone(); let key_id_for_generation = key_id.clone();
let (private_key, public_key) = let (private_key, public_key) =
task::spawn_blocking(move || jwt::generate(&key_id_for_generation)).await??; task::spawn_blocking(move || jwt::generate(&key_id_for_generation)).await??;
// Insert keys in database // Insert keys in database
jwt::database::save_new_keys(&mut transaction, &key_id, &private_key, &public_key) jwt::database::save_new_keys(&mut transaction, &key_id, &private_key, &public_key)
.await?; .await?;
// Revoke all keys except new one // Revoke all keys except new one
jwt::database::revoke_all_except_one(&mut transaction, &key_id).await?; jwt::database::revoke_all_except_one(&mut transaction, &key_id).await?;
// Get app // Get app
let app = App::get_one_by_id(&mut transaction, "ezidam") let app = App::get_one_by_id(&mut transaction, "ezidam")
.await? .await?
.ok_or_else(|| Error::not_found("Could not find application"))?; .ok_or_else(|| Error::not_found("Could not find application"))?;
// Get user info // Get user info
let user = User::get_by_login(&mut transaction, &admin.0.subject) let user = User::get_by_login(&mut transaction, &admin.0.subject)
.await? .await?
.ok_or_else(|| Error::not_found("Could not find user"))?; .ok_or_else(|| Error::not_found("Could not find user"))?;
// Revoke all refresh tokens // Revoke all refresh tokens
RefreshToken::revoke_all(&mut transaction).await?; RefreshToken::revoke_all(&mut transaction).await?;
// Generate refresh token // Generate refresh token
let refresh_token = let refresh_token =
generate_refresh_token(&mut transaction, ip_address, user.id(), app.id()) generate_refresh_token(&mut transaction, ip_address, user.id(), app.id())
.await .await
.map_err(Error::internal_server_error)?; .map_err(Error::internal_server_error)?;
// Add refresh token as a cookie // Add refresh token as a cookie
let mut cookie = Cookie::new(REFRESH_TOKEN_COOKIE_NAME, refresh_token); let mut cookie = Cookie::new(REFRESH_TOKEN_COOKIE_NAME, refresh_token);
cookie.set_secure(true); cookie.set_secure(true);
cookie.set_http_only(true); cookie.set_http_only(true);
cookie.set_same_site(SameSite::Strict); cookie.set_same_site(SameSite::Strict);
cookie.set_max_age(Duration::days(REFRESH_TOKEN_DURATION_DAYS)); cookie.set_max_age(Duration::days(REFRESH_TOKEN_DURATION_DAYS));
cookie_jar.add(cookie); cookie_jar.add(cookie);
// Get base url // Get base url
let settings = Settings::get(&mut transaction).await?; let settings = Settings::get(&mut transaction).await?;
let home_page = settings let home_page = settings
.url() .url()
.map(String::from) .map(String::from)
.ok_or_else(|| Error::bad_request("Server url is not set"))?; .ok_or_else(|| Error::bad_request("Server url is not set"))?;
// Generate jwt // Generate jwt
let jwt = generate_jwt( let jwt = generate_jwt(
&mut transaction, &mut transaction,
&private_key, &private_key,
&home_page, &home_page,
&app.id().0, &app.id().0,
&user, &user,
) )
.await .await
.map_err(Error::internal_server_error)?; .map_err(Error::internal_server_error)?;
// Add jwt as a cookie // Add jwt as a cookie
let mut cookie = Cookie::new(JWT_COOKIE_NAME, jwt); let mut cookie = Cookie::new(JWT_COOKIE_NAME, jwt);
cookie.set_secure(true); cookie.set_secure(true);
cookie.set_http_only(true); cookie.set_http_only(true);
cookie.set_same_site(SameSite::Strict); cookie.set_same_site(SameSite::Strict);
cookie.set_max_age(Duration::minutes(JWT_DURATION_MINUTES)); cookie.set_max_age(Duration::minutes(JWT_DURATION_MINUTES));
cookie_jar.add(cookie); cookie_jar.add(cookie);
(FlashKind::Success, "Everyone has been logged out.")
} else {
(FlashKind::Warning, "Nothing to do.")
}
} }
} None => (FlashKind::Warning, "Nothing to do."),
};
transaction.commit().await?; transaction.commit().await?;
Ok(Redirect::to(uri!(settings_security))) Ok(Flash::new(
Redirect::to(uri!(settings_security)),
flash_kind,
flash_message,
))
} }

View file

@ -16,6 +16,13 @@
<!-- Page body --> <!-- Page body -->
<div class="page-body"> <div class="page-body">
<div class="container-xl"> <div class="container-xl">
{% if flash %}
<div class="alert alert-{{flash.0}}" role="alert">
<h4 class="alert-title">{{ flash.1 | safe }}</h4>
</div>
{% endif %}
<div class="card"> <div class="card">
<div class="row g-0"> <div class="row g-0">
<div class="col-3 d-none d-md-block border-end"> <div class="col-3 d-none d-md-block border-end">
@ -39,15 +46,13 @@
</p> </p>
<p>This might take some time, but should not be long.</p> <p>This might take some time, but should not be long.</p>
<div class="row align-items-center"> <div class="row align-items-center mb-4">
<!-- Logout everyone --> <!-- Logout everyone -->
<div class="col-auto"> <div class="col-auto">
<form action="" method="post"> <a class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#modal-logout-confirm">
<button class="btn btn-danger" type="submit" name="logout_everyone" value="true"> Logout everyone
Logout everyone </a>
</button>
</form>
</div> </div>
</div> </div>
@ -57,4 +62,48 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Logout everyone modal -->
<div class="modal modal-blur" tabindex="-1" id="modal-logout-confirm">
<div class="modal-dialog modal-sm modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-status bg-danger"></div>
<div class="modal-body text-center py-4">
<div class="text-danger mb-2">
{% include "icons/alert-triangle-large" %}
</div>
<h3>Do you want to log out everyone?</h3>
<div class="mt-2">All users will be logged out.</div>
<div class="mt-2">This might take some time, but should not be long.</div>
</div>
<div class="modal-footer">
<div class="w-100">
<div class="row">
<div class="col">
<a href="#" class="btn w-100" data-bs-dismiss="modal">Cancel</a>
</div>
<div class="col">
<form action="" method="post">
<button type="submit" name="logout_everyone" value="true" class="btn btn-danger w-100">
Logout everyone
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %} {% endblock content %}