ezidam: admin settings: added security page: logout everyone
This commit is contained in:
parent
d3e88b1f7b
commit
8ab88ae4e1
5 changed files with 177 additions and 5 deletions
|
|
@ -37,10 +37,16 @@ impl AdminMenu {
|
||||||
label: "Server settings",
|
label: "Server settings",
|
||||||
link: uri!(routes::admin::settings::settings_branding).to_string(),
|
link: uri!(routes::admin::settings::settings_branding).to_string(),
|
||||||
icon: icons::SETTINGS,
|
icon: icons::SETTINGS,
|
||||||
sub: Some(vec![SubItem {
|
sub: Some(vec![
|
||||||
|
SubItem {
|
||||||
label: "Branding",
|
label: "Branding",
|
||||||
link: uri!(routes::admin::settings::settings_branding).to_string(),
|
link: uri!(routes::admin::settings::settings_branding).to_string(),
|
||||||
}]),
|
},
|
||||||
|
SubItem {
|
||||||
|
label: "Security",
|
||||||
|
link: uri!(routes::admin::settings::settings_security).to_string(),
|
||||||
|
},
|
||||||
|
]),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ pub enum Page {
|
||||||
Redirect(Redirect),
|
Redirect(Redirect),
|
||||||
AdminDashboard(AdminDashboard),
|
AdminDashboard(AdminDashboard),
|
||||||
AdminSettingsBranding(AdminSettingsBranding),
|
AdminSettingsBranding(AdminSettingsBranding),
|
||||||
|
AdminSettingsSecurity(AdminSettingsSecurity),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
|
|
@ -29,6 +30,7 @@ impl Page {
|
||||||
Page::Redirect(_) => "pages/oauth/redirect",
|
Page::Redirect(_) => "pages/oauth/redirect",
|
||||||
Page::AdminDashboard(_) => "pages/admin/dashboard",
|
Page::AdminDashboard(_) => "pages/admin/dashboard",
|
||||||
Page::AdminSettingsBranding(_) => "pages/admin/settings_branding",
|
Page::AdminSettingsBranding(_) => "pages/admin/settings_branding",
|
||||||
|
Page::AdminSettingsSecurity(_) => "pages/admin/settings_security",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,6 +44,7 @@ impl Page {
|
||||||
Page::Redirect(_) => "Redirecting",
|
Page::Redirect(_) => "Redirecting",
|
||||||
Page::AdminDashboard(_) => "Admin dashboard",
|
Page::AdminDashboard(_) => "Admin dashboard",
|
||||||
Page::AdminSettingsBranding(_) => "Server branding",
|
Page::AdminSettingsBranding(_) => "Server branding",
|
||||||
|
Page::AdminSettingsSecurity(_) => "Server security",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,6 +60,7 @@ impl Page {
|
||||||
Page::Redirect(_) => None,
|
Page::Redirect(_) => None,
|
||||||
Page::AdminDashboard(_) => Some(AdminMenu::Dashboard.into()),
|
Page::AdminDashboard(_) => Some(AdminMenu::Dashboard.into()),
|
||||||
Page::AdminSettingsBranding(_) => Some(AdminMenu::Settings.into()),
|
Page::AdminSettingsBranding(_) => Some(AdminMenu::Settings.into()),
|
||||||
|
Page::AdminSettingsSecurity(_) => Some(AdminMenu::Settings.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,6 +74,7 @@ impl Page {
|
||||||
Page::Redirect(redirect) => Box::new(redirect),
|
Page::Redirect(redirect) => Box::new(redirect),
|
||||||
Page::AdminDashboard(dashboard) => Box::new(dashboard),
|
Page::AdminDashboard(dashboard) => Box::new(dashboard),
|
||||||
Page::AdminSettingsBranding(branding) => Box::new(branding),
|
Page::AdminSettingsBranding(branding) => Box::new(branding),
|
||||||
|
Page::AdminSettingsSecurity(security) => Box::new(security),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,13 @@ pub mod dashboard;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
routes![admin_dashboard, settings_branding, settings_update_branding]
|
routes![
|
||||||
|
admin_dashboard,
|
||||||
|
settings_branding,
|
||||||
|
settings_update_branding,
|
||||||
|
settings_security,
|
||||||
|
settings_security_form,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod content {
|
pub mod content {
|
||||||
|
|
@ -27,4 +33,11 @@ pub mod content {
|
||||||
pub user: JwtClaims,
|
pub user: JwtClaims,
|
||||||
pub business_name: String,
|
pub business_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AdminSettingsSecurity {
|
||||||
|
pub user: JwtClaims,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
use crate::routes::prelude::*;
|
use crate::routes::prelude::*;
|
||||||
|
use crate::tokens::{generate_jwt, generate_refresh_token};
|
||||||
|
use apps::App;
|
||||||
|
use refresh_tokens::RefreshToken;
|
||||||
use rocket::fs::TempFile;
|
use rocket::fs::TempFile;
|
||||||
|
use rocket::http::CookieJar;
|
||||||
use rocket::{get, post};
|
use rocket::{get, post};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use users::User;
|
||||||
|
|
||||||
#[get("/admin/settings/branding")]
|
#[get("/admin/settings/branding")]
|
||||||
pub async fn settings_branding(mut db: Connection<Database>, admin: JwtAdmin) -> Result<Page> {
|
pub async fn settings_branding(mut db: Connection<Database>, admin: JwtAdmin) -> Result<Page> {
|
||||||
|
|
@ -59,3 +65,85 @@ pub async fn settings_update_branding(
|
||||||
|
|
||||||
Ok(Redirect::to(uri!(settings_branding)))
|
Ok(Redirect::to(uri!(settings_branding)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/admin/settings/security")]
|
||||||
|
pub async fn settings_security(admin: JwtAdmin) -> Result<Page> {
|
||||||
|
Ok(Page::AdminSettingsSecurity(
|
||||||
|
super::content::AdminSettingsSecurity { user: admin.0 },
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromForm)]
|
||||||
|
pub struct SecurityForm {
|
||||||
|
pub logout_everyone: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/admin/settings/security", data = "<form>")]
|
||||||
|
pub async fn settings_security_form(
|
||||||
|
mut db: Connection<Database>,
|
||||||
|
admin: JwtAdmin,
|
||||||
|
form: Form<SecurityForm>,
|
||||||
|
ip_address: IpAddr,
|
||||||
|
cookie_jar: &CookieJar<'_>,
|
||||||
|
) -> Result<Redirect> {
|
||||||
|
let mut transaction = db.begin().await?;
|
||||||
|
|
||||||
|
if let Some(logout_everyone) = form.logout_everyone {
|
||||||
|
if logout_everyone {
|
||||||
|
// Generate key id
|
||||||
|
let key_id = task::spawn_blocking(id::KeyID::default).await?;
|
||||||
|
|
||||||
|
// Generate key
|
||||||
|
let key_id_for_generation = key_id.clone();
|
||||||
|
let (private_key, public_key) =
|
||||||
|
task::spawn_blocking(move || jwt::generate(&key_id_for_generation)).await??;
|
||||||
|
|
||||||
|
// Insert keys in database
|
||||||
|
jwt::database::save_new_keys(&mut transaction, &key_id, &private_key, &public_key)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Revoke all keys except new one
|
||||||
|
jwt::database::revoke_all_except_one(&mut transaction, &key_id).await?;
|
||||||
|
|
||||||
|
// Get app
|
||||||
|
let app = App::get_one_by_id(&mut transaction, "ezidam")
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| Error::not_found("Could not find application"))?;
|
||||||
|
|
||||||
|
// Get user info
|
||||||
|
let user = User::get_by_login(&mut transaction, &admin.0.subject)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| Error::not_found("Could not find user"))?;
|
||||||
|
|
||||||
|
// Revoke all refresh tokens
|
||||||
|
RefreshToken::revoke_all(&mut transaction).await?;
|
||||||
|
|
||||||
|
// Generate refresh token
|
||||||
|
generate_refresh_token(&mut transaction, ip_address, user.id(), cookie_jar)
|
||||||
|
.await
|
||||||
|
.map_err(Error::internal_server_error)?;
|
||||||
|
|
||||||
|
// Get base url
|
||||||
|
let settings = Settings::get(&mut transaction).await?;
|
||||||
|
let home_page = settings
|
||||||
|
.url()
|
||||||
|
.map(String::from)
|
||||||
|
.ok_or_else(|| Error::bad_request("Server url is not set"))?;
|
||||||
|
|
||||||
|
// Generate jwt
|
||||||
|
generate_jwt(
|
||||||
|
&mut transaction,
|
||||||
|
&private_key,
|
||||||
|
&home_page,
|
||||||
|
&app.id().0,
|
||||||
|
&user,
|
||||||
|
cookie_jar,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(Error::internal_server_error)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transaction.commit().await?;
|
||||||
|
|
||||||
|
Ok(Redirect::to(uri!(settings_security)))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
{% extends "shell" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<!-- Page header -->
|
||||||
|
<div class="page-header d-print-none">
|
||||||
|
<div class="container-xl">
|
||||||
|
<div class="row g-2 align-items-center">
|
||||||
|
<div class="col">
|
||||||
|
<h2 class="page-title">
|
||||||
|
Server settings
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Page body -->
|
||||||
|
<div class="page-body">
|
||||||
|
<div class="container-xl">
|
||||||
|
<div class="card">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-3 d-none d-md-block border-end">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="list-group list-group-transparent">
|
||||||
|
<a href="./branding"
|
||||||
|
class="list-group-item list-group-item-action d-flex align-items-center">Branding</a>
|
||||||
|
<a href="./security"
|
||||||
|
class="list-group-item list-group-item-action d-flex align-items-center active">Security</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col d-flex flex-column">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="mb-4">Security</h2>
|
||||||
|
|
||||||
|
<h3 class="card-title">Logout everyone</h3>
|
||||||
|
<p class="card-subtitle">
|
||||||
|
This will force all logged-in users to log in again.<br>
|
||||||
|
This action will become effective immediately.
|
||||||
|
</p>
|
||||||
|
<p>This might take some time, but should not be long.</p>
|
||||||
|
|
||||||
|
<div class="row align-items-center">
|
||||||
|
|
||||||
|
<!-- Logout everyone -->
|
||||||
|
<div class="col-auto">
|
||||||
|
<form action="" method="post">
|
||||||
|
<button class="btn btn-danger" type="submit" name="logout_everyone" value="true">
|
||||||
|
Logout everyone
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue