2019-11-15 11:06:11 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repository
import (
2020-01-31 18:49:04 +03:00
"fmt"
2019-11-15 11:06:11 +03:00
"code.gitea.io/gitea/models"
2021-12-10 04:27:50 +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-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2021-12-17 09:24:47 +03:00
"code.gitea.io/gitea/modules/log"
2019-11-15 11:06:11 +03:00
"code.gitea.io/gitea/modules/notification"
2020-01-15 06:14:50 +03:00
"code.gitea.io/gitea/modules/sync"
2019-11-15 11:06:11 +03:00
)
2020-01-15 06:14:50 +03:00
// repoWorkingPool represents a working pool to order the parallel changes to the same repository
var repoWorkingPool = sync . NewExclusivePool ( )
2019-11-15 11:06:11 +03:00
// TransferOwnership transfers all corresponding setting from old user to new one.
2021-12-10 04:27:50 +03:00
func TransferOwnership ( doer , newOwner * user_model . User , repo * repo_model . Repository , teams [ ] * models . Team ) error {
if err := repo . GetOwner ( db . DefaultContext ) ; err != nil {
2019-11-15 11:06:11 +03:00
return err
}
2020-01-31 18:49:04 +03:00
for _ , team := range teams {
if newOwner . ID != team . OrgID {
return fmt . Errorf ( "team %d does not belong to organization" , team . ID )
}
}
2019-11-15 11:06:11 +03:00
oldOwner := repo . Owner
2020-12-25 12:59:32 +03:00
repoWorkingPool . CheckIn ( fmt . Sprint ( repo . ID ) )
2020-01-31 18:49:04 +03:00
if err := models . TransferOwnership ( doer , newOwner . Name , repo ) ; err != nil {
2020-12-25 12:59:32 +03:00
repoWorkingPool . CheckOut ( fmt . Sprint ( repo . ID ) )
2019-11-15 11:06:11 +03:00
return err
}
2020-12-25 12:59:32 +03:00
repoWorkingPool . CheckOut ( fmt . Sprint ( repo . ID ) )
2019-11-15 11:06:11 +03:00
2021-12-10 04:27:50 +03:00
newRepo , err := repo_model . GetRepositoryByID ( repo . ID )
2020-01-31 18:49:04 +03:00
if err != nil {
return err
}
for _ , team := range teams {
if err := team . AddRepository ( newRepo ) ; err != nil {
return err
}
}
2019-11-15 11:06:11 +03:00
notification . NotifyTransferRepository ( doer , repo , oldOwner . Name )
return nil
}
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
2021-12-10 04:27:50 +03:00
func ChangeRepositoryName ( doer * user_model . User , repo * repo_model . Repository , newRepoName string ) error {
2021-12-17 09:24:47 +03:00
log . Trace ( "ChangeRepositoryName: %s/%s -> %s" , doer . Name , repo . Name , newRepoName )
2019-11-15 11:06:11 +03:00
oldRepoName := repo . Name
2019-12-06 07:00:50 +03:00
// Change repository directory name. We must lock the local copy of the
// repo so that we can atomically rename the repo path and updates the
// local copy's origin accordingly.
2019-11-15 11:06:11 +03:00
2020-12-25 12:59:32 +03:00
repoWorkingPool . CheckIn ( fmt . Sprint ( repo . ID ) )
2021-12-12 18:48:20 +03:00
if err := repo_model . ChangeRepositoryName ( doer , repo , newRepoName ) ; err != nil {
2020-12-25 12:59:32 +03:00
repoWorkingPool . CheckOut ( fmt . Sprint ( repo . ID ) )
2019-11-15 11:06:11 +03:00
return err
}
2020-12-25 12:59:32 +03:00
repoWorkingPool . CheckOut ( fmt . Sprint ( repo . ID ) )
2019-11-15 11:06:11 +03:00
2021-12-17 09:24:47 +03:00
repo . Name = newRepoName
2019-11-15 11:06:11 +03:00
notification . NotifyRenameRepository ( doer , repo , oldRepoName )
return nil
}
2021-03-01 03:47:30 +03:00
// StartRepositoryTransfer transfer a repo from one owner to a new one.
// it make repository into pending transfer state, if doer can not create repo for new owner.
2021-12-10 04:27:50 +03:00
func StartRepositoryTransfer ( doer , newOwner * user_model . User , repo * repo_model . Repository , teams [ ] * models . Team ) error {
2021-03-01 03:47:30 +03:00
if err := models . TestRepositoryReadyForTransfer ( repo . Status ) ; err != nil {
return err
}
// Admin is always allowed to transfer || user transfer repo back to his account
if doer . IsAdmin || doer . ID == newOwner . ID {
return TransferOwnership ( doer , newOwner , repo , teams )
}
// If new owner is an org and user can create repos he can transfer directly too
if newOwner . IsOrganization ( ) {
allowed , err := models . CanCreateOrgRepo ( newOwner . ID , doer . ID )
if err != nil {
return err
}
if allowed {
return TransferOwnership ( doer , newOwner , repo , teams )
}
}
2021-06-14 21:30:35 +03:00
// In case the new owner would not have sufficient access to the repo, give access rights for read
hasAccess , err := models . HasAccess ( newOwner . ID , repo )
if err != nil {
return err
}
if ! hasAccess {
2021-12-10 04:27:50 +03:00
if err := models . AddCollaborator ( repo , newOwner ) ; err != nil {
2021-06-14 21:30:35 +03:00
return err
}
2021-12-10 04:27:50 +03:00
if err := models . ChangeCollaborationAccessMode ( repo , newOwner . ID , perm . AccessModeRead ) ; err != nil {
2021-06-14 21:30:35 +03:00
return err
}
}
2021-03-01 03:47:30 +03:00
// Make repo as pending for transfer
2021-12-10 04:27:50 +03:00
repo . Status = repo_model . RepositoryPendingTransfer
2021-03-01 03:47:30 +03:00
if err := models . CreatePendingRepositoryTransfer ( doer , newOwner , repo . ID , teams ) ; err != nil {
return err
}
// notify users who are able to accept / reject transfer
notification . NotifyRepoPendingTransfer ( doer , newOwner , repo )
return nil
}