Password reset config lib

- First part to the config lib
- LDAP connection module
- Constants
This commit is contained in:
Candifloss 2026-07-14 16:41:25 +05:30
parent f64fecd6c4
commit 8a2b9d0952
5 changed files with 305 additions and 12 deletions

View File

@ -6,3 +6,6 @@ license.workspace = true
repository.workspace = true
[dependencies]
anyhow.workspace = true
serde = { workspace = true, features = ["derive"] }
toml.workspace = true

View 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,
})
}
}

View 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";

View File

@ -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};

View 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,
},
}
}
}