mirror of
https://github.com/nitnelave/lldap.git
synced 2023-04-12 14:25:13 +00:00
parent
a07f7ac389
commit
b4d7ada317
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -2456,6 +2456,19 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lldap_set_password"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"lldap_auth",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "local-channel"
|
name = "local-channel"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -3,7 +3,8 @@ members = [
|
|||||||
"server",
|
"server",
|
||||||
"auth",
|
"auth",
|
||||||
"app",
|
"app",
|
||||||
"migration-tool"
|
"migration-tool",
|
||||||
|
"set-password",
|
||||||
]
|
]
|
||||||
|
|
||||||
default-members = ["server"]
|
default-members = ["server"]
|
||||||
|
25
set-password/Cargo.toml
Normal file
25
set-password/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "lldap_set_password"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "*"
|
||||||
|
rand = "0.8"
|
||||||
|
serde = "1"
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
|
[dependencies.clap]
|
||||||
|
features = ["std", "color", "suggestions", "derive", "env"]
|
||||||
|
version = "4"
|
||||||
|
|
||||||
|
[dependencies.lldap_auth]
|
||||||
|
path = "../auth"
|
||||||
|
features = ["opaque_client"]
|
||||||
|
|
||||||
|
[dependencies.reqwest]
|
||||||
|
version = "*"
|
||||||
|
default-features = false
|
||||||
|
features = ["json", "blocking", "rustls-tls"]
|
133
set-password/src/main.rs
Normal file
133
set-password/src/main.rs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
use anyhow::{bail, ensure, Context, Result};
|
||||||
|
use clap::Parser;
|
||||||
|
use lldap_auth::{opaque, registration};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// Set the password for a user in LLDAP.
|
||||||
|
#[derive(Debug, Parser, Clone)]
|
||||||
|
pub struct CliOpts {
|
||||||
|
/// Base LLDAP url, e.g. "https://lldap/".
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub base_url: String,
|
||||||
|
|
||||||
|
/// Admin username.
|
||||||
|
#[clap(long, default_value = "admin")]
|
||||||
|
pub admin_username: String,
|
||||||
|
|
||||||
|
/// Admin password.
|
||||||
|
#[clap(long)]
|
||||||
|
pub admin_password: Option<String>,
|
||||||
|
|
||||||
|
/// Connection token (JWT).
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub token: Option<String>,
|
||||||
|
|
||||||
|
/// Username.
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub username: String,
|
||||||
|
|
||||||
|
/// New password for the user.
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_token(base_url: &str, username: &str, password: &str) -> Result<String> {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
let response = client
|
||||||
|
.post(format!("{base_url}/auth/simple/login"))
|
||||||
|
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
||||||
|
.body(
|
||||||
|
serde_json::to_string(&lldap_auth::login::ClientSimpleLoginRequest {
|
||||||
|
username: username.to_string(),
|
||||||
|
password: password.to_string(),
|
||||||
|
})
|
||||||
|
.expect("Failed to encode the username/password as json to log in"),
|
||||||
|
)
|
||||||
|
.send()?
|
||||||
|
.error_for_status()?;
|
||||||
|
Ok(serde_json::from_str::<lldap_auth::login::ServerLoginResponse>(&response.text()?)?.token)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_server(url: &str, token: &str, body: impl Serialize) -> Result<String> {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
let request = client
|
||||||
|
.post(url)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.bearer_auth(token)
|
||||||
|
.body(serde_json::to_string(&body)?);
|
||||||
|
let response = request.send()?.error_for_status()?;
|
||||||
|
Ok(response.text()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_start(
|
||||||
|
base_url: &str,
|
||||||
|
token: &str,
|
||||||
|
request: registration::ClientRegistrationStartRequest,
|
||||||
|
) -> Result<registration::ServerRegistrationStartResponse> {
|
||||||
|
let request = Some(request);
|
||||||
|
let data = call_server(
|
||||||
|
&format!("{base_url}/auth/opaque/register/start"),
|
||||||
|
token,
|
||||||
|
request,
|
||||||
|
)?;
|
||||||
|
serde_json::from_str(&data).context("Could not parse response")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_finish(
|
||||||
|
base_url: &str,
|
||||||
|
token: &str,
|
||||||
|
request: registration::ClientRegistrationFinishRequest,
|
||||||
|
) -> Result<()> {
|
||||||
|
let request = Some(request);
|
||||||
|
call_server(
|
||||||
|
&format!("{base_url}/auth/opaque/register/finish"),
|
||||||
|
token,
|
||||||
|
request,
|
||||||
|
)
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let opts = CliOpts::parse();
|
||||||
|
ensure!(
|
||||||
|
opts.password.len() >= 8,
|
||||||
|
"New password is too short, expected at least 8 characters"
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
opts.base_url.starts_with("http://") || opts.base_url.starts_with("https://"),
|
||||||
|
"Base URL should start with `http://` or `https://`"
|
||||||
|
);
|
||||||
|
let token = match (opts.token.as_ref(), opts.admin_password.as_ref()) {
|
||||||
|
(Some(token), _) => token.clone(),
|
||||||
|
(None, Some(password)) => {
|
||||||
|
get_token(&opts.base_url, &opts.admin_username, password).context("While logging in")?
|
||||||
|
}
|
||||||
|
(None, None) => bail!("Either the token or the admin password is required"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rng = rand::rngs::OsRng;
|
||||||
|
let registration_start_request =
|
||||||
|
opaque::client::registration::start_registration(&opts.password, &mut rng)
|
||||||
|
.context("Could not initiate password change")?;
|
||||||
|
let start_request = registration::ClientRegistrationStartRequest {
|
||||||
|
username: opts.username.to_string(),
|
||||||
|
registration_start_request: registration_start_request.message,
|
||||||
|
};
|
||||||
|
let res = register_start(&opts.base_url, &token, start_request)?;
|
||||||
|
|
||||||
|
let registration_finish = opaque::client::registration::finish_registration(
|
||||||
|
registration_start_request.state,
|
||||||
|
res.registration_response,
|
||||||
|
&mut rng,
|
||||||
|
)
|
||||||
|
.context("Error during password change")?;
|
||||||
|
let req = registration::ClientRegistrationFinishRequest {
|
||||||
|
server_data: res.server_data,
|
||||||
|
registration_upload: registration_finish.message,
|
||||||
|
};
|
||||||
|
|
||||||
|
register_finish(&opts.base_url, &token, req)?;
|
||||||
|
|
||||||
|
println!("Successfully changed {}'s password", &opts.username);
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user