# How Plane Handles Workspace-Level Permission Checks and Authorization in Django

> Discover how the Plane Django backend secures workspaces with DRF custom permission classes and WorkspaceMember model checks. Learn about authorization and user roles.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: architecture
- Published: 2026-06-22

---

**The Plane Django backend enforces workspace-level security through Django REST Framework (DRF) custom permission classes that query the `WorkspaceMember` model to verify user roles and activity status before allowing access to protected endpoints.**

Plane (makeplane/plane) implements a robust role-based access control (RBAC) system for workspace resources using Django REST Framework's permission framework. The backend relies on a centralized set of custom permission classes defined in [`apps/api/plane/utils/permissions/workspace.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/utils/permissions/workspace.py) to validate every request against a single source of truth: the `WorkspaceMember` model. This architecture ensures consistent authorization across all workspace-related API endpoints, from state management to member administration.

## Role Hierarchy and Membership Model

Plane assigns workspace roles using integer constants that define a clear hierarchy. According to the source code in [`apps/api/plane/utils/permissions/workspace.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/utils/permissions/workspace.py), the system recognizes three distinct permission levels:

- **Admin** = 20
- **Member** = 15
- **Guest** = 5

These values are stored on the `WorkspaceMember` model (`plane.db.models.WorkspaceMember`) and persist in the database alongside a membership status flag. The permission system specifically checks the `is_active=True` field to ensure that only active members can access workspace resources, preventing unauthorized access from suspended or removed users.

## Core Permission Classes

The permission architecture centers on `WorkSpaceBasePermission`, which implements generic authorization rules that specialized classes extend.

### WorkSpaceBasePermission

This base class defines the fundamental access control logic:

- **Anonymous users** are immediately denied access.
- **POST requests** (workspace creation) are permitted for any authenticated user.
- **Safe methods** (`GET`, `HEAD`, `OPTIONS`) are allowed for active workspace members.
- **PUT/PATCH requests** require the user to hold an **Admin** or **Member** role.
- **DELETE requests** require the user to be an **Admin** (workspace owner).

### Specialized Permission Classes

Building on the base logic, Plane provides several specialized classes for specific access patterns:

- **`WorkspaceOwnerPermission`**: Restricts access to users with the **Admin** role (20) only.
- **`WorkspaceAdminPermission`**: Grants access to users with **Admin** or **Member** roles (20 or 15).
- **`WorkspaceEntityPermission`**: Used for most entity-level endpoints; allows safe-method reads for any active member while restricting writes to admins or members.
- **`WorkspaceViewerPermission`** and **`WorkspaceUserPermission`**: Provide read-only access for any active workspace member regardless of specific role tier.

## Permission Flow in DRF Views

When a request reaches a workspace endpoint, Django REST Framework evaluates the view's `permission_classes` attribute before executing any view logic.

### Request Evaluation Process

The permission check follows this deterministic flow:

1. DRF calls `has_permission(request, view)` on the configured permission class.
2. The class extracts `request.user` and `view.workspace_slug` from the request context.
3. It queries the `WorkspaceMember` table to verify an active membership link between the user and the workspace identified by `workspace_slug`.
4. It validates the user's role integer against the requirements for the specific HTTP method.
5. If validation fails, the system returns a **403 Forbidden** response immediately, preventing unauthorized code execution.

### View Integration Examples

Concrete views in `apps/api/plane/app/views/workspace/` attach these permissions via the `permission_classes` list. For example, the state management view uses `WorkspaceEntityPermission` to protect CRUD operations:

```python

# apps/api/plane/app/views/workspace/state.py

from plane.app.permissions import WorkspaceEntityPermission

class WorkspaceStateView(APIView):
    permission_classes = [WorkspaceEntityPermission]   # Protects all HTTP methods

    def get(self, request, workspace_slug):
        # Safe method; any active member can read

        ...

    def post(self, request, workspace_slug):
        # Write operation; only Admin/Member can create

        ...

    def delete(self, request, workspace_slug):
        # Delete operation; only Admin can delete

        ...

```

For finer-grained control, views can combine class-level permissions with method decorators. The member management view demonstrates this pattern using the `@allow_permission` decorator:

```python

# apps/api/plane/app/views/workspace/member.py

from plane.app.permissions import WorkspaceEntityPermission, allow_permission, ROLE

class WorkspaceMemberView(APIView):
    permission_classes = [WorkspaceEntityPermission]

    def get(self, request, workspace_slug):
        # List members – any active member can view

        ...

    @allow_permission(ROLE.ADMIN)       # Custom decorator enforcing ADMIN role

    def put(self, request, workspace_slug):
        # Update member roles – only admins can modify

        ...

```

## Key Implementation Files

The authorization system spans several critical files within the repository:

- **[`apps/api/plane/utils/permissions/workspace.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/utils/permissions/workspace.py)**: Defines all workspace permission classes and role constants.
- **[`apps/api/plane/app/permissions/workspace.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/permissions/workspace.py)**: Maintains a duplicate copy of permission definitions for backward compatibility.
- **`apps/api/plane/app/views/workspace/`**: Directory containing concrete view implementations (e.g., [`state.py`](https://github.com/makeplane/plane/blob/main/state.py), [`member.py`](https://github.com/makeplane/plane/blob/main/member.py)) that attach permission classes.
- **[`plane/db/models.py`](https://github.com/makeplane/plane/blob/main/plane/db/models.py)**: Contains the `WorkspaceMember` model serving as the authorization source of truth.

## Summary

- **DRF custom permission classes** centralize authorization logic in [`apps/api/plane/utils/permissions/workspace.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/utils/permissions/workspace.py), ensuring consistent security enforcement.
- **Role hierarchy** uses integer constants (20 for Admin, 15 for Member, 5 for Guest) stored in the `WorkspaceMember` model to determine access levels.
- **Active membership verification** requires `is_active=True` on the `WorkspaceMember` record, preventing access by suspended users.
- **`WorkSpaceBasePermission`** provides HTTP method-based access control, allowing safe reads for all active members while restricting writes to higher roles.
- **Specialized classes** like `WorkspaceEntityPermission` and `WorkspaceOwnerPermission` offer granular control for specific endpoint patterns.
- **Early rejection** occurs when `has_permission` fails, returning 403 responses before view logic executes, minimizing security exposure.

## Frequently Asked Questions

### What file contains the workspace permission classes in Plane?

The primary definitions reside in [`apps/api/plane/utils/permissions/workspace.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/utils/permissions/workspace.py). A duplicate copy exists at [`apps/api/plane/app/permissions/workspace.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/permissions/workspace.py) for backward compatibility, but both define the same role constants and permission classes like `WorkspaceEntityPermission` and `WorkSpaceBasePermission`.

### How does Plane verify workspace membership during authorization?

The permission classes query the `WorkspaceMember` model using `request.user` and `view.workspace_slug` to locate an active membership record. The system specifically checks that `is_active=True` and validates the role integer against the required threshold for the requested HTTP method, ensuring only current, properly-roled users gain access.

### Can guest users modify workspace resources?

No. Guests (role = 5) are restricted to read-only operations. According to the `WorkSpaceBasePermission` logic, write operations such as `PUT`, `PATCH`, and `POST` require at least **Member** level (15) or **Admin** level (20) depending on the specific permission class applied. Only workspace owners (Admins) can execute `DELETE` operations on workspace-level resources.

### How are specific HTTP methods protected differently in the permission system?

The `WorkSpaceBasePermission` class implements method-based discrimination: safe methods (`GET`, `HEAD`, `OPTIONS`) are permitted for any active member, while `PUT` and `PATCH` require Admin or Member roles. `DELETE` operations are restricted to Admins only. Views can further specialize this behavior by using classes like `WorkspaceViewerPermission` for read-only endpoints or applying the `@allow_permission(ROLE.ADMIN)` decorator to specific methods requiring elevated privileges.