From 49a4464160254604d2c42b760a901952d8bc3c8b Mon Sep 17 00:00:00 2001 From: Wayne Starr Date: Mon, 24 Oct 2022 08:50:22 -0500 Subject: [PATCH] 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 --- routers/api/packages/api.go | 2 + routers/api/packages/npm/npm.go | 43 ++++++++++++++++++++++ tests/integration/api_packages_npm_test.go | 8 +++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index f6ab961f5ec..69c8dd6564b 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -215,6 +215,7 @@ func Routes(ctx gocontext.Context) *web.Route { r.Get("", npm.DownloadPackageFile) r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion) }) + r.Get("/-/{filename}", npm.DownloadPackageFileByName) r.Group("/-rev/{revision}", func() { r.Delete("", npm.DeletePackage) r.Put("", npm.DeletePreview) @@ -227,6 +228,7 @@ func Routes(ctx gocontext.Context) *web.Route { r.Get("", npm.DownloadPackageFile) r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion) }) + r.Get("/-/{filename}", npm.DownloadPackageFileByName) r.Group("/-rev/{revision}", func() { r.Delete("", npm.DeletePackage) r.Put("", npm.DeletePreview) diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 2989ce6e7f6..82dae0cf435 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -106,6 +106,49 @@ func DownloadPackageFile(ctx *context.Context) { 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 func UploadPackage(ctx *context.Context) { npmPackage, err := npm_module.ParsePackage(ctx.Req.Body) diff --git a/tests/integration/api_packages_npm_test.go b/tests/integration/api_packages_npm_test.go index 4fdcf6682e9..20412fe727c 100644 --- a/tests/integration/api_packages_npm_test.go +++ b/tests/integration/api_packages_npm_test.go @@ -127,10 +127,16 @@ func TestPackageNpm(t *testing.T) { b, _ := base64.StdEncoding.DecodeString(data) 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) assert.NoError(t, err) 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) {