From 8ad26976114c4fed6269a40e52632d065167bd20 Mon Sep 17 00:00:00 2001 From: David Svantesson Date: Tue, 15 Oct 2019 02:55:21 +0200 Subject: [PATCH] Recalculate repository access only for specific user (#8481) * Recalculate repository access only for specific user Signed-off-by: David Svantesson * Handle user repositories as well, and only add access if minimum mode * Need to get repo owner to check if organization --- models/access.go | 49 ++++++++++++++++++++++++++++++++++++ models/org_team.go | 4 +-- models/repo_collaboration.go | 19 +++++++++----- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/models/access.go b/models/access.go index 3cdfc62f217..213efe08a68 100644 --- a/models/access.go +++ b/models/access.go @@ -246,6 +246,55 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err return repo.refreshAccesses(e, accessMap) } +// recalculateUserAccess recalculates new access for a single user +// Usable if we know access only affected one user +func (repo *Repository) recalculateUserAccess(e Engine, uid int64) (err error) { + minMode := AccessModeRead + if !repo.IsPrivate { + minMode = AccessModeWrite + } + + accessMode := AccessModeNone + collaborator, err := repo.getCollaboration(e, uid) + if err != nil { + return err + } else if collaborator != nil { + accessMode = collaborator.Mode + } + + if err = repo.getOwner(e); err != nil { + return err + } else if repo.Owner.IsOrganization() { + var teams []Team + if err := e.Join("INNER", "team_repo", "team_repo.team_id = team.id"). + Join("INNER", "team_user", "team_user.team_id = team.id"). + Where("team.org_id = ?", repo.OwnerID). + And("team_repo.repo_id=?", repo.ID). + And("team_user.uid=?", uid). + Find(&teams); err != nil { + return err + } + + for _, t := range teams { + if t.IsOwnerTeam() { + t.Authorize = AccessModeOwner + } + + accessMode = maxAccessMode(accessMode, t.Authorize) + } + } + + // Delete old user accesses and insert new one for repository. + if _, err = e.Delete(&Access{RepoID: repo.ID, UserID: uid}); err != nil { + return fmt.Errorf("delete old user accesses: %v", err) + } else if accessMode >= minMode { + if _, err = e.Insert(&Access{RepoID: repo.ID, UserID: uid, Mode: accessMode}); err != nil { + return fmt.Errorf("insert new user accesses: %v", err) + } + } + return nil +} + func (repo *Repository) recalculateAccesses(e Engine) error { if repo.Owner.IsOrganization() { return repo.recalculateTeamAccesses(e, 0) diff --git a/models/org_team.go b/models/org_team.go index 9170ea2c2af..10d53e3a860 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -723,7 +723,7 @@ func AddTeamMember(team *Team, userID int64) error { // Give access to team repositories. for _, repo := range team.Repos { - if err := repo.recalculateTeamAccesses(sess, 0); err != nil { + if err := repo.recalculateUserAccess(sess, userID); err != nil { return err } if setting.Service.AutoWatchNewRepos { @@ -768,7 +768,7 @@ func removeTeamMember(e *xorm.Session, team *Team, userID int64) error { // Delete access to team repositories. for _, repo := range team.Repos { - if err := repo.recalculateTeamAccesses(e, 0); err != nil { + if err := repo.recalculateUserAccess(e, userID); err != nil { return err } diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index 40ddf6a28cf..3d6447c1963 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -41,12 +41,7 @@ func (repo *Repository) AddCollaborator(u *User) error { return err } - if repo.Owner.IsOrganization() { - err = repo.recalculateTeamAccesses(sess, 0) - } else { - err = repo.recalculateAccesses(sess) - } - if err != nil { + if err = repo.recalculateUserAccess(sess, u.ID); err != nil { return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err) } @@ -89,6 +84,18 @@ func (repo *Repository) GetCollaborators() ([]*Collaborator, error) { return repo.getCollaborators(x) } +func (repo *Repository) getCollaboration(e Engine, uid int64) (*Collaboration, error) { + collaboration := &Collaboration{ + RepoID: repo.ID, + UserID: uid, + } + has, err := e.Get(collaboration) + if !has { + collaboration = nil + } + return collaboration, err +} + func (repo *Repository) isCollaborator(e Engine, userID int64) (bool, error) { return e.Get(&Collaboration{RepoID: repo.ID, UserID: userID}) }