syntax = "proto3"; package chatto.admin.v1; import "buf/validate/validate.proto"; import "chatto/api/v1/roles.proto"; // Trinary permission decision used by permission matrices and write requests. enum PermissionDecision { // Unspecified decision; rejected for writes. PERMISSION_DECISION_UNSPECIFIED = 0; // Explicit allow. PERMISSION_DECISION_ALLOW = 1; // Explicit deny. PERMISSION_DECISION_DENY = 2; // No explicit decision; clears the permission state on writes. PERMISSION_DECISION_NONE = 3; } // Scope tier for permission matrices and write requests. enum PermissionScopeKind { // Unspecified scope; treated as server scope where older clients omit it. PERMISSION_SCOPE_KIND_UNSPECIFIED = 0; // Server tier. PERMISSION_SCOPE_KIND_SERVER = 1; // Room-group tier. PERMISSION_SCOPE_KIND_GROUP = 2; // Room tier. PERMISSION_SCOPE_KIND_ROOM = 3; } // Permission scope reference. message PermissionScope { // Scope tier. PermissionScopeKind kind = 1 [(buf.validate.field).enum.defined_only = true]; // Scope ID for group or room scope. Empty for server scope. string id = 2; } // Permission grants and denials at a single tier. message TierPermissions { // Explicitly granted permissions. repeated string permissions = 1; // Explicitly denied permissions. repeated string permission_denials = 2; } // One role's permission state at a single tier. message TierRole { reserved 1 to 5; reserved "role_name", "display_name", "description", "is_system", "position"; // Explicit state at this tier. TierPermissions override = 6; // Allows inherited from broader tiers. repeated string inherited_allows = 7; // Denials inherited from broader tiers. repeated string inherited_denials = 8; // Public role metadata. chatto.api.v1.Role role = 9; } // Role permission matrix for one tier. message TierRoles { // Permissions configurable at this tier. repeated string applicable_permissions = 1; // Roles sorted by position. repeated TierRole roles = 2; } // Request a role-permission tier matrix. message GetRolePermissionTierMatrixRequest { // Target scope. Omit or use SERVER for server scope. PermissionScope scope = 1; } // Response containing a role-permission tier matrix. message GetRolePermissionTierMatrixResponse { // Requested matrix. TierRoles matrix = 1; } // Matrix column scope. message PermissionMatrixScope { // Stable scope ID for the server, a group, or a room. string id = 1; // Human-readable label. string label = 2; // Scope tier. PermissionScopeKind kind = 3; // Parent group ID for room scopes. string parent_group_id = 4; } // Matrix cell for one permission and scope. message PermissionMatrixCell { // Permission identifier. string permission = 1; // Scope ID matching PermissionMatrixScope.id. string scope_id = 2; // Explicit decision at this scope. PermissionDecision override = 3; // Effective decision at this scope. PermissionDecision effective = 4; // Whether an explicit allow may currently be stored for this target. Absent // when no additional delegation ceiling applies. optional bool allow_permitted = 5; } // Permission matrix for one role across all scopes. message RolePermissionMatrix { // Role name. string role_name = 1; // Permissions rendered as rows. repeated string applicable_permissions = 2; // Scopes rendered as columns. repeated PermissionMatrixScope scopes = 3; // Sparse matrix cells. repeated PermissionMatrixCell cells = 4; } // Request one role's permission matrix. message GetRolePermissionMatrixRequest { // Stable role name. string role_name = 1 [(buf.validate.field).string.min_len = 1]; } // Response containing one role's permission matrix. message GetRolePermissionMatrixResponse { // Requested matrix. RolePermissionMatrix matrix = 1; } // Permission matrix for one user across all scopes. message UserPermissionMatrix { // User ID. string user_id = 1; // Permissions rendered as rows. repeated string applicable_permissions = 2; // Scopes rendered as columns. repeated PermissionMatrixScope scopes = 3; // Sparse matrix cells. repeated PermissionMatrixCell cells = 4; } // One resource-oriented permission decision at a concrete scope. // // This is the row-oriented counterpart to PermissionMatrixCell. Prefer this // shape when building integrations that do not need matrix column joins. message ScopedPermissionDecision { // Permission identifier. string permission = 1; // Server, group, or room scope this decision applies to. PermissionScope scope = 2; // Explicit decision stored at this scope. PermissionDecision override = 3; // Effective decision after inherited broader scopes are applied. PermissionDecision effective = 4; } // Stored permission decision returned after a write. message PermissionDecisionUpdate { // Permission identifier. string permission = 1; // Server, group, or room scope this decision applies to. PermissionScope scope = 2; // Stored decision after the write. NONE means no explicit decision remains. PermissionDecision decision = 3; } // Request explicit/effective permission decisions for one role. message ListRolePermissionDecisionsRequest { // Stable role name. string role_name = 1 [(buf.validate.field).string.min_len = 1]; } // Resource-oriented permission decisions for one role. message ListRolePermissionDecisionsResponse { // Stable role name. string role_name = 1; // Permission decisions keyed by permission and typed scope. repeated ScopedPermissionDecision decisions = 2; } // Request explicit/effective permission decisions for one user. message ListUserPermissionDecisionsRequest { // User ID. string user_id = 1 [(buf.validate.field).string.min_len = 1]; } // Resource-oriented permission decisions for one user. message ListUserPermissionDecisionsResponse { // User ID. string user_id = 1; // Permission decisions keyed by permission and typed scope. repeated ScopedPermissionDecision decisions = 2; } // Level at which a permission explanation found a decision. enum PermissionDecisionLevel { // No decision level was reached. PERMISSION_DECISION_LEVEL_UNSPECIFIED = 0; // Server-level role or user decision. PERMISSION_DECISION_LEVEL_SERVER = 1; // Room-group-level role or user decision. PERMISSION_DECISION_LEVEL_GROUP = 2; // Room-level role or user decision. PERMISSION_DECISION_LEVEL_ROOM = 3; } // One explicit decision encountered while explaining a permission. message PermissionTraceEntry { // Level at which this decision was observed. PermissionDecisionLevel level = 1; // Role name, user ID, or policy marker that produced the decision. string role_name = 2; // Decision observed at this trace step. PermissionDecision decision = 3; // Whether this entry is the winning decision. bool applied = 4; } // Full decision trace for one permission and target user. message PermissionExplanation { // Permission identifier. string permission = 1; // Overall decision after resolving named subjects and the everyone baseline. PermissionDecision state = 2; // Level of the winning decision, when state is not NONE. PermissionDecisionLevel decided_at = 3; // Role name, user ID, or policy marker that produced the winning decision. string decided_by_role = 4; // Ordered decision trace. The first entry is the winning decision. repeated PermissionTraceEntry trace = 5; // Permission whose effective allow includes this permission. Empty when the // permission was resolved directly. string included_by_permission = 6; } // Request permission explanations for a user at server or room scope. message ExplainPermissionsRequest { // User whose permissions should be explained. string user_id = 1 [(buf.validate.field).string.min_len = 1]; // Optional room scope. Omit for server-scoped permissions. string room_id = 2; } // Permission explanations for all permissions applicable at the requested scope. message ExplainPermissionsResponse { // Explanation rows sorted by permission metadata order. repeated PermissionExplanation explanations = 1; } // Request one user's permission matrix. message GetUserPermissionMatrixRequest { // User ID. string user_id = 1 [(buf.validate.field).string.min_len = 1]; } // Response containing one user's permission matrix. message GetUserPermissionMatrixResponse { // Requested matrix. UserPermissionMatrix matrix = 1; } // Request to set one role permission state. message SetRolePermissionRequest { // Role name. string role_name = 1 [(buf.validate.field).string.min_len = 1]; // Permission identifier. string permission = 2 [(buf.validate.field).string.min_len = 1]; // New decision. NONE clears the explicit state. PermissionDecision decision = 3 [(buf.validate.field).enum = { defined_only: true not_in: [0] }]; // Target scope. Omit or use SERVER for server scope. PermissionScope scope = 4; } // Result of setting one role permission state. message SetRolePermissionResponse { // Updated permission decision for the requested role/scope cell. PermissionDecisionUpdate decision = 1; } // Request to set one user permission state. message SetUserPermissionRequest { // Target user ID. string user_id = 1 [(buf.validate.field).string.min_len = 1]; // Permission identifier. string permission = 2 [(buf.validate.field).string.min_len = 1]; // New decision. NONE clears the explicit state. PermissionDecision decision = 3 [(buf.validate.field).enum = { defined_only: true not_in: [0] }]; // Target scope. Omit or use SERVER for server scope. PermissionScope scope = 4; } // Result of setting one user permission state. message SetUserPermissionResponse { // Updated permission decision for the requested user/scope cell. PermissionDecisionUpdate decision = 1; } // Provides permission matrix reads and admin permission writes. service AdminPermissionService { // Gets the role-permission matrix for a single tier. Server scope requires // role.manage; group and room scopes require role.manage or effective // room.manage at the requested resource. rpc GetRolePermissionTierMatrix(GetRolePermissionTierMatrixRequest) returns (GetRolePermissionTierMatrixResponse); // Gets one role's full permission matrix. Requires role.manage. Returns // NOT_FOUND when the role does not exist. rpc GetRolePermissionMatrix(GetRolePermissionMatrixRequest) returns (GetRolePermissionMatrixResponse); // Lists one role's permission decisions as resource-oriented rows. Requires // role.manage. Returns NOT_FOUND when the role does not exist. rpc ListRolePermissionDecisions(ListRolePermissionDecisionsRequest) returns (ListRolePermissionDecisionsResponse); // Gets one user's full permission matrix. Human targets require // user.manage-permissions; bot targets require ownership or bot.manage. // Returns NOT_FOUND when the user does not exist. rpc GetUserPermissionMatrix(GetUserPermissionMatrixRequest) returns (GetUserPermissionMatrixResponse); // Lists one user's permission decisions as resource-oriented rows. Human // targets require user.manage-permissions; bot targets require ownership or // bot.manage. Returns NOT_FOUND when the user does not exist. rpc ListUserPermissionDecisions(ListUserPermissionDecisionsRequest) returns (ListUserPermissionDecisionsResponse); // Explains permission resolution for another user. Requires role.manage. rpc ExplainPermissions(ExplainPermissionsRequest) returns (ExplainPermissionsResponse); // Sets one role permission decision. Server scope requires role.manage; // group and room scopes also accept effective room.manage at that resource. rpc SetRolePermission(SetRolePermissionRequest) returns (SetRolePermissionResponse); // Sets one user permission decision. Human targets require // user.manage-permissions; bot targets require ownership or bot.manage and // accept only allow or clear within the bot owner's current authority. rpc SetUserPermission(SetUserPermissionRequest) returns (SetUserPermissionResponse); }