Skip to main content

webiny-admin-permissions

Admin-side permission UI registration and DI-backed permission checking. Use this skill when adding permission controls to the admin UI — schema-based auto-generated forms, injectable permissions via createPermissionsAbstraction/ createPermissionsFeature, typed hooks (createUsePermissions), the HasPermission component (createHasPermission), and the Security.Permissions component props. Covers both simple apps and complex multi-entity permission schemas.

Informations de source

Dépôt
webiny/webiny-js
Dernière activité de la source
24 juillet 2026 à 12:20
Langue détectée de SKILL.md
anglais
Étoiles
8 045
Forks
682

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
webiny-admin-permissions
description
Admin-side permission UI registration and DI-backed permission checking. Use this skill when adding permission controls to the admin UI — schema-based auto-generated forms, injectable permissions via createPermissionsAbstraction/ createPermissionsFeature, typed hooks (createUsePermissions), the HasPermission component (createHasPermission), and the Security.Permissions component props. Covers both simple apps and complex multi-entity permission schemas.
# Admin Permissions ## Overview Permissions follow three layers: **domain** (schema), **features** (DI artifacts + registration), and **presentation** (hooks, components, UI config). The framework auto-generates the permission UI from a schema and provides injectable permission checking via the DI container. ## Layer 1: Domain — Permission Schema Define the schema in `src/domain/permissionsSchema.ts`: ```ts import { createPermissionSchema } from "webiny/admin/security"; export const SM_PERMISSIONS_SCHEMA = createPermissionSchema({ prefix: "sm", fullAccess: true, entities: [ { id: "product", title: "Products", permission: "sm.product", scopes: ["full", "own"], actions: [ { name: "rwd" }, { name: "pw" }, { name: "import", label: "Import products" }, { name: "export", label: "Export products" } ] }, { id: "category", title: "Categories", permission: "sm.category", scopes: ["full"], actions: [{ name: "rwd" }] }, { id: "settings", title: "Settings", permission: "sm.settings", scopes: ["full"] } ] }); ``` ### Schema Reference | Field | Type | Required | Description | | ---------------- | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `prefix` | `string` | Yes | Permission prefix (e.g., `"sm"`) | | `fullAccess` | `true \| object` | Yes | `true` for standard full access. Pass an object with custom boolean flags for full-access extras (e.g., `{ canForceUnlock: true }`). | | `readOnlyAccess` | `boolean` | No | Whether to show a "Read-only access" option | | `entities` | `EntityDefinition[]` | No | Entity definitions. Omit for binary full/no access. | ### Entity Definition | Field | Type | Required | Description | | ------------ | -------------------------------------- | -------- | ---------------------------------------------- | | `id` | `string` | Yes | Unique identifier for form field naming | | `title` | `string` | No | Display title. Falls back to `id`. | | `permission` | `string` | Yes | Permission name emitted (e.g., `"sm.product"`) | | `scopes` | `("full" \| "own")[]` | Yes | Available access scopes | | `actions` | `ActionDefinition[]` | No | Actions on this entity | | `dependsOn` | `{ entity: string; requires: string }` | No | Dependency on another entity | ### Actions - `{ name: "rwd" }` — Read/Write/Delete select dropdown. Auto-set to `"rwd"` when scope is `"own"`. - `{ name: "pw" }` — Publish/Unpublish checkbox group. - `{ name: "custom", label: "Label" }` — Custom boolean flag. ### Entity Dependencies Child entities can depend on a parent. If the parent lacks the required action, the child is pruned from output. `"own"` scope cascades to dependents. ```ts { id: "review", permission: "sm.review", scopes: ["full", "own"], actions: [{ name: "rwd" }], dependsOn: { entity: "product", requires: "r" } } ``` ### Simple Apps (No Entities) Omit `entities` for binary full/no access: ```ts export const MA_PERMISSIONS_SCHEMA = createPermissionSchema({ prefix: "ma", fullAccess: true }); ``` --- ## Layer 2: Features — DI Artifacts + Registration ### Abstraction (`src/features/permissions/abstractions.ts`) ```ts import { createPermissionsAbstraction } from "webiny/admin/security"; import type { Permissions } from "webiny/admin/security"; import { SM_PERMISSIONS_SCHEMA } from "~/domain/permissionsSchema.js"; export const SmPermissions = createPermissionsAbstraction(SM_PERMISSIONS_SCHEMA); export namespace SmPermissions { export type Interface = Permissions<typeof SM_PERMISSIONS_SCHEMA>; } ``` ### Feature (`src/features/permissions/feature.ts`) ```ts import { createPermissionsFeature } from "webiny/admin/security"; import { SM_PERMISSIONS_SCHEMA } from "~/domain/permissionsSchema.js"; import { SmPermissions } from "./abstractions.js"; export const SmPermissionsFeature = createPermissionsFeature(SM_PERMISSIONS_SCHEMA, SmPermissions); ``` ### Extension Registration Register the feature and the permission UI in your extension component: ```tsx import { AdminConfig, RegisterFeature } from "webiny/admin/security"; import { ReactComponent as Icon } from "@webiny/icons/shield.svg"; import { SM_PERMISSIONS_SCHEMA } from "~/domain/permissionsSchema.js"; import { SmPermissionsFeature } from "~/features/permissions/feature.js"; const { Security } = AdminConfig; export const Extension = () => { return ( <> <RegisterFeature feature={SmPermissionsFeature} /> <AdminConfig> <Security.Permissions name="store-manager" title="Store Manager" description="Manage Store Manager permissions." icon={<Icon />} schema={SM_PERMISSIONS_SCHEMA} /> {/* Routes, menus, etc. */} </AdminConfig> </> ); }; ``` --- ## Layer 3: Presentation — Hooks & Components ### `usePermissions` Hook ```ts // src/presentation/security/usePermissions.ts import { createUsePermissions } from "webiny/admin/security"; import { SmPermissions } from "~/features/permissions/abstractions.js"; export const usePermissions = createUsePermissions(SmPermissions); ``` Usage: ```ts const permissions = usePermissions(); permissions.canAccess("product"); // has any access permissions.canRead("product"); // rwd includes "r" permissions.canCreate("product"); // rwd includes "w" permissions.canEdit("product", item); // rwd includes "w", respects own scope permissions.canDelete("product"); // rwd includes "d" permissions.canPublish("product"); // pw includes "p" permissions.canUnpublish("product"); // pw includes "u" permissions.canAction("import", "product"); // custom boolean flag ``` Entity IDs are fully typed — `canRead("bogus")` produces a type error. ### `HasPermission` Component ```tsx // src/presentation/security/HasPermission.tsx import { createHasPermission } from "webiny/admin/security"; import { SmPermissions } from "~/features/permissions/abstractions.js"; import { SM_PERMISSIONS_SCHEMA } from "~/domain/permissionsSchema.js"; export const HasPermission = createHasPermission(SmPermissions, SM_PERMISSIONS_SCHEMA); ``` Usage in JSX: ```tsx <HasPermission entity="product"> {/* Rendered if user can access "product" */} </HasPermission> <HasPermission any={["product", "category"]}> {/* Rendered if user can access ANY of these entities */} </HasPermission> <HasPermission all={["product", "category"]}> {/* Rendered if user can access ALL of these entities */} </HasPermission> <HasPermission entity="product" action="read"> {/* Rendered if user canRead("product") */} </HasPermission> <HasPermission entity="product" allActions={["read", "publish"]}> {/* Rendered if user canRead AND canPublish "product" */} </HasPermission> <HasPermission entity="product" someActions={["import", "export"]}> {/* Rendered if user can do ANY of these custom actions */} </HasPermission> ``` --- ## DI Injection in Features Inject permissions into feature implementations: ```ts import type { SmPermissions } from "~/features/permissions/abstractions.js"; import { SmPermissions as SmPermissionsAbstraction } from "~/features/permissions/abstractions.js"; class SomeFeatureImpl { constructor(private permissions: SmPermissions.Interface) {} doSomething() { if (this.permissions.canEdit("product")) { // ... } } } const SomeFeature = SomeAbstraction.createImplementation({ implementation: SomeFeatureImpl, dependencies: [SmPermissionsAbstraction] }); ``` --- ## `Security.Permissions` Props | Prop | Type | Required | Description | | ------------- | ------------------ | ------------------------- | ----------------------------------------------- | | `name` | `string` | Yes | Unique identifier for this permission renderer | | `title` | `string` | Yes | Display title in the accordion header | | `description` | `string` | No | Description shown below the title | | `icon` | `ReactElement` | No | Icon in the accordion header | | `schema` | `PermissionSchema` | One of `schema`/`element` | Auto-generate UI from schema | | `element` | `ReactElement` | One of `schema`/`element` | Fully custom permission UI | | `system` | `boolean` | No | If `true`, renders before app-level permissions | --- ## File Structure ``` src/ ├── domain/ │ └── permissionsSchema.ts # createPermissionSchema() ├── features/ │ └── permissions/ │ ├── abstractions.ts # createPermissionsAbstraction() + namespace type │ └── feature.ts # createPermissionsFeature() ├── presentation/ │ └── security/ │ ├── usePermissions.ts # createUsePermissions() │ └── HasPermission.tsx # createHasPermission() └── Extension.tsx # RegisterFeature + Security.Permissions ``` ## Matching API-Side Permissions The admin schema and the API-side `createPermissions` schema should use the **same prefix, entity IDs, and action names**. This ensures the permissions emitted by the admin UI are correctly evaluated by the API. ``` Admin: createPermissionSchema({ prefix: "sm", entities: [{ id: "product", permission: "sm.product", ... }] })
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub