mirror of
https://github.com/lldap/lldap.git
synced 2026-04-05 14:48:10 +00:00
server: extract the sql backend handler to a separate crate
This commit is contained in:
committed by
nitnelave
parent
ee21d83056
commit
55de3ac329
34
Cargo.lock
generated
34
Cargo.lock
generated
@@ -2549,6 +2549,7 @@ dependencies = [
|
||||
"lldap_frontend_options",
|
||||
"lldap_ldap",
|
||||
"lldap_opaque_handler",
|
||||
"lldap_sql_backend_handler",
|
||||
"lldap_test_utils",
|
||||
"lldap_validation",
|
||||
"log",
|
||||
@@ -2778,6 +2779,39 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lldap_sql_backend_handler"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"base64 0.21.7",
|
||||
"bincode",
|
||||
"chrono",
|
||||
"itertools",
|
||||
"ldap3_proto",
|
||||
"lldap_access_control",
|
||||
"lldap_auth",
|
||||
"lldap_domain",
|
||||
"lldap_domain_handlers",
|
||||
"lldap_domain_model",
|
||||
"lldap_opaque_handler",
|
||||
"lldap_test_utils",
|
||||
"log",
|
||||
"mockall",
|
||||
"orion",
|
||||
"pretty_assertions",
|
||||
"rand 0.8.5",
|
||||
"sea-orm",
|
||||
"secstr",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid 1.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lldap_test_utils"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -132,6 +132,12 @@ pub mod server {
|
||||
pub use super::*;
|
||||
pub type ServerRegistration = opaque_ke::ServerRegistration<DefaultSuite>;
|
||||
pub type ServerSetup = opaque_ke::ServerSetup<DefaultSuite>;
|
||||
|
||||
pub fn generate_random_private_key() -> ServerSetup {
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
ServerSetup::new(&mut rng)
|
||||
}
|
||||
|
||||
/// Methods to register a new user, from the server side.
|
||||
pub mod registration {
|
||||
pub use super::*;
|
||||
|
||||
@@ -61,7 +61,6 @@ mod tests {
|
||||
use lldap_domain_handlers::handler::{GroupRequestFilter, UserRequestFilter};
|
||||
use lldap_test_utils::MockTestBackendHandler;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_compare_user() {
|
||||
|
||||
@@ -172,7 +172,6 @@ mod tests {
|
||||
use lldap_test_utils::MockTestBackendHandler;
|
||||
use mockall::predicate::eq;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_user() {
|
||||
|
||||
@@ -115,7 +115,6 @@ mod tests {
|
||||
use lldap_test_utils::MockTestBackendHandler;
|
||||
use mockall::predicate::eq;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_user() {
|
||||
|
||||
@@ -140,7 +140,6 @@ mod tests {
|
||||
use mockall::predicate::eq;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashSet;
|
||||
|
||||
|
||||
fn setup_target_user_groups(
|
||||
mock: &mut MockTestBackendHandler,
|
||||
|
||||
@@ -333,7 +333,6 @@ mod tests {
|
||||
use lldap_test_utils::MockTestBackendHandler;
|
||||
use mockall::predicate::eq;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_root_dse() {
|
||||
|
||||
87
crates/sql-backend-handler/Cargo.toml
Normal file
87
crates/sql-backend-handler/Cargo.toml
Normal file
@@ -0,0 +1,87 @@
|
||||
[package]
|
||||
name = "lldap_sql_backend_handler"
|
||||
version = "0.1.0"
|
||||
description = "SQL backend for LLDAP"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
test = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "*"
|
||||
async-trait = "0.1"
|
||||
base64 = "0.21"
|
||||
bincode = "1.3"
|
||||
itertools = "0.10"
|
||||
ldap3_proto = "0.6.0"
|
||||
orion = "0.17"
|
||||
serde_json = "1"
|
||||
tracing = "*"
|
||||
|
||||
[dependencies.chrono]
|
||||
features = ["serde"]
|
||||
version = "*"
|
||||
|
||||
[dependencies.rand]
|
||||
features = ["small_rng", "getrandom"]
|
||||
version = "0.8"
|
||||
|
||||
[dependencies.sea-orm]
|
||||
workspace = true
|
||||
features = [
|
||||
"macros",
|
||||
"with-chrono",
|
||||
"with-uuid",
|
||||
"sqlx-all",
|
||||
"runtime-actix-rustls",
|
||||
]
|
||||
|
||||
[dependencies.secstr]
|
||||
features = ["serde"]
|
||||
version = "*"
|
||||
|
||||
[dependencies.serde]
|
||||
workspace = true
|
||||
|
||||
[dependencies.uuid]
|
||||
version = "1"
|
||||
features = ["v1", "v3"]
|
||||
|
||||
[dependencies.lldap_access_control]
|
||||
path = "../access-control"
|
||||
|
||||
[dependencies.lldap_auth]
|
||||
path = "../auth"
|
||||
features = ["opaque_server", "opaque_client", "sea_orm"]
|
||||
|
||||
[dependencies.lldap_domain]
|
||||
path = "../domain"
|
||||
|
||||
[dependencies.lldap_domain_handlers]
|
||||
path = "../domain-handlers"
|
||||
|
||||
[dependencies.lldap_domain_model]
|
||||
path = "../domain-model"
|
||||
|
||||
[dependencies.lldap_opaque_handler]
|
||||
path = "../opaque-handler"
|
||||
|
||||
[dev-dependencies.lldap_test_utils]
|
||||
path = "../test-utils"
|
||||
|
||||
[dev-dependencies]
|
||||
log = "*"
|
||||
mockall = "0.11.4"
|
||||
pretty_assertions = "1"
|
||||
|
||||
[dev-dependencies.tokio]
|
||||
features = ["full"]
|
||||
version = "1.25"
|
||||
|
||||
[dev-dependencies.tracing-subscriber]
|
||||
version = "0.3"
|
||||
features = ["env-filter", "tracing-log"]
|
||||
11
crates/sql-backend-handler/src/lib.rs
Normal file
11
crates/sql-backend-handler/src/lib.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
pub(crate) mod logging;
|
||||
pub(crate) mod sql_backend_handler;
|
||||
pub(crate) mod sql_group_backend_handler;
|
||||
pub(crate) mod sql_opaque_handler;
|
||||
pub(crate) mod sql_schema_backend_handler;
|
||||
pub(crate) mod sql_user_backend_handler;
|
||||
|
||||
pub use sql_backend_handler::SqlBackendHandler;
|
||||
pub use sql_opaque_handler::register_password;
|
||||
pub mod sql_migrations;
|
||||
pub mod sql_tables;
|
||||
10
crates/sql-backend-handler/src/logging.rs
Normal file
10
crates/sql-backend-handler/src/logging.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
#[cfg(test)]
|
||||
pub fn init_for_tests() {
|
||||
if let Err(e) = tracing_subscriber::FmtSubscriber::builder()
|
||||
.with_max_level(tracing::Level::DEBUG)
|
||||
.with_test_writer()
|
||||
.try_init()
|
||||
{
|
||||
log::warn!("Could not set up test logging: {:#}", e);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,24 @@
|
||||
use crate::{domain::sql_tables::DbConnection, infra::configuration::Configuration};
|
||||
use crate::sql_tables::DbConnection;
|
||||
use async_trait::async_trait;
|
||||
use lldap_auth::opaque::server::ServerSetup;
|
||||
use lldap_domain_handlers::handler::BackendHandler;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SqlBackendHandler {
|
||||
pub(crate) config: Configuration,
|
||||
pub(crate) opaque_setup: ServerSetup,
|
||||
pub(crate) sql_pool: DbConnection,
|
||||
}
|
||||
|
||||
impl SqlBackendHandler {
|
||||
pub fn new(config: Configuration, sql_pool: DbConnection) -> Self {
|
||||
SqlBackendHandler { config, sql_pool }
|
||||
pub fn new(opaque_setup: ServerSetup, sql_pool: DbConnection) -> Self {
|
||||
SqlBackendHandler {
|
||||
opaque_setup,
|
||||
sql_pool,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pool(&self) -> &DbConnection {
|
||||
&self.sql_pool
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +28,11 @@ impl BackendHandler for SqlBackendHandler {}
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::{domain::sql_tables::init_table, infra::configuration::ConfigurationBuilder};
|
||||
use lldap_auth::{opaque, registration};
|
||||
use crate::sql_tables::init_table;
|
||||
use lldap_auth::{
|
||||
opaque::{self, server::generate_random_private_key},
|
||||
registration,
|
||||
};
|
||||
use lldap_domain::{
|
||||
requests::{CreateGroupRequest, CreateUserRequest},
|
||||
types::{Attribute as DomainAttribute, GroupId, UserId},
|
||||
@@ -32,12 +43,8 @@ pub mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use sea_orm::Database;
|
||||
|
||||
pub fn get_default_config() -> Configuration {
|
||||
ConfigurationBuilder::for_tests()
|
||||
}
|
||||
|
||||
pub async fn get_in_memory_db() -> DbConnection {
|
||||
crate::infra::logging::init_for_tests();
|
||||
crate::logging::init_for_tests();
|
||||
let mut sql_opt = sea_orm::ConnectOptions::new("sqlite::memory:".to_owned());
|
||||
sql_opt
|
||||
.max_connections(1)
|
||||
@@ -139,8 +146,7 @@ pub mod tests {
|
||||
impl TestFixture {
|
||||
pub async fn new() -> Self {
|
||||
let sql_pool = get_initialized_db().await;
|
||||
let config = get_default_config();
|
||||
let handler = SqlBackendHandler::new(config, sql_pool);
|
||||
let handler = SqlBackendHandler::new(generate_random_private_key(), sql_pool);
|
||||
insert_user_no_password(&handler, "bob").await;
|
||||
insert_user_no_password(&handler, "patrick").await;
|
||||
insert_user_no_password(&handler, "John").await;
|
||||
@@ -160,8 +166,7 @@ pub mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_sql_injection() {
|
||||
let sql_pool = get_initialized_db().await;
|
||||
let config = get_default_config();
|
||||
let handler = SqlBackendHandler::new(config, sql_pool);
|
||||
let handler = SqlBackendHandler::new(generate_random_private_key(), sql_pool);
|
||||
let user_name = UserId::new(r#"bob"e"i'o;aü"#);
|
||||
insert_user_no_password(&handler, user_name.as_str()).await;
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::domain::sql_backend_handler::SqlBackendHandler;
|
||||
use crate::sql_backend_handler::SqlBackendHandler;
|
||||
use async_trait::async_trait;
|
||||
use lldap_access_control::UserReadableBackendHandler;
|
||||
use lldap_domain::{
|
||||
@@ -334,7 +334,7 @@ impl SqlBackendHandler {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::domain::sql_backend_handler::tests::*;
|
||||
use crate::sql_backend_handler::tests::*;
|
||||
use lldap_domain::{
|
||||
requests::CreateAttributeRequest,
|
||||
types::{Attribute, AttributeType, GroupName, UserId},
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::domain::sql_tables::{DbConnection, LAST_SCHEMA_VERSION, SchemaVersion};
|
||||
use crate::sql_tables::{DbConnection, LAST_SCHEMA_VERSION, SchemaVersion};
|
||||
use itertools::Itertools;
|
||||
use lldap_domain::types::{AttributeType, GroupId, JpegPhoto, Serialized, UserId, Uuid};
|
||||
use sea_orm::{
|
||||
@@ -30,7 +30,7 @@ pub enum Users {
|
||||
}
|
||||
|
||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum Groups {
|
||||
pub(crate) enum Groups {
|
||||
Table,
|
||||
GroupId,
|
||||
DisplayName,
|
||||
@@ -40,7 +40,7 @@ pub enum Groups {
|
||||
}
|
||||
|
||||
#[derive(DeriveIden, Clone, Copy)]
|
||||
pub enum Memberships {
|
||||
pub(crate) enum Memberships {
|
||||
Table,
|
||||
UserId,
|
||||
GroupId,
|
||||
@@ -48,7 +48,7 @@ pub enum Memberships {
|
||||
|
||||
#[allow(clippy::enum_variant_names)] // The table names are generated from the enum.
|
||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum UserAttributeSchema {
|
||||
pub(crate) enum UserAttributeSchema {
|
||||
Table,
|
||||
UserAttributeSchemaName,
|
||||
UserAttributeSchemaType,
|
||||
@@ -59,7 +59,7 @@ pub enum UserAttributeSchema {
|
||||
}
|
||||
|
||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum UserAttributes {
|
||||
pub(crate) enum UserAttributes {
|
||||
Table,
|
||||
UserAttributeUserId,
|
||||
UserAttributeName,
|
||||
@@ -68,7 +68,7 @@ pub enum UserAttributes {
|
||||
|
||||
#[allow(clippy::enum_variant_names)] // The table names are generated from the enum.
|
||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum GroupAttributeSchema {
|
||||
pub(crate) enum GroupAttributeSchema {
|
||||
Table,
|
||||
GroupAttributeSchemaName,
|
||||
GroupAttributeSchemaType,
|
||||
@@ -79,7 +79,7 @@ pub enum GroupAttributeSchema {
|
||||
}
|
||||
|
||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum GroupAttributes {
|
||||
pub(crate) enum GroupAttributes {
|
||||
Table,
|
||||
GroupAttributeGroupId,
|
||||
GroupAttributeName,
|
||||
@@ -87,14 +87,14 @@ pub enum GroupAttributes {
|
||||
}
|
||||
|
||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum UserObjectClasses {
|
||||
pub(crate) enum UserObjectClasses {
|
||||
Table,
|
||||
LowerObjectClass,
|
||||
ObjectClass,
|
||||
}
|
||||
|
||||
#[derive(DeriveIden, PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum GroupObjectClasses {
|
||||
pub(crate) enum GroupObjectClasses {
|
||||
Table,
|
||||
LowerObjectClass,
|
||||
ObjectClass,
|
||||
@@ -102,7 +102,7 @@ pub enum GroupObjectClasses {
|
||||
|
||||
// Metadata about the SQL DB.
|
||||
#[derive(DeriveIden)]
|
||||
pub enum Metadata {
|
||||
pub(crate) enum Metadata {
|
||||
Table,
|
||||
// Which version of the schema we're at.
|
||||
Version,
|
||||
@@ -111,12 +111,12 @@ pub enum Metadata {
|
||||
}
|
||||
|
||||
#[derive(FromQueryResult, PartialEq, Eq, Debug)]
|
||||
pub struct JustSchemaVersion {
|
||||
pub version: SchemaVersion,
|
||||
pub(crate) struct JustSchemaVersion {
|
||||
pub(crate) version: SchemaVersion,
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "debug", ret)]
|
||||
pub async fn get_schema_version(pool: &DbConnection) -> Option<SchemaVersion> {
|
||||
pub(crate) async fn get_schema_version(pool: &DbConnection) -> Option<SchemaVersion> {
|
||||
JustSchemaVersion::find_by_statement(
|
||||
pool.get_database_backend().build(
|
||||
Query::select()
|
||||
@@ -131,7 +131,7 @@ pub async fn get_schema_version(pool: &DbConnection) -> Option<SchemaVersion> {
|
||||
.map(|j| j.version)
|
||||
}
|
||||
|
||||
pub async fn upgrade_to_v1(pool: &DbConnection) -> std::result::Result<(), sea_orm::DbErr> {
|
||||
pub(crate) async fn upgrade_to_v1(pool: &DbConnection) -> std::result::Result<(), sea_orm::DbErr> {
|
||||
let builder = pool.get_database_backend();
|
||||
// SQLite needs this pragma to be turned on. Other DB might not understand this, so ignore the
|
||||
// error.
|
||||
@@ -1121,7 +1121,7 @@ macro_rules! to_sync {
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn migrate_from_version(
|
||||
pub(crate) async fn migrate_from_version(
|
||||
pool: &DbConnection,
|
||||
version: SchemaVersion,
|
||||
last_version: SchemaVersion,
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::domain::sql_backend_handler::SqlBackendHandler;
|
||||
use crate::SqlBackendHandler;
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine;
|
||||
use lldap_auth::opaque;
|
||||
@@ -19,7 +19,7 @@ type SqlOpaqueHandler = SqlBackendHandler;
|
||||
fn passwords_match(
|
||||
password_file_bytes: &[u8],
|
||||
clear_password: &str,
|
||||
server_setup: &opaque::server::ServerSetup,
|
||||
opaque_setup: &opaque::server::ServerSetup,
|
||||
username: &UserId,
|
||||
) -> Result<()> {
|
||||
use opaque::{client, server};
|
||||
@@ -30,7 +30,7 @@ fn passwords_match(
|
||||
.map_err(opaque::AuthenticationError::ProtocolError)?;
|
||||
let server_login_start_result = server::login::start_login(
|
||||
&mut rng,
|
||||
server_setup,
|
||||
opaque_setup,
|
||||
Some(password_file),
|
||||
client_login_start_result.message,
|
||||
username,
|
||||
@@ -45,7 +45,7 @@ fn passwords_match(
|
||||
impl SqlBackendHandler {
|
||||
fn get_orion_secret_key(&self) -> Result<orion::aead::SecretKey> {
|
||||
Ok(orion::aead::SecretKey::from_slice(
|
||||
self.config.get_server_keys().private(),
|
||||
self.opaque_setup.keypair().private(),
|
||||
)?)
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ impl LoginHandler for SqlBackendHandler {
|
||||
if passwords_match(
|
||||
&password_hash,
|
||||
&request.password,
|
||||
self.config.get_server_setup(),
|
||||
&self.opaque_setup,
|
||||
&request.name,
|
||||
)
|
||||
.is_ok()
|
||||
@@ -117,7 +117,7 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
||||
// Get the CredentialResponse for the user, or a dummy one if no user/no password.
|
||||
let start_response = opaque::server::login::start_login(
|
||||
&mut rng,
|
||||
self.config.get_server_setup(),
|
||||
&self.opaque_setup,
|
||||
maybe_password_file,
|
||||
request.login_start_request,
|
||||
&user_id,
|
||||
@@ -168,7 +168,7 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
||||
) -> Result<registration::ServerRegistrationStartResponse> {
|
||||
// Generate the server-side key and derive the data to send back.
|
||||
let start_response = opaque::server::registration::start_registration(
|
||||
self.config.get_server_setup(),
|
||||
&self.opaque_setup,
|
||||
request.registration_start_request,
|
||||
&request.username,
|
||||
)?;
|
||||
@@ -210,7 +210,7 @@ impl OpaqueHandler for SqlOpaqueHandler {
|
||||
|
||||
/// Convenience function to set a user's password.
|
||||
#[instrument(skip_all, level = "debug", err, fields(username = %username.as_str()))]
|
||||
pub(crate) async fn register_password(
|
||||
pub async fn register_password(
|
||||
opaque_handler: &SqlOpaqueHandler,
|
||||
username: UserId,
|
||||
password: &SecUtf8,
|
||||
@@ -240,8 +240,12 @@ pub(crate) async fn register_password(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use self::opaque::server::generate_random_private_key;
|
||||
|
||||
use super::*;
|
||||
use crate::domain::sql_backend_handler::tests::*;
|
||||
use crate::sql_backend_handler::tests::{
|
||||
get_initialized_db, insert_user, insert_user_no_password,
|
||||
};
|
||||
|
||||
async fn attempt_login(
|
||||
opaque_handler: &SqlOpaqueHandler,
|
||||
@@ -273,33 +277,30 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_opaque_flow() -> Result<()> {
|
||||
let sql_pool = get_initialized_db().await;
|
||||
crate::infra::logging::init_for_tests();
|
||||
let config = get_default_config();
|
||||
let backend_handler = SqlBackendHandler::new(config.clone(), sql_pool.clone());
|
||||
let opaque_handler = SqlOpaqueHandler::new(config, sql_pool);
|
||||
crate::logging::init_for_tests();
|
||||
let backend_handler = SqlBackendHandler::new(generate_random_private_key(), sql_pool);
|
||||
insert_user_no_password(&backend_handler, "bob").await;
|
||||
insert_user_no_password(&backend_handler, "john").await;
|
||||
attempt_login(&opaque_handler, "bob", "bob00")
|
||||
attempt_login(&backend_handler, "bob", "bob00")
|
||||
.await
|
||||
.unwrap_err();
|
||||
register_password(
|
||||
&opaque_handler,
|
||||
&backend_handler,
|
||||
UserId::new("bob"),
|
||||
&secstr::SecUtf8::from("bob00"),
|
||||
)
|
||||
.await?;
|
||||
attempt_login(&opaque_handler, "bob", "wrong_password")
|
||||
attempt_login(&backend_handler, "bob", "wrong_password")
|
||||
.await
|
||||
.unwrap_err();
|
||||
attempt_login(&opaque_handler, "bob", "bob00").await?;
|
||||
attempt_login(&backend_handler, "bob", "bob00").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bind_user() {
|
||||
let sql_pool = get_initialized_db().await;
|
||||
let config = get_default_config();
|
||||
let handler = SqlOpaqueHandler::new(config, sql_pool.clone());
|
||||
let handler = SqlOpaqueHandler::new(generate_random_private_key(), sql_pool.clone());
|
||||
insert_user(&handler, "bob", "bob00").await;
|
||||
|
||||
handler
|
||||
@@ -328,8 +329,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_user_no_password() {
|
||||
let sql_pool = get_initialized_db().await;
|
||||
let config = get_default_config();
|
||||
let handler = SqlBackendHandler::new(config, sql_pool.clone());
|
||||
let handler = SqlBackendHandler::new(generate_random_private_key(), sql_pool.clone());
|
||||
insert_user_no_password(&handler, "bob").await;
|
||||
|
||||
handler
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::domain::sql_backend_handler::SqlBackendHandler;
|
||||
use crate::sql_backend_handler::SqlBackendHandler;
|
||||
use async_trait::async_trait;
|
||||
use lldap_domain::{
|
||||
requests::CreateAttributeRequest,
|
||||
@@ -175,7 +175,7 @@ impl SqlBackendHandler {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::domain::sql_backend_handler::tests::*;
|
||||
use crate::sql_backend_handler::tests::*;
|
||||
use lldap_domain::requests::UpdateUserRequest;
|
||||
use lldap_domain::schema::AttributeList;
|
||||
use lldap_domain::types::{Attribute, AttributeType};
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::domain::sql_migrations::{
|
||||
Metadata, get_schema_version, migrate_from_version, upgrade_to_v1,
|
||||
};
|
||||
use crate::sql_migrations::{Metadata, get_schema_version, migrate_from_version, upgrade_to_v1};
|
||||
use sea_orm::{
|
||||
ConnectionTrait, DeriveValueType, Iden, QueryResult, TryGetable, Value, sea_query::Query,
|
||||
};
|
||||
@@ -68,7 +66,6 @@ pub enum PrivateKeyLocation {
|
||||
KeySeed(ConfigLocation),
|
||||
KeyFile(ConfigLocation, std::ffi::OsString),
|
||||
Default,
|
||||
#[cfg(test)]
|
||||
Tests,
|
||||
}
|
||||
|
||||
@@ -123,7 +120,7 @@ pub async fn set_private_key_info(pool: &DbConnection, info: PrivateKeyInfo) ->
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::domain::sql_migrations;
|
||||
use crate::sql_migrations;
|
||||
use lldap_domain::types::{GroupId, JpegPhoto, Serialized, Uuid};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@@ -185,7 +182,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_already_init_table() {
|
||||
crate::infra::logging::init_for_tests();
|
||||
crate::logging::init_for_tests();
|
||||
let sql_pool = get_in_memory_db().await;
|
||||
init_table(&sql_pool).await.unwrap();
|
||||
init_table(&sql_pool).await.unwrap();
|
||||
@@ -193,7 +190,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_migrate_tables() {
|
||||
crate::infra::logging::init_for_tests();
|
||||
crate::logging::init_for_tests();
|
||||
// Test that we add the column creation_date to groups and uuid to users and groups.
|
||||
let sql_pool = get_in_memory_db().await;
|
||||
sql_pool
|
||||
@@ -324,7 +321,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_migration_to_v4() {
|
||||
crate::infra::logging::init_for_tests();
|
||||
crate::logging::init_for_tests();
|
||||
let sql_pool = get_in_memory_db().await;
|
||||
upgrade_to_v1(&sql_pool).await.unwrap();
|
||||
migrate_from_version(&sql_pool, SchemaVersion(1), SchemaVersion(3))
|
||||
@@ -387,7 +384,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_migration_to_v5() {
|
||||
crate::infra::logging::init_for_tests();
|
||||
crate::logging::init_for_tests();
|
||||
let sql_pool = get_in_memory_db().await;
|
||||
upgrade_to_v1(&sql_pool).await.unwrap();
|
||||
migrate_from_version(&sql_pool, SchemaVersion(1), SchemaVersion(4))
|
||||
@@ -473,7 +470,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_migration_to_v6() {
|
||||
crate::infra::logging::init_for_tests();
|
||||
crate::logging::init_for_tests();
|
||||
let sql_pool = get_in_memory_db().await;
|
||||
upgrade_to_v1(&sql_pool).await.unwrap();
|
||||
migrate_from_version(&sql_pool, SchemaVersion(1), SchemaVersion(5))
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::domain::sql_backend_handler::SqlBackendHandler;
|
||||
use crate::sql_backend_handler::SqlBackendHandler;
|
||||
use async_trait::async_trait;
|
||||
use lldap_domain::{
|
||||
requests::{CreateUserRequest, UpdateUserRequest},
|
||||
@@ -414,7 +414,8 @@ impl UserBackendHandler for SqlBackendHandler {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::domain::sql_backend_handler::tests::*;
|
||||
use crate::sql_backend_handler::tests::*;
|
||||
use lldap_auth::opaque::server::generate_random_private_key;
|
||||
use lldap_domain::types::{Attribute, JpegPhoto};
|
||||
use lldap_domain_handlers::handler::SubStringFilter;
|
||||
use lldap_domain_model::model::UserColumn;
|
||||
@@ -734,7 +735,8 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_user_details() {
|
||||
let handler = SqlBackendHandler::new(get_default_config(), get_initialized_db().await);
|
||||
let handler =
|
||||
SqlBackendHandler::new(generate_random_private_key(), get_initialized_db().await);
|
||||
insert_user_no_password(&handler, "bob").await;
|
||||
{
|
||||
let user = handler.get_user_details(&UserId::new("bob")).await.unwrap();
|
||||
@@ -750,7 +752,8 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_user_lowercase() {
|
||||
let handler = SqlBackendHandler::new(get_default_config(), get_initialized_db().await);
|
||||
let handler =
|
||||
SqlBackendHandler::new(generate_random_private_key(), get_initialized_db().await);
|
||||
insert_user_no_password(&handler, "Bob").await;
|
||||
{
|
||||
let user = handler.get_user_details(&UserId::new("bOb")).await.unwrap();
|
||||
@@ -105,6 +105,9 @@ path = "../crates/frontend-options"
|
||||
[dependencies.lldap_ldap]
|
||||
path = "../crates/ldap"
|
||||
|
||||
[dependencies.lldap_sql_backend_handler]
|
||||
path = "../crates/sql-backend-handler"
|
||||
|
||||
[dependencies.lldap_opaque_handler]
|
||||
path = "../crates/opaque-handler"
|
||||
|
||||
@@ -195,6 +198,10 @@ features = ["test"]
|
||||
[dev-dependencies.lldap_test_utils]
|
||||
path = "../crates/test-utils"
|
||||
|
||||
[dev-dependencies.lldap_sql_backend_handler]
|
||||
path = "../crates/sql-backend-handler"
|
||||
features = ["test"]
|
||||
|
||||
[dev-dependencies.reqwest]
|
||||
version = "*"
|
||||
default-features = false
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
pub mod sql_backend_handler;
|
||||
pub mod sql_group_backend_handler;
|
||||
pub mod sql_migrations;
|
||||
pub mod sql_opaque_handler;
|
||||
pub mod sql_schema_backend_handler;
|
||||
pub mod sql_tables;
|
||||
pub mod sql_user_backend_handler;
|
||||
@@ -1,14 +1,9 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{
|
||||
domain::sql_tables::{ConfigLocation, PrivateKeyHash, PrivateKeyInfo, PrivateKeyLocation},
|
||||
infra::{
|
||||
cli::{
|
||||
GeneralConfigOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts, TestEmailOpts,
|
||||
TrueFalseAlways,
|
||||
},
|
||||
database_string::DatabaseUrl,
|
||||
use crate::infra::{
|
||||
cli::{
|
||||
GeneralConfigOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts, TestEmailOpts,
|
||||
TrueFalseAlways,
|
||||
},
|
||||
database_string::DatabaseUrl,
|
||||
};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use figment::{
|
||||
@@ -16,10 +11,17 @@ use figment::{
|
||||
providers::{Env, Format, Serialized, Toml},
|
||||
};
|
||||
use figment_file_provider_adapter::FileAdapter;
|
||||
use lldap_auth::opaque::{KeyPair, server::ServerSetup};
|
||||
use lldap_auth::opaque::{
|
||||
KeyPair,
|
||||
server::{ServerSetup, generate_random_private_key},
|
||||
};
|
||||
use lldap_domain::types::{AttributeName, UserId};
|
||||
use lldap_sql_backend_handler::sql_tables::{
|
||||
ConfigLocation, PrivateKeyHash, PrivateKeyInfo, PrivateKeyLocation,
|
||||
};
|
||||
use secstr::SecUtf8;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use url::Url;
|
||||
|
||||
@@ -157,18 +159,6 @@ impl ConfigurationBuilder {
|
||||
)?;
|
||||
Ok(self.server_setup(Some(server_setup)).private_build()?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn for_tests() -> Configuration {
|
||||
ConfigurationBuilder::default()
|
||||
.verbose(true)
|
||||
.server_setup(Some(ServerSetupConfig {
|
||||
server_setup: generate_random_private_key(),
|
||||
private_key_location: PrivateKeyLocation::Tests,
|
||||
}))
|
||||
.private_build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn stable_hash(val: &[u8]) -> [u8; 32] {
|
||||
@@ -242,6 +232,9 @@ pub fn compare_private_key_hashes(
|
||||
);
|
||||
}
|
||||
}
|
||||
(PrivateKeyLocation::Tests, _) | (_, PrivateKeyLocation::Tests) => {
|
||||
panic!("Test keys unexpected")
|
||||
}
|
||||
(old_location, new_location) => {
|
||||
bail!(
|
||||
"The private key has changed. It used to come from {old_location:?}, but now it comes from {new_location:?}."
|
||||
@@ -253,11 +246,6 @@ pub fn compare_private_key_hashes(
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_random_private_key() -> ServerSetup {
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
ServerSetup::new(&mut rng)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn set_mode(permissions: &mut std::fs::Permissions) {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::domain::sql_tables::DbConnection;
|
||||
use crate::sql_tables::DbConnection;
|
||||
use actix::prelude::{Actor, AsyncContext, Context};
|
||||
use cron::Schedule;
|
||||
use lldap_domain_model::model::{
|
||||
|
||||
@@ -86,8 +86,8 @@ fn schema<Handler: BackendHandler>() -> Schema<Handler> {
|
||||
}
|
||||
|
||||
pub fn export_schema(opts: ExportGraphQLSchemaOpts) -> anyhow::Result<()> {
|
||||
use crate::domain::sql_backend_handler::SqlBackendHandler;
|
||||
use anyhow::Context;
|
||||
use lldap_sql_backend_handler::SqlBackendHandler;
|
||||
let output = schema::<SqlBackendHandler>().as_schema_language();
|
||||
match opts.output_file {
|
||||
None => println!("{}", output),
|
||||
|
||||
@@ -3,7 +3,7 @@ use sea_orm::{
|
||||
sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Table},
|
||||
};
|
||||
|
||||
pub use crate::domain::{sql_migrations::Users, sql_tables::DbConnection};
|
||||
pub use lldap_sql_backend_handler::{sql_migrations::Users, sql_tables::DbConnection};
|
||||
|
||||
/// Contains the refresh tokens for a given user.
|
||||
#[derive(DeriveIden)]
|
||||
|
||||
@@ -53,14 +53,3 @@ pub fn init(config: &Configuration) -> anyhow::Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn init_for_tests() {
|
||||
if let Err(e) = tracing_subscriber::FmtSubscriber::builder()
|
||||
.with_max_level(tracing::Level::DEBUG)
|
||||
.with_test_writer()
|
||||
.try_init()
|
||||
{
|
||||
log::warn!("Could not set up test logging: {:#}", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::tcp_backend_handler::TcpBackendHandler;
|
||||
use crate::domain::sql_backend_handler::SqlBackendHandler;
|
||||
use crate::infra::tcp_backend_handler::TcpBackendHandler;
|
||||
use async_trait::async_trait;
|
||||
use chrono::NaiveDateTime;
|
||||
use lldap_domain::types::UserId;
|
||||
@@ -7,6 +6,7 @@ use lldap_domain_model::{
|
||||
error::*,
|
||||
model::{self, JwtRefreshStorageColumn, JwtStorageColumn, PasswordResetTokensColumn},
|
||||
};
|
||||
use lldap_sql_backend_handler::SqlBackendHandler;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect,
|
||||
sea_query::{Cond, Expr},
|
||||
@@ -33,7 +33,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
.column(JwtStorageColumn::JwtHash)
|
||||
.filter(JwtStorageColumn::Blacklisted.eq(true))
|
||||
.into_tuple::<(i64,)>()
|
||||
.all(&self.sql_pool)
|
||||
.all(self.pool())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|m| m.0 as u64)
|
||||
@@ -59,7 +59,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
expiry_date: chrono::Utc::now().naive_utc() + duration,
|
||||
}
|
||||
.into_active_model();
|
||||
new_token.insert(&self.sql_pool).await?;
|
||||
new_token.insert(self.pool()).await?;
|
||||
Ok((refresh_token, duration))
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
expiry_date,
|
||||
}
|
||||
.into_active_model();
|
||||
new_token.insert(&self.sql_pool).await?;
|
||||
new_token.insert(self.pool()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
Ok(
|
||||
model::JwtRefreshStorage::find_by_id(refresh_token_hash as i64)
|
||||
.filter(JwtRefreshStorageColumn::UserId.eq(user))
|
||||
.one(&self.sql_pool)
|
||||
.one(self.pool())
|
||||
.await?
|
||||
.is_some(),
|
||||
)
|
||||
@@ -106,7 +106,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
.add(JwtStorageColumn::Blacklisted.eq(false)),
|
||||
)
|
||||
.into_tuple::<(i64,)>()
|
||||
.all(&self.sql_pool)
|
||||
.all(self.pool())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|t| t.0 as u64)
|
||||
@@ -114,7 +114,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
model::JwtStorage::update_many()
|
||||
.col_expr(JwtStorageColumn::Blacklisted, Expr::value(true))
|
||||
.filter(JwtStorageColumn::UserId.eq(user))
|
||||
.exec(&self.sql_pool)
|
||||
.exec(self.pool())
|
||||
.await?;
|
||||
Ok(valid_tokens)
|
||||
}
|
||||
@@ -122,7 +122,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
async fn delete_refresh_token(&self, refresh_token_hash: u64) -> Result<()> {
|
||||
model::JwtRefreshStorage::delete_by_id(refresh_token_hash as i64)
|
||||
.exec(&self.sql_pool)
|
||||
.exec(self.pool())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -131,7 +131,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
async fn start_password_reset(&self, user: &UserId) -> Result<Option<String>> {
|
||||
debug!(?user);
|
||||
if model::User::find_by_id(user.clone())
|
||||
.one(&self.sql_pool)
|
||||
.one(self.pool())
|
||||
.await?
|
||||
.is_none()
|
||||
{
|
||||
@@ -148,7 +148,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
expiry_date: chrono::Utc::now().naive_utc() + duration,
|
||||
}
|
||||
.into_active_model();
|
||||
new_token.insert(&self.sql_pool).await?;
|
||||
new_token.insert(self.pool()).await?;
|
||||
Ok(Some(token))
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
async fn get_user_id_for_password_reset_token(&self, token: &str) -> Result<UserId> {
|
||||
Ok(model::PasswordResetTokens::find_by_id(token.to_owned())
|
||||
.filter(PasswordResetTokensColumn::ExpiryDate.gt(chrono::Utc::now().naive_utc()))
|
||||
.one(&self.sql_pool)
|
||||
.one(self.pool())
|
||||
.await?
|
||||
.ok_or_else(|| DomainError::EntityNotFound("Invalid reset token".to_owned()))?
|
||||
.user_id)
|
||||
@@ -165,7 +165,7 @@ impl TcpBackendHandler for SqlBackendHandler {
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
async fn delete_password_reset_token(&self, token: &str) -> Result<()> {
|
||||
let result = model::PasswordResetTokens::delete_by_id(token.to_owned())
|
||||
.exec(&self.sql_pool)
|
||||
.exec(self.pool())
|
||||
.await?;
|
||||
if result.rows_affected == 0 {
|
||||
return Err(DomainError::EntityNotFound(format!(
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::NaiveDateTime;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use lldap_domain::types::UserId;
|
||||
use lldap_domain_model::error::Result;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[async_trait]
|
||||
pub trait TcpBackendHandler: Sync {
|
||||
|
||||
@@ -3,27 +3,23 @@
|
||||
// TODO: Remove next line when it stops warning about async functions.
|
||||
#![allow(clippy::blocks_in_conditions)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{
|
||||
domain::{
|
||||
sql_backend_handler::SqlBackendHandler,
|
||||
sql_opaque_handler::register_password,
|
||||
sql_tables::{get_private_key_info, set_private_key_info},
|
||||
},
|
||||
infra::{
|
||||
cli::*,
|
||||
configuration::{Configuration, compare_private_key_hashes},
|
||||
database_string::DatabaseUrl,
|
||||
db_cleaner::Scheduler,
|
||||
healthcheck, mail,
|
||||
},
|
||||
use crate::infra::{
|
||||
cli::*,
|
||||
configuration::{Configuration, compare_private_key_hashes},
|
||||
database_string::DatabaseUrl,
|
||||
db_cleaner::Scheduler,
|
||||
healthcheck, mail,
|
||||
};
|
||||
use actix::Actor;
|
||||
use actix_server::ServerBuilder;
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use futures_util::TryFutureExt;
|
||||
use lldap_sql_backend_handler::{
|
||||
SqlBackendHandler, register_password,
|
||||
sql_tables::{self, get_private_key_info, set_private_key_info},
|
||||
};
|
||||
use sea_orm::{Database, DatabaseConnection};
|
||||
use std::time::Duration;
|
||||
use tracing::{Instrument, Level, debug, error, info, instrument, span, warn};
|
||||
|
||||
use lldap_domain::requests::{CreateGroupRequest, CreateUserRequest};
|
||||
@@ -32,7 +28,6 @@ use lldap_domain_handlers::handler::{
|
||||
UserListerBackendHandler, UserRequestFilter,
|
||||
};
|
||||
|
||||
mod domain;
|
||||
mod infra;
|
||||
|
||||
const ADMIN_PASSWORD_MISSING_ERROR: &str = "The LDAP admin password must be initialized. \
|
||||
@@ -109,7 +104,7 @@ async fn setup_sql_tables(database_url: &DatabaseUrl) -> Result<DatabaseConnecti
|
||||
.sqlx_logging_level(log::LevelFilter::Debug);
|
||||
Database::connect(sql_opt).await?
|
||||
};
|
||||
domain::sql_tables::init_table(&sql_pool)
|
||||
sql_tables::init_table(&sql_pool)
|
||||
.await
|
||||
.context("while creating base tables")?;
|
||||
infra::jwt_sql_tables::init_table(&sql_pool)
|
||||
@@ -145,7 +140,8 @@ async fn set_up_server(config: Configuration) -> Result<ServerBuilder> {
|
||||
return Err(anyhow!("The private key encoding the passwords has changed since last successful startup. Changing the private key will invalidate all existing passwords. If you want to proceed, restart the server with the CLI arg --force-update-private-key=true or the env variable LLDAP_FORCE_UPDATE_PRIVATE_KEY=true. You probably also want --force-ldap-user-pass-reset / LLDAP_FORCE_LDAP_USER_PASS_RESET=true to reset the admin password to the value in the configuration.").context(e));
|
||||
}
|
||||
}
|
||||
let backend_handler = SqlBackendHandler::new(config.clone(), sql_pool.clone());
|
||||
let backend_handler =
|
||||
SqlBackendHandler::new(config.get_server_setup().clone(), sql_pool.clone());
|
||||
ensure_group_exists(&backend_handler, "lldap_admin").await?;
|
||||
ensure_group_exists(&backend_handler, "lldap_password_manager").await?;
|
||||
ensure_group_exists(&backend_handler, "lldap_strict_readonly").await?;
|
||||
|
||||
Reference in New Issue
Block a user