Update NuGet group level permissions requirements
🐉 Context
GitLab projects can be used to host NuGet packages.
Users can then connect $ nuget
to pull those packages from GitLab.
They have two "levels" at their disposal:
- The project level endpoint.
- The group level endpoint.
In #387286 (closed), we noticed that pulling packages through the group level endpoint requires users to be direct members of the target group with access level at least reporter
.
This causes problems, as customers try to use the NuGet group level to reduce the amount of endpoints that they need to configure in the NuGet config. We can't certainly ask to have all authorized users be direct members of the target Group. Imagine handling this for a root group that has thousands of projects with different users.
Instead, we should relax the requirements so that we follow what we are doing in other group level endpoints such as the Maven group level endpoint. There, we simply require users to be at least reporter
s of the project that hosts the package but there is no requirement on the Group objects.
Notice that we are considering this issue on private groups/projects. In public groups, this doesn't happen.
⚙ Technical details
In the Nuget group level endpoint, we require the user to have read_package
on the group object. This will require users to be at least reporter
s of that group.
In the Maven group level endpoint, we require the user to have read_group
on the group object. As counter intuitive as this can be, direct members of included projects will automatically have read_group
on parent groups. See this rule.
This MR will thus remove the read_package
check on the group object and replace it with a read_group
check.
Now the NuGet API at any level (project or group) is composed of 4 endpoints:
- Index service. Returns a bunch of urls to locate the other endpoints.
- Metadata for a given package name. Returns data on all packages found. (The package finder will only return packages where user can
read_package
the project). - Metadata for a given package name + version. Returns data on the package found. (The package finder will only return the package where user can
read_package
the project). - Search service. Return packages found given some search terms. (The package finder will only return packages where user can
read_package
the project).
We will have some changes for guest
users as those users have read_group
on the target group but not read_package
on the target project.
Endpoint | Currently | With this MR |
---|---|---|
Index service | forbidden | ok (that's ok, it's only bunch of urls) |
Metadata for a given package name | forbidden | not found as the package finder will return an empty set. |
Metadata for a given package name + version | forbidden | not found as the package finder will return an empty set. |
Search service | forbidden | ok but the search results will be empty |
We can see that when the endpoint can link to a project, the read_package
permission is still enforced and so guest
users can't retrieve any information about packages.
The change on the search service result can be surprising but it's acceptable. A user could be a guest
on an included project but a developer
in a different included project. As such, the search should be successful but only include results where the user has read_package
on the project. If the user has only guest
access on all included projects, then we will return an empty search result.
🤔 What does this MR do and why?
- Check
read_group
in the NuGet group level endpoints. - Update the related specs.
- One word on the specs. Since we have the same endpoints in the project and group level, specs have been heavily centralized with shared examples. The problem is that we have slight differences with the group level endpoints. Those differences are implemented as overrides that the group level spec can use to change the expectations of the shared spec.
- I find the above complex to follow and read. I think we have a proof that we pushed "let's centralized as much examples as possible" too far and we need to "uncentralize" things. This being out of the scope of this MR, I simply added the changes and refactored the overrides to not lose readability.
📺 Screenshots or screen recordings
None
🔧 How to set up and validate locally
Let's create the needed objects:
- Create the following hierarchy:
parent
(private Group) ->subgroup1
(Group) ->subgroup2
(Group) ->project
- Add a user to that
project
with access level at leastreporter
. - Create a PAT for that user.
- Add a NuGet Package to that
project
. gl_pru can help here for that.
Setup $ nuget
to pull from the group
(private Group) endpoint. For this, just follow the instructions that we have in https://docs.gitlab.com/ee/user/packages/nuget_repository/#group-level-endpoint. Use the user PAT to authenticate.
Everything is now ready.
🔥 Using master
nuget install My.Nuget.Pkg -OutputDirectory . -Source gl_local
Feeds used:
http://gdk.test:8000/api/v4/groups/679/-/packages/nuget/index.json
Installing package 'My.Nuget.Pkg' to '/Users/david/projects/sandbox/nuget/pull'.
MSBuild auto-detection: using msbuild version '15.0' from '/Library/Frameworks/Mono.framework/Versions/6.12.0/lib/mono/msbuild/15.0/bin'.
Using credentials from config. UserName: namespace220
The remote server indicated that the previous request was forbidden. Please provide credentials for: http://gdk.test:8000/api/v4/groups/679/-/packages/nuget/index.json
UserName:
Request is forbidden
🚒 Using this MR
Clear the cache if necessary with $ nuget locals all -clear
$ nuget install My.Nuget.Pkg -OutputDirectory . -Source gl_local
Feeds used:
http://gdk.test:8000/api/v4/groups/679/-/packages/nuget/index.json
Installing package 'My.Nuget.Pkg' to '/Users/david/projects/sandbox/nuget/pull'.
GET http://gdk.test:8000/api/v4/groups/679/-/packages/nuget/metadata/my.nuget.pkg/index.json
OK http://gdk.test:8000/api/v4/groups/679/-/packages/nuget/metadata/my.nuget.pkg/index.json 337ms
Attempting to gather dependency information for package 'My.Nuget.Pkg.2.4.8' with respect to project '/Users/david/projects/sandbox/nuget/pull', targeting 'Any,Version=v0.0'
Gathering dependency information took 15 ms
Attempting to resolve dependencies for package 'My.Nuget.Pkg.2.4.8' with DependencyBehavior 'Lowest'
Resolving dependency information took 0 ms
Resolving actions to install package 'My.Nuget.Pkg.2.4.8'
Resolved actions to install package 'My.Nuget.Pkg.2.4.8'
Retrieving package 'My.Nuget.Pkg 2.4.8' from 'gl_local'.
GET http://gdk.test:8000/api/v4/projects/334/packages/nuget/download/My.Nuget.Pkg/2.4.8/my.nuget.pkg.2.4.8.nupkg
OK http://gdk.test:8000/api/v4/projects/334/packages/nuget/download/My.Nuget.Pkg/2.4.8/my.nuget.pkg.2.4.8.nupkg 339ms
Installing My.Nuget.Pkg 2.4.8.
Adding package 'My.Nuget.Pkg.2.4.8' to folder '/Users/david/projects/sandbox/nuget/pull'
Added package 'My.Nuget.Pkg.2.4.8' to folder '/Users/david/projects/sandbox/nuget/pull'
Successfully installed 'My.Nuget.Pkg 2.4.8' to /Users/david/projects/sandbox/nuget/pull
Executing nuget actions took 444 ms
Package installed
🏎 MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.