diff --git a/src/api_data/admin/admin_info_api.rs b/src/api_data/admin/admin_info_api.rs index aaaf8f1..1101ae3 100644 --- a/src/api_data/admin/admin_info_api.rs +++ b/src/api_data/admin/admin_info_api.rs @@ -12,6 +12,7 @@ pub struct AdminInfoAPI { name: String, email: String, time_create: u64, + roles: Vec<&'static str>, } impl AdminInfoAPI { @@ -21,6 +22,7 @@ impl AdminInfoAPI { name: a.name.clone(), email: a.email.clone(), time_create: a.time_create, + roles: a.roles.iter().map(|r| r.to_id()).collect(), } } } \ No newline at end of file diff --git a/src/api_data/admin/admin_role_api.rs b/src/api_data/admin/admin_role_api.rs new file mode 100644 index 0000000..61c2df7 --- /dev/null +++ b/src/api_data/admin/admin_role_api.rs @@ -0,0 +1,24 @@ +//! # Administrator role details api +//! +//! @author Pierre Hubert + +use serde::Serialize; + +use crate::constants::admin::AdminRoleMetadata; + +#[derive(Serialize)] +pub struct AdminRoleDetailsAPI { + id: &'static str, + name: &'static str, + description: &'static str, +} + +impl AdminRoleDetailsAPI { + pub fn new(r: &AdminRoleMetadata) -> Self { + Self { + id: r.id, + name: r.name, + description: r.description, + } + } +} \ No newline at end of file diff --git a/src/api_data/admin/mod.rs b/src/api_data/admin/mod.rs index 133100b..cbb6dec 100644 --- a/src/api_data/admin/mod.rs +++ b/src/api_data/admin/mod.rs @@ -7,4 +7,5 @@ pub mod admin_auth_success; pub mod admin_id_api; pub mod admin_info_api; pub mod admin_keys_api; -pub mod admin_res_create_reset_token; \ No newline at end of file +pub mod admin_res_create_reset_token; +pub mod admin_role_api; \ No newline at end of file diff --git a/src/controllers/admin/admin_account_controller.rs b/src/controllers/admin/admin_account_controller.rs index ec9f9dc..3e3bc63 100644 --- a/src/controllers/admin/admin_account_controller.rs +++ b/src/controllers/admin/admin_account_controller.rs @@ -3,41 +3,19 @@ //! @author Pierre Hubert - use crate::api_data::admin::admin_auth_options::AdminAuthOptions; use crate::api_data::admin::admin_auth_success::AdminAuthSuccess; use crate::api_data::admin::admin_id_api::AdminIDAPI; use crate::api_data::admin::admin_info_api::AdminInfoAPI; use crate::api_data::admin::admin_keys_api::AdminKeyAPI; use crate::api_data::admin::admin_res_create_reset_token::AdminResCreateResetToken; -use crate::data::admin::{AdminKey, NewAdminGeneralSettings}; +use crate::data::admin::NewAdminGeneralSettings; use crate::data::base_request_handler::BaseRequestHandler; -use crate::data::error::Res; use crate::data::http_request_handler::HttpRequestHandler; -use crate::data::webauthn_config::get_wan; -use crate::helpers::{admin_access_token_helper, admin_account_helper, admin_account_key_helper, admin_key_authentication_challenges_helper, admin_key_registration_challenges_helper}; +use crate::helpers::{admin_access_token_helper, admin_account_helper, admin_account_key_helper}; use crate::routes::RequestResult; use crate::utils::date_utils::time; -impl HttpRequestHandler { - pub fn post_admin_auth_key(&mut self, name_mail: &str, name_key_id: &str) -> Res { - let mail = self.post_string(name_mail)?; - let key_id = self.post_u64(name_key_id)?; - - let admin = admin_account_helper::find_admin_by_email(&mail)?; - let keys = admin_account_key_helper::get_admin_keys(admin.id)?; - - let key = keys.into_iter() - .filter(|k| k.id == key_id) - .next(); - - match key { - Some(key) => Ok(key), - None => Err(self.bad_request("The key is not associated with this account!".to_string()).unwrap_err()) - } - } -} - /// Get admin auth options pub fn get_auth_options(r: &mut HttpRequestHandler) -> RequestResult { let mail = r.post_email("mail")?; @@ -137,85 +115,4 @@ pub fn generate_reset_token(r: &mut HttpRequestHandler) -> RequestResult { let token = admin_account_helper::create_new_reset_token(admin_id)?; r.set_response(AdminResCreateResetToken::new(token)) -} - -/// Generate a challenge to register a new key -pub fn challenge_register_key(r: &mut HttpRequestHandler) -> RequestResult { - let mut wan = get_wan(); - - let (res, state) = wan.generate_challenge_register(&r.admin_id()?.id_str(), None)?; - - admin_key_registration_challenges_helper::set(r.admin_id()?, state)?; - - r.set_response(res) -} - -/// Register key -pub fn register_key(r: &mut HttpRequestHandler) -> RequestResult { - let name = r.post_string("name")?; - - let creds = r.post_register_public_key_credential("key")?; - let state = r.some_or_internal_error( - admin_key_registration_challenges_helper::get(r.admin_id()?)?, - "No challenge found!", - )?; - - let wan = get_wan(); - let key = wan.register_credential(creds, state, |_| Ok(false))?; - - admin_account_key_helper::add_key(r.admin_id()?, &name, key)?; - - r.ok() -} - -/// Delete an admin auth key -pub fn delete_auth_key(r: &mut HttpRequestHandler) -> RequestResult { - let admin_id = r.post_admin_id("adminID")?; - let key_id = r.post_u64("keyID")?; - - if admin_id != r.admin_id()? { - unimplemented!(); // TODO - } - - for key in admin_account_key_helper::get_admin_keys(admin_id)? { - if key.id == key_id { - admin_account_key_helper::delete_key(key)?; - - return r.ok(); - } - } - - r.not_found("Requested key was not found!".to_string()) -} - -/// Generate a challenge to authenticate with a security key -pub fn challenge_auth_with_key(r: &mut HttpRequestHandler) -> RequestResult { - let key = r.post_admin_auth_key("mail", "key_id")?; - - let (challenge_response, auth_state) = - get_wan().generate_challenge_authenticate(vec![key.key], None)?; - - admin_key_authentication_challenges_helper::set(key.id, auth_state)?; - - r.set_response(challenge_response) -} - -/// Authenticate a user with a security key -pub fn auth_with_key(r: &mut HttpRequestHandler) -> RequestResult { - let key = r.post_admin_auth_key("mail", "key_id")?; - let credentials = r.post_auth_public_key_credential("credential")?; - - let state = r.some_or_internal_error( - admin_key_authentication_challenges_helper::get(key.id)?, - "Associated authentication state not found!", - )?; - - // Perform authentication - let state = get_wan().authenticate_credential(credentials, state)?; - r.some_or_bad_request(state, "Invalid key!")?; - - // Generate access token - let token = admin_access_token_helper::create(key.admin_id)?; - - r.set_response(AdminAuthSuccess::new(token)) } \ No newline at end of file diff --git a/src/controllers/admin/admin_keys_controller.rs b/src/controllers/admin/admin_keys_controller.rs new file mode 100644 index 0000000..9d10642 --- /dev/null +++ b/src/controllers/admin/admin_keys_controller.rs @@ -0,0 +1,113 @@ +//! # Admin keys controller +//! +//! @author Pierre Hubert + + +use crate::api_data::admin::admin_auth_success::AdminAuthSuccess; +use crate::data::admin::AdminKey; +use crate::data::base_request_handler::BaseRequestHandler; +use crate::data::error::Res; +use crate::data::http_request_handler::HttpRequestHandler; +use crate::data::webauthn_config::get_wan; +use crate::helpers::{admin_access_token_helper, admin_account_helper, admin_account_key_helper, admin_key_authentication_challenges_helper, admin_key_registration_challenges_helper}; +use crate::routes::RequestResult; + +impl HttpRequestHandler { + pub fn post_admin_auth_key(&mut self, name_mail: &str, name_key_id: &str) -> Res { + let mail = self.post_string(name_mail)?; + let key_id = self.post_u64(name_key_id)?; + + let admin = admin_account_helper::find_admin_by_email(&mail)?; + let keys = admin_account_key_helper::get_admin_keys(admin.id)?; + + let key = keys.into_iter() + .filter(|k| k.id == key_id) + .next(); + + match key { + Some(key) => Ok(key), + None => Err(self.bad_request("The key is not associated with this account!".to_string()).unwrap_err()) + } + } +} + +/// Generate a challenge to register a new key +pub fn challenge_register_key(r: &mut HttpRequestHandler) -> RequestResult { + let mut wan = get_wan(); + + let (res, state) = wan.generate_challenge_register(&r.admin_id()?.id_str(), None)?; + + admin_key_registration_challenges_helper::set(r.admin_id()?, state)?; + + r.set_response(res) +} + +/// Register key +pub fn register_key(r: &mut HttpRequestHandler) -> RequestResult { + let name = r.post_string("name")?; + + let creds = r.post_register_public_key_credential("key")?; + let state = r.some_or_internal_error( + admin_key_registration_challenges_helper::get(r.admin_id()?)?, + "No challenge found!", + )?; + + let wan = get_wan(); + let key = wan.register_credential(creds, state, |_| Ok(false))?; + + admin_account_key_helper::add_key(r.admin_id()?, &name, key)?; + + r.ok() +} + +/// Delete an admin auth key +pub fn delete_auth_key(r: &mut HttpRequestHandler) -> RequestResult { + let admin_id = r.post_admin_id("adminID")?; + let key_id = r.post_u64("keyID")?; + + if admin_id != r.admin_id()? { + unimplemented!(); // TODO + } + + for key in admin_account_key_helper::get_admin_keys(admin_id)? { + if key.id == key_id { + admin_account_key_helper::delete_key(key)?; + + return r.ok(); + } + } + + r.not_found("Requested key was not found!".to_string()) +} + +/// Generate a challenge to authenticate with a security key +pub fn challenge_auth_with_key(r: &mut HttpRequestHandler) -> RequestResult { + let key = r.post_admin_auth_key("mail", "key_id")?; + + let (challenge_response, auth_state) = + get_wan().generate_challenge_authenticate(vec![key.key], None)?; + + admin_key_authentication_challenges_helper::set(key.id, auth_state)?; + + r.set_response(challenge_response) +} + +/// Authenticate a user with a security key +pub fn auth_with_key(r: &mut HttpRequestHandler) -> RequestResult { + let key = r.post_admin_auth_key("mail", "key_id")?; + let credentials = r.post_auth_public_key_credential("credential")?; + + let state = r.some_or_internal_error( + admin_key_authentication_challenges_helper::get(key.id)?, + "Associated authentication state not found!", + )?; + + // Perform authentication + let state = get_wan().authenticate_credential(credentials, state)?; + r.some_or_bad_request(state, "Invalid key!")?; + + // Generate access token + let token = admin_access_token_helper::create(key.admin_id)?; + + r.set_response(AdminAuthSuccess::new(token)) +} \ No newline at end of file diff --git a/src/controllers/admin/admin_roles_controller.rs b/src/controllers/admin/admin_roles_controller.rs new file mode 100644 index 0000000..5a1d0c3 --- /dev/null +++ b/src/controllers/admin/admin_roles_controller.rs @@ -0,0 +1,17 @@ +//! # Admin roles controller +//! +//! @author Pierre Hubert + +use crate::api_data::admin::admin_role_api::AdminRoleDetailsAPI; +use crate::constants::admin::ADMIN_ROLES_LIST; +use crate::data::base_request_handler::BaseRequestHandler; +use crate::data::http_request_handler::HttpRequestHandler; +use crate::routes::RequestResult; + +pub fn get_list(r: &mut HttpRequestHandler) -> RequestResult { + let res = ADMIN_ROLES_LIST.iter() + .map(AdminRoleDetailsAPI::new) + .collect::>(); + + r.set_response(res) +} \ No newline at end of file diff --git a/src/controllers/admin/mod.rs b/src/controllers/admin/mod.rs index 3cc1dc5..34626d2 100644 --- a/src/controllers/admin/mod.rs +++ b/src/controllers/admin/mod.rs @@ -2,4 +2,6 @@ //! //! @author Pierre Hubert -pub mod admin_account_controller; \ No newline at end of file +pub mod admin_account_controller; +pub mod admin_keys_controller; +pub mod admin_roles_controller; \ No newline at end of file diff --git a/src/routes.rs b/src/routes.rs index 8e49c19..d04441e 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -355,10 +355,15 @@ pub fn get_routes() -> Vec { Route::admin_post("/admin/accounts/keys", Box::new(admin_account_controller::get_keys_list)), Route::admin_post("/admin/accounts/update_general_settings", Box::new(admin_account_controller::update_general_settings)), Route::admin_post("/admin/accounts/generate_reset_token", Box::new(admin_account_controller::generate_reset_token)), - Route::admin_post("/admin/accounts/challenge_register_key", Box::new(admin_account_controller::challenge_register_key)), - Route::admin_post("/admin/accounts/register_key", Box::new(admin_account_controller::register_key)), - Route::admin_post("/admin/accounts/delete_auth_key", Box::new(admin_account_controller::delete_auth_key)), - Route::limited_admin_post_without_login("/admin/accounts/challenge_auth_with_key", Box::new(admin_account_controller::challenge_auth_with_key), LimitPolicy::ANY(10)), - Route::limited_admin_post_without_login("/admin/accounts/auth_with_key", Box::new(admin_account_controller::auth_with_key), LimitPolicy::ANY(10)), + + // Admin security keys controller + Route::admin_post("/admin/keys/challenge_register_key", Box::new(admin_keys_controller::challenge_register_key)), + Route::admin_post("/admin/keys/register_key", Box::new(admin_keys_controller::register_key)), + Route::admin_post("/admin/keys/delete_auth_key", Box::new(admin_keys_controller::delete_auth_key)), + Route::limited_admin_post_without_login("/admin/keys/challenge_auth_with_key", Box::new(admin_keys_controller::challenge_auth_with_key), LimitPolicy::ANY(10)), + Route::limited_admin_post_without_login("/admin/keys/auth_with_key", Box::new(admin_keys_controller::auth_with_key), LimitPolicy::ANY(10)), + + // Admin roles controller + Route::admin_post("/admin/roles/list", Box::new(admin_roles_controller::get_list)) ] } \ No newline at end of file