Automatically create admin on first start
This commit is contained in:
parent
2d062320a7
commit
b4e8113706
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
.idea
|
.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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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() {
|
#[macro_use]
|
||||||
println!("Hello, world!");
|
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>>;
|
Loading…
Reference in New Issue
Block a user