2014-02-18 03:38:50 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// 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
2015-02-24 08:27:22 +03:00
import (
"fmt"
2015-03-16 11:04:27 +03:00
"github.com/gogits/gogs/modules/log"
2015-02-24 08:27:22 +03:00
)
2015-02-05 16:29:08 +03:00
type AccessMode int
2014-06-25 08:44:48 +04:00
2014-02-17 19:57:23 +04:00
const (
2015-02-09 14:36:33 +03:00
ACCESS_MODE_NONE AccessMode = iota
ACCESS_MODE_READ
ACCESS_MODE_WRITE
ACCESS_MODE_ADMIN
ACCESS_MODE_OWNER
2014-02-17 19:57:23 +04: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)" `
Mode AccessMode
2014-02-17 19:57:23 +04:00
}
2015-02-23 10:15:53 +03:00
func accessLevel ( e Engine , u * User , repo * Repository ) ( AccessMode , error ) {
2015-02-09 14:36:33 +03:00
mode := ACCESS_MODE_NONE
2015-02-23 10:15:53 +03:00
if ! repo . IsPrivate {
2015-02-09 14:36:33 +03:00
mode = ACCESS_MODE_READ
2015-02-12 05:58:37 +03:00
}
2014-05-01 19:32:12 +04:00
2015-02-05 16:29:08 +03:00
if u != nil {
2015-08-08 17:43:14 +03:00
if u . Id == repo . OwnerID {
2015-02-09 14:36:33 +03:00
return ACCESS_MODE_OWNER , nil
2015-02-05 16:29:08 +03:00
}
2014-04-05 02:55:17 +04:00
2015-08-08 17:43:14 +03:00
a := & Access { UserID : u . Id , RepoID : repo . ID }
2015-02-23 10:15:53 +03:00
if has , err := e . Get ( a ) ; ! has || err != nil {
2015-02-05 16:29:08 +03:00
return mode , err
}
return a . Mode , nil
2014-04-12 05:47:39 +04:00
}
2015-02-05 16:29:08 +03:00
return mode , nil
2014-02-17 19:57:23 +04:00
}
2015-01-23 10:54:16 +03:00
2015-02-23 10:15:53 +03:00
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access. User can be nil!
func AccessLevel ( u * User , repo * Repository ) ( AccessMode , error ) {
return accessLevel ( x , u , repo )
}
func hasAccess ( e Engine , u * User , repo * Repository , testMode AccessMode ) ( bool , error ) {
mode , err := accessLevel ( e , u , repo )
2015-02-13 08:58:46 +03:00
return testMode <= mode , err
}
2015-02-23 10:15:53 +03:00
// HasAccess returns true if someone has the request access level. User can be nil!
func HasAccess ( u * User , repo * Repository , testMode AccessMode ) ( bool , error ) {
return hasAccess ( x , u , repo , testMode )
}
2015-01-23 10:54:16 +03:00
// GetAccessibleRepositories finds all repositories where a user has access to,
// besides his own.
2015-02-05 16:29:08 +03:00
func ( u * User ) GetAccessibleRepositories ( ) ( map [ * Repository ] AccessMode , error ) {
2015-01-23 10:54:16 +03:00
accesses := make ( [ ] * Access , 0 , 10 )
2015-02-05 16:29:08 +03:00
if err := x . Find ( & accesses , & Access { UserID : u . Id } ) ; err != nil {
2015-01-23 10:54:16 +03:00
return nil , err
}
2015-02-05 16:29:08 +03:00
repos := make ( map [ * Repository ] AccessMode , len ( accesses ) )
2015-01-23 10:54:16 +03:00
for _ , access := range accesses {
2015-08-08 17:43:14 +03:00
repo , err := GetRepositoryByID ( access . RepoID )
2015-01-23 10:54:16 +03:00
if err != nil {
2015-03-16 11:04:27 +03:00
if IsErrRepoNotExist ( err ) {
log . Error ( 4 , "%v" , err )
continue
}
2015-01-23 10:54:16 +03:00
return nil , err
}
2015-02-12 05:58:37 +03:00
if err = repo . GetOwner ( ) ; err != nil {
2015-02-04 17:08:55 +03:00
return nil , err
2015-08-08 17:43:14 +03:00
} else if repo . OwnerID == u . Id {
2015-02-12 05:58:37 +03:00
continue
2015-02-04 17:08:55 +03:00
}
2015-01-23 10:54:16 +03:00
repos [ repo ] = access . Mode
}
2015-03-01 05:50:04 +03:00
// FIXME: should we generate an ordered list here? Random looks weird.
2015-01-23 10:54:16 +03:00
return repos , nil
}
2015-02-05 16:29:08 +03:00
2015-02-13 08:58:46 +03:00
func maxAccessMode ( modes ... AccessMode ) AccessMode {
max := ACCESS_MODE_NONE
for _ , mode := range modes {
if mode > max {
max = mode
}
}
return max
}
2015-03-01 05:44:09 +03:00
// FIXME: do corss-comparison so reduce deletions and additions to the minimum?
func ( repo * Repository ) refreshAccesses ( e Engine , accessMap map [ int64 ] AccessMode ) ( err error ) {
minMode := ACCESS_MODE_READ
if ! repo . IsPrivate {
minMode = ACCESS_MODE_WRITE
}
newAccesses := make ( [ ] Access , 0 , len ( accessMap ) )
for userID , mode := range accessMap {
if mode < minMode {
continue
}
newAccesses = append ( newAccesses , Access {
UserID : userID ,
2015-08-08 17:43:14 +03:00
RepoID : repo . ID ,
2015-03-01 05:44:09 +03:00
Mode : mode ,
} )
}
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 )
} else if _ , err = e . Insert ( newAccesses ) ; err != nil {
return fmt . Errorf ( "insert new accesses: %v" , err )
}
2015-02-13 10:56:42 +03:00
return nil
}
2015-03-01 05:44:09 +03:00
// FIXME: should be able to have read-only access.
// Give all collaborators write access.
func ( repo * Repository ) refreshCollaboratorAccesses ( e Engine , accessMap map [ int64 ] AccessMode ) error {
2015-02-13 08:58:46 +03:00
collaborators , err := repo . getCollaborators ( e )
2015-02-05 16:29:08 +03:00
if err != nil {
2015-03-01 05:44:09 +03:00
return fmt . Errorf ( "getCollaborators: %v" , err )
2015-02-05 16:29:08 +03:00
}
for _ , c := range collaborators {
2015-02-09 14:36:33 +03:00
accessMap [ c . Id ] = ACCESS_MODE_WRITE
2015-02-05 16:29:08 +03:00
}
2015-07-14 17:56:02 +03:00
// Adds team members access.
if repo . Owner . IsOrganization ( ) {
if err = repo . Owner . GetTeams ( ) ; err != nil {
return fmt . Errorf ( "GetTeams: %v" , err )
}
for _ , t := range repo . Owner . Teams {
if err = t . GetMembers ( ) ; err != nil {
return fmt . Errorf ( "GetMembers: %v" , err )
}
for _ , m := range t . Members {
if t . IsOwnerTeam ( ) {
accessMap [ m . Id ] = ACCESS_MODE_OWNER
} else {
accessMap [ m . Id ] = maxAccessMode ( accessMap [ m . Id ] , t . Authorize )
}
}
}
}
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.
func ( repo * Repository ) recalculateTeamAccesses ( e Engine , ignTeamID int64 ) ( err error ) {
accessMap := make ( map [ int64 ] AccessMode , 20 )
if err = repo . getOwner ( e ) ; err != nil {
2015-02-05 16:29:08 +03:00
return err
}
2015-07-24 11:52:01 +03:00
if err = repo . refreshCollaboratorAccesses ( e , accessMap ) ; err != nil {
return fmt . Errorf ( "refreshCollaboratorAccesses: %v" , err )
}
2015-02-13 08:58:46 +03:00
if repo . Owner . IsOrganization ( ) {
if err = repo . Owner . getTeams ( e ) ; err != nil {
2015-02-05 16:29:08 +03:00
return err
}
2015-03-01 05:44:09 +03:00
for _ , t := range repo . Owner . Teams {
if t . ID == ignTeamID {
continue
}
2015-03-25 01:14:04 +03:00
// Owner team gets owner access, and skip for teams that do not
// have relations with repository.
2015-03-01 05:44:09 +03:00
if t . IsOwnerTeam ( ) {
t . Authorize = ACCESS_MODE_OWNER
2015-08-08 17:43:14 +03:00
} else if ! t . hasRepository ( e , repo . ID ) {
2015-03-25 01:14:04 +03:00
continue
2015-02-05 16:29:08 +03:00
}
2015-03-01 05:44:09 +03:00
if err = t . getMembers ( e ) ; err != nil {
return fmt . Errorf ( "getMembers '%d': %v" , t . ID , err )
2015-02-05 16:29:08 +03:00
}
2015-03-01 05:44:09 +03:00
for _ , m := range t . Members {
accessMap [ m . Id ] = maxAccessMode ( accessMap [ m . Id ] , t . Authorize )
2015-02-05 16:29:08 +03:00
}
}
}
2015-03-01 05:44:09 +03:00
return repo . refreshAccesses ( e , accessMap )
}
2015-02-05 16:29:08 +03:00
2015-03-01 05:44:09 +03:00
func ( repo * Repository ) recalculateAccesses ( e Engine ) error {
accessMap := make ( map [ int64 ] AccessMode , 20 )
if err := repo . refreshCollaboratorAccesses ( e , accessMap ) ; err != nil {
return fmt . Errorf ( "refreshCollaboratorAccesses: %v" , err )
2015-02-05 16:29:08 +03:00
}
2015-03-01 05:44:09 +03:00
return repo . refreshAccesses ( e , 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.
func ( r * Repository ) RecalculateAccesses ( ) error {
return r . recalculateAccesses ( x )
2015-02-05 16:29:08 +03:00
}