Refactor: Remove Dependencies from Migration v111 (#11646)
* Refactor: Remove Dependencys from Migration v111 * Update models/migrations/v111.go Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
		
							parent
							
								
									4609eba2e7
								
							
						
					
					
						commit
						7ecb25b41b
					
				
					 1 changed files with 347 additions and 18 deletions
				
			
		|  | @ -5,35 +5,361 @@ | |||
| package migrations | ||||
| 
 | ||||
| import ( | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"xorm.io/xorm" | ||||
| ) | ||||
| 
 | ||||
| func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { | ||||
| 
 | ||||
| 	type ProtectedBranch struct { | ||||
| 		CanPush                  bool  `xorm:"NOT NULL DEFAULT false"` | ||||
| 		EnableApprovalsWhitelist bool  `xorm:"NOT NULL DEFAULT false"` | ||||
| 		RequiredApprovals        int64 `xorm:"NOT NULL DEFAULT 0"` | ||||
| 		CanPush                   bool    `xorm:"NOT NULL DEFAULT false"` | ||||
| 		EnableApprovalsWhitelist  bool    `xorm:"NOT NULL DEFAULT false"` | ||||
| 		ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"` | ||||
| 		ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` | ||||
| 		RequiredApprovals         int64   `xorm:"NOT NULL DEFAULT 0"` | ||||
| 	} | ||||
| 
 | ||||
| 	type User struct { | ||||
| 		ID   int64 `xorm:"pk autoincr"` | ||||
| 		Type int | ||||
| 
 | ||||
| 		// Permissions
 | ||||
| 		IsAdmin      bool | ||||
| 		IsRestricted bool `xorm:"NOT NULL DEFAULT false"` | ||||
| 		Visibility   int  `xorm:"NOT NULL DEFAULT 0"` | ||||
| 	} | ||||
| 
 | ||||
| 	type Review struct { | ||||
| 		ID       int64 `xorm:"pk autoincr"` | ||||
| 		Official bool  `xorm:"NOT NULL DEFAULT false"` | ||||
| 
 | ||||
| 		ReviewerID int64 `xorm:"index"` | ||||
| 		IssueID    int64 `xorm:"index"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := x.Sync2(new(ProtectedBranch)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := x.Sync2(new(Review)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	const ( | ||||
| 		// ReviewTypeApprove approves changes
 | ||||
| 		ReviewTypeApprove int = 1 | ||||
| 		// ReviewTypeReject gives feedback blocking merge
 | ||||
| 		ReviewTypeReject int = 3 | ||||
| 
 | ||||
| 		// VisibleTypePublic Visible for everyone
 | ||||
| 		VisibleTypePublic int = 0 | ||||
| 		// VisibleTypePrivate Visible only for organization's members
 | ||||
| 		VisibleTypePrivate int = 2 | ||||
| 
 | ||||
| 		// UnitTypeCode is unit type code
 | ||||
| 		UnitTypeCode int = 1 | ||||
| 
 | ||||
| 		// AccessModeNone no access
 | ||||
| 		AccessModeNone int = 0 | ||||
| 		// AccessModeRead read access
 | ||||
| 		AccessModeRead int = 1 | ||||
| 		// AccessModeWrite write access
 | ||||
| 		AccessModeWrite int = 2 | ||||
| 		// AccessModeOwner owner access
 | ||||
| 		AccessModeOwner int = 4 | ||||
| 	) | ||||
| 
 | ||||
| 	// Repository represents a git repository.
 | ||||
| 	type Repository struct { | ||||
| 		ID      int64 `xorm:"pk autoincr"` | ||||
| 		OwnerID int64 `xorm:"UNIQUE(s) index"` | ||||
| 
 | ||||
| 		IsPrivate bool `xorm:"INDEX"` | ||||
| 	} | ||||
| 
 | ||||
| 	type PullRequest struct { | ||||
| 		ID int64 `xorm:"pk autoincr"` | ||||
| 
 | ||||
| 		BaseRepoID int64 `xorm:"INDEX"` | ||||
| 		BaseBranch string | ||||
| 	} | ||||
| 
 | ||||
| 	// RepoUnit describes all units of a repository
 | ||||
| 	type RepoUnit struct { | ||||
| 		ID     int64 | ||||
| 		RepoID int64 `xorm:"INDEX(s)"` | ||||
| 		Type   int   `xorm:"INDEX(s)"` | ||||
| 	} | ||||
| 
 | ||||
| 	type Permission struct { | ||||
| 		AccessMode int | ||||
| 		Units      []*RepoUnit | ||||
| 		UnitsMode  map[int]int | ||||
| 	} | ||||
| 
 | ||||
| 	type TeamUser struct { | ||||
| 		ID     int64 `xorm:"pk autoincr"` | ||||
| 		TeamID int64 `xorm:"UNIQUE(s)"` | ||||
| 		UID    int64 `xorm:"UNIQUE(s)"` | ||||
| 	} | ||||
| 
 | ||||
| 	type Collaboration struct { | ||||
| 		ID     int64 `xorm:"pk autoincr"` | ||||
| 		RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` | ||||
| 		UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` | ||||
| 		Mode   int   `xorm:"DEFAULT 2 NOT NULL"` | ||||
| 	} | ||||
| 
 | ||||
| 	type Access struct { | ||||
| 		ID     int64 `xorm:"pk autoincr"` | ||||
| 		UserID int64 `xorm:"UNIQUE(s)"` | ||||
| 		RepoID int64 `xorm:"UNIQUE(s)"` | ||||
| 		Mode   int | ||||
| 	} | ||||
| 
 | ||||
| 	type TeamUnit struct { | ||||
| 		ID     int64 `xorm:"pk autoincr"` | ||||
| 		OrgID  int64 `xorm:"INDEX"` | ||||
| 		TeamID int64 `xorm:"UNIQUE(s)"` | ||||
| 		Type   int   `xorm:"UNIQUE(s)"` | ||||
| 	} | ||||
| 
 | ||||
| 	// Team represents a organization team.
 | ||||
| 	type Team struct { | ||||
| 		ID        int64 `xorm:"pk autoincr"` | ||||
| 		OrgID     int64 `xorm:"INDEX"` | ||||
| 		Authorize int | ||||
| 	} | ||||
| 
 | ||||
| 	// getUserRepoPermission static function based on models.IsOfficialReviewer at 5d78792385
 | ||||
| 	getUserRepoPermission := func(sess *xorm.Session, repo *Repository, user *User) (Permission, error) { | ||||
| 		var perm Permission | ||||
| 
 | ||||
| 		repoOwner := new(User) | ||||
| 		has, err := sess.ID(repo.OwnerID).Get(repoOwner) | ||||
| 		if err != nil || !has { | ||||
| 			return perm, err | ||||
| 		} | ||||
| 
 | ||||
| 		// Prevent strangers from checking out public repo of private orginization
 | ||||
| 		// Allow user if they are collaborator of a repo within a private orginization but not a member of the orginization itself
 | ||||
| 		hasOrgVisible := true | ||||
| 		// Not SignedUser
 | ||||
| 		if user == nil { | ||||
| 			hasOrgVisible = repoOwner.Visibility == VisibleTypePublic | ||||
| 		} else if !user.IsAdmin { | ||||
| 			isUserPartOfOrg, err := sess. | ||||
| 				Where("uid=?", user.ID). | ||||
| 				And("org_id=?", repoOwner.ID). | ||||
| 				Table("org_user"). | ||||
| 				Exist() | ||||
| 			if err != nil { | ||||
| 				hasOrgVisible = false | ||||
| 			} | ||||
| 			if (repoOwner.Visibility == VisibleTypePrivate || user.IsRestricted) && !isUserPartOfOrg { | ||||
| 				hasOrgVisible = false | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		isCollaborator, err := sess.Get(&Collaboration{RepoID: repo.ID, UserID: user.ID}) | ||||
| 		if err != nil { | ||||
| 			return perm, err | ||||
| 		} | ||||
| 
 | ||||
| 		if repoOwner.Type == 1 && !hasOrgVisible && !isCollaborator { | ||||
| 			perm.AccessMode = AccessModeNone | ||||
| 			return perm, err | ||||
| 		} | ||||
| 
 | ||||
| 		var units []*RepoUnit | ||||
| 		if err := sess.Where("repo_id = ?", repo.ID).Find(&units); err != nil { | ||||
| 			return perm, err | ||||
| 		} | ||||
| 		perm.Units = units | ||||
| 
 | ||||
| 		// anonymous visit public repo
 | ||||
| 		if user == nil { | ||||
| 			perm.AccessMode = AccessModeRead | ||||
| 			return perm, err | ||||
| 		} | ||||
| 
 | ||||
| 		// Admin or the owner has super access to the repository
 | ||||
| 		if user.IsAdmin || user.ID == repo.OwnerID { | ||||
| 			perm.AccessMode = AccessModeOwner | ||||
| 			return perm, err | ||||
| 		} | ||||
| 
 | ||||
| 		accessLevel := func(user *User, repo *Repository) (int, error) { | ||||
| 			mode := AccessModeNone | ||||
| 			var userID int64 | ||||
| 			restricted := false | ||||
| 
 | ||||
| 			if user != nil { | ||||
| 				userID = user.ID | ||||
| 				restricted = user.IsRestricted | ||||
| 			} | ||||
| 
 | ||||
| 			if !restricted && !repo.IsPrivate { | ||||
| 				mode = AccessModeRead | ||||
| 			} | ||||
| 
 | ||||
| 			if userID == 0 { | ||||
| 				return mode, nil | ||||
| 			} | ||||
| 
 | ||||
| 			if userID == repo.OwnerID { | ||||
| 				return AccessModeOwner, nil | ||||
| 			} | ||||
| 
 | ||||
| 			a := &Access{UserID: userID, RepoID: repo.ID} | ||||
| 			if has, err := sess.Get(a); !has || err != nil { | ||||
| 				return mode, err | ||||
| 			} | ||||
| 			return a.Mode, nil | ||||
| 		} | ||||
| 
 | ||||
| 		// plain user
 | ||||
| 		perm.AccessMode, err = accessLevel(user, repo) | ||||
| 		if err != nil { | ||||
| 			return perm, err | ||||
| 		} | ||||
| 
 | ||||
| 		// If Owner is no Org
 | ||||
| 		if repoOwner.Type != 1 { | ||||
| 			return perm, err | ||||
| 		} | ||||
| 
 | ||||
| 		perm.UnitsMode = make(map[int]int) | ||||
| 
 | ||||
| 		// Collaborators on organization
 | ||||
| 		if isCollaborator { | ||||
| 			for _, u := range units { | ||||
| 				perm.UnitsMode[u.Type] = perm.AccessMode | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// get units mode from teams
 | ||||
| 		var teams []*Team | ||||
| 		err = sess. | ||||
| 			Join("INNER", "team_user", "team_user.team_id = team.id"). | ||||
| 			Join("INNER", "team_repo", "team_repo.team_id = team.id"). | ||||
| 			Where("team.org_id = ?", repo.OwnerID). | ||||
| 			And("team_user.uid=?", user.ID). | ||||
| 			And("team_repo.repo_id=?", repo.ID). | ||||
| 			Find(&teams) | ||||
| 		if err != nil { | ||||
| 			return perm, err | ||||
| 		} | ||||
| 
 | ||||
| 		// if user in an owner team
 | ||||
| 		for _, team := range teams { | ||||
| 			if team.Authorize >= AccessModeOwner { | ||||
| 				perm.AccessMode = AccessModeOwner | ||||
| 				perm.UnitsMode = nil | ||||
| 				return perm, err | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		for _, u := range units { | ||||
| 			var found bool | ||||
| 			for _, team := range teams { | ||||
| 
 | ||||
| 				var teamU []*TeamUnit | ||||
| 				var unitEnabled bool | ||||
| 				err = sess.Where("team_id = ?", team.ID).Find(&teamU) | ||||
| 
 | ||||
| 				for _, tu := range teamU { | ||||
| 					if tu.Type == u.Type { | ||||
| 						unitEnabled = true | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				if unitEnabled { | ||||
| 					m := perm.UnitsMode[u.Type] | ||||
| 					if m < team.Authorize { | ||||
| 						perm.UnitsMode[u.Type] = team.Authorize | ||||
| 					} | ||||
| 					found = true | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			// for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
 | ||||
| 			if !found && !repo.IsPrivate && !user.IsRestricted { | ||||
| 				if _, ok := perm.UnitsMode[u.Type]; !ok { | ||||
| 					perm.UnitsMode[u.Type] = AccessModeRead | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// remove no permission units
 | ||||
| 		perm.Units = make([]*RepoUnit, 0, len(units)) | ||||
| 		for t := range perm.UnitsMode { | ||||
| 			for _, u := range units { | ||||
| 				if u.Type == t { | ||||
| 					perm.Units = append(perm.Units, u) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return perm, err | ||||
| 	} | ||||
| 
 | ||||
| 	// isOfficialReviewer static function based on 5d78792385
 | ||||
| 	isOfficialReviewer := func(sess *xorm.Session, issueID int64, reviewer *User) (bool, error) { | ||||
| 		pr := new(PullRequest) | ||||
| 		has, err := sess.ID(issueID).Get(pr) | ||||
| 		if err != nil { | ||||
| 			return false, err | ||||
| 		} else if !has { | ||||
| 			return false, fmt.Errorf("PullRequest for issueID %d not exist", issueID) | ||||
| 		} | ||||
| 
 | ||||
| 		baseRepo := new(Repository) | ||||
| 		has, err = sess.ID(pr.BaseRepoID).Get(baseRepo) | ||||
| 		if err != nil { | ||||
| 			return false, err | ||||
| 		} else if !has { | ||||
| 			return false, fmt.Errorf("baseRepo with id %d not exist", pr.BaseRepoID) | ||||
| 		} | ||||
| 		protectedBranch := new(ProtectedBranch) | ||||
| 		has, err = sess.Where("repo_id=? AND branch_name=?", baseRepo.ID, pr.BaseBranch).Get(protectedBranch) | ||||
| 		if err != nil { | ||||
| 			return false, err | ||||
| 		} | ||||
| 		if !has { | ||||
| 			return false, nil | ||||
| 		} | ||||
| 
 | ||||
| 		if !protectedBranch.EnableApprovalsWhitelist { | ||||
| 
 | ||||
| 			perm, err := getUserRepoPermission(sess, baseRepo, reviewer) | ||||
| 			if err != nil { | ||||
| 				return false, err | ||||
| 			} | ||||
| 			if perm.UnitsMode == nil { | ||||
| 				for _, u := range perm.Units { | ||||
| 					if u.Type == UnitTypeCode { | ||||
| 						return AccessModeWrite <= perm.AccessMode, nil | ||||
| 					} | ||||
| 				} | ||||
| 				return false, nil | ||||
| 			} | ||||
| 			return AccessModeWrite <= perm.UnitsMode[UnitTypeCode], nil | ||||
| 		} | ||||
| 		for _, id := range protectedBranch.ApprovalsWhitelistUserIDs { | ||||
| 			if id == reviewer.ID { | ||||
| 				return true, nil | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// isUserInTeams
 | ||||
| 		return sess.Where("uid=?", reviewer.ID).In("team_id", protectedBranch.ApprovalsWhitelistTeamIDs).Exist(new(TeamUser)) | ||||
| 	} | ||||
| 
 | ||||
| 	sess := x.NewSession() | ||||
| 	defer sess.Close() | ||||
| 
 | ||||
| 	if err := sess.Sync2(new(ProtectedBranch)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := sess.Sync2(new(Review)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err := sess.Exec("UPDATE `protected_branch` SET `enable_whitelist` = ? WHERE enable_whitelist IS NULL", false); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | @ -58,21 +384,24 @@ func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { | |||
| 	totalPages := totalIssues / pageSize | ||||
| 
 | ||||
| 	// Find latest review of each user in each pull request, and set official field if appropriate
 | ||||
| 	reviews := []*models.Review{} | ||||
| 	reviews := []*Review{} | ||||
| 	var page int64 | ||||
| 	for page = 0; page <= totalPages; page++ { | ||||
| 		if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id > ? AND issue_id <= ? AND type in (?, ?) GROUP BY issue_id, reviewer_id)", | ||||
| 			page*pageSize, (page+1)*pageSize, models.ReviewTypeApprove, models.ReviewTypeReject). | ||||
| 			page*pageSize, (page+1)*pageSize, ReviewTypeApprove, ReviewTypeReject). | ||||
| 			Find(&reviews); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		for _, review := range reviews { | ||||
| 			if err := review.LoadAttributes(); err != nil { | ||||
| 				// Error might occur if user or issue doesn't exist, ignore it.
 | ||||
| 			reviewer := new(User) | ||||
| 			has, err := sess.ID(review.ReviewerID).Get(reviewer) | ||||
| 			if err != nil || !has { | ||||
| 				// Error might occur if user doesn't exist, ignore it.
 | ||||
| 				continue | ||||
| 			} | ||||
| 			official, err := models.IsOfficialReviewer(review.Issue, review.Reviewer) | ||||
| 
 | ||||
| 			official, err := isOfficialReviewer(sess, review.IssueID, reviewer) | ||||
| 			if err != nil { | ||||
| 				// Branch might not be proteced or other error, ignore it.
 | ||||
| 				continue | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue