Skip to content

permissions: PUT /api/permissions/roles/{id} with missing 'permissions' field silently wipes all role permissions #269

Closed

Description

@antosubash

Found by QA (browser fetch against a live app, reproduced): PUT /api/permissions/roles/{id} with body {} returns 200 and deletes every permission assigned to the role.

Root cause: RolePermissionsUpdate.permissions uses default_factory=list (modules/permissions/permissions/contracts/schemas.py), so a body that omits the field validates as an explicit empty list, and PermissionService.set_role_permissions treats the list as the full desired state — deleting all rows not in it.

Impact: an admin (or any client with permissions.manage) sending a malformed body can silently strip a role — including locking the admin role out of the role editor itself. Suggested fix: make permissions required in the update schema (explicit [] stays legal as a deliberate clear-all), so a missing field 422s instead of wiping.

Pre-existing on main (not related to PR #267); found during its QA cycle.

https://claude.ai/code/session_01HMniW4BEhpumUTFnWZVKVc

Activity

  1. added
    bugSomething isn't working
    pi-readyApproved for the headless Pi worker
    pi-workingThe headless Pi worker is implementing this issue
    and removed
    pi-readyApproved for the headless Pi worker
    on Aug 21, 2026
  2. antosubash commented on Aug 21, 2026

    @antosubash
    OwnerAuthor

    🤖 Claimed. I am creating an isolated worktree from origin/main and starting implementation.

  3. added
    pi-pr-openThe headless Pi worker opened a draft pull request
    and removed
    pi-workingThe headless Pi worker is implementing this issue
    on Aug 21, 2026
  4. antosubash commented on Aug 21, 2026

    @antosubash
    OwnerAuthor

    ✅ Draft pull request opened: #277

    Implementation

    • Made RolePermissionsUpdate.permissions required.
    • Missing permissions now returns HTTP 422 without changing assignments.
    • Explicit {"permissions": []} remains a valid clear-all operation.
    • Added API regression coverage for preservation and deliberate clearing.

    Changed areas

    • modules/permissions/permissions/contracts/schemas.py
    • modules/permissions/tests/test_permissions_module.py

    Verification

    • uv run pytest modules/permissions/tests — 29 passed
    • Ruff format/check — passed
    • uv run ty check ... — passed
    • git diff --check — passed

    Visual evidence: none required.
    Risks/blockers: none.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpi-pr-openThe headless Pi worker opened a draft pull request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions