Password reset config lib
- First part to the config lib - LDAP connection module - Constants
This commit is contained in:
parent
f64fecd6c4
commit
8a2b9d0952
@ -6,3 +6,6 @@ license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
toml.workspace = true
|
||||
|
||||
129
crates/config/src/connection.rs
Normal file
129
crates/config/src/connection.rs
Normal file
@ -0,0 +1,129 @@
|
||||
//! LDAP connection configuration.
|
||||
//!
|
||||
//! Loads `/etc/ldap-kit/connection.toml`, applies default values,
|
||||
//! validates required fields, and applies write credential fallbacks.
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
|
||||
use crate::constants::{CONNECTION_CONFIG, DEFAULT_LDAP_URL};
|
||||
|
||||
/// Final connection configuration.
|
||||
///
|
||||
/// All defaults and fallbacks have already been applied.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConnectionConfig {
|
||||
pub ldap: LdapSettings,
|
||||
pub read: BindCredentials,
|
||||
pub write: BindCredentials,
|
||||
}
|
||||
|
||||
/// LDAP server settings.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LdapSettings {
|
||||
pub url: String,
|
||||
pub base_dn: String,
|
||||
}
|
||||
|
||||
/// LDAP bind credentials.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BindCredentials {
|
||||
pub bind_dn: String,
|
||||
pub bind_pw: String,
|
||||
}
|
||||
|
||||
/// Structure used only while parsing the TOML file.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RawConnectionConfig {
|
||||
ldap: RawLdapSettings,
|
||||
read: RawBindCredentials,
|
||||
|
||||
#[serde(default)]
|
||||
write: Option<RawBindCredentials>,
|
||||
}
|
||||
|
||||
/// Raw LDAP settings from TOML.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RawLdapSettings {
|
||||
#[serde(default = "default_ldap_url")]
|
||||
url: String,
|
||||
|
||||
base_dn: String,
|
||||
}
|
||||
|
||||
/// Raw bind credentials from TOML.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RawBindCredentials {
|
||||
bind_dn: String,
|
||||
|
||||
#[serde(default)]
|
||||
bind_pw: String,
|
||||
}
|
||||
|
||||
/// Default LDAP URL.
|
||||
fn default_ldap_url() -> String {
|
||||
DEFAULT_LDAP_URL.to_owned()
|
||||
}
|
||||
|
||||
impl ConnectionConfig {
|
||||
/// Load the default connection configuration.
|
||||
pub fn load() -> Result<Self> {
|
||||
Self::load_from(CONNECTION_CONFIG)
|
||||
}
|
||||
|
||||
/// Load connection configuration from a specific file.
|
||||
pub fn load_from(path: &str) -> Result<Self> {
|
||||
let contents = fs::read_to_string(path)?;
|
||||
|
||||
let raw: RawConnectionConfig = toml::from_str(&contents)?;
|
||||
|
||||
Self::from_raw(raw)
|
||||
}
|
||||
|
||||
/// Convert parsed configuration into the final validated structure.
|
||||
fn from_raw(raw: RawConnectionConfig) -> Result<Self> {
|
||||
// Required fields
|
||||
if raw.ldap.base_dn.trim().is_empty() {
|
||||
bail!("ldap.base_dn is required");
|
||||
}
|
||||
|
||||
if raw.read.bind_dn.trim().is_empty() {
|
||||
bail!("read.bind_dn is required");
|
||||
}
|
||||
|
||||
let read = BindCredentials {
|
||||
bind_dn: raw.read.bind_dn,
|
||||
bind_pw: raw.read.bind_pw,
|
||||
};
|
||||
|
||||
// Apply write fallbacks.
|
||||
let write = match raw.write {
|
||||
Some(write) => BindCredentials {
|
||||
bind_dn: if write.bind_dn.trim().is_empty() {
|
||||
read.bind_dn.clone()
|
||||
} else {
|
||||
write.bind_dn
|
||||
},
|
||||
|
||||
bind_pw: if write.bind_pw.is_empty() {
|
||||
read.bind_pw.clone()
|
||||
} else {
|
||||
write.bind_pw
|
||||
},
|
||||
},
|
||||
|
||||
None => read.clone(),
|
||||
};
|
||||
|
||||
Ok(ConnectionConfig {
|
||||
ldap: LdapSettings {
|
||||
url: raw.ldap.url,
|
||||
base_dn: raw.ldap.base_dn,
|
||||
},
|
||||
|
||||
read,
|
||||
write,
|
||||
})
|
||||
}
|
||||
}
|
||||
13
crates/config/src/constants.rs
Normal file
13
crates/config/src/constants.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! Configuration file locations and default values.
|
||||
|
||||
/// Configuration directory.
|
||||
pub const CONFIG_DIR: &str = "/etc/ldap-kit";
|
||||
|
||||
/// Shared LDAP connection configuration.
|
||||
pub const CONNECTION_CONFIG: &str = "/etc/ldap-kit/connection.toml";
|
||||
|
||||
/// Password reset configuration.
|
||||
pub const PSWDRESET_CONFIG: &str = "/etc/ldap-kit/pswdreset.toml";
|
||||
|
||||
/// Default LDAP server URL.
|
||||
pub const DEFAULT_LDAP_URL: &str = "ldap://localhost:389";
|
||||
@ -1,14 +1,20 @@
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
//! Shared configuration library for ldap-kit.
|
||||
//!
|
||||
//! Loads and validates the configuration files used by the ldap-kit
|
||||
//! command line tools.
|
||||
//!
|
||||
//! Each configuration file has its own module. Additional modules can
|
||||
//! be added as new tools are implemented.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
pub mod connection;
|
||||
pub mod constants;
|
||||
pub mod pswdreset;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
// Re-export the public configuration types so callers can write:
|
||||
//
|
||||
// use config::{ConnectionConfig, PswdResetConfig};
|
||||
//
|
||||
// instead of importing each module separately.
|
||||
pub use connection::{BindCredentials, ConnectionConfig, LdapSettings};
|
||||
|
||||
pub use pswdreset::{AttributeSettings, PasswordMethod, PasswordSettings, PswdResetConfig};
|
||||
|
||||
142
crates/config/src/pswdreset.rs
Normal file
142
crates/config/src/pswdreset.rs
Normal file
@ -0,0 +1,142 @@
|
||||
//! Password reset configuration.
|
||||
//!
|
||||
//! Loads `/etc/ldap-kit/pswdreset.toml` and applies default values.
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
|
||||
use crate::constants::PSWDRESET_CONFIG;
|
||||
|
||||
/// Password reset configuration.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PswdResetConfig {
|
||||
pub attributes: AttributeSettings,
|
||||
pub password: PasswordSettings,
|
||||
}
|
||||
|
||||
/// LDAP attribute names used by this tool.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AttributeSettings {
|
||||
pub username: String,
|
||||
pub id_number: String,
|
||||
}
|
||||
|
||||
/// Password reset settings.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PasswordSettings {
|
||||
pub method: PasswordMethod,
|
||||
pub attribute: String,
|
||||
pub default_attribute: String,
|
||||
pub fallback_password: String,
|
||||
}
|
||||
|
||||
/// Password update method.
|
||||
#[derive(Debug, Clone, Copy, Default, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PasswordMethod {
|
||||
/// Try Password Modify first, then fall back to attribute replacement.
|
||||
#[default]
|
||||
Auto,
|
||||
|
||||
/// Always use the LDAP Password Modify extended operation.
|
||||
Passwd,
|
||||
|
||||
/// Replace the password attribute directly.
|
||||
Attribute,
|
||||
}
|
||||
|
||||
/// Raw TOML structure.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RawPswdResetConfig {
|
||||
#[serde(default)]
|
||||
attributes: RawAttributeSettings,
|
||||
|
||||
#[serde(default)]
|
||||
password: RawPasswordSettings,
|
||||
}
|
||||
|
||||
/// Raw attribute settings.
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
struct RawAttributeSettings {
|
||||
#[serde(default = "default_username_attribute")]
|
||||
username: String,
|
||||
|
||||
#[serde(default = "default_id_number_attribute")]
|
||||
id_number: String,
|
||||
}
|
||||
|
||||
/// Raw password settings.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RawPasswordSettings {
|
||||
#[serde(default)]
|
||||
method: PasswordMethod,
|
||||
|
||||
#[serde(default = "default_password_attribute")]
|
||||
attribute: String,
|
||||
|
||||
#[serde(default = "default_default_attribute")]
|
||||
default_attribute: String,
|
||||
|
||||
#[serde(default)]
|
||||
fallback_password: String,
|
||||
}
|
||||
|
||||
impl Default for RawPasswordSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
method: PasswordMethod::default(),
|
||||
attribute: default_password_attribute(),
|
||||
default_attribute: default_default_attribute(),
|
||||
fallback_password: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_username_attribute() -> String {
|
||||
"uid".to_owned()
|
||||
}
|
||||
|
||||
fn default_id_number_attribute() -> String {
|
||||
"uidNumber".to_owned()
|
||||
}
|
||||
|
||||
fn default_password_attribute() -> String {
|
||||
"userPassword".to_owned()
|
||||
}
|
||||
|
||||
fn default_default_attribute() -> String {
|
||||
"uid".to_owned()
|
||||
}
|
||||
|
||||
impl PswdResetConfig {
|
||||
/// Load the default password reset configuration.
|
||||
pub fn load() -> Result<Self> {
|
||||
Self::load_from(PSWDRESET_CONFIG)
|
||||
}
|
||||
|
||||
/// Load configuration from a specific TOML file.
|
||||
pub fn load_from(path: &str) -> Result<Self> {
|
||||
let contents = fs::read_to_string(path)?;
|
||||
|
||||
let raw: RawPswdResetConfig = toml::from_str(&contents)?;
|
||||
|
||||
Ok(Self::from_raw(raw))
|
||||
}
|
||||
|
||||
fn from_raw(raw: RawPswdResetConfig) -> Self {
|
||||
Self {
|
||||
attributes: AttributeSettings {
|
||||
username: raw.attributes.username,
|
||||
id_number: raw.attributes.id_number,
|
||||
},
|
||||
|
||||
password: PasswordSettings {
|
||||
method: raw.password.method,
|
||||
attribute: raw.password.attribute,
|
||||
default_attribute: raw.password.default_attribute,
|
||||
fallback_password: raw.password.fallback_password,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user