src/app/core/permissions/permission-types.ts

Description

The format of the JSON object which defines the rules for each role. The format is <user-role>: <array of DatabaseRule>, meaning for each role an array of rules can be defined. The rules defined in '_default' are prepended to any other rules defined for a user. The rules defined in '_public' are used if a user is not logged in.

Index

Properties

Indexable

[key: string]: DatabaseRule[]

Properties

_default
_default: DatabaseRule[]
Type : DatabaseRule[]
Optional
_public
_public: DatabaseRule[]
Type : DatabaseRule[]
Optional
import { MongoAbility, RawRuleOf } from "@casl/ability";
import { Entity, EntityConstructor } from "../entity/model/entity";

/**
 * The individual CRUD actions, in the order they are displayed as columns of the
 * permission UIs.
 */
export const CRUD_ACTIONS = ["read", "create", "update", "delete"] as const;

export type CrudAction = (typeof CRUD_ACTIONS)[number];

/**
 * The list of action strings that can be used for permissions
 */
const actions = [...CRUD_ACTIONS, "manage"] as const; // "manage" matches any action

/**
 * The type which defines which actions can be used for permissions.
 * The type allows all strings defined in the `actions` array.
 * E.g. "read" or "manage"
 */
export type EntityActionPermission = (typeof actions)[number];

/**
 * The type which defines which subjects can be used for permissions.
 * This matches any entity classes, entity objects and the wildcard string "all"
 * E.g. `Child`, `new Note()` or `all`
 */
export type EntitySubject = EntityConstructor | Entity | string;

/**
 * The format that the JSON defined rules need to have.
 * In the JSON object the Entities can be specified by using their ENTITY_TYPE string representation.
 */
export type DatabaseRule = RawRuleOf<
  MongoAbility<[EntityActionPermission, string]>
>;

/**
 * Section keys in {@link DatabaseRules} that carry special semantics instead of
 * mapping a user role. The underscore prefix marks them as internal so they
 * cannot collide with a realm role name.
 */
export const DEFAULT_SECTION_KEY = "_default";
export const PUBLIC_SECTION_KEY = "_public";

/** A user role starting with this prefix is reserved and never resolved. */
export const RESERVED_ROLE_PREFIX = "_";

/**
 * The realm role that grants access to the administration features, as checked
 * by the admin routes' {@link UserRoleGuard} configuration.
 */
export const ADMIN_APP_ROLE = "admin_app";

/**
 * All section keys that must never be resolved as if they were user role names,
 * even if a realm role with the same name exists.
 *
 * A new key added here does not automatically stop inheriting the "_default"
 * rules; see {@link inheritsDefaultRules}.
 */
export const RESERVED_RULE_CONFIG_KEYS: string[] = [
  DEFAULT_SECTION_KEY,
  PUBLIC_SECTION_KEY,
];

/**
 * Whether a key in {@link DatabaseRules} carries special semantics instead of
 * naming a user role, so it must neither be resolved as a role nor rewritten by
 * a per-role UI. Covers the reserved underscore prefix as well as the legacy
 * (non-prefixed) spellings of not yet migrated configs.
 */
export function isReservedRuleConfigKey(key: string): boolean {
  return (
    key.startsWith(RESERVED_ROLE_PREFIX) ||
    RESERVED_RULE_CONFIG_KEYS.includes(key)
  );
}

/**
 * Marker written into a rule's `reason` by the backend for rules it manages itself
 * to guarantee a baseline. Such rules must not be rewritten by an admin UI.
 */
export const SYSTEM_DEFAULT_RULE_REASON = "[system-default]";

/**
 * Whether the rule applies to the given entity type, ignoring conditions and
 * inversion. `all` matches every entity type.
 */
export function ruleAppliesToSubject(
  subject: DatabaseRule["subject"],
  entityType: string,
): boolean {
  if (Array.isArray(subject)) {
    return subject.includes(entityType) || subject.includes("all");
  }
  return subject === entityType || subject === "all";
}

/**
 * Whether the rule grants the given action for the entity type, ignoring conditions
 * and inversion. `manage` covers every other action, and a rule can list several
 * actions as an array.
 */
export function ruleCoversAction(
  rule: DatabaseRule,
  entityType: string,
  action: EntityActionPermission,
): boolean {
  const actions = Array.isArray(rule.action) ? rule.action : [rule.action];
  return (
    ruleAppliesToSubject(rule.subject, entityType) &&
    (actions.includes(action) || actions.includes("manage"))
  );
}

/**
 * Whether users of the given role also receive the shared "_default" rules.
 * False for "_default", which cannot inherit from itself, and for "_public",
 * which applies to visitors who are not logged in and never get the "_default"
 * rules (see AbilityService.getRulesForUser).
 *
 * The excluded keys are listed explicitly instead of taken from
 * {@link RESERVED_RULE_CONFIG_KEYS}, because a future reserved role that
 * applies to logged-in users would inherit the "_default" rules as usual.
 */
export function inheritsDefaultRules(roleName: string): boolean {
  return ![DEFAULT_SECTION_KEY, PUBLIC_SECTION_KEY].includes(roleName);
}

/**
 * The format of the JSON object which defines the rules for each role.
 * The format is `<user-role>: <array of DatabaseRule>`, meaning for each role an array of rules can be defined.
 * The rules defined in '_default' are prepended to any other rules defined for a user.
 * The rules defined in '_public' are used if a user is not logged in.
 */
export interface DatabaseRules {
  _public?: DatabaseRule[];
  _default?: DatabaseRule[];

  [key: string]: DatabaseRule[];
}

results matching ""

    No results matching ""