///|
pub struct CapabilityDecision {
allowed : Bool
window_label : String
origin : String
platform : String
permission : Permission
capability : String
reason : String
} derive(Debug, Eq)
///|
fn CapabilityDecision::allow(
window_label~ : String,
origin~ : String,
platform~ : String,
permission~ : Permission,
capability~ : String,
) -> CapabilityDecision {
{
allowed: true,
window_label,
origin,
platform,
permission,
capability,
reason: "",
}
}
///|
fn CapabilityDecision::deny(
window_label~ : String,
origin~ : String,
platform~ : String,
permission~ : Permission,
reason~ : String,
) -> CapabilityDecision {
{
allowed: false,
window_label,
origin,
platform,
permission,
capability: "",
reason,
}
}
///|
pub fn CapabilityDecision::allowed(self : CapabilityDecision) -> Bool {
self.allowed
}
///|
pub fn CapabilityDecision::denied(self : CapabilityDecision) -> Bool {
!self.allowed
}
///|
pub fn CapabilityDecision::window_label(self : CapabilityDecision) -> String {
self.window_label
}
///|
pub fn CapabilityDecision::origin(self : CapabilityDecision) -> String {
self.origin
}
///|
pub fn CapabilityDecision::platform(self : CapabilityDecision) -> String {
self.platform
}
///|
pub fn CapabilityDecision::permission(self : CapabilityDecision) -> Permission {
self.permission
}
///|
pub fn CapabilityDecision::capability(self : CapabilityDecision) -> String {
self.capability
}
///|
pub fn CapabilityDecision::reason(self : CapabilityDecision) -> String {
self.reason
}
///|
pub fn CapabilityDecision::to_json(self : CapabilityDecision) -> String {
[
"{",
"\"allowed\":\{capability_bool_json(self.allowed)},",
"\"windowLabel\":\{self.window_label.json_string()},",
"\"origin\":\{self.origin.json_string()},",
"\"platform\":\{self.platform.json_string()},",
"\"permission\":\{self.permission.name().json_string()},",
"\"capability\":\{self.capability.json_string()},",
"\"reason\":\{self.reason.json_string()}",
"}",
].join("")
}
///|
pub struct CapabilityPolicy {
capabilities : Array[Capability]
} derive(Debug, Eq)
///|
pub fn CapabilityPolicy::new(
capabilities? : Array[Capability] = [],
) -> CapabilityPolicy {
{ capabilities: capabilities.copy() }
}
///|
pub fn CapabilityPolicy::capabilities(
self : CapabilityPolicy,
) -> Array[Capability] {
self.capabilities.copy()
}
///|
pub fn CapabilityPolicy::allows(
self : CapabilityPolicy,
window_label~ : String,
permission~ : Permission,
origin? : String = "",
platform? : String = "",
) -> Bool {
self.evaluate(window_label~, permission~, origin~, platform~).allowed()
}
///|
pub fn CapabilityPolicy::allows_window(
self : CapabilityPolicy,
window_label~ : String,
permission~ : Permission,
platform? : String = "",
) -> Bool {
for capability in self.capabilities {
let window_allowed = capability.windows().is_empty() ||
capability.windows().contains(window_label)
let platform_allowed = capability.platforms().is_empty() ||
capability.platforms().contains(platform)
if window_allowed &&
platform_allowed &&
capability.permissions().contains(permission) {
return true
}
}
false
}
///|
pub fn CapabilityPolicy::evaluate(
self : CapabilityPolicy,
window_label~ : String,
permission~ : Permission,
origin? : String = "",
platform? : String = "",
) -> CapabilityDecision {
for capability in self.capabilities {
if capability.allows(window_label~, permission~, origin~, platform~) {
return CapabilityDecision::allow(
window_label~,
origin~,
platform~,
permission~,
capability=capability.name(),
)
}
}
CapabilityDecision::deny(
window_label~,
origin~,
platform~,
permission~,
reason=capability_deny_reason(window_label, origin, platform, permission),
)
}
///|
pub fn CapabilityPolicy::allows_operation(
self : CapabilityPolicy,
window_label~ : String,
permission~ : Permission,
scope~ : OperationScope,
origin? : String = "",
platform? : String = "",
) -> Bool {
self
.evaluate_operation(window_label~, permission~, scope~, origin~, platform~)
.allowed()
}
///|
pub fn CapabilityPolicy::evaluate_operation(
self : CapabilityPolicy,
window_label~ : String,
permission~ : Permission,
scope~ : OperationScope,
origin? : String = "",
platform? : String = "",
) -> CapabilityDecision {
let mut grant_allowed = false
for capability in self.capabilities {
if capability.allows(window_label~, permission~, origin~, platform~) {
grant_allowed = true
if capability.allows_operation(
window_label~,
permission~,
scope~,
origin~,
platform~,
) {
return CapabilityDecision::allow(
window_label~,
origin~,
platform~,
permission~,
capability=capability.name(),
)
}
}
}
let reason = if grant_allowed {
operation_scope_deny_reason(
window_label, origin, platform, permission, scope,
)
} else {
capability_deny_reason(window_label, origin, platform, permission)
}
CapabilityDecision::deny(
window_label~,
origin~,
platform~,
permission~,
reason~,
)
}
///|
pub fn CapabilityPolicy::allowed_origins(
self : CapabilityPolicy,
window_label~ : String,
permission~ : Permission,
platform? : String = "",
) -> Array[String] {
let origins : Array[String] = []
for capability in self.capabilities {
let window_allowed = capability.windows().is_empty() ||
capability.windows().contains(window_label)
let platform_allowed = capability.platforms().is_empty() ||
capability.platforms().contains(platform)
if window_allowed &&
platform_allowed &&
capability.permissions().contains(permission) {
let capability_origins = capability.origins()
if capability_origins.is_empty() {
return []
}
for origin in capability_origins {
if !origins.contains(origin) {
origins.push(origin)
}
}
}
}
origins
}
///|
pub fn CapabilityPolicy::validate(self : CapabilityPolicy) -> Array[String] {
let problems : Array[String] = []
let seen : Array[String] = []
for capability in self.capabilities {
for problem in capability.validate() {
problems.push(problem)
}
if capability.name() != "" {
if seen.contains(capability.name()) {
problems.push("capability name must be unique: \{capability.name()}")
} else {
seen.push(capability.name())
}
}
}
problems
}
///|
pub fn CapabilityPolicy::to_json(self : CapabilityPolicy) -> String {
[
"{",
"\"capabilities\":[\{self.capabilities.map(fn(capability) { capability_policy_capability_json(capability) }).join(",")}]",
"}",
].join("")
}
///|
fn capability_deny_reason(
window_label : String,
origin : String,
platform : String,
permission : Permission,
) -> String {
let platform_text = if platform == "" { "" } else { " platform \{platform}" }
if origin == "" {
"permission denied for window \{window_label}\{platform_text}: \{permission.name()}"
} else {
"permission denied for window \{window_label} origin \{origin}\{platform_text}: \{permission.name()}"
}
}
///|
fn capability_policy_capability_json(capability : Capability) -> String {
[
"{",
"\"name\":\{capability.name().json_string()},",
"\"windows\":[\{capability.windows().map(fn(window) { window.json_string() }).join(",")}],",
"\"origins\":[\{capability.origins().map(fn(origin) { origin.json_string() }).join(",")}],",
"\"platforms\":[\{capability.platforms().map(fn(platform) { platform.json_string() }).join(",")}],",
"\"permissions\":[\{capability.permissions().map(fn(permission) { permission.name().json_string() }).join(",")}],",
"\"operationScopes\":[\{capability.operation_scopes().map(fn(scope) { scope.to_json() }).join(",")}]",
"}",
].join("")
}
///|
fn operation_scope_deny_reason(
window_label : String,
origin : String,
platform : String,
permission : Permission,
scope : OperationScope,
) -> String {
let target = "\{scope.kind().name()}:\{scope.value()}"
let platform_text = if platform == "" { "" } else { " platform \{platform}" }
if origin == "" {
"operation scope denied for window \{window_label}\{platform_text}: \{permission.name()} \{target}"
} else {
"operation scope denied for window \{window_label} origin \{origin}\{platform_text}: \{permission.name()} \{target}"
}
}
///|
fn capability_bool_json(value : Bool) -> String {
if value {
"true"
} else {
"false"
}
}