mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 00:20:25 +08:00 
			
		
		
		
	Add Docker /v2/_catalog endpoint (#20469)
* Added properties for packages. * Fixed authenticate header format. * Added _catalog endpoint. * Check owner visibility. * Extracted condition. * Added test for _catalog. Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		@@ -398,6 +398,8 @@ var migrations = []Migration{
 | 
			
		||||
	NewMigration("Improve Action table indices v2", improveActionTableIndices),
 | 
			
		||||
	// v219 -> v220
 | 
			
		||||
	NewMigration("Add sync_on_commit column to push_mirror table", addSyncOnCommitColForPushMirror),
 | 
			
		||||
	// v220 -> v221
 | 
			
		||||
	NewMigration("Add container repository property", addContainerRepositoryProperty),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCurrentDBVersion returns the current db version
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								models/migrations/v220.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								models/migrations/v220.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
// Copyright 2022 The Gitea Authors. All rights reserved.
 | 
			
		||||
// Use of this source code is governed by a MIT-style
 | 
			
		||||
// license that can be found in the LICENSE file.
 | 
			
		||||
 | 
			
		||||
package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	packages_model "code.gitea.io/gitea/models/packages"
 | 
			
		||||
	container_module "code.gitea.io/gitea/modules/packages/container"
 | 
			
		||||
 | 
			
		||||
	"xorm.io/xorm"
 | 
			
		||||
	"xorm.io/xorm/schemas"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func addContainerRepositoryProperty(x *xorm.Engine) error {
 | 
			
		||||
	switch x.Dialect().URI().DBType {
 | 
			
		||||
	case schemas.SQLITE:
 | 
			
		||||
		_, err := x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name || '/' || p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		_, err := x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, CONCAT(u.lower_name, '/', p.lower_name) FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -12,6 +12,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models/db"
 | 
			
		||||
	"code.gitea.io/gitea/models/packages"
 | 
			
		||||
	user_model "code.gitea.io/gitea/models/user"
 | 
			
		||||
	container_module "code.gitea.io/gitea/modules/packages/container"
 | 
			
		||||
 | 
			
		||||
	"xorm.io/builder"
 | 
			
		||||
@@ -210,6 +211,7 @@ func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*pack
 | 
			
		||||
	return pvs, count, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SearchExpiredUploadedBlobs gets all uploaded blobs which are older than specified
 | 
			
		||||
func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) {
 | 
			
		||||
	var cond builder.Cond = builder.Eq{
 | 
			
		||||
		"package_version.is_internal":   true,
 | 
			
		||||
@@ -225,3 +227,37 @@ func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([
 | 
			
		||||
		Where(cond).
 | 
			
		||||
		Find(&pfs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetRepositories gets a sorted list of all repositories
 | 
			
		||||
func GetRepositories(ctx context.Context, actor *user_model.User, n int, last string) ([]string, error) {
 | 
			
		||||
	var cond builder.Cond = builder.Eq{
 | 
			
		||||
		"package.type":              packages.TypeContainer,
 | 
			
		||||
		"package_property.ref_type": packages.PropertyTypePackage,
 | 
			
		||||
		"package_property.name":     container_module.PropertyRepository,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cond = cond.And(builder.Exists(
 | 
			
		||||
		builder.
 | 
			
		||||
			Select("package_version.id").
 | 
			
		||||
			Where(builder.Eq{"package_version.is_internal": false}.And(builder.Expr("package.id = package_version.package_id"))).
 | 
			
		||||
			From("package_version"),
 | 
			
		||||
	))
 | 
			
		||||
 | 
			
		||||
	if last != "" {
 | 
			
		||||
		cond = cond.And(builder.Gt{"package_property.value": strings.ToLower(last)})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cond = cond.And(user_model.BuildCanSeeUserCondition(actor))
 | 
			
		||||
 | 
			
		||||
	sess := db.GetEngine(ctx).
 | 
			
		||||
		Table("package").
 | 
			
		||||
		Select("package_property.value").
 | 
			
		||||
		Join("INNER", "user", "`user`.id = package.owner_id").
 | 
			
		||||
		Join("INNER", "package_property", "package_property.ref_id = package.id").
 | 
			
		||||
		Where(cond).
 | 
			
		||||
		Asc("package_property.value").
 | 
			
		||||
		Limit(n)
 | 
			
		||||
 | 
			
		||||
	repositories := make([]string, 0, n)
 | 
			
		||||
	return repositories, sess.Find(&repositories)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,15 +40,16 @@ func (l PackagePropertyList) GetByName(name string) string {
 | 
			
		||||
 | 
			
		||||
// PackageDescriptor describes a package
 | 
			
		||||
type PackageDescriptor struct {
 | 
			
		||||
	Package    *Package
 | 
			
		||||
	Owner      *user_model.User
 | 
			
		||||
	Repository *repo_model.Repository
 | 
			
		||||
	Version    *PackageVersion
 | 
			
		||||
	SemVer     *version.Version
 | 
			
		||||
	Creator    *user_model.User
 | 
			
		||||
	Properties PackagePropertyList
 | 
			
		||||
	Metadata   interface{}
 | 
			
		||||
	Files      []*PackageFileDescriptor
 | 
			
		||||
	Package           *Package
 | 
			
		||||
	Owner             *user_model.User
 | 
			
		||||
	Repository        *repo_model.Repository
 | 
			
		||||
	Version           *PackageVersion
 | 
			
		||||
	SemVer            *version.Version
 | 
			
		||||
	Creator           *user_model.User
 | 
			
		||||
	PackageProperties PackagePropertyList
 | 
			
		||||
	VersionProperties PackagePropertyList
 | 
			
		||||
	Metadata          interface{}
 | 
			
		||||
	Files             []*PackageFileDescriptor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageFileDescriptor describes a package file
 | 
			
		||||
@@ -102,6 +103,10 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	pps, err := GetProperties(ctx, PropertyTypePackage, p.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pvps, err := GetProperties(ctx, PropertyTypeVersion, pv.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -152,15 +157,16 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &PackageDescriptor{
 | 
			
		||||
		Package:    p,
 | 
			
		||||
		Owner:      o,
 | 
			
		||||
		Repository: repository,
 | 
			
		||||
		Version:    pv,
 | 
			
		||||
		SemVer:     semVer,
 | 
			
		||||
		Creator:    creator,
 | 
			
		||||
		Properties: PackagePropertyList(pvps),
 | 
			
		||||
		Metadata:   metadata,
 | 
			
		||||
		Files:      pfds,
 | 
			
		||||
		Package:           p,
 | 
			
		||||
		Owner:             o,
 | 
			
		||||
		Repository:        repository,
 | 
			
		||||
		Version:           pv,
 | 
			
		||||
		SemVer:            semVer,
 | 
			
		||||
		Creator:           creator,
 | 
			
		||||
		PackageProperties: PackagePropertyList(pps),
 | 
			
		||||
		VersionProperties: PackagePropertyList(pvps),
 | 
			
		||||
		Metadata:          metadata,
 | 
			
		||||
		Files:             pfds,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -131,6 +131,12 @@ func TryInsertPackage(ctx context.Context, p *Package) (*Package, error) {
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeletePackageByID deletes a package by id
 | 
			
		||||
func DeletePackageByID(ctx context.Context, packageID int64) error {
 | 
			
		||||
	_, err := db.GetEngine(ctx).ID(packageID).Delete(&Package{})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetRepositoryLink sets the linked repository
 | 
			
		||||
func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error {
 | 
			
		||||
	_, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: repoID})
 | 
			
		||||
@@ -192,21 +198,20 @@ func GetPackagesByType(ctx context.Context, ownerID int64, packageType Type) ([]
 | 
			
		||||
		Find(&ps)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeletePackagesIfUnreferenced deletes a package if there are no associated versions
 | 
			
		||||
func DeletePackagesIfUnreferenced(ctx context.Context) error {
 | 
			
		||||
// FindUnreferencedPackages gets all packages without associated versions
 | 
			
		||||
func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) {
 | 
			
		||||
	in := builder.
 | 
			
		||||
		Select("package.id").
 | 
			
		||||
		From("package").
 | 
			
		||||
		LeftJoin("package_version", "package_version.package_id = package.id").
 | 
			
		||||
		Where(builder.Expr("package_version.id IS NULL"))
 | 
			
		||||
 | 
			
		||||
	_, err := db.GetEngine(ctx).
 | 
			
		||||
	ps := make([]*Package, 0, 10)
 | 
			
		||||
	return ps, db.GetEngine(ctx).
 | 
			
		||||
		// double select workaround for MySQL
 | 
			
		||||
		// https://stackoverflow.com/questions/4471277/mysql-delete-from-with-subquery-as-condition
 | 
			
		||||
		Where(builder.In("package.id", builder.Select("id").From(in, "temp"))).
 | 
			
		||||
		Delete(&Package{})
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
		Find(&ps)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasOwnerPackages tests if a user/org has packages
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,11 @@ const (
 | 
			
		||||
	PropertyTypeVersion PropertyType = iota // 0
 | 
			
		||||
	// PropertyTypeFile means the reference is a package file
 | 
			
		||||
	PropertyTypeFile // 1
 | 
			
		||||
	// PropertyTypePackage means the reference is a package
 | 
			
		||||
	PropertyTypePackage // 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PackageProperty represents a property of a package version or file
 | 
			
		||||
// PackageProperty represents a property of a package, version or file
 | 
			
		||||
type PackageProperty struct {
 | 
			
		||||
	ID      int64        `xorm:"pk autoincr"`
 | 
			
		||||
	RefType PropertyType `xorm:"INDEX NOT NULL"`
 | 
			
		||||
@@ -68,3 +70,9 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
 | 
			
		||||
	_, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeletePropertyByName deletes properties by name
 | 
			
		||||
func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
 | 
			
		||||
	_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -58,24 +58,7 @@ func (opts *SearchUserOptions) toSearchQueryBase() *xorm.Session {
 | 
			
		||||
		cond = cond.And(builder.In("visibility", opts.Visible))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.Actor != nil {
 | 
			
		||||
		// If Admin - they see all users!
 | 
			
		||||
		if !opts.Actor.IsAdmin {
 | 
			
		||||
			// Users can see an organization they are a member of
 | 
			
		||||
			accessCond := builder.In("id", builder.Select("org_id").From("org_user").Where(builder.Eq{"uid": opts.Actor.ID}))
 | 
			
		||||
			if !opts.Actor.IsRestricted {
 | 
			
		||||
				// Not-Restricted users can see public and limited users/organizations
 | 
			
		||||
				accessCond = accessCond.Or(builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
 | 
			
		||||
			}
 | 
			
		||||
			// Don't forget about self
 | 
			
		||||
			accessCond = accessCond.Or(builder.Eq{"id": opts.Actor.ID})
 | 
			
		||||
			cond = cond.And(accessCond)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		// Force visibility for privacy
 | 
			
		||||
		// Not logged in - only public users
 | 
			
		||||
		cond = cond.And(builder.In("visibility", structs.VisibleTypePublic))
 | 
			
		||||
	}
 | 
			
		||||
	cond = cond.And(BuildCanSeeUserCondition(opts.Actor))
 | 
			
		||||
 | 
			
		||||
	if opts.UID > 0 {
 | 
			
		||||
		cond = cond.And(builder.Eq{"id": opts.UID})
 | 
			
		||||
@@ -163,3 +146,26 @@ func IterateUser(f func(user *User) error) error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildCanSeeUserCondition creates a condition which can be used to restrict results to users/orgs the actor can see
 | 
			
		||||
func BuildCanSeeUserCondition(actor *User) builder.Cond {
 | 
			
		||||
	if actor != nil {
 | 
			
		||||
		// If Admin - they see all users!
 | 
			
		||||
		if !actor.IsAdmin {
 | 
			
		||||
			// Users can see an organization they are a member of
 | 
			
		||||
			cond := builder.In("`user`.id", builder.Select("org_id").From("org_user").Where(builder.Eq{"uid": actor.ID}))
 | 
			
		||||
			if !actor.IsRestricted {
 | 
			
		||||
				// Not-Restricted users can see public and limited users/organizations
 | 
			
		||||
				cond = cond.Or(builder.In("`user`.visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
 | 
			
		||||
			}
 | 
			
		||||
			// Don't forget about self
 | 
			
		||||
			return cond.Or(builder.Eq{"`user`.id": actor.ID})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Force visibility for privacy
 | 
			
		||||
	// Not logged in - only public users
 | 
			
		||||
	return builder.In("`user`.visibility", structs.VisibleTypePublic)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user