///|
pub(all) struct ScopeCoverage {
possible_requests : Int
represented_requests : Int
missing_requests : Int
missing_examples : Array[AccessRequest]
complete : Bool
diagnostics : Array[Diagnostic]
} derive(Debug)
///|
fn request_triple_exists(
universe : RequestUniverse,
principal_id : String,
action : String,
resource_id : String,
) -> Bool {
for request in universe.requests {
if request.principal_id == principal_id &&
request.action == action &&
request.resource_id == resource_id {
return true
}
}
false
}
///|
/// Measures coverage of principal × action × resource triples. Attribute
/// valuations are intentionally excluded; a represented triple may still
/// omit other important attribute combinations.
pub fn analyze_scope(
universe : RequestUniverse,
actions : Array[String],
max_combinations : Int,
) -> ScopeCoverage {
let diagnostics : Array[Diagnostic] = []
let examples : Array[AccessRequest] = []
if max_combinations < 1 || max_combinations > 100000 {
diagnostics.push({
code: "S001",
severity: Error,
message: "scope limit must be between 1 and 100000",
})
}
if actions.length() == 0 ||
universe.principals.length() == 0 ||
universe.resources.length() == 0 {
diagnostics.push({
code: "S002",
severity: Error,
message: "scope needs principals, resources, and declared actions",
})
}
let unique_actions : Array[String] = []
for action in actions {
if !is_valid_identifier(action) {
diagnostics.push({
code: "S003",
severity: Error,
message: "invalid declared action \{action}",
})
} else if !unique_actions.contains(action) {
unique_actions.push(action)
}
}
if has_errors(diagnostics) {
return {
possible_requests: 0,
represented_requests: 0,
missing_requests: 0,
missing_examples: examples,
complete: false,
diagnostics,
}
}
let mut possible = 0
let mut represented = 0
let mut missing = 0
for principal in universe.principals {
for action in unique_actions {
for resource in universe.resources {
if possible >= max_combinations {
diagnostics.push({
code: "S004",
severity: Error,
message: "scope exceeds configured combination limit",
})
return {
possible_requests: possible,
represented_requests: represented,
missing_requests: missing,
missing_examples: examples,
complete: false,
diagnostics,
}
}
possible += 1
if request_triple_exists(universe, principal.id, action, resource.id) {
represented += 1
} else {
missing += 1
if examples.length() < 10 {
examples.push({
principal_id: principal.id,
action,
resource_id: resource.id,
attributes: [],
})
}
}
}
}
}
{
possible_requests: possible,
represented_requests: represented,
missing_requests: missing,
missing_examples: examples,
complete: true,
diagnostics,
}
}
///|
pub fn render_scope(scope : ScopeCoverage) -> String {
let out = StringBuilder()
out.write_string("Scope calculation complete: \{scope.complete}\n")
out.write_string("Possible triples: \{scope.possible_requests}\n")
out.write_string("Represented triples: \{scope.represented_requests}\n")
out.write_string("Missing triples: \{scope.missing_requests}\n")
out.write_string("Attribute combinations are not enumerated by this check.\n")
for example in scope.missing_examples {
out.write_string(
"Missing: \{example.principal_id} \{example.action} \{example.resource_id}\n",
)
}
for issue in scope.diagnostics {
out.write_string("\{issue.code}: \{issue.message}\n")
}
out.to_string()
}