Massive refactor

This commit is contained in:
2025-03-16 05:18:42 +01:00
parent 0bbb2ca738
commit 5e8e8590f1
9 changed files with 755 additions and 1127 deletions

View File

@@ -1,49 +1,155 @@
use anyhow::{anyhow, Context};
use anyhow::Context;
use lldap_auth::opaque::AuthenticationError;
use lldap_auth::registration::ServerRegistrationStartResponse;
use lldap_auth::{opaque, registration};
use surf::Client;
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
use std::time::Duration;
use tracing::debug;
pub async fn change_password(client: &Client, user_id: &str, password: &str) -> anyhow::Result<()> {
let mut rng = rand::rngs::OsRng;
let registration_start_request =
opaque::client::registration::start_registration(password.as_bytes(), &mut rng)
.context("Could not initiate password change")?;
use cynic::http::{CynicReqwestError, ReqwestExt};
use cynic::{GraphQlError, GraphQlResponse, MutationBuilder, QueryBuilder};
use lldap_auth::login::{ClientSimpleLoginRequest, ServerLoginResponse};
use queries::{CreateUser, CreateUserVariables, ListUsers};
let start_request = registration::ClientRegistrationStartRequest {
username: user_id.into(),
registration_start_request: registration_start_request.message,
};
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("{0}")]
Cynic(#[from] CynicReqwestError),
#[error("{0}")]
Reqwest(#[from] reqwest::Error),
#[error("{0}")]
Authentication(#[from] AuthenticationError),
#[error("{0}")]
GraphQl(#[from] GraphQlError),
}
let mut response = client
.post("/auth/opaque/register/start")
.body_json(&start_request)
.map_err(|e| anyhow!(e))?
.await
.map_err(|e| anyhow!(e))?;
pub type Result<T, E = Error> = std::result::Result<T, E>;
let response: registration::ServerRegistrationStartResponse =
response.body_json().await.map_err(|e| anyhow!(e))?;
let registration_finish = opaque::client::registration::finish_registration(
registration_start_request.state,
response.registration_response,
&mut rng,
)
.context("Error during password change")?;
let request = registration::ClientRegistrationFinishRequest {
server_data: response.server_data,
registration_upload: registration_finish.message,
};
let _response = client
.post("/auth/opaque/register/finish")
.body_json(&request)
.map_err(|e| anyhow!(e))?
.await
.map_err(|e| anyhow!(e))?;
debug!("Changed '{user_id}' password successfully");
fn check_graphql_errors<T>(response: &GraphQlResponse<T>) -> Result<()> {
if let Some(errors) = &response.errors {
if !errors.is_empty() {
Err(errors.first().expect("Should not be empty").clone())?;
}
}
Ok(())
}
pub struct LldapConfig {
username: String,
password: String,
url: String,
}
impl LldapConfig {
pub fn try_from_env() -> anyhow::Result<Self> {
Ok(Self {
username: std::env::var("LLDAP_USERNAME")
.context("Variable 'LLDAP_USERNAME' is not set or invalid")?,
password: std::env::var("LLDAP_PASSWORD")
.context("Variable 'LLDAP_PASSWORD' is not set or invalid")?,
url: std::env::var("LLDAP_URL")
.context("Variable 'LLDAP_URL' is not set or invalid")?,
})
}
pub async fn build_client(&self) -> Result<LldapClient> {
let timeout = Duration::from_secs(1);
let client = reqwest::ClientBuilder::new().timeout(timeout).build()?;
let response: ServerLoginResponse = client
.post(format!("{}/auth/simple/login", self.url))
.json(&ClientSimpleLoginRequest {
username: self.username.clone().into(),
password: self.password.clone(),
})
.send()
.await?
.json()
.await?;
let mut auth: HeaderValue = format!("Bearer {}", response.token)
.try_into()
.expect("Token comes from api and should be ascii");
auth.set_sensitive(true);
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, auth);
let client = reqwest::ClientBuilder::new()
.timeout(timeout)
.default_headers(headers)
.build()?;
Ok(LldapClient(client))
}
}
pub struct LldapClient(reqwest::Client);
impl LldapClient {
pub async fn list_users(&self) -> Result<impl Iterator<Item = String>> {
let operation = ListUsers::build(());
let response = self.0.post("/api/graphql").run_graphql(operation).await?;
check_graphql_errors(&response)?;
Ok(response
.data
.expect("Data should be valid if there are no error")
.users
.into_iter()
.map(|user| user.id))
}
pub async fn create_user(&self, username: &str) -> Result<()> {
let operation = CreateUser::build(CreateUserVariables { id: username });
// TODO: Check the response?
let response = self.0.post("/api/graphql").run_graphql(operation).await?;
check_graphql_errors(&response)
}
pub async fn update_password(&self, username: &str, password: &str) -> Result<()> {
let mut rng = rand::rngs::OsRng;
let registration_start_request =
opaque::client::registration::start_registration(password.as_bytes(), &mut rng)?;
let start_request = registration::ClientRegistrationStartRequest {
username: username.into(),
registration_start_request: registration_start_request.message,
};
let response: ServerRegistrationStartResponse = self
.0
.post("/auth/opaque/register/start")
.json(&start_request)
.send()
.await?
.json()
.await?;
let registration_finish = opaque::client::registration::finish_registration(
registration_start_request.state,
response.registration_response,
&mut rng,
)?;
let request = registration::ClientRegistrationFinishRequest {
server_data: response.server_data,
registration_upload: registration_finish.message,
};
let _response = self
.0
.post("/auth/opaque/register/finish")
.json(&request)
.send()
.await?;
debug!("Changed '{username}' password successfully");
Ok(())
}
}