* fix(permissions): add 11 missing resource types to RESOURCE_HIERARCHY
ResourcePermissionEndpoint and RoleSerializer rely on RESOURCE_HIERARCHY
to enumerate assignable permissions and validate custom roles. 11 types
present in RESOURCE_ACTIONS were missing, causing them to not appear in
the custom role editor and be rejected during role saves.
Added leaf entries and parent children references for: ANALYTICS, AI,
WORKSPACE_ACTIVITY, INITIATIVE_UPDATE_COMMENT, TEAMSPACE_COMMENT,
TEAMSPACE_WORKITEM_VIEW, TEAMSPACE_PAGE, TEAMSPACE_PAGE_COMMENT,
EPIC_UPDATE_COMMENT, PROJECT_ACTIVITY, PROJECT_MEMBER_ACTIVITY.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* Removed role and team from subject_type
* refactor(permissions): unify context derivation via RESOURCE_HIERARCHY and fan-out cache invalidation
Replace hard-coded param-to-scope mappings in both the @can decorator
(_build_permission_context) and the DRF class (HasResourcePermission)
with RESOURCE_HIERARCHY lookups, eliminating duplicated scope inference
logic. Add _invalidate_caches_for_subject to fan out cache invalidation
to member users when granting/revoking non-user subjects (e.g.,
teamspace→project links). Route ResourceGrantsViewSet.partial_update
through permission_engine.grant() for proper audit logging and cache
invalidation.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: project and teamspace access permissions
* refactor(permissions): consolidate cache invalidation into cache.py, extract grants.py and resource_models.py
Move cache invalidation functions to a dedicated cache.py module (single home
for versioned per-user cache strategy). Extract Grant dataclass and
grant/revoke operations into grants.py, resource model registry into
resource_models.py, and AccessResult into context.py — keeping engine.py
focused on permission resolution. Fix all lint errors (F821 forward refs
via TYPE_CHECKING, F401 unused imports in tests).
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* test(permissions): add integration tests for @can decorator lifecycle
Add 8 integration tests exercising the full DRF dispatch pipeline:
@can → engine.check → resolution → response. Uses stub ViewSets with
APIRequestFactory to validate admin/outsider/guest/contributor/commenter
access, deferred condition enforcement, and anonymous user blocking.
Also refactors engine._resolve_permission to extract the shared
resolution loop into _resolve_permission_from_tuples with debug logging.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(permissions): stop using all_objects in grant operations, clean up indexes
Grant operations now use the active-only manager (objects) instead of
all_objects, preventing MultipleObjectsReturned crashes when multiple
soft-deleted rows exist for the same tuple. Soft-deleted records are
left as tombstones. Also removes unique_together (redundant with the
conditional UniqueConstraint) and adds deleted_at__isnull=True to all
ResourcePermission indexes so dead rows aren't indexed.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* style(permissions): minor formatting in decorators.py
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* feat(permissions): make bulk_check() truly batched with O(1) DB queries per hierarchy level
bulk_check() was a loop calling check() N times (N×2-5 queries on cache miss).
Rewrite to batch-build hierarchy chains and prefetch all tuples in a single pass,
reducing query cost to ~4-5 total regardless of N.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* Update guest resource relation based on the guest_view_all_features toggle
* feat(permissions): single-query hierarchy traversal, bridge model support, and import consolidation
Optimize hierarchy chain building to use a single DB query for direct-FK
resource types instead of one query per hierarchy level. Add bridge table
support for page, teamspace_page, and teamspace_workitem_view types that
use M2M through tables. Consolidate HasResourcePermission into the
permissions package and remove the standalone drf.py module. Fix page
view resource_param to use page_id instead of project_id.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: minor bug fixes related to guest user access
* feat: milestone permissions refactor
* Fix lint errors
* refactor: update ProjectArchivedEpicsEmptyState to use props for permissions
- Removed direct user permission checks and replaced them with props to manage filter clearing permissions.
- Simplified the component structure for better readability and maintainability.
* chore: minor cleanup of unused code
* refactor: split permission engine into focused modules, improve permission models
* fix: remove unused imports from engine/core.py
* chore: permissions for wiki
* fix: batch migration queries, add --background flag to init_permissions
* feat: add custom roles and permissions feature flag
* fix: remove auto observables from super classes
* feat: return system role permissions from code and support conditional permissions in custom roles
RoleSerializer changes:
- to_representation: source permissions from system_roles.py for system roles instead of empty DB field
- validate_permissions: support +condition format (e.g., "comment:edit+creator"), validate condition against Condition enum
- validate: strip +condition suffix before namespace validation check
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* Fixed migration sequence
* fix: update delete issue modal to use issue ID for delete permission check
* fix: correct opacity logic for edit permissions in properties tabs
* fix: use of `useTimeLineChartStore` without `TimeLineTypeContext`
* refactor: enhance module permissions handling with new metadata retrieval logic
- Updated `ModulePermissionsInstance` to accept a more flexible constructor for permission checks.
- Introduced `getModuleMetaById` as a private helper in `ModulesStore` to streamline module metadata retrieval.
- Adjusted permission methods to utilize the new metadata structure, improving clarity and maintainability.
* fix: correct opacity logic for export access permissions in settings page
- Updated the opacity logic in the ExportsPage component to correctly reflect access permissions by changing the condition from `!canAccessExports` to `canAccessExports`.
* refactor: update workspace settings access logic to use route-based checks
- Renamed `canAccessWorkspaceSettingByAccessKey` to `canAccessWorkspaceSettingByRoute` for clarity.
- Updated components to utilize the new route-based access check, improving maintainability and consistency across the settings pages.
* fix: update workspace settings access permissions
* refactor: update customer request permissions to use customer permissions
- Modified `getCanEdit` and `getCanDelete` methods to utilize `customerId` instead of `requestId`, enhancing clarity and consistency in permission checks for customer resources.
* refactor: update permissions to use correct resource names
- Changed resource names in `CustomerRequestPermissionsInstance` and `InitiativeLabelPermissionsInstance` to reflect the correct entities, enhancing clarity in permission checks.
- Updated `getCanEditEpic` method in `InitiativePermissionsInstance` to use a placeholder for epicId, improving code consistency.
* refactor: update project and workspace settings access logic
- Modified `canAccessProjectSettingByAccessKey` and `canAccessWorkspaceSettingByRoute` to use `accessKey.startsWith(setting.href)` for more accurate permission checks.
- This change enhances the flexibility of access checks by allowing partial matches on the access key, improving overall permission handling.
* chore: remove unused console logs
* refactor: comment out unused initiative_label permissions
- Commented out the `initiative_label` permissions in `resource-actions.ts` to indicate they are currently unused, improving clarity in the permission definitions.
* fix: format
* Added created_by info in the module response
* fix: normalize workspace_id to UUID in has_active_membership
workspace_id from middleware is a string; direct_tuples keys are UUIDs.
The type mismatch caused conditional grants (+creator) to silently fail
for workspace-scoped resources.
* feat: roles management creation and editing page
* refactor: enhance roles and permissions comparison modal
- Added copyright header to `comparison-modal.tsx` for compliance.
- Refactored the rendering logic for permission cells to improve clarity and maintainability.
- Updated the modal's header structure and button styles for consistency.
- Adjusted navigation path in `create-root.tsx` to ensure correct routing.
* refactor: improve roles and permissions settings UI
- Adjusted padding in `SettingsControlItem` for better spacing.
- Refactored `RolesAndPermissionsSettingsPageRolesList` to destructure props for clarity and added conditional rendering for system roles.
- Replaced `Tabs` with `TabNavigationList` and `TabNavigationItem` in `RolesAndPermissionsSettingsPageRoot` for enhanced navigation experience.
* feat: add role_ref FK to member tables and slug-based guest ceiling
Add `role_ref` FK to Role on WorkspaceMember, ProjectMember,
WorkspaceMemberInvite, and ProjectMemberInvite. The FK is the source
of truth for role assignment — numeric `role` is derived from
`role_ref.level` in save(). Reverse sync (numeric → FK) handles
external APIs and old code paths.
- Add bidirectional sync in save() for both member models
- Extract plan-aware owner/admin logic into _resolve_admin_or_owner()
- Simplify _get_permission_relation() to return role_ref.slug directly
- Add role_ref_id to TRACKED_FIELDS and PERMISSION_RELEVANT_FIELDS
- Replace hardcoded numeric ceiling checks with slug-based
is_project_role_allowed_for_workspace_role()
- Add resolve_role_slug() helper for API input resolution
- Fix .role on QuerySet bug in ProjectInvitationsViewset
- Add unit tests for workspace guest project ceiling
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* dx: improve permissions codebase developer experience
Auto-derive permission registries and resource groupings instead of manual
lists. Replace raw string grants in system roles with typed objects
(WildcardGrant, FULL_ACCESS, Permission & Condition). Add startup validation
for permission system consistency. Auto-generate __all__ from registry.
Remove verbose debug logging from decorators. Rename IssuePermissions to
WorkitemPermissions to match domain terminology. Update CLAUDE.md with
"Adding a New Resource Type" guide and typed grant documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* Fixed migration sequence
* feat: merge role_ref FK schema and backfill into existing migrations
Merge role_ref AddField operations into 0132 (schema migration) and
add role_ref backfill steps 7/8 and 8/8 into 0133 (data migration).
Schema (0132):
- Add role_ref FK (nullable, PROTECT) on WorkspaceMember, ProjectMember,
WorkspaceMemberInvite, ProjectMemberInvite
Data backfill (0133):
- Pre-cache all system Role PKs for zero per-member queries
- Backfill role_ref on workspace/project members with plan-aware
owner/admin resolution for role=20
- Backfill role_ref on workspace/project invites
- Fix backwards_func: clear role_ref before deleting Roles (PROTECT)
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* feat: migrate post-preview-merge endpoints to new permission system
Repurpose WORKITEM_RELATION resource from project-scoped issue relations
to workspace-scoped custom relation definitions. Issue-to-issue linking
now uses WorkitemPermissions.EDIT. Migrate workflow and relation
definition endpoints from @allow_permission to @can decorator.
- Add EDIT to WorkitemRelationPermissions, CREATE to WorkflowPermissions
- Move WORKITEM_RELATION hierarchy from WORKITEM→WORKSPACE
- Migrate WorkItemRelationDefinitionViewSet (5 methods)
- Switch WorkItemRelationDependencyViewSet to WorkitemPermissions (3 methods)
- Migrate WorkItemRelationRelationViewSet from ProjectEntityPermission (3 methods)
- Migrate WorkflowEndpoint/DefaultWorkflowEndpoint post methods (2 methods)
- Migrate WorkflowStatesEndpoint/WorkflowStateTransitionsEndpoint (6 methods)
- Move workitem_relation from project to workspace scope in frontend types
- Update permission migration docs and matrix
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: build error and implement TODO changes
* refactor: consolidate frontend permission documentation
Removed the outdated Conditional Permissions Guide and replaced it with a comprehensive RBAC/GAC Frontend Permission System reference. This new document serves as the single source of truth for all frontend permission work, detailing permission string formats, core stores, implementation patterns, and UI conventions. It aims to streamline the understanding and implementation of permission-related code across the frontend.
* feat: add role_slug API support, auto-join mapping, and ceiling enforcement
Replace numeric role input with role_slug (string) across internal member
APIs. Responses now include both role_slug and role (read-only). Sending
numeric role in write requests returns 400.
Key changes:
- role_ref FK on member/invite tables as source of truth for role assignment
- Slug-based auto-join mapping: owner/admin→admin, guest→guest, else→contributor
- Workspace guest ceiling: guests capped at commenter for explicit assignment
- Signal handler safety net backfills role_ref on bulk_create paths
- Plan change task updates role_ref alongside ResourcePermission
- Serializer mixins (ProjectRoleSlugMixin, WorkspaceRoleSlugMixin) for DRY
- Pre-fetched workspace members to avoid N+1 in ceiling check loop
- Seat limit check runs before guest demotion side effects
- Audit trail captures pre-mutation state for accurate activity logs
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(permissions): harden permission engine and fix role_ref sync
- Return AccessResult(allowed=False) for invalid permission strings instead
of logging and continuing, preventing wildcard grants from matching
- Add permissions_deny check on link relation tuples in the resolver,
ensuring GAC deny rules propagate through teamspace→project links
- Re-derive role_ref when numeric role changes while FK is already set,
fixing stale role_ref causing incorrect permission sync
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: dispatch error response, UUID validation, and bulk grant atomicity
- Return response (not exception object) from dispatch error handler
across all three base classes (app, api, BaseAPIView)
- Skip deferred-conditions enforcement on error responses (status >= 400)
to prevent masking real errors with false 403s
- Validate UUID query params in permission check/resource/relation
endpoints, returning 400 instead of unhandled 500
- Wrap bulk_grant_permissions in transaction.atomic() with
select_for_update() to prevent TOCTOU race on concurrent grants
- Update test to expect 403 Response instead of raw PermissionDenied
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* chore: added workflow permissions
* chore: added premissions for relation management
* fix: build errors
* fix: format
* fix(teamspace): block workspace guests from being added to teamspaces
Workspace guests should not be teamspace members because teamspace
membership grants implicit project access via link relations. The
inverse is already enforced (guest demotion deletes teamspace
memberships), but the add direction had no guard.
Filter added_members against eligible non-guest workspace members
before bulk_create, using role_ref slug with numeric fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* feat: migrate release module and formula endpoint to new permission system
Add RELEASE resource type (workspace-scoped) with ReleasePermissions
class. Migrate all 10 release view files (~37 methods) from
@allow_permission to @can decorator. Migrate IssuePropertyFormula
ValidateEndpoint from ProjectEntityPermission to @can.
- Add RELEASE to ResourceType, hierarchy, model mapping, role grants
- Migrate ReleaseEndpoint, ReleaseTagEndpoint, ReleaseLabelEndpoint,
ReleaseWorkItemEndpoint, ReleaseCommentViewSet,
ReleaseCommentReactionViewSet, ReleaseChangelogEndpoint,
ReleasePageEndpoint, ReleaseAttachmentEndpoint,
ReleaseActivityEndpoint, ReleaseLinkViewSet
- Add inline creator checks for comment edit/delete and attachment delete
- Migrate IssuePropertyFormulaValidateEndpoint to IssuePropertyPermissions.EDIT
- Add release to frontend workspace permission resource actions
- Update permission migration docs and matrix
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(permissions): harden cache layer and batch cache operations
Cache correctness:
- Add IGNORE_EXCEPTIONS to Redis cache config for graceful degradation
when Redis is down (falls through to DB instead of 500)
- Fix TOCTOU race in invalidate_cache_for_user by using raw Redis INCR
(atomic, auto-creates) instead of cache.incr/cache.set pattern
- Add VERSION_KEY_TTL (24h) to bound Redis memory from version counters
- Invalidate custom_role_slugs cache on Role create/delete
- Harden AccessResult.from_cache with try/except for corrupted data
Cache performance:
- Batch cache operations in check_batch() and bulk_check() using
cache.get_many/set_many (945 Redis round-trips → 3 per request)
- Add version param to _get_cache_key to avoid redundant version
lookups in batch operations
Also:
- Fix dispatch return exc → return response in all 3 base classes
- Skip deferred-conditions check on error responses (status < 400)
- Validate UUID query params in permission check endpoints (400 vs 500)
- Wrap bulk_grant_permissions in transaction.atomic + select_for_update
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(permissions): fix role_ref backfill bugs and merge into single-pass steps
Fix two bugs in migration 0133 role_ref backfill:
- Business/Enterprise FK owners got role_ref→admin instead of →owner
- Free/Pro/One workspace invites got role_ref→admin instead of →owner
Merge role_ref setting into earlier iteration steps to avoid redundant
passes over workspace/project members. Enhance stale owner fix to fall
back to highest-role active member when no role=20 member exists.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(permissions): add role_ref backfill to init_permissions command
The init_permissions --migrate-members command created ResourcePermission
tuples but did not set role_ref on member tables. Now sets role_ref in
the same pass as permission creation, matching migration 0133's approach.
Also narrows owner_map prefetch to Business/Enterprise workspaces only.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* chore: added gac permissions for labels
* chore: added permissions for releases
* chore: replaced allowPermissions for manage billing
* chore: added add seats permission at inivite member modal
* fix: build errors
* fix: reorganize permission imports in issue property and type views
- Moved `WorkspaceEntityPermission` import from `plane.permissions` to `plane.ee.permissions` in `formula.py`, `option.py`, and `type.py` files for better clarity and consistency in permission management.
* chore: added permissions for getting started
* fix: getting started at top navigation
* chore: removed redundant admin check for settings page
* refactor: update permission checks to use role slugs instead of relations
- Replaced instances of `getCurrentUserWorkspaceRelation` and `getCurrentUserProjectRelation` with `getCurrentUserWorkspaceRoleSlug` and `getCurrentUserProjectRoleSlug` across various components and hooks.
- Introduced `isGuestRole` utility to streamline guest role checks in permission logic.
- Updated related components to ensure proper handling of user permissions based on roles, enhancing clarity and maintainability.
* fix: build errors
* feat: enhance workspace member invite and role management (#6351)
* feat: enhance workspace member invite and role management
- Introduced role_slug to various serializers and views to improve role handling.
- Updated WorkspaceMemberInvite to auto-populate role_ref from numeric role.
- Refactored member update and invite logic to utilize role_slug for better clarity and consistency.
- Adjusted payment and license tasks to account for role_slug, ensuring accurate member billing and licensing.
- Improved error handling for role updates and invitations, enforcing role hierarchy more effectively.
* feat(permissions): add management authority enforcement
Add tier-based protection guards that prevent unauthorized management of
protected system roles (owner, admin). Custom roles are unprotected by design.
Layer 1 (view helpers):
- can_manage_role() — checks if actor can modify/remove a target member
- can_assign_role() — checks if actor can assign/invite into a role
- Applied to workspace member partial_update, destroy, invite create,
and invite partial_update endpoints. Returns 403 on violation.
Layer 2 (defense in depth):
- _validate_management_authority() in PermissionSyncMixin runs before
engine.grant(), catching cases where a developer forgets Layer 1.
- Checks both new role (assign) and old role (manage) using
ChangeTrackerMixin tracked values. Raises PermissionDenied on violation,
rolling back the transaction atomically.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* feat: standardize role handling across project member components and improved role management
- Replaced `original_role` with `role_slug` in serializers and views for project members to enhance clarity and consistency.
- Updated member-related logic in various components to utilize `role_slug`, improving role management and reducing reliance on numeric roles.
- Adjusted filtering and sorting functions to accommodate the new role structure, ensuring accurate member representation.
- Enhanced error handling and role validation during member updates and invitations, aligning with recent role management improvements.
* feat(permissions): add management authority enforcement for project members
Extend tier-based protection guards to project member views. Only project
admins can manage other project admins. Non-admin roles (contributor,
commenter, guest, custom) are unprotected.
Layer 1 (views):
- Applied to project member create, partial_update, and destroy
- Workspace admin/owner fallback: actors without project membership
are treated as admin-tier for tier checks
Layer 2 (sync guard):
- Extended _validate_management_authority to handle project resource type
- Resolves actor from ProjectMember first, falls back to WorkspaceMember
for workspace admin/owner actors
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---------
Co-authored-by: Dheeraj Kumar Ketireddy <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
* Fixed migration sequence
* chore: legacy role and permissions cleanup (#6502)
* feat: enhance workspace member invite and role management
- Introduced role_slug to various serializers and views to improve role handling.
- Updated WorkspaceMemberInvite to auto-populate role_ref from numeric role.
- Refactored member update and invite logic to utilize role_slug for better clarity and consistency.
- Adjusted payment and license tasks to account for role_slug, ensuring accurate member billing and licensing.
- Improved error handling for role updates and invitations, enforcing role hierarchy more effectively.
* feat(permissions): add management authority enforcement
Add tier-based protection guards that prevent unauthorized management of
protected system roles (owner, admin). Custom roles are unprotected by design.
Layer 1 (view helpers):
- can_manage_role() — checks if actor can modify/remove a target member
- can_assign_role() — checks if actor can assign/invite into a role
- Applied to workspace member partial_update, destroy, invite create,
and invite partial_update endpoints. Returns 403 on violation.
Layer 2 (defense in depth):
- _validate_management_authority() in PermissionSyncMixin runs before
engine.grant(), catching cases where a developer forgets Layer 1.
- Checks both new role (assign) and old role (manage) using
ChangeTrackerMixin tracked values. Raises PermissionDenied on violation,
rolling back the transaction atomically.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* feat: standardize role handling across project member components and improved role management
- Replaced `original_role` with `role_slug` in serializers and views for project members to enhance clarity and consistency.
- Updated member-related logic in various components to utilize `role_slug`, improving role management and reducing reliance on numeric roles.
- Adjusted filtering and sorting functions to accommodate the new role structure, ensuring accurate member representation.
- Enhanced error handling and role validation during member updates and invitations, aligning with recent role management improvements.
* feat(permissions): add management authority enforcement for project members
Extend tier-based protection guards to project member views. Only project
admins can manage other project admins. Non-admin roles (contributor,
commenter, guest, custom) are unprotected.
Layer 1 (views):
- Applied to project member create, partial_update, and destroy
- Workspace admin/owner fallback: actors without project membership
are treated as admin-tier for tier checks
Layer 2 (sync guard):
- Extended _validate_management_authority to handle project resource type
- Resolves actor from ProjectMember first, falls back to WorkspaceMember
for workspace admin/owner actors
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: update project role handling to use role slugs
- Replaced instances of `getProjectRoleByWorkspaceSlugAndProjectId` with `getCurrentUserProjectRoleSlug` across various components to standardize role management.
- Updated `revalidateProjectData` and related fetch keys to utilize `projectRoleSlug` instead of `EUserPermissions`, enhancing clarity and consistency in permission checks.
- Adjusted components to ensure proper handling of user permissions based on role slugs, improving maintainability and reducing reliance on numeric roles.
* refactor: standardize permission checks across custom properties and work item types
- Replaced instances of role-based permission checks with a unified `can` method across various stores, enhancing clarity and consistency in permission management.
- Updated `RootCustomPropertiesStore`, `WorkspaceCustomPropertiesStore`, `RootWorkItemTypesStore`, and related components to utilize the new permission structure.
- Improved maintainability by reducing reliance on specific role retrieval methods, streamlining permission logic for workspace and project resources.
---------
Co-authored-by: Dheeraj Kumar Ketireddy <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
* fix: build errors and permission updates
* feat: Introduce ResourcePermission and PermissionAuditLog models with role management
- Added ResourcePermission model for managing resource access permissions.
- Introduced PermissionAuditLog model to track changes in permissions.
- Created Role model for defining roles with associated permissions.
- Implemented migration to backfill existing roles and permissions for workspaces and projects.
- Updated workspace and project member models to reference roles.
- Enhanced automation endpoints to use new permission checks instead of role-based checks.
* chore: default automation permissions checks
* chore: initiative epic and project scope empty state permissions
* feat: Implement user role assignment for guests across payment tasks and views
* feat: implement granular RBAC for billing and plans access within workspace settings
* fix: format and build errors
* feat: add RBAC permission check to MarkDefaultIssueTypeEndpoint
* feat: workspace and project level automation permissions implementation
* refactor: split AutomationPermissions into ProjectAutomationPermissions and WorkspaceAutomationPermissions for granular access control
* fix: maintain backward-compatible role numbers (20, 15, 5) in member tables
The new Role model has levels like 25 (owner) and 10 (commenter) that
don't exist in the legacy system. Previously, member.role was derived
directly from role_ref.level, breaking backward compatibility. This adds
a centralized member_role_from_role_ref() mapping function and replaces
all 11 call sites that used .level to set .role.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: update user role assignment to use UNPAID_ROLE_SLUGS for consistency across payment tasks
* chore/rbac: permission folding (#6463)
* chore: permission cleanup
* fix: bulk edit inconsistency
* chore: revert the previous flow
* chore: added folding utils
* draft: added cleanup folding constants
* feat: fold comparison modal permissions
* feat: improve permission folding behavior
* fix: preserve folded grant expansion cleanup
* docs: add permission cleanup branch review
* chore: scope permission folding to role management
* chore: add safe permission folding bundles
* chore: added tooltip info for folded permissions
* chore: handled template folding
* fix: search for folded permissions
* fix: use custom copy for folded permission tooltips
* fix:format
---------
Co-authored-by: Prateek Shourya <[email protected]>
* fix: include custom roles in role cache lookups and pass role_ref on project invite acceptance
Role cache helpers used system_only=True, causing custom role slugs to
be rejected as invalid in invite/member views. Also fixed
ProjectJoinEndpoint to propagate role_ref from the invite to the created
ProjectMember, preserving custom role assignment.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* style: fix lint errors across API codebase
Fix E501 line-too-long, F401 unused imports, and F841 unused variable.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* feat: role duplication (#6605)
* feat: role duplication
* fix: format at web
* feat: allow project navigation without membership using project:view permission
Replace member_role-based navigation gate with project:view RBAC
permission check across all project list layouts (gallery, list,
timeline, without-grouping). Users with project:view access can
now visit projects without joining them first. The join button
remains available as a secondary action.
* refactor: update RBAC permissions for workflow state and transition endpoints
* chore: add proprietary license headers to roles and permissions files
* feat: centralize role assignment restrictions in permission stores
- Add getAssignableWorkspaceRoles/getAssignableProjectRoles utilities
mirroring backend's PROTECTED_ROLE_SLUGS for dropdown filtering
- Add canManageWorkspaceRole/canManageProjectRole utilities mirroring
backend's can_manage_role for role hierarchy enforcement
- Embed canManageRole check inside getCanChangeRole in both workspace
and project permission stores, changing canChangeRole from boolean
to (targetRoleSlug: string) => boolean
- Filter role dropdowns in all 5 locations: workspace member change,
workspace invite, workspace pending invitation, project member
change, and project invite modal
- Owners can assign any role, admins up to admin, custom roles below
admin only
* refactor(constants): add foldedUnder/foldTooltipKey to PermissionEntry and foldedChildren/foldTooltipKey to PermissionMatrixRow
* refactor(constants): add folding pass to buildPermissionGroups — collects foldedUnder, validates, annotates parents, filters children
* refactor(utils): add expandFoldedPermissions utility, export buildConditionalGrant
* refactor(constants): annotate workspace permission groups with foldedUnder/foldTooltipKey
* refactor(constants): annotate project permission groups with foldedUnder/foldTooltipKey
* refactor(web): simplify form components — remove override props, read folding data from row
* refactor(web): simplify create-root and details-root — use groups directly, expandFoldedPermissions
* refactor(web): simplify comparison modal — remove folding/override logic
* refactor: delete permission-folding.ts, utils/folding.ts, and stale README — folding is now intrinsic to group definitions
* feat: grant workspace admin full access to custom roles
Workspace admin was previously locked out of custom role management
(create/edit/delete/view), which was owner-only. This adds
WildcardGrant(ResourceType.CUSTOM_ROLE) so admins can manage custom
roles without needing owner privileges.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix: resolve permission mismatches between frontend and backend
Hard Missing — added to backend definitions:
- New ResourceTypes: customer_attachment, workspace_custom_property,
workspace_workitem_type, project_workitem_type
- New Action: comment (for wiki:comment)
- New Permission classes for each new resource type
- Added WikiPermissions.COMMENT
Soft-Only Drift — updated backend permission classes:
- InitiativeUpdatePermissions: added create, edit, delete
- InitiativeUpdateCommentPermissions: added view, edit, delete
- EpicUpdateCommentPermissions: added view
- TeamspaceWorkitemViewPermissions: added share
Dead/Stale — removed from frontend:
- Removed workitem:move from canonical map and permission groups
- Removed cycle_update_comment entry (no runtime usage)
- Removed workitem move from project permission groups
* refactor(constants): extract folded_rows i18n prefix into F constant
* chore: docs cleanup
* fix: format and type errors
* refactor: migrate pages, wiki, and collection permissions to new RBAC/GAC system
- Replace all legacy allowPermissions/EUserWorkspaceRoles/EUserProjectRoles checks
with permissionAccessStore.can() across page stores and components
- Migrate workspace page (wiki) store to use 'wiki' resource actions
- Migrate teamspace page store to use 'teamspace_page' resource actions
- Clean up project page store: remove ROLE_PERMISSIONS_TO_CREATE_PAGE,
tighten isContentEditable to use RBAC
- Add canCurrentUserPublishPage to page instances (wiki:share / page:share)
- Convert canCurrentUserCreatePage to getCanCreatePage(computedFn) with
explicit params — no internal router dependency
- Add wiki_collection permission resource (backend + frontend):
- definitions.py: WIKI_COLLECTION ResourceType + WikiCollectionPermissions class
- inheritance.py: parent declaration (workspace-scoped)
- resource_models.py: Collection model mapping
- system_roles.py: grants for owner/admin/member roles
- resource-actions.ts: frontend type definitions
- Replace wiki:delete proxy in collection stores with proper
wiki_collection:edit/delete/create checks
- Update all consumer components to use store-provided permissions
instead of calling usePermissionAccess().can() directly
- Update collection store tests with permissionAccessStore mock
* refactor: update release permissions to use new RBAC/GAC system
- Replace legacy permission checks with new permissionAccessStore.can() method across release components.
- Update ReleaseEmptyState, ReleaseLayoutHOC, ReleaseDetailRoot, and ReleaseScopeListRoot to utilize the new permission structure.
- Refactor ReleaseInstance to streamline permission handling for editing and workspace slug retrieval.
- Clean up unused permission-related code in user and role management.
- Ensure consistent permission checks for creating and editing releases throughout the application.
* feat(permissions): add Permission Scheme layer for composable role definitions
Introduces a Permission Scheme (PS) layer between permissions and roles.
Roles are now composed of reusable PS bundles instead of flat permission
lists, enabling custom role creation via PS composition.
- Add PermissionScheme and RolePermissionScheme models with M2M on Role
- System roles map 1:1 to system PS defined in code (zero-DB resolution)
- Custom roles resolve via single JOIN query with conditional deduplication
- CRUD + blast radius impact API for permission schemes
- Clone flow: extend system roles by cloning their PS
- Chunked data migration for 150k+ workspace scale with 2min query timeout
- Cache invalidation scoped per workspace to prevent cross-workspace poisoning
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: replace projectsWithCreatePermissions with resource-specific permission helpers
- Add getProjectIdsWithCyclePermission, getProjectIdsWithModulePermission,
getProjectIdsWithWorkItemPermission, getProjectIdsWithEpicPermission to
their respective permission stores
- Update cycles/form, modules/form, issue-modal, epic-modal, and exporter
components to use resource-specific permission checks instead of the
legacy projectsWithCreatePermissions
- Remove projectsWithCreatePermissions and canPerformAnyCreateAction from
UserStore
- Move joinProject/leaveProject from UserPermissionStore to
ProjectMemberStore with corresponding service methods
- Remove legacy role/permission methods from UserPermissionStore
(getWorkspaceRoleByWorkspaceSlug, getProjectRolesByWorkspaceSlug,
fetchUserProjectInfo, fetchUserProjectPermissions)
- Export CollectionActionsForResource type from @plane/types
* refactor: create workspace preferences store and remove legacy UserPermissionStore
- Create PreferencesRootStore with WorkspacePreferencesStore sub-store
for workspace member preferences (draft_issue_count, active_cycles_count,
explored_features, tips, getting_started_checklist)
- Create WorkspacePreferencesService with consolidated
GET/PATCH /api/workspaces/{slug}/preferences/ endpoint
- Create useWorkspacePreferences() hook
- Rename IWorkspaceMemberMe type to WorkspacePreferences, remove role field
- Move leaveWorkspace to WorkspaceMemberStore, remove dead copy from UserService
- Move onboarding mutations from WorkspaceMemberStore to WorkspacePreferencesStore
- Add mutateDraftWorkItemsCount and mutateActiveCyclesCount helpers
- Refactor workspace-wrapper to gate on workspace permissions API (matching
project-wrapper pattern) with WorkspaceAccessRestriction component
- Remove preferences preload from client-bootstrap (permissions API is the gate)
- Fix workspace permissions service to preserve HTTP status codes
- Update flux proxy: use /permissions/ endpoint, rename membership -> permissions
- Backend: rename serializer to WorkspacePreferencesSerializer, rename view to
WorkspacePreferencesEndpoint, remove old /workspace-members/me/ endpoint
- Delete legacy permissions.store.ts, user-permissions hook
- Clean stale test mocks referencing deleted user.permission
* feat(types): add PermissionScheme types and update PermissionRole
* feat(services): add permission scheme CRUD methods
* feat(constants): add roles and schemes tab constants
* feat(store): add PermissionSchemeStore with CRUD and workspace-level fetch
* feat(sidebar): add Project and Workspace roles & schemes navigation items
* feat(store): ensure RoleManagementStore handles permission_schemes in payloads
* feat(i18n): add translation keys for roles and schemes UI
* feat(ui): add roles-and-schemes list page with Roles and Schemes tabs
* feat(ui): add create role modal with name and description
* feat(ui): add attach-schemes sidebar and expandable attached scheme item
* feat(ui): add role detail page with scheme attachment
* feat(ui): add permission scheme detail and create pages
* feat(routes): add workspace and project roles-and-schemes route pages
* feat(routes): register new roles-and-schemes routes in settings router
* feat(api): add status to RoleSerializer and create RoleImpactEndpoint
* feat(api): add reassignment support to role deletion
* feat(api): add disable role with reassignment support
* feat(store): add delete/disable/enable role and impact methods
* feat(i18n): add Phase 2 translation keys for role/scheme management
* feat(ui): add edit role details modal
* feat(ui): add delete role modal with member reassignment
* feat(ui): add disable and enable role modals
* feat(ui): add delete permission scheme modal with impact warning
* feat(ui): wire up role card quick actions with modals
* feat(ui): wire up scheme card quick actions with delete modal
* feat: add member and project counts to role list API and cards
* feat(ui): add members list popover on role cards
* feat(ui): add permission schemes list popover on role cards
* feat(ui): add projects associated section to project role detail
* fix: correct translation keys on create buttons and use scheme terminology on scheme pages
* refactor: move roles & schemes to own sidebar section, remove old roles-and-permissions pages and dead code
* refactor: remove project count and projects associated logic from roles
* fix: preserve active tab via URL search params when navigating back from detail pages
* fix: sidebar section header translation and swap workspace before project in order
* fix(permissions): populate permission schemes for system roles and fix N+1 queries
- RoleSerializer now returns permission_schemes for all roles (was hardcoded [] for system roles)
- View uses prefetch_related on through table with soft-delete filtering — eliminates N+1
- get_permissions() reads from prefetch cache instead of separate JOIN query per role
- Split based_on into based_on_slug (write) and based_on (read PrimaryKeyRelatedField)
- Removed to_representation override — all fields handled by DRF natively
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* refactor: extract scheme/role cards and list loader into separate components, add permission group summary to scheme cards
* refactor: add separate CreateRolePayload and UpdateRolePayload types, use permission_scheme_ids for writes
* fix: resolve infinite API calls and improve loader in delete/disable modals
- Fix useEffect infinite loop caused by unstable t() reference in deps
- Use useRef pattern for t() to keep useCallback deps stable
- Convert .then/.catch/.finally chains to async/await (fixes Oxc lint)
- Replace Spinner with skeleton Loader for better loading UX
* fix: hide description section in scheme detail view when empty
* feat: apply consistent permission checks across roles and schemes UI
- Gate Create Scheme button with custom_role:create (same as Create Role)
- Add canEdit/canDelete/canToggleStatus callbacks with resourceMeta to role cards
- Add canEdit/canDelete callbacks with resourceMeta to scheme cards
- Gate Edit button on scheme detail page with custom_role:edit
- Gate Attach Schemes button on role detail page with custom_role:edit
- Context menus hidden when user lacks all relevant permissions
* feat: redesign role card UI with two-section layout and add MembersFilledIcon, KeyFilledIcon
- Restructure role card: top section (name, description, badges, menu) + bottom section (stats with icons)
- Add MembersFilledIcon and KeyFilledIcon to @plane/propel/icons
- Use Badge component with brand/success/neutral variants for System and Active/Inactive
- Replace lucide Users icon with MembersFilledIcon in role members popover
* feat(permissions): add member_count to role list response
Annotate role queryset with workspace/project member counts via role_ref FK.
RoleSerializer picks the correct count based on namespace.
Also adds serializer tests for permission_schemes, permissions, and member_count.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* revert: remove member_count backend logic to avoid conflicts with base branch
- Remove bulk member count query from role list view
- Remove to_representation override (redundant with teammate's SerializerMethodField)
- Keep member_count on PermissionRole type (teammate's branch provides it)
* feat: implement RoleActivity model and track role changes
* refactor: redesign role detail page with inline sidebar and compact scheme display
- Replace fixed overlay sidebar with inline flex sidebar (workflow pattern)
using negative margin slide-in (-mr-[320px] → mr-0)
- Sidebar now supports toggling schemes (check to add, uncheck to remove)
with full-set save instead of append-only
- Create CompactSchemePermissions component with collapsible cards showing
grouped permissions as flat checkbox+label rows (read-only)
- Delete AttachedSchemeItem (replaced by CompactSchemePermissions)
- Remove RoleDetailsPermissionsList dependency from role detail page
* fix: replace register() with Controller in create/edit role modals
TextArea component forces controlled behavior via default value='', which
breaks react-hook-form's register() pattern (uncontrolled). Switching to
Controller provides proper value/onChange pair.
* refactor(api): align role reassignment with permission-sync pattern
- Drive reassignment through WorkspaceMember/ProjectMember.role_ref so
post_bulk_update syncs ResourcePermission and invalidates per-user
caches; drop the manual ResourcePermission.update + bulk cache invalidate.
- Simplify patch/delete to match PermissionSchemeEndpoint style: single
.exists() gate, consolidated cleanup, request.data.get() over QueryDict
copy+pop gymnastics.
- Fix 'role not found' on RoleImpactEndpoint when workspace slug is
cache-hit: use workspace__slug (URL-guaranteed) instead of
request.workspace (null on cache hit).
* feat(web): require explicit status filter on role-list store getters
- Introduce RoleStatus and RoleStatusFilter; narrow PermissionRole.status
and UpdateRolePayload.status to the literal union.
- Make statusFilter a mandatory arg on getWorkspaceRolesByWorkspaceSlug,
getProjectRolesByWorkspaceSlug, and their id-variants so every call
site explicitly acknowledges whether disabled roles should surface.
- Id-getters now delegate to the role-getters and map to ids.
- Pass 'active' in selection UIs (invite, role-change, member filters,
reassignment targets); pass 'all' in the roles management page and
role comparison modal.
* refactor: drop role impact endpoint, use member_count with optimistic mutations
- Remove RoleImpactEndpoint (view + URL + exports); backend still validates reassignment server-side
- Promote PermissionRole.member_count to required and delete the RoleImpact type
- Drop getRoleImpact service + fetchRoleImpact store method
- Disable/delete role now apply optimistic member_count deltas with revert-on-failure via new adjustRoleMemberCount + restoreRole helpers
- Simplify disable/delete role modals: no impact fetch, no loading skeleton, no submit spinner; read member_count directly from the store
- Add layout.tsx for workspace- and project-roles-and-schemes with revalidateOnMount SWR fetches so list counts stay fresh on entry
* fix(permissions): use request.workspace_id in scheme endpoints
WorkspaceResolverMiddleware only sets request.workspace on cache miss; on
cache hit it sets request.workspace_id but leaves request.workspace as None.
PermissionSchemeEndpoint was trusting request.workspace, so cached requests
saved schemes with workspace_id=NULL and subsequent get/list/impact filters
missed them. Custom schemes would fail to attach to roles with
"Permission schemes not found" while system schemes (legitimately NULL +
is_system=True) worked.
- Read + write workspace via request.workspace_id throughout PermissionSchemeEndpoint and PermissionSchemeImpactEndpoint
- Pass workspace_id via serializer context and use it for slug-uniqueness lookup
- Tighten list/get filter to Q(workspace__isnull=True, is_system=True) so any stale orphan scheme can't leak back into listings
* regression: minor cleanup
* fix: build errors post merge conflicts resolution
* fix: backend errors post merge conflicts resolution
* fix(roles-and-schemes): i18n interpolation, sidebar labels, and UI polish
- fix translation interpolation: switch {{param}} to {param} for IntlMessageFormat
in delete_role, disable_role, enable_role, delete_scheme_modal
- settings sidebar: rename entries to 'Workspace' and 'Project' (keep full page
titles intact via separate sidebar_label key)
- compact scheme permissions: cap height with scrollbar and override
transition-property to reduce expand/collapse jitter
- role card: add text-tertiary to member and scheme icons
- attach schemes sidebar: reorder Add/Discard buttons and swap alignment
- create/edit/delete/disable role modals: add htmlFor to labels, use
text-danger-secondary with spacing for required indicator, tighten textarea
sizing on create
* fix: format
* feat: implement PermissionSchemeActivity for auditing permission scheme changes
* i18n: translate roles-and-permissions and wiki.auth into 18 locales
Translates the new roles-and-permissions namespace (462 keys) and the
wiki.auth permission-denied screen (4 keys) into all 18 non-English
locales, applying the per-locale rules from .claude/skills/translate.md
(DNT glossary, per-script transliteration, register, punctuation, ICU
variable preservation).
After: sync-check reports 100% coverage across all locales (5,279 keys
in each).
* permissions: prune unused actions and align work-item copy
Remove permission entries that bug reports flagged as unneeded and
the surrounding UI never reads:
- workspace:archive (no caller in apps/web)
- initiative_update:* and initiative_update_comment:* (no such
entity in apps/web — the initiative-updates service only aggregates
project and epic updates)
Clean up orphan i18n keys that no longer map to a resource:action:
- workitem.move (no 'move' action on workitem)
- cycle_update_comment (no such resource)
Rename 'Issues' to 'Work Items' across the roles matrix copy in all
19 locales, re-translating to each locale's existing work-item.json
convention. Update the initiatives view tooltip to drop 'and updates'.
Regenerate i18n type keys. sync-check reports 100% coverage across
all 18 non-English locales (5,267 keys each).
* fix: module create permissions
* fix: link add permissions
* fix: project updates
* fix: comment reactions
* chore: remove guest_view_all_features usage (keep DB field)
The new project-level commenter role in RBAC/GAC replaces the
guest_view_all_features toggle. Flipping the flag previously upgraded
guests to a "commenter" permission relation via Project._sync_guest_permissions;
with commenter now a first-class role, the flag is redundant.
This strips every read/write/UI reference to guest_view_all_features
while keeping the BooleanField on Project and ProjectTemplate plus their
migrations so existing DB data and schema stay intact.
After this change:
- Guests see only items they created (pages, work items, state duration, views)
- Broader access is granted via the commenter role
Touched surfaces:
- Project model: drop _sync_guest_permissions, TRACKED_FIELDS, save-hook branch
- Serializers: api/app/template all remove the field
- Views: view/base, issue/state_duration, api/project_page, ee page, ee template
- GraphQL ProjectFeatureType + resolver
- copy_project_data_task
- PI action tools, property mapper, execution helpers, artifact schema
- Contract tests for project + project-template-use
- Frontend: IPartialProject, TProjectTemplateData, template build util, settings UI
- i18n: drop guest_super_permissions from all 19 locales + generated keys
* chore: remove guest_view_all_features from GraphQL + contract tests
Follow-up to 6b89df80af — these four files were not picked up in the
previous commit (lint-staged stash/restore left them modified but
unstaged):
- graphql ProjectFeatureType field and resolver default/assignment
- contract test payloads and template-use assertion
* Revert "chore: remove guest_view_all_features from GraphQL + contract tests"
This reverts commit 94cddec73e496a8559af66cec584ebfcc5e12335.
* graphql: hardcode guest_view_all_features to False for mobile compat
Keep the field on ProjectFeatureType and in the resolver so older mobile
clients don't break, but always return False regardless of the DB value.
The flag is superseded by the project-level commenter role.
* fix: adding comment to epic update
* fix: epic permission check
* fix: added condition label for scheme collapsible rows
* chore: fix loader
* refactor(permissions): remove unused project_role resource and actions
The project_role permission resource was defined but never wired up to any
real use case — nothing in the app gates on project_role:view/create/edit/
delete/define, and the matrix row in Role Administration was cosmetic.
Remove it cleanly across FE and BE so new custom roles cannot accidentally
grant meaningless permissions:
Backend:
- Drop ResourceType.PROJECT_ROLE enum entry
- Drop ProjectRolePermissions class and its __init__.py re-export
- Drop PROJECT_ROLE from the inheritance hierarchy map
Frontend:
- Drop project_role from resource-actions.ts
- Drop the project_role rows from the role_administration matrix group
- Strip the project_role i18n nodes from all 19 locales (surgical,
formatting preserved)
- Regenerate keys.generated.ts (-5 obsolete keys)
- Update the role_administration description in English
Docs:
- Drop project_role:view / project_role:define rows from
PERMISSION_SYSTEM.md
sync-check: all 18 non-English locales remain 100% in sync with English
(5,262 keys each).
Note: this does NOT touch project-role-slug management (admin/contributor/
commenter/guest), the project_roles_and_schemes settings route, the SSO
group-mapping column, or any PROJECT_ROLE_SLUG_MAP / PROJECT_ROLES role
seeding — those are unrelated concepts that reuse the 'project_role' name.
* fix(workspace): add PATCH handler to WorkspacePreferencesEndpoint
The refactor that consolidated WorkspaceMemberMe + onboarding endpoints
into a single /preferences/ route only added the GET handler. Frontend
calls to PATCH /api/workspaces/<slug>/preferences/ returned 405.
Add a PATCH handler that mirrors the onboarding endpoint's semantics
(VIEW permission, partial update of explored_features / tips /
getting_started_checklist) and reuses the annotated-member query so
GET and PATCH responses share the same WorkspacePreferences shape.
* refactor(permissions): use exhaustive Record for access checks
Replace switch-with-default patterns in workspace/project access and
settings hooks with Record<Key, boolean>. TypeScript now enforces
exhaustiveness: adding a new resource or settings tab key becomes a
compile error until an explicit check is provided.
Drop WorkspaceNavigationItemToFeatureFlagMap — folded into the new
Record so feature flags and gating live beside each key.
* fix(permissions): grant workspace admin teamspace:* wildcard
Workspace admin was denied on teamspace management endpoints (e.g. POST
.../teamspaces/<id>/members/) because the admin permission scheme only
granted teamspace:browse + teamspace:create, not teamspace:manage/view/
edit/delete. The engine correctly walked teamspace→workspace hierarchy
but found no matching grant at the workspace level.
Replace the narrow grants with WildcardGrant(ResourceType.TEAMSPACE) so
workspace admin gets full teamspace control, matching the design intent
documented in permission-role-alignment-review.md and the migration
notes for TeamspaceMembersEndpoint.
Docs updated: PERMISSION_MATRIX (teamspace tables now show teamspace:*
for admin), PERMISSION_MIGRATION (clarified the admin grant widening).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fixed lint errors
* feat: implement role-based access control for group syncing and update related models, serializers, and views (#6803)
* feat: implement role-based access control for group syncing and update related models, serializers, and views
* refactor: remove unused role management logic and related imports
* fix: format
* feat(group-sync): add default workspace role handling and migration
- Introduced `default_workspace_role` field in `GroupSyncConfig` model.
- Added migration to populate existing configs with the workspace 'member' role.
- Updated group sync service to utilize the default workspace role when adding users.
- Enhanced serializers and views to support the new role field.
- Updated frontend components to allow selection of the default workspace role.
* feat(i18n): update default workspace role translations for multiple languages
---------
Co-authored-by: Prateek Shourya <[email protected]>
* fix(sidebar): show current project in list for non-member admins
When workspace admin+ users navigate to a project they haven't joined,
include the current project (from router) in the sidebar list so they
can access nested resources like work items, cycles, and modules.
* fix(project-actions): hide leave project for non-members
Restrict the Leave project action to users who actually have a
member_role on the project. Workspace admins viewing a project they
haven't joined no longer see the option.
* fix(views): restrict creator-only check to private views on IssueView
Admins (W-admin, P-admin) and other roles with workitem_view:edit/delete
grants were blocked from editing or deleting any view they didn't create,
which contradicts the wildcard grants held by admins. The inline creator
check now applies only to private views (access == PRIVATE_ACCESS); public
views can be edited and deleted by anyone with the underlying permission.
Also add the destroy-side check on both WorkspaceViewViewSet and
IssueViewViewSet — previously delete had no ownership check at all, so
even private views could be deleted by anyone with the delete grant.
Introduce IssueView.PRIVATE_ACCESS / PUBLIC_ACCESS class constants and
replace magic numbers 0/1 in the view layer. The `access` field's
`choices` and `default` are left unchanged to avoid a spurious migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fix(permissions): resolve correct settings tab in route-based gate
The settings-route access helpers iterated WORKSPACE_SETTINGS /
PROJECT_SETTINGS in insertion order and used startsWith, so the shortest
href ("/settings" for workspace, "" for project — both belonging to
the 'general' tab) always matched first. Every per-tab authorization
check collapsed to the general-tab gate.
Sort entries by href length descending and match either exactly or at a
segment boundary so deeper routes resolve to their own tab and don't
get swallowed by shorter prefixes.
* fix(permissions): honor requestId in customer request edit/delete checks
The interface declared getCanEdit/getCanDelete as (requestId: string) =>
boolean, but the implementations ignored the argument and returned the
same verdict for every request under a customer. Thread the requestId
through resourceMeta.resourceId so per-request grants evaluate correctly.
* fix(permissions): use correct actions for project member management
getCanManageMembers now checks project_member:edit (not the view action),
getCanUpdateMemberRole delegates to getCanManageMembers, and
getCanRemoveMember checks project_member:remove directly. Previously any
member who could view the list was treated as able to manage or remove
others.
* fix(permissions): refresh current-user permissions after self role change
After a self-targeted workspace or project member-role update, re-fetch
the current user's permission grants so the UI reflects the new
capabilities without requiring a reload.
Also drops the explicit revalidateOnFocus: false on the permissions SWR
so out-of-band role changes (an admin updating the user from another
session) get picked up when the tab regains focus.
* fix(permissions): gate active projects list on projects, not archives
The projects list layout was using pageKey="archives", which checks
the archives-view predicate (guest exclusion). Use pageKey="projects"
so the active projects list is gated on projectPermissions.getCanBrowse.
* chore(permissions): drop unsupported workspace:archive permission
The workspace archive action is not a user-facing concept — it was
defined on the backend (WorkspacePermissions.ARCHIVE and the admin
permission scheme) but never wired into any view or client call.
Remove the definition and its scheme entry to keep backend and frontend
permission surfaces aligned.
* chore(permissions): migrate @allow_permission decorators to @can
Convert backend views still using the legacy 3-role @allow_permission
decorator to the new @can decorator.
- app/views/issue/work_item.py: WorkItemListProjectEndpoint now uses
@can(WorkitemPermissions.VIEW, resource_param="project_id",
defer_conditions=True) with an inline creator-condition queryset
filter so guests see only their own items. The workspace-scope
WorkItemListWorkspaceEndpoint uses @can(WorkspacePermissions.VIEW,
resource_param="workspace_id"), matching the pattern used by
IssueDetailIdentifierEndpoint.
- app/views/issue/vote.py: all three verbs on IssueVoteEndpoint use
@can(WorkitemPermissions.REACT, resource_param="work_item_id"),
matching the IssueReaction endpoints.
- app/views/issue/state_duration.py: @can(WorkitemPermissions.VIEW,
resource_param="work_item_id"). The inline guest/epic business
guards are preserved — they remain equal-or-more-restrictive than
the engine's workitem:view+creator grant.
- ee/views/app/project/user_import.py: ProjectMembersImportEndpoint.post
uses @can(ProjectMemberPermissions.INVITE, resource_param="project_id").
- api/views/worklog.py: all five worklog endpoints use
@can(WorkitemPermissions.EDIT, resource_param=<issue_id or project_id>),
which admins and contributors have unconditionally and guests lack —
matching the legacy ADMIN/MEMBER gate.
Unused endpoints flagged with explicit 'migrate to @can before
re-enabling' TODOs (intake/base.py, project/invite.py, external/base.py,
ee/template/asset.py) are left unchanged, per the TODO contract.
* fix(permissions): skip hydration entities missing _permissions
Without a guard, an entity without _permissions written the key into
projectPermissionsMap/teamspacePermissionsMap as undefined. Subsequent
can() lookups would read undefined permission_grants and fall through
to workspace-scope permissions, which may grant more than intended.
* docs(permissions): update matrix and migration tracker for PR #6121 changes
Add migration entries for the seven endpoints migrated to @can in this
branch (WorkItemListProjectEndpoint, WorkItemListWorkspaceEndpoint,
IssueVoteEndpoint, WorkItemStateDurationEndpoint, WorkItemWorklogEndpoint,
ProjectWorklogAPIEndpoint, ProjectMembersImportEndpoint), record the
workspace:archive permission removal, and add the matching role-access
rows to the permission matrix.
* feat: enhance export capabilities across various views
- Added `getCanExport` methods to `EpicPermissions`, `ProjectViewPermissions`, and `WorkspaceViewPermissions` to manage export permissions more effectively.
- Updated `useQuickActionsFactory` to utilize new permissions for export functionality in quick actions.
- Refactored export permission checks in `DefaultWorkspaceViewQuickActions` to leverage global view permissions.
- Cleaned up unused imports and optimized permission checks for better performance.
* fix(api): repair missing imports and drag dead @allow_permission to @can
Two files had broken references that surfaced as ruff F821 errors after
the earlier @allow_permission migration work:
- app/views/view/base.py: re-add imports for Project, ProjectMember,
and check_if_current_user_is_teamspace_member — all used inline in
the guest-visibility filter on view count.
- ee/views/app/workflow/states.py: WorkflowDefaultStateEndpoint.post
still referenced allow_permission / ROLE without importing them;
migrate to @can(WorkflowPermissions.EDIT, resource_param="project_id")
which matches the pattern used by every other endpoint in the file.
Also apply ruff format to the touched files (including the small
whitespace churn in permissions/definitions.py from dropping
WorkspacePermissions.ARCHIVE earlier).
* refactor(permissions): use permission engine in _get_view_workitem_count
Replace the legacy ProjectMember role=5 + guest_view_all_features +
check_if_current_user_is_teamspace_member triple-check with a single
permission_engine.check() call on workitem:view with defer_conditions=True.
Role-agnostic: any role (system guest or custom) granting workitem:view
only via the creator condition gets restricted to their own issues.
Teamspace access is covered by link-relation traversal in the engine.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fix: format errors
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Prateek Shourya <[email protected]>
Co-authored-by: Aaryan Khandelwal <[email protected]>
Co-authored-by: vamsikrishnamathala <[email protected]>
Co-authored-by: pablohashescobar <[email protected]>
Co-authored-by: Vamsi Krishna <[email protected]>
Co-authored-by: Nikhil <[email protected]>