Skip to content

fix(services): allow Write members to manage their own services#437

Open
angel-manuel wants to merge 1 commit into
devfrom
fix/service-mutation-write-acl
Open

fix(services): allow Write members to manage their own services#437
angel-manuel wants to merge 1 commit into
devfrom
fix/service-mutation-write-acl

Conversation

@angel-manuel

@angel-manuel angel-manuel commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

create_service (POST /v1/services) is gated by WriteAcl, but the service management paths — delete, update, update-status — required AdminAcl. That asymmetry meant a Write-level member (e.g. an app acting through an org-service-key + X-Overslash-As impersonating a Write identity) could create a service instance but then hit 403 "admin access required" when trying to delete or edit its own service.

Fix

Lower delete_service, update_service, and update_service_status to WriteAcl and enforce a strict per-row ownership check (require_owner_or_admin):

  • A caller may mutate a service it owns at Write level.
  • Mutating another identity's service, or an org-level (owner_identity_id IS NULL) service, still requires Admin → otherwise 403 "admin access required".

This mirrors what create_service already does (org-level creation re-checks Admin) and matches the established update_template precedent (templates.rs).

The check is strict identity equality — deliberately NOT a ceiling/family check — so an agent cannot reach its owner-user's or a sibling agent's services through the API (those stay dashboard-managed). The is_system guard runs before the ownership check, so system services still return 400 regardless of ownership.

Note on framing

The task referenced delete_connection as the model, but that handler actually returns { "deleted": false } at 200 for a non-owner/non-admin (it uses the is_org_admin flag). This PR instead follows the explicit requirement — a real 403 for non-owned rows — which is exactly the shape of update_template (owner_identity_id != acl.identity_id && access_level < Admin → Forbidden).

Tests

New crates/overslash-api/tests/services_admin_view.rs (seeds rows by direct SQL to control owner_identity_id/is_system; deletes by UUID path):

  • (a) Write member deletes its own service → 200
  • (b) Write member deletes another identity's service without admin → 403 (row survives)
  • (c) Write member deletes an org-level service without admin → 403 (row survives)
  • (d) Deleting an is_system service → 400 (row survives)
  • (e) Admin deletes any (other-owned + org-level) → 200
  • update-status parity: Write member changing another's status → 403

Verification

  • cargo check / cargo fmt / cargo clippy clean; vet (agentic) reports no issues.
  • New suite 6/6 pass. Regression suites pass: service_instances (14), groups (22 — incl. admin_agent_cannot_delete_owner_user_service_by_name still 404 by-name), org_acl (9).

Backend-only auth change; no new user-facing surface (the dashboard already calls these endpoints and simply stops getting spurious 403s for Write members acting on their own services).

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
overslash Ready Ready Preview, Comment Jul 8, 2026 5:51am

Request Review

Service create is gated by WriteAcl, but delete/update/update-status
required AdminAcl — so a Write-level member (e.g. an app acting via
org-service-key + X-Overslash-As) could create a service instance but
got 403 "admin access required" when deleting or editing its own.

Lower those three handlers to WriteAcl and enforce a strict per-row
ownership check (require_owner_or_admin): a caller may mutate a service
it owns at Write level; another identity's service, or an org-level
(owner_identity_id IS NULL) service, still requires Admin. This mirrors
create_service (org-level create re-checks Admin) and update_template.

The check is strict identity equality — deliberately NOT a ceiling/family
check — so an agent cannot reach its owner-user's or a sibling agent's
services through the API; those stay dashboard-managed. The is_system
guard runs before the ownership check, so system services still 400.

Adds route tests (services_admin_view.rs) covering own/other/org-level/
system/admin delete and an update-status parity case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant