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 | package migrations | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models" | 	"fmt" | ||||||
| 
 | 
 | ||||||
| 	"xorm.io/xorm" | 	"xorm.io/xorm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { | func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { | ||||||
| 
 |  | ||||||
| 	type ProtectedBranch struct { | 	type ProtectedBranch struct { | ||||||
| 		CanPush                  bool  `xorm:"NOT NULL DEFAULT false"` | 		CanPush                   bool    `xorm:"NOT NULL DEFAULT false"` | ||||||
| 		EnableApprovalsWhitelist bool  `xorm:"NOT NULL DEFAULT false"` | 		EnableApprovalsWhitelist  bool    `xorm:"NOT NULL DEFAULT false"` | ||||||
| 		RequiredApprovals        int64 `xorm:"NOT NULL DEFAULT 0"` | 		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 { | 	type Review struct { | ||||||
| 		ID       int64 `xorm:"pk autoincr"` | 		ID       int64 `xorm:"pk autoincr"` | ||||||
| 		Official bool  `xorm:"NOT NULL DEFAULT false"` | 		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() | 	sess := x.NewSession() | ||||||
| 	defer sess.Close() | 	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 { | 	if _, err := sess.Exec("UPDATE `protected_branch` SET `enable_whitelist` = ? WHERE enable_whitelist IS NULL", false); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | @ -58,21 +384,24 @@ func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { | ||||||
| 	totalPages := totalIssues / pageSize | 	totalPages := totalIssues / pageSize | ||||||
| 
 | 
 | ||||||
| 	// Find latest review of each user in each pull request, and set official field if appropriate
 | 	// Find latest review of each user in each pull request, and set official field if appropriate
 | ||||||
| 	reviews := []*models.Review{} | 	reviews := []*Review{} | ||||||
| 	var page int64 | 	var page int64 | ||||||
| 	for page = 0; page <= totalPages; page++ { | 	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)", | 		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 { | 			Find(&reviews); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for _, review := range reviews { | 		for _, review := range reviews { | ||||||
| 			if err := review.LoadAttributes(); err != nil { | 			reviewer := new(User) | ||||||
| 				// Error might occur if user or issue doesn't exist, ignore it.
 | 			has, err := sess.ID(review.ReviewerID).Get(reviewer) | ||||||
|  | 			if err != nil || !has { | ||||||
|  | 				// Error might occur if user doesn't exist, ignore it.
 | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			official, err := models.IsOfficialReviewer(review.Issue, review.Reviewer) | 
 | ||||||
|  | 			official, err := isOfficialReviewer(sess, review.IssueID, reviewer) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				// Branch might not be proteced or other error, ignore it.
 | 				// Branch might not be proteced or other error, ignore it.
 | ||||||
| 				continue | 				continue | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue