///|
pub(all) enum Permission {
App
FileSystemRead
FileSystemWrite
FileDialog
Network
Localhost
DeepLink
SingleInstance
Menu
Tray
AutoLaunch
Window
WindowState
Updater
ServiceDiscovery
Shell
Opener
Dialog
Clipboard
Notification
ProcessInfo
ProcessEnvironment
ProcessControl
Custom(String)
} derive(Debug, Eq)
///|
pub fn Permission::command(route : String) -> Permission {
Custom(route)
}
///|
pub fn Permission::named(name : String) -> Result[Permission, String] {
match name {
"app" => Ok(App)
"filesystem.read" => Ok(FileSystemRead)
"filesystem.write" => Ok(FileSystemWrite)
"file-dialog" => Ok(FileDialog)
"network" => Ok(Network)
"localhost" => Ok(Localhost)
"deep-link" => Ok(DeepLink)
"single-instance" => Ok(SingleInstance)
"menu" => Ok(Menu)
"tray" => Ok(Tray)
"auto-launch" => Ok(AutoLaunch)
"window" => Ok(Window)
"window-state" => Ok(WindowState)
"updater" => Ok(Updater)
"service-discovery" => Ok(ServiceDiscovery)
"shell" => Ok(Shell)
"opener" => Ok(Opener)
"dialog" => Ok(Dialog)
"clipboard" => Ok(Clipboard)
"notification" => Ok(Notification)
"process.info" => Ok(ProcessInfo)
"process.environment" => Ok(ProcessEnvironment)
"process.control" => Ok(ProcessControl)
_ =>
if name.has_prefix("custom:") {
let value = name[7:].to_owned()
if value == "" {
Err("custom permission name is required")
} else {
Ok(Custom(value))
}
} else {
Err("unknown permission: \{name}")
}
}
}
///|
pub fn Permission::name(self : Permission) -> String {
match self {
App => "app"
FileSystemRead => "filesystem.read"
FileSystemWrite => "filesystem.write"
FileDialog => "file-dialog"
Network => "network"
Localhost => "localhost"
DeepLink => "deep-link"
SingleInstance => "single-instance"
Menu => "menu"
Tray => "tray"
AutoLaunch => "auto-launch"
Window => "window"
WindowState => "window-state"
Updater => "updater"
ServiceDiscovery => "service-discovery"
Shell => "shell"
Opener => "opener"
Dialog => "dialog"
Clipboard => "clipboard"
Notification => "notification"
ProcessInfo => "process.info"
ProcessEnvironment => "process.environment"
ProcessControl => "process.control"
Custom(value) => "custom:\{value}"
}
}
///|
pub struct Capability {
name : String
windows : Array[String]
origins : Array[String]
platforms : Array[String]
permissions : Array[Permission]
operation_scopes : Array[OperationScope]
} derive(Debug, Eq)
///|
pub fn Capability::new(name : String) -> Capability {
{
name,
windows: [],
origins: [],
platforms: [],
permissions: [],
operation_scopes: [],
}
}
///|
pub fn Capability::window(self : Capability, label : String) -> Capability {
let windows = self.windows.copy()
windows.push(label)
{ ..self, windows, }
}
///|
pub fn Capability::origin(self : Capability, origin : String) -> Capability {
let origins = self.origins.copy()
origins.push(origin)
{ ..self, origins, }
}
///|
pub fn Capability::platform(self : Capability, platform : String) -> Capability {
let platforms = self.platforms.copy()
platforms.push(platform)
{ ..self, platforms, }
}
///|
pub fn Capability::permission(
self : Capability,
permission : Permission,
) -> Capability {
let permissions = self.permissions.copy()
permissions.push(permission)
{ ..self, permissions, }
}
///|
pub fn Capability::command(self : Capability, route : String) -> Capability {
self.permission(Permission::command(route))
}
///|
pub fn Capability::operation_scope(
self : Capability,
scope : OperationScope,
) -> Capability {
let operation_scopes = self.operation_scopes.copy()
operation_scopes.push(scope)
{ ..self, operation_scopes, }
}
///|
pub fn Capability::path_scope(
self : Capability,
root~ : String,
writable? : Bool = false,
) -> Capability {
self.operation_scope(OperationScope::path(root~, writable~))
}
///|
pub fn Capability::shell_scope(
self : Capability,
command : String,
) -> Capability {
self.operation_scope(OperationScope::shell(command))
}
///|
pub fn Capability::url_scope(self : Capability, origin : String) -> Capability {
self.operation_scope(OperationScope::url(origin))
}
///|
pub fn Capability::allows(
self : Capability,
window_label~ : String,
permission~ : Permission,
origin? : String = "",
platform? : String = "",
) -> Bool {
let window_allowed = self.windows.is_empty() ||
self.windows.contains(window_label)
let origin_allowed = self.origins.is_empty() || self.origins.contains(origin)
let platform_allowed = self.platforms.is_empty() ||
self.platforms.contains(platform)
window_allowed &&
origin_allowed &&
platform_allowed &&
self.permissions.contains(permission)
}
///|
pub fn Capability::allows_operation(
self : Capability,
window_label~ : String,
permission~ : Permission,
scope~ : OperationScope,
origin? : String = "",
platform? : String = "",
) -> Bool {
self.allows(window_label~, permission~, origin~, platform~) &&
self.operation_scopes.any(fn(allowed) { allowed.allows(scope) })
}
///|
pub fn Capability::name(self : Capability) -> String {
self.name
}
///|
pub fn Capability::permissions(self : Capability) -> Array[Permission] {
self.permissions.copy()
}
///|
pub fn Capability::windows(self : Capability) -> Array[String] {
self.windows.copy()
}
///|
pub fn Capability::origins(self : Capability) -> Array[String] {
self.origins.copy()
}
///|
pub fn Capability::platforms(self : Capability) -> Array[String] {
self.platforms.copy()
}
///|
pub fn Capability::operation_scopes(self : Capability) -> Array[OperationScope] {
self.operation_scopes.copy()
}
///|
pub fn Capability::operation_scopes_by_kind(
self : Capability,
kind : OperationScopeKind,
) -> Array[OperationScope] {
self.operation_scopes.filter(fn(scope) { scope.kind() == kind })
}
///|
pub fn Capability::validate(self : Capability) -> Array[String] {
let problems : Array[String] = []
if self.name == "" {
problems.push("capability name is required")
}
if self.permissions.is_empty() {
problems.push("capability must include at least one permission")
}
for origin in self.origins {
if origin == "" {
problems.push("capability origin must not be empty")
}
}
for platform in self.platforms {
if platform == "" {
problems.push("capability platform must not be empty")
}
}
let seen_scopes : Array[String] = []
for scope in self.operation_scopes {
for problem in scope.validate() {
problems.push(problem)
}
let fingerprint = scope.fingerprint()
if seen_scopes.contains(fingerprint) {
problems.push("capability operation scope must be unique: \{fingerprint}")
} else {
seen_scopes.push(fingerprint)
}
}
problems
}