kanidmd_lib/plugins/
domain.rs1use std::iter::once;
8use std::sync::Arc;
9
10use regex::Regex;
11use tracing::trace;
12
13use crate::event::{CreateEvent, ModifyEvent};
14use crate::plugins::Plugin;
15use crate::prelude::*;
16
17lazy_static! {
18 pub static ref DOMAIN_LDAP_BASEDN_RE: Regex = {
19 #[allow(clippy::expect_used)]
20 Regex::new(r"^(dc|o|ou)=[a-z][a-z0-9]*(,(dc|o|ou)=[a-z][a-z0-9]*)*$")
21 .expect("Invalid domain ldap basedn regex")
22 };
23}
24
25pub struct Domain {}
26
27impl Plugin for Domain {
28 fn id() -> &'static str {
29 "plugin_domain"
30 }
31
32 #[instrument(level = "debug", name = "domain_pre_create_transform", skip_all)]
33 fn pre_create_transform(
34 qs: &mut QueryServerWriteTransaction,
35 cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
36 _ce: &CreateEvent,
37 ) -> Result<(), OperationError> {
38 Self::modify_inner(qs, cand)
39 }
40
41 #[instrument(level = "debug", name = "domain_pre_modify", skip_all)]
42 fn pre_modify(
43 qs: &mut QueryServerWriteTransaction,
44 _pre_cand: &[Arc<EntrySealedCommitted>],
45 cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
46 _me: &ModifyEvent,
47 ) -> Result<(), OperationError> {
48 Self::modify_inner(qs, cand)
49 }
50
51 #[instrument(level = "debug", name = "domain_pre_batch_modify", skip_all)]
52 fn pre_batch_modify(
53 qs: &mut QueryServerWriteTransaction,
54 _pre_cand: &[Arc<EntrySealedCommitted>],
55 cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
56 _me: &BatchModifyEvent,
57 ) -> Result<(), OperationError> {
58 Self::modify_inner(qs, cand)
59 }
60}
61
62impl Domain {
63 fn modify_inner<T: Clone + std::fmt::Debug>(
65 qs: &mut QueryServerWriteTransaction,
66 cand: &mut [Entry<EntryInvalid, T>],
67 ) -> Result<(), OperationError> {
68 cand.iter_mut().try_for_each(|e| {
69 if e.attribute_equality(Attribute::Class, &EntryClass::DomainInfo.into())
70 && e.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO)
71 {
72 if let Some(basedn) = e.get_ava_single_iutf8(Attribute::DomainLdapBasedn) {
74 if !DOMAIN_LDAP_BASEDN_RE.is_match(basedn) {
75 error!(
76 "Invalid {} '{}'. Must pass regex \"{}\"",
77 Attribute::DomainLdapBasedn,
78 basedn,
79 *DOMAIN_LDAP_BASEDN_RE
80 );
81 return Err(OperationError::InvalidState);
82 }
83 }
84
85 let u = Value::Uuid(qs.get_domain_uuid());
87 e.set_ava(&Attribute::DomainUuid, once(u));
88 trace!("plugin_domain: Applying uuid transform");
89
90 if !e.attribute_pres(Attribute::DomainName) {
92 let n = Value::new_iname(qs.get_domain_name());
93 e.set_ava(&Attribute::DomainName, once(n));
94 trace!("plugin_domain: Applying domain_name transform");
95 }
96
97 if !e.attribute_pres(Attribute::Version) {
99 let n = Value::Uint32(DOMAIN_LEVEL_0);
100 e.set_ava(&Attribute::Version, once(n));
101 warn!("plugin_domain: Applying domain version transform");
102 } else {
103 debug!("plugin_domain: NOT Applying domain version transform");
104 };
105
106 if DOMAIN_MIN_REMIGRATION_LEVEL < DOMAIN_LEVEL_10
114 && !e.attribute_pres(Attribute::DomainDisplayName)
115 {
116 let domain_display_name =
117 Value::new_utf8(format!("Kanidm {}", qs.get_domain_name()));
118 security_info!(
119 "plugin_domain: setting default domain_display_name to {:?}",
120 domain_display_name
121 );
122
123 e.set_ava(&Attribute::DomainDisplayName, once(domain_display_name));
124 }
125
126 Ok(())
127 } else {
128 Ok(())
129 }
130 })
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use crate::prelude::*;
137
138 #[qs_test]
140 async fn test_domain_generate_uuid(server: &QueryServer) {
141 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
142 let e_dom = server_txn
143 .internal_search_uuid(UUID_DOMAIN_INFO)
144 .expect("must not fail");
145
146 let u_dom = server_txn.get_domain_uuid();
147
148 assert!(e_dom.attribute_equality(Attribute::DomainUuid, &PartialValue::Uuid(u_dom)));
149 }
150}