MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-349

6.4 Roles & permissions — project membership + access gating

Done
Description

Gate access at the project level, not just the workspace — the mirror-faithful model the PM core has been deferring (// TODO(6.4): gate by project role). A project gets an access level (Jira team-managed: open / limited / private) and an explicit project membership with a per-project role (admin / member / viewer); Story 1.2's flat workspace-membership role string is formalized into the same role set. Browse access is then computed: open → any workspace member (view + edit); limited → any workspace member (view + comment, no edit); private → only project members (workspace owner/admin always). This is enforced at the service layer and gated in the UI.

Verified mirror (rung 1, mistake #33 — checked, not asserted). Jira gates by project: a team-managed project’s Access level (open/limited/private) decides whether site members see it, and a private project requires explicit per-project role assignment; company-managed restrict the "Browse Projects" permission to project roles. So project access = per-project membership/role. We mirror the team-managed three-level model.

Migration-aware (no lockout). Existing WorkspaceMembership.role values map to the new role set (owner→owner/admin, the rest→member); existing projects default to open so every current workspace member keeps access on deploy (a private default would lock everyone out). Project membership is created lazily / on access-level change — making a project private seeds its current viewers as members (the Jira "set private → add the people who had access" shape), never an empty private project the owner is locked out of.

Scope: the role + project-membership + access-level model; the service-layer browse/edit gate threaded into the existing project / board / issue reads (the retrofit of the deferred seams); the project Members + Access admin UI; UI gating (inaccessible projects hidden from the switcher + a no-access state on direct nav; assignable users scoped to project members, the Jira behaviour); the seed update; tests.

Out of scope (Epic-6 siblings / later): field-level / workflow-level permission schemes (Jira’s full permission-scheme matrix — we ship the browse/edit gate + project roles, not the dozen-permission grid); global/site admin roles beyond workspace owner/admin; per-issue security levels; the unified project-admin area (Story 6.5 folds Members/Access in alongside workflow 2.2.5 + board-column 3.6).

Verification

  • Pull the Story branch, pnpm install, pnpm prisma migrate dev, pnpm db:seed, pnpm dev.
  • pnpm test — vitest covers: the browse gate per access level (open/limited/private), project-role assignment, the workspace-role migration mapping + the open project default, and assignable-users scoping to project members.
  • Private-project gating (the core check): as the PM (zhuyue@motir.co) set the motir project to Private; sign in as a workspace member who is NOT a project member → the project is absent from the switcher and a direct /boards / /issues link shows the no-access state; add them as a project member → it appears and opens.
  • Roles: a project viewer can open the board/issues but cannot edit (create/move/assign disabled); a member can edit; an admin can manage members + access. Workspace owner/admin always have access regardless of project membership.
  • Access levels: open → every workspace member sees + edits; limited → every member sees + comments but cannot edit; private → only members.
  • Assignable users: on a private project, the assignee/reporter pickers list only project members (not the whole workspace).
  • pnpm test:e2e --grep project-access drives the real stack: a non-member is denied a private project; a member/viewer/admin get the right capabilities.