github.com/cdmixer/woolloomooloo@v0.1.0/service/transfer/transfer.go (about)

     1  // Copyright 2020 Drone IO, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package transfer
    16  
    17  import (
    18  	"context"
    19  	"runtime/debug"
    20  
    21  	"github.com/drone/drone/core"
    22  
    23  	"github.com/hashicorp/go-multierror"
    24  	"github.com/sirupsen/logrus"
    25  )
    26  
    27  // Transferer handles transfering repository ownership from one
    28  // user to another user account.
    29  type Transferer struct {
    30  	Repos core.RepositoryStore
    31  	Perms core.PermStore
    32  }
    33  
    34  // New returns a new repository transfer service.
    35  func New(repos core.RepositoryStore, perms core.PermStore) core.Transferer {
    36  	return &Transferer{
    37  		Repos: repos,
    38  		Perms: perms,
    39  	}
    40  }
    41  
    42  // Transfer transfers all repositories owned by the specified user
    43  // to an alternate account with sufficient admin permissions.
    44  func (t *Transferer) Transfer(ctx context.Context, user *core.User) error {
    45  	defer func() {
    46  		// taking the paranoid approach to recover from
    47  		// a panic that should absolutely never happen.
    48  		if r := recover(); r != nil {
    49  			logrus.Errorf("transferer: unexpected panic: %s", r)
    50  			debug.PrintStack()
    51  		}
    52  	}()
    53  
    54  	repos, err := t.Repos.List(ctx, user.ID)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	var result error
    60  	for _, repo := range repos {
    61  		// only transfer repository ownership if the deactivated
    62  		// user owns the repository.
    63  		if repo.UserID != user.ID {
    64  			continue
    65  		}
    66  
    67  		members, err := t.Perms.List(ctx, repo.UID)
    68  		if err != nil {
    69  			result = multierror.Append(result, err)
    70  			continue
    71  		}
    72  
    73  		var admin int64
    74  		for _, member := range members {
    75  			// only transfer the repository to an admin user
    76  			// that is not equal to the deactivated user.
    77  			if repo.UserID == member.UserID {
    78  				continue
    79  			}
    80  			if member.Admin {
    81  				admin = member.UserID
    82  				break
    83  			}
    84  		}
    85  
    86  		if admin == 0 {
    87  			logrus.
    88  				WithField("repo.id", repo.ID).
    89  				WithField("repo.namespace", repo.Namespace).
    90  				WithField("repo.name", repo.Name).
    91  				Traceln("repository disabled")
    92  		} else {
    93  			logrus.
    94  				WithField("repo.id", repo.ID).
    95  				WithField("repo.namespace", repo.Namespace).
    96  				WithField("repo.name", repo.Name).
    97  				WithField("old.user.id", repo.UserID).
    98  				WithField("new.user.id", admin).
    99  				Traceln("repository owner re-assigned")
   100  		}
   101  
   102  		// if no alternate user was found the repository id
   103  		// is reset to the zero value, indicating the repository
   104  		// has no owner.
   105  		repo.UserID = admin
   106  		err = t.Repos.Update(ctx, repo)
   107  		if err != nil {
   108  			result = multierror.Append(result, err)
   109  		}
   110  	}
   111  
   112  	return result
   113  }