mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Hide private repositories in packages (#19584)
This commit is contained in:
		| @@ -71,6 +71,44 @@ func TestPackageAPI(t *testing.T) { | |||||||
| 		assert.Equal(t, packageVersion, p.Version) | 		assert.Equal(t, packageVersion, p.Version) | ||||||
| 		assert.NotNil(t, p.Creator) | 		assert.NotNil(t, p.Creator) | ||||||
| 		assert.Equal(t, user.Name, p.Creator.UserName) | 		assert.Equal(t, user.Name, p.Creator.UserName) | ||||||
|  |  | ||||||
|  | 		t.Run("RepositoryLink", func(t *testing.T) { | ||||||
|  | 			defer PrintCurrentTest(t)() | ||||||
|  |  | ||||||
|  | 			p, err := packages_model.GetPackageByName(db.DefaultContext, user.ID, packages_model.TypeGeneric, packageName) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 			// no repository link | ||||||
|  | 			req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | ||||||
|  | 			resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
|  | 			var ap1 *api.Package | ||||||
|  | 			DecodeJSON(t, resp, &ap1) | ||||||
|  | 			assert.Nil(t, ap1.Repository) | ||||||
|  |  | ||||||
|  | 			// link to public repository | ||||||
|  | 			assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 1)) | ||||||
|  |  | ||||||
|  | 			req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | ||||||
|  | 			resp = MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
|  | 			var ap2 *api.Package | ||||||
|  | 			DecodeJSON(t, resp, &ap2) | ||||||
|  | 			assert.NotNil(t, ap2.Repository) | ||||||
|  | 			assert.EqualValues(t, 1, ap2.Repository.ID) | ||||||
|  |  | ||||||
|  | 			// link to private repository | ||||||
|  | 			assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 2)) | ||||||
|  |  | ||||||
|  | 			req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) | ||||||
|  | 			resp = MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
|  | 			var ap3 *api.Package | ||||||
|  | 			DecodeJSON(t, resp, &ap3) | ||||||
|  | 			assert.Nil(t, ap3.Repository) | ||||||
|  |  | ||||||
|  | 			assert.NoError(t, packages_model.UnlinkRepositoryFromAllPackages(db.DefaultContext, 2)) | ||||||
|  | 		}) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	t.Run("ListPackageFiles", func(t *testing.T) { | 	t.Run("ListPackageFiles", func(t *testing.T) { | ||||||
|   | |||||||
| @@ -5,28 +5,38 @@ | |||||||
| package convert | package convert | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/packages" | 	"code.gitea.io/gitea/models/packages" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // ToPackage convert a packages.PackageDescriptor to api.Package | // ToPackage convert a packages.PackageDescriptor to api.Package | ||||||
| func ToPackage(pd *packages.PackageDescriptor) *api.Package { | func ToPackage(ctx context.Context, pd *packages.PackageDescriptor, doer *user_model.User) (*api.Package, error) { | ||||||
| 	var repo *api.Repository | 	var repo *api.Repository | ||||||
| 	if pd.Repository != nil { | 	if pd.Repository != nil { | ||||||
| 		repo = ToRepo(pd.Repository, perm.AccessModeNone) | 		permission, err := models.GetUserRepoPermission(ctx, pd.Repository, doer) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if permission.HasAccess() { | ||||||
|  | 			repo = ToRepo(pd.Repository, permission.AccessMode) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return &api.Package{ | 	return &api.Package{ | ||||||
| 		ID:         pd.Version.ID, | 		ID:         pd.Version.ID, | ||||||
| 		Owner:      ToUser(pd.Owner, nil), | 		Owner:      ToUser(pd.Owner, doer), | ||||||
| 		Repository: repo, | 		Repository: repo, | ||||||
| 		Creator:    ToUser(pd.Creator, nil), | 		Creator:    ToUser(pd.Creator, doer), | ||||||
| 		Type:       string(pd.Package.Type), | 		Type:       string(pd.Package.Type), | ||||||
| 		Name:       pd.Package.Name, | 		Name:       pd.Package.Name, | ||||||
| 		Version:    pd.Version.Version, | 		Version:    pd.Version.Version, | ||||||
| 		CreatedAt:  pd.Version.CreatedUnix.AsTime(), | 		CreatedAt:  pd.Version.CreatedUnix.AsTime(), | ||||||
| 	} | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // ToPackageFile converts packages.PackageFileDescriptor to api.PackageFile | // ToPackageFile converts packages.PackageFileDescriptor to api.PackageFile | ||||||
|   | |||||||
| @@ -872,16 +872,18 @@ func notifyPackage(sender *user_model.User, pd *packages_model.PackageDescriptor | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	org := pd.Owner | 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("webhook.notifyPackage Package: %s[%d]", pd.Package.Name, pd.Package.ID)) | ||||||
| 	if !org.IsOrganization() { | 	defer finished() | ||||||
| 		org = nil |  | ||||||
|  | 	apiPackage, err := convert.ToPackage(ctx, pd, sender) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("Error converting package: %v", err) | ||||||
|  | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := webhook_services.PrepareWebhooks(pd.Repository, webhook.HookEventPackage, &api.PackagePayload{ | 	if err := webhook_services.PrepareWebhooks(pd.Repository, webhook.HookEventPackage, &api.PackagePayload{ | ||||||
| 		Action:  action, | 		Action:  action, | ||||||
| 		Repository:   convert.ToRepo(pd.Repository, perm.AccessModeNone), | 		Package: apiPackage, | ||||||
| 		Package:      convert.ToPackage(pd), |  | ||||||
| 		Organization: convert.ToUser(org, nil), |  | ||||||
| 		Sender:  convert.ToUser(sender, nil), | 		Sender:  convert.ToUser(sender, nil), | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		log.Error("PrepareWebhooks: %v", err) | 		log.Error("PrepareWebhooks: %v", err) | ||||||
|   | |||||||
| @@ -73,7 +73,12 @@ func ListPackages(ctx *context.APIContext) { | |||||||
|  |  | ||||||
| 	apiPackages := make([]*api.Package, 0, len(pds)) | 	apiPackages := make([]*api.Package, 0, len(pds)) | ||||||
| 	for _, pd := range pds { | 	for _, pd := range pds { | ||||||
| 		apiPackages = append(apiPackages, convert.ToPackage(pd)) | 		apiPackage, err := convert.ToPackage(ctx, pd, ctx.Doer) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.Error(http.StatusInternalServerError, "Error converting package for api", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		apiPackages = append(apiPackages, apiPackage) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.SetLinkHeader(int(count), listOptions.PageSize) | 	ctx.SetLinkHeader(int(count), listOptions.PageSize) | ||||||
| @@ -115,7 +120,13 @@ func GetPackage(ctx *context.APIContext) { | |||||||
| 	//   "404": | 	//   "404": | ||||||
| 	//     "$ref": "#/responses/notFound" | 	//     "$ref": "#/responses/notFound" | ||||||
|  |  | ||||||
| 	ctx.JSON(http.StatusOK, convert.ToPackage(ctx.Package.Descriptor)) | 	apiPackage, err := convert.ToPackage(ctx, ctx.Package.Descriptor, ctx.Doer) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Error(http.StatusInternalServerError, "Error converting package for api", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.JSON(http.StatusOK, apiPackage) | ||||||
| } | } | ||||||
|  |  | ||||||
| // DeletePackage deletes a package | // DeletePackage deletes a package | ||||||
|   | |||||||
| @@ -62,6 +62,7 @@ func Packages(ctx *context.Context) { | |||||||
| 	ctx.Data["HasPackages"] = hasPackages | 	ctx.Data["HasPackages"] = hasPackages | ||||||
| 	ctx.Data["PackageDescriptors"] = pds | 	ctx.Data["PackageDescriptors"] = pds | ||||||
| 	ctx.Data["Total"] = total | 	ctx.Data["Total"] = total | ||||||
|  | 	ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository | ||||||
|  |  | ||||||
| 	pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) | 	pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) | ||||||
| 	pager.AddParam(ctx, "q", "Query") | 	pager.AddParam(ctx, "q", "Query") | ||||||
|   | |||||||
| @@ -58,6 +58,23 @@ func ListPackages(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	repositoryAccessMap := make(map[int64]bool) | ||||||
|  | 	for _, pd := range pds { | ||||||
|  | 		if pd.Repository == nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if _, has := repositoryAccessMap[pd.Repository.ID]; has { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		permission, err := models.GetUserRepoPermission(ctx, pd.Repository, ctx.Doer) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("GetUserRepoPermission", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		repositoryAccessMap[pd.Repository.ID] = permission.HasAccess() | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	hasPackages, err := packages_model.HasOwnerPackages(ctx, ctx.ContextUser.ID) | 	hasPackages, err := packages_model.HasOwnerPackages(ctx, ctx.ContextUser.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("HasOwnerPackages", err) | 		ctx.ServerError("HasOwnerPackages", err) | ||||||
| @@ -72,6 +89,7 @@ func ListPackages(ctx *context.Context) { | |||||||
| 	ctx.Data["HasPackages"] = hasPackages | 	ctx.Data["HasPackages"] = hasPackages | ||||||
| 	ctx.Data["PackageDescriptors"] = pds | 	ctx.Data["PackageDescriptors"] = pds | ||||||
| 	ctx.Data["Total"] = total | 	ctx.Data["Total"] = total | ||||||
|  | 	ctx.Data["RepositoryAccessMap"] = repositoryAccessMap | ||||||
|  |  | ||||||
| 	pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) | 	pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) | ||||||
| 	pager.AddParam(ctx, "q", "Query") | 	pager.AddParam(ctx, "q", "Query") | ||||||
| @@ -157,6 +175,17 @@ func ViewPackageVersion(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin() | 	ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin() | ||||||
|  |  | ||||||
|  | 	hasRepositoryAccess := false | ||||||
|  | 	if pd.Repository != nil { | ||||||
|  | 		permission, err := models.GetUserRepoPermission(ctx, pd.Repository, ctx.Doer) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("GetUserRepoPermission", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		hasRepositoryAccess = permission.HasAccess() | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess | ||||||
|  |  | ||||||
| 	ctx.HTML(http.StatusOK, tplPackagesView) | 	ctx.HTML(http.StatusOK, tplPackagesView) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -30,7 +30,11 @@ | |||||||
| 					</div> | 					</div> | ||||||
| 					<div class="desc issue-item-bottom-row df ac fw my-1"> | 					<div class="desc issue-item-bottom-row df ac fw my-1"> | ||||||
| 						{{$timeStr := TimeSinceUnix .Version.CreatedUnix $.i18n.Lang}} | 						{{$timeStr := TimeSinceUnix .Version.CreatedUnix $.i18n.Lang}} | ||||||
|  | 						{{$hasRepositoryAccess := false}} | ||||||
| 						{{if .Repository}} | 						{{if .Repository}} | ||||||
|  | 							{{$hasRepositoryAccess = index $.RepositoryAccessMap .Repository.ID}} | ||||||
|  | 						{{end}} | ||||||
|  | 						{{if $hasRepositoryAccess}} | ||||||
| 							{{$.i18n.Tr "packages.published_by_in" $timeStr .Creator.HomeLink (.Creator.GetDisplayName | Escape) .Repository.HTMLURL (.Repository.FullName | Escape) | Safe}} | 							{{$.i18n.Tr "packages.published_by_in" $timeStr .Creator.HomeLink (.Creator.GetDisplayName | Escape) .Repository.HTMLURL (.Repository.FullName | Escape) | Safe}} | ||||||
| 						{{else}} | 						{{else}} | ||||||
| 							{{$.i18n.Tr "packages.published_by" $timeStr .Creator.HomeLink (.Creator.GetDisplayName | Escape) | Safe}} | 							{{$.i18n.Tr "packages.published_by" $timeStr .Creator.HomeLink (.Creator.GetDisplayName | Escape) | Safe}} | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ | |||||||
| 					</div> | 					</div> | ||||||
| 					<div> | 					<div> | ||||||
| 						{{$timeStr := TimeSinceUnix .PackageDescriptor.Version.CreatedUnix $.i18n.Lang}} | 						{{$timeStr := TimeSinceUnix .PackageDescriptor.Version.CreatedUnix $.i18n.Lang}} | ||||||
| 						{{if .PackageDescriptor.Repository}} | 						{{if .HasRepositoryAccess}} | ||||||
| 							{{.i18n.Tr "packages.published_by_in" $timeStr .PackageDescriptor.Creator.HomeLink (.PackageDescriptor.Creator.GetDisplayName | Escape) .PackageDescriptor.Repository.HTMLURL (.PackageDescriptor.Repository.FullName | Escape) | Safe}} | 							{{.i18n.Tr "packages.published_by_in" $timeStr .PackageDescriptor.Creator.HomeLink (.PackageDescriptor.Creator.GetDisplayName | Escape) .PackageDescriptor.Repository.HTMLURL (.PackageDescriptor.Repository.FullName | Escape) | Safe}} | ||||||
| 						{{else}} | 						{{else}} | ||||||
| 							{{.i18n.Tr "packages.published_by" $timeStr .PackageDescriptor.Creator.HomeLink (.PackageDescriptor.Creator.GetDisplayName | Escape) | Safe}} | 							{{.i18n.Tr "packages.published_by" $timeStr .PackageDescriptor.Creator.HomeLink (.PackageDescriptor.Creator.GetDisplayName | Escape) | Safe}} | ||||||
| @@ -35,7 +35,7 @@ | |||||||
| 						<strong>{{.i18n.Tr "packages.details"}}</strong> | 						<strong>{{.i18n.Tr "packages.details"}}</strong> | ||||||
| 						<div class="ui relaxed list"> | 						<div class="ui relaxed list"> | ||||||
| 							<div class="item">{{svg .PackageDescriptor.Package.Type.SVGName 16 "mr-3"}} {{.PackageDescriptor.Package.Type.Name}}</div> | 							<div class="item">{{svg .PackageDescriptor.Package.Type.SVGName 16 "mr-3"}} {{.PackageDescriptor.Package.Type.Name}}</div> | ||||||
| 							{{if .PackageDescriptor.Repository}} | 							{{if .HasRepositoryAccess}} | ||||||
| 							<div class="item">{{svg "octicon-repo" 16 "mr-3"}} <a href="{{.PackageDescriptor.Repository.HTMLURL}}">{{.PackageDescriptor.Repository.FullName}}</a></div> | 							<div class="item">{{svg "octicon-repo" 16 "mr-3"}} <a href="{{.PackageDescriptor.Repository.HTMLURL}}">{{.PackageDescriptor.Repository.FullName}}</a></div> | ||||||
| 							{{end}} | 							{{end}} | ||||||
| 							<div class="item">{{svg "octicon-calendar" 16 "mr-3"}} {{.PackageDescriptor.Version.CreatedUnix.FormatDate}}</div> | 							<div class="item">{{svg "octicon-calendar" 16 "mr-3"}} {{.PackageDescriptor.Version.CreatedUnix.FormatDate}}</div> | ||||||
| @@ -76,10 +76,10 @@ | |||||||
| 							{{end}} | 							{{end}} | ||||||
| 							</div> | 							</div> | ||||||
| 						{{end}} | 						{{end}} | ||||||
| 						{{if or .CanWritePackages .PackageDescriptor.Repository}} | 						{{if or .CanWritePackages .HasRepositoryAccess}} | ||||||
| 							<div class="ui divider"></div> | 							<div class="ui divider"></div> | ||||||
| 							<div class="ui relaxed list"> | 							<div class="ui relaxed list"> | ||||||
| 								{{if .PackageDescriptor.Repository}} | 								{{if .HasRepositoryAccess}} | ||||||
| 								<div class="item">{{svg "octicon-issue-opened" 16 "mr-3"}} <a href="{{.PackageDescriptor.Repository.HTMLURL}}/issues">{{.i18n.Tr "repo.issues"}}</a></div> | 								<div class="item">{{svg "octicon-issue-opened" 16 "mr-3"}} <a href="{{.PackageDescriptor.Repository.HTMLURL}}/issues">{{.i18n.Tr "repo.issues"}}</a></div> | ||||||
| 								{{end}} | 								{{end}} | ||||||
| 								{{if .CanWritePackages}} | 								{{if .CanWritePackages}} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user