Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 70 additions & 8 deletions crates/ogar-dir-sim/src/change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,39 @@
//! replace (`from`): applying it where `from` no longer holds is refused, and
//! the same expectation becomes a plan operation's [`Precondition`](crate::Precondition).

use ogar_dir_core::Guid128;
use ogar_dir_core::{Guid128, OuHhtl};

/// Kind of a directory node.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NodeKind {
/// User.
User,
/// Group.
Group,
}

/// The canonical semantic fields of one node — exactly what a snapshot row
/// holds, nothing else. A created node carries it as its content; a deleted
/// node carries it as the compare-and-set expectation (delete what was
/// read, never whatever is there now).
///
/// `active` is the single enabled flag as observed by the one source the
/// node came from (AD: `userAccountControl` ACCOUNTDISABLE clear). How AD
/// and Entra enabled state combine is not settled; see
/// `docs/DIRECTORY-SIMULATION-POC.md` V4.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeState {
/// Kind.
pub kind: NodeKind,
/// Enabled. Users only: groups have no enabled flag and carry `true`.
pub active: bool,
/// Raw UPN.
pub upn: Option<String>,
/// Raw primary SMTP.
pub primary_smtp: Option<String>,
/// OU location (numeric HHTL, never a DN), if known.
pub ou: Option<OuHhtl>,
}

/// Attribute a change can set.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand All @@ -16,22 +48,38 @@ pub enum Attribute {

/// One semantic change. `Ord` is total, so a sorted change list is a
/// canonical form (used for determinism).
///
/// Variant order is load-bearing: a sorted list is also a safe application
/// order, respecting every dependency between kinds of change:
///
/// 1. `RemoveMembership` — a node's edges go before the node;
/// 2. `DeleteNode` — frees its UPN / SMTP before anything claims them;
/// 3. `SetAttribute` — renames away from (or into) addresses only after
/// deletes freed them and before creates take them;
/// 4. `CreateNode` — after every address it needs is free;
/// 5. `AddMembership` — once both endpoints exist.
///
/// Within step 3, the plan additionally orders a chain of renames (one
/// object releases the value the next claims). A cycle of renames (two
/// nodes swapping an address) needs a temporary value, which no change list
/// carries yet.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Change {
/// `user` becomes a member of `group`.
AddMembership {
/// Member.
user: Guid128,
/// Group.
group: Guid128,
},
/// `user` stops being a member of `group`.
RemoveMembership {
/// Member.
user: Guid128,
/// Group.
group: Guid128,
},
/// A node stops existing. Refused while it still has a membership: the
/// change list must remove those edges itself, so none is stranded.
DeleteNode {
/// Identity.
node: Guid128,
/// The state the deletion expects to remove (compare-and-set).
state: NodeState,
},
/// Compare-and-set of one attribute.
SetAttribute {
/// Object.
Expand All @@ -43,6 +91,20 @@ pub enum Change {
/// New value (raw).
to: Option<String>,
},
/// A node that did not exist comes into existence.
CreateNode {
/// Identity.
node: Guid128,
/// Its content.
state: NodeState,
},
/// `user` becomes a member of `group`.
AddMembership {
/// Member.
user: Guid128,
/// Group.
group: Guid128,
},
}

/// Comparison form of a UPN / SMTP address: trimmed and lowercased with
Expand Down
2 changes: 1 addition & 1 deletion crates/ogar-dir-sim/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub mod plan;
pub mod provenance;
pub mod violation;

pub use change::{Attribute, Change, normalize};
pub use change::{Attribute, Change, NodeKind, NodeState, normalize};
pub use plan::{ExecutionPlan, Operation, PlanError, PlannedOp, Precondition};
pub use provenance::{EvidenceRef, Origin, RuleId, TAG_DESIRED, TAG_OBSERVED, Version, VersionId};
pub use violation::{Endpoint, Violation};
Loading
Loading