1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2024-11-22 21:39:21 +00:00

Start to implement admin roles logic

This commit is contained in:
Pierre HUBERT 2021-05-14 18:25:53 +02:00
parent 57a5752fe7
commit 73837553c7
8 changed files with 182 additions and 23 deletions

View File

@ -289,3 +289,10 @@ CREATE TABLE `comunic_admin_key` (
`time_add` INT NULL, `time_add` INT NULL,
`credential` TEXT NULL, `credential` TEXT NULL,
PRIMARY KEY (`id`)); PRIMARY KEY (`id`));
CREATE TABLE `comunic_admin_roles` (
`id` INT NOT NULL AUTO_INCREMENT,
`admin_id` INT NOT NULL,
`role_id` VARCHAR(25) NULL,
`time_insert` INT NULL,
PRIMARY KEY (`id`));

View File

@ -15,3 +15,10 @@ CREATE TABLE `comunic_admin_key` (
`time_add` INT NULL, `time_add` INT NULL,
`credential` TEXT NULL, `credential` TEXT NULL,
PRIMARY KEY (`id`)); PRIMARY KEY (`id`));
CREATE TABLE `comunic_admin_roles` (
`id` INT NOT NULL AUTO_INCREMENT,
`admin_id` INT NOT NULL,
`role_id` VARCHAR(25) NULL,
`time_insert` INT NULL,
PRIMARY KEY (`id`));

View File

@ -64,6 +64,7 @@ pub mod database_tables_names {
/// Administrators tables /// Administrators tables
pub const ADMIN_LIST_TABLE: &str = "comunic_admin"; pub const ADMIN_LIST_TABLE: &str = "comunic_admin";
pub const ADMIN_KEYS_TABLE: &str = "comunic_admin_key"; pub const ADMIN_KEYS_TABLE: &str = "comunic_admin_key";
pub const ADMIN_ROLES_TABLE: &str = "comunic_admin_roles";
} }
/// Push Notifications Database prefix /// Push Notifications Database prefix
@ -260,3 +261,42 @@ pub mod accounts_info_policy {
/// Url where Firebase push notifications can be sent /// Url where Firebase push notifications can be sent
pub const FIREBASE_PUSH_MESSAGE_URL: &str = "https://fcm.googleapis.com/v1/projects/{PROJECT_ID}/messages:send"; pub const FIREBASE_PUSH_MESSAGE_URL: &str = "https://fcm.googleapis.com/v1/projects/{PROJECT_ID}/messages:send";
/// Admin-specific constants
pub mod admin {
#[derive(Copy, Clone, Eq, PartialEq)]
#[allow(non_camel_case_types)]
pub enum AdminRole {
MANAGE_ADMINS,
MANAGE_USERS,
ACCESS_FULL_ADMIN_LOGS,
}
pub struct AdminRoleMetadata {
pub role: AdminRole,
pub id: &'static str,
pub name: &'static str,
pub description: &'static str,
}
pub const ADMIN_ROLES_LIST: [AdminRoleMetadata; 3] = [
AdminRoleMetadata {
role: AdminRole::MANAGE_ADMINS,
id: "manage_admins",
name: "Manage administrators",
description: "Allow the admin to create, list and update all administrators",
},
AdminRoleMetadata {
role: AdminRole::MANAGE_USERS,
id: "manage_users",
name: "Manage Comunic users",
description: "Allow the admin to list, reset password and delete Comunic users",
},
AdminRoleMetadata {
role: AdminRole::ACCESS_FULL_ADMIN_LOGS,
id: "access_full_admin_logs",
name: "Access full admin logs",
description: "Allow the admin to access the action history of all admins",
}
];
}

View File

@ -4,6 +4,8 @@
use webauthn_rs::proto::Credential; use webauthn_rs::proto::Credential;
use crate::constants::admin::{ADMIN_ROLES_LIST, AdminRole};
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub struct AdminID(u64); pub struct AdminID(u64);
@ -37,6 +39,7 @@ pub struct Admin {
pub name: String, pub name: String,
pub email: String, pub email: String,
pub reset_token: Option<AdminResetToken>, pub reset_token: Option<AdminResetToken>,
pub roles: Vec<AdminRole>,
} }
pub struct AdminKey { pub struct AdminKey {
@ -63,3 +66,20 @@ pub struct NewAdminGeneralSettings {
pub name: String, pub name: String,
pub email: String, pub email: String,
} }
impl AdminRole {
pub fn from_id(id: &str) -> Option<Self> {
ADMIN_ROLES_LIST.iter()
.filter(|r| r.id.eq(id))
.map(|r| r.role)
.next()
}
pub fn to_id(&self) -> &'static str {
ADMIN_ROLES_LIST.iter()
.filter(|r| r.role.eq(self))
.map(|r| r.id)
.next()
.expect("Should have found a role!!!")
}
}

View File

@ -6,7 +6,7 @@ use crate::constants::{ADMIN_RESET_TOKEN_LENGTH, ADMIN_RESET_TOKEN_LIFETIME};
use crate::constants::database_tables_names::ADMIN_LIST_TABLE; use crate::constants::database_tables_names::ADMIN_LIST_TABLE;
use crate::data::admin::{Admin, AdminID, AdminResetToken, NewAdmin, NewAdminGeneralSettings}; use crate::data::admin::{Admin, AdminID, AdminResetToken, NewAdmin, NewAdminGeneralSettings};
use crate::data::error::{ExecError, Res}; use crate::data::error::{ExecError, Res};
use crate::helpers::database; use crate::helpers::{admin_roles_helper, database};
use crate::utils::crypt_utils::rand_str; use crate::utils::crypt_utils::rand_str;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;
@ -85,12 +85,14 @@ fn db_to_admin(row: &database::RowResult) -> Res<Admin> {
}) })
}; };
let admin_id = row.get_admin_id("id")?;
Ok(Admin { Ok(Admin {
id: row.get_admin_id("id")?, id: admin_id,
time_create: row.get_u64("time_create")?, time_create: row.get_u64("time_create")?,
name: row.get_str("name")?, name: row.get_str("name")?,
email: row.get_str("email")?, email: row.get_str("email")?,
reset_token, reset_token,
roles: admin_roles_helper::get_roles(admin_id)?,
}) })
} }

View File

@ -0,0 +1,37 @@
//! # Admin roles helper
//!
//! @author Pierre Hubert
use crate::constants::admin::AdminRole;
use crate::constants::database_tables_names::ADMIN_ROLES_TABLE;
use crate::data::admin::AdminID;
use crate::data::error::{ExecError, Res};
use crate::helpers::database;
use crate::utils::date_utils::time;
/// Get the list of roles of a given administrator
pub fn get_roles(id: AdminID) -> Res<Vec<AdminRole>> {
database::QueryInfo::new(ADMIN_ROLES_TABLE)
.cond_admin_id("id", id)
.exec(db_to_role)
}
/// Add a new role to a user
pub fn add_role(id: AdminID, role: AdminRole) -> Res {
database::InsertQuery::new(ADMIN_ROLES_TABLE)
.add_admin_id("admin_id", id)
.add_str("role_id", role.to_id())
.add_u64("time_insert", time())
.insert_drop_result()
}
fn db_to_role(row: &database::RowResult) -> Res<AdminRole> {
let role_id = row.get_str("role_id")?;
let role = AdminRole::from_id(&role_id);
match role {
None => Err(ExecError::boxed_string(format!("Role {} from database not found in the list!", role_id))),
Some(r) => Ok(r)
}
}

View File

@ -27,3 +27,4 @@ pub mod admin_account_key_helper;
pub mod admin_access_token_helper; pub mod admin_access_token_helper;
pub mod admin_key_registration_challenges_helper; pub mod admin_key_registration_challenges_helper;
pub mod admin_key_authentication_challenges_helper; pub mod admin_key_authentication_challenges_helper;
pub mod admin_roles_helper;

View File

@ -1,17 +1,18 @@
use comunic_server::{cleanup_thread, server}; use comunic_server::{cleanup_thread, server};
use comunic_server::constants::admin::{ADMIN_ROLES_LIST, AdminRole};
use comunic_server::data::admin::NewAdmin; use comunic_server::data::admin::NewAdmin;
use comunic_server::data::config::{conf, Config}; use comunic_server::data::config::{conf, Config};
use comunic_server::data::error::Res; use comunic_server::data::error::Res;
use comunic_server::data::user::UserID; use comunic_server::data::user::UserID;
use comunic_server::helpers::{account_helper, admin_account_helper, database}; use comunic_server::helpers::{account_helper, admin_account_helper, admin_roles_helper, database};
use comunic_server::utils::date_utils::current_year; use comunic_server::utils::date_utils::current_year;
type MainActionFunction = Res; type MainActionFunction = Res;
struct Action { struct Action {
name: String, name: &'static str,
description: String, description: &'static str,
arguments: Vec<String>, arguments: Vec<&'static str>,
function: Box<dyn Fn(Vec<String>) -> MainActionFunction>, function: Box<dyn Fn(Vec<String>) -> MainActionFunction>,
} }
@ -19,43 +20,59 @@ fn get_actions() -> Vec<Action> {
vec![ vec![
// Start server // Start server
Action { Action {
name: "serve".to_string(), name: "serve",
description: "Start the Comunic Server (default action)".to_string(), description: "Start the Comunic Server (default action)",
arguments: vec![], arguments: vec![],
function: Box::new(serve), function: Box::new(serve),
}, },
// Show help // Show help
Action { Action {
name: "help".to_string(), name: "help",
description: "Show this help".to_string(), description: "Show this help",
arguments: vec![], arguments: vec![],
function: Box::new(help), function: Box::new(help),
}, },
// Reset password // Reset password
Action { Action {
name: "reset_password".to_string(), name: "reset_password",
description: "Create a password reset URL for a user".to_string(), description: "Create a password reset URL for a user",
arguments: vec!["user_id".to_string()], arguments: vec!["user_id"],
function: Box::new(reset_password), function: Box::new(reset_password),
}, },
// Create a new administrator // Create a new administrator
Action { Action {
name: "create_admin".to_string(), name: "create_admin",
description: "Create a new administrator account".to_string(), description: "Create a new administrator account",
arguments: vec!["name".to_string(), "email".to_string()], arguments: vec!["name", "email"],
function: Box::new(create_admin), function: Box::new(create_admin),
}, },
// Create a reset token for an admin // Create a reset token for an admin
Action { Action {
name: "create_admin_reset_token".to_string(), name: "create_admin_reset_token",
description: "Create a new reset token to register a new access key to an admin account".to_string(), description: "Create a new reset token to register a new access key to an admin account",
arguments: vec!["email".to_string()], arguments: vec!["email"],
function: Box::new(create_admin_reset_token), function: Box::new(create_admin_reset_token),
}, },
// Get the list of available admin roles
Action {
name: "list_admin_roles",
description: "Get the list of available admin roles",
arguments: vec![],
function: Box::new(list_admin_roles),
},
// Attribute a role to an admin
Action {
name: "grant_admin_role",
description: "Grant a role to an admin",
arguments: vec!["mail", "role_id"],
function: Box::new(grant_admin_role),
}
] ]
} }
@ -143,7 +160,7 @@ fn help(_a: Vec<String>) -> Res {
println!("Usage: {} [conf-file] [action] [args...]", std::env::args().next().unwrap()); println!("Usage: {} [conf-file] [action] [args...]", std::env::args().next().unwrap());
println!("Available actions:"); println!("Available actions:");
for action in get_actions() { for action in get_actions() {
println!("\t{}\t{} - {}", println!("\t{} {}\t- {}",
action.name, action.name,
action.arguments.iter().map(|s| format!("[{}]", s)).collect::<Vec<String>>().join(" "), action.arguments.iter().map(|s| format!("[{}]", s)).collect::<Vec<String>>().join(" "),
action.description action.description
@ -194,3 +211,31 @@ fn create_admin_reset_token(args: Vec<String>) -> Res {
Ok(()) Ok(())
} }
fn list_admin_roles(_a: Vec<String>) -> Res {
println!("Here are the currently defined roles in the code:\n");
for role in Vec::from(ADMIN_ROLES_LIST) {
println!("* {} - {}\n{}\n", role.id, role.name, role.description);
}
Ok(())
}
fn grant_admin_role(args: Vec<String>) -> Res {
let role = AdminRole::from_id(&args[1])
.expect("Requested role does not exist!");
let admin = admin_account_helper::find_admin_by_email(&args[0])
.expect("Failed to load admin information!");
if admin.roles.contains(&role) {
eprintln!("The administrator has already this role!");
std::process::exit(-3);
}
admin_roles_helper::add_role(admin.id, role)?;
println!("Success.");
Ok(())
}