fix: Properly load/save DB and stop creating dummy users
This commit is contained in:
parent
c35bfaeaf8
commit
1e0b9ac166
4 changed files with 37 additions and 43 deletions
|
|
@ -1,5 +1,4 @@
|
|||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Error as JsonError;
|
||||
|
||||
use std::fmt;
|
||||
|
|
@ -35,7 +34,7 @@ impl fmt::Display for FilesystemDatabaseError {
|
|||
|
||||
impl std::error::Error for FilesystemDatabaseError {}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct FilesystemDatabase {
|
||||
// TODO: Once we have common validation steps in place across DB backends, we can reduce cloning.
|
||||
// For now, we clone the DB on every write operation, and update it when saving to disk
|
||||
|
|
@ -59,10 +58,14 @@ impl FilesystemDatabase {
|
|||
path: path.to_path_buf(),
|
||||
};
|
||||
|
||||
db.save().await?;
|
||||
tracing::info!("Initializing empty database in file {path}. Checking permissions...");
|
||||
db.save_self().await?;
|
||||
tracing::info!("Database successfully created");
|
||||
|
||||
return Ok(Database::new(db));
|
||||
}
|
||||
|
||||
tracing::info!("Loading database from file {path}");
|
||||
let s = tokio::fs::read(path)
|
||||
.await
|
||||
.map_err(|e| Box::new(FilesystemDatabaseError::ReadFileIO(path.to_path_buf(), e)))?;
|
||||
|
|
@ -74,13 +77,29 @@ impl FilesystemDatabase {
|
|||
}))
|
||||
}
|
||||
|
||||
pub async fn save(&mut self) -> Result<(), BoxedError> {
|
||||
let s = serde_json::to_string(&self)
|
||||
/// Attempts to save a new state of the DB, effectively switching to the new state
|
||||
/// only if the save is successful.
|
||||
pub async fn save(&mut self, new_db: MemoryDatabase) -> Result<(), BoxedError> {
|
||||
self.save_inner(&new_db).await?;
|
||||
self.inner = new_db;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Saves an in-memory DB to disk. Cannot be used directly to observe exclusive access.
|
||||
async fn save_inner(&self, inner: &MemoryDatabase) -> Result<(), BoxedError> {
|
||||
let s = serde_json::to_string(inner)
|
||||
.map_err(|e| Box::new(FilesystemDatabaseError::WriteFileJson(self.path.clone(), e)))?;
|
||||
Ok(tokio::fs::write(&self.path, &s)
|
||||
.await
|
||||
.map_err(|e| Box::new(FilesystemDatabaseError::WriteFileIO(self.path.clone(), e)))?)
|
||||
}
|
||||
|
||||
/// Save the database without switching to a new state.
|
||||
///
|
||||
/// Used when initializing the DB, to check for permissions.
|
||||
pub async fn save_self(&mut self) -> Result<(), BoxedError> {
|
||||
self.save_inner(&self.inner).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
|
@ -95,9 +114,7 @@ impl DatabaseInterface for FilesystemDatabase {
|
|||
return Ok(Err(e));
|
||||
}
|
||||
|
||||
self.save().await?;
|
||||
self.inner = new_db;
|
||||
|
||||
self.save(new_db).await?;
|
||||
Ok(Ok(()))
|
||||
}
|
||||
|
||||
|
|
@ -119,9 +136,7 @@ impl DatabaseInterface for FilesystemDatabase {
|
|||
return Ok(Err(e));
|
||||
}
|
||||
|
||||
self.save().await?;
|
||||
self.inner = new_db;
|
||||
|
||||
self.save(new_db).await?;
|
||||
Ok(Ok(()))
|
||||
}
|
||||
|
||||
|
|
@ -136,9 +151,7 @@ impl DatabaseInterface for FilesystemDatabase {
|
|||
return Ok(Err(e));
|
||||
}
|
||||
|
||||
self.save().await?;
|
||||
self.inner = new_db;
|
||||
|
||||
self.save(new_db).await?;
|
||||
Ok(Ok(()))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ pub trait DatabaseInterface: std::fmt::Debug + Send + Sync + 'static {
|
|||
) -> Result<Result<(), UserCreationError>, BoxedError>;
|
||||
|
||||
async fn list_all_domains(&self) -> Result<Vec<Domain>, BoxedError>;
|
||||
#[expect(unused)]
|
||||
async fn list_all_users(&self) -> Result<Vec<User>, BoxedError>;
|
||||
|
||||
/// List users on a specific domain.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ pub struct MemoryDatabase {
|
|||
impl MemoryDatabase {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new() -> Database {
|
||||
tracing::warn!("Using in-memory database. Data will not be saved across restarts!");
|
||||
Database::new(Self::default())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
37
src/main.rs
37
src/main.rs
|
|
@ -18,36 +18,16 @@ use ldap::ldap_handler;
|
|||
use listener::ListenerPath;
|
||||
|
||||
async fn create_dummy_users(db: &mut Database) {
|
||||
db.create_user(User {
|
||||
username: "admin".to_string(),
|
||||
domain: None,
|
||||
password: "adminadmin".to_string(),
|
||||
// TODO: what should we put here?
|
||||
mail: "admin".to_string(),
|
||||
role: Role::Admin,
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
for letter in &["a", "b", "c"] {
|
||||
let domain = format!("{letter}.localhost");
|
||||
db.create_domain(&domain).await.unwrap().unwrap();
|
||||
// TODO: customize admin password
|
||||
if db.list_all_users().await.unwrap().is_empty() {
|
||||
tracing::info!("Creating admin account with default `adminadmin` password");
|
||||
db.create_user(User {
|
||||
username: letter.to_string(),
|
||||
domain: Some(domain.clone()),
|
||||
username: "admin".to_string(),
|
||||
domain: None,
|
||||
password: "adminadmin".to_string(),
|
||||
mail: format!("{letter}@{domain}"),
|
||||
role: Role::DomainAdmin(domain.clone()),
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
db.create_user(User {
|
||||
username: format!("user{letter}"),
|
||||
domain: Some(domain.clone()),
|
||||
password: "adminadmin".to_string(),
|
||||
mail: format!("user{letter}@{domain}"),
|
||||
role: Role::User,
|
||||
// TODO: what should we put here?
|
||||
mail: "admin".to_string(),
|
||||
role: Role::Admin,
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
|
|
@ -74,6 +54,7 @@ async fn main() -> Result<(), GlobalError> {
|
|||
} else {
|
||||
MemoryDatabase::new()
|
||||
};
|
||||
tracing::info!("Database loaded successfully");
|
||||
create_dummy_users(&mut db).await;
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
|
|
|
|||
Loading…
Reference in a new issue