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.
Best use case
webiny-admin-permissions is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
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.
Teams using webiny-admin-permissions should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/admin-permissions/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How webiny-admin-permissions Compares
| Feature / Agent | webiny-admin-permissions | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
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.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
Top AI Agents for Productivity
See the top AI agent skills for productivity, workflow automation, operational systems, documentation, and everyday task execution.
AI Agents for Marketing
Discover AI agents for marketing workflows, from SEO and content production to campaign research, outreach, and analytics.
AI Agents for Startups
Explore AI agent skills for startup validation, product research, growth experiments, documentation, and fast execution with small teams.
SKILL.md Source
# 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", ... }] })
API: createPermissions({ prefix: "sm", entities: [{ id: "product", permission: "sm.product", ... }] })
```
See **webiny-api-permissions** for the API-side implementation.
## Related Skills
- **webiny-api-permissions** — API-side permission checking (canRead, canEdit, etc.)
- **webiny-admin-architect** — Admin architecture, headless and presentation features
- **webiny-admin-ui-extensions** — Admin UI customization, decorators, configRelated Skills
webiny-v5-to-v6-migration
Migration patterns for converting v5 Webiny code to v6 architecture. Use this skill when migrating existing v5 plugins to v6 features, converting context plugins to DI services, adapting v5 event subscriptions to v6 EventHandlers, or understanding how v5 patterns translate to v6. Targeted at AI agents performing migrations.
webiny-api-permissions
Schema-based permission system for API features. Use this skill when implementing authorization in use cases, defining permission schemas with createPermissionSchema, creating injectable permissions via createPermissionsAbstraction/createPermissionsFeature, checking read/write/delete/publish permissions, handling own-record scoping, or testing permission scenarios. Covers the full pattern from schema definition to use case integration to test matrices.
webiny-sdk
Using @webiny/sdk to read and write CMS data from external applications. Use this skill when the developer is building a Next.js, Vue, Node.js, or any external app that needs to fetch or write content to Webiny, set up the SDK, use the Result pattern, list/get/create/update/publish entries, filter and sort queries, use TypeScript generics for type safety, work with the File Manager, or create API keys programmatically. Covers read vs preview mode, the `values` wrapper requirement, correct method names, and the `fields` required parameter.
webiny-project-structure
Webiny project layout, webiny.config.tsx anatomy, and extension registration. Use this skill when the developer asks about folder structure, where custom code goes, how to register extensions, what webiny.config.tsx does, or how the project is organized. Also use when they need to understand the relationship between extensions/, webiny.config.tsx, and the different extension types (Api, Admin, Infra, CLI).
webiny-local-development
Deploying, developing locally, managing environments, and debugging Webiny projects. Use this skill when the developer asks about deployment commands (deploy, destroy, info), local development with watch mode (API or Admin), the Local Lambda Development system, environment management (long-lived vs short-lived, production vs dev modes), build parameters, state files, debugging API/Admin/Infrastructure errors, or the redeploy-after-watch requirement.
webiny-infrastructure-extensions
Modifying AWS infrastructure using Pulumi handlers and declarative Infra components. Use this skill when the developer wants to customize AWS infrastructure, add Pulumi handlers, configure OpenSearch, VPC, resource tags, regions, custom domains, blue-green deployments, environment-conditional config, or manage production vs development infrastructure modes. Covers CorePulumi.Interface, all <Infra.*> declarative components, and <Infra.Env.Is>.
webiny-infra-catalog
Infrastructure — 33 abstractions. Infrastructure extensions.
webiny-extensions-catalog
extensions — 5 abstractions.
webiny-cli-command-catalog
cli/command — 1 abstractions.
webiny-cli-catalog
cli — 2 abstractions.
webiny-api-tenant-manager-catalog
API — Tenant Manager — 2 abstractions. Tenant management event handlers and use cases.
webiny-api-tasks-catalog
api/tasks — 2 abstractions.