Database setup stage 1 done

This commit is contained in:
Nevernown
2025-11-21 22:27:04 +01:00
parent 194b564e43
commit 679cc2ce1a
6 changed files with 291 additions and 12 deletions

View File

@@ -12,3 +12,4 @@ tracing = "0.1.41"
tracing-subscriber = "0.3.19"
sigma = { path = "../sigma" }
rusqlite = "0.37.0"
argon2 = { version = "0.5.3", features = ["std"] }

View File

@@ -1,25 +1,42 @@
use axum::{Json, Router, http::StatusCode, routing::get};
#[allow(unused)]
use log::{info, debug, warn, error, trace};
use rusqlite::Connection;
use rusqlite::{Connection, params};
use sigma::repsonse::SigmaInformation;
use tracing::instrument;
mod user;
const NAME: &str = "TinyIdentity";
const VERSION: &str = "0.0.0.1";
const DB_PATH: &str = "identity.db";
const DB_CREATE_USER_TABLE: &str =
"CREATE TABLE IF NOT EXISTS users (
const DB_CREATE_USER_TABLE: &str = "
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
pass TEXT NOT NULL
)";
const DB_CREATE_META_TABLE: &str =
"CREATE TABLE IF NOT EXISTS meta (
key VARCHAR(16) PRIMARY KEY,
pass BLOB NOT NULL
);
";
const DB_CREATE_SETTINGS_TABLE: &str = "
CREATE TABLE IF NOT EXISTS settings (
key VARCHAR(16) PRIMARY KEY NOT NULL,
value TEXT NOT NULL
)";
);
";
const DB_INSERT_SETUP_STAGE_SETTING: &str = "
INSERT INTO settings (key, value) VALUES ('setupStage', 0)
";
const UPDATE_SETUP_STATUS: &str = "
UPDATE settings
SET value = ?1
WHERE key = 'setupStage';
";
const SELECT_SETUP_STATUS: &str = "
SELECT value
FROM settings
WHERE key = 'setupStage'
";
pub fn router() -> Router {
@@ -38,19 +55,49 @@ async fn setup() -> Result<StatusCode, StatusCode> {
error!("{:?}", e);
return Err(StatusCode::INTERNAL_SERVER_ERROR)
}
let connection = connection.expect("Connection cannot be an error here.");
let connection = connection.expect("Connection always exists here.");
info!("Connected to identity database");
if let Err(e) = connection.execute(DB_CREATE_USER_TABLE, [],) {
error!("{:?}", e);
return Err(StatusCode::INTERNAL_SERVER_ERROR)
}
info!("Created user table");
if let Err(e) = connection.execute(DB_CREATE_META_TABLE, [],) {
if let Err(e) = connection.execute(DB_CREATE_SETTINGS_TABLE, [],) {
error!("{:?}", e);
return Err(StatusCode::INTERNAL_SERVER_ERROR)
}
info!("Created settings table");
Ok(StatusCode::OK)
let stage = match SetupStage::get(&connection) {
Ok(stage) => stage,
Err(_) => {
error!("Coud, not retrieve setup status");
return Err(StatusCode::INTERNAL_SERVER_ERROR);
},
};
info!("Retreived setup status setting: {stage:?}");
if stage != SetupStage::New {
error!("{NAME} is already set up");
return Err(StatusCode::CONFLICT)
}
info!("Proceeding with setup steps because setup status is {stage:?}");
if let Err(e) = connection.execute(DB_INSERT_SETUP_STAGE_SETTING, [],) {
error!("{:?}", e);
return Err(StatusCode::INTERNAL_SERVER_ERROR)
}
info!("Updated setup stage setting");
if let Err(_) = user::create(&connection, "admin", "changeme") {
Err(StatusCode::INTERNAL_SERVER_ERROR)
} else if let Err(_) = stage.next().set(&connection) {
Err(StatusCode::INTERNAL_SERVER_ERROR)
} else {
Ok(StatusCode::OK)
}
}
#[instrument]
@@ -61,4 +108,61 @@ async fn dummy() -> Json<()> {
#[instrument]
async fn information() -> Json<SigmaInformation> {
Json(SigmaInformation::new(NAME, VERSION))
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SetupStage {
New,
Finished
}
impl SetupStage {
fn get(connection: &Connection) -> Result<Self, ()> {
let result = connection.query_one(SELECT_SETUP_STATUS, [], |row| {
row.get::<&str, String>("value")
});
match result {
Ok(value) => SetupStage::try_from(value),
Err(rusqlite::Error::QueryReturnedNoRows) => return Ok(SetupStage::New),
Err(e) => {
error!("{e:}");
return Err(())
},
}
}
fn set(&self, connection: &Connection) -> Result<(), ()>{
let value = format!("{}", *self as u32);
if let Err(e) = connection.execute(UPDATE_SETUP_STATUS, params![value]) {
error!("{e:?}");
Err(())
} else {
Ok(())
}
}
fn next(&self) -> Self {
match &self {
Self::New => Self::Finished,
Self::Finished => Self::Finished,
}
}
}
impl TryFrom<String> for SetupStage {
type Error = ();
fn try_from(value: String) -> Result<Self, ()> {
if let Ok(value) = value.parse::<u32>() {
match value {
0 => Ok(Self::New),
1 => Ok(Self::Finished),
_ => {
error!("Unknown setup stage value {value}");
Err(())
}
}
} else {
error!("Could not parse server status '{value}' to u32 (SetupStage)");
Err(())
}
}
}

47
identity/src/user.rs Normal file
View File

@@ -0,0 +1,47 @@
use argon2::password_hash::SaltString;
use argon2::PasswordHasher;
use argon2::password_hash::rand_core::OsRng;
use log::{error, info};
use rusqlite::{Connection, params};
const CREATE_USER: &str = "
INSERT INTO users (name, pass) VALUES (?1, ?2)
";
const SELECT_USER: &str ="
SELECT * FROM users WHERE name = ?1
";
struct User {
id: u64,
name: String,
pass: String
}
pub fn create(connection: &Connection, name: &str, pass: &str) -> Result<(), ()> {
let argon2 = argon2::Argon2::default();
let salt = SaltString::generate(&mut OsRng);
let pass = match argon2.hash_password(pass.as_bytes(), &salt) {
Ok(pass) => pass.to_string(),
Err(e) => {
error!("{e:?}");
return Err(());
},
};
info!("Hashed password for user {}", name);
match connection.execute(CREATE_USER, params![name, pass]) {
Ok(_) => return Ok(()),
Err(e) => {
error!("{e:?}");
return Err(())
},
}
}
pub fn verify(connection: &Connection, name: &str, pass: &str) {
// match connection.query_one(SELECT_USER, params![name], |row| {})) {
// }
}