Allow for resolution of NPM registry paths that match upstream (#21568)
This PR fixes issue #21567 allowing for package tarball URLs to match the upstream registry (and GitLab/JFrog Artifactory URLs). It uses a regex to parse the filename (which contains the NPM version) and does a fuzzy search to pull it out. The regex was built/expanded from http://json.schemastore.org/package, https://github.com/Masterminds/semver, and https://docs.npmjs.com/cli/v6/using-npm/semver and is testable here: https://regex101.com/r/OydBJq/5 Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		
							parent
							
								
									0218fa7cf1
								
							
						
					
					
						commit
						49a4464160
					
				| 
						 | 
					@ -215,6 +215,7 @@ func Routes(ctx gocontext.Context) *web.Route {
 | 
				
			||||||
					r.Get("", npm.DownloadPackageFile)
 | 
										r.Get("", npm.DownloadPackageFile)
 | 
				
			||||||
					r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
 | 
										r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
 | 
									r.Get("/-/{filename}", npm.DownloadPackageFileByName)
 | 
				
			||||||
				r.Group("/-rev/{revision}", func() {
 | 
									r.Group("/-rev/{revision}", func() {
 | 
				
			||||||
					r.Delete("", npm.DeletePackage)
 | 
										r.Delete("", npm.DeletePackage)
 | 
				
			||||||
					r.Put("", npm.DeletePreview)
 | 
										r.Put("", npm.DeletePreview)
 | 
				
			||||||
| 
						 | 
					@ -227,6 +228,7 @@ func Routes(ctx gocontext.Context) *web.Route {
 | 
				
			||||||
					r.Get("", npm.DownloadPackageFile)
 | 
										r.Get("", npm.DownloadPackageFile)
 | 
				
			||||||
					r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
 | 
										r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
 | 
									r.Get("/-/{filename}", npm.DownloadPackageFileByName)
 | 
				
			||||||
				r.Group("/-rev/{revision}", func() {
 | 
									r.Group("/-rev/{revision}", func() {
 | 
				
			||||||
					r.Delete("", npm.DeletePackage)
 | 
										r.Delete("", npm.DeletePackage)
 | 
				
			||||||
					r.Put("", npm.DeletePreview)
 | 
										r.Put("", npm.DeletePreview)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,6 +106,49 @@ func DownloadPackageFile(ctx *context.Context) {
 | 
				
			||||||
	ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
 | 
						ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DownloadPackageFileByName finds the version and serves the contents of a package
 | 
				
			||||||
 | 
					func DownloadPackageFileByName(ctx *context.Context) {
 | 
				
			||||||
 | 
						filename := ctx.Params("filename")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
 | 
				
			||||||
 | 
							OwnerID: ctx.Package.Owner.ID,
 | 
				
			||||||
 | 
							Type:    packages_model.TypeNpm,
 | 
				
			||||||
 | 
							Name: packages_model.SearchValue{
 | 
				
			||||||
 | 
								ExactMatch: true,
 | 
				
			||||||
 | 
								Value:      packageNameFromParams(ctx),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							HasFileWithName: filename,
 | 
				
			||||||
 | 
							IsInternal:      util.OptionalBoolFalse,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							apiError(ctx, http.StatusInternalServerError, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(pvs) != 1 {
 | 
				
			||||||
 | 
							apiError(ctx, http.StatusNotFound, nil)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s, pf, err := packages_service.GetFileStreamByPackageVersion(
 | 
				
			||||||
 | 
							ctx,
 | 
				
			||||||
 | 
							pvs[0],
 | 
				
			||||||
 | 
							&packages_service.PackageFileInfo{
 | 
				
			||||||
 | 
								Filename: filename,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if err == packages_model.ErrPackageFileNotExist {
 | 
				
			||||||
 | 
								apiError(ctx, http.StatusNotFound, err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							apiError(ctx, http.StatusInternalServerError, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer s.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UploadPackage creates a new package
 | 
					// UploadPackage creates a new package
 | 
				
			||||||
func UploadPackage(ctx *context.Context) {
 | 
					func UploadPackage(ctx *context.Context) {
 | 
				
			||||||
	npmPackage, err := npm_module.ParsePackage(ctx.Req.Body)
 | 
						npmPackage, err := npm_module.ParsePackage(ctx.Req.Body)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,10 +127,16 @@ func TestPackageNpm(t *testing.T) {
 | 
				
			||||||
		b, _ := base64.StdEncoding.DecodeString(data)
 | 
							b, _ := base64.StdEncoding.DecodeString(data)
 | 
				
			||||||
		assert.Equal(t, b, resp.Body.Bytes())
 | 
							assert.Equal(t, b, resp.Body.Bytes())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", fmt.Sprintf("%s/-/%s", root, filename))
 | 
				
			||||||
 | 
							req = addTokenAuthHeader(req, token)
 | 
				
			||||||
 | 
							resp = MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assert.Equal(t, b, resp.Body.Bytes())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
 | 
							pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Len(t, pvs, 1)
 | 
							assert.Len(t, pvs, 1)
 | 
				
			||||||
		assert.Equal(t, int64(1), pvs[0].DownloadCount)
 | 
							assert.Equal(t, int64(2), pvs[0].DownloadCount)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.Run("PackageMetadata", func(t *testing.T) {
 | 
						t.Run("PackageMetadata", func(t *testing.T) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue