# RBAC backend plugin for Backstage

This plugin seamlessly integrates with the [Backstage permission framework](https://backstage.io/docs/permissions/overview/) to empower you with robust role-based access control capabilities within your Backstage environment.

The Backstage permission framework is a core component of the Backstage project, designed to provide meticulous control over resource and action access. Our RBAC plugin harnesses the power of this framework, allowing you to tailor access permissions without the need for coding. Instead, you can effortlessly manage your access policies through User interface embedded within Backstage or via the configuration files.

With the RBAC plugin, you'll have the means to efficiently administer permissions within your Backstage instance by assigning them to users and groups.

## Prerequisites

Before you dive into utilizing the RBAC plugin for Backstage, there are a few essential prerequisites to ensure a seamless experience. Please review the following requirements to make sure your environment is properly set up

### Setup Permission Framework

**NOTE**: This section is only relevant if you are still on the old backend system.

To effectively utilize the RBAC plugin, you must have the Backstage permission framework in place. If you're using the Red Hat Developer Hub, some of these steps may have already been completed for you. However, for other Backstage application instances, please verify that the following prerequisites are satisfied:

You need to [set up the permission framework in Backstage](https://backstage.io/docs/permissions/getting-started/).Since this plugin provides a dynamic policy that replaces the traditional one, there's no need to create a policy manually. Please note that one of the requirements for permission framework is enabling the [service-to-service authentication](https://backstage.io/docs/auth/service-to-service-auth/#setup). Ensure that you complete these authentication setup steps as well.

### Identity resolver

The permission framework, and consequently, this RBAC plugin, rely on the concept of group membership. To ensure smooth operation, please follow the [Sign-in identities and resolvers](https://backstage.io/docs/auth/identity-resolver/) documentation. It's crucial that when populating groups, you include any groups that you plan to assign permissions to.

## Installation

To integrate the RBAC plugin into your Backstage instance, follow these steps.

### Installing the plugin

Add the RBAC plugin packages as dependencies by running the following command.

```SHELL
yarn workspace backend add @backstage-community/plugin-rbac-backend
```

**NOTE**: If you are using Red Hat Developer Hub, backend plugin is pre-installed and you do not need this step.

### Configuring the Backend

#### New Backend System

The RBAC plugin supports the integration with the new backend system.

Add the RBAC plugin to the `packages/backend/src/index.ts` file and remove the Allow All Permission policy module.

```diff
// permission plugin
backend.add(import('@backstage/plugin-permission-backend'));
- backend.add(
-    import('@backstage/plugin-permission-backend-module-allow-all-policy'),
-  );
+ backend.add(import('@backstage-community/plugin-rbac-backend'));
```

### Configure policy admins

The RBAC plugin empowers you to manage permission policies for users and groups with a designated group of individuals known as policy administrators. These administrators are granted access to the RBAC plugin's REST API and user interface as well as the ability to read from the catalog.

You can specify the policy administrators in your application configuration as follows:

```YAML
permission:
  enabled: true
  rbac:
    admin:
      users:
        - name: user:default/alice
        - name: group:default/admins
```

The RBAC plugin also enables you to grant users the title of 'super user,' which provides them with unrestricted access throughout the Backstage instance.

You can specify the super users in your application configuration as follows:

```YAML
permission:
  enabled: true
  rbac:
    admin:
      superUsers:
        - name: user:default/alice
        - name: user:default/mike
        - name: group:default/admins
```

> **Note:** **Transient memberships are not supported for `superUsers`.** Meaning, when a group is specified as a super user, only direct group memberships are taken into account. Users who belong to a sub-group of a configured super user group will not be granted super user access.

For more information on the available API endpoints accessible to the policy administrators, refer to the [API documentation](./docs/apis.md).

### Configure default role

You can optionally assign a default role to all authenticated users by using `defaultPermissions.defaultRole`.
This ensures that every authenticated user receives the specified role in addition to any other roles they may have.
You can also define baseline permissions for that role using `defaultPermissions.basicPermissions`.
This is especially useful when using [Sign-In without Users in the Catalog](https://backstage.io/docs/auth/identity-resolver/#sign-in-without-users-in-the-catalog).

```YAML
permission:
  rbac:
    defaultPermissions:
      defaultRole: role:default/my-default-role
      basicPermissions:
        - permission: catalog.entity.read
          action: read
        - permission: catalog-entity
          action: read
        - permission: catalog.entity.create
          action: create
```

If configured, the RBAC backend will automatically include the default role in each authenticated user's roles and evaluate the configured `basicPermissions` for that role.
When `defaultPermissions.defaultRole` is set, `defaultPermissions.basicPermissions` must contain at least one permission entry.

### Configure plugins with permission

In order for the RBAC UI to display the available permissions provided by installed plugins, you must supply the corresponding list of plugin IDs. There are two ways to achieve this:

- Application configuration(`app-config.yaml`)
- REST API

#### Configure plugins with Application configuration

You can specify the plugins with permissions in your application configuration as follows:

```YAML
permission:
  enabled: true
  rbac:
    pluginsWithPermission:
      - catalog
      - scaffolder
      - permission
    admin:
      users:
        - name: user:default/alice
        - name: group:default/admins
```

#### Configure plugins with REST API

You can specify the plugins with permissions using the corresponding [REST API](./docs/apis.md#plugin-ids-that-support-the-backstage-permission-framework).

Curl Examples:

Get the object containing the list of plugin IDs:

```
curl -X GET "http://localhost:7007/api/permission/plugins/id" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $token" -v
```

Add more plugin IDs:

```
curl -X POST "http://localhost:7007/api/permission/plugins/id" \
  -d '{ "ids": [ "permission", "scaffolder" ] }' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $token" -v
```

Remove plugin IDs:

```
curl -X DELETE "http://localhost:7007/api/permission/plugins/id" \
  -d '{ "ids": [ "permission", "scaffolder" ] }' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $token" -v
```

Notice: The REST API does not allow deletion of plugin IDs that were provided via application configuration, in order to prevent an inconsistent state after a deployment restart. These ID values can only be removed through the configuration file.

For more information on the available permissions, refer to the [RBAC permissions documentation](./docs/permissions.md).

### Configuring policies via file

The RBAC plugin also allows you to import policies from an external file. These policies are defined in the [Casbin rules format](https://casbin.org/docs/category/the-basics), known for its simplicity and clarity. For a quick start, please refer to the format details in the provided link.

Here's an example of an external permission policies configuration file named `rbac-policy.csv`:

```CSV
p, role:default/team_a, catalog-entity, read, deny
p, role:default/team_b, catalog.entity.create, create, deny

g, user:default/bob, role:default/team_a

g, group:default/team_b, role:default/team_b
```

---

**NOTE**: When you add a role in the permission policies configuration file, ensure that the role is associated with at least one permission policy with the `allow` effect.

---

You can specify the path to this configuration file in your application configuration:

```YAML
permission:
  enabled: true
  rbac:
    policies-csv-file: /some/path/rbac-policy.csv
```

Also, there is an additional configuration value that allows for the reloading of the CSV file without the need to restart.

```YAML
permission:
  enabled: true
  rbac:
    policies-csv-file: /some/path/rbac-policy.csv
    policyFileReload: true
```

For more information on the available permissions, refer to the [RBAC permissions documentation](./docs/permissions.md).

We also have a fairly strict validation for permission policies and roles based on the originating role's source information, refer to the [api documentation](./docs/apis.md).

#### Policy validation and compatibility (hardening)

Recent releases tighten validation so malformed or unsafe policy data cannot poison the Casbin store (which uses CSV-shaped persistence for database-backed policies). In practice:

- **`permission` values must not contain double quotes (`"`)** on REST writes, CSV file import, providers, and configured `defaultPermissions.basicPermissions`. Values that previously slipped through could break policy loading; they are now rejected up front. See [API documentation](./docs/apis.md) for the REST contract.
- **Full policy reload failures are surfaced correctly**: if the enforcer cannot reload policies from storage (for example due to legacy poisoned rows), errors propagate after audit logging instead of leaving a stale model and misleading follow-on errors.
- **Policy write APIs** expect bodies in the documented shapes (for example, `POST`/`DELETE` `/policies` bodies must be JSON **arrays**). Invalid shapes return `400` with clear messages where applicable.
- **Configured admins and default permissions** are validated with the same policy rules as runtime writes where relevant, so bad config fails early at startup or sync instead of corrupting storage.
- **Plugin ID registration** (`POST`/`DELETE` `/plugins/id`) enforces sensible bounds (list size, per-ID length, no duplicates). Very large registrations may need to be split into multiple requests.
- **CSV policy files** (`policies-csv-file`) are parsed **line by line**. Lines that are syntactically invalid for CSV parsing are **skipped with a warning** so the rest of the file can still be applied; fix or remove bad lines in the file for a fully consistent load. Semantic validation (invalid entity refs, duplicates, source mismatches) continues to skip individual rows with warnings, as before.

- **Conditional policies**: `permissionMapping` must list **distinct** Backstage permission actions only (no duplicates), with at most one entry per supported action (`create`, `read`, `update`, `delete`, `use`). Criteria trees and YAML conditional files still support optional `permission.rbac.validation.*` limits described below.

### Configuring conditional policies via file

The RBAC plugin allows you to import conditional policies from an external file. User can defined conditional policies for roles created with the help of the policies-csv-file. Conditional policies should be defined as object sequences in the YAML format.

You can specify the path to this configuration file in your application configuration:

```YAML
permission:
  enabled: true
  rbac:
    conditionalPoliciesFile: /some/path/conditional-policies.yaml
    policies-csv-file: /some/path/rbac-policy.csv
```

Also, there is an additional configuration value that allows for the reloading of the file without the need to restart.

```YAML
permission:
  enabled: true
  rbac:
    conditionalPoliciesFile: /some/path/conditional-policies.yaml
    policies-csv-file: /some/path/rbac-policy.csv
    policyFileReload: true
```

This feature supports nested conditional policies.

Example of the conditional policies file:

```yaml
---
result: CONDITIONAL
roleEntityRef: role:default/test
pluginId: catalog
resourceType: catalog-entity
permissionMapping:
  - read
  - update
conditions:
  rule: IS_ENTITY_OWNER
  resourceType: catalog-entity
  params:
    claims:
      - group:default/team-a
      - group:default/team-b
---
result: CONDITIONAL
roleEntityRef: role:default/test
pluginId: catalog
resourceType: catalog-entity
permissionMapping:
  - delete
conditions:
  rule: IS_ENTITY_OWNER
  resourceType: catalog-entity
  params:
    claims:
      - group:default/team-a
```

Information about condition policies format you can find in the doc: [Conditional policies documentation](./docs/conditions.md). There is only one difference: yaml format compare to json. But yaml and json are back convertiable.

### Optional validation limits for conditional policies

The RBAC backend now supports configurable limits for conditional policy payload complexity and conditional YAML file ingestion size. Defaults are chosen to be protective while still supporting typical policy definitions.

Use these settings if your environment has larger policy sets and needs temporary tuning during upgrades:

```YAML
permission:
  enabled: true
  rbac:
    validation:
      conditionalPolicies:
        maxConditionDepth: 12
        maxConditionNodeCount: 256
        maxCriteriaItems: 64
      conditionalPoliciesFile:
        maxBytes: 1048576
        maxDocuments: 256
```

Field descriptions:

- `permission.rbac.validation.conditionalPolicies.maxConditionDepth`: Maximum nesting depth in a conditional criteria tree.
- `permission.rbac.validation.conditionalPolicies.maxConditionNodeCount`: Maximum number of criteria nodes in a conditional criteria tree.
- `permission.rbac.validation.conditionalPolicies.maxCriteriaItems`: Maximum number of entries allowed per `allOf`/`anyOf`.
- `permission.rbac.validation.conditionalPoliciesFile.maxBytes`: Maximum allowed byte size for `conditionalPoliciesFile`.
- `permission.rbac.validation.conditionalPoliciesFile.maxDocuments`: Maximum number of YAML documents allowed in `conditionalPoliciesFile`.

All values must be positive integers.

If you already use very large conditional payloads or YAML files, raise these limits in config during upgrade rather than relying on defaults.

### Configuring Database Storage for policies

The RBAC plugin offers the option to store policies in a database. It supports two database storage options:

- sqlite3: Suitable for development environments.
- postgres: Recommended for production environments.

Ensure that you have already configured the database backend for your Backstage instance, as the RBAC plugin utilizes the same database configuration.

#### Database connections and pool limits

The RBAC backend currently uses **two separate PostgreSQL connection paths** for the database:

1. **Knex** — conditional policies, role metadata, etc for the permission plugin
2. **TypeORM (Casbin adapter)** — Casbin policy storage for RBAC

Each path maintains its own connection pool. In horizontally scaled (HA) deployments, this extra pool can contribute to maxing out connection resources.

**Mitigations today:**

- Lower `backend.database.knexConfig.pool.max` to reduce per-plugin pool size.
- Size your PostgreSQL instance to account for total connections across all Backstage core plugins and RBAC's Casbin pool.

Consolidating RBAC onto a single shared database connection for both Knex and Casbin is a known improvement area to be addressed in the future.

#### Passwordless PostgreSQL in the Cloud

The RBAC plugin stores policies in the same database configured under `backend.database`. Passwordless authentication is supported when Backstage configures a dynamic Knex connection resolver, including **Azure Database for PostgreSQL with Entra authentication** (`connection.type: azure`) and **AWS RDS with IAM authentication** (`connection.type: rds`). Configure `backend.database` the same way as the rest of your Backstage instance — see [Passwordless PostgreSQL in the Cloud](https://backstage.io/docs/getting-started/config/database/#passwordless-postgresql-in-the-cloud) in the Backstage documentation. No additional RBAC-specific database configuration is required.

Google Cloud SQL with Cloud IAM (`connection.type: cloudsql`) is not supported for RBAC policy storage yet.

### Optional maximum depth

The RBAC plugin also includes an option max depth feature for organizations with potentially complex group hierarchy, this configuration value will ensure that the RBAC plugin will stop at a certain depth when building user graphs.

```YAML
permission:
  enabled: true
  rbac:
    maxDepth: 1
```

The maxDepth must be greater than 0 to ensure that the graphs are built correctly. Also the graph will be built with a hierarchy of 1 + maxDepth.

More information about group hierarchy can be found in the doc: [Group hierarchy](./docs/group-hierarchy.md).

### Optional RBAC provider module support

We also include the ability to create and load in RBAC backend plugin modules that can be used to make connections to third part access management tools. For more information, consult the [RBAC Providers documentation](./docs/providers.md).

### Optional configuration to control policy decision precedence

Controls the evaluation order between permission policies (basic) and conditional policies for resource permissions.

- Default: `conditional` (conditional policies take precedence when present)
- Set to `basic` to evaluate basic permission policy first

```YAML
permission:
  enabled: true
  rbac:
    policyDecisionPrecedence: basic # or conditional
```
