2022-05-11 13:09:36 +03:00
// Copyright 2022 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2022-05-11 13:09:36 +03:00
package repo
import (
"context"
"fmt"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
2022-06-12 18:51:54 +03:00
"code.gitea.io/gitea/models/unit"
2022-05-11 13:09:36 +03:00
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
)
// Collaboration represent the relation between an individual and a repository.
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 perm . AccessMode ` xorm:"DEFAULT 2 NOT NULL" `
CreatedUnix timeutil . TimeStamp ` xorm:"INDEX created" `
UpdatedUnix timeutil . TimeStamp ` xorm:"INDEX updated" `
}
func init ( ) {
db . RegisterModel ( new ( Collaboration ) )
}
// Collaborator represents a user with collaboration details.
type Collaborator struct {
* user_model . User
Collaboration * Collaboration
}
// GetCollaborators returns the collaborators for a repository
func GetCollaborators ( ctx context . Context , repoID int64 , listOptions db . ListOptions ) ( [ ] * Collaborator , error ) {
2022-05-20 17:08:52 +03:00
collaborations , err := getCollaborations ( ctx , repoID , listOptions )
2022-05-11 13:09:36 +03:00
if err != nil {
2022-10-24 22:29:17 +03:00
return nil , fmt . Errorf ( "getCollaborations: %w" , err )
2022-05-11 13:09:36 +03:00
}
collaborators := make ( [ ] * Collaborator , 0 , len ( collaborations ) )
for _ , c := range collaborations {
2022-05-20 17:08:52 +03:00
user , err := user_model . GetUserByIDCtx ( ctx , c . UserID )
2022-05-11 13:09:36 +03:00
if err != nil {
if user_model . IsErrUserNotExist ( err ) {
log . Warn ( "Inconsistent DB: User: %d is listed as collaborator of %-v but does not exist" , c . UserID , repoID )
user = user_model . NewGhostUser ( )
} else {
return nil , err
}
}
collaborators = append ( collaborators , & Collaborator {
User : user ,
Collaboration : c ,
} )
}
return collaborators , nil
}
// CountCollaborators returns total number of collaborators for a repository
func CountCollaborators ( repoID int64 ) ( int64 , error ) {
return db . GetEngine ( db . DefaultContext ) . Where ( "repo_id = ? " , repoID ) . Count ( & Collaboration { } )
}
// GetCollaboration get collaboration for a repository id with a user id
func GetCollaboration ( ctx context . Context , repoID , uid int64 ) ( * Collaboration , error ) {
collaboration := & Collaboration {
RepoID : repoID ,
UserID : uid ,
}
has , err := db . GetEngine ( ctx ) . Get ( collaboration )
if ! has {
collaboration = nil
}
return collaboration , err
}
// IsCollaborator check if a user is a collaborator of a repository
func IsCollaborator ( ctx context . Context , repoID , userID int64 ) ( bool , error ) {
return db . GetEngine ( ctx ) . Get ( & Collaboration { RepoID : repoID , UserID : userID } )
}
2022-05-20 17:08:52 +03:00
func getCollaborations ( ctx context . Context , repoID int64 , listOptions db . ListOptions ) ( [ ] * Collaboration , error ) {
2022-05-11 13:09:36 +03:00
if listOptions . Page == 0 {
collaborations := make ( [ ] * Collaboration , 0 , 8 )
2022-05-20 17:08:52 +03:00
return collaborations , db . GetEngine ( ctx ) . Find ( & collaborations , & Collaboration { RepoID : repoID } )
2022-05-11 13:09:36 +03:00
}
2022-05-20 17:08:52 +03:00
e := db . GetEngine ( ctx )
2022-05-11 13:09:36 +03:00
e = db . SetEnginePagination ( e , & listOptions )
collaborations := make ( [ ] * Collaboration , 0 , listOptions . PageSize )
return collaborations , e . Find ( & collaborations , & Collaboration { RepoID : repoID } )
}
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
func ChangeCollaborationAccessModeCtx ( ctx context . Context , repo * Repository , uid int64 , mode perm . AccessMode ) error {
// Discard invalid input
if mode <= perm . AccessModeNone || mode > perm . AccessModeOwner {
return nil
}
e := db . GetEngine ( ctx )
collaboration := & Collaboration {
RepoID : repo . ID ,
UserID : uid ,
}
has , err := e . Get ( collaboration )
if err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "get collaboration: %w" , err )
2022-05-11 13:09:36 +03:00
} else if ! has {
return nil
}
if collaboration . Mode == mode {
return nil
}
collaboration . Mode = mode
if _ , err = e .
ID ( collaboration . ID ) .
Cols ( "mode" ) .
Update ( collaboration ) ; err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "update collaboration: %w" , err )
2022-05-11 13:09:36 +03:00
} else if _ , err = e . Exec ( "UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?" , mode , uid , repo . ID ) ; err != nil {
2022-10-24 22:29:17 +03:00
return fmt . Errorf ( "update access table: %w" , err )
2022-05-11 13:09:36 +03:00
}
return nil
}
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
func ChangeCollaborationAccessMode ( repo * Repository , uid int64 , mode perm . AccessMode ) error {
2022-11-12 23:18:50 +03:00
ctx , committer , err := db . TxContext ( db . DefaultContext )
2022-05-11 13:09:36 +03:00
if err != nil {
return err
}
defer committer . Close ( )
if err := ChangeCollaborationAccessModeCtx ( ctx , repo , uid , mode ) ; err != nil {
return err
}
return committer . Commit ( )
}
2022-06-12 18:51:54 +03:00
// IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository
func IsOwnerMemberCollaborator ( repo * Repository , userID int64 ) ( bool , error ) {
if repo . OwnerID == userID {
return true , nil
}
teamMember , err := db . GetEngine ( db . DefaultContext ) . Join ( "INNER" , "team_repo" , "team_repo.team_id = team_user.team_id" ) .
Join ( "INNER" , "team_unit" , "team_unit.team_id = team_user.team_id" ) .
Where ( "team_repo.repo_id = ?" , repo . ID ) .
And ( "team_unit.`type` = ?" , unit . TypeCode ) .
And ( "team_user.uid = ?" , userID ) . Table ( "team_user" ) . Exist ( )
if err != nil {
return false , err
}
if teamMember {
return true , nil
}
return db . GetEngine ( db . DefaultContext ) . Get ( & Collaboration { RepoID : repo . ID , UserID : userID } )
}