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>
This commit is contained in:
		
							parent
							
								
									cca189ef97
								
							
						
					
					
						commit
						0c8ce71188
					
				| @ -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) | ||||||
|  | 			} else if c.UseTokenAuth { | ||||||
|  | 				req = addNuGetAPIKeyHeader(req, token) | ||||||
|  | 			} | ||||||
|  | 			resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 
 | 
 | ||||||
| 		root := setting.AppURL + url[1:] | 			var result nuget.ServiceIndexResponse | ||||||
| 		for _, r := range result.Resources { | 			DecodeJSON(t, resp, &result) | ||||||
| 			switch r.Type { | 
 | ||||||
| 			case "SearchQueryService": | 			assert.Equal(t, "3.0.0", result.Version) | ||||||
| 				fallthrough | 			assert.NotEmpty(t, result.Resources) | ||||||
| 			case "SearchQueryService/3.0.0-beta": | 
 | ||||||
| 				fallthrough | 			root := setting.AppURL + url[1:] | ||||||
| 			case "SearchQueryService/3.0.0-rc": | 			for _, r := range result.Resources { | ||||||
| 				assert.Equal(t, root+"/query", r.ID) | 				switch r.Type { | ||||||
| 			case "RegistrationsBaseUrl": | 				case "SearchQueryService": | ||||||
| 				fallthrough | 					fallthrough | ||||||
| 			case "RegistrationsBaseUrl/3.0.0-beta": | 				case "SearchQueryService/3.0.0-beta": | ||||||
| 				fallthrough | 					fallthrough | ||||||
| 			case "RegistrationsBaseUrl/3.0.0-rc": | 				case "SearchQueryService/3.0.0-rc": | ||||||
| 				assert.Equal(t, root+"/registration", r.ID) | 					assert.Equal(t, root+"/query", r.ID) | ||||||
| 			case "PackageBaseAddress/3.0.0": | 				case "RegistrationsBaseUrl": | ||||||
| 				assert.Equal(t, root+"/package", r.ID) | 					fallthrough | ||||||
| 			case "PackagePublish/2.0.0": | 				case "RegistrationsBaseUrl/3.0.0-beta": | ||||||
| 				assert.Equal(t, root, r.ID) | 					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…
	
		Reference in New Issue
	
	Block a user