admin/users: add new user
This commit is contained in:
parent
85facf7dc6
commit
306f2a60c4
8 changed files with 167 additions and 5 deletions
|
|
@ -30,6 +30,7 @@ pub enum Page {
|
|||
UserSecurityTotp(UserSecurityTotp),
|
||||
AuthorizeTotp(AuthorizeTotp),
|
||||
AdminUsersView(AdminUsersView),
|
||||
AdminUsersNew(AdminUsersNew),
|
||||
}
|
||||
|
||||
impl Page {
|
||||
|
|
@ -56,6 +57,7 @@ impl Page {
|
|||
Page::UserSecurityTotp(_) => "pages/settings/totp",
|
||||
Page::AuthorizeTotp(_) => "pages/oauth/totp",
|
||||
Page::AdminUsersView(_) => "pages/admin/users/view",
|
||||
Page::AdminUsersNew(_) => "pages/admin/users/new",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +84,7 @@ impl Page {
|
|||
Page::UserSecurityTotp(_) => "Enable One-time password",
|
||||
Page::AuthorizeTotp(_) => "Verifying your account",
|
||||
Page::AdminUsersView(_) => "User info",
|
||||
Page::AdminUsersNew(_) => "New user",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -110,6 +113,7 @@ impl Page {
|
|||
Page::UserSecurityTotp(_) => Some(UserMenu::Settings.into()),
|
||||
Page::AuthorizeTotp(_) => None,
|
||||
Page::AdminUsersView(_) => Some(AdminMenu::Users.into()),
|
||||
Page::AdminUsersNew(_) => Some(AdminMenu::Users.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,6 +140,7 @@ impl Page {
|
|||
Page::UserSecurityTotp(totp) => Box::new(totp),
|
||||
Page::AuthorizeTotp(totp) => Box::new(totp),
|
||||
Page::AdminUsersView(view) => Box::new(view),
|
||||
Page::AdminUsersNew(new) => Box::new(new),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ pub fn routes() -> Vec<Route> {
|
|||
admin_apps_new_secret,
|
||||
admin_apps_archive,
|
||||
admin_users_list,
|
||||
admin_users_new,
|
||||
admin_users_new_form,
|
||||
admin_users_view,
|
||||
admin_users_archive,
|
||||
admin_users_password_reset,
|
||||
|
|
@ -103,4 +105,11 @@ pub mod content {
|
|||
pub local: User,
|
||||
pub password_recover_expiration: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
#[derive(Clone)]
|
||||
pub struct AdminUsersNew {
|
||||
pub user: JwtClaims,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,62 @@ pub async fn admin_users_list(
|
|||
.unwrap_or_else(|| page.into()))
|
||||
}
|
||||
|
||||
#[get("/admin/users/new")]
|
||||
pub async fn admin_users_new(admin: JwtAdmin, flash: Option<FlashMessage<'_>>) -> Result<Template> {
|
||||
let page = Page::AdminUsersNew(super::content::AdminUsersNew { user: admin.0 });
|
||||
|
||||
Ok(flash
|
||||
.map(|flash| Page::with_flash(page.clone(), flash))
|
||||
.unwrap_or_else(|| page.into()))
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct UsersNewForm<'r> {
|
||||
pub username: &'r str,
|
||||
}
|
||||
|
||||
#[post("/admin/users/new", data = "<form>")]
|
||||
pub async fn admin_users_new_form(
|
||||
_admin: JwtAdmin,
|
||||
mut db: Connection<Database>,
|
||||
form: Form<UsersNewForm<'_>>,
|
||||
) -> Result<Flash<Redirect>> {
|
||||
// Parse username
|
||||
let username = match Username::from_str(form.username) {
|
||||
Ok(username) => username,
|
||||
Err(_) => {
|
||||
return Ok(Flash::new(
|
||||
Redirect::to(uri!(admin_users_new)),
|
||||
FlashKind::Danger,
|
||||
INVALID_USERNAME_ERROR,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Generate UserID
|
||||
let user_id = task::spawn_blocking(UserID::default).await?;
|
||||
|
||||
let mut transaction = db.begin().await?;
|
||||
|
||||
// Insert user in database
|
||||
if let Err(e) = User::insert(&mut transaction, &user_id, false, &username, None).await {
|
||||
return Ok(Flash::new(
|
||||
Redirect::to(uri!(admin_users_new)),
|
||||
FlashKind::Danger,
|
||||
e.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
let id = RocketUserID(user_id);
|
||||
Ok(Flash::new(
|
||||
Redirect::to(uri!(admin_users_view(id))),
|
||||
FlashKind::Success,
|
||||
"User has been created.",
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/admin/users/<id>")]
|
||||
pub async fn admin_users_view(
|
||||
admin_not_current: JwtAdminNotCurrent,
|
||||
|
|
|
|||
61
crates/ezidam/templates/pages/admin/users/new.html.tera
Normal file
61
crates/ezidam/templates/pages/admin/users/new.html.tera
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{% extends "shell" %}
|
||||
|
||||
{% import "utils/form" as form %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header d-print-none">
|
||||
<div class="container-xl">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<div class="page-pretitle">
|
||||
Admin dashboard
|
||||
</div>
|
||||
<h2 class="page-title">
|
||||
Users
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page body -->
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
|
||||
{% if flash %}
|
||||
<div class="alert alert-{{flash.0}}" role="alert">
|
||||
<h4 class="alert-title">{{ flash.1 | safe }}</h4>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form id="new_user" action="" method="post" autocomplete="off" class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">New User</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Username -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required" for="username">Username</label>
|
||||
<div>
|
||||
<input name="username" id="username" type="text" placeholder="Enter username"
|
||||
class="form-control"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card-footer text-end">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ form::disable_button_delay_submit(form_id="new_user") }}
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block libs_js %}
|
||||
{% endblock lib_js %}
|
||||
|
||||
{% block additional_js %}
|
||||
{% endblock additional_js %}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
{% if flash %}
|
||||
<div class="alert alert-{{flash.0}}" role="alert">
|
||||
<h4 class="alert-title">{{ flash.1 }}</h4>
|
||||
<h4 class="alert-title">{{ flash.1 | safe }}</h4>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub const INVALID_USERNAME_ERROR: &str = "Invalid username. Pattern is [a-zA-Z0-9]";
|
||||
pub const INVALID_USERNAME_ERROR: &str = "Invalid username. Pattern is <code>[a-zA-Z0-9]</code>";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct Username(pub String);
|
||||
|
|
|
|||
|
|
@ -43,15 +43,43 @@ impl User {
|
|||
is_admin: bool,
|
||||
username: &Username,
|
||||
password: Option<&Password>,
|
||||
) -> Result<Option<()>, Error> {
|
||||
Ok(DatabaseUsers::insert(
|
||||
) -> Result<(), Error> {
|
||||
DatabaseUsers::insert(
|
||||
conn,
|
||||
&id.0,
|
||||
is_admin,
|
||||
username.as_ref(),
|
||||
password.map(|p| p.hash()),
|
||||
)
|
||||
.await?)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
DatabaseError::UniqueConstraintPrimaryKey => Error::IdNotAvailable(id.to_string()),
|
||||
DatabaseError::UniqueConstraint(column) => {
|
||||
if &column == "username" {
|
||||
Error::UsernameNotAvailable(username.into())
|
||||
} else {
|
||||
Error::ColumnNotAvailable(column)
|
||||
}
|
||||
}
|
||||
_ => e.into(),
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
|
||||
// DatabaseUsers::set_username(conn, self.id.as_ref(), username.as_ref())
|
||||
// .await
|
||||
// .map_err(|e| match e {
|
||||
// DatabaseError::UniqueConstraint(column) => {
|
||||
// if &column == "username" {
|
||||
// Error::UsernameNotAvailable(username.into())
|
||||
// } else {
|
||||
// Error::ColumnNotAvailable(column)
|
||||
// }
|
||||
// }
|
||||
// _ => e.into(),
|
||||
// })?;
|
||||
//
|
||||
// Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_by_id(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ pub enum Error {
|
|||
#[error("The database column \"{0}\" is not available.")]
|
||||
ColumnNotAvailable(String),
|
||||
|
||||
#[error("The generated id \"{0}\" is not available. Please retry.")]
|
||||
IdNotAvailable(String),
|
||||
|
||||
#[error("The username \"{0}\" is not available.")]
|
||||
UsernameNotAvailable(String),
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue