feat: delegated package management for shared Packeton instances — closes #367#368
Open
devpanel-lc wants to merge 7 commits into
Open
feat: delegated package management for shared Packeton instances — closes #367#368devpanel-lc wants to merge 7 commits into
devpanel-lc wants to merge 7 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_MAINTAINERcan 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
ownersrelation (ManyToMany to User). Group owners can manage their own Groups and packages assigned to them, without needing admin privileges. This is gated behind a newALLOW_MAINTAINER_GROUPSenvironment variable (defaultfalse), so the existing behavior is completely unchanged when the flag is off.When
ALLOW_MAINTAINER_GROUPS=false(the default):PackageManageVoterdenies non-admin users — no group-based MANAGE grants are checkedROLE_MAINTAINERchecks in controllers continue to work as beforeWhen
ALLOW_MAINTAINER_GROUPS=true:MANAGEaccess to that packageOwner-scoped SSH Credentials — Maintainable credentials (
SshCredentials) gain anownerrelation (ManyToOne to User). Maintainers can create, edit, and delete their own credentials. Admins can manage all credentials. This is independent of theALLOW_MAINTAINER_GROUPSflag.Architecture
The implementation follows the existing Symfony security voter pattern:
PackageManageVoterMANAGEPackageGroupOwnerVoterEDIT,ADD_PACKAGE,REMOVE_PACKAGEGroupCredentialManageVoterMANAGESshCredentialsAll voters implement
CacheableVoterInterfaceand are auto-registered viaautoconfigure: trueinconfig/services.yaml.Changes
Schema
Group::$owners— ManyToMany to User viagroup_ownerjoin tableUser::$ownedGroups— inverse side of the relationSshCredentials::$owner— ManyToOne to UserConfiguration
.env—ALLOW_MAINTAINER_GROUPS=falseconfig/packages/packeton.yaml—allow_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 rulesControllers
GroupController— flag checks inindexAction,createAction,updateAction; owner filtering on index query;handleUpdatepassesisAdminto formPackageController—canEditPackageandcanDeletePackagenow checkPackageManageVoter::MANAGEfor group-based access; null-safegetMaintainers()?->contains()bugfixUserController— credential edit/delete gated onCredentialManageVoter::MANAGE; list query filtered by owner (non-admin);isAdmintemplate variableForms
GroupType— owner selection field (admin-only when flag on); packages query includes user's own packages plus group-assigned packages;ParameterBagInterface+TokenStorageInterface+ManagerRegistryinjectedGroupAclPermissionCollectionType—allowed_packagesoption registeredCredentialType— owner-scoped query builder filteringTemplates
group/index.html.twig— delete button admin-only, create button gated on flaggroup/update.html.twig— Select2 CSS, conditional owners fieldpackage/viewPackage.html.twig—MANAGEvoter on action blocksuser/sshkey.html.twig— owner labels for admins, conditional headingMenuBuilder— "My Groups" link gated on flagTests — 75 tests, 120 assertions (PHPUnit 10, SQLite fixture)
GroupControllerTest(8 tests) — flag-OFF behavior: maintainer blocked from all group pages, admin unaffectedGroupControllerFlagOnTest(13 tests) — flag-ON behavior: maintainer can create/edit own groups, manage packages through groups, cannot edit others' groups or deleteCredentialControllerTest(11 tests) — owner-scoped CRUD, list visibility, admin bypassSchema 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_GROUPSdefaults tofalse— zero impact on existing deploymentsROLE_MAINTAINERchecks in controllers remain untouchedcanEditPackagenull-safety bugfix (?->ongetMaintainers()) is a standalone improvement unrelated to the feature flagBugfixes included
PackageController::canEditPackage()— fixed null-pointer exception when$package->getMaintainers()returns null (pre-existing bug)