Split lk-search/src/main.rs
- Module for arg-parsing - New crate for ldap client code - Introduce man-page generation
This commit is contained in:
parent
e7d5da4678
commit
76eb96a96f
@ -21,3 +21,4 @@ ldap3 = "0.12.1"
|
|||||||
serde = { version = "1.0.229", features = ["derive"] }
|
serde = { version = "1.0.229", features = ["derive"] }
|
||||||
tokio = { version = "1.53.1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.53.1", features = ["macros", "rt-multi-thread"] }
|
||||||
toml = "1.1.6"
|
toml = "1.1.6"
|
||||||
|
clap_mangen = "0.3.3"
|
||||||
@ -1 +1,39 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use ldap3::{LdapConn, Scope, SearchEntry};
|
||||||
|
|
||||||
|
use config::ConnectionConfig;
|
||||||
|
|
||||||
|
/// LDAP client.
|
||||||
|
pub struct LdapClient {
|
||||||
|
connection: LdapConn,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LdapClient {
|
||||||
|
/// Connect to the LDAP server using the read credentials.
|
||||||
|
pub fn connect(config: &ConnectionConfig) -> Result<Self> {
|
||||||
|
let mut connection = LdapConn::new(&config.ldap.url)?;
|
||||||
|
|
||||||
|
connection
|
||||||
|
.simple_bind(&config.read.bind_dn, &config.read.bind_pw)?
|
||||||
|
.success()?;
|
||||||
|
|
||||||
|
Ok(Self { connection })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search the LDAP directory.
|
||||||
|
///
|
||||||
|
/// Returns the matching entries as `ldap3::SearchEntry` values.
|
||||||
|
pub fn search(
|
||||||
|
&mut self,
|
||||||
|
base_dn: &str,
|
||||||
|
filter: &str,
|
||||||
|
attributes: Vec<&str>,
|
||||||
|
) -> Result<Vec<SearchEntry>> {
|
||||||
|
let (entries, _) = self
|
||||||
|
.connection
|
||||||
|
.search(base_dn, Scope::Subtree, filter, attributes)?
|
||||||
|
.success()?;
|
||||||
|
|
||||||
|
Ok(entries.into_iter().map(SearchEntry::construct).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
mod client;
|
mod client;
|
||||||
mod user;
|
|
||||||
|
|
||||||
//pub use client::LdapClient;
|
pub use client::LdapClient;
|
||||||
pub use user::User;
|
|
||||||
|
|||||||
@ -9,4 +9,9 @@ repository.workspace = true
|
|||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
config = { path = "../../crates/config" }
|
config = { path = "../../crates/config" }
|
||||||
|
ldap = { path = "../../crates/ldap" }
|
||||||
ldap3.workspace = true
|
ldap3.workspace = true
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
clap.workspace = true
|
||||||
|
clap_mangen.workspace = true
|
||||||
23
tools/lk-search/build.rs
Normal file
23
tools/lk-search/build.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::CommandFactory;
|
||||||
|
use clap_mangen::Man;
|
||||||
|
|
||||||
|
#[path = "src/args.rs"]
|
||||||
|
mod args;
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
|
||||||
|
let command = args::Args::command();
|
||||||
|
let man = Man::new(command);
|
||||||
|
|
||||||
|
let mut output = Vec::new();
|
||||||
|
man.render(&mut output)?;
|
||||||
|
|
||||||
|
fs::write(out_dir.join("lk-search.1"), output)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
18
tools/lk-search/src/args.rs
Normal file
18
tools/lk-search/src/args.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use clap::{ArgGroup, Parser};
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
#[command(name = "lk-search", about = "Search an LDAP directory")]
|
||||||
|
#[command(group(
|
||||||
|
ArgGroup::new("search")
|
||||||
|
.required(true)
|
||||||
|
.args(["username", "id"])
|
||||||
|
))]
|
||||||
|
pub struct Args {
|
||||||
|
/// Search by username.
|
||||||
|
#[arg(short = 'u', long = "username", value_name = "USERNAME")]
|
||||||
|
pub username: Option<String>,
|
||||||
|
|
||||||
|
/// Search by identifier.
|
||||||
|
#[arg(short = 'i', long = "id", value_name = "ID")]
|
||||||
|
pub id: Option<String>,
|
||||||
|
}
|
||||||
@ -1,49 +1,31 @@
|
|||||||
|
mod args;
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use clap::{ArgGroup, Parser};
|
use clap::Parser;
|
||||||
use ldap3::{LdapConn, Scope, SearchEntry};
|
|
||||||
|
|
||||||
use config::ConnectionConfig;
|
use config::ConnectionConfig;
|
||||||
|
use ldap::LdapClient;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
use args::Args;
|
||||||
#[command(name = "lk-search", about = "Search an LDAP user")]
|
|
||||||
#[command(group(
|
|
||||||
ArgGroup::new("user")
|
|
||||||
.required(true)
|
|
||||||
.args(["username", "id_number"])
|
|
||||||
))]
|
|
||||||
struct Args {
|
|
||||||
/// Search by username.
|
|
||||||
#[arg(short = 'u', long = "username", value_name = "USERNAME")]
|
|
||||||
username: Option<String>,
|
|
||||||
|
|
||||||
/// Search by ID number.
|
|
||||||
#[arg(short = 'n', long = "id-number", value_name = "ID")]
|
|
||||||
id_number: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let config = ConnectionConfig::load()?;
|
let config = ConnectionConfig::load()?;
|
||||||
|
|
||||||
let (search_attribute, search_value) = match (&args.username, &args.id_number) {
|
let (search_attribute, search_value) = match (&args.username, &args.id) {
|
||||||
(Some(username), None) => ("uid", username),
|
(Some(username), None) => ("uid", username),
|
||||||
(None, Some(id_number)) => ("uidNumber", id_number),
|
(None, Some(id)) => ("uidNumber", id),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let filter = format!("({search_attribute}={search_value})");
|
let filter = format!("({search_attribute}={search_value})");
|
||||||
|
|
||||||
let mut ldap = LdapConn::new(&config.ldap.url)?;
|
let mut ldap = LdapClient::connect(&config)?;
|
||||||
|
|
||||||
ldap.simple_bind(&config.read.bind_dn, &config.read.bind_pw)?
|
let entries = ldap.search(&config.ldap.base_dn, &filter, vec!["*"])?;
|
||||||
.success()?;
|
|
||||||
|
|
||||||
let (entries, _result) = ldap
|
|
||||||
.search(&config.ldap.base_dn, Scope::Subtree, &filter, vec!["*"])?
|
|
||||||
.success()?;
|
|
||||||
|
|
||||||
if entries.is_empty() {
|
if entries.is_empty() {
|
||||||
bail!("User not found.");
|
bail!("No matching entries found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if entries.len() > 1 {
|
if entries.len() > 1 {
|
||||||
@ -53,11 +35,11 @@ fn main() -> Result<()> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = SearchEntry::construct(entries.into_iter().next().unwrap());
|
let entry = &entries[0];
|
||||||
|
|
||||||
println!("DN: {}", entry.dn);
|
println!("DN: {}", entry.dn);
|
||||||
|
|
||||||
for (attribute, values) in entry.attrs {
|
for (attribute, values) in &entry.attrs {
|
||||||
println!("{attribute}:");
|
println!("{attribute}:");
|
||||||
|
|
||||||
for value in values {
|
for value in values {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user