> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Permission

> Authorize what a user can do with ability-based, CASL-backed permission classes.

The `@ooneex/permission` component is a fine-grained access control layer built on [CASL](https://casl.js.org). You describe what is allowed as a set of abilities — pairs of an action (`read`, `update`, `delete`, …) and a subject (`User`, `Article`, `all`, …) — then ask whether the current user `can` or `cannot` perform an action. Permissions are written as classes that extend the abstract `Permission` base, registered with the container, and attached to routes so authorization runs before your handler.

## Why this component

* **Ability-based, not flag-based.** Authorize concrete `action` + `subject` pairs instead of scattering boolean role checks across your code.
* **User-aware.** `setUserPermissions(context)` grants abilities from the request context — the same permission class adapts to admins, owners, and guests.
* **Field-level control.** `can()` and `cannot()` take an optional field, so you can allow reading `name` while forbidding `password`.
* **Type-safe subjects and actions.** `EPermissionAction` ships 60+ actions and `EPermissionSubject` common subjects, with literal types you can extend per domain.
* **Container-managed.** Register a permission class with a decorator and resolve it from the container or wire it to a route.

## How it works

A permission is a class extending `Permission`. You implement three methods, then `build()` compiles the rules into a CASL ability you query with `can`/`cannot`.

| Method                            | Purpose                                                                                               |
| --------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `allow()`                         | Declare baseline abilities with `this.ability.can(...)` / `this.ability.cannot(...)`. Returns `this`. |
| `setUserPermissions(context)`     | Add abilities derived from the request context (the user, their roles, ownership). Returns `this`.    |
| `check(context)`                  | Custom gate run before the route handler; return `false` to deny outright.                            |
| `build()`                         | Compile declared rules into the ability. Must run before `can`/`cannot`. Returns `this`.              |
| `can(action, subject, field?)`    | Whether the action is allowed on the subject (optionally a field).                                    |
| `cannot(action, subject, field?)` | Whether the action is forbidden on the subject (optionally a field).                                  |

The lifecycle is a fluent chain: declare context-driven rules, declare baseline rules, build, then query.

```typescript theme={null}
const permission = new ArticlePermission();

permission
  .setUserPermissions(context) // abilities from the current user
  .allow() // baseline abilities
  .build(); // compile before checking

if (permission.can(EPermissionAction.EDIT, "Article")) {
  // proceed
}
```

`can` and `cannot` throw a `PermissionException` (`NOT_BUILT`) if called before `build()`. The `MANAGE` action and the `all` subject act as wildcards — `this.ability.can("manage", "all")` grants everything.

## Decorator and usage

### `@decorator.permission()`

Registers a permission class with the container. It accepts an optional scope (defaults to singleton).

```typescript theme={null}
import type { ContextType } from "@ooneex/controller";
import { decorator, EPermissionAction, Permission } from "@ooneex/permission";

@decorator.permission()
export class ArticlePermission extends Permission {
  public allow(): this {
    this.ability.can(EPermissionAction.READ, "Article");
    this.ability.can(EPermissionAction.VIEW, "Article");
    return this;
  }

  public setUserPermissions(context: ContextType): this {
    const { user } = context;
    if (!user) return this;

    // Authenticated users can create and edit articles
    this.ability.can(EPermissionAction.CREATE, "Article");
    this.ability.can(EPermissionAction.EDIT, "Article");

    // Admins manage everything
    if (user.roles.includes("ROLE_ADMIN")) {
      this.ability.can(EPermissionAction.MANAGE, "all");
    }

    return this;
  }

  public check(context: ContextType): boolean {
    return true;
  }
}
```

Attach the class to a route and authorization runs automatically before the handler:

```typescript theme={null}
import type { ContextType, IController } from "@ooneex/controller";
import { Route } from "@ooneex/routing";
import { ArticlePermission } from "@/permissions/ArticlePermission";

@Route.http({
  name: "api.articles.update",
  path: "/api/articles/:id",
  method: "PUT",
  permission: ArticlePermission,
})
class ArticleUpdateController implements IController {
  public async index(context: ContextType) {
    // Reached only when the permission allows it
  }
}
```

You can also evaluate a permission manually — useful for ownership checks the route-level gate can't know about:

```typescript theme={null}
const permission = new ArticlePermission();

permission
  .setUserPermissions(context)
  .allow()
  .build();

if (permission.cannot(EPermissionAction.DELETE, "Article")) {
  return context.response.exception("Not authorized", { status: 403 });
}
```

Restrict to specific fields by passing a field name:

```typescript theme={null}
permission.can(EPermissionAction.READ, "User", "name"); // true
permission.can(EPermissionAction.READ, "User", "password"); // false
```

## Exceptions

The component throws `PermissionException` when an ability is queried before the permission has been built. It carries a machine-readable `key`, a human-readable `message`, and a `data` object.

| Key         | When                                              |
| ----------- | ------------------------------------------------- |
| `NOT_BUILT` | `can()` or `cannot()` is called before `build()`. |

```typescript theme={null}
import { PermissionException } from "@ooneex/permission";

try {
  permission.can(EPermissionAction.READ, "Article");
} catch (error) {
  if (error instanceof PermissionException) {
    logger.error(`Permission error [${error.key}]: ${error.message}`, error.data);
  } else {
    throw error;
  }
}
```

## Best practices

* **Always `build()` before checking.** Compile the ability once after declaring rules; querying before throws `PermissionException` (`NOT_BUILT`).
* **Keep the chain consistent.** Run `setUserPermissions()` before `allow()` and `build()` last so context-driven rules are in place.
* **Use the action and subject enums.** Prefer `EPermissionAction` and `EPermissionSubject` over raw strings to stay type-safe and consistent.
* **Reserve `manage` / `all` for admins.** The wildcard grants every action on every subject — scope it to trusted roles only.
* **Move ownership logic into the permission.** Pass owner data through the context and decide inside `setUserPermissions()` rather than in the controller.
* **Deny early in `check()`.** Use it for coarse gates (method, IP, headers) and let `can`/`cannot` handle the fine-grained decisions.
* **Throw `PermissionException` with a stable `key`.** Keep keys constant and put variable detail in `data`.

## CLI command

Scaffold a permission class and its test file with the generator. It writes the class under `modules/<module>/src/permissions/<Name>Permission.ts` and installs `@ooneex/permission` if it is missing.

```bash theme={null}
# Interactive: prompts for the name
ooneex permission:create

# Provide the name
ooneex permission:create --name=EditArticle

# Target a module and overwrite
ooneex permission:create --name=EditArticle --module=blog --override
```

| Option       | Description                                                               | Default             |
| ------------ | ------------------------------------------------------------------------- | ------------------- |
| `--name`     | Permission class name. The `Permission` suffix is appended automatically. | Prompted if omitted |
| `--module`   | Target module the class is generated into.                                | `shared`            |
| `--override` | Overwrite an existing class without prompting.                            | `false`             |

The generated class is a ready-to-fill stub with the three methods and inline examples:

```typescript theme={null}
import type { ContextType } from "@ooneex/controller";
import { decorator, Permission } from "@ooneex/permission";

@decorator.permission()
export class EditArticlePermission extends Permission {
  public allow(): this {
    // Example: Add permissions using this.ability.can()
    // this.ability.can("read", "YourEntity");
    // this.ability.can(["read", "update"], "YourEntity", { userId: user.id });

    return this;
  }

  public setUserPermissions(context: ContextType): this {
    // Example: Grant full access to admins
    // const { user } = context;
    //
    // if (!user) {
    //   return this;
    // }
    //
    // const { roles } = user;
    // if (roles.includes("ROLE_ADMIN")) {
    //   this.ability.can("manage", "all");
    //   return this;
    // }

    return this;
  }

  public check(context: ContextType): boolean {
    // Example: Restrict access based on request method
    // const { method } = context;
    // if (method === "DELETE") {
    //   return false;
    // }

    return true;
  }
}
```

See [permission:create](/cli/commands/permission-create) for the full command reference.

## Use with Claude and Codex

The generator ships a matching `permission:create` skill. It runs the scaffold and then guides your AI agent through completing the permission — implementing `allow()` with ability rules and `setUserPermissions()` with role-based logic. Initialize the skills once for your agent:

<Tabs>
  <Tab title="Claude">
    ```bash theme={null}
    ooneex claude:init
    ```

    Then ask Claude in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Create a permission that controls who can edit articles.
    ```
  </Tab>

  <Tab title="Codex">
    ```bash theme={null}
    ooneex codex:init
    ```

    Then ask Codex in natural language — it maps the request to the generator, runs it, and fills in the implementation:

    ```text Prompt icon="terminal" wrap theme={null}
    Create a permission that controls who can edit articles.
    ```
  </Tab>
</Tabs>

For example, the prompt above maps to `permission:create --name=EditArticle`, then implements `allow()` and `setUserPermissions()` so only the right users can edit articles.
