Add update user method

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/14/head
trivernis 4 years ago
parent f6fead5baa
commit 81ae1a5c3e
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -8,7 +8,7 @@ use r2d2::Pool;
use r2d2_postgres::PostgresConnectionManager;
use crate::database::models::CreatePermissionsEntry;
use crate::database::permissions::{Permissions, DEFAULT_PERMISSIONS};
use crate::database::permissions::{Permissions, USER_MANAGEMENT_PERMISSIONS};
use crate::database::role_permissions::RolePermissions;
use crate::database::roles::Roles;
use crate::database::user_roles::UserRoles;
@ -94,7 +94,7 @@ impl Database {
log::debug!("Failed to create admin role {}", e.to_string())
}
self.permissions.create_permissions(
DEFAULT_PERMISSIONS
USER_MANAGEMENT_PERMISSIONS
.iter()
.map(|(name, description)| CreatePermissionsEntry {
name: name.to_string(),

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use zeroize::Zeroize;
/// Record to store data in when retrieving rows from the users table
#[derive(Clone, Debug, Zeroize)]
#[derive(Clone, Debug, Zeroize, Serialize)]
#[zeroize(drop)]
pub struct UserRecord {
pub id: i32,
@ -55,3 +55,11 @@ pub struct CreatePermissionsEntry {
pub name: String,
pub description: String,
}
/// Information about the user that doesn't contain any critical information
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct UserInformation {
pub id: i32,
pub name: String,
pub email: String,
}

@ -7,15 +7,22 @@ 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";
pub(crate) const UPDATE_ROLE_PERMISSION: &str = "ROLE_UPDATE";
pub(crate) const DELETE_ROLE_PERMISSION: &str = "ROLE_DELETE";
pub(crate) const DEFAULT_PERMISSIONS: &[(&'static str, &'static str)] = &[
(CREATE_ROLE_PERMISSION, "Allows the user to create roles"),
(UPDATE_ROLE_PERMISSION, "Allows the user to update roles"),
(DELETE_ROLE_PERMISSION, "Allows the user to delete roles"),
(VIEW_ROLE_PERMISSION, "Allows to see information for roles"),
pub(crate) const ROLE_VIEW_PERM: &str = "ROLE_VIEW";
pub(crate) const ROLE_CREATE_PERM: &str = "ROLE_CREATE";
pub(crate) const ROLE_UPDATE_PERM: &str = "ROLE_UPDATE";
pub(crate) const ROLE_DELETE_PERM: &str = "ROLE_DELETE";
pub(crate) const USER_UPDATE_PERM: &str = "USER_UPDATE";
pub(crate) const USER_MANAGEMENT_PERMISSIONS: &[(&'static str, &'static str)] = &[
(ROLE_CREATE_PERM, "Allows the user to create roles"),
(ROLE_UPDATE_PERM, "Allows the user to update roles"),
(ROLE_DELETE_PERM, "Allows the user to delete roles"),
(ROLE_VIEW_PERM, "Allows to see information for roles"),
(
USER_UPDATE_PERM,
"Allows changing the name, password and email of a user",
),
];
/// The permissions table that stores defined

@ -7,7 +7,7 @@ use std::sync::Arc;
use parking_lot::Mutex;
use zeroize::{Zeroize, Zeroizing};
use crate::database::models::UserRecord;
use crate::database::models::{UserInformation, UserRecord};
use crate::database::tokens::{SessionTokens, TokenStore};
use crate::database::user_roles::UserRoles;
use crate::database::{DatabaseResult, PostgresPool, Table};
@ -78,6 +78,55 @@ impl Users {
Ok(UserRecord::from_ordered_row(&row))
}
pub fn update_user(
&self,
old_email: String,
name: String,
email: String,
password: String,
) -> DatabaseResult<UserInformation> {
let mut connection = self.pool.get()?;
let mut password = Zeroizing::new(password);
if connection
.query_opt("SELECT email FROM users WHERE email = $1", &[&old_email])?
.is_none()
{
log::trace!("Failed to create user: Record doesn't exist!");
return Err(DBError::RecordDoesNotExist);
}
if old_email != email
&& connection
.query_opt("SELECT email FROM users WHERE email = $1", &[&old_email])?
.is_some()
{
log::trace!("Failed to create user: New Record exists!");
return Err(DBError::GenericError(format!(
"A user for the email {} already exists!",
email
)));
}
let salt = Zeroizing::new(create_salt());
let pw_hash =
hash_password(password.as_bytes(), &*salt).map_err(|e| DBError::GenericError(e))?;
password.zeroize();
let new_record = connection.query_one(
"UPDATE users SET name = $1, email = $2, password_hash = $3, salt = $4 WHERE email = $5 RETURNING *",
&[&name, &email, &pw_hash.to_vec(), &salt.to_vec(), &old_email],
)?;
Ok(serde_postgres::from_row::<UserInformation>(&new_record)?)
}
/// Returns information about a user by Id
pub fn get_user(&self, id: i32) -> DatabaseResult<UserInformation> {
let mut connection = self.pool.get()?;
let result = connection
.query_opt("SELECT id, name, email FROM users WHERE id = $1", &[&id])?
.ok_or(DBError::RecordDoesNotExist)?;
Ok(serde_postgres::from_row::<UserInformation>(&result)?)
}
/// Creates new tokens for a user login that can be used by services
/// that need those tokens to verify a user login
pub fn create_tokens(

@ -11,19 +11,20 @@ use rouille::{Request, Response, Server};
use serde::export::Formatter;
use serde::Serialize;
use crate::database::models::Role;
use crate::database::models::{Role, UserInformation};
use crate::database::permissions::{
CREATE_ROLE_PERMISSION, DELETE_ROLE_PERMISSION, UPDATE_ROLE_PERMISSION, VIEW_ROLE_PERMISSION,
ROLE_CREATE_PERM, ROLE_DELETE_PERM, ROLE_UPDATE_PERM, ROLE_VIEW_PERM, USER_UPDATE_PERM,
};
use crate::database::tokens::SessionTokens;
use crate::database::Database;
use crate::server::documentation::RESTDocumentation;
use crate::server::messages::{
DeleteRoleResponse, ErrorMessage, FullRoleData, LoginMessage, LogoutConfirmation,
LogoutMessage, ModifyRoleRequest, RefreshMessage,
LogoutMessage, ModifyRoleRequest, RefreshMessage, UpdateUserRequest,
};
use crate::utils::error::DBError;
use crate::utils::get_user_id_from_token;
use serde::de::DeserializeOwned;
macro_rules! require_permission {
($database:expr,$request:expr,$permission:expr) => {
@ -127,6 +128,9 @@ impl UserHttpServer {
(POST) (/roles/{name: String}/delete) => {
Self::delete_role(&database, request, name).unwrap_or_else(HTTPError::into)
},
(POST) (/users/{email: String}/update) => {
Self::update_user(&database, request, email).unwrap_or_else(HTTPError::into)
},
_ => if request.method() == "OPTIONS" {
Response::empty_204()
} else {
@ -196,6 +200,11 @@ impl UserHttpServer {
"POST",
"Deletes a role",
)?;
doc.add_path::<UpdateUserRequest, UserInformation>(
"/users/{email:String}/update",
"POST",
"Change user information",
)?;
Ok(doc)
}
@ -241,7 +250,7 @@ impl UserHttpServer {
/// Returns the data for a given role
fn get_role(database: &Database, request: &Request, name: String) -> HTTPResult<Response> {
require_permission!(database, request, VIEW_ROLE_PERMISSION);
require_permission!(database, request, ROLE_VIEW_PERM);
let role = database.roles.get_role(name)?;
let permissions = database.role_permission.by_role(role.id)?;
@ -254,14 +263,14 @@ impl UserHttpServer {
/// Returns a list of all roles
fn get_roles(database: &Database, request: &Request) -> HTTPResult<Response> {
require_permission!(database, request, VIEW_ROLE_PERMISSION);
require_permission!(database, request, ROLE_VIEW_PERM);
let roles = database.roles.get_roles()?;
Ok(Response::json(&roles))
}
fn create_role(database: &Database, request: &Request) -> HTTPResult<Response> {
require_permission!(database, request, CREATE_ROLE_PERMISSION);
require_permission!(database, request, ROLE_CREATE_PERM);
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
@ -289,9 +298,9 @@ impl UserHttpServer {
}
fn update_role(database: &Database, request: &Request, name: String) -> 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))?;
require_permission!(database, request, ROLE_UPDATE_PERM);
let message: ModifyRoleRequest = deserialize_body(&request)?;
let not_existing = database
.permissions
.get_not_existing(&message.permissions)?;
@ -318,7 +327,7 @@ impl UserHttpServer {
}
fn delete_role(database: &Database, request: &Request, role: String) -> HTTPResult<Response> {
require_permission!(database, request, DELETE_ROLE_PERMISSION);
require_permission!(database, request, ROLE_DELETE_PERM);
database.roles.delete_role(&role)?;
Ok(Response::json(&DeleteRoleResponse {
@ -326,6 +335,22 @@ impl UserHttpServer {
role,
}))
}
fn update_user(database: &Database, request: &Request, email: String) -> HTTPResult<Response> {
let (_, id) = validate_request_token(request, database)?;
let message = deserialize_body::<UpdateUserRequest>(&request)?;
let logged_in_user = database.users.get_user(id)?;
if logged_in_user.email != message.email {
require_permission!(database, request, USER_UPDATE_PERM);
}
let record =
database
.users
.update_user(email, message.name, message.email, message.password)?;
Ok(Response::json(&record))
}
}
/// Parses the body of a http request into a string representation
@ -340,6 +365,12 @@ fn parse_string_body(request: &Request) -> HTTPResult<String> {
Ok(string_body)
}
/// Deserialized a json body into the given type
fn deserialize_body<T: DeserializeOwned>(request: &Request) -> HTTPResult<T> {
serde_json::from_str(parse_string_body(request)?.as_str())
.map_err(|e| HTTPError::new(e.to_string(), 400))
}
/// Parses and validates the request token from the http header
fn validate_request_token(request: &Request, database: &Database) -> HTTPResult<(String, i32)> {
lazy_static::lazy_static! {static ref BEARER_REGEX: Regex = Regex::new(r"^[bB]earer\s+").unwrap();}

@ -119,3 +119,10 @@ pub struct DeleteRoleResponse {
pub success: bool,
pub role: String,
}
#[derive(Deserialize, JsonSchema)]
pub struct UpdateUserRequest {
pub name: String,
pub email: String,
pub password: String,
}

Loading…
Cancel
Save