///|
fn same_rule_scope(a : AccessRule, b : AccessRule) -> Bool {
a.roles == b.roles &&
a.actions == b.actions &&
a.resource_kinds == b.resource_kinds &&
a.resource_ids == b.resource_ids &&
a.tenant_relation == b.tenant_relation &&
a.conditions == b.conditions
}
///|
fn broad_selector(values : Array[String]) -> Bool {
values.length() == 0 || values.contains("*")
}
///|
fn rule_is_broad_permit(rule : AccessRule) -> Bool {
rule.effect is Permit &&
rule.tenant_relation is AnyTenant &&
broad_selector(rule.roles) &&
broad_selector(rule.actions) &&
broad_selector(rule.resource_kinds) &&
broad_selector(rule.resource_ids) &&
rule.conditions.length() == 0
}
///|
/// Static and sample-scoped lint. Lint warnings do not change evaluation.
pub fn lint_policy(
policy : AccessPolicy,
universe : RequestUniverse,
) -> Array[Diagnostic] {
let issues : Array[Diagnostic] = []
for rule in policy.rules {
if rule_is_broad_permit(rule) {
issues.push({
code: "L001",
severity: Warning,
message: "rule \{rule.id} permits every supplied role, action, resource, and tenant",
})
} else if rule.effect is Permit &&
rule.tenant_relation is AnyTenant &&
broad_selector(rule.resource_ids) {
issues.push({
code: "L002",
severity: Info,
message: "rule \{rule.id} may grant across tenants",
})
}
}
for i in 0.. Int {
let mut count = 0
for issue in issues {
if issue.severity == severity {
count += 1
}
}
count
}
///|
pub fn render_lint(issues : Array[Diagnostic]) -> String {
let out = StringBuilder()
out.write_string("Policy lint\n")
out.write_string("Warnings: \{count_lint(issues, Warning)}\n")
for issue in issues {
out.write_string("\{issue.code}: \{issue.message}\n")
}
out.to_string()
}