Access Control Deep Dive
Entity-level documentation for AdminRole (role assignment junction with tenant scoping and expiry), AdminUserGroup (group membership), and UserGroupRestriction (additive field-level restrictions for group members).
SCRIPE's access control system is built on three junction/restriction entities. AdminRole links an admin to a role, optionally scoped to a specific tenant with an optional expiry date. AdminUserGroup links an admin to a user group, granting all roles inherited by that group. UserGroupRestriction defines field-level restrictions that are applied additively (UNION) to all group members' API responses. These three entities work together to produce a fine-grained, auditable access control model.
AdminRole Entity
AdminRole is the junction entity between Admin and Role. TenantId scoping enables a single admin to have different roles across different tenants — a common pattern where a platform admin has SuperAdmin at platform level but only ReadOnly when drilling into a specific tenant. InheritToChildren cascades the role to all child tenants in a hierarchy. ExpiresAt enables time-limited role grants for contractors or temporary access.
| Field | Type | Notes |
|---|---|---|
| AdminId | Guid | FK → Admin receiving the role assignment |
| RoleId | Guid | FK → Role being assigned |
| TenantId | Guid? | Tenant scope for this assignment. null = platform-level role. GUID = tenant-scoped role (admin only has this role when operating within that tenant) |
| InheritToChildren | bool | When true, this role assignment cascades to child tenants in a tenant hierarchy |
| ExpiresAt | DateTime? | Optional expiry for time-limited role grants. null = permanent |
| AssignedBy | Guid? | The admin who created this assignment (kept alongside AuditableEntity.CreatedBy for explicit tracking) |
public class AdminRole : AuditableEntity<Guid>
{
public Guid AdminId { get; set; }
public Guid RoleId { get; set; }
public Guid? TenantId { get; set; }
public bool InheritToChildren { get; set; }
public DateTime? ExpiresAt { get; set; }
public Guid? AssignedBy { get; set; }
// Navigation
public virtual Admin Admin { get; set; } = null!;
public virtual Role Role { get; set; } = null!;
public virtual Tenant? Tenant { get; set; }
}AdminUserGroup Entity
AdminUserGroup is the membership junction between Admin and UserGroup. An admin inherits all roles assigned to a group via RolePermission records. Groups simplify bulk role management — instead of assigning roles individually, assign them to a group and add admins to that group. AdminUserGroup is auditable via AuditableEntity.
| Field | Type | Notes |
|---|---|---|
| AdminId | Guid | FK → Admin who is a member of the group |
| UserGroupId | Guid | FK → UserGroup the admin belongs to |
UserGroupRestriction Entity
UserGroupRestriction defines field-level data restrictions for a user group. When an admin belongs to a group with restrictions, the listed fields are nullified in API responses for that resource. Restrictions are additive — group restrictions UNION with role-level restrictions, never override or reduce them. This means belonging to more groups can only increase restrictions, never decrease them.
| Field | Type | Notes |
|---|---|---|
| UserGroupId | Guid | FK → UserGroup that owns this restriction |
| PermissionCode | string (max 100) | Permission resource code (e.g., 'admins', 'users', 'employees'). Matched case-insensitively during restriction merging |
| RestrictedFieldsJson | string (max 2000) | JSON array of field names to restrict (e.g., ['salary','ssn','bankAccount']). These fields are nullified in API responses for group members |
public class UserGroupRestriction : AuditableEntity<Guid>
{
public Guid UserGroupId { get; set; }
// Permission resource code (e.g., "admins", "users", "employees")
[MaxLength(100)]
public string PermissionCode { get; set; } = null!;
// JSON array: ["salary","ssn","bankAccount"]
// These fields are nullified in API responses for group members
[MaxLength(2000)]
public string RestrictedFieldsJson { get; set; } = null!;
public virtual UserGroup UserGroup { get; set; } = null!;
}Restriction Evaluation Flow
When an admin makes an API request for a restricted resource, SCRIPE evaluates all applicable restrictions and applies them as a UNION to the response payload.