Kaizen
Browse modulesAccessaccess/serverClasses

Class: PermissionService

Defined in: server/access/permission-service.ts:12

Authoring surface for the optional RbacPermissions registry mirror. The authoritative catalog is in code (definePermissions()); this service persists it for admin-UI listing and can sync the in-code catalog into the table.

Extends

Constructors

Constructor

new PermissionService(deps: AccessFeatureDeps): PermissionService;

Defined in: server/access/base-access-service.ts:36

Parameters

ParameterType
depsAccessFeatureDeps

Returns

PermissionService

Inherited from

BaseAccessService.constructor

Properties

deps

protected readonly deps: AccessFeatureDeps;

Defined in: server/access/base-access-service.ts:34

Inherited from

BaseAccessService.deps

Accessors

actorId

Get Signature

get protected actorId(): string | null;

Defined in: server/access/base-access-service.ts:40

Returns

string | null

Inherited from

BaseAccessService.actorId

Methods

coarsePermissionsForActor()

protected coarsePermissionsForActor(
   actor: {
  id: string;
  type: SubjectType;
}, 
   scope: GrantScope, 
   now?: Date
): Promise<CoarseActorAuthority>;

Defined in: server/access/base-access-service.ts:117

H3: derive an actor's UNCONDITIONALLY-held authority for a target scope — the delegable set for both GrantService.create's CreateGrantGuard and RoleService.createVersion's CreateVersionGuard. Only grants the actor holds with NO selector and NO condition contribute, mirroring hasCoarsePermission: a narrowly-held permission must not be re-delegable (as a grant) or re-addable (to a role) unconstrained. Effective-date validity is honored. Shared here (not duplicated per service) so the two escalation guards can never drift on what "coarsely held" means.

Derived over scopeChain(scope), so a platform admin can delegate inside an organization without mode: "system" (which would skip every escalation guard). The chain is derived here, never accepted as a parameter — see scopeChain.

scopeKeys reports PROVENANCE: which scopes actually backed the actor. That is what GrantService's self-org cap reads instead of a caller-asserted actorOrganizationId, making it server-derived and unspoofable.

Parameters

ParameterType
actor{ id: string; type: SubjectType; }
actor.idstring
actor.typeSubjectType
scopeGrantScope
nowDate

Returns

Promise<CoarseActorAuthority>

Inherited from

BaseAccessService.coarsePermissionsForActor


create()

create(input: {
  action: string;
  delegable?: boolean;
  description?: string | null;
  id?: string;
  organizationId?: string | null;
}): Promise<{
  action: string;
  createdAt: Date;
  createdBy: string | null;
  delegable: boolean;
  deletedAt: Date | null;
  deletedBy: string | null;
  description: string | null;
  id: string;
  organizationId: string | null;
  updatedAt: Date;
  updatedBy: string | null;
}>;

Defined in: server/access/permission-service.ts:13

Parameters

ParameterType
input{ action: string; delegable?: boolean; description?: string | null; id?: string; organizationId?: string | null; }
input.actionstring
input.delegable?boolean
input.description?string | null
input.id?string
input.organizationId?string | null

Returns

Promise<{ action: string; createdAt: Date; createdBy: string | null; delegable: boolean; deletedAt: Date | null; deletedBy: string | null; description: string | null; id: string; organizationId: string | null; updatedAt: Date; updatedBy: string | null; }>


listForScope()

listForScope(organizationId: string | null): Promise<{
  action: string;
  createdAt: Date;
  createdBy: string | null;
  delegable: boolean;
  deletedAt: Date | null;
  deletedBy: string | null;
  description: string | null;
  id: string;
  organizationId: string | null;
  updatedAt: Date;
  updatedBy: string | null;
}[]>;

Defined in: server/access/permission-service.ts:24

List catalog rows visible in a scope (org's own + global).

Parameters

ParameterType
organizationIdstring | null

Returns

Promise<{ action: string; createdAt: Date; createdBy: string | null; delegable: boolean; deletedAt: Date | null; deletedBy: string | null; description: string | null; id: string; organizationId: string | null; updatedAt: Date; updatedBy: string | null; }[]>


loadActiveGrants()

protected loadActiveGrants(
   repos: AccessRepositories, 
   subject: Subject, 
   scopes: GrantScope[]
): Promise<{
  conditionLogic: JsonValue;
  createdAt: Date;
  createdBy: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  effectiveEnd: Date | null;
  effectiveStart: Date | null;
  grantFingerprint: string;
  id: string;
  organizationId: string | null;
  roleId: string;
  scopeKey: string;
  scopeType: RbacScopeType;
  selector: JsonValue;
  subjectId: string;
  subjectType: RbacSubjectType;
  updatedAt: Date;
  updatedBy: string | null;
}[]>;

Defined in: server/access/base-access-service.ts:70

Security-critical lookup: load a subject's active grants across a set of scopes, expanding org-group membership for non-group subjects. The single source of truth for "which grants apply to this subject right now" — every caller (snapshot compilation, coarse-permission derivation) funnels here so the scopeKey derivation and group-expansion rule can never drift between call sites.

Queried one scope at a time rather than with a single scopeKey IN (…): group expansion is a PER-SCOPE rule, not a property of the subject. It runs only for an organization-scoped lookup AND a non-group subject — a group subject's grants are read directly (it has no "groups" of its own to expand), and platform scope has no org-group notion, so a group's platform-scoped grant must NOT reach its members through an org-scoped chain. Flattening the chain into one query with a unioned groupIds would silently widen exactly that.

Must be called inside a repos.transaction(...) so the membership reads and the grant reads share one transaction.

Parameters

ParameterType
reposAccessRepositories
subjectSubject
scopesGrantScope[]

Returns

Promise<{ conditionLogic: JsonValue; createdAt: Date; createdBy: string | null; deletedAt: Date | null; deletedBy: string | null; effectiveEnd: Date | null; effectiveStart: Date | null; grantFingerprint: string; id: string; organizationId: string | null; roleId: string; scopeKey: string; scopeType: RbacScopeType; selector: JsonValue; subjectId: string; subjectType: RbacSubjectType; updatedAt: Date; updatedBy: string | null; }[]>

Inherited from

BaseAccessService.loadActiveGrants


syncCatalog()

syncCatalog(organizationId?: string | null): Promise<number>;

Defined in: server/access/permission-service.ts:35

Sync the in-code permission catalog (from definePermissions, wired as deps.catalog) into the RbacPermissions table for the given scope. Idempotent: inserts only missing actions. Returns the count inserted.

Parameters

ParameterTypeDefault value
organizationIdstring | nullnull

Returns

Promise<number>


transaction()

protected transaction<T>(fn: (deps: AccessFeatureDeps) => Promise<T>): Promise<T>;

Defined in: server/access/base-access-service.ts:44

Type Parameters

Type Parameter
T

Parameters

ParameterType
fn(deps: AccessFeatureDeps) => Promise<T>

Returns

Promise<T>

Inherited from

BaseAccessService.transaction

On this page