From 7359c259eb05b239db722ad69b70c503ef81e31e Mon Sep 17 00:00:00 2001 From: trivernis Date: Fri, 13 Nov 2020 16:15:30 +0100 Subject: [PATCH] Add role delete and change update Signed-off-by: trivernis --- src/database/roles.rs | 43 +++++++++++++++++++++++++++--- src/server/documentation/mod.rs | 6 +++-- src/server/documentation/style.css | 4 +++ src/server/http_server.rs | 42 +++++++++++++++++++++-------- src/server/messages.rs | 6 +++++ 5 files changed, 84 insertions(+), 17 deletions(-) diff --git a/src/database/roles.rs b/src/database/roles.rs index 963ae79..fde074b 100644 --- a/src/database/roles.rs +++ b/src/database/roles.rs @@ -4,7 +4,9 @@ use crate::database::models::Role; use crate::database::role_permissions::RolePermissions; -use crate::database::{DatabaseResult, PostgresPool, Table, DEFAULT_ADMIN_EMAIL, ENV_ADMIN_EMAIL}; +use crate::database::{ + DatabaseResult, PostgresPool, Table, ADMIN_ROLE_NAME, DEFAULT_ADMIN_EMAIL, ENV_ADMIN_EMAIL, +}; use crate::utils::error::DBError; use std::collections::HashSet; use std::iter::FromIterator; @@ -112,21 +114,35 @@ impl Roles { pub fn update_role( &self, + old_name: String, name: String, description: Option, permissions: Vec, ) -> DatabaseResult { + if old_name == ADMIN_ROLE_NAME { + return Err(DBError::GenericError( + "The admin role can't be altered!".to_string(), + )); + } let permissions = HashSet::from_iter(permissions.into_iter()); let mut connection = self.pool.get()?; let mut transaction = connection.transaction()?; let id: i32 = transaction - .query_opt("SELECT id FROM roles WHERE name = $1", &[&name])? + .query_opt("SELECT id FROM roles WHERE name = $1", &[&old_name])? .ok_or(DBError::RecordDoesNotExist)? .get(0); + let name_exists = + transaction.query_opt("SELECT id FROM roles WHERE name = $1", &[&name])?; + if name_exists.is_some() { + return Err(DBError::GenericError(format!( + "A role with the name {} already exists!", + name + ))); + } let update_result = transaction.query_one( - "UPDATE roles SET description = $2 WHERE id = $1 RETURNING *", - &[&id, &description], + "UPDATE roles SET name = $3, description = $2 WHERE id = $1 RETURNING *", + &[&id, &description, &name], )?; let current_permissions = transaction .query( @@ -155,4 +171,23 @@ impl Roles { Ok(serde_postgres::from_row::(&update_result)?) } + + /// Deletes a role if it exists + pub fn delete_role(&self, name: &String) -> DatabaseResult<()> { + if name == ADMIN_ROLE_NAME { + return Err(DBError::GenericError( + "The admin role can't be altered!".to_string(), + )); + } + let mut connection = self.pool.get()?; + let result = connection.query_opt("SELECT id FROM roles WHERE name = $1", &[name])?; + + if result.is_none() { + Err(DBError::RecordDoesNotExist) + } else { + connection.query("DELETE FROM roles WHERE name = $1", &[name])?; + + Ok(()) + } + } } diff --git a/src/server/documentation/mod.rs b/src/server/documentation/mod.rs index 5973c0a..9d1fda4 100644 --- a/src/server/documentation/mod.rs +++ b/src/server/documentation/mod.rs @@ -32,7 +32,9 @@ impl RESTDocumentation { } fn landing(&self) -> String { - let types = self.paths.keys().fold("".to_string(), |a, b| { + let mut keys = self.paths.keys().cloned().collect::>(); + keys.sort(); + let types = keys.into_iter().fold("".to_string(), |a, b| { format!("{}
{2}", a, self.base_path, b) }); @@ -53,7 +55,7 @@ impl RESTDocumentation { let content = format!( "\ Back -

{}: {}

+

{}: {}

{}

Input

{} diff --git a/src/server/documentation/style.css b/src/server/documentation/style.css index f06705d..dc6e524 100644 --- a/src/server/documentation/style.css +++ b/src/server/documentation/style.css @@ -8,6 +8,10 @@ body { font-family: "Fira Sans", "Noto Sans", sans-serif; } +code { + font-family: "Fira Code", "DejaVu Sans Mono", monospace; +} + code > pre { font-family: "Fira Code", "DejaVu Sans Mono", monospace; max-width: 100%; diff --git a/src/server/http_server.rs b/src/server/http_server.rs index 03892d7..94f0357 100644 --- a/src/server/http_server.rs +++ b/src/server/http_server.rs @@ -13,14 +13,14 @@ use serde::Serialize; use crate::database::models::Role; use crate::database::permissions::{ - CREATE_ROLE_PERMISSION, UPDATE_ROLE_PERMISSION, VIEW_ROLE_PERMISSION, + CREATE_ROLE_PERMISSION, DELETE_ROLE_PERMISSION, UPDATE_ROLE_PERMISSION, VIEW_ROLE_PERMISSION, }; use crate::database::tokens::SessionTokens; use crate::database::Database; use crate::server::documentation::RESTDocumentation; use crate::server::messages::{ - ErrorMessage, FullRoleData, LoginMessage, LogoutConfirmation, LogoutMessage, ModifyRoleRequest, - RefreshMessage, + DeleteRoleResponse, ErrorMessage, FullRoleData, LoginMessage, LogoutConfirmation, + LogoutMessage, ModifyRoleRequest, RefreshMessage, }; use crate::utils::error::DBError; use crate::utils::get_user_id_from_token; @@ -121,8 +121,11 @@ impl UserHttpServer { (POST) (/roles/create) => { Self::create_role(&database, request).unwrap_or_else(HTTPError::into) }, - (POST) (/roles/update) => { - Self::update_role(&database, request).unwrap_or_else(HTTPError::into) + (POST) (/roles/{name:String}/update) => { + Self::update_role(&database, request, name).unwrap_or_else(HTTPError::into) + }, + (POST) (/roles/{name: String}/delete) => { + Self::delete_role(&database, request, name).unwrap_or_else(HTTPError::into) }, _ => if request.method() == "OPTIONS" { Response::empty_204() @@ -184,10 +187,15 @@ impl UserHttpServer { "Creates a new role", )?; doc.add_path::( - "/roles/update", + "/roles/{name:String}/update", "POST", "Updates an existing role", )?; + doc.add_path::<(), DeleteRoleResponse>( + "/roles/{name:String}/delete", + "POST", + "Deletes a role", + )?; Ok(doc) } @@ -280,7 +288,7 @@ impl UserHttpServer { .with_status_code(201)) } - fn update_role(database: &Database, request: &Request) -> HTTPResult { + fn update_role(database: &Database, request: &Request, name: String) -> HTTPResult { require_permission!(database, request, UPDATE_ROLE_PERMISSION); let message: ModifyRoleRequest = serde_json::from_str(parse_string_body(request)?.as_str()) .map_err(|e| HTTPError::new(e.to_string(), 400))?; @@ -294,10 +302,12 @@ impl UserHttpServer { ))) .with_status_code(400)); } - let role = - database - .roles - .update_role(message.name, message.description, message.permissions)?; + let role = database.roles.update_role( + name, + message.name, + message.description, + message.permissions, + )?; let permissions = database.role_permission.by_role(role.id)?; Ok(Response::json(&FullRoleData { @@ -306,6 +316,16 @@ impl UserHttpServer { name: role.name, })) } + + fn delete_role(database: &Database, request: &Request, role: String) -> HTTPResult { + require_permission!(database, request, DELETE_ROLE_PERMISSION); + database.roles.delete_role(&role)?; + + Ok(Response::json(&DeleteRoleResponse { + success: true, + role, + })) + } } /// Parses the body of a http request into a string representation diff --git a/src/server/messages.rs b/src/server/messages.rs index 488203e..73e3fdc 100644 --- a/src/server/messages.rs +++ b/src/server/messages.rs @@ -113,3 +113,9 @@ pub struct FullRoleData { pub name: String, pub permissions: Vec, } + +#[derive(Serialize, JsonSchema)] +pub struct DeleteRoleResponse { + pub success: bool, + pub role: String, +}