///|
pub(all) enum RuleDeltaKind {
AddedRule
RemovedRule
ModifiedRule
UnchangedRule
} derive(Eq, Debug)
///|
pub(all) struct RuleDelta {
rule_id : String
kind : RuleDeltaKind
} derive(Debug)
///|
pub(all) struct StructuralDiff {
rules : Array[RuleDelta]
added_role_edges : Array[RoleInheritance]
removed_role_edges : Array[RoleInheritance]
} derive(Debug)
///|
fn has_edge(edges : Array[RoleInheritance], target : RoleInheritance) -> Bool {
for edge in edges {
if edge.child == target.child && edge.parent == target.parent {
return true
}
}
false
}
///|
/// Lists syntactic policy changes by stable rule ID and directed role edge.
/// It complements, but cannot replace, behavioral comparison on requests.
pub fn structural_diff(
before : AccessPolicy,
after : AccessPolicy,
) -> StructuralDiff {
let rules : Array[RuleDelta] = []
for old_rule in before.rules {
match find_rule(after, old_rule.id) {
None => rules.push({ rule_id: old_rule.id, kind: RemovedRule, })
Some(new_rule) =>
if new_rule == old_rule {
rules.push({ rule_id: old_rule.id, kind: UnchangedRule, })
} else {
rules.push({ rule_id: old_rule.id, kind: ModifiedRule, })
}
}
}
for new_rule in after.rules {
if find_rule(before, new_rule.id) is None {
rules.push({ rule_id: new_rule.id, kind: AddedRule, })
}
}
let added_role_edges : Array[RoleInheritance] = []
let removed_role_edges : Array[RoleInheritance] = []
for edge in after.role_inheritance {
if !has_edge(before.role_inheritance, edge) {
added_role_edges.push(edge)
}
}
for edge in before.role_inheritance {
if !has_edge(after.role_inheritance, edge) {
removed_role_edges.push(edge)
}
}
{ rules, added_role_edges, removed_role_edges, }
}
///|
fn delta_name(kind : RuleDeltaKind) -> String {
match kind {
AddedRule => "ADDED"
RemovedRule => "REMOVED"
ModifiedRule => "MODIFIED"
UnchangedRule => "UNCHANGED"
}
}
///|
pub fn render_structural_diff(diff : StructuralDiff) -> String {
let out = StringBuilder()
out.write_string("Structural policy changes\n")
for rule in diff.rules {
if !(rule.kind is UnchangedRule) {
out.write_string("\{delta_name(rule.kind)} rule \{rule.rule_id}\n")
}
}
for edge in diff.added_role_edges {
out.write_string("ADDED role edge \{edge.child} -> \{edge.parent}\n")
}
for edge in diff.removed_role_edges {
out.write_string("REMOVED role edge \{edge.child} -> \{edge.parent}\n")
}
out.to_string()
}