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 <name>`
but not with `--source <url>/index.json`.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
forgejo
KN4CK3R 2 years ago committed by GitHub
parent cca189ef97
commit 0c8ce71188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -69,7 +69,7 @@ func Routes(ctx gocontext.Context) *web.Route {
r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata) r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata)
r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile) r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile)
r.Put("", reqPackageAccess(perm.AccessModeWrite), composer.UploadPackage) r.Put("", reqPackageAccess(perm.AccessModeWrite), composer.UploadPackage)
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/conan", func() { r.Group("/conan", func() {
r.Group("/v1", func() { r.Group("/v1", func() {
r.Get("/ping", conan.Ping) r.Get("/ping", conan.Ping)
@ -157,7 +157,7 @@ func Routes(ctx gocontext.Context) *web.Route {
}, conan.ExtractPathParameters) }, conan.ExtractPathParameters)
}) })
}) })
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/generic", func() { r.Group("/generic", func() {
r.Group("/{packagename}/{packageversion}", func() { r.Group("/{packagename}/{packageversion}", func() {
r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage) r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage)
@ -169,33 +169,35 @@ func Routes(ctx gocontext.Context) *web.Route {
}, reqPackageAccess(perm.AccessModeWrite)) }, reqPackageAccess(perm.AccessModeWrite))
}) })
}) })
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/helm", func() { r.Group("/helm", func() {
r.Get("/index.yaml", helm.Index) r.Get("/index.yaml", helm.Index)
r.Get("/{filename}", helm.DownloadPackageFile) r.Get("/{filename}", helm.DownloadPackageFile)
r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage) r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage)
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/maven", func() { r.Group("/maven", func() {
r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile) r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile)
r.Get("/*", maven.DownloadPackageFile) r.Get("/*", maven.DownloadPackageFile)
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/nuget", func() { r.Group("/nuget", func() {
r.Get("/index.json", nuget.ServiceIndex) r.Get("/index.json", nuget.ServiceIndex) // Needs to be unauthenticated for the NuGet client.
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.Group("", func() {
r.Put("/", nuget.UploadPackage) r.Get("/query", nuget.SearchService)
r.Put("/symbolpackage", nuget.UploadSymbolPackage) r.Group("/registration/{id}", func() {
r.Delete("/{id}/{version}", nuget.DeletePackage) r.Get("/index.json", nuget.RegistrationIndex)
}, reqPackageAccess(perm.AccessModeWrite)) r.Get("/{version}", nuget.RegistrationLeaf)
r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile) })
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("/npm", func() {
r.Group("/@{scope}/{id}", func() { r.Group("/@{scope}/{id}", func() {
@ -239,7 +241,7 @@ func Routes(ctx gocontext.Context) *web.Route {
r.Group("/-/v1/search", func() { r.Group("/-/v1/search", func() {
r.Get("", npm.PackageSearch) r.Get("", npm.PackageSearch)
}) })
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/pub", func() { r.Group("/pub", func() {
r.Group("/api/packages", func() { r.Group("/api/packages", func() {
r.Group("/versions/new", func() { r.Group("/versions/new", func() {
@ -253,12 +255,12 @@ func Routes(ctx gocontext.Context) *web.Route {
r.Get("/{version}", pub.PackageVersionMetadata) r.Get("/{version}", pub.PackageVersionMetadata)
}) })
}) })
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/pypi", func() { r.Group("/pypi", func() {
r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile) r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile)
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile) r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
r.Get("/simple/{id}", pypi.PackageMetadata) r.Get("/simple/{id}", pypi.PackageMetadata)
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/rubygems", func() { r.Group("/rubygems", func() {
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages) r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest) r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
@ -269,7 +271,7 @@ func Routes(ctx gocontext.Context) *web.Route {
r.Post("/", rubygems.UploadPackageFile) r.Post("/", rubygems.UploadPackageFile)
r.Delete("/yank", rubygems.DeletePackage) r.Delete("/yank", rubygems.DeletePackage)
}, reqPackageAccess(perm.AccessModeWrite)) }, reqPackageAccess(perm.AccessModeWrite))
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/vagrant", func() { r.Group("/vagrant", func() {
r.Group("/authenticate", func() { r.Group("/authenticate", func() {
r.Get("", vagrant.CheckAuthenticate) r.Get("", vagrant.CheckAuthenticate)
@ -282,8 +284,8 @@ func Routes(ctx gocontext.Context) *web.Route {
r.Put("", reqPackageAccess(perm.AccessModeWrite), vagrant.UploadPackageFile) r.Put("", reqPackageAccess(perm.AccessModeWrite), vagrant.UploadPackageFile)
}) })
}) })
}) }, reqPackageAccess(perm.AccessModeRead))
}, context_service.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) }, context_service.UserAssignmentWeb(), context.PackageAssignment())
return r return r
} }

@ -19,6 +19,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
nuget_module "code.gitea.io/gitea/modules/packages/nuget" nuget_module "code.gitea.io/gitea/modules/packages/nuget"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/packages/nuget" "code.gitea.io/gitea/routers/api/packages/nuget"
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
@ -66,39 +67,58 @@ func TestPackageNuGet(t *testing.T) {
t.Run("ServiceIndex", func(t *testing.T) { t.Run("ServiceIndex", func(t *testing.T) {
defer tests.PrintCurrentTest(t)() defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Visibility: structs.VisibleTypePrivate})
req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) cases := []struct {
req = addNuGetAPIKeyHeader(req, token) Owner string
resp := MakeRequest(t, req, http.StatusOK) 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 for _, c := range cases {
DecodeJSON(t, resp, &result) url := fmt.Sprintf("/api/packages/%s/nuget", c.Owner)
assert.Equal(t, "3.0.0", result.Version) req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url))
assert.NotEmpty(t, result.Resources) if c.UseBasicAuth {
req = AddBasicAuthHeader(req, user.Name)
root := setting.AppURL + url[1:] } else if c.UseTokenAuth {
for _, r := range result.Resources { req = addNuGetAPIKeyHeader(req, token)
switch r.Type { }
case "SearchQueryService": resp := MakeRequest(t, req, http.StatusOK)
fallthrough
case "SearchQueryService/3.0.0-beta": var result nuget.ServiceIndexResponse
fallthrough DecodeJSON(t, resp, &result)
case "SearchQueryService/3.0.0-rc":
assert.Equal(t, root+"/query", r.ID) assert.Equal(t, "3.0.0", result.Version)
case "RegistrationsBaseUrl": assert.NotEmpty(t, result.Resources)
fallthrough
case "RegistrationsBaseUrl/3.0.0-beta": root := setting.AppURL + url[1:]
fallthrough for _, r := range result.Resources {
case "RegistrationsBaseUrl/3.0.0-rc": switch r.Type {
assert.Equal(t, root+"/registration", r.ID) case "SearchQueryService":
case "PackageBaseAddress/3.0.0": fallthrough
assert.Equal(t, root+"/package", r.ID) case "SearchQueryService/3.0.0-beta":
case "PackagePublish/2.0.0": fallthrough
assert.Equal(t, root, r.ID) 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)
}
} }
} }
}) })

Loading…
Cancel
Save