///|
pub fn classify_change(before : Decision, after : Decision) -> ChangeKind {
match (before.kind, after.kind) {
(Denied, Allowed) => NewGrant
(Allowed, Denied) => RevokedGrant
(Allowed, Allowed) => UnchangedAllow
(Denied, Denied) => UnchangedDeny
_ => Inconclusive
}
}
///|
/// Every claim is scoped to the supplied request universe. Invalid input
/// returns an incomplete report and never silently asserts equivalence.
pub fn compare_policies(
before : AccessPolicy,
after : AccessPolicy,
universe : RequestUniverse,
) -> AuditReport {
let diagnostics : Array[Diagnostic] = []
for issue in validate_policy(before) {
diagnostics.push(issue)
}
for issue in validate_policy(after) {
diagnostics.push(issue)
}
for issue in validate_universe(universe) {
diagnostics.push(issue)
}
let changes : Array[DecisionChange] = []
if has_errors(diagnostics) {
return {
before_name: before.name,
after_name: after.name,
requests_considered: 0,
changes,
diagnostics,
complete: false,
}
}
for request in universe.requests {
let old_decision = evaluate_request(before, universe, request)
let new_decision = evaluate_request(after, universe, request)
changes.push({
request,
before: old_decision,
after: new_decision,
kind: classify_change(old_decision, new_decision),
})
}
{
before_name: before.name,
after_name: after.name,
requests_considered: universe.requests.length(),
changes,
diagnostics,
complete: true,
}
}
///|
pub fn count_changes(report : AuditReport, kind : ChangeKind) -> Int {
let mut count = 0
for change in report.changes {
if change.kind == kind {
count += 1
}
}
count
}
///|
pub fn find_witnesses(
report : AuditReport,
kind : ChangeKind,
) -> Array[DecisionChange] {
let witnesses : Array[DecisionChange] = []
for change in report.changes {
if change.kind == kind {
witnesses.push(change)
}
}
witnesses
}