# How to Manage User Roles and Permissions in Plane: A Complete Guide

> Master user roles and permissions in Plane. Learn how Plane enforces access control with enums and Django permission classes for robust security. 

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: how-to-guide
- Published: 2026-06-20

---

**Plane manages user roles through numeric enum hierarchies (admin=20, member=15, guest=5) enforced via a centralized permission store on the frontend and Django permission classes on the backend.**

Managing user roles and permissions in Plane requires understanding its dual-layer security model that separates **workspace-level** access from **project-level** access. The open-source project management platform implements a type-safe permission system using TypeScript enums on the frontend and Python permission classes on the backend, ensuring consistent access control across both UI components and API endpoints.

## Understanding Plane's Role Hierarchy

Plane defines role hierarchies through numeric enums where higher values indicate greater privileges. The system recognizes two distinct permission domains that work together to control access.

### Workspace and Project Role Enums

The platform uses two primary enum definitions:

- **`EUserWorkspaceRoles`** (defined in [`packages/types/src/workspace.ts`](https://github.com/makeplane/plane/blob/main/packages/types/src/workspace.ts)): Governs workspace-level access with values `ADMIN = 20`, `MEMBER = 15`, and `GUEST = 5`
- **`EUserPermissions`** (defined in [`packages/constants/src/user.ts`](https://github.com/makeplane/plane/blob/main/packages/constants/src/user.ts)): Controls project-level permissions using identical numeric values (`ADMIN = 20`, `MEMBER = 15`, `GUEST = 5`)

These numeric values enable simple comparison operations throughout the codebase. When a user holds workspace **admin** status, the `BaseUserPermissionStore` automatically elevates their project-level role to `ADMIN` as well, creating a cascading privilege model.

## Frontend Permission Management

The frontend architecture centralizes permission logic in a dedicated store that aggregates user data from both workspace and project contexts.

### The BaseUserPermissionStore

Located at [`apps/web/core/store/user/base-permissions.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/user/base-permissions.store.ts), the `BaseUserPermissionStore` serves as the single source of truth for permission state. It fetches role data through two primary methods:

- **`fetchUserWorkspaceInfo`**: Retrieves workspace membership details
- **`fetchUserProjectInfo`**: Fetches project-specific role assignments

The store exposes computed helpers including `getWorkspaceRoleByWorkspaceSlug` and `getProjectRoleByWorkspaceSlugAndProjectId` to resolve current permission levels. The critical **`allowPermissions`** method accepts an array of permitted roles, a permission level enum (`WORKSPACE` or `PROJECT`), and optional identifiers to determine if the current user satisfies access requirements.

### Role Utility Helpers

Utility functions in [`packages/utils/src/permission/role.ts`](https://github.com/makeplane/plane/blob/main/packages/utils/src/permission/role.ts) handle role transformations:

- **`getUserRole`**: Maps numeric enum values to human-readable strings (e.g., `20` → `"ADMIN"`)
- **`getHighestRole`**: Accepts a set of roles and returns the most privileged one

These utilities power UI components that need to display role labels or determine the highest access level across multiple memberships.

## Backend Enforcement

On the server side, Plane implements Django permission classes to guard API endpoints. The **`ProjectMemberPermission`** class in [`apps/api/plane/app/permissions/project.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/permissions/project.py) inspects the `ProjectMember` model—which stores roles as integers matching the frontend enums—to validate HTTP requests. This ensures that role-based restrictions apply at the API boundary, preventing unauthorized data access even if frontend checks are bypassed.

## Practical Implementation Examples

### Checking Permissions in React Components

To conditionally render UI elements based on project permissions, use the `allowPermissions` method from the root store:

```tsx
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useRootStore } from "@/store";

function DeleteButton({ workspaceSlug, projectId }: { workspaceSlug: string; projectId: string }) {
  const { user } = useRootStore();

  const canDelete = user.allowPermissions(
    [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
    EUserPermissionsLevel.PROJECT,
    workspaceSlug,
    projectId
  );

  return canDelete ? <button>Delete</button> : null;
}

```

This pattern checks if the current user holds either `ADMIN` (20) or `MEMBER` (15) status for the specified project before rendering the delete button.

### Updating Member Roles via API

To programmatically change a user's role, invoke the project member service with the appropriate enum value:

```ts
import projectMemberService from "@/services/project/project-member.service";
import { EUserPermissions } from "@plane/constants";

// Promote a user to project admin
await projectMemberService.updateProjectMember(
  workspaceSlug,
  projectId,
  userId,
  { role: EUserPermissions.ADMIN }
);

```

The service sends the numeric value `20` to the backend, where the `ProjectMember` model stores it in the database.

### Displaying Role Labels

Convert numeric permissions to readable strings for the UI:

```ts
import { getUserRole } from "@plane/utils/permission/role";
import { EUserPermissions } from "@plane/constants";

const roleLabel = getUserRole(EUserPermissions.MEMBER); // Returns "MEMBER"

```

## Summary

- **Plane uses numeric enums** (`EUserPermissions` and `EUserWorkspaceRoles`) with values 20 (admin), 15 (member), and 5 (guest) to represent role hierarchies
- **The `BaseUserPermissionStore`** in [`apps/web/core/store/user/base-permissions.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/user/base-permissions.store.ts) centralizes frontend permission logic and provides the `allowPermissions` method for access checks
- **Workspace admins automatically receive project admin privileges**, creating a cascading permission model
- **Backend enforcement** occurs through `ProjectMemberPermission` in [`apps/api/plane/app/permissions/project.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/permissions/project.py), which validates roles against the `ProjectMember` model
- **Utility functions** in [`packages/utils/src/permission/role.ts`](https://github.com/makeplane/plane/blob/main/packages/utils/src/permission/role.ts) handle conversion between numeric values and readable strings

## Frequently Asked Questions

### What are the different user roles in Plane?

Plane implements three role tiers: **Admin** (numeric value 20), **Member** (15), and **Guest** (5). These values apply to both workspace membership (`EUserWorkspaceRoles`) and project membership (`EUserPermissions`). Higher numeric values indicate greater privileges, enabling simple greater-than comparisons when checking permission levels.

### How does Plane handle workspace admin versus project admin permissions?

According to the `BaseUserPermissionStore` implementation, workspace admins automatically inherit project-level admin rights. When the store's `getProjectRole` method detects that a user holds `ADMIN` status at the workspace level, it elevates their effective project role to `ADMIN` regardless of their specific project assignment. This eliminates the need for redundant role assignments while maintaining security boundaries.

### Where does Plane enforce permission checks in the codebase?

Plane enforces permissions at two primary layers. The frontend uses the `BaseUserPermissionStore` ([`apps/web/core/store/user/base-permissions.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/user/base-permissions.store.ts)) to conditionally render UI elements, while the backend relies on Django permission classes like `ProjectMemberPermission` ([`apps/api/plane/app/permissions/project.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/permissions/project.py)) to protect API endpoints. The backend checks the `ProjectMember` model's integer role field against required levels for each HTTP request.

### How do I check if a user has specific permissions in a Plane React component?

Import `EUserPermissions` and `EUserPermissionsLevel` from `@plane/constants`, then call the `allowPermissions` method from the root store. Pass an array of allowed roles (e.g., `[EUserPermissions.ADMIN, EUserPermissions.MEMBER]`), the permission level (`EUserPermissionsLevel.PROJECT` or `WORKSPACE`), and the relevant workspace slug and project ID. The method returns a boolean indicating whether the current user satisfies the requirements.