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.

FieldTypeNotes
AdminIdGuidFK → Admin receiving the role assignment
RoleIdGuidFK → Role being assigned
TenantIdGuid?Tenant scope for this assignment. null = platform-level role. GUID = tenant-scoped role (admin only has this role when operating within that tenant)
InheritToChildrenboolWhen true, this role assignment cascades to child tenants in a tenant hierarchy
ExpiresAtDateTime?Optional expiry for time-limited role grants. null = permanent
AssignedByGuid?The admin who created this assignment (kept alongside AuditableEntity.CreatedBy for explicit tracking)
Identity.Domain/Entities/AdminRole.csC#
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.

FieldTypeNotes
AdminIdGuidFK → Admin who is a member of the group
UserGroupIdGuidFK → 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.

FieldTypeNotes
UserGroupIdGuidFK → UserGroup that owns this restriction
PermissionCodestring (max 100)Permission resource code (e.g., 'admins', 'users', 'employees'). Matched case-insensitively during restriction merging
RestrictedFieldsJsonstring (max 2000)JSON array of field names to restrict (e.g., ['salary','ssn','bankAccount']). These fields are nullified in API responses for group members
Identity.Domain/Entities/UserGroupRestriction.csC#
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.

Admin makes API request
Load admin's direct role restrictions
Load group memberships (AdminUserGroup)
Load group restrictions (UserGroupRestriction)
UNION all restricted fields (additive, never reduces)
Nullify restricted fields in API response