2016-03-06 02:08:42 +03:00
// Copyright 2016 The Gogs Authors. All rights reserved.
2020-01-24 22:00:29 +03:00
// Copyright 2020 The Gitea Authors.
2016-03-06 02:08:42 +03:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
2021-12-10 04:27:50 +03:00
"context"
2016-03-06 02:08:42 +03:00
"fmt"
2020-04-08 00:52:01 +03:00
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-11-28 14:58:28 +03:00
"code.gitea.io/gitea/models/perm"
2021-12-10 04:27:50 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 22:57:58 +03:00
"code.gitea.io/gitea/models/unit"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2021-09-27 21:07:19 +03:00
"code.gitea.io/gitea/modules/log"
2020-10-13 03:01:57 +03:00
"code.gitea.io/gitea/modules/timeutil"
2020-04-08 00:52:01 +03:00
"xorm.io/builder"
2016-03-06 02:08:42 +03:00
)
// Collaboration represent the relation between an individual and a repository.
type Collaboration struct {
2020-10-13 03:01:57 +03:00
ID int64 ` xorm:"pk autoincr" `
RepoID int64 ` xorm:"UNIQUE(s) INDEX NOT NULL" `
UserID int64 ` xorm:"UNIQUE(s) INDEX NOT NULL" `
2021-11-28 14:58:28 +03:00
Mode perm . AccessMode ` xorm:"DEFAULT 2 NOT NULL" `
2020-10-13 03:01:57 +03:00
CreatedUnix timeutil . TimeStamp ` xorm:"INDEX created" `
UpdatedUnix timeutil . TimeStamp ` xorm:"INDEX updated" `
2016-03-06 02:08:42 +03:00
}
2021-09-19 14:49:59 +03:00
func init ( ) {
db . RegisterModel ( new ( Collaboration ) )
}
2021-12-10 04:27:50 +03:00
func addCollaborator ( ctx context . Context , repo * repo_model . Repository , u * user_model . User ) error {
2016-03-06 02:08:42 +03:00
collaboration := & Collaboration {
RepoID : repo . ID ,
2016-07-23 20:08:22 +03:00
UserID : u . ID ,
2016-03-06 02:08:42 +03:00
}
2021-12-10 04:27:50 +03:00
e := db . GetEngine ( ctx )
2016-03-06 02:08:42 +03:00
2019-11-20 14:27:49 +03:00
has , err := e . Get ( collaboration )
2016-03-06 02:08:42 +03:00
if err != nil {
return err
} else if has {
return nil
}
2021-11-28 14:58:28 +03:00
collaboration . Mode = perm . AccessModeWrite
2016-03-06 02:08:42 +03:00
2019-11-20 14:27:49 +03:00
if _ , err = e . InsertOne ( collaboration ) ; err != nil {
2016-03-06 02:08:42 +03:00
return err
}
2021-12-10 04:27:50 +03:00
return recalculateUserAccess ( ctx , repo , u . ID )
2019-11-20 14:27:49 +03:00
}
// AddCollaborator adds new collaboration to a repository with default access mode.
2021-12-10 04:27:50 +03:00
func AddCollaborator ( repo * repo_model . Repository , u * user_model . User ) error {
2021-11-21 18:41:00 +03:00
ctx , committer , err := db . TxContext ( )
if err != nil {
2016-03-06 02:08:42 +03:00
return err
}
2021-11-21 18:41:00 +03:00
defer committer . Close ( )
2016-03-06 02:08:42 +03:00
2021-12-10 04:27:50 +03:00
if err := addCollaborator ( ctx , repo , u ) ; err != nil {
2019-11-20 14:27:49 +03:00
return err
2016-03-06 02:08:42 +03:00
}
2021-11-21 18:41:00 +03:00
return committer . Commit ( )
2016-03-06 02:08:42 +03:00
}
2021-12-10 04:27:50 +03:00
func getCollaborations ( e db . Engine , repoID int64 , listOptions db . ListOptions ) ( [ ] * Collaboration , error ) {
2020-01-24 22:00:29 +03:00
if listOptions . Page == 0 {
collaborations := make ( [ ] * Collaboration , 0 , 8 )
2021-12-10 04:27:50 +03:00
return collaborations , e . Find ( & collaborations , & Collaboration { RepoID : repoID } )
2020-01-24 22:00:29 +03:00
}
2021-09-24 14:32:56 +03:00
e = db . SetEnginePagination ( e , & listOptions )
2020-01-24 22:00:29 +03:00
collaborations := make ( [ ] * Collaboration , 0 , listOptions . PageSize )
2021-12-10 04:27:50 +03:00
return collaborations , e . Find ( & collaborations , & Collaboration { RepoID : repoID } )
2016-03-06 02:08:42 +03:00
}
// Collaborator represents a user with collaboration details.
type Collaborator struct {
2021-11-24 12:49:20 +03:00
* user_model . User
2016-03-06 02:08:42 +03:00
Collaboration * Collaboration
}
2021-12-10 04:27:50 +03:00
func getCollaborators ( e db . Engine , repoID int64 , listOptions db . ListOptions ) ( [ ] * Collaborator , error ) {
collaborations , err := getCollaborations ( e , repoID , listOptions )
2016-03-06 02:08:42 +03:00
if err != nil {
return nil , fmt . Errorf ( "getCollaborations: %v" , err )
}
2021-09-27 21:07:19 +03:00
collaborators := make ( [ ] * Collaborator , 0 , len ( collaborations ) )
for _ , c := range collaborations {
2021-11-24 12:49:20 +03:00
user , err := user_model . GetUserByIDEngine ( e , c . UserID )
2016-03-06 02:08:42 +03:00
if err != nil {
2021-11-24 12:49:20 +03:00
if user_model . IsErrUserNotExist ( err ) {
2021-12-10 04:27:50 +03:00
log . Warn ( "Inconsistent DB: User: %d is listed as collaborator of %-v but does not exist" , c . UserID , repoID )
2021-11-24 12:49:20 +03:00
user = user_model . NewGhostUser ( )
2021-09-27 21:07:19 +03:00
} else {
return nil , err
}
2016-03-06 02:08:42 +03:00
}
2021-09-27 21:07:19 +03:00
collaborators = append ( collaborators , & Collaborator {
2016-03-06 02:08:42 +03:00
User : user ,
Collaboration : c ,
2021-09-27 21:07:19 +03:00
} )
2016-03-06 02:08:42 +03:00
}
return collaborators , nil
}
// GetCollaborators returns the collaborators for a repository
2021-12-10 04:27:50 +03:00
func GetCollaborators ( repoID int64 , listOptions db . ListOptions ) ( [ ] * Collaborator , error ) {
return getCollaborators ( db . GetEngine ( db . DefaultContext ) , repoID , listOptions )
2016-03-06 02:08:42 +03:00
}
2021-08-12 15:43:08 +03:00
// CountCollaborators returns total number of collaborators for a repository
2021-12-10 04:27:50 +03:00
func CountCollaborators ( repoID int64 ) ( int64 , error ) {
return db . GetEngine ( db . DefaultContext ) . Where ( "repo_id = ? " , repoID ) . Count ( & Collaboration { } )
2021-08-12 15:43:08 +03:00
}
2021-12-10 04:27:50 +03:00
func getCollaboration ( e db . Engine , repoID , uid int64 ) ( * Collaboration , error ) {
2019-10-15 03:55:21 +03:00
collaboration := & Collaboration {
2021-12-10 04:27:50 +03:00
RepoID : repoID ,
2019-10-15 03:55:21 +03:00
UserID : uid ,
}
has , err := e . Get ( collaboration )
if ! has {
collaboration = nil
}
return collaboration , err
}
2021-12-10 04:27:50 +03:00
func isCollaborator ( e db . Engine , repoID , userID int64 ) ( bool , error ) {
return e . Get ( & Collaboration { RepoID : repoID , UserID : userID } )
2016-12-26 10:37:01 +03:00
}
// IsCollaborator check if a user is a collaborator of a repository
2021-12-10 04:27:50 +03:00
func IsCollaborator ( repoID , userID int64 ) ( bool , error ) {
return isCollaborator ( db . GetEngine ( db . DefaultContext ) , repoID , userID )
2016-12-26 10:37:01 +03:00
}
2021-12-10 04:27:50 +03:00
func changeCollaborationAccessMode ( e db . Engine , repo * repo_model . Repository , uid int64 , mode perm . AccessMode ) error {
2016-03-06 02:08:42 +03:00
// Discard invalid input
2021-11-28 14:58:28 +03:00
if mode <= perm . AccessModeNone || mode > perm . AccessModeOwner {
2016-03-06 02:08:42 +03:00
return nil
}
collaboration := & Collaboration {
RepoID : repo . ID ,
UserID : uid ,
}
2019-11-20 14:27:49 +03:00
has , err := e . Get ( collaboration )
2016-03-06 02:08:42 +03:00
if err != nil {
return fmt . Errorf ( "get collaboration: %v" , err )
} else if ! has {
return nil
}
2016-08-11 21:35:46 +03:00
if collaboration . Mode == mode {
return nil
}
2016-03-06 02:08:42 +03:00
collaboration . Mode = mode
2019-11-20 14:27:49 +03:00
if _ , err = e .
2019-06-12 22:41:28 +03:00
ID ( collaboration . ID ) .
2017-09-25 07:59:27 +03:00
Cols ( "mode" ) .
2016-11-10 18:16:32 +03:00
Update ( collaboration ) ; err != nil {
2016-03-06 02:08:42 +03:00
return fmt . Errorf ( "update collaboration: %v" , err )
2019-11-20 14:27:49 +03:00
} else if _ , err = e . Exec ( "UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?" , mode , uid , repo . ID ) ; err != nil {
2016-03-06 02:08:42 +03:00
return fmt . Errorf ( "update access table: %v" , err )
}
2019-11-20 14:27:49 +03:00
return nil
}
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
2021-12-10 04:27:50 +03:00
func ChangeCollaborationAccessMode ( repo * repo_model . Repository , uid int64 , mode perm . AccessMode ) error {
2021-11-21 18:41:00 +03:00
ctx , committer , err := db . TxContext ( )
if err != nil {
2019-11-20 14:27:49 +03:00
return err
}
2021-11-21 18:41:00 +03:00
defer committer . Close ( )
2019-11-20 14:27:49 +03:00
2021-12-10 04:27:50 +03:00
if err := changeCollaborationAccessMode ( db . GetEngine ( ctx ) , repo , uid , mode ) ; err != nil {
2019-11-20 14:27:49 +03:00
return err
}
2021-11-21 18:41:00 +03:00
return committer . Commit ( )
2016-03-06 02:08:42 +03:00
}
// DeleteCollaboration removes collaboration relation between the user and repository.
2021-12-10 04:27:50 +03:00
func DeleteCollaboration ( repo * repo_model . Repository , uid int64 ) ( err error ) {
2016-03-06 02:08:42 +03:00
collaboration := & Collaboration {
RepoID : repo . ID ,
UserID : uid ,
}
2021-11-21 18:41:00 +03:00
ctx , committer , err := db . TxContext ( )
if err != nil {
2016-03-06 02:08:42 +03:00
return err
}
2021-11-21 18:41:00 +03:00
defer committer . Close ( )
2021-12-12 18:48:20 +03:00
if has , err := db . GetEngine ( ctx ) . Delete ( collaboration ) ; err != nil || has == 0 {
2016-03-06 02:08:42 +03:00
return err
2021-12-10 04:27:50 +03:00
} else if err = recalculateAccesses ( ctx , repo ) ; err != nil {
2016-03-06 02:08:42 +03:00
return err
}
2021-12-12 18:48:20 +03:00
if err = repo_model . WatchRepoCtx ( ctx , uid , repo . ID , false ) ; err != nil {
2018-06-19 22:44:33 +03:00
return err
}
2021-12-10 04:27:50 +03:00
if err = reconsiderWatches ( ctx , repo , uid ) ; err != nil {
2020-04-08 00:52:01 +03:00
return err
}
// Unassign a user from any issue (s)he has been assigned to in the repository
2021-12-10 04:27:50 +03:00
if err := reconsiderRepoIssuesAssignee ( ctx , repo , uid ) ; err != nil {
2018-06-19 22:44:33 +03:00
return err
}
2021-11-21 18:41:00 +03:00
return committer . Commit ( )
2016-03-06 02:08:42 +03:00
}
2019-09-23 23:08:03 +03:00
2021-12-10 04:27:50 +03:00
func reconsiderRepoIssuesAssignee ( ctx context . Context , repo * repo_model . Repository , uid int64 ) error {
user , err := user_model . GetUserByIDEngine ( db . GetEngine ( ctx ) , uid )
2020-04-08 00:52:01 +03:00
if err != nil {
return err
}
2021-12-10 04:27:50 +03:00
if canAssigned , err := canBeAssigned ( ctx , user , repo , true ) ; err != nil || canAssigned {
2020-04-08 00:52:01 +03:00
return err
}
2021-12-10 04:27:50 +03:00
if _ , err := db . GetEngine ( ctx ) . Where ( builder . Eq { "assignee_id" : uid } ) .
2020-04-08 00:52:01 +03:00
In ( "issue_id" , builder . Select ( "id" ) . From ( "issue" ) . Where ( builder . Eq { "repo_id" : repo . ID } ) ) .
Delete ( & IssueAssignees { } ) ; err != nil {
return fmt . Errorf ( "Could not delete assignee[%d] %v" , uid , err )
}
return nil
}
2021-12-10 04:27:50 +03:00
func reconsiderWatches ( ctx context . Context , repo * repo_model . Repository , uid int64 ) error {
if has , err := hasAccess ( ctx , uid , repo ) ; err != nil || has {
2020-04-08 00:52:01 +03:00
return err
}
2021-12-12 18:48:20 +03:00
if err := repo_model . WatchRepoCtx ( ctx , uid , repo . ID , false ) ; err != nil {
2020-04-08 00:52:01 +03:00
return err
}
// Remove all IssueWatches a user has subscribed to in the repository
2021-12-12 18:48:20 +03:00
return removeIssueWatchersByRepoID ( db . GetEngine ( ctx ) , uid , repo . ID )
2020-04-08 00:52:01 +03:00
}
2021-12-10 04:27:50 +03:00
func getRepoTeams ( e db . Engine , repo * repo_model . Repository ) ( teams [ ] * Team , err error ) {
2019-09-23 23:08:03 +03:00
return teams , e .
Join ( "INNER" , "team_repo" , "team_repo.team_id = team.id" ) .
Where ( "team.org_id = ?" , repo . OwnerID ) .
And ( "team_repo.repo_id=?" , repo . ID ) .
OrderBy ( "CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END" ) .
Find ( & teams )
}
// GetRepoTeams gets the list of teams that has access to the repository
2021-12-10 04:27:50 +03:00
func GetRepoTeams ( repo * repo_model . Repository ) ( [ ] * Team , error ) {
return getRepoTeams ( db . GetEngine ( db . DefaultContext ) , repo )
2019-09-23 23:08:03 +03:00
}
2020-02-27 22:20:55 +03:00
// IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository
2021-12-10 04:27:50 +03:00
func IsOwnerMemberCollaborator ( repo * repo_model . Repository , userID int64 ) ( bool , error ) {
2020-02-27 22:20:55 +03:00
if repo . OwnerID == userID {
return true , nil
}
2021-09-23 18:45:36 +03:00
teamMember , err := db . GetEngine ( db . DefaultContext ) . Join ( "INNER" , "team_repo" , "team_repo.team_id = team_user.team_id" ) .
2020-02-27 22:20:55 +03:00
Join ( "INNER" , "team_unit" , "team_unit.team_id = team_user.team_id" ) .
Where ( "team_repo.repo_id = ?" , repo . ID ) .
2021-11-09 22:57:58 +03:00
And ( "team_unit.`type` = ?" , unit . TypeCode ) .
2020-02-27 22:20:55 +03:00
And ( "team_user.uid = ?" , userID ) . Table ( "team_user" ) . Exist ( & TeamUser { } )
if err != nil {
return false , err
}
if teamMember {
return true , nil
}
2021-09-23 18:45:36 +03:00
return db . GetEngine ( db . DefaultContext ) . Get ( & Collaboration { RepoID : repo . ID , UserID : userID } )
2020-02-27 22:20:55 +03:00
}