@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/log"
@ -113,10 +114,10 @@ func IsValidReviewRequest(ctx context.Context, reviewer, doer *user_model.User,
return err
return err
}
}
var pemResult bool
canDoerChangeReviewRequests := CanDoerChangeReviewRequests ( ctx , doer , issue . Repo , issue )
if isAdd {
if isAdd {
pemResult = permReviewer . CanAccessAny ( perm . AccessModeRead , unit . TypePullRequests )
if ! permReviewer . CanAccessAny ( perm . AccessModeRead , unit . TypePullRequests ) {
if ! pemResult {
return issues_model . ErrNotValidReviewRequest {
return issues_model . ErrNotValidReviewRequest {
Reason : "Reviewer can't read" ,
Reason : "Reviewer can't read" ,
UserID : doer . ID ,
UserID : doer . ID ,
@ -124,28 +125,6 @@ func IsValidReviewRequest(ctx context.Context, reviewer, doer *user_model.User,
}
}
}
}
if doer . ID == issue . PosterID && issue . OriginalAuthorID == 0 && lastreview != nil && lastreview . Type != issues_model . ReviewTypeRequest {
return nil
}
pemResult = doer . ID == issue . PosterID
if ! pemResult {
pemResult = permDoer . CanAccessAny ( perm . AccessModeWrite , unit . TypePullRequests )
}
if ! pemResult {
pemResult , err = issues_model . IsOfficialReviewer ( ctx , issue , doer )
if err != nil {
return err
}
if ! pemResult {
return issues_model . ErrNotValidReviewRequest {
Reason : "Doer can't choose reviewer" ,
UserID : doer . ID ,
RepoID : issue . Repo . ID ,
}
}
}
if reviewer . ID == issue . PosterID && issue . OriginalAuthorID == 0 {
if reviewer . ID == issue . PosterID && issue . OriginalAuthorID == 0 {
return issues_model . ErrNotValidReviewRequest {
return issues_model . ErrNotValidReviewRequest {
Reason : "poster of pr can't be reviewer" ,
Reason : "poster of pr can't be reviewer" ,
@ -153,22 +132,35 @@ func IsValidReviewRequest(ctx context.Context, reviewer, doer *user_model.User,
RepoID : issue . Repo . ID ,
RepoID : issue . Repo . ID ,
}
}
}
}
} else {
if lastreview != nil && lastreview . Type == issues_model . ReviewTypeRequest && lastreview . ReviewerID == doer . ID {
if canDoerChangeReviewRequests {
return nil
return nil
}
}
pemResult = permDoer . IsAdmin ( )
if doer . ID == issue . PosterID && issue . OriginalAuthorID == 0 && lastreview != nil && lastreview . Type != issues_model . ReviewTypeRequest {
if ! pemResult {
return nil
return issues_model . ErrNotValidReviewRequest {
}
Reason : "Doer is not admin" ,
UserID : doer . ID ,
return issues_model . ErrNotValidReviewRequest {
RepoID : issue . Repo . ID ,
Reason : "Doer can't choose reviewer" ,
}
UserID : doer . ID ,
RepoID : issue . Repo . ID ,
}
}
}
}
return nil
if canDoerChangeReviewRequests {
return nil
}
if lastreview != nil && lastreview . Type == issues_model . ReviewTypeRequest && lastreview . ReviewerID == doer . ID {
return nil
}
return issues_model . ErrNotValidReviewRequest {
Reason : "Doer can't remove reviewer" ,
UserID : doer . ID ,
RepoID : issue . Repo . ID ,
}
}
}
// IsValidTeamReviewRequest Check permission for ReviewRequest Team
// IsValidTeamReviewRequest Check permission for ReviewRequest Team
@ -181,11 +173,7 @@ func IsValidTeamReviewRequest(ctx context.Context, reviewer *organization.Team,
}
}
}
}
permission , err := access_model . GetUserRepoPermission ( ctx , issue . Repo , doer )
canDoerChangeReviewRequests := CanDoerChangeReviewRequests ( ctx , doer , issue . Repo , issue )
if err != nil {
log . Error ( "Unable to GetUserRepoPermission for %-v in %-v#%d" , doer , issue . Repo , issue . Index )
return err
}
if isAdd {
if isAdd {
if issue . Repo . IsPrivate {
if issue . Repo . IsPrivate {
@ -200,30 +188,26 @@ func IsValidTeamReviewRequest(ctx context.Context, reviewer *organization.Team,
}
}
}
}
doerCanWrite := permission . CanAccessAny ( perm . AccessModeWrite , unit . TypePullRequests )
if canDoerChangeReviewRequests {
if ! doerCanWrite && doer . ID != issue . PosterID {
return nil
official , err := issues_model . IsOfficialReviewer ( ctx , issue , doer )
if err != nil {
log . Error ( "Unable to Check if IsOfficialReviewer for %-v in %-v#%d" , doer , issue . Repo , issue . Index )
return err
}
if ! official {
return issues_model . ErrNotValidReviewRequest {
Reason : "Doer can't choose reviewer" ,
UserID : doer . ID ,
RepoID : issue . Repo . ID ,
}
}
}
}
} else if ! permission . IsAdmin ( ) {
return issues_model . ErrNotValidReviewRequest {
return issues_model . ErrNotValidReviewRequest {
Reason : "Only admin users can remove team requests. Doer is not admin " ,
Reason : "Doer can't choose reviewer" ,
UserID : doer . ID ,
UserID : doer . ID ,
RepoID : issue . Repo . ID ,
RepoID : issue . Repo . ID ,
}
}
}
}
return nil
if canDoerChangeReviewRequests {
return nil
}
return issues_model . ErrNotValidReviewRequest {
Reason : "Doer can't remove reviewer" ,
UserID : doer . ID ,
RepoID : issue . Repo . ID ,
}
}
}
// TeamReviewRequest add or remove a review request from a team for this PR, and make comment for it.
// TeamReviewRequest add or remove a review request from a team for this PR, and make comment for it.
@ -264,3 +248,50 @@ func TeamReviewRequest(ctx context.Context, issue *issues_model.Issue, doer *use
return comment , err
return comment , err
}
}
// CanDoerChangeReviewRequests returns if the doer can add/remove review requests of a PR
func CanDoerChangeReviewRequests ( ctx context . Context , doer * user_model . User , repo * repo_model . Repository , issue * issues_model . Issue ) bool {
// The poster of the PR can change the reviewers
if doer . ID == issue . PosterID {
return true
}
// The owner of the repo can change the reviewers
if doer . ID == repo . OwnerID {
return true
}
// Collaborators of the repo can change the reviewers
isCollaborator , err := repo_model . IsCollaborator ( ctx , repo . ID , doer . ID )
if err != nil {
log . Error ( "IsCollaborator: %v" , err )
return false
}
if isCollaborator {
return true
}
// If the repo's owner is an organization, members of teams with read permission on pull requests can change reviewers
if repo . Owner . IsOrganization ( ) {
teams , err := organization . GetTeamsWithAccessToRepo ( ctx , repo . OwnerID , repo . ID , perm . AccessModeRead )
if err != nil {
log . Error ( "GetTeamsWithAccessToRepo: %v" , err )
return false
}
for _ , team := range teams {
if ! team . UnitEnabled ( ctx , unit . TypePullRequests ) {
continue
}
isMember , err := organization . IsTeamMember ( ctx , repo . OwnerID , team . ID , doer . ID )
if err != nil {
log . Error ( "IsTeamMember: %v" , err )
continue
}
if isMember {
return true
}
}
}
return false
}