1// Manage and generate domain uuid's and/or trust related domain
2// management.
34// The primary point of this is to generate a unique domain UUID on startup
5// which is importart for management of the replication topo and trust
6// relationships.
7use std::iter::once;
8use std::sync::Arc;
910use regex::Regex;
11use tracing::trace;
1213use crate::event::{CreateEvent, ModifyEvent};
14use crate::plugins::Plugin;
15use crate::prelude::*;
1617lazy_static! {
18pub static ref DOMAIN_LDAP_BASEDN_RE: Regex = {
19#[allow(clippy::expect_used)]
20Regex::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}
2425pub struct Domain {}
2627impl Plugin for Domain {
28fn id() -> &'static str {
29"plugin_domain"
30}
3132#[instrument(level = "debug", name = "domain_pre_create_transform", skip_all)]
33fn pre_create_transform(
34 qs: &mut QueryServerWriteTransaction,
35 cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
36 _ce: &CreateEvent,
37 ) -> Result<(), OperationError> {
38Self::modify_inner(qs, cand)
39 }
4041#[instrument(level = "debug", name = "domain_pre_modify", skip_all)]
42fn pre_modify(
43 qs: &mut QueryServerWriteTransaction,
44 _pre_cand: &[Arc<EntrySealedCommitted>],
45 cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
46 _me: &ModifyEvent,
47 ) -> Result<(), OperationError> {
48Self::modify_inner(qs, cand)
49 }
5051#[instrument(level = "debug", name = "domain_pre_batch_modify", skip_all)]
52fn 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> {
58Self::modify_inner(qs, cand)
59 }
60}
6162impl Domain {
63/// Generates the cookie key for the domain.
64fn 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| {
69if e.attribute_equality(Attribute::Class, &EntryClass::DomainInfo.into())
70 && e.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO)
71 {
72// Validate the domain ldap basedn syntax.
73if let Some(basedn) = e.get_ava_single_iutf8(Attribute::DomainLdapBasedn) {
74if !DOMAIN_LDAP_BASEDN_RE.is_match(basedn) {
75error!(
76"Invalid {} '{}'. Must pass regex \"{}\"",
77 Attribute::DomainLdapBasedn,
78 basedn,
79*DOMAIN_LDAP_BASEDN_RE
80 );
81return Err(OperationError::InvalidState);
82 }
83 }
8485// We always set this, because the DB uuid is authoritative.
86let u = Value::Uuid(qs.get_domain_uuid());
87 e.set_ava(&Attribute::DomainUuid, once(u));
88trace!("plugin_domain: Applying uuid transform");
8990// We only apply this if one isn't provided.
91if !e.attribute_pres(Attribute::DomainName) {
92let n = Value::new_iname(qs.get_domain_name());
93 e.set_ava(&Attribute::DomainName, once(n));
94trace!("plugin_domain: Applying domain_name transform");
95 }
9697// Setup the minimum functional level if one is not set already.
98if !e.attribute_pres(Attribute::Version) {
99let n = Value::Uint32(DOMAIN_LEVEL_0);
100 e.set_ava(&Attribute::Version, once(n));
101warn!("plugin_domain: Applying domain version transform");
102 } else {
103debug!("plugin_domain: NOT Applying domain version transform");
104 };
105106// create the domain_display_name if it's missing. This was the behaviour in versions
107 // prior to DL10. Rather than checking the domain version itself, the issue is we
108 // have to check the min remigration level. This is because during a server setup
109 // we start from the MIN remigration level and work up, and the domain version == 0.
110 //
111 // So effectively we only skip setting this value after we know that we are at DL12
112 // since we could never go back to anything lower than 10 at that point.
113if DOMAIN_MIN_REMIGRATION_LEVEL < DOMAIN_LEVEL_10
114 && !e.attribute_pres(Attribute::DomainDisplayName)
115 {
116let domain_display_name =
117 Value::new_utf8(format!("Kanidm {}", qs.get_domain_name()));
118security_info!(
119"plugin_domain: setting default domain_display_name to {:?}",
120 domain_display_name
121 );
122123 e.set_ava(&Attribute::DomainDisplayName, once(domain_display_name));
124 }
125126Ok(())
127 } else {
128Ok(())
129 }
130 })
131 }
132}
133134#[cfg(test)]
135mod tests {
136use crate::prelude::*;
137138// test we can create and generate the id
139#[qs_test]
140async fn test_domain_generate_uuid(server: &QueryServer) {
141let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
142let e_dom = server_txn
143 .internal_search_uuid(UUID_DOMAIN_INFO)
144 .expect("must not fail");
145146let u_dom = server_txn.get_domain_uuid();
147148assert!(e_dom.attribute_equality(Attribute::DomainUuid, &PartialValue::Uuid(u_dom)));
149 }
150}