JMAP Registry API implementation - part 6

This commit is contained in:
mdecimus
2026-03-01 18:30:04 +01:00
parent 07d5748f6b
commit 9ccc2ed6f0
74 changed files with 2417 additions and 785 deletions

View File

@@ -11,13 +11,18 @@ use ldap3::LdapConnSettings;
use registry::schema::structs;
impl LdapDirectory {
pub fn open(config: structs::LdapDirectory) -> Result<Directory, String> {
pub async fn open(config: structs::LdapDirectory) -> Result<Directory, String> {
let bind_dn = if let Some(dn) = config.bind_dn {
Bind::new(
dn,
config.bind_secret.ok_or_else(|| {
"LDAP bind password is required when bind DN is set".to_string()
})?,
config
.bind_secret
.secret()
.await?
.map(|v| v.into_owned())
.ok_or_else(|| {
"LDAP bind password is required when bind DN is set".to_string()
})?,
)
.into()
} else {

View File

@@ -9,7 +9,7 @@ use crate::Directory;
use registry::schema::structs;
impl OpenIdDirectory {
pub fn open(config: structs::OidcDirectory) -> Result<Directory, String> {
pub async fn open(config: structs::OidcDirectory) -> Result<Directory, String> {
Ok(Directory::OpenId(match config {
structs::OidcDirectory::UserInfo(config) => OpenIdDirectory::UserInfo {
endpoint: config.endpoint,
@@ -19,12 +19,15 @@ impl OpenIdDirectory {
claim_name: config.claim_name,
},
structs::OidcDirectory::Introspect(config) => {
let client = config.http_auth.build_http_client(
config.http_headers,
None,
config.timeout,
config.allow_invalid_certs,
)?;
let client = config
.http_auth
.build_http_client(
config.http_headers,
None,
config.timeout,
config.allow_invalid_certs,
)
.await?;
OpenIdDirectory::Introspect {
client,
endpoint: config.endpoint,

View File

@@ -22,11 +22,11 @@ impl Directories {
for directory in bp.list_infallible::<structs::Directory>().await {
let id = directory.id;
let result = match directory.object {
structs::Directory::Ldap(directory) => LdapDirectory::open(directory),
structs::Directory::Ldap(directory) => LdapDirectory::open(directory).await,
structs::Directory::Sql(directory) => {
SqlDirectory::open(directory, &bp.data_store).await
}
structs::Directory::Oidc(directory) => OpenIdDirectory::open(directory),
structs::Directory::Oidc(directory) => OpenIdDirectory::open(directory).await,
};
match result {

View File

@@ -5,11 +5,15 @@
*/
use argon2::Argon2;
use argon2::PasswordHasher;
use mail_builder::encoders::base64::base64_encode;
use mail_parser::decoders::base64::base64_decode;
use password_hash::PasswordHash;
use password_hash::SaltString;
use password_hash::rand_core::OsRng;
use pbkdf2::Pbkdf2;
use pwhash::{bcrypt, bsdi_crypt, md5_crypt, sha1_crypt, sha256_crypt, sha512_crypt, unix_crypt};
use registry::schema::enums::PasswordHashAlgorithm;
use scrypt::Scrypt;
use sha1::Digest;
use sha1::Sha1;
@@ -219,3 +223,50 @@ pub async fn verify_secret_hash(hashed_secret: &str, secret: &[u8]) -> trc::Resu
Ok(false)
}
}
pub async fn hash_secret(algorithm: PasswordHashAlgorithm, secret: String) -> trc::Result<String> {
let (tx, rx) = oneshot::channel();
tokio::task::spawn_blocking(move || {
let salt = SaltString::generate(&mut OsRng);
let result = match algorithm {
PasswordHashAlgorithm::Argon2id => {
let hasher = Argon2::default();
hasher
.hash_password(secret.as_bytes(), &salt)
.map(|h| h.to_string())
}
PasswordHashAlgorithm::Bcrypt => {
return tx
.send(bcrypt::hash(secret.as_bytes()).map_err(|err| {
trc::AuthEvent::Error
.reason(err)
.details("Bcrypt hash failed")
}))
.ok()
.unwrap_or(());
}
PasswordHashAlgorithm::Scrypt => Scrypt
.hash_password(secret.as_bytes(), &salt)
.map(|h| h.to_string()),
PasswordHashAlgorithm::Pbkdf2 => Pbkdf2
.hash_password(secret.as_bytes(), &salt)
.map(|h| h.to_string()),
};
tx.send(result.map_err(|err| {
trc::AuthEvent::Error
.reason(err)
.details("Password hash failed")
}))
.ok();
});
match rx.await {
Ok(result) => result,
Err(err) => Err(trc::EventType::Server(trc::ServerEvent::ThreadError)
.caused_by(trc::location!())
.reason(err)),
}
}