// Permission system for role-based access control. // // Roles follow the format: `domain.category:action` // - Domain: Feature area (control-room, media, etc.) // - Category: Sub-area within domain (default: general) // - Action: Permission level (viewer < user < editor < admin) /// Permission domains matching feature areas. enum Domain { controlRoom('control-room'), library('library'), media('media'), ai('ai'), housekeeper('housekeeper'), developer('developer'), documents('documents'), gaming('gaming'), admin('admin'); const Domain(this.value); /// The API string value for this domain. final String value; /// Parse a domain string from API response. static Domain? fromString(String value) { for (final domain in Domain.values) { if (domain.value == value) return domain; } return null; } } /// Permission actions in hierarchical order. /// /// Higher actions imply lower ones: /// - admin implies editor, user, viewer /// - editor implies user, viewer /// - user implies viewer enum Action { viewer(1), user(2), editor(3), admin(4); const Action(this.level); /// Numeric level for comparison (higher = more permissions). final int level; /// Check if this action grants at least the required action. bool grants(Action required) => level >= required.level; /// Parse an action string from API response. static Action? fromString(String value) { for (final action in Action.values) { if (action.name == value) return action; } return null; } } /// A permission role assigned to a user. /// /// Roles are parsed from the API format: `domain.category:action` class Role { const Role({ required this.id, required this.name, required this.domain, required this.category, required this.action, }); /// Unique role ID. final String id; /// Full role name (e.g., "control-room.general:admin"). final String name; /// Permission domain. final Domain domain; /// Permission category (usually "general"). final String category; /// Permission action level. final Action action; /// Check if this role grants access for the given domain and action. /// /// Global admin (`admin.general:admin`) grants access to everything. /// Otherwise, domain and category must match, and action level must be sufficient. bool grants(Domain domain, Action action, {String category = 'general'}) { // Global admin override if (this.domain == Domain.admin && this.category == 'general' && this.action == Action.admin) { return true; } // Check domain and category match if (this.domain != domain || this.category != category) { return false; } // Check action hierarchy return this.action.grants(action); } @override String toString() => 'Role($name)'; @override bool operator ==(Object other) => identical(this, other) || other is Role && runtimeType == other.runtimeType && id == other.id; @override int get hashCode => id.hashCode; } /// Extension for checking permissions on a list of roles. extension RoleListPermissions on List { /// Check if any role grants the required permission. bool hasPermission(Domain domain, Action action, {String category = 'general'}) { return any((role) => role.grants(domain, action, category: category)); } /// Check if any role grants any of the required permissions. bool hasAnyPermission(List<(Domain, Action)> permissions) { return permissions.any((p) => hasPermission(p.$1, p.$2)); } /// Check if user is a global admin. bool get isGlobalAdmin => hasPermission(Domain.admin, Action.admin); }