From eb798b4889c6c97c4a1709c02620ecd0d85ff45b Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Fri, 15 Jul 2022 04:50:21 +0200 Subject: [PATCH 01/12] npm packages: automatically set repository link for package based on the repository url present inside package.json; closes #20146 --- modules/packages/npm/creator.go | 1 + modules/packages/npm/metadata.go | 1 + routers/api/packages/npm/npm.go | 38 ++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 5e7e0e2983301..01ca79e935911 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -216,6 +216,7 @@ func ParsePackage(r io.Reader) (*Package, error) { Author: meta.Author.Name, License: meta.License, ProjectURL: meta.Homepage, + Repository: meta.Repository, Keywords: meta.Keywords, Dependencies: meta.Dependencies, DevelopmentDependencies: meta.DevDependencies, diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go index 77b77472a7a2c..bae34de8d80fe 100644 --- a/modules/packages/npm/metadata.go +++ b/modules/packages/npm/metadata.go @@ -14,6 +14,7 @@ type Metadata struct { Author string `json:"author,omitempty"` License string `json:"license,omitempty"` ProjectURL string `json:"project_url,omitempty"` + Repository Repository `json:"repository,omitempty"` Keywords []string `json:"keywords,omitempty"` Dependencies map[string]string `json:"dependencies,omitempty"` DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"` diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 0d25f173e922b..ff9969cd93394 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" npm_module "code.gitea.io/gitea/modules/packages/npm" @@ -217,6 +218,43 @@ func UploadPackage(ctx *context.Context) { } } + if npmPackage.Metadata.Repository.Type == "git" { + repoURL := npmPackage.Metadata.Repository.URL + + // possible urls for git: + // https://my.domain/sub-path//.git + // git+ssh://user@my.domain//.git + // user@my.domain:/.git + + var pathSegments []string + if strings.HasPrefix(repoURL, setting.AppURL) { + pathSegments = strings.Split(strings.Replace(repoURL, setting.AppURL, "", 1), "/") + } else { + sshURL := setting.SSH.User + "@" + setting.SSH.Domain + ":" + if strings.HasPrefix(repoURL, sshURL) { + pathSegments = strings.Split(strings.Replace(repoURL, sshURL, "", 1), "/") + } else { + sshURL := "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/" + if strings.HasPrefix(repoURL, sshURL) { + pathSegments = strings.Split(strings.Replace(repoURL, sshURL, "", 1), "/") + } + } + } + + if len(pathSegments) == 2 { + ownerName := pathSegments[0] + repoName := strings.Replace(pathSegments[1], ".git", "", 1) + repository, err := repo.GetRepositoryByOwnerAndName(ownerName, repoName) + if err == nil { + if pv.CreatorID == repository.OwnerID { + if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repository.ID); err != nil { + ctx.ServerError("SetRepositoryLink", err) + } + } + } + } + } + ctx.Status(http.StatusCreated) } From 66eecabe86f56d0cd3dd6878c9f59e2af66ee304 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Sat, 16 Jul 2022 18:11:30 +0200 Subject: [PATCH 02/12] Moved repository lookup for npm packages into own function and adding tests for it --- modules/packages/npm/creator.go | 45 ++++++++++++++- modules/packages/npm/creator_test.go | 83 ++++++++++++++++++++++++++++ routers/api/packages/npm/npm.go | 39 ++----------- 3 files changed, 132 insertions(+), 35 deletions(-) diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 01ca79e935911..7e3667d7815eb 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -14,8 +14,10 @@ import ( "strings" "time" + "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/validation" "github.com/hashicorp/go-version" @@ -164,6 +166,48 @@ type Repository struct { URL string `json:"url"` } +func (r *Repository) GetGiteaRepository() (*repo.Repository, error) { + if r.Type != "git" { + return nil, fmt.Errorf("only git repositories are supported") + } + + // possible urls for git: + // https://my.domain/sub-path//.git + // git+ssh://user@my.domain//.git + // user@my.domain:/.git + + retrievePathSegments := func(repoURL string) []string { + if strings.HasPrefix(r.URL, setting.AppURL) { + return strings.Split(strings.Replace(r.URL, setting.AppURL, "", 1), "/") + } + + sshURLVariants := [4]string{ + setting.SSH.Domain + ":", + setting.SSH.User + "@" + setting.SSH.Domain + ":", + "git+ssh://" + setting.SSH.Domain + "/", + "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/", + } + + for _, sshURL := range sshURLVariants { + if strings.HasPrefix(r.URL, sshURL) { + return strings.Split(strings.Replace(r.URL, sshURL, "", 1), "/") + } + } + + return make([]string, 0) + } + + pathSegments := retrievePathSegments(r.URL) + + if len(pathSegments) != 2 { + return nil, fmt.Errorf("unknown or malformed repository URL") + } + + ownerName := pathSegments[0] + repoName := strings.Replace(pathSegments[1], ".git", "", 1) + return repo.GetRepositoryByOwnerAndName(ownerName, repoName) +} + // PackageAttachment https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#package type PackageAttachment struct { ContentType string `json:"content_type"` @@ -216,7 +260,6 @@ func ParsePackage(r io.Reader) (*Package, error) { Author: meta.Author.Name, License: meta.License, ProjectURL: meta.Homepage, - Repository: meta.Repository, Keywords: meta.Keywords, Dependencies: meta.Dependencies, DevelopmentDependencies: meta.DevDependencies, diff --git a/modules/packages/npm/creator_test.go b/modules/packages/npm/creator_test.go index 168f950038d54..6264fd03f7820 100644 --- a/modules/packages/npm/creator_test.go +++ b/modules/packages/npm/creator_test.go @@ -7,14 +7,97 @@ import ( "bytes" "encoding/base64" "fmt" + "path/filepath" "strings" "testing" + _ "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/json" "github.com/stretchr/testify/assert" ) +func TestMain(m *testing.M) { + unittest.MainTest(m, &unittest.TestOptions{ + GiteaRootPath: filepath.Join("..", "..", ".."), + }) +} + +func TestGetGiteaRepository(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("InvalidType", func(t *testing.T) { + r := Repository{Type: "svn", URL: ""} + repo, err := r.GetGiteaRepository() + + assert.Nil(t, repo) + assert.Error(t, err) + }) + + t.Run("InvalidPath", func(t *testing.T) { + r := Repository{Type: "git", URL: "something"} + repo, err := r.GetGiteaRepository() + + assert.Nil(t, repo) + assert.Error(t, err) + }) + + t.Run("ValidHttpURL", func(t *testing.T) { + test := func(t *testing.T, url string) { + r := Repository{Type: "git", URL: url} + repo, err := r.GetGiteaRepository() + + assert.NotNil(t, repo) + assert.NoError(t, err) + + assert.Equal(t, repo.ID, int64(2)) + assert.Equal(t, repo.OwnerID, int64(2)) + } + + test(t, "https://try.gitea.io/user2/repo2") + test(t, "https://try.gitea.io/user2/repo2.git") + }) + + t.Run("ValidGitSshURL", func(t *testing.T) { + test := func(t *testing.T, url string) { + r := Repository{Type: "git", URL: url} + repo, err := r.GetGiteaRepository() + + assert.NotNil(t, repo) + assert.NoError(t, err) + + assert.Equal(t, repo.ID, int64(2)) + assert.Equal(t, repo.OwnerID, int64(2)) + } + + test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2") + test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git") + + test(t, "git+ssh://try.gitea.io/user2/repo2") + test(t, "git+ssh://try.gitea.io/user2/repo2.git") + }) + + t.Run("ValidImplicitSshURL", func(t *testing.T) { + test := func(t *testing.T, url string) { + r := Repository{Type: "git", URL: url} + repo, err := r.GetGiteaRepository() + + assert.NotNil(t, repo) + assert.NoError(t, err) + + assert.Equal(t, repo.ID, int64(2)) + assert.Equal(t, repo.OwnerID, int64(2)) + } + + test(t, "sshuser@try.gitea.io:user2/repo2") + test(t, "sshuser@try.gitea.io:user2/repo2.git") + + test(t, "try.gitea.io:user2/repo2") + test(t, "try.gitea.io:user2/repo2.git") + }) +} + func TestParsePackage(t *testing.T) { packageScope := "@scope" packageName := "test-package" diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index ff9969cd93394..830e26210c95e 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" - "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" npm_module "code.gitea.io/gitea/modules/packages/npm" @@ -218,39 +217,11 @@ func UploadPackage(ctx *context.Context) { } } - if npmPackage.Metadata.Repository.Type == "git" { - repoURL := npmPackage.Metadata.Repository.URL - - // possible urls for git: - // https://my.domain/sub-path//.git - // git+ssh://user@my.domain//.git - // user@my.domain:/.git - - var pathSegments []string - if strings.HasPrefix(repoURL, setting.AppURL) { - pathSegments = strings.Split(strings.Replace(repoURL, setting.AppURL, "", 1), "/") - } else { - sshURL := setting.SSH.User + "@" + setting.SSH.Domain + ":" - if strings.HasPrefix(repoURL, sshURL) { - pathSegments = strings.Split(strings.Replace(repoURL, sshURL, "", 1), "/") - } else { - sshURL := "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/" - if strings.HasPrefix(repoURL, sshURL) { - pathSegments = strings.Split(strings.Replace(repoURL, sshURL, "", 1), "/") - } - } - } - - if len(pathSegments) == 2 { - ownerName := pathSegments[0] - repoName := strings.Replace(pathSegments[1], ".git", "", 1) - repository, err := repo.GetRepositoryByOwnerAndName(ownerName, repoName) - if err == nil { - if pv.CreatorID == repository.OwnerID { - if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repository.ID); err != nil { - ctx.ServerError("SetRepositoryLink", err) - } - } + repository, err := npmPackage.Metadata.Repository.GetGiteaRepository() + if err == nil { + if pv.CreatorID == repository.OwnerID { + if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repository.ID); err != nil { + ctx.ServerError("SetRepositoryLink", err) } } } From bf19f4e23545c29e697bb4c84f02cb4426e9347d Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Wed, 20 Jul 2022 00:11:59 +0200 Subject: [PATCH 03/12] Moving url to repo lookup to repo model; adding permission check when setting npm-package repo-link --- models/repo/repo.go | 38 ++++++++++++++ models/repo/repo_test.go | 62 +++++++++++++++++++++++ modules/packages/npm/creator.go | 48 ++---------------- modules/packages/npm/creator_test.go | 74 ---------------------------- modules/packages/npm/metadata.go | 2 +- routers/api/packages/npm/npm.go | 16 ++++-- 6 files changed, 118 insertions(+), 122 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index dcffb63fd1970..1b8f7aa0cb447 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -658,6 +658,44 @@ func GetRepositoryByName(ownerID int64, name string) (*Repository, error) { return repo, err } +func GetRepositoryByURL(repoURL string) (*Repository, error) { + // possible urls for git: + // https://my.domain/sub-path//.git + // git+ssh://user@my.domain//.git + // user@my.domain:/.git + + retrievePathSegments := func(repoURL string) []string { + if strings.HasPrefix(repoURL, setting.AppURL) { + return strings.Split(strings.Replace(repoURL, setting.AppURL, "", 1), "/") + } + + sshURLVariants := [4]string{ + setting.SSH.Domain + ":", + setting.SSH.User + "@" + setting.SSH.Domain + ":", + "git+ssh://" + setting.SSH.Domain + "/", + "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/", + } + + for _, sshURL := range sshURLVariants { + if strings.HasPrefix(repoURL, sshURL) { + return strings.Split(strings.Replace(repoURL, sshURL, "", 1), "/") + } + } + + return []string{} + } + + pathSegments := retrievePathSegments(repoURL) + + if len(pathSegments) < 2 { + return nil, fmt.Errorf("unknown or malformed repository URL") + } + + ownerName := pathSegments[0] + repoName := strings.Replace(pathSegments[1], ".git", "", 1) + return GetRepositoryByOwnerAndName(ownerName, repoName) +} + // GetRepositoryByID returns the repository by given id if exists. func GetRepositoryByID(ctx context.Context, id int64) (*Repository, error) { repo := new(Repository) diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go index fb473151eb34b..839d3a560f31d 100644 --- a/models/repo/repo_test.go +++ b/models/repo/repo_test.go @@ -124,3 +124,65 @@ func TestMetas(t *testing.T) { assert.Equal(t, "user3", metas["org"]) assert.Equal(t, ",owners,team1,", metas["teams"]) } + +func TestGetRepositoryByURL(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("InvalidPath", func(t *testing.T) { + repo, err := repo_model.GetRepositoryByURL("something") + + assert.Nil(t, repo) + assert.Error(t, err) + }) + + t.Run("ValidHttpURL", func(t *testing.T) { + test := func(t *testing.T, url string) { + repo, err := repo_model.GetRepositoryByURL(url) + + assert.NotNil(t, repo) + assert.NoError(t, err) + + assert.Equal(t, repo.ID, int64(2)) + assert.Equal(t, repo.OwnerID, int64(2)) + } + + test(t, "https://try.gitea.io/user2/repo2") + test(t, "https://try.gitea.io/user2/repo2.git") + }) + + t.Run("ValidGitSshURL", func(t *testing.T) { + test := func(t *testing.T, url string) { + repo, err := repo_model.GetRepositoryByURL(url) + + assert.NotNil(t, repo) + assert.NoError(t, err) + + assert.Equal(t, repo.ID, int64(2)) + assert.Equal(t, repo.OwnerID, int64(2)) + } + + test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2") + test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git") + + test(t, "git+ssh://try.gitea.io/user2/repo2") + test(t, "git+ssh://try.gitea.io/user2/repo2.git") + }) + + t.Run("ValidImplicitSshURL", func(t *testing.T) { + test := func(t *testing.T, url string) { + repo, err := repo_model.GetRepositoryByURL(url) + + assert.NotNil(t, repo) + assert.NoError(t, err) + + assert.Equal(t, repo.ID, int64(2)) + assert.Equal(t, repo.OwnerID, int64(2)) + } + + test(t, "sshuser@try.gitea.io:user2/repo2") + test(t, "sshuser@try.gitea.io:user2/repo2.git") + + test(t, "try.gitea.io:user2/repo2") + test(t, "try.gitea.io:user2/repo2.git") + }) +} \ No newline at end of file diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 7e3667d7815eb..5d2dab2ac3573 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -14,10 +14,8 @@ import ( "strings" "time" - "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/validation" "github.com/hashicorp/go-version" @@ -166,48 +164,6 @@ type Repository struct { URL string `json:"url"` } -func (r *Repository) GetGiteaRepository() (*repo.Repository, error) { - if r.Type != "git" { - return nil, fmt.Errorf("only git repositories are supported") - } - - // possible urls for git: - // https://my.domain/sub-path//.git - // git+ssh://user@my.domain//.git - // user@my.domain:/.git - - retrievePathSegments := func(repoURL string) []string { - if strings.HasPrefix(r.URL, setting.AppURL) { - return strings.Split(strings.Replace(r.URL, setting.AppURL, "", 1), "/") - } - - sshURLVariants := [4]string{ - setting.SSH.Domain + ":", - setting.SSH.User + "@" + setting.SSH.Domain + ":", - "git+ssh://" + setting.SSH.Domain + "/", - "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/", - } - - for _, sshURL := range sshURLVariants { - if strings.HasPrefix(r.URL, sshURL) { - return strings.Split(strings.Replace(r.URL, sshURL, "", 1), "/") - } - } - - return make([]string, 0) - } - - pathSegments := retrievePathSegments(r.URL) - - if len(pathSegments) != 2 { - return nil, fmt.Errorf("unknown or malformed repository URL") - } - - ownerName := pathSegments[0] - repoName := strings.Replace(pathSegments[1], ".git", "", 1) - return repo.GetRepositoryByOwnerAndName(ownerName, repoName) -} - // PackageAttachment https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#package type PackageAttachment struct { ContentType string `json:"content_type"` @@ -249,6 +205,10 @@ func ParsePackage(r io.Reader) (*Package, error) { meta.Homepage = "" } + if meta.Repository.Type != "git" { + meta.Repository.URL = "" + } + p := &Package{ Name: meta.Name, Version: v.String(), diff --git a/modules/packages/npm/creator_test.go b/modules/packages/npm/creator_test.go index 6264fd03f7820..a19c3ed40d41d 100644 --- a/modules/packages/npm/creator_test.go +++ b/modules/packages/npm/creator_test.go @@ -24,80 +24,6 @@ func TestMain(m *testing.M) { }) } -func TestGetGiteaRepository(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - t.Run("InvalidType", func(t *testing.T) { - r := Repository{Type: "svn", URL: ""} - repo, err := r.GetGiteaRepository() - - assert.Nil(t, repo) - assert.Error(t, err) - }) - - t.Run("InvalidPath", func(t *testing.T) { - r := Repository{Type: "git", URL: "something"} - repo, err := r.GetGiteaRepository() - - assert.Nil(t, repo) - assert.Error(t, err) - }) - - t.Run("ValidHttpURL", func(t *testing.T) { - test := func(t *testing.T, url string) { - r := Repository{Type: "git", URL: url} - repo, err := r.GetGiteaRepository() - - assert.NotNil(t, repo) - assert.NoError(t, err) - - assert.Equal(t, repo.ID, int64(2)) - assert.Equal(t, repo.OwnerID, int64(2)) - } - - test(t, "https://try.gitea.io/user2/repo2") - test(t, "https://try.gitea.io/user2/repo2.git") - }) - - t.Run("ValidGitSshURL", func(t *testing.T) { - test := func(t *testing.T, url string) { - r := Repository{Type: "git", URL: url} - repo, err := r.GetGiteaRepository() - - assert.NotNil(t, repo) - assert.NoError(t, err) - - assert.Equal(t, repo.ID, int64(2)) - assert.Equal(t, repo.OwnerID, int64(2)) - } - - test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2") - test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git") - - test(t, "git+ssh://try.gitea.io/user2/repo2") - test(t, "git+ssh://try.gitea.io/user2/repo2.git") - }) - - t.Run("ValidImplicitSshURL", func(t *testing.T) { - test := func(t *testing.T, url string) { - r := Repository{Type: "git", URL: url} - repo, err := r.GetGiteaRepository() - - assert.NotNil(t, repo) - assert.NoError(t, err) - - assert.Equal(t, repo.ID, int64(2)) - assert.Equal(t, repo.OwnerID, int64(2)) - } - - test(t, "sshuser@try.gitea.io:user2/repo2") - test(t, "sshuser@try.gitea.io:user2/repo2.git") - - test(t, "try.gitea.io:user2/repo2") - test(t, "try.gitea.io:user2/repo2.git") - }) -} - func TestParsePackage(t *testing.T) { packageScope := "@scope" packageName := "test-package" diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go index bae34de8d80fe..9b43fd6da1b17 100644 --- a/modules/packages/npm/metadata.go +++ b/modules/packages/npm/metadata.go @@ -14,7 +14,7 @@ type Metadata struct { Author string `json:"author,omitempty"` License string `json:"license,omitempty"` ProjectURL string `json:"project_url,omitempty"` - Repository Repository `json:"repository,omitempty"` + RepositoryURL string `json:"repository_url,omitempty"` Keywords []string `json:"keywords,omitempty"` Dependencies map[string]string `json:"dependencies,omitempty"` DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"` diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 830e26210c95e..2ce4ca507d40e 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -13,6 +13,9 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" + access_model "code.gitea.io/gitea/models/perm/access" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/context" packages_module "code.gitea.io/gitea/modules/packages" npm_module "code.gitea.io/gitea/modules/packages/npm" @@ -217,10 +220,17 @@ func UploadPackage(ctx *context.Context) { } } - repository, err := npmPackage.Metadata.Repository.GetGiteaRepository() + repo, err := repo_model.GetRepositoryByURL(npmPackage.Metadata.RepositoryURL) if err == nil { - if pv.CreatorID == repository.OwnerID { - if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repository.ID); err != nil { + flag := repo.OwnerID == ctx.Doer.ID + + if !flag { + perms, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) + flag = err == nil && perms.CanWrite(unit.TypePackages) + } + + if flag { + if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repo.ID); err != nil { ctx.ServerError("SetRepositoryLink", err) } } From 0a146985ad06a87e92744056bc9685ee51d22538 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Wed, 20 Jul 2022 00:21:05 +0200 Subject: [PATCH 04/12] Make gofmt happy --- models/repo/repo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go index 839d3a560f31d..1273266ff98cf 100644 --- a/models/repo/repo_test.go +++ b/models/repo/repo_test.go @@ -185,4 +185,4 @@ func TestGetRepositoryByURL(t *testing.T) { test(t, "try.gitea.io:user2/repo2") test(t, "try.gitea.io:user2/repo2.git") }) -} \ No newline at end of file +} From 6a491314f5253dea6bfcd5a75d91c248ed69a531 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Thu, 21 Jul 2022 04:22:30 +0200 Subject: [PATCH 05/12] Adding better errors; moving permission check before upload handling to avoid confusion --- routers/api/packages/npm/npm.go | 37 +++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 2ce4ca507d40e..86d008856d7d1 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -169,6 +169,26 @@ func UploadPackage(ctx *context.Context) { return } + repo, err := repo_model.GetRepositoryByURL(npmPackage.Metadata.RepositoryURL) + if err == nil { + flag := repo.OwnerID == ctx.Doer.ID + + if !flag { + perms, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + flag = perms.CanWrite(unit.TypePackages) + } + + if !flag { + apiError(ctx, http.StatusForbidden, "no permission to upload this package") + return + } + } + buf, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(npmPackage.Data), 32*1024*1024) if err != nil { apiError(ctx, http.StatusInternalServerError, err) @@ -220,19 +240,10 @@ func UploadPackage(ctx *context.Context) { } } - repo, err := repo_model.GetRepositoryByURL(npmPackage.Metadata.RepositoryURL) - if err == nil { - flag := repo.OwnerID == ctx.Doer.ID - - if !flag { - perms, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) - flag = err == nil && perms.CanWrite(unit.TypePackages) - } - - if flag { - if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repo.ID); err != nil { - ctx.ServerError("SetRepositoryLink", err) - } + if repo != nil { + if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repo.ID); err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return } } From 438679463e521b5a519d486e39008e892b1e2c4b Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Thu, 21 Jul 2022 04:23:10 +0200 Subject: [PATCH 06/12] Removing unnecessary test code --- modules/packages/npm/creator_test.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/modules/packages/npm/creator_test.go b/modules/packages/npm/creator_test.go index a19c3ed40d41d..168f950038d54 100644 --- a/modules/packages/npm/creator_test.go +++ b/modules/packages/npm/creator_test.go @@ -7,23 +7,14 @@ import ( "bytes" "encoding/base64" "fmt" - "path/filepath" "strings" "testing" - _ "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/json" "github.com/stretchr/testify/assert" ) -func TestMain(m *testing.M) { - unittest.MainTest(m, &unittest.TestOptions{ - GiteaRootPath: filepath.Join("..", "..", ".."), - }) -} - func TestParsePackage(t *testing.T) { packageScope := "@scope" packageName := "test-package" From 976d8d581841deb59e240f317c3dbdece41937bb Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Mon, 5 Sep 2022 21:24:30 +0200 Subject: [PATCH 07/12] Adding better comments & variable names; refactored anonymous function into a toplevel one --- models/repo/repo.go | 51 ++++++++++++++++++--------------- routers/api/packages/npm/npm.go | 8 +++--- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index 1b8f7aa0cb447..e65e57cf9640d 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -658,41 +658,46 @@ func GetRepositoryByName(ownerID int64, name string) (*Repository, error) { return repo, err } -func GetRepositoryByURL(repoURL string) (*Repository, error) { - // possible urls for git: - // https://my.domain/sub-path//.git - // git+ssh://user@my.domain//.git - // user@my.domain:/.git +// getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url +func getRepositoryURLPathSegments(repoURL string) []string { + if strings.HasPrefix(repoURL, setting.AppURL) { + return strings.Split(strings.Replace(repoURL, setting.AppURL, "", 1), "/") + } - retrievePathSegments := func(repoURL string) []string { - if strings.HasPrefix(repoURL, setting.AppURL) { - return strings.Split(strings.Replace(repoURL, setting.AppURL, "", 1), "/") - } + sshURLVariants := [4]string{ + setting.SSH.Domain + ":", + setting.SSH.User + "@" + setting.SSH.Domain + ":", + "git+ssh://" + setting.SSH.Domain + "/", + "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/", + } - sshURLVariants := [4]string{ - setting.SSH.Domain + ":", - setting.SSH.User + "@" + setting.SSH.Domain + ":", - "git+ssh://" + setting.SSH.Domain + "/", - "git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/", + for _, sshURL := range sshURLVariants { + if strings.HasPrefix(repoURL, sshURL) { + return strings.Split(strings.Replace(repoURL, sshURL, "", 1), "/") } + } - for _, sshURL := range sshURLVariants { - if strings.HasPrefix(repoURL, sshURL) { - return strings.Split(strings.Replace(repoURL, sshURL, "", 1), "/") - } - } + return []string{} +} - return []string{} - } +// GetRepositoryByURL returns the repository by given url +func GetRepositoryByURL(repoURL string) (*Repository, error) { + // possible urls for git: + // https://my.domain/sub-path//.git + // https://my.domain/sub-path// + // git+ssh://user@my.domain//.git + // git+ssh://user@my.domain// + // user@my.domain:/.git + // user@my.domain:/ - pathSegments := retrievePathSegments(repoURL) + pathSegments := getRepositoryURLPathSegments(repoURL) if len(pathSegments) < 2 { return nil, fmt.Errorf("unknown or malformed repository URL") } ownerName := pathSegments[0] - repoName := strings.Replace(pathSegments[1], ".git", "", 1) + repoName := strings.TrimSuffix(pathSegments[1], ".git") return GetRepositoryByOwnerAndName(ownerName, repoName) } diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 86d008856d7d1..cab9354310dbf 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -171,19 +171,19 @@ func UploadPackage(ctx *context.Context) { repo, err := repo_model.GetRepositoryByURL(npmPackage.Metadata.RepositoryURL) if err == nil { - flag := repo.OwnerID == ctx.Doer.ID + canWrite := repo.OwnerID == ctx.Doer.ID - if !flag { + if !canWrite { perms, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - flag = perms.CanWrite(unit.TypePackages) + canWrite = perms.CanWrite(unit.TypePackages) } - if !flag { + if !canWrite { apiError(ctx, http.StatusForbidden, "no permission to upload this package") return } From 382ffe34b7c4861fc7ca44c01a9773e06a6c2dd7 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Sat, 10 Sep 2022 17:54:34 +0200 Subject: [PATCH 08/12] Replacing some strings.Replace with strings.TrimSuffix; make cleaner default return for getRepositoryURLPathSegments() --- models/repo/repo.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index e65e57cf9640d..d0c7e3a32b887 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -661,7 +661,7 @@ func GetRepositoryByName(ownerID int64, name string) (*Repository, error) { // getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url func getRepositoryURLPathSegments(repoURL string) []string { if strings.HasPrefix(repoURL, setting.AppURL) { - return strings.Split(strings.Replace(repoURL, setting.AppURL, "", 1), "/") + return strings.Split(strings.TrimPrefix(repoURL, setting.AppURL), "/") } sshURLVariants := [4]string{ @@ -673,11 +673,11 @@ func getRepositoryURLPathSegments(repoURL string) []string { for _, sshURL := range sshURLVariants { if strings.HasPrefix(repoURL, sshURL) { - return strings.Split(strings.Replace(repoURL, sshURL, "", 1), "/") + return strings.Split(strings.TrimPrefix(repoURL, sshURL), "/") } } - return []string{} + return nil } // GetRepositoryByURL returns the repository by given url From 70bc17483a6f4c58a0a1835d477957857446ad9f Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Tue, 13 Sep 2022 15:12:50 +0200 Subject: [PATCH 09/12] Fixing length check for pathSegments in GetRepositoryByURL --- models/repo/repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index d0c7e3a32b887..20e0686bc90b2 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -692,7 +692,7 @@ func GetRepositoryByURL(repoURL string) (*Repository, error) { pathSegments := getRepositoryURLPathSegments(repoURL) - if len(pathSegments) < 2 { + if len(pathSegments) != 2 { return nil, fmt.Errorf("unknown or malformed repository URL") } From 275ac8ab73a158151758cc0e18b6bdd43ebac6ab Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Tue, 28 Mar 2023 11:02:43 +0200 Subject: [PATCH 10/12] Refactor GetRepositoryByURL to support ctx as first parameter --- models/repo/repo.go | 4 ++-- models/repo/repo_test.go | 8 ++++---- routers/api/packages/npm/npm.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index 20e0686bc90b2..3653dae015d6b 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -681,7 +681,7 @@ func getRepositoryURLPathSegments(repoURL string) []string { } // GetRepositoryByURL returns the repository by given url -func GetRepositoryByURL(repoURL string) (*Repository, error) { +func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error) { // possible urls for git: // https://my.domain/sub-path//.git // https://my.domain/sub-path// @@ -698,7 +698,7 @@ func GetRepositoryByURL(repoURL string) (*Repository, error) { ownerName := pathSegments[0] repoName := strings.TrimSuffix(pathSegments[1], ".git") - return GetRepositoryByOwnerAndName(ownerName, repoName) + return GetRepositoryByOwnerAndName(ctx, ownerName, repoName) } // GetRepositoryByID returns the repository by given id if exists. diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go index 1273266ff98cf..92a58ea3f9b21 100644 --- a/models/repo/repo_test.go +++ b/models/repo/repo_test.go @@ -129,7 +129,7 @@ func TestGetRepositoryByURL(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) t.Run("InvalidPath", func(t *testing.T) { - repo, err := repo_model.GetRepositoryByURL("something") + repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, "something") assert.Nil(t, repo) assert.Error(t, err) @@ -137,7 +137,7 @@ func TestGetRepositoryByURL(t *testing.T) { t.Run("ValidHttpURL", func(t *testing.T) { test := func(t *testing.T, url string) { - repo, err := repo_model.GetRepositoryByURL(url) + repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url) assert.NotNil(t, repo) assert.NoError(t, err) @@ -152,7 +152,7 @@ func TestGetRepositoryByURL(t *testing.T) { t.Run("ValidGitSshURL", func(t *testing.T) { test := func(t *testing.T, url string) { - repo, err := repo_model.GetRepositoryByURL(url) + repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url) assert.NotNil(t, repo) assert.NoError(t, err) @@ -170,7 +170,7 @@ func TestGetRepositoryByURL(t *testing.T) { t.Run("ValidImplicitSshURL", func(t *testing.T) { test := func(t *testing.T, url string) { - repo, err := repo_model.GetRepositoryByURL(url) + repo, err := repo_model.GetRepositoryByURL(db.DefaultContext, url) assert.NotNil(t, repo) assert.NoError(t, err) diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index cab9354310dbf..990782ab300ac 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -169,7 +169,7 @@ func UploadPackage(ctx *context.Context) { return } - repo, err := repo_model.GetRepositoryByURL(npmPackage.Metadata.RepositoryURL) + repo, err := repo_model.GetRepositoryByURL(ctx, npmPackage.Metadata.RepositoryURL) if err == nil { canWrite := repo.OwnerID == ctx.Doer.ID From af00cce93b166889cacdb4ce424b76dc07c47d32 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Tue, 28 Mar 2023 11:40:12 +0200 Subject: [PATCH 11/12] NPM Packages: change Metadata.RepositoryUrl to Metadata.Repository.URL --- modules/packages/npm/metadata.go | 1 - routers/api/packages/npm/npm.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go index 9b43fd6da1b17..77b77472a7a2c 100644 --- a/modules/packages/npm/metadata.go +++ b/modules/packages/npm/metadata.go @@ -14,7 +14,6 @@ type Metadata struct { Author string `json:"author,omitempty"` License string `json:"license,omitempty"` ProjectURL string `json:"project_url,omitempty"` - RepositoryURL string `json:"repository_url,omitempty"` Keywords []string `json:"keywords,omitempty"` Dependencies map[string]string `json:"dependencies,omitempty"` DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"` diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 990782ab300ac..51b34d3e2721e 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -169,7 +169,7 @@ func UploadPackage(ctx *context.Context) { return } - repo, err := repo_model.GetRepositoryByURL(ctx, npmPackage.Metadata.RepositoryURL) + repo, err := repo_model.GetRepositoryByURL(ctx, npmPackage.Metadata.Repository.URL) if err == nil { canWrite := repo.OwnerID == ctx.Doer.ID From bf25dc2e3b1718fa704f83d0885cec76c81f5214 Mon Sep 17 00:00:00 2001 From: Mai-Lapyst Date: Tue, 28 Mar 2023 11:41:11 +0200 Subject: [PATCH 12/12] NPM Packages: revert change that empties the repositories url in the metadata if the type isn't git --- modules/packages/npm/creator.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 5d2dab2ac3573..5e7e0e2983301 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -205,10 +205,6 @@ func ParsePackage(r io.Reader) (*Package, error) { meta.Homepage = "" } - if meta.Repository.Type != "git" { - meta.Repository.URL = "" - } - p := &Package{ Name: meta.Name, Version: v.String(),