start on some SQLite code

This commit is contained in:
Nadja Reitzenstein 2023-04-04 16:53:00 +02:00
parent a9143b0cdd
commit a4f6c77b26
4 changed files with 151 additions and 0 deletions

46
Cargo.lock generated
View File

@ -1085,6 +1085,7 @@ dependencies = [
"rkyv_typename",
"rsasl",
"rumqttc",
"rusqlite",
"rust-argon2",
"rustls",
"rustls-native-certs",
@ -1215,6 +1216,18 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fastrand"
version = "1.8.0"
@ -1514,6 +1527,15 @@ dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa"
dependencies = [
"hashbrown",
]
[[package]]
name = "hdrhistogram"
version = "7.5.2"
@ -1799,6 +1821,16 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "libsqlite3-sys"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.8"
@ -2633,6 +2665,20 @@ dependencies = [
"webpki",
]
[[package]]
name = "rusqlite"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]]
name = "rust-argon2"
version = "0.8.3"

View File

@ -112,6 +112,8 @@ rustls-native-certs = "0.6.1"
shadow-rs = "0.11"
rusqlite = "0.28"
[dependencies.rsasl]
version = "2.0.0"
default_features = false

View File

@ -15,6 +15,7 @@ use std::sync::Arc;
use thiserror::Error;
pub mod db;
mod sqlite;
use crate::users::db::UserData;
use crate::UserDB;
@ -211,3 +212,12 @@ impl Users {
Ok(0)
}
}
pub trait UserDb {
type User;
type Error: std::error::Error + miette::Diagnostic;
fn lookup(&self, userid: &str) -> Result<Option<Self::User>, Self::Error>;
fn get_roles(&self, user: &Self::User) -> Result<Vec<String>, Self::Error>;
}

93
bffhd/users/sqlite.rs Normal file
View File

@ -0,0 +1,93 @@
use std::collections::HashMap;
use std::path::Path;
use rusqlite::{Connection, OptionalExtension};
use crate::users::db::{User, UserData};
pub struct UserSqliteDB {
conn: Connection,
}
impl UserSqliteDB {
pub fn open(path: impl AsRef<Path>) -> rusqlite::Result<Self> {
let conn = Connection::open(path)?;
conn.execute("CREATE TABLE IF NOT EXISTS users (\
id INTEGER PRIMARY KEY,\
name TEXT NOT NULL,\
password TEXT\
)", ())?;
conn.execute("CREATE TABLE IF NOT EXISTS user_kv (\
id INTEGER PRIMARY KEY,\
user INTEGER,\
key TEXT NOT NULL,\
value TEXT NOT NULL,\
FOREIGN KEY(user) REFERENCES users(id)\
)", ())?;
conn.execute("CREATE TABLE IF NOT EXISTS user_roles (\
id INTEGER PRIMARY KEY,\
user INTEGER,\
role TEXT NOT NULL,\
FOREIGN KEY(user) REFERENCES users(id)\
)", ())?;
Ok(Self { conn })
}
pub fn get(&self, uid: &str) -> rusqlite::Result<Option<User>> {
let sqlite_user = self.conn.query_row("SELECT id, name, password FROM users WHERE name = ?1", [uid], |row| {
Ok(SqliteUser {
id: row.get(0)?,
name: row.get(1)?,
password: row.get(2)?,
})
}).optional()?;
if let Some(SqliteUser { name, password, .. }) = sqlite_user {
let mut kv_stmt = self.conn.prepare("SELECT key, value FROM user_kv WHERE user_id = :id")?;
let kv: HashMap<String, String> = kv_stmt
.query_map(&[(":id", &name)], |row| Ok((row.get(0)?, row.get(1)?)))?
.filter_map(|fields| fields.ok())
.collect();
let mut roles_stmt = self.conn.prepare("SELECT role FROM user_roles WHERE user_id = :id")?;
let roles: Vec<String> = roles_stmt
.query_map(&[(":id", &name)], |row| row.get(0))?
.filter_map(|fields| fields.ok())
.collect();
let passwd = if password.is_empty() { None } else { Some(password) };
Ok(Some(User {
id: name,
userdata: UserData {
roles,
passwd,
kv,
}
}))
} else {
Ok(None)
}
}
pub fn put(&self, uid: &str, user: &SqliteUser) -> rusqlite::Result<()> {
todo!()
}
}
pub struct SqliteUser {
id: i32,
name: String,
password: String,
}
struct SqliteUserKv {
id: i32,
user_id: i32,
key: String,
value: String,
}
struct SqliteRoles {
id: i32,
user_id: i32,
role: String,
}