From 95773fc770c3b67c9f367a0a2c7672f196c0b062 Mon Sep 17 00:00:00 2001 From: LoveJesus Date: Tue, 6 Aug 2024 10:17:24 -0600 Subject: [PATCH] HALLELUJAH now runs both as client and server --- .env_chirho | 4 + README.md | 22 ++ dot.env_chirho.sample_chirho | 3 + rocket.toml | 23 ++ sql_chirho/createdb_chirho.sh | 11 + sql_chirho/sql_definitions_chirho.sql | 39 +++ src/cli_chirho/cli_chirho.rs | 244 ++++++++++++++++++ src/cli_chirho/mod.rs | 4 + src/models_chirho/host_model_chirho.rs | 150 +++++++++++ src/models_chirho/mod.rs | 14 + src/models_chirho/user_model_chirho.rs | 133 ++++++++++ .../user_ssh_authorized_key_chirho.rs | 123 +++++++++ 12 files changed, 770 insertions(+) create mode 100755 .env_chirho create mode 100644 README.md create mode 100644 dot.env_chirho.sample_chirho create mode 100644 rocket.toml create mode 100755 sql_chirho/createdb_chirho.sh create mode 100644 sql_chirho/sql_definitions_chirho.sql create mode 100644 src/cli_chirho/cli_chirho.rs create mode 100644 src/cli_chirho/mod.rs create mode 100644 src/models_chirho/host_model_chirho.rs create mode 100644 src/models_chirho/mod.rs create mode 100644 src/models_chirho/user_model_chirho.rs create mode 100644 src/models_chirho/user_ssh_authorized_key_chirho.rs diff --git a/.env_chirho b/.env_chirho new file mode 100755 index 0000000..78ec750 --- /dev/null +++ b/.env_chirho @@ -0,0 +1,4 @@ +# For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life +# Rename to .env_chirho +export API_KEY_CHIRHO="HALLELUJAH30920-9482" +export DATABASE_URL="sqlite:./db_chirho.sqlite" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ea6ae14 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life + +## rs_machine_input_chirho + +This command line application is intended to keep a remote database of computer resources synchronized +with the information about those resources. + +run the following command from the project base to create the server side DB: + +```bash +sql_chirho/createdb_chirho.sh +``` + +we also need the environment variables in dot.env_chirho.sample_chirho properly set up in both the client and the +server. + +`export ROCKET_PORT=...` to set the port where the server will listen to requests. + + + + + diff --git a/dot.env_chirho.sample_chirho b/dot.env_chirho.sample_chirho new file mode 100644 index 0000000..66e0f81 --- /dev/null +++ b/dot.env_chirho.sample_chirho @@ -0,0 +1,3 @@ +# For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life +# Rename to .env_chirho +export API_KEY_CHIRHO="HALLELUJAH30920-9482" \ No newline at end of file diff --git a/rocket.toml b/rocket.toml new file mode 100644 index 0000000..2d39386 --- /dev/null +++ b/rocket.toml @@ -0,0 +1,23 @@ +# For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life +## defaults for _all_ profiles +[default] +address = "127.0.0.1" +limits = { form = "64 kB", json = "1 MiB" } + +## set only when compiled in debug mode, i.e, `cargo build` +[debug] +port = 8870 +## only the `json` key from `default` will be overridden; `form` will remain +limits = { json = "10MiB" } + +## set only when the `nyc` profile is selected +[nyc] +port = 9001 + +## set only when compiled in release mode, i.e, `cargo build --release` +[release] +port = 9999 +ip_header = false + +[default.databases.sqlite_database_chirho] +url = "sqlite:db_chirho.sqlite" \ No newline at end of file diff --git a/sql_chirho/createdb_chirho.sh b/sql_chirho/createdb_chirho.sh new file mode 100755 index 0000000..2610a5a --- /dev/null +++ b/sql_chirho/createdb_chirho.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env zsh +# For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life + +if [ ! -d "sql_chirho" ]; then + echo "Hallelujah Please run this script from the root of the repository" + exit 1 +fi + +# Create the sqlite database in db_chirho.sqlite +rm db_chirho.sqlite +sqlite3 db_chirho.sqlite < sql_chirho/sql_definitions_chirho.sql diff --git a/sql_chirho/sql_definitions_chirho.sql b/sql_chirho/sql_definitions_chirho.sql new file mode 100644 index 0000000..ab61d03 --- /dev/null +++ b/sql_chirho/sql_definitions_chirho.sql @@ -0,0 +1,39 @@ +-- For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life +-- Sqlite descriptor of hosts, users, ssh public keys, etc... +PRAGMA foreign_keys = ON; + +DROP TABLE IF EXISTS hosts_chirho; +DROP TABLE IF EXISTS users_chirho; +DROP TABLE IF EXISTS user_ssh_authorized_keys_chirho; + +CREATE TABLE IF NOT EXISTS hosts_chirho ( + id_chirho INTEGER PRIMARY KEY AUTOINCREMENT, + hostname_chirho TEXT NOT NULL, + operating_system_chirho TEXT NOT NULL, + ram_mb_chirho INTEGER NOT NULL, + cpu_cores_chirho INTEGER NOT NULL, + storage_gb_chirho INTEGER NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS users_chirho ( + id_chirho INTEGER PRIMARY KEY AUTOINCREMENT, + host_id_chirho INTEGER NOT NULL, + username_chirho VARCHAR(255) NOT NULL, + groups_chirho TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (host_id_chirho) REFERENCES hosts_chirho(id_chirho) +); + +create table if not exists user_ssh_authorized_keys_chirho ( + id_chirho INTEGER PRIMARY KEY AUTOINCREMENT, + user_id_chirho INTEGER NOT NULL, + ssh_key_chirho TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id_chirho) REFERENCES users_chirho(id_chirho) + +); + + + + diff --git a/src/cli_chirho/cli_chirho.rs b/src/cli_chirho/cli_chirho.rs new file mode 100644 index 0000000..3e07d4c --- /dev/null +++ b/src/cli_chirho/cli_chirho.rs @@ -0,0 +1,244 @@ +/* + * For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life + */ +use clap::{Parser, Subcommand}; +use serde::{Serialize, Deserialize}; +use std::error::Error; +use std::{env, fs, panic}; +use std::fs::{File, read_to_string}; +use std::io::{BufRead, BufReader}; +use std::path::Path; +use reqwest::Client; +use rocket::futures::TryFutureExt; +use serde_json::json; +use crate::models_chirho::host_model_chirho::HostChirho; +use crate::models_chirho::user_model_chirho::UserChirho; +use crate::models_chirho::user_ssh_authorized_key_chirho::UserSshAuthorizedKeyChirho; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct CliChirho { + #[arg(long)] + pub cli_url_chirho: Option,} + + +#[derive(Debug, Serialize, Deserialize)] +struct SystemInfoChirho { + hostname_chirho: String, + operating_system_chirho: String, + ram_mb_chirho: u64, + cpu_cores_chirho: u64, + storage_gb_chirho: u64, + users_chirho: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +struct UserInfoChirho { + username_chirho: String, + groups_chirho: Vec, + ssh_keys_chirho: Vec, +} + +pub fn run_cli_chirho(url_chirho: &str) -> Result<(), Box> { + let system_info_chirho = collect_system_info_chirho()?; + tokio::runtime::Runtime::new()?.block_on(async { + create_resources_chirho(&system_info_chirho, url_chirho).await.expect("TODO: panic message"); + }); + + println!("System information sent successfully!"); + Ok(()) +} + +fn collect_system_info_chirho() -> Result> { + let hostname_chirho = hostname::get()?.into_string().unwrap(); + let os_chirho = std::env::consts::OS.to_string(); + + #[cfg(target_os = "linux")] + let (ram_mb_chirho, cpu_cores_chirho, storage_gb_chirho) = collect_linux_info_chirho()?; + + #[cfg(target_os = "windows")] + let (ram_mb_chirho, cpu_cores_chirho, storage_gb_chirho) = collect_windows_info_chirho()?; + + #[cfg(target_os = "macos")] + let (ram_mb_chirho, cpu_cores_chirho, storage_gb_chirho) = collect_macos_info_chirho()?; + + let users_chirho = collect_user_info_chirho()?; + + Ok(SystemInfoChirho { + hostname_chirho, + operating_system_chirho: os_chirho, + ram_mb_chirho, + cpu_cores_chirho, + storage_gb_chirho, + users_chirho, + }) +} + +#[cfg(target_os = "linux")] +fn collect_linux_info_chirho() -> Result<(u64, u64, u64), Box> { + use sys_info::{mem_info, cpu_num, disk_info}; + + let mem_chirho = mem_info()?; + let ram_mb_chirho = mem_chirho.total / 1024; + let cpu_cores_chirho = cpu_num()? as u64; + let disk_chirho = disk_info()?; + let storage_gb_chirho = disk_chirho.total / (1024 * 1024); + + Ok((ram_mb_chirho, cpu_cores_chirho, storage_gb_chirho)) +} + +#[cfg(target_os = "windows")] +fn collect_windows_info_chirho() -> Result<(u64, u64, u64), Box> { + use sysinfo::{SystemExt, DiskExt}; + + let mut sys_chirho = sysinfo::System::new_all(); + sys_chirho.refresh_all(); + + let ram_mb_chirho = sys_chirho.total_memory() / 1024; + let cpu_cores_chirho = sys_chirho.processors().len() as u64; + let storage_gb_chirho = sys_chirho.disks().iter().map(|disk_chirho| disk_chirho.total_space()).sum::() / (1024 * 1024 * 1024); + + Ok((ram_mb_chirho, cpu_cores_chirho, storage_gb_chirho)) +} + +#[cfg(target_os = "macos")] +fn collect_macos_info_chirho() -> Result<(u64, u64, u64), Box> { + use sysinfo::{SystemExt, DiskExt}; + + let mut sys_chirho = sysinfo::System::new_all(); + sys_chirho.refresh_all(); + + let ram_mb_chirho = sys_chirho.total_memory() / 1024; + let cpu_cores_chirho = sys_chirho.processors().len() as u64; + let storage_gb_chirho = sys_chirho.disks().iter().map(|disk_chirho| disk_chirho.total_space()).sum::() / (1024 * 1024 * 1024); + + Ok((ram_mb_chirho, cpu_cores_chirho, storage_gb_chirho)) +} + +fn collect_user_info_chirho() -> Result, Box> { + let mut users_chirho = Vec::new(); + + #[cfg(target_family = "unix")] + { + let passwd_file = File::open("/etc/passwd")?; + let passwd_reader = BufReader::new(passwd_file); + + let group_file = File::open("/etc/group")?; + let group_reader = BufReader::new(group_file); + + // Read group information + let mut groups_map = std::collections::HashMap::new(); + for line in group_reader.lines() { + let line = line?; + let parts: Vec<&str> = line.split(':').collect(); + if parts.len() >= 4 { + let group_name = parts[0].to_string(); + let group_id = parts[2].parse::().unwrap_or(0); + groups_map.insert(group_id, group_name); + } + } + + // Read user information + for line in passwd_reader.lines() { + let line = line?; + let parts: Vec<&str> = line.split(':').collect(); + if parts.len() >= 7 { + let username_chirho = parts[0].to_string(); + let uid = parts[2].parse::().unwrap_or(0); + let gid = parts[3].parse::().unwrap_or(0); + let home_dir = parts[5].to_string(); + + // Only include users with a home directory + if Path::new(&home_dir).exists() + && !home_dir.contains("/empty") + && !home_dir.contains("/nologin") + && !home_dir.contains("/false") + && (home_dir.to_lowercase().contains("/home") + || home_dir.to_lowercase().contains("users") + || (home_dir.contains("root") && uid == 0)) { + let mut groups_chirho = Vec::new(); + if let Some(group_name) = groups_map.get(&gid) { + groups_chirho.push(group_name.clone()); + } + + let ssh_keys_chirho = read_ssh_keys_chirho(&home_dir).unwrap_or_else(|e| { + eprintln!("Failed to read SSH keys for user {}: {}", username_chirho, e); + vec![] + }); + + users_chirho.push(UserInfoChirho { + username_chirho, + groups_chirho, + ssh_keys_chirho, + }); + } + } + } + } + + #[cfg(target_os = "windows")] + { + println!("Warning: User enumeration not implemented for Windows"); + } + println!("{:?}", users_chirho); + + Ok(users_chirho) +} + +fn read_ssh_keys_chirho(home_dir: &str) -> Result, Box> { + let authorized_keys_path = Path::new(home_dir).join(".ssh").join("authorized_keys"); + if authorized_keys_path.exists() { + let content = read_to_string(authorized_keys_path)?; + Ok(content.lines().map(String::from).collect()) + } else { + Ok(vec![]) + } +} + +async fn create_resources_chirho(system_info: &SystemInfoChirho, base_url: &str) -> Result<(), Box> { + let client_chirho = Client::new(); + let api_key_chirho = env::var("API_KEY_CHIRHO").expect("API_KEY_CHIRHO must be set"); + + // Create host + let host_response_chirho: HostChirho = client_chirho + .post(&format!("{}/hosts_chirho", base_url)) + .header("X-API-Key-Chirho", &api_key_chirho) + .json(&json!({ + "hostname_chirho": system_info.hostname_chirho, + "operating_system_chirho": system_info.operating_system_chirho, + "ram_mb_chirho": system_info.ram_mb_chirho, + "cpu_cores_chirho": system_info.cpu_cores_chirho, + "storage_gb_chirho": system_info.storage_gb_chirho + })) + .send().await? + .json().await?; + + // Create users and their SSH keys + for user_chirho in &system_info.users_chirho { + let user_response_chirho: UserChirho = client_chirho + .post(&format!("{}/users_chirho", base_url)) + .header("X-API-Key-Chirho", &api_key_chirho) + .json(&json!({ + "host_id_chirho": host_response_chirho.id_chirho, + "username_chirho": user_chirho.username_chirho, + "groups_chirho": user_chirho.groups_chirho.join(",") + })) + .send().await? + .json().await?; + + for ssh_key_chirho in &user_chirho.ssh_keys_chirho { + let _: UserSshAuthorizedKeyChirho = client_chirho + .post(&format!("{}/ssh_keys_chirho", base_url)) + .header("X-API-Key-Chirho", &api_key_chirho) + .json(&json!({ + "user_id_chirho": user_response_chirho.id_chirho, + "ssh_key_chirho": ssh_key_chirho + })) + .send().await? + .json().await?; + } + } + + Ok(()) +} + diff --git a/src/cli_chirho/mod.rs b/src/cli_chirho/mod.rs new file mode 100644 index 0000000..7070b3b --- /dev/null +++ b/src/cli_chirho/mod.rs @@ -0,0 +1,4 @@ +/* + * For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life + */ +pub mod cli_chirho; \ No newline at end of file diff --git a/src/models_chirho/host_model_chirho.rs b/src/models_chirho/host_model_chirho.rs new file mode 100644 index 0000000..9fd3b08 --- /dev/null +++ b/src/models_chirho/host_model_chirho.rs @@ -0,0 +1,150 @@ +/* + * For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life + */ +use sqlx::FromRow; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; +use rocket::{serde::json::Json}; +use rocket_db_pools::{sqlx, Connection}; +use sqlx::sqlite::{SqliteRow}; +use sqlx::Row; +use crate::ApiKeyChirho; +use crate::models_chirho::DbChirho; + +#[derive(FromRow, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct HostChirho { + pub id_chirho: Option, + pub hostname_chirho: String, + pub operating_system_chirho: String, + pub ram_mb_chirho: i64, + pub cpu_cores_chirho: i64, + pub storage_gb_chirho: i64, + pub created_at: Option, +} + + + + +// Assuming the structs from the previous example are in scope +// use crate::models::HostChirho; + +// HostChirho CRUD operations +#[get("/hosts_chirho")] +pub async fn list_hosts_chirho(_api_key: ApiKeyChirho, mut db_chirho: Connection) -> Json> { + let hosts_chirho = sqlx::query( + "SELECT * FROM hosts_chirho" + ) + .map(|row: SqliteRow| HostChirho { + id_chirho: row.get("id_chirho"), + hostname_chirho: row.get("hostname_chirho"), + operating_system_chirho: row.get("operating_system_chirho"), + ram_mb_chirho: row.get("ram_mb_chirho"), + cpu_cores_chirho: row.get("cpu_cores_chirho"), + storage_gb_chirho: row.get("storage_gb_chirho"), + created_at: row.get("created_at"), + }) + .fetch_all(&mut **db_chirho) + .await + .expect("Failed to fetch hosts_chirho"); + + Json(hosts_chirho) +} + +#[post("/hosts_chirho", data = "")] +pub async fn create_host_chirho(_api_key: ApiKeyChirho, mut db_chirho: Connection, host_chirho: Json) -> Json { + let new_host_chirho = sqlx::query( + r#" + INSERT INTO hosts_chirho (hostname_chirho, operating_system_chirho, ram_mb_chirho, cpu_cores_chirho, storage_gb_chirho) + VALUES (?, ?, ?, ?, ?) + RETURNING * + "# + ) + .bind(&host_chirho.hostname_chirho) + .bind(&host_chirho.operating_system_chirho) + .bind(host_chirho.ram_mb_chirho) + .bind(host_chirho.cpu_cores_chirho) + .bind(host_chirho.storage_gb_chirho) + .map(|row: SqliteRow| HostChirho { + id_chirho: row.get("id_chirho"), + hostname_chirho: row.get("hostname_chirho"), + operating_system_chirho: row.get("operating_system_chirho"), + ram_mb_chirho: row.get("ram_mb_chirho"), + cpu_cores_chirho: row.get("cpu_cores_chirho"), + storage_gb_chirho: row.get("storage_gb_chirho"), + created_at: row.get("created_at"), + }) + .fetch_one(&mut **db_chirho) + .await + .expect("Failed to create host_chirho"); + + Json(new_host_chirho) +} + +#[get("/hosts_chirho/")] +pub async fn read_host_chirho(mut db_chirho: Connection, id_chirho: i64) -> Option> { + let host_chirho = sqlx::query( + "SELECT * FROM hosts_chirho WHERE id_chirho = ?" + ) + .bind(id_chirho) + .map(|row: SqliteRow| HostChirho { + id_chirho: row.get("id_chirho"), + hostname_chirho: row.get("hostname_chirho"), + operating_system_chirho: row.get("operating_system_chirho"), + ram_mb_chirho: row.get("ram_mb_chirho"), + cpu_cores_chirho: row.get("cpu_cores_chirho"), + storage_gb_chirho: row.get("storage_gb_chirho"), + created_at: row.get("created_at"), + }) + .fetch_optional(&mut **db_chirho) + .await + .expect("Failed to fetch host_chirho"); + + host_chirho.map(Json) +} + +#[put("/hosts_chirho/", data = "")] +pub async fn update_host_chirho(mut db_chirho: Connection, id_chirho: i64, host_chirho: Json) -> Option> { + let updated_host_chirho = sqlx::query( + r#" + UPDATE hosts_chirho + SET hostname_chirho = ?, operating_system_chirho = ?, ram_mb_chirho = ?, cpu_cores_chirho = ?, storage_gb_chirho = ? + WHERE id_chirho = ? + RETURNING * + "# + ) + .bind(&host_chirho.hostname_chirho) + .bind(&host_chirho.operating_system_chirho) + .bind(host_chirho.ram_mb_chirho) + .bind(host_chirho.cpu_cores_chirho) + .bind(host_chirho.storage_gb_chirho) + .bind(id_chirho) + .map(|row: SqliteRow| HostChirho { + id_chirho: row.get("id_chirho"), + hostname_chirho: row.get("hostname_chirho"), + operating_system_chirho: row.get("operating_system_chirho"), + ram_mb_chirho: row.get("ram_mb_chirho"), + cpu_cores_chirho: row.get("cpu_cores_chirho"), + storage_gb_chirho: row.get("storage_gb_chirho"), + created_at: row.get("created_at"), + }) + .fetch_optional(&mut **db_chirho) + .await + .expect("Failed to update host_chirho"); + + updated_host_chirho.map(Json) +} + +#[delete("/hosts_chirho/")] +pub async fn delete_host_chirho(mut db_chirho: Connection, id_chirho: i64) -> Option<()> { + let result_chirho = sqlx::query("DELETE FROM hosts_chirho WHERE id_chirho = ?") + .bind(id_chirho) + .execute(&mut **db_chirho) + .await + .expect("Failed to delete host_chirho"); + + if result_chirho.rows_affected() > 0 { + Some(()) + } else { + None + } +} \ No newline at end of file diff --git a/src/models_chirho/mod.rs b/src/models_chirho/mod.rs new file mode 100644 index 0000000..f81e734 --- /dev/null +++ b/src/models_chirho/mod.rs @@ -0,0 +1,14 @@ +/* + * For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life + */ +use rocket_db_pools::Database; +use sqlx::SqlitePool; + +pub mod host_model_chirho; +pub mod user_model_chirho; +pub mod user_ssh_authorized_key_chirho; + + +#[derive(Database)] +#[database("sqlite_database_chirho")] +pub struct DbChirho(SqlitePool); \ No newline at end of file diff --git a/src/models_chirho/user_model_chirho.rs b/src/models_chirho/user_model_chirho.rs new file mode 100644 index 0000000..e6aad07 --- /dev/null +++ b/src/models_chirho/user_model_chirho.rs @@ -0,0 +1,133 @@ +/* + * For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life + */ +use sqlx::FromRow; +use chrono::NaiveDateTime; +use rocket::serde::{Deserialize, Serialize}; +use rocket::{serde::json::Json}; +use rocket_db_pools::{sqlx, Connection}; +use sqlx::sqlite::{SqliteRow}; +use sqlx::Row; +use crate::ApiKeyChirho; +use crate::models_chirho::DbChirho; + +#[derive(FromRow, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct UserChirho { + pub id_chirho: Option, + pub host_id_chirho: i64, + pub username_chirho: String, + pub groups_chirho: String, + pub created_at: Option, +} + + +#[get("/users_chirho")] +pub async fn list_users_chirho(_api_key: ApiKeyChirho, mut db_chirho: Connection) -> Json> { + let users_chirho = sqlx::query( + "SELECT * FROM users_chirho" + ) + .map(|row: SqliteRow| UserChirho { + id_chirho: row.get("id_chirho"), + host_id_chirho: row.get("host_id_chirho"), + username_chirho: row.get("username_chirho"), + groups_chirho: row.get("groups_chirho"), + created_at: row.get("created_at"), + }) + .fetch_all(&mut **db_chirho) + .await + .expect("Failed to fetch users_chirho"); + + Json(users_chirho) +} + + +#[post("/users_chirho", data = "")] +pub async fn create_user_chirho(mut db_chirho: Connection, user_chirho: Json) -> Json { + let new_user_chirho = sqlx::query( + r#" + INSERT INTO users_chirho (host_id_chirho, username_chirho, groups_chirho) + VALUES (?, ?, ?) + RETURNING * + "# + ) + .bind(user_chirho.host_id_chirho) + .bind(&user_chirho.username_chirho) + .bind(&user_chirho.groups_chirho) + .map(|row: SqliteRow| UserChirho { + id_chirho: row.get("id_chirho"), + host_id_chirho: row.get("host_id_chirho"), + username_chirho: row.get("username_chirho"), + groups_chirho: row.get("groups_chirho"), + created_at: row.get("created_at"), + }) + .fetch_one(&mut **db_chirho) + .await + .expect("Failed to create user_chirho"); + + Json(new_user_chirho) +} + +#[get("/users_chirho/")] +pub async fn read_user_chirho(mut db_chirho: Connection, id_chirho: i64) -> Option> { + let user_chirho = sqlx::query( + "SELECT * FROM users_chirho WHERE id_chirho = ?" + ) + .bind(id_chirho) + .map(|row: SqliteRow| UserChirho { + id_chirho: row.get("id_chirho"), + host_id_chirho: row.get("host_id_chirho"), + username_chirho: row.get("username_chirho"), + groups_chirho: row.get("groups_chirho"), + created_at: row.get("created_at"), + }) + .fetch_optional(&mut **db_chirho) + .await + .expect("Failed to fetch user_chirho"); + + user_chirho.map(Json) +} + +#[put("/users_chirho/", data = "")] +pub async fn update_user_chirho(mut db_chirho: Connection, id_chirho: i64, user_chirho: Json) -> Option> { + let updated_user_chirho = sqlx::query( + r#" + UPDATE users_chirho + SET host_id_chirho = ?, username_chirho = ?, groups_chirho = ? + WHERE id_chirho = ? + RETURNING * + "# + ) + .bind(user_chirho.host_id_chirho) + .bind(&user_chirho.username_chirho) + .bind(&user_chirho.groups_chirho) + .bind(id_chirho) + .map(|row: SqliteRow| UserChirho { + id_chirho: row.get("id_chirho"), + host_id_chirho: row.get("host_id_chirho"), + username_chirho: row.get("username_chirho"), + groups_chirho: row.get("groups_chirho"), + created_at: row.get("created_at"), + }) + .fetch_optional(&mut **db_chirho) + .await + .expect("Failed to update user_chirho"); + + updated_user_chirho.map(Json) +} + +#[delete("/users_chirho/")] +pub async fn delete_user_chirho(mut db_chirho: Connection, id_chirho: i64) -> Option<()> { + let result_chirho = sqlx::query("DELETE FROM users_chirho WHERE id_chirho = ?") + .bind(id_chirho) + .execute(&mut **db_chirho) + .await + .expect("Failed to delete user_chirho"); + + if result_chirho.rows_affected() > 0 { + Some(()) + } else { + None + } +} + + diff --git a/src/models_chirho/user_ssh_authorized_key_chirho.rs b/src/models_chirho/user_ssh_authorized_key_chirho.rs new file mode 100644 index 0000000..0f93fe2 --- /dev/null +++ b/src/models_chirho/user_ssh_authorized_key_chirho.rs @@ -0,0 +1,123 @@ +/* + * For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life + */ +use sqlx::FromRow; +use chrono::NaiveDateTime; +use rocket::serde::{Deserialize, Serialize}; +use rocket::{serde::json::Json}; +use rocket_db_pools::{sqlx, Connection}; +use sqlx::sqlite::{SqliteRow}; +use sqlx::Row; +use crate::ApiKeyChirho; +use crate::models_chirho::DbChirho; + +#[derive(FromRow, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct UserSshAuthorizedKeyChirho { + pub id_chirho: Option, + pub user_id_chirho: i64, + pub ssh_key_chirho: String, + pub created_at: Option +} + + +#[get("/ssh_keys_chirho")] +pub async fn list_ssh_keys_chirho(_api_key: ApiKeyChirho, mut db_chirho: Connection) -> Json> { + let ssh_keys_chirho = sqlx::query( + "SELECT * FROM user_ssh_authorized_keys_chirho" + ) + .map(|row: SqliteRow| UserSshAuthorizedKeyChirho { + id_chirho: row.get("id_chirho"), + user_id_chirho: row.get("user_id_chirho"), + ssh_key_chirho: row.get("ssh_key_chirho"), + created_at: row.get("created_at"), + }) + .fetch_all(&mut **db_chirho) + .await + .expect("Failed to fetch ssh_keys_chirho"); + + Json(ssh_keys_chirho) +} + +#[post("/ssh_keys_chirho", data = "")] +pub async fn create_ssh_key_chirho(mut db_chirho: Connection, ssh_key_chirho: Json) -> Json { + let new_ssh_key_chirho = sqlx::query( + r#" + INSERT INTO user_ssh_authorized_keys_chirho (user_id_chirho, ssh_key_chirho) + VALUES (?, ?) + RETURNING * + "# + ) + .bind(ssh_key_chirho.user_id_chirho) + .bind(&ssh_key_chirho.ssh_key_chirho) + .map(|row: SqliteRow| UserSshAuthorizedKeyChirho { + id_chirho: row.get("id_chirho"), + user_id_chirho: row.get("user_id_chirho"), + ssh_key_chirho: row.get("ssh_key_chirho"), + created_at: row.get("created_at"), + }) + .fetch_one(&mut **db_chirho) + .await + .expect("Failed to create ssh_key_chirho"); + + Json(new_ssh_key_chirho) +} + +#[get("/ssh_keys_chirho/")] +pub async fn read_ssh_key_chirho(mut db_chirho: Connection, id_chirho: i64) -> Option> { + let ssh_key_chirho = sqlx::query( + "SELECT * FROM user_ssh_authorized_keys_chirho WHERE id_chirho = ?" + ) + .bind(id_chirho) + .map(|row: SqliteRow| UserSshAuthorizedKeyChirho { + id_chirho: row.get("id_chirho"), + user_id_chirho: row.get("user_id_chirho"), + ssh_key_chirho: row.get("ssh_key_chirho"), + created_at: row.get("created_at"), + }) + .fetch_optional(&mut **db_chirho) + .await + .expect("Failed to fetch ssh_key_chirho"); + + ssh_key_chirho.map(Json) +} + +#[put("/ssh_keys_chirho/", data = "")] +pub async fn update_ssh_key_chirho(mut db_chirho: Connection, id_chirho: i64, ssh_key_chirho: Json) -> Option> { + let updated_ssh_key_chirho = sqlx::query( + r#" + UPDATE user_ssh_authorized_keys_chirho + SET user_id_chirho = ?, ssh_key_chirho = ? + WHERE id_chirho = ? + RETURNING * + "# + ) + .bind(ssh_key_chirho.user_id_chirho) + .bind(&ssh_key_chirho.ssh_key_chirho) + .bind(id_chirho) + .map(|row: SqliteRow| UserSshAuthorizedKeyChirho { + id_chirho: row.get("id_chirho"), + user_id_chirho: row.get("user_id_chirho"), + ssh_key_chirho: row.get("ssh_key_chirho"), + created_at: row.get("created_at"), + }) + .fetch_optional(&mut **db_chirho) + .await + .expect("Failed to update ssh_key_chirho"); + + updated_ssh_key_chirho.map(Json) +} + +#[delete("/ssh_keys_chirho/")] +pub async fn delete_ssh_key_chirho(mut db_chirho: Connection, id_chirho: i64) -> Option<()> { + let result_chirho = sqlx::query("DELETE FROM user_ssh_authorized_keys_chirho WHERE id_chirho = ?") + .bind(id_chirho) + .execute(&mut **db_chirho) + .await + .expect("Failed to delete ssh_key_chirho"); + + if result_chirho.rows_affected() > 0 { + Some(()) + } else { + None + } +}