Skip to content

Update NuGet group level permissions requirements

David Fernandez requested to merge 387286-fix-nuget-group-level into master

🐉 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:

  1. The project level endpoint.
  2. 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 reporters 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 reporters 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:

  1. Create the following hierarchy: parent (private Group) -> subgroup1 (Group) -> subgroup2 (Group) -> project
  2. Add a user to that project with access level at least reporter.
  3. Create a PAT for that user.
  4. 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.

Edited by David Fernandez

Merge request reports