From 5bb0980757a4de2f26e603383e068c587c94fd4f Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sat, 24 Sep 2022 17:17:08 +0200 Subject: [PATCH 1/2] Make NuGet service index publicly accessible (#21242) Addition to #20734, Fixes #20717 The `/index.json` endpoint needs to be accessible even if the registry is private. The NuGet client uses this endpoint without authentification. The old fix only works if the NuGet cli is used with `--source ` but not with `--source /index.json`. Co-authored-by: wxiaoguang --- integrations/api_packages_nuget_test.go | 80 +++++++++++++++---------- routers/api/packages/api.go | 48 ++++++++------- 2 files changed, 75 insertions(+), 53 deletions(-) diff --git a/integrations/api_packages_nuget_test.go b/integrations/api_packages_nuget_test.go index 06eb485541efa..c79b49381b20e 100644 --- a/integrations/api_packages_nuget_test.go +++ b/integrations/api_packages_nuget_test.go @@ -19,6 +19,7 @@ import ( user_model "code.gitea.io/gitea/models/user" nuget_module "code.gitea.io/gitea/modules/packages/nuget" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/packages/nuget" "github.com/stretchr/testify/assert" @@ -65,39 +66,58 @@ func TestPackageNuGet(t *testing.T) { t.Run("ServiceIndex", func(t *testing.T) { defer PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) - req = AddBasicAuthHeader(req, user.Name) - MakeRequest(t, req, http.StatusOK) + privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Visibility: structs.VisibleTypePrivate}) - req = NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) - req = addNuGetAPIKeyHeader(req, token) - resp := MakeRequest(t, req, http.StatusOK) + cases := []struct { + Owner string + UseBasicAuth bool + UseTokenAuth bool + }{ + {privateUser.Name, false, false}, + {privateUser.Name, true, false}, + {privateUser.Name, false, true}, + {user.Name, false, false}, + {user.Name, true, false}, + {user.Name, false, true}, + } - var result nuget.ServiceIndexResponse - DecodeJSON(t, resp, &result) + for _, c := range cases { + url := fmt.Sprintf("/api/packages/%s/nuget", c.Owner) - assert.Equal(t, "3.0.0", result.Version) - assert.NotEmpty(t, result.Resources) - - root := setting.AppURL + url[1:] - for _, r := range result.Resources { - switch r.Type { - case "SearchQueryService": - fallthrough - case "SearchQueryService/3.0.0-beta": - fallthrough - case "SearchQueryService/3.0.0-rc": - assert.Equal(t, root+"/query", r.ID) - case "RegistrationsBaseUrl": - fallthrough - case "RegistrationsBaseUrl/3.0.0-beta": - fallthrough - case "RegistrationsBaseUrl/3.0.0-rc": - assert.Equal(t, root+"/registration", r.ID) - case "PackageBaseAddress/3.0.0": - assert.Equal(t, root+"/package", r.ID) - case "PackagePublish/2.0.0": - assert.Equal(t, root, r.ID) + req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) + if c.UseBasicAuth { + req = AddBasicAuthHeader(req, user.Name) + } else if c.UseTokenAuth { + req = addNuGetAPIKeyHeader(req, token) + } + resp := MakeRequest(t, req, http.StatusOK) + + var result nuget.ServiceIndexResponse + DecodeJSON(t, resp, &result) + + assert.Equal(t, "3.0.0", result.Version) + assert.NotEmpty(t, result.Resources) + + root := setting.AppURL + url[1:] + for _, r := range result.Resources { + switch r.Type { + case "SearchQueryService": + fallthrough + case "SearchQueryService/3.0.0-beta": + fallthrough + case "SearchQueryService/3.0.0-rc": + assert.Equal(t, root+"/query", r.ID) + case "RegistrationsBaseUrl": + fallthrough + case "RegistrationsBaseUrl/3.0.0-beta": + fallthrough + case "RegistrationsBaseUrl/3.0.0-rc": + assert.Equal(t, root+"/registration", r.ID) + case "PackageBaseAddress/3.0.0": + assert.Equal(t, root+"/package", r.ID) + case "PackagePublish/2.0.0": + assert.Equal(t, root, r.ID) + } } } }) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index b27569aba45c9..dd5cf4a4c0ed8 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -66,7 +66,7 @@ func Routes() *web.Route { r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata) r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile) r.Put("", reqPackageAccess(perm.AccessModeWrite), composer.UploadPackage) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/conan", func() { r.Group("/v1", func() { r.Get("/ping", conan.Ping) @@ -154,7 +154,7 @@ func Routes() *web.Route { }, conan.ExtractPathParameters) }) }) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/generic", func() { r.Group("/{packagename}/{packageversion}/{filename}", func() { r.Get("", generic.DownloadPackageFile) @@ -163,33 +163,35 @@ func Routes() *web.Route { r.Delete("", generic.DeletePackage) }, reqPackageAccess(perm.AccessModeWrite)) }) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/helm", func() { r.Get("/index.yaml", helm.Index) r.Get("/{filename}", helm.DownloadPackageFile) r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/maven", func() { r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile) r.Get("/*", maven.DownloadPackageFile) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/nuget", func() { - r.Get("/index.json", nuget.ServiceIndex) - r.Get("/query", nuget.SearchService) - r.Group("/registration/{id}", func() { - r.Get("/index.json", nuget.RegistrationIndex) - r.Get("/{version}", nuget.RegistrationLeaf) - }) - r.Group("/package/{id}", func() { - r.Get("/index.json", nuget.EnumeratePackageVersions) - r.Get("/{version}/{filename}", nuget.DownloadPackageFile) - }) + r.Get("/index.json", nuget.ServiceIndex) // Needs to be unauthenticated for the NuGet client. r.Group("", func() { - r.Put("/", nuget.UploadPackage) - r.Put("/symbolpackage", nuget.UploadSymbolPackage) - r.Delete("/{id}/{version}", nuget.DeletePackage) - }, reqPackageAccess(perm.AccessModeWrite)) - r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile) + r.Get("/query", nuget.SearchService) + r.Group("/registration/{id}", func() { + r.Get("/index.json", nuget.RegistrationIndex) + r.Get("/{version}", nuget.RegistrationLeaf) + }) + r.Group("/package/{id}", func() { + r.Get("/index.json", nuget.EnumeratePackageVersions) + r.Get("/{version}/{filename}", nuget.DownloadPackageFile) + }) + r.Group("", func() { + r.Put("/", nuget.UploadPackage) + r.Put("/symbolpackage", nuget.UploadSymbolPackage) + r.Delete("/{id}/{version}", nuget.DeletePackage) + }, reqPackageAccess(perm.AccessModeWrite)) + r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile) + }, reqPackageAccess(perm.AccessModeRead)) }) r.Group("/npm", func() { r.Group("/@{scope}/{id}", func() { @@ -216,12 +218,12 @@ func Routes() *web.Route { r.Delete("", npm.DeletePackageTag) }, reqPackageAccess(perm.AccessModeWrite)) }) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/pypi", func() { r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile) r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile) r.Get("/simple/{id}", pypi.PackageMetadata) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/rubygems", func() { r.Get("/specs.4.8.gz", rubygems.EnumeratePackages) r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest) @@ -233,7 +235,7 @@ func Routes() *web.Route { r.Delete("/yank", rubygems.DeletePackage) }, reqPackageAccess(perm.AccessModeWrite)) }) - }, context_service.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) + }, context_service.UserAssignmentWeb(), context.PackageAssignment()) return r } From 45cf01db431ed60d56eee9769755514309f5e991 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 3 Oct 2022 08:45:42 +0000 Subject: [PATCH 2/2] Fix test. --- integrations/api_packages_nuget_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/api_packages_nuget_test.go b/integrations/api_packages_nuget_test.go index c79b49381b20e..1fb7c4728b9c3 100644 --- a/integrations/api_packages_nuget_test.go +++ b/integrations/api_packages_nuget_test.go @@ -66,7 +66,7 @@ func TestPackageNuGet(t *testing.T) { t.Run("ServiceIndex", func(t *testing.T) { defer PrintCurrentTest(t)() - privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Visibility: structs.VisibleTypePrivate}) + privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Visibility: structs.VisibleTypePrivate}).(*user_model.User) cases := []struct { Owner string