Add path roles/update to update a specific role

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/13/head
trivernis 4 years ago
parent 7f4e89caf2
commit 314da98c17
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -4,6 +4,8 @@
use crate::database::models::{CreatePermissionsEntry, Permission};
use crate::database::{DatabaseResult, PostgresPool, Table, ADMIN_ROLE_NAME};
use std::collections::HashSet;
use std::iter::FromIterator;
pub(crate) const VIEW_ROLE_PERMISSION: &str = "ROLE_VIEW";
pub(crate) const CREATE_ROLE_PERMISSION: &str = "ROLE_CREATE";
@ -86,4 +88,24 @@ impl Permissions {
Ok(created_permissions)
}
/// Returns a list of permission IDs that don't exist in the database
pub fn get_not_existing(&self, permissions_vec: &Vec<i32>) -> DatabaseResult<Vec<i32>> {
let permissions = HashSet::from_iter(permissions_vec.iter().cloned());
let mut connection = self.pool.get()?;
let rows = connection.query(
"SELECT id FROM permissions WHERE id = ANY($1)",
&[permissions_vec],
)?;
let existing_perms = rows
.into_iter()
.map(|row| -> i32 { row.get(0) })
.collect::<HashSet<i32>>();
let not_existing_perms = permissions
.difference(&existing_perms)
.cloned()
.collect::<Vec<i32>>();
Ok(not_existing_perms)
}
}

@ -6,6 +6,8 @@ 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::utils::error::DBError;
use std::collections::HashSet;
use std::iter::FromIterator;
/// The role table that stores
/// all defined roles
@ -48,68 +50,39 @@ impl Roles {
description: Option<String>,
permissions: Vec<i32>,
) -> DatabaseResult<Role> {
let permissions: HashSet<i32> = HashSet::from_iter(permissions.into_iter());
let mut connection = self.pool.get()?;
let exists = connection.query_opt("SELECT id FROM roles WHERE name = $1", &[&name])?;
if exists.is_some() {
return Err(DBError::RecordExists);
}
let permissions_exist = connection.query(
"SELECT id FROM permissions WHERE permissions.id = ANY ($1)",
&[&permissions],
)?;
if permissions_exist.len() != permissions.len() {
return Err(DBError::GenericError(format!(
"Not all provided permissions exist! Existing permissions: {:?}",
permissions_exist
.iter()
.map(|row| -> i32 { row.get(0) })
.collect::<Vec<i32>>()
)));
}
log::trace!("Preparing transaction");
let admin_email = dotenv::var(ENV_ADMIN_EMAIL).unwrap_or(DEFAULT_ADMIN_EMAIL.to_string());
let mut transaction = connection.transaction()?;
let result: DatabaseResult<Role> = {
let row = transaction.query_one(
"INSERT INTO roles (name, description) VALUES ($1, $2) RETURNING *",
&[&name, &description],
let row = transaction.query_one(
"INSERT INTO roles (name, description) VALUES ($1, $2) RETURNING *",
&[&name, &description],
)?;
let role: Role = serde_postgres::from_row(&row)?;
for permission in permissions {
transaction.execute(
"INSERT INTO role_permissions (role_id, permission_id) VALUES ($1, $2);",
&[&role.id, &permission],
)?;
let role: Role = serde_postgres::from_row(&row)?;
for permission in permissions {
transaction.execute(
"INSERT INTO role_permissions (role_id, permission_id) VALUES ($1, $2);",
&[&role.id, &permission],
)?;
}
if let Err(e) = transaction.execute(
"INSERT INTO user_roles (user_id, role_id) VALUES ((SELECT id FROM users WHERE email = $1), $2)",
&[&admin_email, &role.id],
) {
log::debug!("Failed to add role to admin user: {}", e);
}
Ok(role)
};
match result {
Err(e) => {
log::warn!("Failed to create role {}: {}", name, e);
log::trace!("Rolling back...");
transaction.rollback()?;
log::trace!("Rolled back!");
Err(e)
}
Ok(role) => {
log::debug!("Successfully created role {} with id {}", name, role.id);
log::trace!("Committing...");
transaction.commit()?;
log::trace!("Committed!");
Ok(role)
}
}
if let Err(e) = transaction.execute(
"INSERT INTO user_roles (user_id, role_id) VALUES ((SELECT id FROM users WHERE email = $1), $2)",
&[&admin_email, &role.id],
) {
log::debug!("Failed to add role to admin user: {}", e);
}
transaction.commit()?;
Ok(role)
}
/// Returns information for a role
@ -136,4 +109,50 @@ impl Roles {
Ok(roles)
}
pub fn update_role(
&self,
name: String,
description: Option<String>,
permissions: Vec<i32>,
) -> DatabaseResult<Role> {
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])?
.ok_or(DBError::RecordDoesNotExist)?
.get(0);
let update_result = transaction.query_one(
"UPDATE roles SET description = $2 WHERE id = $1 RETURNING *",
&[&id, &description],
)?;
let current_permissions = transaction
.query(
"SELECT permission_id from role_permissions WHERE role_id = $1",
&[&id],
)?
.into_iter()
.map(|r| -> i32 { r.get(0) })
.collect::<HashSet<i32>>();
let new_permissions = permissions.difference(&current_permissions);
let deleted_permissions = current_permissions.difference(&permissions);
for new in new_permissions {
transaction.query(
"INSERT INTO role_permissions (role_id, permission_id) VALUES ($1, $2)",
&[&id, new],
)?;
}
for deleted in deleted_permissions {
transaction.query(
"DELETE FROM role_permissions WHERE role_id = $1 AND permission_id = $2",
&[&id, deleted],
)?;
}
transaction.commit()?;
Ok(serde_postgres::from_row::<Role>(&update_result)?)
}
}

@ -11,10 +11,13 @@ use rouille::{Request, Response, Server};
use serde::export::Formatter;
use serde::Serialize;
use crate::database::permissions::{CREATE_ROLE_PERMISSION, VIEW_ROLE_PERMISSION};
use crate::database::permissions::{
CREATE_ROLE_PERMISSION, UPDATE_ROLE_PERMISSION, VIEW_ROLE_PERMISSION,
};
use crate::database::Database;
use crate::server::messages::{
CreateRoleRequest, FullRowData, LoginMessage, LogoutConfirmation, LogoutMessage, RefreshMessage,
ErrorMessage, FullRoleData, LoginMessage, LogoutConfirmation, LogoutMessage, ModifyRoleRequest,
RefreshMessage,
};
use crate::utils::error::DBError;
use crate::utils::get_user_id_from_token;
@ -112,6 +115,9 @@ 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)
},
_ => if request.method() == "OPTIONS" {
Response::empty_204()
} else {
@ -180,7 +186,7 @@ impl UserHttpServer {
let role = database.roles.get_role(name)?;
let permissions = database.role_permission.by_role(role.id)?;
Ok(Response::json(&FullRowData {
Ok(Response::json(&FullRoleData {
id: role.id,
name: role.name,
permissions,
@ -197,21 +203,58 @@ impl UserHttpServer {
fn create_role(database: &Database, request: &Request) -> HTTPResult<Response> {
require_permission!(database, request, CREATE_ROLE_PERMISSION);
let message: CreateRoleRequest = serde_json::from_str(parse_string_body(request)?.as_str())
let message: ModifyRoleRequest = serde_json::from_str(parse_string_body(request)?.as_str())
.map_err(|e| HTTPError::new(e.to_string(), 400))?;
let not_existing = database
.permissions
.get_not_existing(&message.permissions)?;
if !not_existing.is_empty() {
return Ok(Response::json(&ErrorMessage::new(format!(
"The permissions {:?} don't exist",
not_existing
)))
.with_status_code(400));
}
let role =
database
.roles
.create_role(message.name, message.description, message.permissions)?;
let permissions = database.role_permission.by_role(role.id)?;
Ok(Response::json(&FullRowData {
Ok(Response::json(&FullRoleData {
id: role.id,
permissions,
name: role.name,
})
.with_status_code(201))
}
fn update_role(database: &Database, request: &Request) -> HTTPResult<Response> {
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))?;
let not_existing = database
.permissions
.get_not_existing(&message.permissions)?;
if !not_existing.is_empty() {
return Ok(Response::json(&ErrorMessage::new(format!(
"The permissions {:?} don't exist",
not_existing
)))
.with_status_code(400));
}
let role =
database
.roles
.update_role(message.name, message.description, message.permissions)?;
let permissions = database.role_permission.by_role(role.id)?;
Ok(Response::json(&FullRoleData {
id: role.id,
permissions,
name: role.name,
}))
}
}
/// Parses the body of a http request into a string representation

@ -72,7 +72,7 @@ pub struct GetPermissionsRequest {
}
#[derive(Deserialize)]
pub struct CreateRoleRequest {
pub struct ModifyRoleRequest {
pub name: String,
pub description: Option<String>,
pub permissions: Vec<i32>,
@ -108,7 +108,7 @@ pub struct LogoutConfirmation {
}
#[derive(Serialize)]
pub struct FullRowData {
pub struct FullRoleData {
pub id: i32,
pub name: String,
pub permissions: Vec<Permission>,

@ -14,7 +14,7 @@ use serde::Deserialize;
use crate::database::Database;
use crate::server::messages::{
CreatePermissionsRequest, CreateRoleRequest, ErrorMessage, GetPermissionsRequest, InfoEntry,
CreatePermissionsRequest, ErrorMessage, GetPermissionsRequest, InfoEntry, ModifyRoleRequest,
TokenRequest,
};
use crate::utils::get_user_id_from_token;
@ -185,7 +185,7 @@ impl UserRpcServer {
/// Handles the requests for creating new roles
fn handle_create_role(database: Database, data: &Vec<u8>) -> RpcResult<Message> {
log::trace!("Create Role");
let message = CreateRoleRequest::deserialize(&mut Deserializer::new(&mut data.as_slice()))
let message = ModifyRoleRequest::deserialize(&mut Deserializer::new(&mut data.as_slice()))
.map_err(|e| ErrorMessage::new(e.to_string()))?;
let role =
database

Loading…
Cancel
Save