admin/roles: new role
This commit is contained in:
parent
efaf2bda5a
commit
8fa2fb7ddc
6 changed files with 167 additions and 0 deletions
|
|
@ -33,6 +33,7 @@ pub enum Page {
|
|||
AdminUsersView(AdminUsersView),
|
||||
AdminUsersNew(AdminUsersNew),
|
||||
AdminRolesList(AdminRolesList),
|
||||
AdminRolesNew(AdminRolesNew),
|
||||
}
|
||||
|
||||
impl Page {
|
||||
|
|
@ -62,6 +63,7 @@ impl Page {
|
|||
Page::AdminUsersView(_) => "pages/admin/users/view",
|
||||
Page::AdminUsersNew(_) => "pages/admin/users/new",
|
||||
Page::AdminRolesList(_) => "pages/admin/roles/list",
|
||||
Page::AdminRolesNew(_) => "pages/admin/roles/new",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +93,7 @@ impl Page {
|
|||
Page::AdminUsersView(_) => "User info",
|
||||
Page::AdminUsersNew(_) => "New user",
|
||||
Page::AdminRolesList(_) => "Roles",
|
||||
Page::AdminRolesNew(_) => "New role",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,6 +125,7 @@ impl Page {
|
|||
Page::AdminUsersView(_) => Some(AdminMenu::Users.into()),
|
||||
Page::AdminUsersNew(_) => Some(AdminMenu::Users.into()),
|
||||
Page::AdminRolesList(_) => Some(AdminMenu::Roles.into()),
|
||||
Page::AdminRolesNew(_) => Some(AdminMenu::Roles.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,6 +155,7 @@ impl Page {
|
|||
Page::AdminUsersView(view) => Box::new(view),
|
||||
Page::AdminUsersNew(new) => Box::new(new),
|
||||
Page::AdminRolesList(list) => Box::new(list),
|
||||
Page::AdminRolesNew(new) => Box::new(new),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ pub fn routes() -> Vec<Route> {
|
|||
admin_users_totp_backup_delete,
|
||||
admin_users_info_update,
|
||||
admin_roles_list,
|
||||
admin_roles_new,
|
||||
admin_roles_new_form,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -134,4 +136,11 @@ pub mod content {
|
|||
pub user: JwtClaims,
|
||||
pub roles: Vec<Role>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
#[derive(Clone)]
|
||||
pub struct AdminRolesNew {
|
||||
pub user: JwtClaims,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::routes::prelude::*;
|
||||
use rocket::{get, post};
|
||||
use roles::Role;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[get("/admin/roles")]
|
||||
pub async fn admin_roles_list(
|
||||
|
|
@ -23,3 +24,56 @@ pub async fn admin_roles_list(
|
|||
.map(|flash| Page::with_flash(page.clone(), flash))
|
||||
.unwrap_or_else(|| page.into()))
|
||||
}
|
||||
|
||||
#[get("/admin/roles/new")]
|
||||
pub async fn admin_roles_new(admin: JwtAdmin, flash: Option<FlashMessage<'_>>) -> Result<Template> {
|
||||
let page = Page::AdminRolesNew(super::content::AdminRolesNew { user: admin.0 });
|
||||
|
||||
Ok(flash
|
||||
.map(|flash| Page::with_flash(page.clone(), flash))
|
||||
.unwrap_or_else(|| page.into()))
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct RolesNewForm<'r> {
|
||||
pub name: &'r str,
|
||||
pub label: &'r str,
|
||||
}
|
||||
|
||||
#[post("/admin/roles/new", data = "<form>")]
|
||||
pub async fn admin_roles_new_form(
|
||||
_admin: JwtAdmin,
|
||||
mut db: Connection<Database>,
|
||||
form: Form<RolesNewForm<'_>>,
|
||||
) -> Result<Flash<Redirect>> {
|
||||
// Parse name
|
||||
let name = match RoleID::from_str(form.name) {
|
||||
Ok(role_id) => role_id,
|
||||
Err(_) => {
|
||||
return Ok(Flash::new(
|
||||
Redirect::to(uri!(admin_roles_new)),
|
||||
FlashKind::Danger,
|
||||
"Invalid role name",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let mut transaction = db.begin().await?;
|
||||
|
||||
// Insert role in database
|
||||
if let Err(e) = Role::insert(&mut transaction, &name, form.label).await {
|
||||
return Ok(Flash::new(
|
||||
Redirect::to(uri!(admin_roles_new)),
|
||||
FlashKind::Danger,
|
||||
e.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok(Flash::new(
|
||||
Redirect::to(uri!(admin_roles_list)),
|
||||
FlashKind::Success,
|
||||
"Role has been created.",
|
||||
))
|
||||
}
|
||||
|
|
|
|||
78
crates/ezidam/templates/pages/admin/roles/new.html.tera
Normal file
78
crates/ezidam/templates/pages/admin/roles/new.html.tera
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
{% 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">
|
||||
Roles
|
||||
</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_role" action="" method="post" autocomplete="off" class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">New Role</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Name -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required" for="name">Name</label>
|
||||
<div>
|
||||
<input name="name" id="name" type="text" placeholder="Enter name"
|
||||
class="form-control"
|
||||
required>
|
||||
<small class="form-hint">
|
||||
This name identifies the role inside the system. It is not public.<br>
|
||||
Allowed characters: <code>[a-z][0-9]-</code>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Label -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required" for="label">Label</label>
|
||||
<div>
|
||||
<input name="label" id="label" type="text" placeholder="Enter label"
|
||||
class="form-control"
|
||||
required>
|
||||
<small class="form-hint">
|
||||
The label is shown to the users.
|
||||
</small>
|
||||
</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_role") }}
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block libs_js %}
|
||||
{% endblock lib_js %}
|
||||
|
||||
{% block additional_js %}
|
||||
{% endblock additional_js %}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::error::Error;
|
||||
use crate::Role;
|
||||
use database::sqlx::SqliteExecutor;
|
||||
use database::Error as DatabaseError;
|
||||
use database::Roles as DatabaseRoles;
|
||||
use id::RoleID;
|
||||
|
||||
|
|
@ -23,4 +24,21 @@ impl Role {
|
|||
.map(Self::from)
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
pub async fn insert(
|
||||
conn: impl SqliteExecutor<'_>,
|
||||
name: &RoleID,
|
||||
label: &str,
|
||||
) -> Result<(), Error> {
|
||||
DatabaseRoles::insert(conn, &name.0, label)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
DatabaseError::UniqueConstraintPrimaryKey => {
|
||||
Error::NameNotAvailable(name.to_string())
|
||||
}
|
||||
_ => e.into(),
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,7 @@
|
|||
pub enum Error {
|
||||
#[error("Database: {0}")]
|
||||
Database(#[from] database::Error),
|
||||
|
||||
#[error("The name \"{0}\" is not available.")]
|
||||
NameNotAvailable(String),
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue