@@ -73,6 +73,88 @@ new RBACContentGuard:
7373!!! warning
7474 The PyPI access policies do not support ` creation_hooks ` or ` queryset_scoping ` .
7575
76+ ## PackagePermissionGuard
77+
78+ The PackagePermissionGuard provides fine-grained, package-level access control within a single distribution/index.
79+ Unlike RBACContentGuard which applies to all packages in a distribution, PackagePermissionGuard allows you to
80+ control download and upload permissions for individual PyPI packages.
81+
82+ ### Creating a PackagePermissionGuard
83+
84+ ``` bash
85+ pulp content-guard package-permission create --name my-package-guard
86+ ```
87+
88+ ### Managing Package Permissions
89+
90+ PackagePermissionGuard uses two separate policies:
91+ - ` download_policy ` : Controls who can download specific packages
92+ - ` upload_policy ` : Controls who can upload specific packages
93+
94+ Both policies map package names (normalized) to lists of user/group PRNs.
95+
96+ #### Adding Permissions
97+
98+ Use the ` add ` action to grant users/groups access to packages:
99+
100+ ``` bash
101+ # Add download permissions for multiple packages
102+ pulp content-guard package-permission add \
103+ --name my-package-guard \
104+ --packages shelf-reader django \
105+ --users-groups prn:core.user:alice prn:core.group:developers \
106+ --policy-type download
107+
108+ # Add upload permissions
109+ pulp content-guard package-permission add \
110+ --name my-package-guard \
111+ --packages shelf-reader \
112+ --users-groups prn:core.user:bob \
113+ --policy-type upload
114+ ```
115+
116+ #### Removing Permissions
117+
118+ Use the ` remove ` action to revoke access:
119+
120+ ``` bash
121+ # Remove specific users/groups from a package
122+ pulp content-guard package-permission remove \
123+ --name my-package-guard \
124+ --packages shelf-reader \
125+ --users-groups prn:core.user:alice \
126+ --policy-type download
127+
128+ # Remove all permissions for a package (use '*' in users-groups)
129+ pulp content-guard package-permission remove \
130+ --name my-package-guard \
131+ --packages django \
132+ --users-groups ' *' \
133+ --policy-type download
134+
135+ # Remove all packages from a policy (use '*' in packages)
136+ pulp content-guard package-permission remove \
137+ --name my-package-guard \
138+ --packages ' *' \
139+ --users-groups ' ' \
140+ --policy-type download
141+ ```
142+
143+ ### How PackagePermissionGuard Works
144+
145+ - ** Downloads** : During downloads the guard's ` permit() ` method checks the ` download_policy ` to see
146+ if there is a policy for the package. If no policy the download is permited. If there is one it
147+ then checks that the user of the request is in the policy list for that package.
148+
149+ - ** Uploads** : For uploads, the endpoint's access policy checks the ` upload_policy ` to see if the
150+ user/group has permission for the package being uploaded. If the package or user is not in the
151+ policy the upload is denied.
152+
153+ !!! note
154+ For the content guard to properly protect uploads the ` legacy ` and ` simple ` AccessPolies must
155+ use the ` package_permission_check ` condition for their ` create ` action. This is the current
156+ default with a fallback to ` index_has_repo_perm ` when there is no content guard on the distro.
157+
76158## Index Specific Access Conditions
77159
78160Pulp Python comes with two specific access condition methods that can be used in the PyPI access policies.
@@ -89,5 +171,15 @@ the modify python repository permission.
89171This access condition checks if the user has the supplied permission on the index (distribution) itself. If no
90172permission is specified for the method then it will use ` python.view_pythondistribution ` as its default.
91173
174+ ### ` package_permission_check `
175+
176+ This access condition checks PackagePermissionGuard for package-level permissions. It extracts the package name
177+ from the request (from URL path for downloads,from filename for uploads) and checks if the user/group has
178+ permission for that specific package in the guard's policy. This condition is used by default in PyPI upload
179+ endpoints to enable package-level upload control when a PackagePermissionGuard is attached to the distribution.
180+
181+ - For downloads: Checks ` download_policy ` and denies only if package in policy and user/group is not.
182+ - For uploads: Checks ` upload_policy ` and denies access if package not in policy or user/group not allowed
183+
92184!!! note
93- Both access condition methods are compatible with the Pulp Domains feature.
185+ All access condition methods are compatible with the Pulp Domains feature.
0 commit comments