Skip to content

basalt / permissions/src / Gate

Class: Gate ​

Defined in: permissions/src/index.ts:460

Constructors ​

Constructor ​

> new Gate(options): Gate

Defined in: permissions/src/index.ts:472

Parameters ​

options ​

GateOptions

Returns ​

Gate

Accessors ​

store ​

Get Signature ​

> get store(): AccessStore

Defined in: permissions/src/index.ts:462

The grants this gate reads. Exposed for accessRoutes(); treat as read-only.

Returns ​

AccessStore

Methods ​

actor() ​

> actor(): Promise<PolicyUser | null>

Defined in: permissions/src/index.ts:515

The user of the current request, with its roles attached.

What @basaltkit/auth puts in the context is PublicUser — { id, email, emailVerified }. No roles, and rightly so: auth does not know this package exists.

But policies receive that object, and PolicyUser is open, so user.roles?.includes('partner') reads undefined and the policy denies. The right failure mode, and an invisible one — a partner treated as a stranger in their own firm, with no error anywhere to say why.

Nothing filled the gap, so every service wrote this by hand and memoised it under a private context key it had to invent. Here it is once, memoised per request and per scope, because the same person can hold different roles in two tenants.

null when there is no user — a background job, a public route. An object with an empty id would be an actor that fails every check for a reason nobody can read.

Returns ​

Promise<PolicyUser | null>


assignRole() ​

> assignRole(userId, role, scope?): Promise<void>

Defined in: permissions/src/index.ts:666

Gives userId a role in scope (default: the current scope) and emits permission:role_assigned.

Parameters ​

userId ​

string

role ​

string

scope? ​

string

Returns ​

Promise<void>


audienceRoles() ​

> audienceRoles(userId): Promise<string[]>

Defined in: permissions/src/index.ts:640

The roles the audience guard confines on. Inside a tenant, the roles held IN that tenant decide; the global (and legacy) roles decide only when the tenant grants none. Not the effectiveRoles union: one unnamed global role (a baseline user every signup gets) would otherwise count as "something else" and un-confine a tenant's portal client in every tenant. A confined role assigned globally still confines wherever the user holds no tenant role, and a tenant role that no rule names still un-confines.

Parameters ​

userId ​

string

Returns ​

Promise<string[]>


authorize() ​

> authorize(user, permission, resource?): Promise<void>

Defined in: permissions/src/index.ts:837

Like can(), but throws PERMISSION_DENIED (403).

Parameters ​

user ​

PolicyUser

permission ​

string

resource? ​

unknown

Returns ​

Promise<void>


can() ​

> can(user, permission, resource?): Promise<boolean>

Defined in: permissions/src/index.ts:543

Permission check. With a resource, a matching policy ('resource:action') decides; otherwise the granted permission strings (with wildcards) do. Grants are looked up in the current scope AND the global scope.

Side-effect free: store and grant reads plus the superAdmin callback (keep it pure), never a hook, a denial record or a write — authorize() is what emits permission:denied. The plugin's http:route-visibility check relies on this to answer listings without auditing them.

Parameters ​

user ​

PolicyUser

permission ​

string

resource? ​

unknown

Returns ​

Promise<boolean>


delegate() ​

> delegate(input): Promise<Delegation>

Defined in: permissions/src/index.ts:803

Delegate a subset of from's authority to to (bounded at check time). Needs a delegations store.

Parameters ​

input ​
expiresAt? ​

number

from ​

string

permissions ​

string[]

scope? ​

string

to ​

string

Returns ​

Promise<Delegation>


denied() ​

> denied(userId, permission): Promise<PermissionDeniedError>

Defined in: permissions/src/index.ts:660

Emits permission:denied and returns the error to throw. Every refusal the package makes goes through here, so the audit trail sees them all.

Parameters ​

userId ​

string

permission ​

string

Returns ​

Promise<PermissionDeniedError>


describeAccess() ​

> describeAccess(user): Promise<AccessReport>

Defined in: permissions/src/index.ts:877

Everything user may do right now, with where each permission comes from — what GET /me/access answers. Covers every source a check consults: direct and role grants in the current scope AND the global one (plus the legacy global scope when read), live temporary grants, live delegations (bounded, like the check, by the delegator's own direct permissions) and the superAdmin bypass (reported as '*').

Side-effect free, like can(). Not a security surface: every request is still decided by the Gate — this only lets an interface show the doors that open and hide the ones that don't.

Parameters ​

user ​

PolicyUser

Returns ​

Promise<AccessReport>


effectiveRoles() ​

> effectiveRoles(userId): Promise<string[]>

Defined in: permissions/src/index.ts:622

The union of the user's roles over every scope a check consults.

Parameters ​

userId ​

string

Returns ​

Promise<string[]>


grantTemporarily() ​

> grantTemporarily(userId, permissions, options?): Promise<TemporaryGrant>

Defined in: permissions/src/index.ts:760

Grant a user extra permissions until expiresAt (or ttlMs from now). Needs a temporaryGrants store.

Parameters ​

userId ​

string

permissions ​

string[]

options? ​
expiresAt? ​

number

grantedBy? ​

string

reason? ​

string

scope? ​

string

ttlMs? ​

number

Returns ​

Promise<TemporaryGrant>


grantToRole() ​

> grantToRole(role, permissions, scope?): Promise<void>

Defined in: permissions/src/index.ts:684

Grants permissions to a role and emits permission:granted.

Parameters ​

role ​

string

permissions ​

string[]

scope? ​

string

Returns ​

Promise<void>


grantToUser() ​

> grantToUser(userId, permissions, scope?): Promise<void>

Defined in: permissions/src/index.ts:693

Grants permissions directly to a user and emits permission:granted.

Parameters ​

userId ​

string

permissions ​

string[]

scope? ​

string

Returns ​

Promise<void>


hasPolicy() ​

> hasPolicy(permission): boolean

Defined in: permissions/src/index.ts:578

True when a registered policy check decides exactly permission (resource:action) — i.e. when can(user, permission, resource) would consult a policy instead of throwing MissingPolicyError (or falling back to RBAC under onMissingPolicy: 'rbac'). A pure lookup.

Parameters ​

permission ​

string

Returns ​

boolean


hasRole() ​

> hasRole(user, role): Promise<boolean>

Defined in: permissions/src/index.ts:851

Whether the user actually holds role — in the current scope or globally. Role membership, not authority: the superAdmin bypass does NOT make a super admin a member of every role (it short-circuits can()/authorize() instead). Ask isSuperAdmin for that, or — better — check the permission the role stands for with can().

Parameters ​

user ​

PolicyUser

role ​

string

Returns ​

Promise<boolean>


isSuperAdmin() ​

> isSuperAdmin(user): Promise<boolean>

Defined in: permissions/src/index.ts:860

Whether the configured superAdmin callback lets user bypass every check. false without one.

Parameters ​

user ​

PolicyUser

Returns ​

Promise<boolean>


register() ​

> register(policy): this

Defined in: permissions/src/index.ts:487

Parameters ​

policy ​

Policy<never>

Returns ​

this


removeRole() ​

> removeRole(userId, role, scope?): Promise<void>

Defined in: permissions/src/index.ts:675

Takes a role away and emits permission:role_removed.

Parameters ​

userId ​

string

role ​

string

scope? ​

string

Returns ​

Promise<void>


rolePermissions() ​

> rolePermissions(role, scope): Promise<string[]>

Defined in: permissions/src/index.ts:708

The permissions role carries when held in scope: the store's definition in that scope, the roleCatalog entry, and — with inheritGlobalRolePermissions — the store's global definition. Always evaluated FOR scope: the caller only uses the result for a role held there, so nothing here widens a grant to another tenant.

Parameters ​

role ​

string

scope ​

string

Returns ​

Promise<string[]>

Released under the MIT License.