diff --git a/kanidmd/lib/src/constants/uuids.rs b/kanidmd/lib/src/constants/uuids.rs index 8845cf389..b9f03efe4 100644 --- a/kanidmd/lib/src/constants/uuids.rs +++ b/kanidmd/lib/src/constants/uuids.rs @@ -220,6 +220,9 @@ pub const UUID_SCHEMA_CLASS_SYNC_OBJECT: Uuid = uuid!("00000000-0000-0000-0000-f pub const UUID_SCHEMA_ATTR_SYNC_CLASS: Uuid = uuid!("00000000-0000-0000-0000-ffff00000124"); pub const UUID_SCHEMA_ATTR_SYNC_ALLOWED: Uuid = uuid!("00000000-0000-0000-0000-ffff00000125"); +pub const UUID_SCHEMA_ATTR_EMAILPRIMARY: Uuid = uuid!("00000000-0000-0000-0000-ffff00000126"); +pub const UUID_SCHEMA_ATTR_EMAILALTERNATIVE: Uuid = uuid!("00000000-0000-0000-0000-ffff00000127"); + // System and domain infos // I'd like to strongly criticise william of the past for making poor choices about these allocations. pub const UUID_SYSTEM_INFO: Uuid = uuid!("00000000-0000-0000-0000-ffffff000001"); diff --git a/kanidmd/lib/src/entry.rs b/kanidmd/lib/src/entry.rs index b82cc4be9..56dd54b38 100644 --- a/kanidmd/lib/src/entry.rs +++ b/kanidmd/lib/src/entry.rs @@ -1795,6 +1795,24 @@ impl Entry { atype: "entrydn".to_string(), vals: vec![dn.as_bytes().to_vec()], }), + "mail;primary" | "emailprimary" => { + attr_map.get(kani_a).map(|pvs| LdapPartialAttribute { + atype: ldap_a.to_string(), + vals: pvs + .first() + .map(|first| vec![first.clone()]) + .unwrap_or_default(), + }) + } + "mail;alternative" | "emailalternative" => { + attr_map.get(kani_a).map(|pvs| LdapPartialAttribute { + atype: ldap_a.to_string(), + vals: pvs + .split_first() + .map(|(_, rest)| rest.to_vec()) + .unwrap_or_default(), + }) + } _ => attr_map.get(kani_a).map(|pvs| LdapPartialAttribute { atype: ldap_a.to_string(), vals: pvs.clone(), diff --git a/kanidmd/lib/src/ldap.rs b/kanidmd/lib/src/ldap.rs index 539bb7262..60af83bc5 100644 --- a/kanidmd/lib/src/ldap.rs +++ b/kanidmd/lib/src/ldap.rs @@ -526,14 +526,18 @@ fn operationerr_to_ldapresultcode(e: OperationError) -> (LdapResultCode, String) #[inline] pub(crate) fn ldap_all_vattrs() -> Vec { vec![ - "entryuuid".to_string(), - "objectclass".to_string(), - "entrydn".to_string(), + "cn".to_string(), "email".to_string(), "emailaddress".to_string(), + "emailalternative".to_string(), + "emailprimary".to_string(), + "entrydn".to_string(), + "entryuuid".to_string(), "keys".to_string(), + "mail;alternative".to_string(), + "mail;primary".to_string(), + "objectclass".to_string(), "sshpublickey".to_string(), - "cn".to_string(), "uidnumber".to_string(), ] } @@ -547,13 +551,17 @@ pub(crate) fn ldap_vattr_map(input: &str) -> &str { // // LDAP NAME KANI ATTR SOURCE NAME match input { - "entryuuid" => "uuid", - "objectclass" => "class", + "cn" => "name", "email" => "mail", "emailaddress" => "mail", + "emailalternative" => "mail", + "emailprimary" => "mail", + "entryuuid" => "uuid", "keys" => "ssh_publickey", + "mail;alternative" => "mail", + "mail;primary" => "mail", + "objectclass" => "class", "sshpublickey" => "ssh_publickey", - "cn" => "name", "uidnumber" => "gidnumber", a => a, } @@ -912,7 +920,7 @@ mod tests { base: "dc=example,dc=com".to_string(), scope: LdapSearchScope::Subtree, filter: LdapFilter::Equality("name".to_string(), "testperson1".to_string()), - attrs: vec!["name".to_string(), "mail".to_string()], + attrs: vec!["name".to_string(), "mail".to_string(), "mail;primary".to_string(), "mail;alternative".to_string(), "emailprimary".to_string(), "emailalternative".to_string()], }; let sa_uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"); @@ -942,6 +950,13 @@ mod tests { "mail", Value::EmailAddress("testperson1@example.com".to_string(), true) ), + ( + "mail", + Value::EmailAddress( + "testperson1.alternative@example.com".to_string(), + false + ) + ), ("description", Value::new_utf8s("testperson1")), ("displayname", Value::new_utf8s("testperson1")), ("gidnumber", Value::new_uint32(12345678)), @@ -1038,7 +1053,12 @@ mod tests { lsre, "spn=testperson1@example.com,dc=example,dc=com", ("name", "testperson1"), - ("mail", "testperson1@example.com") + ("mail", "testperson1@example.com"), + ("mail", "testperson1.alternative@example.com"), + ("mail;primary", "testperson1@example.com"), + ("mail;alternative", "testperson1.alternative@example.com"), + ("emailprimary", "testperson1@example.com"), + ("emailalternative", "testperson1.alternative@example.com") ); } _ => assert!(false), diff --git a/kanidmd/lib/src/schema.rs b/kanidmd/lib/src/schema.rs index 052c6c1ec..bcdfc68e5 100644 --- a/kanidmd/lib/src/schema.rs +++ b/kanidmd/lib/src/schema.rs @@ -1430,6 +1430,34 @@ impl<'a> SchemaWriteTransaction<'a> { syntax: SyntaxType::EmailAddress, }, ); + self.attributes.insert( + AttrString::from("emailprimary"), + SchemaAttribute { + name: AttrString::from("emailprimary"), + uuid: UUID_SCHEMA_ATTR_EMAILPRIMARY, + description: String::from("An LDAP Compatible primary email"), + multivalue: false, + unique: false, + phantom: true, + sync_allowed: false, + index: vec![], + syntax: SyntaxType::EmailAddress, + }, + ); + self.attributes.insert( + AttrString::from("emailalternative"), + SchemaAttribute { + name: AttrString::from("emailalternative"), + uuid: UUID_SCHEMA_ATTR_EMAILALTERNATIVE, + description: String::from("An LDAP Compatible alternative email"), + multivalue: false, + unique: false, + phantom: true, + sync_allowed: false, + index: vec![], + syntax: SyntaxType::EmailAddress, + }, + ); self.attributes.insert( AttrString::from("emailaddress"), SchemaAttribute { diff --git a/kanidmd/lib/src/valueset/address.rs b/kanidmd/lib/src/valueset/address.rs index 74c01c37d..67ecf5eb2 100644 --- a/kanidmd/lib/src/valueset/address.rs +++ b/kanidmd/lib/src/valueset/address.rs @@ -319,7 +319,18 @@ impl ValueSetT for ValueSetEmailAddress { } fn to_proto_string_clone_iter(&self) -> Box + '_> { - Box::new(self.set.iter().cloned()) + if self.primary.is_empty() { + Box::new(self.set.iter().cloned()) + } else { + Box::new( + std::iter::once(self.primary.clone()).chain( + self.set + .iter() + .filter(|mail| **mail != self.primary) + .cloned(), + ), + ) + } } fn to_db_valueset_v2(&self) -> DbValueSetV2 {