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  }