From a4f6c77b2694a801adb41cdb92da1bc41aa59edb Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Tue, 4 Apr 2023 16:53:00 +0200 Subject: [PATCH 1/2] start on some SQLite code --- Cargo.lock | 46 +++++++++++++++++++++ Cargo.toml | 2 + bffhd/users/mod.rs | 10 +++++ bffhd/users/sqlite.rs | 93 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 bffhd/users/sqlite.rs diff --git a/Cargo.lock b/Cargo.lock index 2fe2470..a13030d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 2805841..16f5477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/bffhd/users/mod.rs b/bffhd/users/mod.rs index bd7a7f9..f4a93a7 100644 --- a/bffhd/users/mod.rs +++ b/bffhd/users/mod.rs @@ -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, Self::Error>; + + fn get_roles(&self, user: &Self::User) -> Result, Self::Error>; +} \ No newline at end of file diff --git a/bffhd/users/sqlite.rs b/bffhd/users/sqlite.rs new file mode 100644 index 0000000..0dd1500 --- /dev/null +++ b/bffhd/users/sqlite.rs @@ -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) -> rusqlite::Result { + 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> { + 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 = 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 = 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, +} \ No newline at end of file From 187a9f6781da6bf968ae57ae7269969382641340 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 30 Apr 2023 00:46:45 +0200 Subject: [PATCH 2/2] It do be compiling --- Cargo.lock | 137 +++++++++++++----- Cargo.toml | 8 +- api/schema | 2 +- bffhd/lib.rs | 1 + bffhd/sql/mod.rs | 5 + bffhd/sql/models.rs | 16 ++ bffhd/sql/schema.rs | 35 +++++ bffhd/sql/sqlite.rs | 76 ++++++++++ bffhd/users/mod.rs | 2 +- build.rs | 1 + diesel.toml | 8 + .../2023-04-20-114002_create_userdb/down.sql | 3 + .../2023-04-20-114002_create_userdb/up.sql | 20 +++ runtime/lightproc/src/proc_handle.rs | 1 - 14 files changed, 274 insertions(+), 41 deletions(-) create mode 100644 bffhd/sql/mod.rs create mode 100644 bffhd/sql/models.rs create mode 100644 bffhd/sql/schema.rs create mode 100644 bffhd/sql/sqlite.rs create mode 100644 diesel.toml create mode 100644 migrations/2023-04-20-114002_create_userdb/down.sql create mode 100644 migrations/2023-04-20-114002_create_userdb/up.sql diff --git a/Cargo.lock b/Cargo.lock index a13030d..8b31016 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1040,6 +1040,40 @@ dependencies = [ "syn", ] +[[package]] +name = "diesel" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72eb77396836a4505da85bae0712fa324b74acfe1876d7c2f7e694ef3d0ee373" +dependencies = [ + "diesel_derives", + "libsqlite3-sys", + "r2d2", +] + +[[package]] +name = "diesel_derives" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad74fdcf086be3d4fdd142f67937678fe60ed431c3b2f08599e7687269410c4" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_migrations" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9ae22beef5e9d6fab9225ddb073c1c6c1a7a6ded5019d5da11d1e5c5adc34e2" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + [[package]] name = "diflouroborane" version = "0.4.2" @@ -1059,6 +1093,8 @@ dependencies = [ "clap 3.2.23", "console", "desfire", + "diesel", + "diesel_migrations", "dirs", "erased-serde", "executor", @@ -1085,7 +1121,6 @@ dependencies = [ "rkyv_typename", "rsasl", "rumqttc", - "rusqlite", "rust-argon2", "rustls", "rustls-native-certs", @@ -1216,18 +1251,6 @@ 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" @@ -1527,15 +1550,6 @@ 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" @@ -2000,6 +2014,27 @@ dependencies = [ "syn", ] +[[package]] +name = "migrations_internals" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c493c09323068c01e54c685f7da41a9ccf9219735c3766fbfd6099806ea08fbc" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "migrations_macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a8ff27a350511de30cdabb77147501c36ef02e0451d957abea2f30caffb2b58" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + [[package]] name = "mime" version = "0.3.16" @@ -2362,6 +2397,30 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.51" @@ -2433,6 +2492,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r2d2" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + [[package]] name = "rand" version = "0.8.5" @@ -2665,20 +2735,6 @@ 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" @@ -2763,6 +2819,15 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + [[package]] name = "scopeguard" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 16f5477..47ff063 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,8 @@ rkyv_dyn = "0.7" inventory = "0.1" linkme = "0.2.10" chrono = { version = "0.4", features = ["serde"] } +diesel = { version = "2.0.4", features = ["r2d2", "sqlite"] } +diesel_migrations = "2.0.0" # Password hashing for internal users rust-argon2 = "0.8.3" @@ -112,13 +114,15 @@ rustls-native-certs = "0.6.1" shadow-rs = "0.11" -rusqlite = "0.28" - [dependencies.rsasl] version = "2.0.0" default_features = false features = ["unstable_custom_mechanism", "provider", "registry_static", "config_builder", "plain"] +[features] +default = ["sqlite"] +sqlite = ["diesel/sqlite"] + [dev-dependencies] futures-test = "0.3.16" tempfile = "3.2" diff --git a/api/schema b/api/schema index 19f20f5..cde4677 160000 --- a/api/schema +++ b/api/schema @@ -1 +1 @@ -Subproject commit 19f20f5154f0eced6288ff56cac840025ee51da1 +Subproject commit cde4677575f8e133ac764663e131c80fc891d545 diff --git a/bffhd/lib.rs b/bffhd/lib.rs index 8e24c15..ad0c84a 100644 --- a/bffhd/lib.rs +++ b/bffhd/lib.rs @@ -16,6 +16,7 @@ pub mod config; /// Internal Databases build on top of LMDB, a mmap()'ed B-tree DB optimized for reads pub mod db; +pub mod sql; /// Shared error type pub mod error; diff --git a/bffhd/sql/mod.rs b/bffhd/sql/mod.rs new file mode 100644 index 0000000..287b028 --- /dev/null +++ b/bffhd/sql/mod.rs @@ -0,0 +1,5 @@ +mod schema; +mod models; + +#[cfg(feature = "sqlite")] +pub mod sqlite; \ No newline at end of file diff --git a/bffhd/sql/models.rs b/bffhd/sql/models.rs new file mode 100644 index 0000000..705a507 --- /dev/null +++ b/bffhd/sql/models.rs @@ -0,0 +1,16 @@ +use diesel::{Insertable, Queryable}; +use super::schema::users; + +#[derive(Queryable)] +pub struct User { + pub id: i32, + pub name: String, + pub password: Option, +} + +#[derive(Insertable)] +#[diesel(table_name = users)] +pub struct NewUser<'a> { + pub name: &'a str, + pub password: Option<&'a str>, +} \ No newline at end of file diff --git a/bffhd/sql/schema.rs b/bffhd/sql/schema.rs new file mode 100644 index 0000000..d44f8bd --- /dev/null +++ b/bffhd/sql/schema.rs @@ -0,0 +1,35 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + user_kv (id) { + id -> Integer, + user -> Nullable, + key -> Text, + value -> Binary, + } +} + +diesel::table! { + user_roles (id) { + id -> Integer, + user -> Nullable, + role -> Text, + } +} + +diesel::table! { + users (id) { + id -> Integer, + name -> Text, + password -> Nullable, + } +} + +diesel::joinable!(user_kv -> users (user)); +diesel::joinable!(user_roles -> users (user)); + +diesel::allow_tables_to_appear_in_same_query!( + user_kv, + user_roles, + users, +); diff --git a/bffhd/sql/sqlite.rs b/bffhd/sql/sqlite.rs new file mode 100644 index 0000000..d5e978d --- /dev/null +++ b/bffhd/sql/sqlite.rs @@ -0,0 +1,76 @@ +use std::error::Error; +use thiserror::Error; +use diesel::{Connection, ExpressionMethods, Insertable, OptionalExtension, QueryDsl, RunQueryDsl, SqliteConnection}; +use diesel::associations::HasTable; +use diesel::r2d2::{ConnectionManager, Pool}; +use diesel::sqlite::Sqlite; +use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; +use crate::sql::models::{NewUser, User}; +use crate::sql::schema::users::password; +use crate::users::UserDb; + +const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); + +pub struct SqliteUserDB { + connection: Pool>, +} + +#[derive(Debug, Error)] +pub enum SqliteError { + #[error("failed to open new connection")] + PoolError(#[from] #[source] diesel::r2d2::PoolError), + #[error("failed to execute SQL commands")] + SqlError(#[from] #[source] diesel::r2d2::Error), + #[error("failed to apply migration")] + MigrationError(#[source] Box), +} + +impl SqliteUserDB { + fn new(connection: Pool>) -> Self { + Self { connection } + } + + fn run_migrations(db: &mut impl MigrationHarness) -> Result<(), SqliteError> { + let applied_migrations = db + .run_pending_migrations(MIGRATIONS) + .map_err(SqliteError::MigrationError)?; + for version in applied_migrations { + tracing::info!(?version, "applied migration"); + } + Ok(()) + } + + pub fn setup(url: impl Into) -> Result { + let mut manager = ConnectionManager::::new(url); + let pool = Pool::builder() + .test_on_check_out(true) + .build(manager)?; + + let mut conn = pool.get()?; + Self::run_migrations(&mut conn)?; + + Ok(Self::new(pool)) + } + + pub fn lookup(&self, uid: &str) -> Result, SqliteError> { + use super::schema::users::dsl::*; + + let mut conn = self.connection.get()?; + users + .filter(name.eq(uid)) + .first(&mut conn) + .optional() + .map_err(|e| SqliteError::SqlError(diesel::r2d2::Error::QueryError(e))) + } + + pub fn insert(&self, new_user: NewUser) -> Result<(), SqliteError> { + use super::schema::users::dsl::*; + + let mut conn = self.connection.get()?; + new_user + .insert_into(users) + .execute(&mut conn) + .map(|_| ()) + .map_err(|e| SqliteError::SqlError(diesel::r2d2::Error::QueryError(e))) + } +} \ No newline at end of file diff --git a/bffhd/users/mod.rs b/bffhd/users/mod.rs index f4a93a7..ce9bc59 100644 --- a/bffhd/users/mod.rs +++ b/bffhd/users/mod.rs @@ -13,9 +13,9 @@ use std::path::Path; use std::sync::Arc; use thiserror::Error; +use crate::sql::sqlite::SqliteUserDB; pub mod db; -mod sqlite; use crate::users::db::UserData; use crate::UserDB; diff --git a/build.rs b/build.rs index 37f84e1..7974404 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,5 @@ fn main() { // Extract build-time information using the `shadow-rs` crate shadow_rs::new(); + println!("cargo:rerun-if-changed=migrations"); } diff --git a/diesel.toml b/diesel.toml new file mode 100644 index 0000000..d81ae29 --- /dev/null +++ b/diesel.toml @@ -0,0 +1,8 @@ +# For documentation on how to configure this file, +# see https://diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "bffhd/sql/schema.rs" + +[migrations_directory] +dir = "migrations" diff --git a/migrations/2023-04-20-114002_create_userdb/down.sql b/migrations/2023-04-20-114002_create_userdb/down.sql new file mode 100644 index 0000000..1e4e09b --- /dev/null +++ b/migrations/2023-04-20-114002_create_userdb/down.sql @@ -0,0 +1,3 @@ +DROP TABLE user_roles; +DROP TABLE user_kv; +DROP TABLE users; \ No newline at end of file diff --git a/migrations/2023-04-20-114002_create_userdb/up.sql b/migrations/2023-04-20-114002_create_userdb/up.sql new file mode 100644 index 0000000..53aef4d --- /dev/null +++ b/migrations/2023-04-20-114002_create_userdb/up.sql @@ -0,0 +1,20 @@ +CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + password TEXT +); + +CREATE TABLE IF NOT EXISTS user_kv ( + id INTEGER PRIMARY KEY NOT NULL, + user INTEGER NOT NULL, + key TEXT NOT NULL, + value BLOB NOT NULL, + FOREIGN KEY (user) REFERENCES users(id) +); + +CREATE TABLE IF NOT EXISTS user_roles ( + id INTEGER PRIMARY KEY NOT NULL, + user INTEGER NOT NULL, + role TEXT NOT NULL, + FOREIGN KEY (user) REFERENCES users(id) +); diff --git a/runtime/lightproc/src/proc_handle.rs b/runtime/lightproc/src/proc_handle.rs index f04fef0..3452219 100644 --- a/runtime/lightproc/src/proc_handle.rs +++ b/runtime/lightproc/src/proc_handle.rs @@ -6,7 +6,6 @@ use crate::state::*; use std::fmt::{self, Debug, Formatter}; use std::future::Future; use std::marker::{PhantomData, Unpin}; -use std::mem::MaybeUninit; use std::pin::Pin; use std::ptr::NonNull; use std::sync::atomic::Ordering;