diff --git a/Cargo.lock b/Cargo.lock index 1b5a3fe..72e6163 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2009,14 +2009,14 @@ dependencies = [ ] [[package]] -name = "ldap3_server" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7873d5bd5baabdb77aa2d8a762bf8b1136f9f90cc90f44639b4c0d4486281dcb" +name = "ldap3_proto" +version = "0.2.3" +source = "git+https://github.com/nitnelave/ldap3_server/?rev=7b50b2b82c383f5f70e02e11072bb916629ed2bc#7b50b2b82c383f5f70e02e11072bb916629ed2bc" dependencies = [ "bytes", "lber", - "tokio-util 0.6.10", + "tokio-util 0.7.3", + "tracing", ] [[package]] @@ -2120,7 +2120,7 @@ dependencies = [ "juniper", "juniper_actix", "jwt", - "ldap3_server", + "ldap3_proto", "lettre", "lldap_auth", "log", @@ -2144,7 +2144,7 @@ dependencies = [ "tokio", "tokio-rustls 0.23.4", "tokio-stream", - "tokio-util 0.6.10", + "tokio-util 0.7.3", "tracing", "tracing-actix-web", "tracing-attributes", diff --git a/Cargo.toml b/Cargo.toml index 92e3454..71c003e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,8 @@ members = [ ] default-members = ["server"] + +# Remove once https://github.com/kanidm/ldap3_proto/pull/8 is merged. +[patch.crates-io.ldap3_proto] +git = 'https://github.com/nitnelave/ldap3_server/' +rev = '7b50b2b82c383f5f70e02e11072bb916629ed2bc' diff --git a/server/Cargo.toml b/server/Cargo.toml index 81b104f..1ef8764 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -27,7 +27,7 @@ itertools = "0.10.1" juniper = "0.15.10" juniper_actix = "0.4.0" jwt = "0.13" -ldap3_server = "=0.1.11" +ldap3_proto = "*" log = "*" orion = "0.16" rustls = "0.20" @@ -39,7 +39,7 @@ thiserror = "*" time = "0.2" tokio-rustls = "0.23" tokio-stream = "*" -tokio-util = "0.6.3" +tokio-util = "0.7.3" tracing = "*" tracing-actix-web = "0.4.0-beta.7" tracing-attributes = "^0.1.21" @@ -104,7 +104,7 @@ version = "*" [dependencies.tokio] features = ["full"] -version = "1.13.1" +version = "1.17" [dependencies.uuid] features = ["v3"] diff --git a/server/src/domain/handler.rs b/server/src/domain/handler.rs index 8be3176..65f07fa 100644 --- a/server/src/domain/handler.rs +++ b/server/src/domain/handler.rs @@ -126,6 +126,11 @@ impl From<&JpegPhoto> for String { } impl JpegPhoto { + pub fn into_bytes(self) -> Vec { + self.0 + } + + #[cfg(test)] pub fn for_tests() -> Self { use image::{ImageOutputFormat, Rgb, RgbImage}; let img = RgbImage::from_fn(32, 32, |x, y| { diff --git a/server/src/infra/ldap_handler.rs b/server/src/infra/ldap_handler.rs index 30b1b09..d50f59b 100644 --- a/server/src/infra/ldap_handler.rs +++ b/server/src/infra/ldap_handler.rs @@ -10,7 +10,7 @@ use crate::{ }; use anyhow::{bail, Context, Result}; use itertools::Itertools; -use ldap3_server::proto::{ +use ldap3_proto::proto::{ LdapBindCred, LdapBindRequest, LdapBindResponse, LdapExtendedRequest, LdapExtendedResponse, LdapFilter, LdapOp, LdapPartialAttribute, LdapPasswordModifyRequest, LdapResult, LdapResultCode, LdapSearchRequest, LdapSearchResultEntry, LdapSearchScope, @@ -154,22 +154,22 @@ fn get_user_attribute( base_dn_str: &str, groups: Option<&[GroupDetails]>, ignored_user_attributes: &[String], -) -> Result>> { +) -> Result>>> { let attribute = attribute.to_ascii_lowercase(); Ok(Some(match attribute.as_str() { "objectclass" => vec![ - "inetOrgPerson".to_string(), - "posixAccount".to_string(), - "mailAccount".to_string(), - "person".to_string(), + b"inetOrgPerson".to_vec(), + b"posixAccount".to_vec(), + b"mailAccount".to_vec(), + b"person".to_vec(), ], // dn is always returned as part of the base response. "dn" | "distinguishedname" => return Ok(None), - "uid" => vec![user.user_id.to_string()], - "entryuuid" => vec![user.uuid.to_string()], - "mail" => vec![user.email.clone()], - "givenname" => vec![user.first_name.clone()], - "sn" => vec![user.last_name.clone()], + "uid" => vec![user.user_id.to_string().into_bytes()], + "entryuuid" => vec![user.uuid.to_string().into_bytes()], + "mail" => vec![user.email.clone().into_bytes()], + "givenname" => vec![user.first_name.clone().into_bytes()], + "sn" => vec![user.last_name.clone().into_bytes()], "memberof" => groups .into_iter() .flatten() @@ -178,10 +178,11 @@ fn get_user_attribute( "uid={},ou=groups,{}", &id_and_name.display_name, base_dn_str ) + .into_bytes() }) .collect(), - "cn" | "displayname" => vec![user.display_name.clone()], - "createtimestamp" | "modifytimestamp" => vec![user.creation_date.to_rfc3339()], + "cn" | "displayname" => vec![user.display_name.clone().into_bytes()], + "createtimestamp" | "modifytimestamp" => vec![user.creation_date.to_rfc3339().into_bytes()], "1.1" => return Ok(None), // We ignore the operational attribute wildcard. "+" => return Ok(None), @@ -279,19 +280,19 @@ fn get_group_attribute( attribute: &str, user_filter: &Option<&UserId>, ignored_group_attributes: &[String], -) -> Result>> { +) -> Result>>> { let attribute = attribute.to_ascii_lowercase(); Ok(Some(match attribute.as_str() { - "objectclass" => vec!["groupOfUniqueNames".to_string()], + "objectclass" => vec![b"groupOfUniqueNames".to_vec()], // Always returned as part of the base response. "dn" | "distinguishedname" => return Ok(None), - "cn" | "uid" => vec![group.display_name.clone()], - "entryuuid" => vec![group.uuid.to_string()], + "cn" | "uid" => vec![group.display_name.clone().into_bytes()], + "entryuuid" => vec![group.uuid.to_string().into_bytes()], "member" | "uniquemember" => group .users .iter() .filter(|u| user_filter.map(|f| *u == f).unwrap_or(true)) - .map(|u| format!("uid={},ou=people,{}", u, base_dn_str)) + .map(|u| format!("uid={},ou=people,{}", u, base_dn_str).into_bytes()) .collect(), "1.1" => return Ok(None), // We ignore the operational attribute wildcard @@ -418,27 +419,27 @@ fn root_dse_response(base_dn: &str) -> LdapOp { attributes: vec![ LdapPartialAttribute { atype: "objectClass".to_string(), - vals: vec!["top".to_string()], + vals: vec![b"top".to_vec()], }, LdapPartialAttribute { atype: "vendorName".to_string(), - vals: vec!["LLDAP".to_string()], + vals: vec![b"LLDAP".to_vec()], }, LdapPartialAttribute { atype: "vendorVersion".to_string(), - vals: vec!["lldap_0.2.0".to_string()], + vals: vec![b"lldap_0.2.0".to_vec()], }, LdapPartialAttribute { atype: "supportedLDAPVersion".to_string(), - vals: vec!["3".to_string()], + vals: vec![b"3".to_vec()], }, LdapPartialAttribute { atype: "supportedExtension".to_string(), - vals: vec!["1.3.6.1.4.1.4203.1.11.1".to_string()], + vals: vec![b"1.3.6.1.4.1.4203.1.11.1".to_vec()], }, LdapPartialAttribute { atype: "defaultnamingcontext".to_string(), - vals: vec![base_dn.to_string()], + vals: vec![base_dn.to_string().into_bytes()], }, ], }) @@ -1032,7 +1033,7 @@ mod tests { }; use async_trait::async_trait; use chrono::TimeZone; - use ldap3_server::proto::{LdapDerefAliases, LdapSearchScope}; + use ldap3_proto::proto::{LdapDerefAliases, LdapSearchScope}; use mockall::predicate::eq; use std::collections::HashSet; use tokio; @@ -1314,7 +1315,7 @@ mod tests { dn: "uid=bob,ou=people,dc=example,dc=com".to_string(), attributes: vec![LdapPartialAttribute { atype: "memberOf".to_string(), - vals: vec!["uid=rockstars,ou=groups,dc=example,dc=com".to_string()] + vals: vec![b"uid=rockstars,ou=groups,dc=example,dc=com".to_vec()] }], }), make_search_success(), @@ -1491,39 +1492,39 @@ mod tests { LdapPartialAttribute { atype: "objectClass".to_string(), vals: vec![ - "inetOrgPerson".to_string(), - "posixAccount".to_string(), - "mailAccount".to_string(), - "person".to_string() + b"inetOrgPerson".to_vec(), + b"posixAccount".to_vec(), + b"mailAccount".to_vec(), + b"person".to_vec() ] }, LdapPartialAttribute { atype: "uid".to_string(), - vals: vec!["bob_1".to_string()] + vals: vec![b"bob_1".to_vec()] }, LdapPartialAttribute { atype: "mail".to_string(), - vals: vec!["bob@bobmail.bob".to_string()] + vals: vec![b"bob@bobmail.bob".to_vec()] }, LdapPartialAttribute { atype: "givenName".to_string(), - vals: vec!["Bôb".to_string()] + vals: vec!["Bôb".to_string().into_bytes()] }, LdapPartialAttribute { atype: "sn".to_string(), - vals: vec!["Böbberson".to_string()] + vals: vec!["Böbberson".to_string().into_bytes()] }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["Bôb Böbberson".to_string()] + vals: vec!["Bôb Böbberson".to_string().into_bytes()] }, LdapPartialAttribute { atype: "createTimestamp".to_string(), - vals: vec!["1970-01-01T00:00:00+00:00".to_string()] + vals: vec![b"1970-01-01T00:00:00+00:00".to_vec()] }, LdapPartialAttribute { atype: "entryUuid".to_string(), - vals: vec!["698e1d5f-7a40-3151-8745-b9b8a37839da".to_string()] + vals: vec![b"698e1d5f-7a40-3151-8745-b9b8a37839da".to_vec()] }, ], }), @@ -1533,39 +1534,39 @@ mod tests { LdapPartialAttribute { atype: "objectClass".to_string(), vals: vec![ - "inetOrgPerson".to_string(), - "posixAccount".to_string(), - "mailAccount".to_string(), - "person".to_string() + b"inetOrgPerson".to_vec(), + b"posixAccount".to_vec(), + b"mailAccount".to_vec(), + b"person".to_vec() ] }, LdapPartialAttribute { atype: "uid".to_string(), - vals: vec!["jim".to_string()] + vals: vec![b"jim".to_vec()] }, LdapPartialAttribute { atype: "mail".to_string(), - vals: vec!["jim@cricket.jim".to_string()] + vals: vec![b"jim@cricket.jim".to_vec()] }, LdapPartialAttribute { atype: "givenName".to_string(), - vals: vec!["Jim".to_string()] + vals: vec![b"Jim".to_vec()] }, LdapPartialAttribute { atype: "sn".to_string(), - vals: vec!["Cricket".to_string()] + vals: vec![b"Cricket".to_vec()] }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["Jimminy Cricket".to_string()] + vals: vec![b"Jimminy Cricket".to_vec()] }, LdapPartialAttribute { atype: "createTimestamp".to_string(), - vals: vec!["2014-07-08T09:10:11+00:00".to_string()] + vals: vec![b"2014-07-08T09:10:11+00:00".to_vec()] }, LdapPartialAttribute { atype: "entryUuid".to_string(), - vals: vec!["04ac75e0-2900-3e21-926c-2f732c26b3fc".to_string()] + vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()] }, ], }), @@ -1612,22 +1613,22 @@ mod tests { attributes: vec![ LdapPartialAttribute { atype: "objectClass".to_string(), - vals: vec!["groupOfUniqueNames".to_string(),] + vals: vec![b"groupOfUniqueNames".to_vec(),] }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["group_1".to_string()] + vals: vec![b"group_1".to_vec()] }, LdapPartialAttribute { atype: "uniqueMember".to_string(), vals: vec![ - "uid=bob,ou=people,dc=example,dc=com".to_string(), - "uid=john,ou=people,dc=example,dc=com".to_string(), + b"uid=bob,ou=people,dc=example,dc=com".to_vec(), + b"uid=john,ou=people,dc=example,dc=com".to_vec(), ] }, LdapPartialAttribute { atype: "entryUuid".to_string(), - vals: vec!["04ac75e0-2900-3e21-926c-2f732c26b3fc".to_string()], + vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()], }, ], }), @@ -1636,19 +1637,19 @@ mod tests { attributes: vec![ LdapPartialAttribute { atype: "objectClass".to_string(), - vals: vec!["groupOfUniqueNames".to_string(),] + vals: vec![b"groupOfUniqueNames".to_vec(),] }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["BestGroup".to_string()] + vals: vec![b"BestGroup".to_vec()] }, LdapPartialAttribute { atype: "uniqueMember".to_string(), - vals: vec!["uid=john,ou=people,dc=example,dc=com".to_string()] + vals: vec![b"uid=john,ou=people,dc=example,dc=com".to_vec()] }, LdapPartialAttribute { atype: "entryUuid".to_string(), - vals: vec!["04ac75e0-2900-3e21-926c-2f732c26b3fc".to_string()], + vals: vec![b"04ac75e0-2900-3e21-926c-2f732c26b3fc".to_vec()], }, ], }), @@ -1750,7 +1751,7 @@ mod tests { dn: "cn=group_1,ou=groups,dc=example,dc=com".to_string(), attributes: vec![LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["group_1".to_string()] + vals: vec![b"group_1".to_vec()] },], }), make_search_success(), @@ -1826,7 +1827,7 @@ mod tests { "ou=groups,dc=example,dc=com", LdapFilter::And(vec![LdapFilter::Substring( "whatever".to_string(), - ldap3_server::proto::LdapSubstringFilter::default(), + ldap3_proto::proto::LdapSubstringFilter::default(), )]), vec!["cn"], ); @@ -1970,10 +1971,10 @@ mod tests { attributes: vec![LdapPartialAttribute { atype: "objectclass".to_string(), vals: vec![ - "inetOrgPerson".to_string(), - "posixAccount".to_string(), - "mailAccount".to_string(), - "person".to_string() + b"inetOrgPerson".to_vec(), + b"posixAccount".to_vec(), + b"mailAccount".to_vec(), + b"person".to_vec() ] },] }), @@ -2025,15 +2026,15 @@ mod tests { LdapPartialAttribute { atype: "objectClass".to_string(), vals: vec![ - "inetOrgPerson".to_string(), - "posixAccount".to_string(), - "mailAccount".to_string(), - "person".to_string() + b"inetOrgPerson".to_vec(), + b"posixAccount".to_vec(), + b"mailAccount".to_vec(), + b"person".to_vec() ] }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["Bôb Böbberson".to_string()] + vals: vec!["Bôb Böbberson".to_string().into_bytes()] }, ], }), @@ -2042,11 +2043,11 @@ mod tests { attributes: vec![ LdapPartialAttribute { atype: "objectClass".to_string(), - vals: vec!["groupOfUniqueNames".to_string(),] + vals: vec![b"groupOfUniqueNames".to_vec(),] }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["group_1".to_string()] + vals: vec![b"group_1".to_vec()] }, ], }), @@ -2098,35 +2099,35 @@ mod tests { LdapPartialAttribute { atype: "objectclass".to_string(), vals: vec![ - "inetOrgPerson".to_string(), - "posixAccount".to_string(), - "mailAccount".to_string(), - "person".to_string(), + b"inetOrgPerson".to_vec(), + b"posixAccount".to_vec(), + b"mailAccount".to_vec(), + b"person".to_vec(), ], }, LdapPartialAttribute { atype: "uid".to_string(), - vals: vec!["bob_1".to_string()], + vals: vec![b"bob_1".to_vec()], }, LdapPartialAttribute { atype: "mail".to_string(), - vals: vec!["bob@bobmail.bob".to_string()], + vals: vec![b"bob@bobmail.bob".to_vec()], }, LdapPartialAttribute { atype: "givenname".to_string(), - vals: vec!["Bôb".to_string()], + vals: vec!["Bôb".to_string().into_bytes()], }, LdapPartialAttribute { atype: "sn".to_string(), - vals: vec!["Böbberson".to_string()], + vals: vec!["Böbberson".to_string().into_bytes()], }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["Bôb Böbberson".to_string()], + vals: vec!["Bôb Böbberson".to_string().into_bytes()], }, LdapPartialAttribute { atype: "createtimestamp".to_string(), - vals: vec![chrono::Utc.timestamp(0, 0).to_rfc3339()], + vals: vec![chrono::Utc.timestamp(0, 0).to_rfc3339().into_bytes()], }, ], }), @@ -2136,30 +2137,30 @@ mod tests { attributes: vec![ LdapPartialAttribute { atype: "objectclass".to_string(), - vals: vec!["groupOfUniqueNames".to_string()], + vals: vec![b"groupOfUniqueNames".to_vec()], }, // UID LdapPartialAttribute { atype: "uid".to_string(), - vals: vec!["group_1".to_string()], + vals: vec![b"group_1".to_vec()], }, LdapPartialAttribute { atype: "cn".to_string(), - vals: vec!["group_1".to_string()], + vals: vec![b"group_1".to_vec()], }, //member / uniquemember : "uid={},ou=people,{}" LdapPartialAttribute { atype: "member".to_string(), vals: vec![ - "uid=bob,ou=people,dc=example,dc=com".to_string(), - "uid=john,ou=people,dc=example,dc=com".to_string(), + b"uid=bob,ou=people,dc=example,dc=com".to_vec(), + b"uid=john,ou=people,dc=example,dc=com".to_vec(), ], }, LdapPartialAttribute { atype: "uniquemember".to_string(), vals: vec![ - "uid=bob,ou=people,dc=example,dc=com".to_string(), - "uid=john,ou=people,dc=example,dc=com".to_string(), + b"uid=bob,ou=people,dc=example,dc=com".to_vec(), + b"uid=john,ou=people,dc=example,dc=com".to_vec(), ], }, ], @@ -2234,7 +2235,7 @@ mod tests { let request = make_user_search_request( LdapFilter::Substring( "uid".to_string(), - ldap3_server::proto::LdapSubstringFilter::default(), + ldap3_proto::proto::LdapSubstringFilter::default(), ), vec!["objectClass"], ); diff --git a/server/src/infra/ldap_server.rs b/server/src/infra/ldap_server.rs index 9a1c55c..613fe2b 100644 --- a/server/src/infra/ldap_server.rs +++ b/server/src/infra/ldap_server.rs @@ -9,7 +9,7 @@ use actix_rt::net::TcpStream; use actix_server::ServerBuilder; use actix_service::{fn_service, ServiceFactoryExt}; use anyhow::{Context, Result}; -use ldap3_server::{proto::LdapMsg, LdapCodec}; +use ldap3_proto::{proto::LdapMsg, LdapCodec}; use tokio_rustls::TlsAcceptor as RustlsTlsAcceptor; use tokio_util::codec::{FramedRead, FramedWrite}; use tracing::{debug, error, info, instrument};