code.gitea.io/gitea@v1.21.7/models/git/protected_tag.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package git 5 6 import ( 7 "context" 8 "regexp" 9 "strings" 10 11 "code.gitea.io/gitea/models/db" 12 "code.gitea.io/gitea/models/organization" 13 "code.gitea.io/gitea/modules/base" 14 "code.gitea.io/gitea/modules/timeutil" 15 16 "github.com/gobwas/glob" 17 ) 18 19 // ProtectedTag struct 20 type ProtectedTag struct { 21 ID int64 `xorm:"pk autoincr"` 22 RepoID int64 23 NamePattern string 24 RegexPattern *regexp.Regexp `xorm:"-"` 25 GlobPattern glob.Glob `xorm:"-"` 26 AllowlistUserIDs []int64 `xorm:"JSON TEXT"` 27 AllowlistTeamIDs []int64 `xorm:"JSON TEXT"` 28 29 CreatedUnix timeutil.TimeStamp `xorm:"created"` 30 UpdatedUnix timeutil.TimeStamp `xorm:"updated"` 31 } 32 33 func init() { 34 db.RegisterModel(new(ProtectedTag)) 35 } 36 37 // EnsureCompiledPattern ensures the glob pattern is compiled 38 func (pt *ProtectedTag) EnsureCompiledPattern() error { 39 if pt.RegexPattern != nil || pt.GlobPattern != nil { 40 return nil 41 } 42 43 var err error 44 if len(pt.NamePattern) >= 2 && strings.HasPrefix(pt.NamePattern, "/") && strings.HasSuffix(pt.NamePattern, "/") { 45 pt.RegexPattern, err = regexp.Compile(pt.NamePattern[1 : len(pt.NamePattern)-1]) 46 } else { 47 pt.GlobPattern, err = glob.Compile(pt.NamePattern) 48 } 49 return err 50 } 51 52 func (pt *ProtectedTag) matchString(name string) bool { 53 if pt.RegexPattern != nil { 54 return pt.RegexPattern.MatchString(name) 55 } 56 return pt.GlobPattern.Match(name) 57 } 58 59 // InsertProtectedTag inserts a protected tag to database 60 func InsertProtectedTag(ctx context.Context, pt *ProtectedTag) error { 61 _, err := db.GetEngine(ctx).Insert(pt) 62 return err 63 } 64 65 // UpdateProtectedTag updates the protected tag 66 func UpdateProtectedTag(ctx context.Context, pt *ProtectedTag) error { 67 _, err := db.GetEngine(ctx).ID(pt.ID).AllCols().Update(pt) 68 return err 69 } 70 71 // DeleteProtectedTag deletes a protected tag by ID 72 func DeleteProtectedTag(ctx context.Context, pt *ProtectedTag) error { 73 _, err := db.GetEngine(ctx).ID(pt.ID).Delete(&ProtectedTag{}) 74 return err 75 } 76 77 // IsUserAllowedModifyTag returns true if the user is allowed to modify the tag 78 func IsUserAllowedModifyTag(ctx context.Context, pt *ProtectedTag, userID int64) (bool, error) { 79 if base.Int64sContains(pt.AllowlistUserIDs, userID) { 80 return true, nil 81 } 82 83 if len(pt.AllowlistTeamIDs) == 0 { 84 return false, nil 85 } 86 87 in, err := organization.IsUserInTeams(ctx, userID, pt.AllowlistTeamIDs) 88 if err != nil { 89 return false, err 90 } 91 return in, nil 92 } 93 94 // GetProtectedTags gets all protected tags of the repository 95 func GetProtectedTags(ctx context.Context, repoID int64) ([]*ProtectedTag, error) { 96 tags := make([]*ProtectedTag, 0) 97 return tags, db.GetEngine(ctx).Find(&tags, &ProtectedTag{RepoID: repoID}) 98 } 99 100 // GetProtectedTagByID gets the protected tag with the specific id 101 func GetProtectedTagByID(ctx context.Context, id int64) (*ProtectedTag, error) { 102 tag := new(ProtectedTag) 103 has, err := db.GetEngine(ctx).ID(id).Get(tag) 104 if err != nil { 105 return nil, err 106 } 107 if !has { 108 return nil, nil 109 } 110 return tag, nil 111 } 112 113 // IsUserAllowedToControlTag checks if a user can control the specific tag. 114 // It returns true if the tag name is not protected or the user is allowed to control it. 115 func IsUserAllowedToControlTag(ctx context.Context, tags []*ProtectedTag, tagName string, userID int64) (bool, error) { 116 isAllowed := true 117 for _, tag := range tags { 118 err := tag.EnsureCompiledPattern() 119 if err != nil { 120 return false, err 121 } 122 123 if !tag.matchString(tagName) { 124 continue 125 } 126 127 isAllowed, err = IsUserAllowedModifyTag(ctx, tag, userID) 128 if err != nil { 129 return false, err 130 } 131 if isAllowed { 132 break 133 } 134 } 135 136 return isAllowed, nil 137 }