2014-02-18 03:38:50 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-04-22 23:40:51 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2014-02-18 03:38:50 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-02-17 19:57:23 +04:00
package models
2019-04-22 23:40:51 +03:00
import (
2021-12-10 04:27:50 +03:00
"context"
2019-04-22 23:40:51 +03:00
"fmt"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2022-03-29 09:29:02 +03:00
"code.gitea.io/gitea/models/organization"
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"
2019-04-22 23:40:51 +03:00
)
2015-02-24 08:27:22 +03:00
2015-02-05 16:29:08 +03:00
// Access represents the highest access level of a user to the repository. The only access type
// that is not in this table is the real owner of a repository. In case of an organization
// repository, the members of the owners team are in this table.
type Access struct {
ID int64 ` xorm:"pk autoincr" `
UserID int64 ` xorm:"UNIQUE(s)" `
RepoID int64 ` xorm:"UNIQUE(s)" `
2021-11-28 14:58:28 +03:00
Mode perm . AccessMode
2014-02-17 19:57:23 +04:00
}
2021-09-19 14:49:59 +03:00
func init ( ) {
db . RegisterModel ( new ( Access ) )
}
2021-12-10 04:27:50 +03:00
func accessLevel ( e db . Engine , user * user_model . User , repo * repo_model . Repository ) ( perm . AccessMode , error ) {
2021-11-28 14:58:28 +03:00
mode := perm . AccessModeNone
2020-01-13 20:33:46 +03:00
var userID int64
restricted := false
if user != nil {
userID = user . ID
restricted = user . IsRestricted
}
if ! restricted && ! repo . IsPrivate {
2021-11-28 14:58:28 +03:00
mode = perm . AccessModeRead
2015-02-12 05:58:37 +03:00
}
2014-05-01 19:32:12 +04:00
2017-03-15 03:51:46 +03:00
if userID == 0 {
2015-11-19 19:40:00 +03:00
return mode , nil
}
2014-04-05 02:55:17 +04:00
2017-03-15 03:51:46 +03:00
if userID == repo . OwnerID {
2021-11-28 14:58:28 +03:00
return perm . AccessModeOwner , nil
2014-04-12 05:47:39 +04:00
}
2015-02-05 16:29:08 +03:00
2017-03-15 03:51:46 +03:00
a := & Access { UserID : userID , RepoID : repo . ID }
2015-11-19 19:40:00 +03:00
if has , err := e . Get ( a ) ; ! has || err != nil {
return mode , err
}
return a . Mode , nil
2014-02-17 19:57:23 +04:00
}
2015-01-23 10:54:16 +03:00
2021-11-28 14:58:28 +03:00
func maxAccessMode ( modes ... perm . AccessMode ) perm . AccessMode {
max := perm . AccessModeNone
2015-02-13 08:58:46 +03:00
for _ , mode := range modes {
if mode > max {
max = mode
}
}
return max
}
2020-01-13 20:33:46 +03:00
type userAccess struct {
2021-11-24 12:49:20 +03:00
User * user_model . User
2021-11-28 14:58:28 +03:00
Mode perm . AccessMode
2020-01-13 20:33:46 +03:00
}
// updateUserAccess updates an access map so that user has at least mode
2021-11-28 14:58:28 +03:00
func updateUserAccess ( accessMap map [ int64 ] * userAccess , user * user_model . User , mode perm . AccessMode ) {
2020-01-13 20:33:46 +03:00
if ua , ok := accessMap [ user . ID ] ; ok {
ua . Mode = maxAccessMode ( ua . Mode , mode )
} else {
accessMap [ user . ID ] = & userAccess { User : user , Mode : mode }
}
}
2017-01-05 03:50:34 +03:00
// FIXME: do cross-comparison so reduce deletions and additions to the minimum?
2021-12-10 04:27:50 +03:00
func refreshAccesses ( e db . Engine , repo * repo_model . Repository , accessMap map [ int64 ] * userAccess ) ( err error ) {
2021-11-28 14:58:28 +03:00
minMode := perm . AccessModeRead
2015-03-01 05:44:09 +03:00
if ! repo . IsPrivate {
2021-11-28 14:58:28 +03:00
minMode = perm . AccessModeWrite
2015-03-01 05:44:09 +03:00
}
newAccesses := make ( [ ] Access , 0 , len ( accessMap ) )
2020-01-13 20:33:46 +03:00
for userID , ua := range accessMap {
if ua . Mode < minMode && ! ua . User . IsRestricted {
2015-03-01 05:44:09 +03:00
continue
}
2020-01-13 20:33:46 +03:00
2015-03-01 05:44:09 +03:00
newAccesses = append ( newAccesses , Access {
UserID : userID ,
2015-08-08 17:43:14 +03:00
RepoID : repo . ID ,
2020-01-13 20:33:46 +03:00
Mode : ua . Mode ,
2015-03-01 05:44:09 +03:00
} )
}
2015-02-13 10:56:42 +03:00
2015-03-01 05:44:09 +03:00
// Delete old accesses and insert new ones for repository.
2015-08-08 17:43:14 +03:00
if _ , err = e . Delete ( & Access { RepoID : repo . ID } ) ; err != nil {
2015-03-01 05:44:09 +03:00
return fmt . Errorf ( "delete old accesses: %v" , err )
2020-03-22 18:12:55 +03:00
}
if len ( newAccesses ) == 0 {
return nil
}
if _ , err = e . Insert ( newAccesses ) ; err != nil {
2015-03-01 05:44:09 +03:00
return fmt . Errorf ( "insert new accesses: %v" , err )
}
2015-02-13 10:56:42 +03:00
return nil
}
2016-03-06 02:08:42 +03:00
// refreshCollaboratorAccesses retrieves repository collaborations with their access modes.
2021-12-10 04:27:50 +03:00
func refreshCollaboratorAccesses ( e db . Engine , repoID int64 , accessMap map [ int64 ] * userAccess ) error {
collaborators , err := getCollaborators ( e , repoID , db . ListOptions { } )
2015-02-05 16:29:08 +03:00
if err != nil {
2016-03-06 02:08:42 +03:00
return fmt . Errorf ( "getCollaborations: %v" , err )
2015-02-05 16:29:08 +03:00
}
2020-01-13 20:33:46 +03:00
for _ , c := range collaborators {
2021-09-27 21:07:19 +03:00
if c . User . IsGhost ( ) {
continue
}
2020-01-13 20:33:46 +03:00
updateUserAccess ( accessMap , c . User , c . Collaboration . Mode )
2015-02-05 16:29:08 +03:00
}
2015-03-01 05:44:09 +03:00
return nil
}
// recalculateTeamAccesses recalculates new accesses for teams of an organization
// except the team whose ID is given. It is used to assign a team ID when
// remove repository from that team.
2021-12-10 04:27:50 +03:00
func recalculateTeamAccesses ( ctx context . Context , repo * repo_model . Repository , ignTeamID int64 ) ( err error ) {
2020-01-13 20:33:46 +03:00
accessMap := make ( map [ int64 ] * userAccess , 20 )
2015-03-01 05:44:09 +03:00
2021-12-10 04:27:50 +03:00
if err = repo . GetOwner ( ctx ) ; err != nil {
2015-02-05 16:29:08 +03:00
return err
2015-08-28 08:51:15 +03:00
} else if ! repo . Owner . IsOrganization ( ) {
return fmt . Errorf ( "owner is not an organization: %d" , repo . OwnerID )
2015-02-05 16:29:08 +03:00
}
2015-08-28 08:51:15 +03:00
2021-12-10 04:27:50 +03:00
e := db . GetEngine ( ctx )
if err = refreshCollaboratorAccesses ( e , repo . ID , accessMap ) ; err != nil {
2015-07-24 11:52:01 +03:00
return fmt . Errorf ( "refreshCollaboratorAccesses: %v" , err )
}
2015-02-05 16:29:08 +03:00
2022-03-29 09:29:02 +03:00
teams , err := organization . FindOrgTeams ( ctx , repo . Owner . ID )
2021-11-19 14:41:40 +03:00
if err != nil {
2015-08-28 08:51:15 +03:00
return err
}
2015-03-25 01:14:04 +03:00
2021-11-19 14:41:40 +03:00
for _ , t := range teams {
2015-08-28 08:51:15 +03:00
if t . ID == ignTeamID {
continue
}
2015-02-05 16:29:08 +03:00
2015-08-28 08:51:15 +03:00
// Owner team gets owner access, and skip for teams that do not
// have relations with repository.
if t . IsOwnerTeam ( ) {
2022-01-05 06:37:00 +03:00
t . AccessMode = perm . AccessModeOwner
2022-03-29 09:29:02 +03:00
} else if ! hasRepository ( ctx , t , repo . ID ) {
2015-08-28 08:51:15 +03:00
continue
}
2022-03-29 09:29:02 +03:00
if err = t . GetMembersCtx ( ctx ) ; err != nil {
2015-08-28 08:51:15 +03:00
return fmt . Errorf ( "getMembers '%d': %v" , t . ID , err )
}
for _ , m := range t . Members {
2022-01-05 06:37:00 +03:00
updateUserAccess ( accessMap , m , t . AccessMode )
2015-02-05 16:29:08 +03:00
}
}
2021-12-10 04:27:50 +03:00
return refreshAccesses ( e , repo , accessMap )
2015-03-01 05:44:09 +03:00
}
2015-02-05 16:29:08 +03:00
2019-10-15 03:55:21 +03:00
// recalculateUserAccess recalculates new access for a single user
// Usable if we know access only affected one user
2021-12-10 04:27:50 +03:00
func recalculateUserAccess ( ctx context . Context , repo * repo_model . Repository , uid int64 ) ( err error ) {
2021-11-28 14:58:28 +03:00
minMode := perm . AccessModeRead
2019-10-15 03:55:21 +03:00
if ! repo . IsPrivate {
2021-11-28 14:58:28 +03:00
minMode = perm . AccessModeWrite
2019-10-15 03:55:21 +03:00
}
2021-11-28 14:58:28 +03:00
accessMode := perm . AccessModeNone
2021-12-10 04:27:50 +03:00
e := db . GetEngine ( ctx )
collaborator , err := getCollaboration ( e , repo . ID , uid )
2019-10-15 03:55:21 +03:00
if err != nil {
return err
} else if collaborator != nil {
accessMode = collaborator . Mode
}
2021-12-10 04:27:50 +03:00
if err = repo . GetOwner ( ctx ) ; err != nil {
2019-10-15 03:55:21 +03:00
return err
} else if repo . Owner . IsOrganization ( ) {
2022-03-29 09:29:02 +03:00
var teams [ ] organization . Team
2019-10-15 03:55:21 +03:00
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 ( ) {
2022-01-05 06:37:00 +03:00
t . AccessMode = perm . AccessModeOwner
2019-10-15 03:55:21 +03:00
}
2022-01-05 06:37:00 +03:00
accessMode = maxAccessMode ( accessMode , t . AccessMode )
2019-10-15 03:55:21 +03:00
}
}
// 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
}
2021-12-10 04:27:50 +03:00
func recalculateAccesses ( ctx context . Context , repo * repo_model . Repository ) error {
2015-08-28 08:51:15 +03:00
if repo . Owner . IsOrganization ( ) {
2021-12-10 04:27:50 +03:00
return recalculateTeamAccesses ( ctx , repo , 0 )
2015-08-28 08:51:15 +03:00
}
2021-12-10 04:27:50 +03:00
e := db . GetEngine ( ctx )
2020-01-13 20:33:46 +03:00
accessMap := make ( map [ int64 ] * userAccess , 20 )
2021-12-10 04:27:50 +03:00
if err := refreshCollaboratorAccesses ( e , repo . ID , accessMap ) ; err != nil {
2015-03-01 05:44:09 +03:00
return fmt . Errorf ( "refreshCollaboratorAccesses: %v" , err )
2015-02-05 16:29:08 +03:00
}
2021-12-10 04:27:50 +03:00
return refreshAccesses ( e , repo , accessMap )
2015-02-13 08:58:46 +03:00
}
2015-02-05 16:29:08 +03:00
2015-02-13 08:58:46 +03:00
// RecalculateAccesses recalculates all accesses for repository.
2021-12-10 04:27:50 +03:00
func RecalculateAccesses ( repo * repo_model . Repository ) error {
return recalculateAccesses ( db . DefaultContext , repo )
2015-02-05 16:29:08 +03:00
}