Skip to main content

kanidmd_lib/server/
migrations.rs

1use crate::prelude::*;
2
3use crate::migration_data;
4use kanidm_proto::internal::{
5    // DomainUpgradeCheckItem as ProtoDomainUpgradeCheckItem,
6    DomainUpgradeCheckReport as ProtoDomainUpgradeCheckReport,
7    // DomainUpgradeCheckStatus as ProtoDomainUpgradeCheckStatus,
8};
9
10use super::ServerPhase;
11
12impl QueryServer {
13    #[instrument(level = "info", name = "system_initialisation", skip_all)]
14    pub async fn initialise_helper(
15        &self,
16        ts: Duration,
17        domain_target_level: DomainVersion,
18    ) -> Result<(), OperationError> {
19        // We need to perform this in a single transaction pass to prevent tainting
20        // databases during upgrades.
21        let mut write_txn = self.write(ts).await?;
22
23        // Check our database version - attempt to do an initial indexing
24        // based on the in memory configuration. This ONLY triggers ONCE on
25        // the very first run of the instance when the DB in newely created.
26        write_txn.upgrade_reindex(SYSTEM_INDEX_VERSION)?;
27
28        // Because we init the schema here, and commit, this reloads meaning
29        // that the on-disk index meta has been loaded, so our subsequent
30        // migrations will be correctly indexed.
31        //
32        // Remember, that this would normally mean that it's possible for schema
33        // to be mis-indexed (IE we index the new schemas here before we read
34        // the schema to tell us what's indexed), but because we have the in
35        // mem schema that defines how schema is structured, and this is all
36        // marked "system", then we won't have an issue here.
37        if domain_target_level < DOMAIN_LEVEL_1_11 {
38            // We don't create these in the DB after 1_11
39            write_txn
40                .initialise_schema_core()
41                .and_then(|_| write_txn.reload())?;
42        }
43
44        // This is what tells us if the domain entry existed before or not. This
45        // is now the primary method of migrations and version detection.
46        let db_domain_version = match write_txn.internal_search_uuid(UUID_DOMAIN_INFO) {
47            Ok(e) => Ok(e.get_ava_single_uint32(Attribute::Version).unwrap_or(0)),
48            Err(OperationError::NoMatchingEntries) => Ok(0),
49            Err(r) => Err(r),
50        }?;
51
52        debug!(?db_domain_version, "Before setting internal domain info");
53
54        if db_domain_version == DOMAIN_LEVEL_0 {
55            // This is here to catch when we increase domain levels but didn't create the migration
56            // hooks. If this fails it probably means you need to add another migration hook
57            // in the above.
58            debug_assert!(domain_target_level <= DOMAIN_MAX_LEVEL);
59
60            // Assert that we have a minimum creation level that is valid.
61            const { assert!(DOMAIN_MIN_CREATION_LEVEL == DOMAIN_LEVEL_11) };
62
63            // No domain info was present, so neither was the rest of the IDM. Bring up the
64            // full IDM here.
65
66            match domain_target_level {
67                DOMAIN_LEVEL_11 => write_txn.migrate_domain_10_to_11()?,
68                DOMAIN_LEVEL_12 => write_txn.migrate_domain_11_to_12()?,
69                DOMAIN_LEVEL_13 => write_txn.migrate_domain_12_to_13()?,
70                DOMAIN_LEVEL_14 => write_txn.migrate_domain_13_to_14()?,
71                DOMAIN_LEVEL_1_11 => write_txn.migrate_domain_1_10_to_1_11()?,
72                DOMAIN_LEVEL_1_12 => write_txn.migrate_domain_1_11_to_1_12()?,
73                DOMAIN_LEVEL_1_13 => write_txn.migrate_domain_1_12_to_1_13()?,
74                _ => {
75                    error!("Invalid requested domain target level for server bootstrap");
76                    debug_assert!(false);
77                    return Err(OperationError::MG0009InvalidTargetLevelForBootstrap);
78                }
79            }
80
81            write_txn
82                .internal_apply_domain_migration(domain_target_level)
83                .map(|()| {
84                    warn!(
85                        "Domain level has been bootstrapped to {}",
86                        domain_target_level
87                    );
88                })?;
89        }
90
91        // These steps apply both to bootstrapping and normal startup, since we now have
92        // a DB with data populated in either path.
93
94        // Domain info is now present, so we need to reflect that in our server
95        // domain structures. If we don't do this, the in memory domain level
96        // is stuck at 0 which can confuse init domain info below.
97        //
98        // This also is where the former domain taint flag will be loaded to
99        // d_info so that if the *previous* execution of the database was
100        // a devel version, we'll still trigger the forced remigration in
101        // in the case that we are moving from dev -> stable.
102        write_txn.force_domain_reload();
103
104        write_txn.reload()?;
105
106        assert!(write_txn.get_domain_version() > DOMAIN_LEVEL_0);
107
108        // Indicate the schema is now ready, which allows dyngroups to work when they
109        // are created in the next phase of migrations.
110        write_txn.set_phase(ServerPhase::SchemaReady);
111
112        // #2756 - if we *aren't* creating the base IDM entries, then we
113        // need to force dyn groups to reload since we're now at schema
114        // ready. This is done indirectly by ... reloading the schema again.
115        //
116        // This is because dyngroups don't load until server phase >= schemaready
117        // and the reload path for these is either a change in the dyngroup entry
118        // itself or a change to schema reloading. Since we aren't changing the
119        // dyngroup here, we have to go via the schema reload path.
120        write_txn.force_schema_reload();
121
122        // Reload as init idm affects access controls.
123        write_txn.reload()?;
124
125        // Domain info is now ready and reloaded, we can proceed.
126        write_txn.set_phase(ServerPhase::DomainInfoReady);
127
128        // This is the start of domain info related migrations which we will need in future
129        // to handle replication. Due to the access control rework, and the addition of "managed by"
130        // syntax, we need to ensure both nodes "fence" replication from each other. We do this
131        // by changing domain infos to be incompatible during this phase.
132
133        // The reloads will have populated this structure now.
134        let domain_info_version = write_txn.get_domain_version();
135        let domain_patch_level = write_txn.get_domain_patch_level();
136        let domain_development_taint = write_txn.get_domain_development_taint();
137        debug!(
138            ?db_domain_version,
139            ?domain_patch_level,
140            ?domain_development_taint,
141            "After setting internal domain info"
142        );
143
144        let mut reload_required = false;
145
146        // If the database domain info is a lower version than our target level, we reload.
147        if domain_info_version < domain_target_level {
148            // if (domain_target_level - domain_info_version) > DOMAIN_MIGRATION_SKIPS {
149            if domain_info_version < DOMAIN_MIGRATION_FROM_MIN {
150                error!(
151                    "UNABLE TO PROCEED. You are attempting a skip update which is NOT SUPPORTED."
152                );
153                error!(
154                    "For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html"
155                );
156                error!(domain_previous_version = ?domain_info_version, domain_target_version = ?domain_target_level, domain_migration_minimum_limit = ?DOMAIN_MIGRATION_FROM_MIN);
157                return Err(OperationError::MG0008SkipUpgradeAttempted);
158            }
159
160            // Apply each step in order.
161            for domain_target_level_step in domain_info_version..domain_target_level {
162                // Rust has no way to do a range with the minimum excluded and the maximum
163                // included, so we have to do min -> max which includes min and excludes max,
164                // and by adding 1 we gett the same result.
165                let domain_target_level_step = domain_target_level_step + 1;
166
167                // Note that this triggers a reload for us, we don't need to manually determine if
168                // one is needed or not.
169                write_txn
170                    .internal_apply_domain_migration(domain_target_level_step)
171                    .map(|()| {
172                        warn!(
173                            "Domain level has been raised to {}",
174                            domain_target_level_step
175                        );
176                    })?;
177            }
178        } else if domain_info_version > domain_target_level {
179            // This is a DOWNGRADE which may not proceed.
180            error!("UNABLE TO PROCEED. You are attempting a downgrade which is NOT SUPPORTED.");
181            error!(
182                "For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html"
183            );
184            error!(domain_previous_version = ?domain_info_version, domain_target_version = ?domain_target_level);
185            return Err(OperationError::MG0010DowngradeNotAllowed);
186        } else if domain_development_taint {
187            // This forces pre-release versions to re-migrate each start up. This solves
188            // the domain-version-sprawl issue so that during a development cycle we can
189            // do a single domain version bump, and continue to extend the migrations
190            // within that release cycle to contain what we require.
191            //
192            // If this is a pre-release build
193            // AND
194            // we are NOT in a test environment
195            // AND
196            // We did not already need a version migration as above
197            write_txn.domain_remigrate(DOMAIN_PREVIOUS_TGT_LEVEL)?;
198
199            reload_required = true;
200        }
201
202        // If we are new enough to support patches, and we are lower than the target patch level
203        // then a reload will be applied after we raise the patch level.
204        if domain_patch_level < DOMAIN_TGT_PATCH_LEVEL {
205            write_txn
206                .internal_modify_uuid(
207                    UUID_DOMAIN_INFO,
208                    &ModifyList::new_purge_and_set(
209                        Attribute::PatchLevel,
210                        Value::new_uint32(DOMAIN_TGT_PATCH_LEVEL),
211                    ),
212                )
213                .map(|()| {
214                    warn!(
215                        "Domain patch level has been raised to {}",
216                        domain_patch_level
217                    );
218                })?;
219
220            reload_required = true;
221        };
222
223        // Execute whatever operations we have batched up and ready to go. This is needed
224        // to preserve ordering of the operations - if we reloaded after a remigrate then
225        // we would have skipped the patch level fix which needs to have occurred *first*.
226        if reload_required {
227            write_txn.reload()?;
228        }
229
230        // Now set the db/domain devel taint flag to match our current release status
231        // if it changes. This is what breaks the cycle of db taint from dev -> stable
232        let current_devel_flag = option_env!("KANIDM_PRE_RELEASE").is_some();
233        if current_devel_flag {
234            warn!("Domain Development Taint mode is enabled");
235        }
236        if domain_development_taint != current_devel_flag {
237            write_txn.internal_modify_uuid(
238                UUID_DOMAIN_INFO,
239                &ModifyList::new_purge_and_set(
240                    Attribute::DomainDevelopmentTaint,
241                    Value::Bool(current_devel_flag),
242                ),
243            )?;
244        }
245
246        // We are ready to run
247        write_txn.set_phase(ServerPhase::Running);
248
249        // Commit all changes, this also triggers the final reload, this should be a no-op
250        // since we already did all the needed loads above.
251        write_txn.commit()?;
252
253        debug!("Database version check and migrations success! ☀️  ");
254        Ok(())
255    }
256}
257
258impl QueryServerWriteTransaction<'_> {
259    /// Apply a domain migration `to_level`. Errors if `to_level` is not greater than or equal to
260    /// the active level.
261    #[instrument(level = "debug", skip(self))]
262    pub(crate) fn internal_apply_domain_migration(
263        &mut self,
264        to_level: u32,
265    ) -> Result<(), OperationError> {
266        self.internal_modify_uuid(
267            UUID_DOMAIN_INFO,
268            &ModifyList::new_purge_and_set(Attribute::Version, Value::new_uint32(to_level)),
269        )
270        .and_then(|()| self.reload())
271    }
272
273    fn internal_migrate_or_create_batch(
274        &mut self,
275        msg: &str,
276        entries: Vec<EntryInitNew>,
277    ) -> Result<(), OperationError> {
278        let r: Result<(), _> = entries
279            .into_iter()
280            .try_for_each(|entry| self.internal_migrate_or_create(entry));
281
282        if let Err(err) = r {
283            error!(?err, msg);
284            debug_assert!(false);
285        }
286
287        Ok(())
288    }
289
290    #[instrument(level = "debug", skip_all)]
291    /// - If the thing exists:
292    ///   - Ensure the set of attributes match and are present
293    ///     (but don't delete multivalue, or extended attributes in the situation.
294    /// - If not:
295    ///   - Create the entry
296    ///
297    /// This will extra classes an attributes alone!
298    ///
299    /// NOTE: `gen_modlist*` IS schema aware and will handle multivalue correctly!
300    fn internal_migrate_or_create(
301        &mut self,
302        e: Entry<EntryInit, EntryNew>,
303    ) -> Result<(), OperationError> {
304        // NOTE: Ignoring an attribute only affects the migration phase, not create.
305        self.internal_migrate_or_create_ignore_attrs(
306            e,
307            &[
308                // If the credential type is present, we don't want to touch it.
309                Attribute::CredentialTypeMinimum,
310            ],
311        )
312    }
313
314    #[instrument(level = "debug", skip_all)]
315    fn internal_delete_batch(
316        &mut self,
317        msg: &str,
318        entries: Vec<Uuid>,
319    ) -> Result<(), OperationError> {
320        let filter = entries
321            .into_iter()
322            .map(|uuid| f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)))
323            .collect();
324
325        // Don't attempt to delete already removed entries that are in the recycle bin.
326        let filter = filter!(f_or(filter));
327
328        let result = self.internal_delete(&filter);
329
330        match result {
331            Ok(_) | Err(OperationError::NoMatchingEntries) => Ok(()),
332            Err(err) => {
333                error!(?err, msg);
334                Err(err)
335            }
336        }
337    }
338
339    /// This is the same as [QueryServerWriteTransaction::internal_migrate_or_create]
340    /// but it will ignore the specified list of attributes, so that if an admin has
341    /// modified those values then we don't stomp them.
342    #[instrument(level = "trace", skip_all)]
343    fn internal_migrate_or_create_ignore_attrs(
344        &mut self,
345        mut e: Entry<EntryInit, EntryNew>,
346        attrs: &[Attribute],
347    ) -> Result<(), OperationError> {
348        trace!("operating on {:?}", e.get_uuid());
349
350        let Some(filt) = e.filter_from_attrs(&[Attribute::Uuid]) else {
351            return Err(OperationError::FilterGeneration);
352        };
353
354        trace!("search {:?}", filt);
355
356        let results = self.internal_search(filt.clone())?;
357
358        if results.is_empty() {
359            // The entry does not exist. Create it.
360
361            // If there are create-once members, set them up now.
362            if let Some(members_create_once) = e.pop_ava(Attribute::MemberCreateOnce) {
363                if let Some(members) = e.get_ava_mut(Attribute::Member) {
364                    // Merge
365                    members.merge(&members_create_once).inspect_err(|err| {
366                        error!(?err, "Unable to merge member sets, mismatched types?");
367                    })?;
368                } else {
369                    // Just push
370                    e.set_ava_set(&Attribute::Member, members_create_once);
371                }
372            };
373
374            self.internal_create(vec![e])
375        } else if results.len() == 1 {
376            // This is always ignored during migration.
377            e.remove_ava(&Attribute::MemberCreateOnce);
378
379            // For each ignored attr, we remove it from entry.
380            for attr in attrs.iter() {
381                e.remove_ava(attr);
382            }
383
384            // If the thing is subset, pass
385            match e.gen_modlist_assert(&self.schema) {
386                Ok(modlist) => {
387                    // Apply to &results[0]
388                    trace!(?modlist);
389                    self.internal_modify(&filt, &modlist)
390                }
391                Err(e) => Err(OperationError::SchemaViolation(e)),
392            }
393        } else {
394            admin_error!(
395                "Invalid Result Set - Expected One Entry for {:?} - {:?}",
396                filt,
397                results
398            );
399            Err(OperationError::InvalidDbState)
400        }
401    }
402
403    // Commented as an example of patch application
404    /*
405    /// Patch Application - This triggers a one-shot fixup task for issue #3178
406    /// to force access controls to re-migrate in existing databases so that they're
407    /// content matches expected values.
408    #[instrument(level = "info", skip_all)]
409    pub(crate) fn migrate_domain_patch_level_2(&mut self) -> Result<(), OperationError> {
410        admin_warn!("applying domain patch 2.");
411
412        debug_assert!(*self.phase >= ServerPhase::SchemaReady);
413
414        let idm_data = migration_data::dl9::phase_7_builtin_access_control_profiles();
415
416        idm_data
417            .into_iter()
418            .try_for_each(|entry| self.internal_migrate_or_create(entry))
419            .map_err(|err| {
420                error!(?err, "migrate_domain_patch_level_2 -> Error");
421                err
422            })?;
423
424        self.reload()?;
425
426        Ok(())
427    }
428    */
429
430    /// Migration domain level 10 to 11 (1.7.0)
431    #[instrument(level = "info", skip_all)]
432    pub(crate) fn migrate_domain_10_to_11(&mut self) -> Result<(), OperationError> {
433        if !cfg!(test) && DOMAIN_TGT_LEVEL < DOMAIN_LEVEL_10 {
434            error!("Unable to raise domain level from 10 to 11.");
435            return Err(OperationError::MG0004DomainLevelInDevelopment);
436        }
437
438        // =========== Apply changes ==============
439        self.internal_migrate_or_create_batch(
440            "phase 1 - schema attrs",
441            migration_data::dl11::phase_1_schema_attrs(),
442        )?;
443
444        self.internal_migrate_or_create_batch(
445            "phase 2 - schema classes",
446            migration_data::dl11::phase_2_schema_classes(),
447        )?;
448
449        // Reload for the new schema.
450        self.reload()?;
451
452        // Since we just loaded in a ton of schema, lets reindex it in case we added
453        // new indexes, or this is a bootstrap and we have no indexes yet.
454        self.reindex(false)?;
455
456        // Set Phase
457        // Indicate the schema is now ready, which allows dyngroups to work when they
458        // are created in the next phase of migrations.
459        self.set_phase(ServerPhase::SchemaReady);
460
461        self.internal_migrate_or_create_batch(
462            "phase 3 - key provider",
463            migration_data::dl11::phase_3_key_provider(),
464        )?;
465
466        // Reload for the new key providers
467        self.reload()?;
468
469        self.internal_migrate_or_create_batch(
470            "phase 4 - system entries",
471            migration_data::dl11::phase_4_system_entries(),
472        )?;
473
474        // Reload for the new system entries
475        self.reload()?;
476
477        // Domain info is now ready and reloaded, we can proceed.
478        self.set_phase(ServerPhase::DomainInfoReady);
479
480        // Bring up the IDM entries.
481        self.internal_migrate_or_create_batch(
482            "phase 5 - builtin admin entries",
483            migration_data::dl11::phase_5_builtin_admin_entries()?,
484        )?;
485
486        self.internal_migrate_or_create_batch(
487            "phase 6 - builtin not admin entries",
488            migration_data::dl11::phase_6_builtin_non_admin_entries()?,
489        )?;
490
491        self.internal_migrate_or_create_batch(
492            "phase 7 - builtin access control profiles",
493            migration_data::dl11::phase_7_builtin_access_control_profiles(),
494        )?;
495
496        self.reload()?;
497
498        Ok(())
499    }
500
501    /// Migration domain level 11 to 12 (1.8.0)
502    #[instrument(level = "info", skip_all)]
503    pub(crate) fn migrate_domain_11_to_12(&mut self) -> Result<(), OperationError> {
504        if !cfg!(test) && DOMAIN_TGT_LEVEL < DOMAIN_LEVEL_11 {
505            error!("Unable to raise domain level from 11 to 12.");
506            return Err(OperationError::MG0004DomainLevelInDevelopment);
507        }
508
509        // =========== Apply changes ==============
510        self.internal_migrate_or_create_batch(
511            "phase 1 - schema attrs",
512            migration_data::dl12::phase_1_schema_attrs(),
513        )?;
514
515        self.internal_migrate_or_create_batch(
516            "phase 2 - schema classes",
517            migration_data::dl12::phase_2_schema_classes(),
518        )?;
519
520        // Reload for the new schema.
521        self.reload()?;
522
523        // Since we just loaded in a ton of schema, lets reindex it in case we added
524        // new indexes, or this is a bootstrap and we have no indexes yet.
525        self.reindex(false)?;
526
527        // Set Phase
528        // Indicate the schema is now ready, which allows dyngroups to work when they
529        // are created in the next phase of migrations.
530        self.set_phase(ServerPhase::SchemaReady);
531
532        self.internal_migrate_or_create_batch(
533            "phase 3 - key provider",
534            migration_data::dl12::phase_3_key_provider(),
535        )?;
536
537        // Reload for the new key providers
538        self.reload()?;
539
540        self.internal_migrate_or_create_batch(
541            "phase 4 - system entries",
542            migration_data::dl12::phase_4_system_entries(),
543        )?;
544
545        // Reload for the new system entries
546        self.reload()?;
547
548        // Domain info is now ready and reloaded, we can proceed.
549        self.set_phase(ServerPhase::DomainInfoReady);
550
551        // Bring up the IDM entries.
552        self.internal_migrate_or_create_batch(
553            "phase 5 - builtin admin entries",
554            migration_data::dl12::phase_5_builtin_admin_entries()?,
555        )?;
556
557        self.internal_migrate_or_create_batch(
558            "phase 6 - builtin not admin entries",
559            migration_data::dl12::phase_6_builtin_non_admin_entries()?,
560        )?;
561
562        self.internal_migrate_or_create_batch(
563            "phase 7 - builtin access control profiles",
564            migration_data::dl12::phase_7_builtin_access_control_profiles(),
565        )?;
566
567        self.reload()?;
568
569        // Cleanup any leftover id keys
570        let modlist = ModifyList::new_purge(Attribute::IdVerificationEcKey);
571        let filter = filter_all!(f_pres(Attribute::IdVerificationEcKey));
572
573        self.internal_modify(&filter, &modlist)?;
574
575        Ok(())
576    }
577
578    /// Migration domain level 12 to 13 (1.9.0)
579    #[instrument(level = "info", skip_all)]
580    pub(crate) fn migrate_domain_12_to_13(&mut self) -> Result<(), OperationError> {
581        if !cfg!(test) && DOMAIN_TGT_LEVEL < DOMAIN_LEVEL_12 {
582            error!("Unable to raise domain level from 12 to 13.");
583            return Err(OperationError::MG0004DomainLevelInDevelopment);
584        }
585
586        // =========== Apply changes ==============
587        self.internal_migrate_or_create_batch(
588            "phase 1 - schema attrs",
589            migration_data::dl13::phase_1_schema_attrs(),
590        )?;
591
592        self.internal_migrate_or_create_batch(
593            "phase 2 - schema classes",
594            migration_data::dl13::phase_2_schema_classes(),
595        )?;
596
597        // Reload for the new schema.
598        self.reload()?;
599
600        // Since we just loaded in a ton of schema, lets reindex it in case we added
601        // new indexes, or this is a bootstrap and we have no indexes yet.
602        self.reindex(false)?;
603
604        // Set Phase
605        // Indicate the schema is now ready, which allows dyngroups to work when they
606        // are created in the next phase of migrations.
607        self.set_phase(ServerPhase::SchemaReady);
608
609        self.internal_migrate_or_create_batch(
610            "phase 3 - key provider",
611            migration_data::dl13::phase_3_key_provider(),
612        )?;
613
614        // Reload for the new key providers
615        self.reload()?;
616
617        self.internal_migrate_or_create_batch(
618            "phase 4 - system entries",
619            migration_data::dl13::phase_4_system_entries(),
620        )?;
621
622        // Reload for the new system entries
623        self.reload()?;
624
625        // Domain info is now ready and reloaded, we can proceed.
626        self.set_phase(ServerPhase::DomainInfoReady);
627
628        // Bring up the IDM entries.
629        self.internal_migrate_or_create_batch(
630            "phase 5 - builtin admin entries",
631            migration_data::dl13::phase_5_builtin_admin_entries()?,
632        )?;
633
634        self.internal_migrate_or_create_batch(
635            "phase 6 - builtin not admin entries",
636            migration_data::dl13::phase_6_builtin_non_admin_entries()?,
637        )?;
638
639        self.internal_migrate_or_create_batch(
640            "phase 7 - builtin access control profiles",
641            migration_data::dl13::phase_7_builtin_access_control_profiles(),
642        )?;
643
644        self.internal_delete_batch(
645            "phase 8 - delete UUIDS",
646            migration_data::dl13::phase_8_delete_uuids(),
647        )?;
648
649        self.reload()?;
650
651        Ok(())
652    }
653
654    /// Migration domain level 13 to 14 (1.10.0)
655    #[instrument(level = "info", skip_all)]
656    pub(crate) fn migrate_domain_13_to_14(&mut self) -> Result<(), OperationError> {
657        if !cfg!(test) && DOMAIN_TGT_LEVEL < DOMAIN_LEVEL_13 {
658            error!("Unable to raise domain level from 13 to 14.");
659            return Err(OperationError::MG0004DomainLevelInDevelopment);
660        }
661
662        // =========== Apply changes ==============
663        self.internal_migrate_or_create_batch(
664            &format!("phase 1 - schema attrs target {}", DOMAIN_TGT_LEVEL),
665            migration_data::dl14::phase_1_schema_attrs(),
666        )?;
667
668        self.internal_migrate_or_create_batch(
669            "phase 2 - schema classes",
670            migration_data::dl14::phase_2_schema_classes(),
671        )?;
672
673        // Reload for the new schema.
674        self.reload()?;
675
676        // Since we just loaded in a ton of schema, lets reindex it in case we added
677        // new indexes, or this is a bootstrap and we have no indexes yet.
678        self.reindex(false)?;
679
680        // Set Phase
681        // Indicate the schema is now ready, which allows dyngroups to work when they
682        // are created in the next phase of migrations.
683        self.set_phase(ServerPhase::SchemaReady);
684
685        self.internal_migrate_or_create_batch(
686            "phase 3 - key provider",
687            migration_data::dl14::phase_3_key_provider(),
688        )?;
689
690        // Reload for the new key providers
691        self.reload()?;
692
693        self.internal_migrate_or_create_batch(
694            "phase 4 - dl14 system entries",
695            migration_data::dl14::phase_4_system_entries(),
696        )?;
697
698        // Reload for the new system entries
699        self.reload()?;
700
701        // Domain info is now ready and reloaded, we can proceed.
702        self.set_phase(ServerPhase::DomainInfoReady);
703
704        // Bring up the IDM entries.
705        self.internal_migrate_or_create_batch(
706            "phase 5 - builtin admin entries",
707            migration_data::dl14::phase_5_builtin_admin_entries()?,
708        )?;
709
710        self.internal_migrate_or_create_batch(
711            "phase 6 - builtin not admin entries",
712            migration_data::dl14::phase_6_builtin_non_admin_entries()?,
713        )?;
714
715        self.internal_migrate_or_create_batch(
716            "phase 7 - builtin access control profiles",
717            migration_data::dl14::phase_7_builtin_access_control_profiles(),
718        )?;
719
720        self.internal_delete_batch(
721            "phase 8 - delete UUIDS",
722            migration_data::dl14::phase_8_delete_uuids(),
723        )?;
724
725        self.reload()?;
726
727        // Default PasswordChangedTime to UNIX_EPOCH
728        let filter = filter_all!(f_and!([
729            f_eq(Attribute::Class, EntryClass::Person.into()),
730            f_andnot(f_pres(Attribute::PasswordChangedTime)),
731        ]));
732        let modlist = ModifyList::new_purge_and_set(
733            Attribute::PasswordChangedTime,
734            Value::DateTime(time::OffsetDateTime::UNIX_EPOCH),
735        );
736        self.internal_modify(&filter, &modlist)?;
737
738        Ok(())
739    }
740
741    pub(crate) fn migrate_schema_1_11(&mut self) -> Result<(), OperationError> {
742        self.schema.extend_in_memory(
743            migration_data::dl15::phase_1_schema_attrs(),
744            migration_data::dl15::phase_2_schema_classes(),
745        )
746    }
747
748    /// Migration domain level 14 to 15 (1.11.0)
749    #[instrument(level = "info", skip_all)]
750    pub(crate) fn migrate_domain_1_10_to_1_11(&mut self) -> Result<(), OperationError> {
751        if !cfg!(test) && DOMAIN_TGT_LEVEL < DOMAIN_LEVEL_14 {
752            error!(
753                "Unable to raise domain level from {} to {}.",
754                DOMAIN_LEVEL_14, DOMAIN_LEVEL_1_11
755            );
756            return Err(OperationError::MG0004DomainLevelInDevelopment);
757        }
758
759        // =========== Apply changes ==============
760        self.migrate_schema_1_11()?;
761
762        // Reload for the new schema.
763        self.reload()?;
764
765        // Since we just loaded in a ton of schema, lets reindex it in case we added
766        // new indexes, or this is a bootstrap and we have no indexes yet.
767        self.reindex(false)?;
768
769        // Delete all existing DB contained schema.
770
771        let filter = filter!(f_and(vec![
772            f_eq(Attribute::Class, EntryClass::ClassType.into()),
773            f_eq(Attribute::Class, EntryClass::AttributeType.into()),
774        ]));
775
776        self.internal_delete_if_exists(&filter)?;
777
778        // Set Phase
779        // Indicate the schema is now ready, which allows dyngroups to work when they
780        // are created in the next phase of migrations.
781        self.set_phase(ServerPhase::SchemaReady);
782
783        self.internal_migrate_or_create_batch(
784            "phase 3 - key provider",
785            migration_data::dl15::phase_3_key_provider(),
786        )?;
787
788        // Reload for the new key providers
789        self.reload()?;
790
791        self.internal_migrate_or_create_batch(
792            "phase 4 - dl15 system entries",
793            migration_data::dl15::phase_4_system_entries(),
794        )?;
795
796        // Reload for the new system entries
797        self.reload()?;
798
799        // Domain info is now ready and reloaded, we can proceed.
800        self.set_phase(ServerPhase::DomainInfoReady);
801
802        // Bring up the IDM entries.
803        self.internal_migrate_or_create_batch(
804            "phase 5 - builtin admin entries",
805            migration_data::dl15::phase_5_builtin_admin_entries()?,
806        )?;
807
808        self.internal_migrate_or_create_batch(
809            "phase 6 - builtin not admin entries",
810            migration_data::dl15::phase_6_builtin_non_admin_entries()?,
811        )?;
812
813        self.internal_migrate_or_create_batch(
814            "phase 7 - builtin access control profiles",
815            migration_data::dl15::phase_7_builtin_access_control_profiles(),
816        )?;
817
818        self.internal_delete_batch(
819            "phase 8 - delete UUIDS",
820            migration_data::dl15::phase_8_delete_uuids(),
821        )?;
822
823        self.reload()?;
824
825        // Default PasswordChangedTime to UNIX_EPOCH
826        let filter = filter_all!(f_and!([
827            f_eq(Attribute::Class, EntryClass::Person.into()),
828            f_andnot(f_pres(Attribute::PasswordChangedTime)),
829        ]));
830        let modlist = ModifyList::new_purge_and_set(
831            Attribute::PasswordChangedTime,
832            Value::DateTime(time::OffsetDateTime::UNIX_EPOCH),
833        );
834        self.internal_modify(&filter, &modlist)?;
835
836        Ok(())
837    }
838
839    pub(crate) fn migrate_schema_1_12(&mut self) -> Result<(), OperationError> {
840        self.schema.extend_in_memory(
841            migration_data::dl_1_12::phase_1_schema_attrs(),
842            migration_data::dl_1_12::phase_2_schema_classes(),
843        )
844    }
845
846    /// Migration domain level 1.11 to 1.12
847    #[instrument(level = "info", skip_all)]
848    pub(crate) fn migrate_domain_1_11_to_1_12(&mut self) -> Result<(), OperationError> {
849        if !cfg!(test) && DOMAIN_TGT_LEVEL < DOMAIN_LEVEL_1_12 {
850            error!("Unable to raise domain level from 1_11 to 1_12.");
851            return Err(OperationError::MG0004DomainLevelInDevelopment);
852        }
853
854        use migration_data::dl_1_12 as dl_target;
855
856        // =========== Apply changes ==============
857        self.migrate_schema_1_12()?;
858
859        // Reload for the new schema.
860        self.reload()?;
861
862        // Since we just loaded in a ton of schema, lets reindex it in case we added
863        // new indexes, or this is a bootstrap and we have no indexes yet.
864        self.reindex(false)?;
865
866        // Delete all existing DB contained schema.
867
868        let filter = filter!(f_and(vec![
869            f_eq(Attribute::Class, EntryClass::ClassType.into()),
870            f_eq(Attribute::Class, EntryClass::AttributeType.into()),
871        ]));
872
873        self.internal_delete_if_exists(&filter)?;
874
875        // Set Phase
876        // Indicate the schema is now ready, which allows dyngroups to work when they
877        // are created in the next phase of migrations.
878        self.set_phase(ServerPhase::SchemaReady);
879
880        self.internal_migrate_or_create_batch(
881            "phase 3 - key provider",
882            dl_target::phase_3_key_provider(),
883        )?;
884
885        // Reload for the new key providers
886        self.reload()?;
887
888        self.internal_migrate_or_create_batch(
889            "phase 4 - system entries",
890            dl_target::phase_4_system_entries(),
891        )?;
892
893        // Reload for the new system entries
894        self.reload()?;
895
896        // Domain info is now ready and reloaded, we can proceed.
897        self.set_phase(ServerPhase::DomainInfoReady);
898
899        // Bring up the IDM entries.
900        self.internal_migrate_or_create_batch(
901            "phase 5 - builtin admin entries",
902            dl_target::phase_5_builtin_admin_entries()?,
903        )?;
904
905        self.internal_migrate_or_create_batch(
906            "phase 6 - builtin not admin entries",
907            dl_target::phase_6_builtin_non_admin_entries()?,
908        )?;
909
910        self.internal_migrate_or_create_batch(
911            "phase 7 - builtin access control profiles",
912            dl_target::phase_7_builtin_access_control_profiles(),
913        )?;
914
915        self.internal_delete_batch("phase 8 - delete UUIDS", dl_target::phase_8_delete_uuids())?;
916
917        self.reload()?;
918
919        Ok(())
920    }
921
922    /// Migration domain level 1.12 to 1.13
923    #[instrument(level = "info", skip_all)]
924    pub(crate) fn migrate_domain_1_12_to_1_13(&mut self) -> Result<(), OperationError> {
925        if !cfg!(test) && DOMAIN_TGT_LEVEL < DOMAIN_LEVEL_1_13 {
926            error!("Unable to raise domain level from 1_12 to 1_13.");
927            return Err(OperationError::MG0004DomainLevelInDevelopment);
928        }
929
930        Ok(())
931    }
932
933    #[instrument(level = "info", skip_all)]
934    pub(crate) fn initialise_schema_core(&mut self) -> Result<(), OperationError> {
935        debug!("initialise_schema_core -> start ...");
936        // Load in all the "core" schema, that we already have in "memory".
937        let entries = self.schema.to_entries();
938
939        // admin_debug!("Dumping schemas: {:?}", entries);
940
941        // internal_migrate_or_create.
942        let r: Result<_, _> = entries.into_iter().try_for_each(|e| {
943            trace!(?e, "init schema entry");
944            self.internal_migrate_or_create(e)
945        });
946        if r.is_ok() {
947            debug!("initialise_schema_core -> Ok!");
948        } else {
949            error!(?r, "initialise_schema_core -> Error");
950        }
951        // why do we have error handling if it's always supposed to be `Ok`?
952        debug_assert!(r.is_ok());
953        r
954    }
955}
956
957impl QueryServerReadTransaction<'_> {
958    /// Retrieve the domain info of this server
959    pub fn domain_upgrade_check(
960        &mut self,
961    ) -> Result<ProtoDomainUpgradeCheckReport, OperationError> {
962        let d_info = &self.d_info;
963
964        let name = d_info.d_name.clone();
965        let uuid = d_info.d_uuid;
966        let current_level = d_info.d_vers;
967        let upgrade_level = DOMAIN_TGT_NEXT_LEVEL;
968
969        let report_items = Vec::with_capacity(1);
970
971        // Intentionally left commented to serve as a future check template.
972        /*
973        if current_level <= DOMAIN_LEVEL_7 && upgrade_level >= DOMAIN_LEVEL_8 {
974            let item = self
975                .domain_upgrade_check_7_to_8_oauth2_strict_redirect_uri()
976                .map_err(|err| {
977                    error!(
978                        ?err,
979                        "Failed to perform domain upgrade check 7 to 8 - oauth2-strict-redirect_uri"
980                    );
981                    err
982                })?;
983            report_items.push(item);
984        }
985        */
986
987        Ok(ProtoDomainUpgradeCheckReport {
988            name,
989            uuid,
990            current_level,
991            upgrade_level,
992            report_items,
993        })
994    }
995
996    // Intentionally left commented to serve as a future check template.
997    /*
998    pub(crate) fn domain_upgrade_check_7_to_8_oauth2_strict_redirect_uri(
999        &mut self,
1000    ) -> Result<ProtoDomainUpgradeCheckItem, OperationError> {
1001        let filter = filter!(f_and!([
1002            f_eq(Attribute::Class, EntryClass::OAuth2ResourceServer.into()),
1003            f_andnot(f_pres(Attribute::OAuth2StrictRedirectUri)),
1004        ]));
1005
1006        let results = self.internal_search(filter)?;
1007
1008        let affected_entries = results
1009            .into_iter()
1010            .map(|entry| entry.get_display_id())
1011            .collect::<Vec<_>>();
1012
1013        let status = if affected_entries.is_empty() {
1014            ProtoDomainUpgradeCheckStatus::Pass7To8Oauth2StrictRedirectUri
1015        } else {
1016            ProtoDomainUpgradeCheckStatus::Fail7To8Oauth2StrictRedirectUri
1017        };
1018
1019        Ok(ProtoDomainUpgradeCheckItem {
1020            status,
1021            from_level: DOMAIN_LEVEL_7,
1022            to_level: DOMAIN_LEVEL_8,
1023            affected_entries,
1024        })
1025    }
1026    */
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031    // use super::{ProtoDomainUpgradeCheckItem, ProtoDomainUpgradeCheckStatus};
1032    use crate::prelude::*;
1033    use crate::value::CredentialType;
1034    use crate::valueset::ValueSetCredentialType;
1035
1036    #[qs_test]
1037    async fn test_init_idempotent_schema_core(server: &QueryServer) {
1038        {
1039            // Setup and abort.
1040            let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
1041            assert!(server_txn.initialise_schema_core().is_ok());
1042        }
1043        {
1044            let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
1045            assert!(server_txn.initialise_schema_core().is_ok());
1046            assert!(server_txn.initialise_schema_core().is_ok());
1047            assert!(server_txn.commit().is_ok());
1048        }
1049        {
1050            // Now do it again in a new txn, but abort
1051            let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
1052            assert!(server_txn.initialise_schema_core().is_ok());
1053        }
1054        {
1055            // Now do it again in a new txn.
1056            let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
1057            assert!(server_txn.initialise_schema_core().is_ok());
1058            assert!(server_txn.commit().is_ok());
1059        }
1060    }
1061
1062    /// This test is for ongoing/longterm checks over the previous to current version.
1063    /// This is in contrast to the specific version checks below that are often to
1064    /// test a version to version migration.
1065    #[qs_test(domain_level=DOMAIN_PREVIOUS_TGT_LEVEL)]
1066    async fn test_migrations_dl_previous_to_dl_target(server: &QueryServer) {
1067        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1068
1069        let db_domain_version = write_txn
1070            .internal_search_uuid(UUID_DOMAIN_INFO)
1071            .expect("unable to access domain entry")
1072            .get_ava_single_uint32(Attribute::Version)
1073            .expect("Attribute Version not present");
1074
1075        assert_eq!(db_domain_version, DOMAIN_PREVIOUS_TGT_LEVEL);
1076
1077        // == SETUP ==
1078
1079        // Add a member to a group - it should not be removed.
1080        // Remove a default member from a group - it should be returned.
1081        let modlist = ModifyList::new_set(
1082            Attribute::Member,
1083            // This achieves both because this removes IDM_ADMIN from the group
1084            // while setting only anon as a member.
1085            ValueSetRefer::new(UUID_ANONYMOUS),
1086        );
1087        write_txn
1088            .internal_modify_uuid(UUID_IDM_ADMINS, &modlist)
1089            .expect("Unable to modify CredentialTypeMinimum");
1090
1091        // Remove a group from an object that is "create once".  It should not
1092        // be re-added.
1093        let modlist = ModifyList::new_purge(Attribute::Member);
1094        write_txn
1095            .internal_modify_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE, &modlist)
1096            .expect("Unable to remove idm_all_persons from self-write");
1097
1098        // Change default account policy - it should not be reverted.
1099        let modlist = ModifyList::new_set(
1100            Attribute::CredentialTypeMinimum,
1101            ValueSetCredentialType::new(CredentialType::Any),
1102        );
1103        write_txn
1104            .internal_modify_uuid(UUID_IDM_ALL_PERSONS, &modlist)
1105            .expect("Unable to modify CredentialTypeMinimum");
1106
1107        write_txn.commit().expect("Unable to commit");
1108
1109        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1110
1111        // == Increase the version ==
1112        write_txn
1113            .internal_apply_domain_migration(DOMAIN_TGT_LEVEL)
1114            .expect("Unable to set domain level");
1115
1116        // post migration verification.
1117        // Check that our group is as we left it
1118        let idm_admins_entry = write_txn
1119            .internal_search_uuid(UUID_IDM_ADMINS)
1120            .expect("Unable to retrieve all persons");
1121
1122        let members = idm_admins_entry
1123            .get_ava_refer(Attribute::Member)
1124            .expect("No members present");
1125
1126        // Still present
1127        assert!(members.contains(&UUID_ANONYMOUS));
1128        // Was reverted
1129        assert!(members.contains(&UUID_IDM_ADMIN));
1130
1131        // Check that self-write still doesn't have all persons.
1132        let idm_people_self_name_write_entry = write_txn
1133            .internal_search_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE)
1134            .expect("Unable to retrieve all persons");
1135
1136        let members = idm_people_self_name_write_entry.get_ava_refer(Attribute::Member);
1137
1138        // There are no members!
1139        assert!(members.is_none());
1140
1141        // Check that the account policy did not revert.
1142        let all_persons_entry = write_txn
1143            .internal_search_uuid(UUID_IDM_ALL_PERSONS)
1144            .expect("Unable to retrieve all persons");
1145
1146        assert_eq!(
1147            all_persons_entry.get_ava_single_credential_type(Attribute::CredentialTypeMinimum),
1148            Some(CredentialType::Any)
1149        );
1150
1151        write_txn.commit().expect("Unable to commit");
1152    }
1153
1154    #[qs_test(domain_level=DOMAIN_TGT_LEVEL)]
1155    async fn test_migrations_prevent_downgrades(server: &QueryServer) {
1156        let curtime = duration_from_epoch_now();
1157
1158        let mut write_txn = server.write(curtime).await.unwrap();
1159
1160        let db_domain_version = write_txn
1161            .internal_search_uuid(UUID_DOMAIN_INFO)
1162            .expect("unable to access domain entry")
1163            .get_ava_single_uint32(Attribute::Version)
1164            .expect("Attribute Version not present");
1165
1166        assert_eq!(db_domain_version, DOMAIN_TGT_LEVEL);
1167
1168        drop(write_txn);
1169
1170        // MUST NOT SUCCEED.
1171        let err = server
1172            .initialise_helper(curtime, DOMAIN_PREVIOUS_TGT_LEVEL)
1173            .await
1174            .expect_err("Domain level was lowered!!!!");
1175
1176        assert_eq!(err, OperationError::MG0010DowngradeNotAllowed);
1177    }
1178
1179    #[qs_test(domain_level=DOMAIN_MIGRATION_FROM_INVALID)]
1180    async fn test_migrations_prevent_skips(server: &QueryServer) {
1181        let curtime = duration_from_epoch_now();
1182
1183        let mut write_txn = server.write(curtime).await.unwrap();
1184
1185        let db_domain_version = write_txn
1186            .internal_search_uuid(UUID_DOMAIN_INFO)
1187            .expect("unable to access domain entry")
1188            .get_ava_single_uint32(Attribute::Version)
1189            .expect("Attribute Version not present");
1190
1191        assert_eq!(db_domain_version, DOMAIN_MIGRATION_FROM_INVALID);
1192
1193        drop(write_txn);
1194
1195        // MUST NOT SUCCEED.
1196        let err = server
1197            .initialise_helper(curtime, DOMAIN_TGT_LEVEL)
1198            .await
1199            .expect_err("Migration went ahead!!!!");
1200
1201        assert_eq!(err, OperationError::MG0008SkipUpgradeAttempted);
1202    }
1203
1204    #[qs_test(domain_level=DOMAIN_MIGRATION_FROM_MIN)]
1205    async fn test_migrations_skip_valid(server: &QueryServer) {
1206        let curtime = duration_from_epoch_now();
1207        // This is a smoke test that X -> Z migrations work for some range. This doesn't
1208        // absolve us of the need to write more detailed migration tests.
1209        let mut write_txn = server.write(curtime).await.unwrap();
1210
1211        let db_domain_version = write_txn
1212            .internal_search_uuid(UUID_DOMAIN_INFO)
1213            .expect("unable to access domain entry")
1214            .get_ava_single_uint32(Attribute::Version)
1215            .expect("Attribute Version not present");
1216
1217        assert_eq!(db_domain_version, DOMAIN_MIGRATION_FROM_MIN);
1218
1219        drop(write_txn);
1220
1221        // MUST SUCCEED.
1222        server
1223            .initialise_helper(curtime, DOMAIN_TGT_LEVEL)
1224            .await
1225            .expect("Migration failed!!!!")
1226    }
1227
1228    #[qs_test(domain_level=DOMAIN_LEVEL_11)]
1229    async fn test_migrations_dl11_dl12(server: &QueryServer) {
1230        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1231
1232        let db_domain_version = write_txn
1233            .internal_search_uuid(UUID_DOMAIN_INFO)
1234            .expect("unable to access domain entry")
1235            .get_ava_single_uint32(Attribute::Version)
1236            .expect("Attribute Version not present");
1237
1238        assert_eq!(db_domain_version, DOMAIN_LEVEL_11);
1239
1240        // Make a new person.
1241        let tuuid = Uuid::new_v4();
1242        let e1 = entry_init!(
1243            (Attribute::Class, EntryClass::Object.to_value()),
1244            (Attribute::Class, EntryClass::Person.to_value()),
1245            (Attribute::Class, EntryClass::Account.to_value()),
1246            (Attribute::Name, Value::new_iname("testperson1")),
1247            (Attribute::Uuid, Value::Uuid(tuuid)),
1248            (Attribute::Description, Value::new_utf8s("testperson1")),
1249            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
1250        );
1251
1252        write_txn
1253            .internal_create(vec![e1])
1254            .expect("Unable to create user");
1255
1256        let user = write_txn
1257            .internal_search_uuid(tuuid)
1258            .expect("Unable to load user");
1259
1260        // They still have an id verification key
1261        assert!(user.get_ava_set(Attribute::IdVerificationEcKey).is_some());
1262
1263        write_txn.commit().expect("Unable to commit");
1264
1265        // == pre migration verification. ==
1266        // check we currently would fail a migration.
1267
1268        // let mut read_txn = server.read().await.unwrap();
1269        // drop(read_txn);
1270
1271        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1272
1273        // Fix any issues
1274
1275        // == Increase the version ==
1276        write_txn
1277            .internal_apply_domain_migration(DOMAIN_LEVEL_12)
1278            .expect("Unable to set domain level to version 12");
1279
1280        // post migration verification.
1281        let user = write_txn
1282            .internal_search_uuid(tuuid)
1283            .expect("Unable to load user");
1284
1285        // The key has been removed.
1286        assert!(user.get_ava_set(Attribute::IdVerificationEcKey).is_none());
1287
1288        // New users don't get a key
1289        let t2uuid = Uuid::new_v4();
1290        let e2 = entry_init!(
1291            (Attribute::Class, EntryClass::Object.to_value()),
1292            (Attribute::Class, EntryClass::Person.to_value()),
1293            (Attribute::Class, EntryClass::Account.to_value()),
1294            (Attribute::Name, Value::new_iname("testperson2")),
1295            (Attribute::Uuid, Value::Uuid(t2uuid)),
1296            (Attribute::Description, Value::new_utf8s("testperson2")),
1297            (Attribute::DisplayName, Value::new_utf8s("testperson2"))
1298        );
1299
1300        write_txn
1301            .internal_create(vec![e2])
1302            .expect("Unable to create user");
1303
1304        let user = write_txn
1305            .internal_search_uuid(t2uuid)
1306            .expect("Unable to load user");
1307
1308        // No key!
1309        assert!(user.get_ava_set(Attribute::IdVerificationEcKey).is_none());
1310
1311        write_txn.commit().expect("Unable to commit");
1312    }
1313
1314    #[qs_test(domain_level=DOMAIN_LEVEL_12)]
1315    async fn test_migrations_dl12_dl13(server: &QueryServer) {
1316        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1317
1318        let db_domain_version = write_txn
1319            .internal_search_uuid(UUID_DOMAIN_INFO)
1320            .expect("unable to access domain entry")
1321            .get_ava_single_uint32(Attribute::Version)
1322            .expect("Attribute Version not present");
1323
1324        assert_eq!(db_domain_version, DOMAIN_LEVEL_12);
1325
1326        write_txn.commit().expect("Unable to commit");
1327
1328        // == pre migration verification. ==
1329        // check we currently would fail a migration.
1330
1331        // let mut read_txn = server.read().await.unwrap();
1332        // drop(read_txn);
1333
1334        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1335
1336        // Fix any issues
1337
1338        // == Increase the version ==
1339        write_txn
1340            .internal_apply_domain_migration(DOMAIN_LEVEL_13)
1341            .expect("Unable to set domain level to version 13");
1342
1343        // post migration verification.
1344
1345        write_txn.commit().expect("Unable to commit");
1346    }
1347
1348    #[qs_test(domain_level=DOMAIN_LEVEL_13)]
1349    async fn test_migrations_dl13_dl14(server: &QueryServer) {
1350        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1351
1352        let db_domain_version = write_txn
1353            .internal_search_uuid(UUID_DOMAIN_INFO)
1354            .expect("unable to access domain entry")
1355            .get_ava_single_uint32(Attribute::Version)
1356            .expect("Attribute Version not present");
1357
1358        assert_eq!(db_domain_version, DOMAIN_LEVEL_13);
1359
1360        // Create a person without pwd_changed_time
1361        let tuuid = Uuid::new_v4();
1362        let e1 = entry_init!(
1363            (Attribute::Class, EntryClass::Object.to_value()),
1364            (Attribute::Class, EntryClass::Person.to_value()),
1365            (Attribute::Class, EntryClass::Account.to_value()),
1366            (Attribute::Name, Value::new_iname("testperson1")),
1367            (Attribute::Uuid, Value::Uuid(tuuid)),
1368            (Attribute::Description, Value::new_utf8s("testperson1")),
1369            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
1370        );
1371
1372        write_txn
1373            .internal_create(vec![e1])
1374            .expect("Unable to create test person");
1375
1376        let user = write_txn
1377            .internal_search_uuid(tuuid)
1378            .expect("Unable to load test person");
1379
1380        // sanity check
1381        assert!(user
1382            .get_ava_single_datetime(Attribute::PasswordChangedTime)
1383            .is_none());
1384
1385        write_txn.commit().expect("Unable to commit");
1386
1387        // == pre migration verification. ==
1388        // check we currently would fail a migration.
1389
1390        // let mut read_txn = server.read().await.unwrap();
1391        // drop(read_txn);
1392
1393        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1394
1395        // Fix any issues
1396
1397        // == Increase the version ==
1398        write_txn
1399            .internal_apply_domain_migration(DOMAIN_LEVEL_14)
1400            .expect("Unable to set domain level to version 14");
1401
1402        // post migration verification.
1403        // pwd_changed_time should be defaulted to UNIX_EPOCH
1404        let user = write_txn
1405            .internal_search_uuid(tuuid)
1406            .expect("Unable to load test person after migration");
1407
1408        let pwd_changed = user
1409            .get_ava_single_datetime(Attribute::PasswordChangedTime)
1410            .expect("PasswordChangedTime should be set after DL13->DL14 migration");
1411
1412        assert_eq!(pwd_changed, time::OffsetDateTime::UNIX_EPOCH);
1413
1414        write_txn.commit().expect("Unable to commit");
1415    }
1416
1417    #[qs_test(domain_level=DOMAIN_LEVEL_14)]
1418    async fn test_migrations_dl14_dl1_11(server: &QueryServer) {
1419        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1420
1421        let db_domain_version = write_txn
1422            .internal_search_uuid(UUID_DOMAIN_INFO)
1423            .expect("unable to access domain entry")
1424            .get_ava_single_uint32(Attribute::Version)
1425            .expect("Attribute Version not present");
1426
1427        assert_eq!(db_domain_version, DOMAIN_LEVEL_14);
1428
1429        write_txn.commit().expect("Unable to commit");
1430
1431        // == pre migration verification. ==
1432        // check we currently would fail a migration.
1433
1434        // let mut read_txn = server.read().await.unwrap();
1435        // drop(read_txn);
1436
1437        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1438
1439        // Fix any issues
1440
1441        // == Increase the version ==
1442        write_txn
1443            .internal_apply_domain_migration(DOMAIN_LEVEL_1_11)
1444            .expect("Unable to set domain level to version 1_11");
1445
1446        // post migration verification.
1447
1448        // Assert all lingering schema db entries are removed.
1449
1450        let filter = filter!(f_and(vec![
1451            f_eq(Attribute::Class, EntryClass::ClassType.into()),
1452            f_eq(Attribute::Class, EntryClass::AttributeType.into()),
1453        ]));
1454
1455        let entries_remain = write_txn.internal_exists(&filter).unwrap();
1456        assert!(!entries_remain);
1457
1458        write_txn.commit().expect("Unable to commit");
1459    }
1460
1461    #[qs_test(domain_level=DOMAIN_LEVEL_1_11)]
1462    async fn test_migrations_dl1_11_to_dl_1_12(server: &QueryServer) {
1463        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1464
1465        let db_domain_version = write_txn
1466            .internal_search_uuid(UUID_DOMAIN_INFO)
1467            .expect("unable to access domain entry")
1468            .get_ava_single_uint32(Attribute::Version)
1469            .expect("Attribute Version not present");
1470
1471        assert_eq!(db_domain_version, DOMAIN_LEVEL_1_11);
1472
1473        write_txn.commit().expect("Unable to commit");
1474
1475        // == pre migration verification. ==
1476        // check we currently would fail a migration.
1477
1478        // let mut read_txn = server.read().await.unwrap();
1479        // drop(read_txn);
1480
1481        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
1482
1483        // Fix any issues
1484
1485        // == Increase the version ==
1486        write_txn
1487            .internal_apply_domain_migration(DOMAIN_LEVEL_1_12)
1488            .expect("Unable to set domain level to version 1_12");
1489
1490        // post migration verification.
1491
1492        write_txn.commit().expect("Unable to commit");
1493    }
1494}