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
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[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 {
|
//! Shared configuration library for ldap-kit.
|
||||||
left + right
|
//!
|
||||||
}
|
//! 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)]
|
pub mod connection;
|
||||||
mod tests {
|
pub mod constants;
|
||||||
use super::*;
|
pub mod pswdreset;
|
||||||
|
|
||||||
#[test]
|
// Re-export the public configuration types so callers can write:
|
||||||
fn it_works() {
|
//
|
||||||
let result = add(2, 2);
|
// use config::{ConnectionConfig, PswdResetConfig};
|
||||||
assert_eq!(result, 4);
|
//
|
||||||
}
|
// 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