2021-02-13 15:36:39 +00:00
|
|
|
use comunic_server::{cleanup_thread, server};
|
2021-05-14 16:25:53 +00:00
|
|
|
use comunic_server::constants::admin::{ADMIN_ROLES_LIST, AdminRole};
|
2021-05-08 17:17:33 +00:00
|
|
|
use comunic_server::data::admin::NewAdmin;
|
2021-10-17 11:51:05 +00:00
|
|
|
use comunic_server::data::api_client::APIClient;
|
2020-05-20 17:05:59 +00:00
|
|
|
use comunic_server::data::config::{conf, Config};
|
2021-05-04 17:51:18 +00:00
|
|
|
use comunic_server::data::error::Res;
|
|
|
|
use comunic_server::data::user::UserID;
|
2021-10-17 11:51:05 +00:00
|
|
|
use comunic_server::helpers::{account_helper, admin_account_helper, admin_roles_helper, api_helper, database};
|
2021-05-04 17:36:31 +00:00
|
|
|
use comunic_server::utils::date_utils::current_year;
|
2020-05-22 06:51:15 +00:00
|
|
|
|
2021-05-04 17:51:18 +00:00
|
|
|
type MainActionFunction = Res;
|
2021-05-04 17:18:47 +00:00
|
|
|
|
|
|
|
struct Action {
|
2021-05-14 16:25:53 +00:00
|
|
|
name: &'static str,
|
|
|
|
description: &'static str,
|
|
|
|
arguments: Vec<&'static str>,
|
2021-05-04 17:51:18 +00:00
|
|
|
function: Box<dyn Fn(Vec<String>) -> MainActionFunction>,
|
2021-05-04 17:36:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_actions() -> Vec<Action> {
|
|
|
|
vec![
|
|
|
|
// Start server
|
|
|
|
Action {
|
2021-05-14 16:25:53 +00:00
|
|
|
name: "serve",
|
|
|
|
description: "Start the Comunic Server (default action)",
|
2021-05-04 17:36:31 +00:00
|
|
|
arguments: vec![],
|
|
|
|
function: Box::new(serve),
|
|
|
|
},
|
|
|
|
|
|
|
|
// Show help
|
|
|
|
Action {
|
2021-05-14 16:25:53 +00:00
|
|
|
name: "help",
|
|
|
|
description: "Show this help",
|
2021-05-04 17:36:31 +00:00
|
|
|
arguments: vec![],
|
|
|
|
function: Box::new(help),
|
2021-05-04 17:51:18 +00:00
|
|
|
},
|
|
|
|
|
2021-10-17 11:51:05 +00:00
|
|
|
// Get the list of registered clients
|
|
|
|
Action {
|
|
|
|
name: "clients_list",
|
|
|
|
description: "Get the list of registered clients",
|
|
|
|
arguments: vec![],
|
|
|
|
function: Box::new(list_clients),
|
|
|
|
},
|
|
|
|
|
|
|
|
// Register a new API client
|
|
|
|
Action {
|
|
|
|
name: "register_client",
|
|
|
|
description: "Register a new API client",
|
|
|
|
arguments: vec!["name", "origin", "comment"],
|
|
|
|
function: Box::new(register_client),
|
|
|
|
},
|
|
|
|
|
|
|
|
// Reset a user password
|
2021-05-04 17:51:18 +00:00
|
|
|
Action {
|
2021-05-14 16:25:53 +00:00
|
|
|
name: "reset_password",
|
|
|
|
description: "Create a password reset URL for a user",
|
|
|
|
arguments: vec!["user_id"],
|
2021-05-04 17:51:18 +00:00
|
|
|
function: Box::new(reset_password),
|
2021-05-08 17:17:33 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Create a new administrator
|
|
|
|
Action {
|
2021-05-14 16:25:53 +00:00
|
|
|
name: "create_admin",
|
|
|
|
description: "Create a new administrator account",
|
|
|
|
arguments: vec!["name", "email"],
|
2021-05-08 17:17:33 +00:00
|
|
|
function: Box::new(create_admin),
|
|
|
|
},
|
2021-05-08 17:22:47 +00:00
|
|
|
|
|
|
|
// Create a reset token for an admin
|
|
|
|
Action {
|
2021-05-14 16:25:53 +00:00
|
|
|
name: "create_admin_reset_token",
|
|
|
|
description: "Create a new reset token to register a new access key to an admin account",
|
|
|
|
arguments: vec!["email"],
|
2021-05-08 17:22:47 +00:00
|
|
|
function: Box::new(create_admin_reset_token),
|
|
|
|
},
|
2021-05-14 16:25:53 +00:00
|
|
|
|
|
|
|
// 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),
|
2022-03-09 18:58:46 +00:00
|
|
|
},
|
2021-05-04 17:36:31 +00:00
|
|
|
]
|
2021-05-04 17:18:47 +00:00
|
|
|
}
|
|
|
|
|
2022-03-09 18:58:46 +00:00
|
|
|
|
|
|
|
fn main() {
|
2021-05-04 17:18:47 +00:00
|
|
|
let args: Vec<String> = std::env::args().collect();
|
|
|
|
let conf_file = match args.get(1) {
|
|
|
|
Some(el) => el.to_string(),
|
2021-05-04 17:51:18 +00:00
|
|
|
None => {
|
|
|
|
eprintln!("Please specify configuration file as first argument!");
|
|
|
|
std::process::exit(-3);
|
|
|
|
}
|
2021-02-12 16:35:26 +00:00
|
|
|
};
|
2020-05-20 17:05:59 +00:00
|
|
|
|
|
|
|
// Load configuration
|
2021-02-12 16:35:26 +00:00
|
|
|
Config::load(&conf_file).expect("Could not load configuration!");
|
2020-05-20 17:05:59 +00:00
|
|
|
|
2020-05-21 07:21:58 +00:00
|
|
|
// Connect to the database
|
|
|
|
database::connect(&conf().database).expect("Could not connect to database!");
|
2020-05-20 17:05:59 +00:00
|
|
|
|
2021-05-04 17:18:47 +00:00
|
|
|
// Get selected action
|
|
|
|
let action = args
|
|
|
|
.get(2)
|
|
|
|
.map(|a| a.as_str())
|
|
|
|
.unwrap_or("serve")
|
|
|
|
.to_string();
|
|
|
|
|
2021-05-04 17:36:31 +00:00
|
|
|
let actions = get_actions();
|
|
|
|
|
|
|
|
let selected_action = actions
|
|
|
|
.iter()
|
|
|
|
.filter(|p| p.name.eq(&action))
|
|
|
|
.next();
|
|
|
|
|
|
|
|
let selected_action = match selected_action {
|
|
|
|
None => {
|
|
|
|
eprintln!("Action {} invalid! For more information try 'help'!", action);
|
|
|
|
std::process::exit(-1);
|
|
|
|
}
|
|
|
|
Some(a) => a
|
|
|
|
};
|
|
|
|
|
2021-05-04 17:51:18 +00:00
|
|
|
if !selected_action.arguments.is_empty() && selected_action.arguments.len() + 3 != args.len() {
|
2021-05-04 17:36:31 +00:00
|
|
|
eprintln!("Invalid number of arguments!");
|
|
|
|
std::process::exit(-2);
|
|
|
|
}
|
|
|
|
|
2021-05-05 05:38:40 +00:00
|
|
|
let args = match args.len() {
|
|
|
|
0 | 1 | 2 => vec![],
|
|
|
|
_ => (&args[3..]).to_vec()
|
|
|
|
};
|
2021-05-04 17:51:18 +00:00
|
|
|
|
|
|
|
let res = (selected_action.function)(args.to_vec());
|
|
|
|
res.expect("Failed to execute action!");
|
2020-05-20 15:46:05 +00:00
|
|
|
}
|
2021-05-04 17:18:47 +00:00
|
|
|
|
|
|
|
/// Start Comunic Server (main action)
|
2021-05-04 17:51:18 +00:00
|
|
|
fn serve(_a: Vec<String>) -> Res {
|
2022-03-09 18:58:46 +00:00
|
|
|
// Start cleanup thread
|
|
|
|
cleanup_thread::start().expect("Failed to start cleanup thread!");
|
2021-05-04 17:18:47 +00:00
|
|
|
|
2022-03-09 18:58:46 +00:00
|
|
|
// Start the server
|
|
|
|
Ok(server::start_server(conf())?)
|
2021-05-04 17:36:31 +00:00
|
|
|
}
|
|
|
|
|
2021-05-04 17:51:18 +00:00
|
|
|
fn help(_a: Vec<String>) -> Res {
|
2021-05-04 17:36:31 +00:00
|
|
|
println!("Comunic API v3 Server - (c) Pierre HUBERT 2012 - {}", current_year());
|
|
|
|
|
2021-05-04 17:51:18 +00:00
|
|
|
|
|
|
|
println!("Usage: {} [conf-file] [action] [args...]", std::env::args().next().unwrap());
|
2021-05-04 17:36:31 +00:00
|
|
|
println!("Available actions:");
|
|
|
|
for action in get_actions() {
|
2021-05-14 16:25:53 +00:00
|
|
|
println!("\t{} {}\t- {}",
|
2021-05-04 17:36:31 +00:00
|
|
|
action.name,
|
|
|
|
action.arguments.iter().map(|s| format!("[{}]", s)).collect::<Vec<String>>().join(" "),
|
|
|
|
action.description
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-05-04 17:51:18 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-10-17 11:51:05 +00:00
|
|
|
fn list_clients(_args: Vec<String>) -> Res {
|
|
|
|
for client in api_helper::get_clients()? {
|
|
|
|
println!(
|
2021-10-17 11:57:02 +00:00
|
|
|
"Client {}\n* Name: {}\n* Domain: {}\n* Comment: {}\n* Default tokens expiration time: {}\n* Firebase project: {}\n\n",
|
2021-10-17 11:51:05 +00:00
|
|
|
client.id,
|
|
|
|
client.name,
|
|
|
|
client.domain.unwrap_or("None".to_string()),
|
|
|
|
client.comment.unwrap_or("None".to_string()),
|
|
|
|
client.default_expiration_time,
|
|
|
|
client.firebase_project_name.unwrap_or("None".to_string())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_client(args: Vec<String>) -> Res {
|
|
|
|
let client = APIClient {
|
|
|
|
id: 0,
|
|
|
|
name: args[0].to_string(),
|
|
|
|
domain: match args[1].is_empty() {
|
|
|
|
true => None,
|
|
|
|
false => Some(args[1].to_string())
|
|
|
|
},
|
|
|
|
comment: match args[2].is_empty() {
|
|
|
|
true => None,
|
|
|
|
false => Some(args[2].to_string())
|
|
|
|
},
|
|
|
|
default_expiration_time: 0,
|
|
|
|
firebase_project_name: None,
|
|
|
|
firebase_service_account_file: None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let id = api_helper::register_client(&client)?;
|
|
|
|
println!("Registered client #{}", id);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-05-04 17:51:18 +00:00
|
|
|
fn reset_password(args: Vec<String>) -> Res {
|
|
|
|
let user_id = UserID::new(args[0].parse::<u64>()?);
|
|
|
|
let token = account_helper::generate_password_reset_token(&user_id)?;
|
|
|
|
|
|
|
|
println!("{}", conf().password_reset_url.replace("{TOKEN}", &token));
|
|
|
|
|
2021-05-08 17:17:33 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_admin(args: Vec<String>) -> Res {
|
|
|
|
let new_admin = NewAdmin {
|
|
|
|
name: args[0].to_string(),
|
|
|
|
email: args[1].to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
if !mailchecker::is_valid(&new_admin.email) {
|
|
|
|
eprintln!("Specified email address is not valid!");
|
|
|
|
std::process::exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
let id = admin_account_helper::create(&new_admin)
|
|
|
|
.expect("Failed to create account!");
|
|
|
|
|
|
|
|
println!("* New admin ID: {}", id.id());
|
|
|
|
|
2021-05-08 17:22:47 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_admin_reset_token(args: Vec<String>) -> Res {
|
|
|
|
let admin = admin_account_helper::find_admin_by_email(&args[0])
|
|
|
|
.expect("Failed to load admin information!");
|
|
|
|
|
|
|
|
println!("Generate a new reset token for {} ({})", admin.name, admin.email);
|
|
|
|
|
|
|
|
let token = admin_account_helper::create_new_reset_token(admin.id)
|
|
|
|
.expect("Failed to create admin reset token!");
|
|
|
|
|
|
|
|
println!("Reset token: {}", token.token);
|
|
|
|
|
2021-05-14 16:25:53 +00:00
|
|
|
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.");
|
2021-05-04 17:18:47 +00:00
|
|
|
Ok(())
|
|
|
|
}
|