Skip to content

feat: delegated package management for shared Packeton instances — closes #367#368

Open
devpanel-lc wants to merge 7 commits into
vtsykun:masterfrom
devpanel-lc:feature/maintainer-group-scoping
Open

feat: delegated package management for shared Packeton instances — closes #367#368
devpanel-lc wants to merge 7 commits into
vtsykun:masterfrom
devpanel-lc:feature/maintainer-group-scoping

Conversation

@devpanel-lc

@devpanel-lc devpanel-lc commented Jul 16, 2026

Copy link
Copy Markdown

Problem

Packeton's existing access control model provides excellent package consumer ACL through Customers and Groups, but package maintenance is instance-wide. A user with ROLE_MAINTAINER can create and manage packages across the entire instance. This makes it unsuitable for organizations hosting multiple independent teams within a single Packeton installation.

What this PR adds

Owner-scoped Groups — Groups gain an owners relation (ManyToMany to User). Group owners can manage their own Groups and packages assigned to them, without needing admin privileges. This is gated behind a new ALLOW_MAINTAINER_GROUPS environment variable (default false), so the existing behavior is completely unchanged when the flag is off.

When ALLOW_MAINTAINER_GROUPS=false (the default):

  • Setting this variable to FALSE disable all this new functionality
  • Maintainers cannot access any group pages (index, create, update)
  • The PackageManageVoter denies non-admin users — no group-based MANAGE grants are checked
  • The UI does not render group links for maintainers
  • All existing ROLE_MAINTAINER checks in controllers continue to work as before

When ALLOW_MAINTAINER_GROUPS=true:

  • Setting this variable to TRUE enables all this new functionality
  • Maintainers can create Groups (auto-set as sole owner)
  • Maintainers can edit Groups they own, add/remove packages they own to their Groups
  • A maintainer who owns a Group containing a package gets MANAGE access to that package
  • Maintainers cannot add other owners or delete Groups — admin-only
  • The "Create group" and "My Groups" links appear in the UI

Owner-scoped SSH Credentials — Maintainable credentials (SshCredentials) gain an owner relation (ManyToOne to User). Maintainers can create, edit, and delete their own credentials. Admins can manage all credentials. This is independent of the ALLOW_MAINTAINER_GROUPS flag.

Architecture

The implementation follows the existing Symfony security voter pattern:

Voter Attribute(s) Subject Grants
PackageManageVoter MANAGE Package Admin always; maintainer when flag on AND package is in a group they own
GroupOwnerVoter EDIT, ADD_PACKAGE, REMOVE_PACKAGE Group Admin always; maintainer when they are an owner of the group
CredentialManageVoter MANAGE SshCredentials Admin always; maintainer when they are the credential owner

All voters implement CacheableVoterInterface and are auto-registered via autoconfigure: true in config/services.yaml.

Changes

Schema

  • Group::$owners — ManyToMany to User via group_owner join table
  • User::$ownedGroups — inverse side of the relation
  • SshCredentials::$owner — ManyToOne to User

Configuration

  • .envALLOW_MAINTAINER_GROUPS=false
  • config/packages/packeton.yamlallow_maintainer_group_creation: '%env(bool:ALLOW_MAINTAINER_GROUPS)%'
  • config/packages/security.yaml{ path: ^/groups, roles: ROLE_MAINTAINER } and { path: ^/users/sshkey, roles: ROLE_MAINTAINER } access_control rules

Controllers

  • GroupController — flag checks in indexAction, createAction, updateAction; owner filtering on index query; handleUpdate passes isAdmin to form
  • PackageControllercanEditPackage and canDeletePackage now check PackageManageVoter::MANAGE for group-based access; null-safe getMaintainers()?->contains() bugfix
  • UserController — credential edit/delete gated on CredentialManageVoter::MANAGE; list query filtered by owner (non-admin); isAdmin template variable

Forms

  • GroupType — owner selection field (admin-only when flag on); packages query includes user's own packages plus group-assigned packages; ParameterBagInterface + TokenStorageInterface + ManagerRegistry injected
  • GroupAclPermissionCollectionTypeallowed_packages option registered
  • CredentialType — owner-scoped query builder filtering

Templates

  • group/index.html.twig — delete button admin-only, create button gated on flag
  • group/update.html.twig — Select2 CSS, conditional owners field
  • package/viewPackage.html.twigMANAGE voter on action blocks
  • user/sshkey.html.twig — owner labels for admins, conditional heading
  • MenuBuilder — "My Groups" link gated on flag

Tests — 75 tests, 120 assertions (PHPUnit 10, SQLite fixture)

  • GroupControllerTest (8 tests) — flag-OFF behavior: maintainer blocked from all group pages, admin unaffected
  • GroupControllerFlagOnTest (13 tests) — flag-ON behavior: maintainer can create/edit own groups, manage packages through groups, cannot edit others' groups or delete
  • CredentialControllerTest (11 tests) — owner-scoped CRUD, list visibility, admin bypass
  • Remaining 53 tests — pre-existing suite, all unchanged and passing

Schema migration

Applied directly via bin/console doctrine:schema:update --force --complete. No Doctrine Migrations were generated — this follows the existing pattern in the codebase.

Backward compatibility

  • ALLOW_MAINTAINER_GROUPS defaults to false — zero impact on existing deployments
  • Existing Groups without owners are handled gracefully (backward compat tests included)
  • All existing ROLE_MAINTAINER checks in controllers remain untouched
  • The canEditPackage null-safety bugfix (?-> on getMaintainers()) is a standalone improvement unrelated to the feature flag

Bugfixes included

  • PackageController::canEditPackage() — fixed null-pointer exception when $package->getMaintainers() returns null (pre-existing bug)
  • Grant maintainers read access to their own packages without requiring ROLE_FULL_CUSTOMER

@devpanel-lc
devpanel-lc requested a review from vtsykun as a code owner July 16, 2026 04:33
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