diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go index a3ea2c2f9a..5c3fcb93c5 100644 --- a/routers/api/packages/composer/api.go +++ b/routers/api/packages/composer/api.go @@ -9,7 +9,9 @@ import ( "time" packages_model "code.gitea.io/gitea/models/packages" + access_model "code.gitea.io/gitea/models/perm/access" composer_module "code.gitea.io/gitea/modules/packages/composer" + "code.gitea.io/gitea/services/context" ) // ServiceIndexResponse contains registry endpoints @@ -91,7 +93,7 @@ type Source struct { Reference string `json:"reference"` } -func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *PackageMetadataResponse { +func createPackageMetadataResponse(ctx *context.Context, registryURL string, pds []*packages_model.PackageDescriptor) (*PackageMetadataResponse, error) { versions := make([]*PackageVersionMetadata, 0, len(pds)) for _, pd := range pds { @@ -116,10 +118,17 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac }, } if pd.Repository != nil { - pkg.Source = Source{ - URL: pd.Repository.HTMLURL(), - Type: "git", - Reference: pd.Version.Version, + permission, err := access_model.GetDoerRepoPermission(ctx, pd.Repository, ctx.Doer) + if err != nil { + return nil, err + } + + if permission.HasAnyUnitAccess() { + pkg.Source = Source{ + URL: pd.Repository.HTMLURL(), + Type: "git", + Reference: pd.Version.Version, + } } } @@ -131,5 +140,5 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac Packages: map[string][]*PackageVersionMetadata{ pds[0].Package.Name: versions, }, - } + }, nil } diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go index 8eb66ca244..2b124a25a4 100644 --- a/routers/api/packages/composer/composer.go +++ b/routers/api/packages/composer/composer.go @@ -145,10 +145,15 @@ func PackageMetadata(ctx *context.Context) { return } - resp := createPackageMetadataResponse( + resp, err := createPackageMetadataResponse( + ctx, setting.AppURL+"api/packages/"+ctx.Package.Owner.Name+"/composer", pds, ) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } ctx.JSON(http.StatusOK, resp) } diff --git a/templates/package/settings.tmpl b/templates/package/settings.tmpl index d4241e6541..d5577a8974 100644 --- a/templates/package/settings.tmpl +++ b/templates/package/settings.tmpl @@ -10,7 +10,13 @@ {{template "user/overview/header" .}} {{end}} {{template "base/alert" .}} -

{{.PackageDescriptor.Package.Name}} / {{ctx.Locale.Tr "repo.settings"}}

+

+ {{.PackageDescriptor.Package.Name}} + + {{template "package/shared/visibility_badge" dict "Package" .PackageDescriptor.Package "Owner" .PackageDescriptor.Owner}} + + / {{ctx.Locale.Tr "repo.settings"}} +

{{ctx.Locale.Tr "packages.settings.link"}}

diff --git a/templates/package/shared/cleanup_rules/preview.tmpl b/templates/package/shared/cleanup_rules/preview.tmpl index 0991a07fbc..15ad94debd 100644 --- a/templates/package/shared/cleanup_rules/preview.tmpl +++ b/templates/package/shared/cleanup_rules/preview.tmpl @@ -18,7 +18,12 @@ {{range .VersionsToRemove}} {{.Package.Type.Name}} - {{.Package.Name}} + + {{.Package.Name}} + + {{template "package/shared/visibility_badge" dict "Package" .Package "Owner" .Owner}} + + {{.Version.Version}} {{.Creator.Name}} {{FileSize .CalculateBlobSize}} diff --git a/templates/package/shared/list.tmpl b/templates/package/shared/list.tmpl index 54e2a2c20e..69bd7acbe8 100644 --- a/templates/package/shared/list.tmpl +++ b/templates/package/shared/list.tmpl @@ -21,7 +21,10 @@
{{.Package.Name}} - {{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}} + + {{template "package/shared/visibility_badge" dict "Package" .Package "Owner" .Owner}} + {{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}} +
{{$timeStr := DateUtils.TimeSince .Version.CreatedUnix}} diff --git a/templates/package/shared/versionlist.tmpl b/templates/package/shared/versionlist.tmpl index 3ef9e790f1..a381f324fa 100644 --- a/templates/package/shared/versionlist.tmpl +++ b/templates/package/shared/versionlist.tmpl @@ -1,4 +1,10 @@ -

{{.PackageDescriptor.Package.Name}} / {{ctx.Locale.Tr "packages.versions"}}

+

+ {{.PackageDescriptor.Package.Name}} + + {{template "package/shared/visibility_badge" dict "Package" .PackageDescriptor.Package "Owner" .PackageDescriptor.Owner}} + + / {{ctx.Locale.Tr "packages.versions"}} +

{{template "shared/search/input" dict "Value" .Query "Placeholder" (ctx.Locale.Tr "search.package_kind")}} diff --git a/templates/package/shared/view.tmpl b/templates/package/shared/view.tmpl index 9e32d5fdc2..5bd093178b 100644 --- a/templates/package/shared/view.tmpl +++ b/templates/package/shared/view.tmpl @@ -1,6 +1,11 @@
{{$packageVersionLink := print $.PackageDescriptor.PackageWebLink "/" (PathEscape .PackageDescriptor.Version.LowerVersion)}} -

{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})

+
+

{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})

+ + {{template "package/shared/visibility_badge" dict "Package" .PackageDescriptor.Package "Owner" .PackageDescriptor.Owner}} + +
{{$timeStr := DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}} {{if .HasRepositoryAccess}} diff --git a/templates/package/shared/visibility_badge.tmpl b/templates/package/shared/visibility_badge.tmpl new file mode 100644 index 0000000000..75502bd4b0 --- /dev/null +++ b/templates/package/shared/visibility_badge.tmpl @@ -0,0 +1,7 @@ +{{if .Package.IsInternal}} + {{ctx.Locale.Tr "repo.desc.private"}} +{{else if .Owner.Visibility.IsPrivate}} + {{ctx.Locale.Tr "repo.desc.private"}} +{{else if .Owner.Visibility.IsLimited}} + {{ctx.Locale.Tr "repo.desc.internal"}} +{{end}} diff --git a/tests/integration/api_packages_composer_test.go b/tests/integration/api_packages_composer_test.go index a56f2dd39b..963bdba256 100644 --- a/tests/integration/api_packages_composer_test.go +++ b/tests/integration/api_packages_composer_test.go @@ -27,6 +27,7 @@ func TestPackageComposer(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + otherUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) vendorName := "gitea" projectName := "composer-package" @@ -243,5 +244,49 @@ func TestPackageComposer(t *testing.T) { assert.Equal(t, repo1.HTMLURL(), pkgs[0].Source.URL) assert.Equal(t, "git", pkgs[0].Source.Type) assert.Equal(t, packageVersion, pkgs[0].Source.Reference) + + // Private repository links remain visible to callers who can access the repository. + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + err = packages.SetRepositoryLink(t.Context(), userPkgs[0].ID, repo2.ID) + assert.NoError(t, err) + + req = NewRequest(t, "GET", fmt.Sprintf("%s/p2/%s/%s.json", url, vendorName, projectName)). + AddBasicAuth(user.Name) + resp = MakeRequest(t, req, http.StatusOK) + + result = DecodeJSON(t, resp, &composer.PackageMetadataResponse{}) + pkgs = result.Packages[packageName] + assert.Len(t, pkgs, 1) + assert.Equal(t, repo2.HTMLURL(), pkgs[0].Source.URL) + assert.Equal(t, "git", pkgs[0].Source.Type) + assert.Equal(t, packageVersion, pkgs[0].Source.Reference) + + // Callers without repository access still get the package metadata, but not the private source URL. + req = NewRequest(t, "GET", fmt.Sprintf("%s/p2/%s/%s.json", url, vendorName, projectName)). + AddBasicAuth(otherUser.Name) + resp = MakeRequest(t, req, http.StatusOK) + + result = DecodeJSON(t, resp, &composer.PackageMetadataResponse{}) + pkgs = result.Packages[packageName] + assert.Len(t, pkgs, 1) + assert.Empty(t, pkgs[0].Source.URL) + assert.Empty(t, pkgs[0].Source.Type) + assert.Empty(t, pkgs[0].Source.Reference) + }) + + t.Run("WebVisibilityBadge", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + listReq := NewRequest(t, "GET", fmt.Sprintf("/%s/-/packages", user.Name)). + AddBasicAuth(user.Name) + listResp := MakeRequest(t, listReq, http.StatusOK) + listDoc := NewHTMLParser(t, bytes.NewReader(listResp.Body.Bytes())) + assert.Equal(t, 0, listDoc.Find(".item-title .ui.basic.label").Length()) + + viewReq := NewRequest(t, "GET", fmt.Sprintf("/%s/-/packages/composer/%s/%s", user.Name, neturl.PathEscape(packageName), neturl.PathEscape(packageVersion))). + AddBasicAuth(user.Name) + viewResp := MakeRequest(t, viewReq, http.StatusOK) + viewDoc := NewHTMLParser(t, bytes.NewReader(viewResp.Body.Bytes())) + assert.Equal(t, 0, viewDoc.Find(".issue-title-header .ui.basic.label").Length()) }) }