chore: Migrate all the crates to edition 2024

This commit is contained in:
Valentin Tolmer
2025-03-30 21:10:04 -05:00
committed by nitnelave
parent e18f2af54f
commit ba9bcb3894
76 changed files with 358 additions and 302 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ name = "lldap"
version = "0.6.2-alpha"
description = "Super-simple and lightweight LDAP server"
categories = ["authentication", "command-line-utilities"]
edition = "2021"
edition.workspace = true
keywords = ["cli", "ldap", "graphql", "server", "authentication"]
authors.workspace = true
homepage.workspace = true
+1 -1
View File
@@ -1,4 +1,4 @@
use anyhow::{bail, Context as AnyhowContext, Result};
use anyhow::{Context as AnyhowContext, Result, bail};
use lldap_domain::types::{AttributeType, AttributeValue, JpegPhoto};
pub fn deserialize_attribute_value(
+10 -9
View File
@@ -1,6 +1,6 @@
use chrono::TimeZone;
use ldap3_proto::{
proto::LdapOp, LdapFilter, LdapPartialAttribute, LdapResultCode, LdapSearchResultEntry,
LdapFilter, LdapPartialAttribute, LdapResultCode, LdapSearchResultEntry, proto::LdapOp,
};
use tracing::{debug, instrument, warn};
@@ -9,10 +9,9 @@ use crate::domain::{
ldap::{
error::{LdapError, LdapResult},
utils::{
expand_attribute_wildcards, get_custom_attribute,
get_group_id_from_distinguished_name_or_plain_name,
get_user_id_from_distinguished_name_or_plain_name, map_group_field, ExpandedAttributes,
GroupFieldType, LdapInfo,
ExpandedAttributes, GroupFieldType, LdapInfo, expand_attribute_wildcards,
get_custom_attribute, get_group_id_from_distinguished_name_or_plain_name,
get_user_id_from_distinguished_name_or_plain_name, map_group_field,
},
},
schema::PublicSchema,
@@ -51,10 +50,12 @@ pub fn get_group_attribute(
vec![group.id.0.to_string().into_bytes()]
}
GroupFieldType::DisplayName => vec![group.display_name.to_string().into_bytes()],
GroupFieldType::CreationDate => vec![chrono::Utc
.from_utc_datetime(&group.creation_date)
.to_rfc3339()
.into_bytes()],
GroupFieldType::CreationDate => vec![
chrono::Utc
.from_utc_datetime(&group.creation_date)
.to_rfc3339()
.into_bytes(),
],
GroupFieldType::Member => group
.users
.iter()
+10 -9
View File
@@ -1,6 +1,6 @@
use chrono::TimeZone;
use ldap3_proto::{
proto::LdapOp, LdapFilter, LdapPartialAttribute, LdapResultCode, LdapSearchResultEntry,
LdapFilter, LdapPartialAttribute, LdapResultCode, LdapSearchResultEntry, proto::LdapOp,
};
use tracing::{debug, instrument, warn};
@@ -9,10 +9,9 @@ use crate::domain::{
ldap::{
error::{LdapError, LdapResult},
utils::{
expand_attribute_wildcards, get_custom_attribute,
get_group_id_from_distinguished_name_or_plain_name,
get_user_id_from_distinguished_name_or_plain_name, map_user_field, ExpandedAttributes,
LdapInfo, UserFieldType,
ExpandedAttributes, LdapInfo, UserFieldType, expand_attribute_wildcards,
get_custom_attribute, get_group_id_from_distinguished_name_or_plain_name,
get_user_id_from_distinguished_name_or_plain_name, map_user_field,
},
},
schema::PublicSchema,
@@ -74,10 +73,12 @@ pub fn get_user_attribute(
UserFieldType::PrimaryField(UserColumn::DisplayName) => {
vec![user.display_name.clone()?.into_bytes()]
}
UserFieldType::PrimaryField(UserColumn::CreationDate) => vec![chrono::Utc
.from_utc_datetime(&user.creation_date)
.to_rfc3339()
.into_bytes()],
UserFieldType::PrimaryField(UserColumn::CreationDate) => vec![
chrono::Utc
.from_utc_datetime(&user.creation_date)
.to_rfc3339()
.into_bytes(),
],
UserFieldType::Attribute(attr, _, _) => get_custom_attribute(&user.attributes, &attr)?,
UserFieldType::NoMatch => match attribute.as_str() {
"1.1" => return None,
@@ -12,12 +12,12 @@ use lldap_domain_handlers::handler::{
};
use lldap_domain_model::{
error::{DomainError, Result},
model::{self, deserialize, GroupColumn, MembershipColumn},
model::{self, GroupColumn, MembershipColumn, deserialize},
};
use sea_orm::{
sea_query::{Alias, Cond, Expr, Func, IntoCondition, OnConflict, SimpleExpr},
ActiveModelTrait, ColumnTrait, DatabaseTransaction, EntityTrait, QueryFilter, QueryOrder,
QuerySelect, QueryTrait, Set, TransactionTrait,
sea_query::{Alias, Cond, Expr, Func, IntoCondition, OnConflict, SimpleExpr},
};
use tracing::instrument;
+5 -5
View File
@@ -1,13 +1,13 @@
use crate::domain::sql_tables::{DbConnection, SchemaVersion, LAST_SCHEMA_VERSION};
use crate::domain::sql_tables::{DbConnection, LAST_SCHEMA_VERSION, SchemaVersion};
use itertools::Itertools;
use lldap_domain::types::{AttributeType, GroupId, JpegPhoto, Serialized, UserId, Uuid};
use sea_orm::{
sea_query::{
all, BinOper, ColumnDef, Expr, ForeignKey, ForeignKeyAction, Func, Index, Query,
SimpleExpr, Table, Value,
},
ConnectionTrait, DatabaseTransaction, DbErr, DeriveIden, FromQueryResult, Iden, Order,
Statement, TransactionTrait,
sea_query::{
BinOper, ColumnDef, Expr, ForeignKey, ForeignKeyAction, Func, Index, Query, SimpleExpr,
Table, Value, all,
},
};
use serde::{Deserialize, Serialize};
use tracing::{error, info, instrument, warn};
+1 -1
View File
@@ -1,5 +1,5 @@
use super::{
opaque_handler::{login, registration, OpaqueHandler},
opaque_handler::{OpaqueHandler, login, registration},
sql_backend_handler::SqlBackendHandler,
};
use async_trait::async_trait;
+49 -39
View File
@@ -252,27 +252,31 @@ mod tests {
is_hardcoded: false,
is_readonly: false,
};
assert!(fixture
.handler
.get_schema()
.await
.unwrap()
.user_attributes
.attributes
.contains(&expected_value));
assert!(
fixture
.handler
.get_schema()
.await
.unwrap()
.user_attributes
.attributes
.contains(&expected_value)
);
fixture
.handler
.delete_user_attribute(&"new_attribute".into())
.await
.unwrap();
assert!(!fixture
.handler
.get_schema()
.await
.unwrap()
.user_attributes
.attributes
.contains(&expected_value));
assert!(
!fixture
.handler
.get_schema()
.await
.unwrap()
.user_attributes
.attributes
.contains(&expected_value)
);
}
#[tokio::test]
@@ -336,27 +340,31 @@ mod tests {
is_hardcoded: false,
is_readonly: false,
};
assert!(fixture
.handler
.get_schema()
.await
.unwrap()
.group_attributes
.attributes
.contains(&expected_value));
assert!(
fixture
.handler
.get_schema()
.await
.unwrap()
.group_attributes
.attributes
.contains(&expected_value)
);
fixture
.handler
.delete_group_attribute(&"new_attriBUte".into())
.await
.unwrap();
assert!(!fixture
.handler
.get_schema()
.await
.unwrap()
.group_attributes
.attributes
.contains(&expected_value));
assert!(
!fixture
.handler
.get_schema()
.await
.unwrap()
.group_attributes
.attributes
.contains(&expected_value)
);
}
#[tokio::test]
@@ -396,12 +404,14 @@ mod tests {
.delete_user_object_class(&new_object_class)
.await
.unwrap();
assert!(fixture
.handler
.get_schema()
.await
.unwrap()
.extra_user_object_classes
.is_empty());
assert!(
fixture
.handler
.get_schema()
.await
.unwrap()
.extra_user_object_classes
.is_empty()
);
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
use crate::domain::sql_migrations::{
get_schema_version, migrate_from_version, upgrade_to_v1, Metadata,
Metadata, get_schema_version, migrate_from_version, upgrade_to_v1,
};
use sea_orm::{
sea_query::Query, ConnectionTrait, DeriveValueType, Iden, QueryResult, TryGetable, Value,
ConnectionTrait, DeriveValueType, Iden, QueryResult, TryGetable, Value, sea_query::Query,
};
use serde::{Deserialize, Serialize};
@@ -9,14 +9,14 @@ use lldap_domain_handlers::handler::{
};
use lldap_domain_model::{
error::{DomainError, Result},
model::{self, deserialize, GroupColumn, UserColumn},
model::{self, GroupColumn, UserColumn, deserialize},
};
use sea_orm::{
sea_query::{
query::OnConflict, Alias, Cond, Expr, Func, IntoColumnRef, IntoCondition, SimpleExpr,
},
ActiveModelTrait, ActiveValue, ColumnTrait, DatabaseTransaction, EntityTrait, ModelTrait,
QueryFilter, QueryOrder, QuerySelect, QueryTrait, Set, TransactionTrait,
sea_query::{
Alias, Cond, Expr, Func, IntoColumnRef, IntoCondition, SimpleExpr, query::OnConflict,
},
};
use std::collections::HashSet;
use tracing::instrument;
@@ -800,7 +800,7 @@ mod tests {
#[tokio::test]
async fn test_get_user_groups() {
let fixture = TestFixture::new().await;
let get_group_ids = |user: &'static str| async {
let get_group_ids = async |user: &'static str| {
let mut groups = fixture
.handler
.get_user_groups(&UserId::new(user))
+4 -4
View File
@@ -183,14 +183,14 @@ impl<Handler: BackendHandler> AccessControlledBackendHandler<Handler> {
pub fn get_admin_handler(
&self,
validation_result: &ValidationResults,
) -> Option<&impl AdminBackendHandler> {
) -> Option<&(impl AdminBackendHandler + use<Handler>)> {
validation_result.is_admin().then_some(&self.handler)
}
pub fn get_readonly_handler(
&self,
validation_result: &ValidationResults,
) -> Option<&impl ReadonlyBackendHandler> {
) -> Option<&(impl ReadonlyBackendHandler + use<Handler>)> {
validation_result.can_read_all().then_some(&self.handler)
}
@@ -198,7 +198,7 @@ impl<Handler: BackendHandler> AccessControlledBackendHandler<Handler> {
&self,
validation_result: &ValidationResults,
user_id: &UserId,
) -> Option<&impl UserWriteableBackendHandler> {
) -> Option<&(impl UserWriteableBackendHandler + use<Handler>)> {
validation_result
.can_write(user_id)
.then_some(&self.handler)
@@ -208,7 +208,7 @@ impl<Handler: BackendHandler> AccessControlledBackendHandler<Handler> {
&self,
validation_result: &ValidationResults,
user_id: &UserId,
) -> Option<&impl UserReadableBackendHandler> {
) -> Option<&(impl UserReadableBackendHandler + use<Handler>)> {
validation_result.can_read(user_id).then_some(&self.handler)
}
+5 -4
View File
@@ -1,13 +1,14 @@
use actix_web::{
HttpRequest, HttpResponse,
cookie::{Cookie, SameSite},
dev::{Service, ServiceRequest, ServiceResponse, Transform},
error::{ErrorBadRequest, ErrorUnauthorized},
web, HttpRequest, HttpResponse,
web,
};
use actix_web_httpauth::extractors::bearer::BearerAuth;
use anyhow::Result;
use chrono::prelude::*;
use futures::future::{ok, Ready};
use futures::future::{Ready, ok};
use futures_util::FutureExt;
use hmac::Hmac;
use jwt::{SignWithKey, VerifyWithKey};
@@ -22,7 +23,7 @@ use time::ext::NumericalDuration;
use tracing::{debug, info, instrument, warn};
use lldap_auth::{
access_control::ValidationResults, login, password_reset, registration, JWTClaims,
JWTClaims, access_control::ValidationResults, login, password_reset, registration,
};
use lldap_domain::types::{GroupDetails, GroupName, UserId};
use lldap_domain_handlers::handler::{
@@ -35,7 +36,7 @@ use crate::{
infra::{
access_control::{ReadonlyBackendHandler, UserReadableBackendHandler},
tcp_backend_handler::*,
tcp_server::{error_to_http_response, AppState, TcpError, TcpResult},
tcp_server::{AppState, TcpError, TcpResult, error_to_http_response},
},
};
+1 -1
View File
@@ -1,6 +1,6 @@
use std::str::FromStr;
use clap::{builder::EnumValueParser, Parser};
use clap::{Parser, builder::EnumValueParser};
use lettre::message::Mailbox;
use serde::{Deserialize, Serialize};
use strum::{EnumString, IntoStaticStr};
+32 -16
View File
@@ -10,13 +10,13 @@ use crate::{
database_string::DatabaseUrl,
},
};
use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use figment::{
providers::{Env, Format, Serialized, Toml},
Figment,
providers::{Env, Format, Serialized, Toml},
};
use figment_file_provider_adapter::FileAdapter;
use lldap_auth::opaque::{server::ServerSetup, KeyPair};
use lldap_auth::opaque::{KeyPair, server::ServerSetup};
use lldap_domain::types::{AttributeName, UserId};
use secstr::SecUtf8;
use serde::{Deserialize, Serialize};
@@ -219,23 +219,33 @@ pub fn compare_private_key_hashes(
PrivateKeyLocation::KeyFile(old_location, file_path),
PrivateKeyLocation::KeySeed(new_location),
) => {
bail!("The private key is configured to be generated from a seed (from {new_location:?}), but it used to come from the file {file_path:?} (defined in {old_location:?}). Did you just upgrade from <=v0.4 to >=v0.5? The key seed was not supported, revert to just using the file.");
bail!(
"The private key is configured to be generated from a seed (from {new_location:?}), but it used to come from the file {file_path:?} (defined in {old_location:?}). Did you just upgrade from <=v0.4 to >=v0.5? The key seed was not supported, revert to just using the file."
);
}
(PrivateKeyLocation::Default, PrivateKeyLocation::KeySeed(new_location)) => {
bail!("The private key is configured to be generated from a seed (from {new_location:?}), but it used to come from default key file \"server_key\". Did you just upgrade from <=v0.4 to >=v0.5? The key seed was not yet supported, revert to just using the file.");
bail!(
"The private key is configured to be generated from a seed (from {new_location:?}), but it used to come from default key file \"server_key\". Did you just upgrade from <=v0.4 to >=v0.5? The key seed was not yet supported, revert to just using the file."
);
}
(
PrivateKeyLocation::KeyFile(old_location, old_path),
PrivateKeyLocation::KeyFile(new_location, new_path),
) => {
if old_path == new_path {
bail!("The contents of the private key file from {old_path:?} have changed. This usually means that the file was deleted and re-created. If using docker, make sure that the folder is made persistent (by mounting a volume or a directory). If you have several instances of LLDAP, make sure they share the same file (or switch to a key seed).");
bail!(
"The contents of the private key file from {old_path:?} have changed. This usually means that the file was deleted and re-created. If using docker, make sure that the folder is made persistent (by mounting a volume or a directory). If you have several instances of LLDAP, make sure they share the same file (or switch to a key seed)."
);
} else {
bail!("The private key file used to be {old_path:?} (defined in {old_location:?}), but now is {new_path:?} (defined in {new_location:?}. Make sure to copy the old file in the new location.");
bail!(
"The private key file used to be {old_path:?} (defined in {old_location:?}), but now is {new_path:?} (defined in {new_location:?}. Make sure to copy the old file in the new location."
);
}
}
(old_location, new_location) => {
bail!("The private key has changed. It used to come from {old_location:?}, but now it comes from {new_location:?}.");
bail!(
"The private key has changed. It used to come from {old_location:?}, but now it comes from {new_location:?}."
);
}
}
}
@@ -362,7 +372,9 @@ fn get_server_setup<L: Into<PrivateKeyLocationOrFigment>>(
file_path
);
} else if file_path == "server_key" {
eprintln!("WARNING: A key_seed was given, we will ignore the key_file and generate one from the seed! Set key_file to an empty string in the config to silence this message.");
eprintln!(
"WARNING: A key_seed was given, we will ignore the key_file and generate one from the seed! Set key_file to an empty string in the config to silence this message."
);
} else {
println!("Generating the private key from the key_seed");
}
@@ -609,7 +621,7 @@ where
figment_config,
)?);
if config.jwt_secret.is_none() {
use rand::{seq::SliceRandom, Rng};
use rand::{Rng, seq::SliceRandom};
struct Symbols;
impl rand::prelude::Distribution<char> for Symbols {
@@ -617,18 +629,22 @@ where
*b"01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+,-./:;<=>?_~!@#$%^&*()[]{}:;".choose(rng).unwrap() as char
}
}
bail!("The JWT secret must be initialized to a random string, preferably at least 32 characters long. \
bail!(
"The JWT secret must be initialized to a random string, preferably at least 32 characters long. \
Either set the `jwt_secret` config value or the `LLDAP_JWT_SECRET` environment variable. \
You can generate the value by running\n\
LC_ALL=C tr -dc 'A-Za-z0-9!#%&'\\''()*+,-./:;<=>?@[\\]^_{{|}}~' </dev/urandom | head -c 32; echo ''\n\
or you can use this random value: {}",
rand::thread_rng()
.sample_iter(&Symbols)
.take(32)
.collect::<String>());
rand::thread_rng()
.sample_iter(&Symbols)
.take(32)
.collect::<String>()
);
}
if config.smtp_options.tls_required.is_some() {
println!("DEPRECATED: smtp_options.tls_required field is deprecated, it never did anything. You can replace it with smtp_options.smtp_encryption.");
println!(
"DEPRECATED: smtp_options.tls_required field is deprecated, it never did anything. You can replace it with smtp_options.smtp_encryption."
);
}
Ok(config)
}
+9 -9
View File
@@ -11,14 +11,14 @@ use crate::infra::{
use actix_web::FromRequest;
use actix_web::HttpMessage;
use actix_web::{error::JsonPayloadError, web, Error, HttpRequest, HttpResponse};
use actix_web::{Error, HttpRequest, HttpResponse, error::JsonPayloadError, web};
use actix_web_httpauth::extractors::bearer::BearerAuth;
use juniper::{
http::{
graphiql::graphiql_source, playground::playground_source, GraphQLBatchRequest,
GraphQLRequest,
},
EmptySubscription, FieldError, RootNode, ScalarValue,
http::{
GraphQLBatchRequest, GraphQLRequest, graphiql::graphiql_source,
playground::playground_source,
},
};
use lldap_auth::{access_control::ValidationResults, types::UserId};
use lldap_domain_handlers::handler::BackendHandler;
@@ -48,18 +48,18 @@ impl<Handler: BackendHandler> Context<Handler> {
}
}
pub fn get_admin_handler(&self) -> Option<&impl AdminBackendHandler> {
pub fn get_admin_handler(&self) -> Option<&(impl AdminBackendHandler + use<Handler>)> {
self.handler.get_admin_handler(&self.validation_result)
}
pub fn get_readonly_handler(&self) -> Option<&impl ReadonlyBackendHandler> {
pub fn get_readonly_handler(&self) -> Option<&(impl ReadonlyBackendHandler + use<Handler>)> {
self.handler.get_readonly_handler(&self.validation_result)
}
pub fn get_writeable_handler(
&self,
user_id: &UserId,
) -> Option<&impl UserWriteableBackendHandler> {
) -> Option<&(impl UserWriteableBackendHandler + use<Handler>)> {
self.handler
.get_writeable_handler(&self.validation_result, user_id)
}
@@ -67,7 +67,7 @@ impl<Handler: BackendHandler> Context<Handler> {
pub fn get_readable_handler(
&self,
user_id: &UserId,
) -> Option<&impl UserReadableBackendHandler> {
) -> Option<&(impl UserReadableBackendHandler + use<Handler>)> {
self.handler
.get_readable_handler(&self.validation_result, user_id)
}
+17 -13
View File
@@ -7,11 +7,11 @@ use crate::{
AdminBackendHandler, ReadonlyBackendHandler, UserReadableBackendHandler,
UserWriteableBackendHandler,
},
graphql::api::{field_error_callback, Context},
graphql::api::{Context, field_error_callback},
},
};
use anyhow::{anyhow, Context as AnyhowContext};
use juniper::{graphql_object, FieldError, FieldResult, GraphQLInputObject, GraphQLObject};
use anyhow::{Context as AnyhowContext, anyhow};
use juniper::{FieldError, FieldResult, GraphQLInputObject, GraphQLObject, graphql_object};
use lldap_domain::{
requests::{
CreateAttributeRequest, CreateGroupRequest, CreateUserRequest, UpdateGroupRequest,
@@ -24,8 +24,8 @@ use lldap_domain::{
},
};
use lldap_domain_handlers::handler::BackendHandler;
use lldap_validation::attributes::{validate_attribute_name, ALLOWED_CHARACTERS_DESCRIPTION};
use tracing::{debug, debug_span, Instrument, Span};
use lldap_validation::attributes::{ALLOWED_CHARACTERS_DESCRIPTION, validate_attribute_name};
use tracing::{Instrument, Span, debug, debug_span};
#[derive(PartialEq, Eq, Debug)]
/// The top-level GraphQL mutation type.
@@ -785,8 +785,8 @@ mod tests {
use super::*;
use crate::infra::{graphql::query::Query, test_utils::MockTestBackendHandler};
use juniper::{
execute, graphql_value, DefaultScalarValue, EmptySubscription, GraphQLType, InputValue,
RootNode, Variables,
DefaultScalarValue, EmptySubscription, GraphQLType, InputValue, RootNode, Variables,
execute, graphql_value,
};
use lldap_auth::access_control::{Permission, ValidationResults};
use lldap_domain::types::{AttributeName, AttributeType};
@@ -897,9 +897,11 @@ mod tests {
let expected_error_msg =
"Cannot create attribute with invalid name. Valid characters: a-z, A-Z, 0-9, and dash (-). Invalid chars found: _"
.to_string();
assert!(errors
.iter()
.all(|e| e.error().message() == expected_error_msg));
assert!(
errors
.iter()
.all(|e| e.error().message() == expected_error_msg)
);
}
Err(_) => {
panic!();
@@ -1000,9 +1002,11 @@ mod tests {
let expected_error_msg =
"Cannot create attribute with invalid name. Valid characters: a-z, A-Z, 0-9, and dash (-). Invalid chars found: _"
.to_string();
assert!(errors
.iter()
.all(|e| e.error().message() == expected_error_msg));
assert!(
errors
.iter()
.all(|e| e.error().message() == expected_error_msg)
);
}
Err(_) => {
panic!();
+7 -7
View File
@@ -3,24 +3,24 @@ use std::sync::Arc;
use crate::{
domain::{
deserialize::deserialize_attribute_value,
ldap::utils::{map_user_field, UserFieldType},
ldap::utils::{UserFieldType, map_user_field},
schema::PublicSchema,
},
infra::{
access_control::{ReadonlyBackendHandler, UserReadableBackendHandler},
graphql::api::{field_error_callback, Context},
graphql::api::{Context, field_error_callback},
},
};
use anyhow::Context as AnyhowContext;
use chrono::TimeZone;
use juniper::{graphql_object, FieldResult, GraphQLInputObject};
use juniper::{FieldResult, GraphQLInputObject, graphql_object};
use lldap_domain::types::{
AttributeType, Cardinality, GroupDetails, GroupId, LdapObjectClass, UserId,
};
use lldap_domain_handlers::handler::{BackendHandler, ReadSchemaBackendHandler};
use lldap_domain_model::model::UserColumn;
use serde::{Deserialize, Serialize};
use tracing::{debug, debug_span, Instrument, Span};
use tracing::{Instrument, Span, debug, debug_span};
type DomainRequestFilter = lldap_domain_handlers::handler::UserRequestFilter;
type DomainUser = lldap_domain::types::User;
@@ -786,11 +786,11 @@ impl<Handler: BackendHandler> AttributeValue<Handler> {
#[cfg(test)]
mod tests {
use super::*;
use crate::infra::test_utils::{setup_default_schema, MockTestBackendHandler};
use crate::infra::test_utils::{MockTestBackendHandler, setup_default_schema};
use chrono::TimeZone;
use juniper::{
execute, graphql_value, DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLType,
RootNode, Variables,
DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLType, RootNode, Variables,
execute, graphql_value,
};
use lldap_auth::access_control::{Permission, ValidationResults};
use lldap_domain::{
+2 -2
View File
@@ -1,12 +1,12 @@
use crate::infra::{configuration::LdapsOptions, ldap_server::read_certificates};
use anyhow::{anyhow, bail, ensure, Context, Result};
use anyhow::{Context, Result, anyhow, bail, ensure};
use futures_util::SinkExt;
use ldap3_proto::{
LdapCodec,
proto::{
LdapDerefAliases, LdapFilter, LdapMsg, LdapOp, LdapSearchRequest, LdapSearchResultEntry,
LdapSearchScope,
},
LdapCodec,
};
use tokio::net::TcpStream;
use tokio_rustls::TlsConnector as RustlsTlsConnector;
+1 -1
View File
@@ -1,6 +1,6 @@
use sea_orm::{
sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Table},
ConnectionTrait, DeriveIden,
sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Table},
};
pub use crate::domain::{sql_migrations::Users, sql_tables::DbConnection};
+17 -13
View File
@@ -6,7 +6,7 @@ use crate::{
group::{convert_groups_to_ldap_op, get_groups_list},
user::{convert_users_to_ldap_op, get_user_list},
utils::{
get_user_id_from_distinguished_name, is_subtree, parse_distinguished_name, LdapInfo,
LdapInfo, get_user_id_from_distinguished_name, is_subtree, parse_distinguished_name,
},
},
opaque_handler::OpaqueHandler,
@@ -175,9 +175,11 @@ fn root_dse_response(base_dn: &str) -> LdapOp {
},
LdapPartialAttribute {
atype: "vendorVersion".to_string(),
vals: vec![concat!("lldap_", env!("CARGO_PKG_VERSION"))
.to_string()
.into_bytes()],
vals: vec![
concat!("lldap_", env!("CARGO_PKG_VERSION"))
.to_string()
.into_bytes(),
],
},
LdapPartialAttribute {
atype: "supportedLDAPVersion".to_string(),
@@ -229,13 +231,13 @@ impl<Backend> LdapHandler<Backend> {
}
impl<Backend: LoginHandler> LdapHandler<Backend> {
pub fn get_login_handler(&self) -> &impl LoginHandler {
pub fn get_login_handler(&self) -> &(impl LoginHandler + use<Backend>) {
self.backend_handler.unsafe_get_handler()
}
}
impl<Backend: OpaqueHandler> LdapHandler<Backend> {
pub fn get_opaque_handler(&self) -> &impl OpaqueHandler {
pub fn get_opaque_handler(&self) -> &(impl OpaqueHandler + use<Backend>) {
self.backend_handler.unsafe_get_handler()
}
}
@@ -589,7 +591,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
x
}
let get_user_list = cast(|filter: &LdapFilter| async {
let get_user_list = cast(async |filter: &LdapFilter| {
let need_groups = request
.attrs
.iter()
@@ -927,7 +929,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
#[cfg(test)]
mod tests {
use super::*;
use crate::infra::test_utils::{setup_default_schema, MockTestBackendHandler};
use crate::infra::test_utils::{MockTestBackendHandler, setup_default_schema};
use chrono::TimeZone;
use ldap3_proto::proto::{
LdapDerefAliases, LdapSearchScope, LdapSubstringFilter, LdapWhoamiRequest,
@@ -2308,11 +2310,13 @@ mod tests {
},
LdapPartialAttribute {
atype: "createtimestamp".to_string(),
vals: vec![chrono::Utc
.timestamp_opt(0, 0)
.unwrap()
.to_rfc3339()
.into_bytes()],
vals: vec![
chrono::Utc
.timestamp_opt(0, 0)
.unwrap()
.to_rfc3339()
.into_bytes(),
],
},
LdapPartialAttribute {
atype: "entryuuid".to_string(),
+3 -3
View File
@@ -8,9 +8,9 @@ use crate::{
};
use actix_rt::net::TcpStream;
use actix_server::ServerBuilder;
use actix_service::{fn_service, ServiceFactoryExt};
use anyhow::{anyhow, Context, Result};
use ldap3_proto::{control::LdapControl, proto::LdapMsg, proto::LdapOp, LdapCodec};
use actix_service::{ServiceFactoryExt, fn_service};
use anyhow::{Context, Result, anyhow};
use ldap3_proto::{LdapCodec, control::LdapControl, proto::LdapMsg, proto::LdapOp};
use lldap_domain::types::AttributeName;
use lldap_domain_handlers::handler::{BackendHandler, LoginHandler};
use rustls::PrivateKey;
+2 -2
View File
@@ -1,10 +1,10 @@
use crate::infra::configuration::Configuration;
use actix_web::{
dev::{ServiceRequest, ServiceResponse},
Error,
dev::{ServiceRequest, ServiceResponse},
};
use std::env;
use tracing::{debug, error, Span};
use tracing::{Span, debug, error};
use tracing_actix_web::RootSpanBuilder;
use tracing_subscriber::{filter::EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
+3 -3
View File
@@ -1,8 +1,8 @@
use crate::infra::{cli::SmtpEncryption, configuration::MailOptions};
use anyhow::{anyhow, Ok, Result};
use anyhow::{Ok, Result, anyhow};
use lettre::{
message::Mailbox, transport::smtp::authentication::Credentials, AsyncSmtpTransport,
AsyncTransport, Message, Tokio1Executor,
AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor, message::Mailbox,
transport::smtp::authentication::Credentials,
};
use std::time::Duration;
use tokio::time::sleep;
+2 -2
View File
@@ -8,14 +8,14 @@ use lldap_domain_model::{
model::{self, JwtRefreshStorageColumn, JwtStorageColumn, PasswordResetTokensColumn},
};
use sea_orm::{
sea_query::{Cond, Expr},
ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect,
sea_query::{Cond, Expr},
};
use std::collections::HashSet;
use tracing::{debug, instrument};
fn gen_random_string(len: usize) -> String {
use rand::{distributions::Alphanumeric, rngs::SmallRng, Rng, SeedableRng};
use rand::{Rng, SeedableRng, distributions::Alphanumeric, rngs::SmallRng};
let mut rng = SmallRng::from_entropy();
std::iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
+11 -8
View File
@@ -9,10 +9,10 @@ use crate::{
},
};
use actix_files::Files;
use actix_http::{header, HttpServiceBuilder};
use actix_http::{HttpServiceBuilder, header};
use actix_server::ServerBuilder;
use actix_service::map_config;
use actix_web::{dev::AppConfig, guard, web, App, HttpResponse, Responder};
use actix_web::{App, HttpResponse, Responder, dev::AppConfig, guard, web};
use anyhow::{Context, Result};
use hmac::Hmac;
use lldap_domain_handlers::handler::{BackendHandler, LoginHandler};
@@ -136,7 +136,7 @@ fn http_config<Backend>(
}))
.route(
"/health",
web::get().to(|| async { HttpResponse::Ok().finish() }),
web::get().to(async || HttpResponse::Ok().finish()),
)
.route("/settings", web::get().to(get_settings::<Backend>))
.service(
@@ -180,22 +180,22 @@ pub(crate) struct AppState<Backend> {
}
impl<Backend: BackendHandler> AppState<Backend> {
pub fn get_readonly_handler(&self) -> &impl ReadonlyBackendHandler {
pub fn get_readonly_handler(&self) -> &(impl ReadonlyBackendHandler + use<Backend>) {
self.backend_handler.unsafe_get_handler()
}
}
impl<Backend: TcpBackendHandler> AppState<Backend> {
pub fn get_tcp_handler(&self) -> &impl TcpBackendHandler {
pub fn get_tcp_handler(&self) -> &(impl TcpBackendHandler + use<Backend>) {
self.backend_handler.unsafe_get_handler()
}
}
impl<Backend: OpaqueHandler> AppState<Backend> {
pub fn get_opaque_handler(&self) -> &impl OpaqueHandler {
pub fn get_opaque_handler(&self) -> &(impl OpaqueHandler + use<Backend>) {
self.backend_handler.unsafe_get_handler()
}
}
impl<Backend: LoginHandler> AppState<Backend> {
pub fn get_login_handler(&self) -> &impl LoginHandler {
pub fn get_login_handler(&self) -> &(impl LoginHandler + use<Backend>) {
self.backend_handler.unsafe_get_handler()
}
}
@@ -218,7 +218,10 @@ where
let mail_options = config.smtp_options.clone();
let verbose = config.verbose;
if !assets_path.join("index.html").exists() {
warn!("Cannot find {}, please ensure that assets_path is set correctly and that the front-end files exist.", assets_path.to_string_lossy())
warn!(
"Cannot find {}, please ensure that assets_path is set correctly and that the front-end files exist.",
assets_path.to_string_lossy()
)
}
info!("Starting the API/web server on port {}", config.http_port);
server_builder
+13 -7
View File
@@ -13,7 +13,7 @@ use crate::{
},
infra::{
cli::*,
configuration::{compare_private_key_hashes, Configuration},
configuration::{Configuration, compare_private_key_hashes},
database_string::DatabaseUrl,
db_cleaner::Scheduler,
healthcheck, mail,
@@ -21,10 +21,10 @@ use crate::{
};
use actix::Actor;
use actix_server::ServerBuilder;
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{Context, Result, anyhow, bail};
use futures_util::TryFutureExt;
use sea_orm::{Database, DatabaseConnection};
use tracing::{debug, error, info, instrument, span, warn, Instrument, Level};
use tracing::{Instrument, Level, debug, error, info, instrument, span, warn};
use lldap_domain::requests::{CreateGroupRequest, CreateUserRequest};
use lldap_domain_handlers::handler::{
@@ -35,7 +35,7 @@ use lldap_domain_handlers::handler::{
mod domain;
mod infra;
const ADMIN_PASSWORD_MISSING_ERROR : &str = "The LDAP admin password must be initialized. \
const ADMIN_PASSWORD_MISSING_ERROR: &str = "The LDAP admin password must be initialized. \
Either set the `ldap_user_pass` config value or the `LLDAP_LDAP_USER_PASS` environment variable. \
A minimum of 8 characters is recommended.";
@@ -133,7 +133,9 @@ async fn set_up_server(config: Configuration) -> Result<ServerBuilder> {
force_update_private_key,
) {
(Ok(false), true) => {
bail!("The private key has not changed, but force_update_private_key/LLDAP_FORCE_UPDATE_PRIVATE_KEY is set to true. Please set force_update_private_key to false and restart the server.");
bail!(
"The private key has not changed, but force_update_private_key/LLDAP_FORCE_UPDATE_PRIVATE_KEY is set to true. Please set force_update_private_key to false and restart the server."
);
}
(Ok(true), _) | (Err(_), true) => {
set_private_key_info(&sql_pool, private_key_info).await?;
@@ -159,7 +161,9 @@ async fn set_up_server(config: Configuration) -> Result<ServerBuilder> {
false
};
if !admin_present {
warn!("Could not find an admin user, trying to create the user \"admin\" with the config-provided password");
warn!(
"Could not find an admin user, trying to create the user \"admin\" with the config-provided password"
);
create_admin_user(&backend_handler, &config)
.await
.map_err(|e| anyhow!("Error setting up admin login/account: {:#}", e))
@@ -189,7 +193,9 @@ async fn set_up_server(config: Configuration) -> Result<ServerBuilder> {
))?;
}
if config.force_update_private_key || config.force_ldap_user_pass_reset.is_yes() {
bail!("Restart the server without --force-update-private-key or --force-ldap-user-pass-reset to continue.");
bail!(
"Restart the server without --force-update-private-key or --force-ldap-user-pass-reset to continue."
);
}
let server_builder = infra::ldap_server::build_ldap_server(
&config,
+1 -1
View File
@@ -2,8 +2,8 @@ use crate::common::{
auth::get_token,
env,
graphql::{
add_user_to_group, create_group, create_user, delete_group_query, delete_user_query, post,
AddUserToGroup, CreateGroup, CreateUser, DeleteGroupQuery, DeleteUserQuery,
add_user_to_group, create_group, create_user, delete_group_query, delete_user_query, post,
},
};
use assert_cmd::prelude::*;
+1 -1
View File
@@ -1,5 +1,5 @@
use crate::common::env;
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result, anyhow};
use graphql_client::GraphQLQuery;
use reqwest::blocking::Client;
+2 -2
View File
@@ -1,8 +1,8 @@
use crate::common::{
auth::get_token,
env,
fixture::{new_id, LLDAPFixture, User},
graphql::{get_user_details, list_users, post, GetUserDetails, ListUsers},
fixture::{LLDAPFixture, User, new_id},
graphql::{GetUserDetails, ListUsers, get_user_details, list_users, post},
};
use reqwest::blocking::ClientBuilder;
use serial_test::file_serial;
+1 -1
View File
@@ -2,7 +2,7 @@ use std::collections::HashSet;
use crate::common::{
env,
fixture::{new_id, LLDAPFixture, User},
fixture::{LLDAPFixture, User, new_id},
};
use ldap3::{LdapConn, Scope, SearchEntry};
use serial_test::file_serial;
+25 -17
View File
@@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
use crate::common::{
env,
fixture::{new_id, LLDAPFixture, User},
fixture::{LLDAPFixture, User, new_id},
};
use ldap3::{LdapConn, Scope, SearchEntry, SearchResult};
use serial_test::file_serial;
@@ -43,19 +43,25 @@ fn basic_users_search() {
.expect("failed to find users"),
);
assert!(found_users.contains_key(&user1_name));
assert!(found_users
.get(&user1_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group1_name, base_dn).as_str()));
assert!(
found_users
.get(&user1_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group1_name, base_dn).as_str())
);
assert!(found_users.contains_key(&user2_name));
assert!(found_users
.get(&user2_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group1_name, base_dn).as_str()));
assert!(found_users
.get(&user2_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group2_name, base_dn).as_str()));
assert!(
found_users
.get(&user2_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group1_name, base_dn).as_str())
);
assert!(
found_users
.get(&user2_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", &group2_name, base_dn).as_str())
);
assert!(found_users.contains_key(&user3_name));
assert!(found_users.get(&user3_name).unwrap().is_empty());
ldap.unbind().expect("failed to unbind ldap connection");
@@ -87,10 +93,12 @@ fn admin_search() {
);
assert!(found_users.contains_key(&admin_name));
assert!(found_users
.get(&admin_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", admin_group_name, base_dn).as_str()));
assert!(
found_users
.get(&admin_name)
.unwrap()
.contains(format!("cn={},ou=groups,{}", admin_group_name, base_dn).as_str())
);
ldap.unbind().expect("failed to unbind ldap connection");
}