Merge pull request #15 from fLotte-meets-HWR-DB/develop

User Role management
leon_tries_rust
Trivernis 4 years ago committed by GitHub
commit fdc7b63845
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,6 +5,8 @@
use crate::database::models::Role; use crate::database::models::Role;
use crate::database::{DatabaseResult, PostgresPool, Table}; use crate::database::{DatabaseResult, PostgresPool, Table};
use crate::utils::error::DBError; use crate::utils::error::DBError;
use std::collections::HashSet;
use std::iter::FromIterator;
/// A table that stores the relation between users and roles /// A table that stores the relation between users and roles
#[derive(Clone)] #[derive(Clone)]
@ -43,4 +45,37 @@ impl UserRoles {
serde_postgres::from_rows(&rows).map_err(DBError::from) serde_postgres::from_rows(&rows).map_err(DBError::from)
} }
pub fn update_roles(&self, user_id: i32, roles: Vec<String>) -> DatabaseResult<Vec<Role>> {
let mut connection = self.pool.get()?;
let mut transaction = connection.transaction()?;
let role_ids_result = transaction.query(
"SELECT roles.id FROM roles WHERE roles.name = ANY ($1)",
&[&roles],
)?;
let role_ids: Vec<i32> = serde_postgres::from_rows(role_ids_result.iter())?;
let role_ids: HashSet<i32> = HashSet::from_iter(role_ids.into_iter());
let role_result = transaction.query("SELECT roles.id FROM roles, user_roles WHERE roles.id = user_roles.role_id AND user_roles.user_id = $1", &[&user_id])?;
let current_roles: Vec<i32> = serde_postgres::from_rows(role_result.iter())?;
let current_roles = HashSet::from_iter(current_roles.into_iter());
let added_roles: HashSet<&i32> = role_ids.difference(&current_roles).collect();
let removed_roles: HashSet<&i32> = current_roles.difference(&role_ids).collect();
for role in removed_roles {
transaction.query(
"DELETE FROM user_roles WHERE role_id = $1 AND user_id = $2",
&[role, &user_id],
)?;
}
for role in added_roles {
transaction.query(
"INSERT INTO user_roles (user_id, role_id) VALUES ($1, $2)",
&[&user_id, role],
)?;
}
transaction.commit()?;
Ok(self.by_user(user_id)?)
}
} }

@ -7,7 +7,7 @@ use std::sync::Arc;
use parking_lot::Mutex; use parking_lot::Mutex;
use zeroize::{Zeroize, Zeroizing}; use zeroize::{Zeroize, Zeroizing};
use crate::database::models::{UserInformation, UserRecord}; use crate::database::models::{Permission, UserInformation, UserRecord};
use crate::database::tokens::{SessionTokens, TokenStore}; use crate::database::tokens::{SessionTokens, TokenStore};
use crate::database::user_roles::UserRoles; use crate::database::user_roles::UserRoles;
use crate::database::{DatabaseResult, PostgresPool, Table}; use crate::database::{DatabaseResult, PostgresPool, Table};
@ -242,6 +242,7 @@ impl Users {
pub fn delete_tokens(&self, request_token: &String) -> DatabaseResult<bool> { pub fn delete_tokens(&self, request_token: &String) -> DatabaseResult<bool> {
let mut token_store = self.token_store.lock(); let mut token_store = self.token_store.lock();
let tokens = token_store.get_by_request_token(request_token); let tokens = token_store.get_by_request_token(request_token);
if let Some(tokens) = tokens { if let Some(tokens) = tokens {
tokens.invalidate(); tokens.invalidate();
@ -288,4 +289,22 @@ impl Users {
Ok(pw_hash == *original_pw_hash.as_slice()) Ok(pw_hash == *original_pw_hash.as_slice())
} }
pub fn get_permissions(&self, email: &String) -> DatabaseResult<Vec<Permission>> {
let mut connection = self.pool.get()?;
let results = connection.query(
"\
SELECT permissions.id, permissions.name, permissions.description
FROM permissions, role_permissions, user_roles, users
WHERE users.email = $1
AND users.id = user_roles.user_id
AND role_permissions.role_id = user_roles.role_id
AND permissions.id = role_permissions.permission_id
",
&[&email],
)?;
let permissions: Vec<Permission> = serde_postgres::from_rows(results.iter())?;
Ok(permissions)
}
} }

@ -11,7 +11,7 @@ use rouille::{Request, Response, Server};
use serde::export::Formatter; use serde::export::Formatter;
use serde::Serialize; use serde::Serialize;
use crate::database::models::{Role, UserFullInformation, UserInformation}; use crate::database::models::{Permission, Role, UserFullInformation, UserInformation};
use crate::database::permissions::{ use crate::database::permissions::{
ROLE_CREATE_PERM, ROLE_DELETE_PERM, ROLE_UPDATE_PERM, ROLE_VIEW_PERM, USER_CREATE_PERM, ROLE_CREATE_PERM, ROLE_DELETE_PERM, ROLE_UPDATE_PERM, ROLE_VIEW_PERM, USER_CREATE_PERM,
USER_DELETE_PERM, USER_UPDATE_PERM, USER_VIEW_PERM, USER_DELETE_PERM, USER_UPDATE_PERM, USER_VIEW_PERM,
@ -133,6 +133,9 @@ impl UserHttpServer {
(GET) (/users/{email: String}) => { (GET) (/users/{email: String}) => {
Self::get_user(&database, request, email).unwrap_or_else(HTTPError::into) Self::get_user(&database, request, email).unwrap_or_else(HTTPError::into)
}, },
(GET) (/users/{email: String}/permissions) => {
Self::get_user_permissions(&database, request, email).unwrap_or_else(HTTPError::into)
},
(GET) (/users) => { (GET) (/users) => {
Self::get_users(&database, request).unwrap_or_else(HTTPError::into) Self::get_users(&database, request).unwrap_or_else(HTTPError::into)
}, },
@ -239,6 +242,11 @@ impl UserHttpServer {
"POST", "POST",
"Deletes a user", "Deletes a user",
)?; )?;
doc.add_path::<(), Vec<Permission>>(
"/users/{email:String}/permissions",
"GET",
"Returns a list of permissions the user was granted",
)?;
Ok(doc) Ok(doc)
} }
@ -375,7 +383,7 @@ impl UserHttpServer {
/// Returns information for a single user /// Returns information for a single user
fn get_user(database: &Database, request: &Request, email: String) -> HTTPResult<Response> { fn get_user(database: &Database, request: &Request, email: String) -> HTTPResult<Response> {
require_permission!(database, request, USER_VIEW_PERM); check_user_permission_or_self(request, database, &email, USER_VIEW_PERM)?;
let user = database.users.get_user_by_email(&email)?; let user = database.users.get_user_by_email(&email)?;
let roles = database.user_roles.by_user(user.id)?; let roles = database.user_roles.by_user(user.id)?;
@ -410,9 +418,10 @@ impl UserHttpServer {
/// Updates the information of a user. This requires the operating user to revalidate his password /// Updates the information of a user. This requires the operating user to revalidate his password
fn update_user(database: &Database, request: &Request, email: String) -> HTTPResult<Response> { fn update_user(database: &Database, request: &Request, email: String) -> HTTPResult<Response> {
let (_, id) = validate_request_token(request, database)?; let logged_in_user =
check_user_permission_or_self(request, database, &email, USER_UPDATE_PERM)?;
let message = deserialize_body::<UpdateUserRequest>(&request)?; let message = deserialize_body::<UpdateUserRequest>(&request)?;
let logged_in_user = database.users.get_user(id)?;
if !database if !database
.users .users
.validate_login(&logged_in_user.email, &message.own_password)? .validate_login(&logged_in_user.email, &message.own_password)?
@ -423,9 +432,6 @@ impl UserHttpServer {
)); ));
} }
if logged_in_user.email != email {
require_permission!(database, request, USER_UPDATE_PERM);
}
let user_record = database.users.get_user_by_email(&email)?; let user_record = database.users.get_user_by_email(&email)?;
let record = database.users.update_user( let record = database.users.update_user(
&email, &email,
@ -433,15 +439,26 @@ impl UserHttpServer {
&message.email.clone().unwrap_or(user_record.email), &message.email.clone().unwrap_or(user_record.email),
&message.password, &message.password,
)?; )?;
let roles = if let Some(roles) = &message.roles {
require_permission!(database, request, USER_UPDATE_PERM);
database.user_roles.update_roles(record.id, roles.clone())?
} else {
database.user_roles.by_user(record.id)?
};
Ok(Response::json(&record)) Ok(Response::json(&UserFullInformation {
id: record.id,
email: record.email,
name: record.name,
roles,
}))
} }
/// Deletes a user completely /// Deletes a user completely
fn delete_user(database: &Database, request: &Request, email: String) -> HTTPResult<Response> { fn delete_user(database: &Database, request: &Request, email: String) -> HTTPResult<Response> {
let (_, id) = validate_request_token(request, database)?; let logged_in_user =
let message = deserialize_body::<DeleteUserRequest>(&request)?; check_user_permission_or_self(request, database, &email, USER_DELETE_PERM)?;
let logged_in_user = database.users.get_user(id)?; let message = deserialize_body::<DeleteUserRequest>(request)?;
if !database if !database
.users .users
@ -452,9 +469,7 @@ impl UserHttpServer {
401, 401,
)); ));
} }
if !database.users.has_permission(id, USER_DELETE_PERM)? {
return Err(HTTPError::new("Insufficient permissions".to_string(), 403));
}
database.users.delete_user(&email)?; database.users.delete_user(&email)?;
Ok(Response::json(&DeleteUserResponse { Ok(Response::json(&DeleteUserResponse {
@ -462,6 +477,18 @@ impl UserHttpServer {
email, email,
})) }))
} }
/// Returns a list of permissions the user has
fn get_user_permissions(
database: &Database,
request: &Request,
email: String,
) -> HTTPResult<Response> {
check_user_permission_or_self(request, database, &email, USER_VIEW_PERM)?;
let permissions = database.users.get_permissions(&email)?;
Ok(Response::json(&permissions))
}
} }
/// Parses the body of a http request into a string representation /// Parses the body of a http request into a string representation
@ -500,3 +527,20 @@ fn validate_request_token(request: &Request, database: &Database) -> HTTPResult<
)) ))
} }
} }
/// Returns if the user has a certain permission or queries him/herself
fn check_user_permission_or_self(
request: &Request,
database: &Database,
email: &String,
permission: &str,
) -> HTTPResult<UserInformation> {
let (_, id) = validate_request_token(request, database)?;
let logged_in_user = database.users.get_user(id)?;
if &logged_in_user.email != email && !database.users.has_permission(id, permission)? {
Err(HTTPError::new("Insufficient permission".to_string(), 403))
} else {
Ok(logged_in_user)
}
}

@ -126,6 +126,7 @@ pub struct UpdateUserRequest {
pub name: Option<String>, pub name: Option<String>,
pub email: Option<String>, pub email: Option<String>,
pub password: Option<String>, pub password: Option<String>,
pub roles: Option<Vec<String>>,
pub own_password: String, pub own_password: String,
} }

Loading…
Cancel
Save