Automatically create admin on first start
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1,3 @@
 | 
			
		||||
/target
 | 
			
		||||
.idea
 | 
			
		||||
storage
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1587
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1587
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -6,3 +6,10 @@ edition = "2021"
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
rocket = "0.5.0-rc.1"
 | 
			
		||||
log = "0.4.16"
 | 
			
		||||
serde_json = "1.0.79"
 | 
			
		||||
env_logger = "0.9.0"
 | 
			
		||||
serde = { version = "1.0.136", features = ["derive"] }
 | 
			
		||||
bcrypt = "0.12.1"
 | 
			
		||||
uuid = { version = "0.8.2", features = ["v4"] }
 | 
			
		||||
							
								
								
									
										6
									
								
								src/constants.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/constants.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
/// File in storage containing users list
 | 
			
		||||
pub const USERS_LIST_FILE: &str = "users.json";
 | 
			
		||||
 | 
			
		||||
/// Default built-in credentials
 | 
			
		||||
pub const DEFAULT_ADMIN_USERNAME: &str = "admin";
 | 
			
		||||
pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";
 | 
			
		||||
							
								
								
									
										19
									
								
								src/data/app_config.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/data/app_config.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
use rocket::serde::Deserialize;
 | 
			
		||||
use crate::constants::USERS_LIST_FILE;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
#[serde(crate = "rocket::serde")]
 | 
			
		||||
pub struct AppConfig {
 | 
			
		||||
    storage_path: PathBuf,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AppConfig {
 | 
			
		||||
    pub fn storage_path(&self) -> &Path {
 | 
			
		||||
        self.storage_path.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn users_file(&self) -> PathBuf {
 | 
			
		||||
        self.storage_path.join(USERS_LIST_FILE)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								src/data/entity_manager.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/data/entity_manager.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
 | 
			
		||||
use crate::utils::err::Res;
 | 
			
		||||
 | 
			
		||||
pub struct EntityManager<E> {
 | 
			
		||||
    file_path: PathBuf,
 | 
			
		||||
    list: Vec<E>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<E> EntityManager<E> where E: rocket::serde::Serialize + rocket::serde::DeserializeOwned + Eq + Clone {
 | 
			
		||||
    /// Open entity
 | 
			
		||||
    pub fn open_or_create<A: AsRef<Path>>(path: A) -> Res<Self> {
 | 
			
		||||
        if !path.as_ref().is_file() {
 | 
			
		||||
            log::warn!("Entities at {:?} does not point to a file, creating a new empty entity container...", path.as_ref());
 | 
			
		||||
            return Ok(Self {
 | 
			
		||||
                file_path: path.as_ref().to_path_buf(),
 | 
			
		||||
                list: vec![],
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        log::info!("Open existing entity file {:?}", path.as_ref());
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            file_path: path.as_ref().to_path_buf(),
 | 
			
		||||
            list: serde_json::from_str(&std::fs::read_to_string(path.as_ref())?)?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the number of entries in the list
 | 
			
		||||
    pub fn len(&self) -> usize {
 | 
			
		||||
        self.list.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Save the list
 | 
			
		||||
    fn save(&self) -> Res {
 | 
			
		||||
        Ok(std::fs::write(&self.file_path, serde_json::to_string(&self.list)?)?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Insert a new element in the list
 | 
			
		||||
    pub fn insert(&mut self, el: E) -> Res {
 | 
			
		||||
        self.list.push(el);
 | 
			
		||||
        self.save()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								src/data/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/data/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
pub mod app_config;
 | 
			
		||||
pub mod user;
 | 
			
		||||
pub mod service;
 | 
			
		||||
pub mod entity_manager;
 | 
			
		||||
							
								
								
									
										2
									
								
								src/data/service.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/data/service.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
 | 
			
		||||
pub struct ServiceID(String);
 | 
			
		||||
							
								
								
									
										48
									
								
								src/data/user.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/data/user.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
use crate::data::service::ServiceID;
 | 
			
		||||
use crate::utils::err::Res;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub struct User {
 | 
			
		||||
    pub uid: String,
 | 
			
		||||
    pub first_name: String,
 | 
			
		||||
    pub last_last: String,
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    pub password: String,
 | 
			
		||||
    pub need_reset_password: bool,
 | 
			
		||||
    pub enabled: bool,
 | 
			
		||||
    pub admin: bool,
 | 
			
		||||
 | 
			
		||||
    /// None = all services
 | 
			
		||||
    /// Some([]) = no service
 | 
			
		||||
    pub authorized_services: Option<Vec<ServiceID>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for User {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.uid.eq(&other.uid)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Eq for User {}
 | 
			
		||||
 | 
			
		||||
impl Default for User {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            uid: uuid::Uuid::new_v4().to_string(),
 | 
			
		||||
            first_name: "".to_string(),
 | 
			
		||||
            last_last: "".to_string(),
 | 
			
		||||
            username: "".to_string(),
 | 
			
		||||
            email: "".to_string(),
 | 
			
		||||
            password: "".to_string(),
 | 
			
		||||
            need_reset_password: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
            admin: false,
 | 
			
		||||
            authorized_services: None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn hash_password<P: AsRef<[u8]>>(pwd: P) -> Res<String> {
 | 
			
		||||
    Ok(bcrypt::hash(pwd, bcrypt::DEFAULT_COST)?)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
pub mod data;
 | 
			
		||||
pub mod utils;
 | 
			
		||||
pub mod constants;
 | 
			
		||||
							
								
								
									
										52
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,3 +1,51 @@
 | 
			
		||||
fn main() {
 | 
			
		||||
    println!("Hello, world!");
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate rocket;
 | 
			
		||||
 | 
			
		||||
use rocket::fairing::AdHoc;
 | 
			
		||||
 | 
			
		||||
use basic_oidc::constants::{DEFAULT_ADMIN_PASSWORD, DEFAULT_ADMIN_USERNAME};
 | 
			
		||||
use basic_oidc::data::app_config::AppConfig;
 | 
			
		||||
use basic_oidc::data::entity_manager::EntityManager;
 | 
			
		||||
use basic_oidc::data::user::{hash_password, User};
 | 
			
		||||
 | 
			
		||||
#[get("/health")]
 | 
			
		||||
fn index() -> &'static str {
 | 
			
		||||
    "Running"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[launch]
 | 
			
		||||
fn rocket() -> _ {
 | 
			
		||||
    //env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
 | 
			
		||||
 | 
			
		||||
    let rocket = rocket::build()
 | 
			
		||||
        .mount("/", routes![index])
 | 
			
		||||
        .attach(AdHoc::config::<AppConfig>());
 | 
			
		||||
    let figment = rocket.figment();
 | 
			
		||||
 | 
			
		||||
    // Initialize application
 | 
			
		||||
    let config: AppConfig = figment.extract().expect("config");
 | 
			
		||||
 | 
			
		||||
    if !config.storage_path().exists() {
 | 
			
		||||
        log::error!("Specified storage path {:?} does not exists!", config.storage_path());
 | 
			
		||||
        panic!()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut users = EntityManager::<User>::open_or_create(config.users_file())
 | 
			
		||||
        .expect("Failed to load users list!");
 | 
			
		||||
 | 
			
		||||
    // Create initial user if required
 | 
			
		||||
    if users.len() == 0 {
 | 
			
		||||
        log::info!("Create default {} user", DEFAULT_ADMIN_USERNAME);
 | 
			
		||||
        let mut default_admin = User::default();
 | 
			
		||||
        default_admin.username = DEFAULT_ADMIN_USERNAME.to_string();
 | 
			
		||||
        default_admin.password = hash_password(DEFAULT_ADMIN_PASSWORD).unwrap();
 | 
			
		||||
        default_admin.need_reset_password = true;
 | 
			
		||||
        default_admin.authorized_services = None;
 | 
			
		||||
        default_admin.admin = true;
 | 
			
		||||
 | 
			
		||||
        users.insert(default_admin)
 | 
			
		||||
            .expect("Failed to create initial user!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rocket
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								src/utils.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/utils.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
pub mod err;
 | 
			
		||||
							
								
								
									
										2
									
								
								src/utils/err.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/utils/err.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
pub type Res<A = ()> = Result<A, Box<dyn Error>>;
 | 
			
		||||
		Reference in New Issue
	
	Block a user