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/timeutil"
2024-01-15 05:19:25 +03:00
"xorm.io/builder"
2022-05-11 13:09:36 +03:00
)
// 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
}
2024-03-04 11:16:03 +03:00
type FindCollaborationOptions struct {
db . ListOptions
RepoID int64
RepoOwnerID int64
CollaboratorID int64
}
func ( opts * FindCollaborationOptions ) ToConds ( ) builder . Cond {
cond := builder . NewCond ( )
if opts . RepoID != 0 {
cond = cond . And ( builder . Eq { "collaboration.repo_id" : opts . RepoID } )
}
if opts . RepoOwnerID != 0 {
cond = cond . And ( builder . Eq { "repository.owner_id" : opts . RepoOwnerID } )
}
if opts . CollaboratorID != 0 {
cond = cond . And ( builder . Eq { "collaboration.user_id" : opts . CollaboratorID } )
}
return cond
}
func ( opts * FindCollaborationOptions ) ToJoins ( ) [ ] db . JoinFunc {
if opts . RepoOwnerID != 0 {
return [ ] db . JoinFunc {
func ( e db . Engine ) error {
e . Join ( "INNER" , "repository" , "repository.id = collaboration.repo_id" )
return nil
} ,
}
}
return nil
}
2022-05-11 13:09:36 +03:00
// GetCollaborators returns the collaborators for a repository
2024-03-04 11:16:03 +03:00
func GetCollaborators ( ctx context . Context , opts * FindCollaborationOptions ) ( [ ] * Collaborator , int64 , error ) {
collaborations , total , err := db . FindAndCount [ Collaboration ] ( ctx , opts )
2022-05-11 13:09:36 +03:00
if err != nil {
2024-03-04 11:16:03 +03:00
return nil , 0 , fmt . Errorf ( "db.FindAndCount[Collaboration]: %w" , err )
2022-05-11 13:09:36 +03:00
}
collaborators := make ( [ ] * Collaborator , 0 , len ( collaborations ) )
2024-01-15 05:19:25 +03:00
userIDs := make ( [ ] int64 , 0 , len ( collaborations ) )
2022-05-11 13:09:36 +03:00
for _ , c := range collaborations {
2024-01-15 05:19:25 +03:00
userIDs = append ( userIDs , c . UserID )
}
usersMap := make ( map [ int64 ] * user_model . User )
if err := db . GetEngine ( ctx ) . In ( "id" , userIDs ) . Find ( & usersMap ) ; err != nil {
2024-03-04 11:16:03 +03:00
return nil , 0 , fmt . Errorf ( "Find users map by user ids: %w" , err )
2024-01-15 05:19:25 +03:00
}
for _ , c := range collaborations {
u := usersMap [ c . UserID ]
if u == nil {
u = user_model . NewGhostUser ( )
2022-05-11 13:09:36 +03:00
}
collaborators = append ( collaborators , & Collaborator {
2024-01-15 05:19:25 +03:00
User : u ,
2022-05-11 13:09:36 +03:00
Collaboration : c ,
} )
}
2024-03-04 11:16:03 +03:00
return collaborators , total , nil
2022-05-11 13:09:36 +03:00
}
// 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 } )
}
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
2022-12-10 05:46:31 +03:00
func ChangeCollaborationAccessMode ( ctx context . Context , repo * Repository , uid int64 , mode perm . AccessMode ) error {
2022-05-11 13:09:36 +03:00
// Discard invalid input
if mode <= perm . AccessModeNone || mode > perm . AccessModeOwner {
return nil
}
2023-01-08 04:34:58 +03:00
return db . WithTx ( ctx , func ( ctx context . Context ) error {
2022-12-10 05:46:31 +03:00
e := db . GetEngine ( ctx )
2022-05-11 13:09:36 +03:00
2022-12-10 05:46:31 +03:00
collaboration := & Collaboration {
RepoID : repo . ID ,
UserID : uid ,
}
has , err := e . Get ( collaboration )
if err != nil {
return fmt . Errorf ( "get collaboration: %w" , err )
} else if ! has {
return nil
}
2022-05-11 13:09:36 +03:00
2022-12-10 05:46:31 +03:00
if collaboration . Mode == mode {
return nil
}
collaboration . Mode = mode
if _ , err = e .
ID ( collaboration . ID ) .
Cols ( "mode" ) .
Update ( collaboration ) ; err != nil {
return fmt . Errorf ( "update collaboration: %w" , err )
} else if _ , err = e . Exec ( "UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?" , mode , uid , repo . ID ) ; err != nil {
return fmt . Errorf ( "update access table: %w" , err )
}
2022-05-11 13:09:36 +03:00
2022-12-10 05:46:31 +03:00
return nil
} )
2022-05-11 13:09:36 +03:00
}
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
2023-09-29 15:12:54 +03:00
func IsOwnerMemberCollaborator ( ctx context . Context , repo * Repository , userID int64 ) ( bool , error ) {
2022-06-12 18:51:54 +03:00
if repo . OwnerID == userID {
return true , nil
}
2023-09-29 15:12:54 +03:00
teamMember , err := db . GetEngine ( ctx ) . Join ( "INNER" , "team_repo" , "team_repo.team_id = team_user.team_id" ) .
2022-06-12 18:51:54 +03:00
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
}
2023-09-29 15:12:54 +03:00
return db . GetEngine ( ctx ) . Get ( & Collaboration { RepoID : repo . ID , UserID : userID } )
2022-06-12 18:51:54 +03:00
}