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),
|
AdminUsersView(AdminUsersView),
|
||||||
AdminUsersNew(AdminUsersNew),
|
AdminUsersNew(AdminUsersNew),
|
||||||
AdminRolesList(AdminRolesList),
|
AdminRolesList(AdminRolesList),
|
||||||
|
AdminRolesNew(AdminRolesNew),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
|
|
@ -62,6 +63,7 @@ impl Page {
|
||||||
Page::AdminUsersView(_) => "pages/admin/users/view",
|
Page::AdminUsersView(_) => "pages/admin/users/view",
|
||||||
Page::AdminUsersNew(_) => "pages/admin/users/new",
|
Page::AdminUsersNew(_) => "pages/admin/users/new",
|
||||||
Page::AdminRolesList(_) => "pages/admin/roles/list",
|
Page::AdminRolesList(_) => "pages/admin/roles/list",
|
||||||
|
Page::AdminRolesNew(_) => "pages/admin/roles/new",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +93,7 @@ impl Page {
|
||||||
Page::AdminUsersView(_) => "User info",
|
Page::AdminUsersView(_) => "User info",
|
||||||
Page::AdminUsersNew(_) => "New user",
|
Page::AdminUsersNew(_) => "New user",
|
||||||
Page::AdminRolesList(_) => "Roles",
|
Page::AdminRolesList(_) => "Roles",
|
||||||
|
Page::AdminRolesNew(_) => "New role",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,6 +125,7 @@ impl Page {
|
||||||
Page::AdminUsersView(_) => Some(AdminMenu::Users.into()),
|
Page::AdminUsersView(_) => Some(AdminMenu::Users.into()),
|
||||||
Page::AdminUsersNew(_) => Some(AdminMenu::Users.into()),
|
Page::AdminUsersNew(_) => Some(AdminMenu::Users.into()),
|
||||||
Page::AdminRolesList(_) => Some(AdminMenu::Roles.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::AdminUsersView(view) => Box::new(view),
|
||||||
Page::AdminUsersNew(new) => Box::new(new),
|
Page::AdminUsersNew(new) => Box::new(new),
|
||||||
Page::AdminRolesList(list) => Box::new(list),
|
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_totp_backup_delete,
|
||||||
admin_users_info_update,
|
admin_users_info_update,
|
||||||
admin_roles_list,
|
admin_roles_list,
|
||||||
|
admin_roles_new,
|
||||||
|
admin_roles_new_form,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,4 +136,11 @@ pub mod content {
|
||||||
pub user: JwtClaims,
|
pub user: JwtClaims,
|
||||||
pub roles: Vec<Role>,
|
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 crate::routes::prelude::*;
|
||||||
use rocket::{get, post};
|
use rocket::{get, post};
|
||||||
use roles::Role;
|
use roles::Role;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[get("/admin/roles")]
|
#[get("/admin/roles")]
|
||||||
pub async fn admin_roles_list(
|
pub async fn admin_roles_list(
|
||||||
|
|
@ -23,3 +24,56 @@ pub async fn admin_roles_list(
|
||||||
.map(|flash| Page::with_flash(page.clone(), flash))
|
.map(|flash| Page::with_flash(page.clone(), flash))
|
||||||
.unwrap_or_else(|| page.into()))
|
.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::error::Error;
|
||||||
use crate::Role;
|
use crate::Role;
|
||||||
use database::sqlx::SqliteExecutor;
|
use database::sqlx::SqliteExecutor;
|
||||||
|
use database::Error as DatabaseError;
|
||||||
use database::Roles as DatabaseRoles;
|
use database::Roles as DatabaseRoles;
|
||||||
use id::RoleID;
|
use id::RoleID;
|
||||||
|
|
||||||
|
|
@ -23,4 +24,21 @@ impl Role {
|
||||||
.map(Self::from)
|
.map(Self::from)
|
||||||
.collect::<Vec<_>>())
|
.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 {
|
pub enum Error {
|
||||||
#[error("Database: {0}")]
|
#[error("Database: {0}")]
|
||||||
Database(#[from] database::Error),
|
Database(#[from] database::Error),
|
||||||
|
|
||||||
|
#[error("The name \"{0}\" is not available.")]
|
||||||
|
NameNotAvailable(String),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue