2018-11-28 19:26:14 +08:00
// Copyright 2018 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 models
2019-04-22 21:40:51 +01:00
import (
2021-12-10 09:27:50 +08:00
"context"
2019-04-22 21:40:51 +01:00
"fmt"
2021-09-19 19:49:59 +08:00
"code.gitea.io/gitea/models/db"
2021-11-28 19:58:28 +08:00
perm_model "code.gitea.io/gitea/models/perm"
2021-12-10 09:27:50 +08:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-10 03:57:58 +08:00
"code.gitea.io/gitea/models/unit"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2019-04-22 21:40:51 +01:00
"code.gitea.io/gitea/modules/log"
)
2018-11-28 19:26:14 +08:00
// Permission contains all the permissions related variables to a repository for a user
type Permission struct {
2021-11-28 19:58:28 +08:00
AccessMode perm_model . AccessMode
2021-12-10 09:27:50 +08:00
Units [ ] * repo_model . RepoUnit
2021-11-28 19:58:28 +08:00
UnitsMode map [ unit . Type ] perm_model . AccessMode
2018-11-28 19:26:14 +08:00
}
// IsOwner returns true if current user is the owner of repository.
func ( p * Permission ) IsOwner ( ) bool {
2021-11-28 19:58:28 +08:00
return p . AccessMode >= perm_model . AccessModeOwner
2018-11-28 19:26:14 +08:00
}
// IsAdmin returns true if current user has admin or higher access of repository.
func ( p * Permission ) IsAdmin ( ) bool {
2021-11-28 19:58:28 +08:00
return p . AccessMode >= perm_model . AccessModeAdmin
2018-11-28 19:26:14 +08:00
}
// HasAccess returns true if the current user has at least read access to any unit of this repository
func ( p * Permission ) HasAccess ( ) bool {
if p . UnitsMode == nil {
2021-11-28 19:58:28 +08:00
return p . AccessMode >= perm_model . AccessModeRead
2018-11-28 19:26:14 +08:00
}
return len ( p . UnitsMode ) > 0
}
// UnitAccessMode returns current user accessmode to the specify unit of the repository
2021-11-28 19:58:28 +08:00
func ( p * Permission ) UnitAccessMode ( unitType unit . Type ) perm_model . AccessMode {
2018-11-28 19:26:14 +08:00
if p . UnitsMode == nil {
for _ , u := range p . Units {
if u . Type == unitType {
return p . AccessMode
}
}
2021-11-28 19:58:28 +08:00
return perm_model . AccessModeNone
2018-11-28 19:26:14 +08:00
}
return p . UnitsMode [ unitType ]
}
// CanAccess returns true if user has mode access to the unit of the repository
2021-11-28 19:58:28 +08:00
func ( p * Permission ) CanAccess ( mode perm_model . AccessMode , unitType unit . Type ) bool {
2018-11-28 19:26:14 +08:00
return p . UnitAccessMode ( unitType ) >= mode
}
// CanAccessAny returns true if user has mode access to any of the units of the repository
2021-11-28 19:58:28 +08:00
func ( p * Permission ) CanAccessAny ( mode perm_model . AccessMode , unitTypes ... unit . Type ) bool {
2018-11-28 19:26:14 +08:00
for _ , u := range unitTypes {
if p . CanAccess ( mode , u ) {
return true
}
}
return false
}
// CanRead returns true if user could read to this unit
2021-11-10 03:57:58 +08:00
func ( p * Permission ) CanRead ( unitType unit . Type ) bool {
2021-11-28 19:58:28 +08:00
return p . CanAccess ( perm_model . AccessModeRead , unitType )
2018-11-28 19:26:14 +08:00
}
// CanReadAny returns true if user has read access to any of the units of the repository
2021-11-10 03:57:58 +08:00
func ( p * Permission ) CanReadAny ( unitTypes ... unit . Type ) bool {
2021-11-28 19:58:28 +08:00
return p . CanAccessAny ( perm_model . AccessModeRead , unitTypes ... )
2018-11-28 19:26:14 +08:00
}
// CanReadIssuesOrPulls returns true if isPull is true and user could read pull requests and
// returns true if isPull is false and user could read to issues
func ( p * Permission ) CanReadIssuesOrPulls ( isPull bool ) bool {
if isPull {
2021-11-10 03:57:58 +08:00
return p . CanRead ( unit . TypePullRequests )
2018-11-28 19:26:14 +08:00
}
2021-11-10 03:57:58 +08:00
return p . CanRead ( unit . TypeIssues )
2018-11-28 19:26:14 +08:00
}
// CanWrite returns true if user could write to this unit
2021-11-10 03:57:58 +08:00
func ( p * Permission ) CanWrite ( unitType unit . Type ) bool {
2021-11-28 19:58:28 +08:00
return p . CanAccess ( perm_model . AccessModeWrite , unitType )
2018-11-28 19:26:14 +08:00
}
// CanWriteIssuesOrPulls returns true if isPull is true and user could write to pull requests and
// returns true if isPull is false and user could write to issues
func ( p * Permission ) CanWriteIssuesOrPulls ( isPull bool ) bool {
if isPull {
2021-11-10 03:57:58 +08:00
return p . CanWrite ( unit . TypePullRequests )
2018-11-28 19:26:14 +08:00
}
2021-11-10 03:57:58 +08:00
return p . CanWrite ( unit . TypeIssues )
2018-11-28 19:26:14 +08:00
}
2019-04-22 21:40:51 +01:00
// ColorFormat writes a colored string for these Permissions
func ( p * Permission ) ColorFormat ( s fmt . State ) {
noColor := log . ColorBytes ( log . Reset )
2021-11-28 19:58:28 +08:00
format := "perm_model.AccessMode: %-v, %d Units, %d UnitsMode(s): [ "
2019-04-22 21:40:51 +01:00
args := [ ] interface { } {
p . AccessMode ,
log . NewColoredValueBytes ( len ( p . Units ) , & noColor ) ,
log . NewColoredValueBytes ( len ( p . UnitsMode ) , & noColor ) ,
}
if s . Flag ( '+' ) {
for i , unit := range p . Units {
config := ""
if unit . Config != nil {
configBytes , err := unit . Config . ToDB ( )
config = string ( configBytes )
if err != nil {
2019-07-23 20:50:39 +02:00
config = err . Error ( )
2019-04-22 21:40:51 +01:00
}
}
format += "\nUnits[%d]: ID: %d RepoID: %d Type: %-v Config: %s"
args = append ( args ,
log . NewColoredValueBytes ( i , & noColor ) ,
log . NewColoredIDValue ( unit . ID ) ,
log . NewColoredIDValue ( unit . RepoID ) ,
unit . Type ,
config )
}
for key , value := range p . UnitsMode {
format += "\nUnitMode[%-v]: %-v"
args = append ( args ,
key ,
value )
}
} else {
format += "..."
}
format += " ]"
log . ColorFprintf ( s , format , args ... )
}
2018-11-28 19:26:14 +08:00
// GetUserRepoPermission returns the user permissions to the repository
2021-12-10 09:27:50 +08:00
func GetUserRepoPermission ( repo * repo_model . Repository , user * user_model . User ) ( Permission , error ) {
return getUserRepoPermission ( db . DefaultContext , repo , user )
2018-11-28 19:26:14 +08:00
}
2021-12-10 09:27:50 +08:00
func getUserRepoPermission ( ctx context . Context , repo * repo_model . Repository , user * user_model . User ) ( perm Permission , err error ) {
2019-04-22 21:40:51 +01:00
if log . IsTrace ( ) {
defer func ( ) {
if user == nil {
log . Trace ( "Permission Loaded for anonymous user in %-v:\nPermissions: %-+v" ,
repo ,
perm )
return
}
log . Trace ( "Permission Loaded for %-v in %-v:\nPermissions: %-+v" ,
user ,
repo ,
perm )
} ( )
}
2018-11-28 19:26:14 +08:00
// anonymous user visit private repo.
// TODO: anonymous user visit public unit of private repo???
if user == nil && repo . IsPrivate {
2021-11-28 19:58:28 +08:00
perm . AccessMode = perm_model . AccessModeNone
2018-11-28 19:26:14 +08:00
return
}
2021-12-10 09:27:50 +08:00
e := db . GetEngine ( ctx )
var is bool
2019-05-16 11:48:40 -04:00
if user != nil {
2021-12-10 09:27:50 +08:00
is , err = isCollaborator ( e , repo . ID , user . ID )
2019-05-16 11:48:40 -04:00
if err != nil {
return perm , err
}
}
2021-12-10 09:27:50 +08:00
if err = repo . GetOwner ( ctx ) ; err != nil {
2020-01-12 17:36:21 +08:00
return
}
2021-07-08 07:38:13 -04:00
// Prevent strangers from checking out public repo of private organization/users
2021-06-26 22:53:14 +03:00
// Allow user if they are collaborator of a repo within a private user or a private organization but not a member of the organization itself
2021-12-10 09:27:50 +08:00
if ! hasOrgOrUserVisible ( e , repo . Owner , user ) && ! is {
2021-11-28 19:58:28 +08:00
perm . AccessMode = perm_model . AccessModeNone
2019-04-26 02:59:10 +08:00
return
}
2021-12-10 09:27:50 +08:00
if err = repo . LoadUnits ( ctx ) ; err != nil {
2018-11-28 19:26:14 +08:00
return
}
perm . Units = repo . Units
// anonymous visit public repo
if user == nil {
2021-11-28 19:58:28 +08:00
perm . AccessMode = perm_model . AccessModeRead
2018-11-28 19:26:14 +08:00
return
}
// Admin or the owner has super access to the repository
if user . IsAdmin || user . ID == repo . OwnerID {
2021-11-28 19:58:28 +08:00
perm . AccessMode = perm_model . AccessModeOwner
2018-11-28 19:26:14 +08:00
return
}
// plain user
2020-01-13 19:33:46 +02:00
perm . AccessMode , err = accessLevel ( e , user , repo )
2018-11-28 19:26:14 +08:00
if err != nil {
return
}
2021-12-10 09:27:50 +08:00
if err = repo . GetOwner ( ctx ) ; err != nil {
2018-11-28 19:26:14 +08:00
return
}
if ! repo . Owner . IsOrganization ( ) {
return
}
2021-11-28 19:58:28 +08:00
perm . UnitsMode = make ( map [ unit . Type ] perm_model . AccessMode )
2018-11-28 19:26:14 +08:00
// Collaborators on organization
2021-12-10 09:27:50 +08:00
if is {
2018-11-28 19:26:14 +08:00
for _ , u := range repo . Units {
perm . UnitsMode [ u . Type ] = perm . AccessMode
}
}
// get units mode from teams
teams , err := getUserRepoTeams ( e , repo . OwnerID , user . ID , repo . ID )
if err != nil {
return
}
2019-02-23 00:14:45 +08:00
// if user in an owner team
for _ , team := range teams {
2022-01-05 11:37:00 +08:00
if team . AccessMode >= perm_model . AccessModeAdmin {
2021-11-28 19:58:28 +08:00
perm . AccessMode = perm_model . AccessModeOwner
2019-02-23 00:14:45 +08:00
perm . UnitsMode = nil
return
}
}
2018-11-28 19:26:14 +08:00
for _ , u := range repo . Units {
var found bool
for _ , team := range teams {
2022-01-05 11:37:00 +08:00
teamMode := team . unitAccessMode ( e , u . Type )
if teamMode > perm_model . AccessModeNone {
2018-11-28 19:26:14 +08:00
m := perm . UnitsMode [ u . Type ]
2022-01-05 11:37:00 +08:00
if m < teamMode {
perm . UnitsMode [ u . Type ] = teamMode
2018-11-28 19:26:14 +08:00
}
found = true
}
}
2020-01-13 19:33:46 +02:00
// for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
if ! found && ! repo . IsPrivate && ! user . IsRestricted {
2018-11-28 19:26:14 +08:00
if _ , ok := perm . UnitsMode [ u . Type ] ; ! ok {
2021-11-28 19:58:28 +08:00
perm . UnitsMode [ u . Type ] = perm_model . AccessModeRead
2018-11-28 19:26:14 +08:00
}
}
}
// remove no permission units
2021-12-10 09:27:50 +08:00
perm . Units = make ( [ ] * repo_model . RepoUnit , 0 , len ( repo . Units ) )
2018-11-28 19:26:14 +08:00
for t := range perm . UnitsMode {
for _ , u := range repo . Units {
if u . Type == t {
perm . Units = append ( perm . Units , u )
}
}
}
return
}
2020-11-28 23:52:29 +08:00
// IsUserRealRepoAdmin check if this user is real repo admin
2021-12-10 09:27:50 +08:00
func IsUserRealRepoAdmin ( repo * repo_model . Repository , user * user_model . User ) ( bool , error ) {
2020-11-28 23:52:29 +08:00
if repo . OwnerID == user . ID {
return true , nil
}
2021-12-10 09:27:50 +08:00
if err := repo . GetOwner ( db . DefaultContext ) ; err != nil {
2020-11-28 23:52:29 +08:00
return false , err
}
2021-12-10 09:27:50 +08:00
accessMode , err := accessLevel ( db . GetEngine ( db . DefaultContext ) , user , repo )
2020-11-28 23:52:29 +08:00
if err != nil {
return false , err
}
2021-11-28 19:58:28 +08:00
return accessMode >= perm_model . AccessModeAdmin , nil
2020-11-28 23:52:29 +08:00
}
2020-01-11 08:29:34 +01:00
// IsUserRepoAdmin return true if user has admin right of a repo
2021-12-10 09:27:50 +08:00
func IsUserRepoAdmin ( repo * repo_model . Repository , user * user_model . User ) ( bool , error ) {
2021-09-23 16:45:36 +01:00
return isUserRepoAdmin ( db . GetEngine ( db . DefaultContext ) , repo , user )
2018-11-28 19:26:14 +08:00
}
2021-12-10 09:27:50 +08:00
func isUserRepoAdmin ( e db . Engine , repo * repo_model . Repository , user * user_model . User ) ( bool , error ) {
2018-11-28 19:26:14 +08:00
if user == nil || repo == nil {
return false , nil
}
if user . IsAdmin {
return true , nil
}
2020-01-13 19:33:46 +02:00
mode , err := accessLevel ( e , user , repo )
2018-11-28 19:26:14 +08:00
if err != nil {
return false , err
}
2021-11-28 19:58:28 +08:00
if mode >= perm_model . AccessModeAdmin {
2018-11-28 19:26:14 +08:00
return true , nil
}
teams , err := getUserRepoTeams ( e , repo . OwnerID , user . ID , repo . ID )
if err != nil {
return false , err
}
for _ , team := range teams {
2022-01-05 11:37:00 +08:00
if team . AccessMode >= perm_model . AccessModeAdmin {
2018-11-28 19:26:14 +08:00
return true , nil
}
}
return false , nil
}
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access.
2021-12-10 09:27:50 +08:00
func AccessLevel ( user * user_model . User , repo * repo_model . Repository ) ( perm_model . AccessMode , error ) {
return accessLevelUnit ( db . DefaultContext , user , repo , unit . TypeCode )
2018-11-28 19:26:14 +08:00
}
2019-10-28 10:11:50 +08:00
// AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the
// user does not have access.
2021-12-10 09:27:50 +08:00
func AccessLevelUnit ( user * user_model . User , repo * repo_model . Repository , unitType unit . Type ) ( perm_model . AccessMode , error ) {
return accessLevelUnit ( db . DefaultContext , user , repo , unitType )
2019-10-28 10:11:50 +08:00
}
2021-12-10 09:27:50 +08:00
func accessLevelUnit ( ctx context . Context , user * user_model . User , repo * repo_model . Repository , unitType unit . Type ) ( perm_model . AccessMode , error ) {
perm , err := getUserRepoPermission ( ctx , repo , user )
2018-11-28 19:26:14 +08:00
if err != nil {
2021-11-28 19:58:28 +08:00
return perm_model . AccessModeNone , err
2018-11-28 19:26:14 +08:00
}
2019-03-24 10:08:45 +01:00
return perm . UnitAccessMode ( unitType ) , nil
2018-11-28 19:26:14 +08:00
}
2021-12-10 09:27:50 +08:00
func hasAccessUnit ( ctx context . Context , user * user_model . User , repo * repo_model . Repository , unitType unit . Type , testMode perm_model . AccessMode ) ( bool , error ) {
mode , err := accessLevelUnit ( ctx , user , repo , unitType )
2018-11-28 19:26:14 +08:00
return testMode <= mode , err
}
2021-07-08 07:38:13 -04:00
// HasAccessUnit returns true if user has testMode to the unit of the repository
2021-12-10 09:27:50 +08:00
func HasAccessUnit ( user * user_model . User , repo * repo_model . Repository , unitType unit . Type , testMode perm_model . AccessMode ) ( bool , error ) {
return hasAccessUnit ( db . DefaultContext , user , repo , unitType , testMode )
2018-11-28 19:26:14 +08:00
}
2019-10-25 16:46:37 +02:00
// CanBeAssigned return true if user can be assigned to issue or pull requests in repo
// Currently any write access (code, issues or pr's) is assignable, to match assignee list in user interface.
2018-11-28 19:26:14 +08:00
// FIXME: user could send PullRequest also could be assigned???
2021-12-10 09:27:50 +08:00
func CanBeAssigned ( user * user_model . User , repo * repo_model . Repository , isPull bool ) ( bool , error ) {
return canBeAssigned ( db . DefaultContext , user , repo , isPull )
2020-04-07 23:52:01 +02:00
}
2021-12-10 09:27:50 +08:00
func canBeAssigned ( ctx context . Context , user * user_model . User , repo * repo_model . Repository , _ bool ) ( bool , error ) {
2019-10-25 16:46:37 +02:00
if user . IsOrganization ( ) {
return false , fmt . Errorf ( "Organization can't be added as assignee [user_id: %d, repo_id: %d]" , user . ID , repo . ID )
}
2021-12-10 09:27:50 +08:00
perm , err := getUserRepoPermission ( ctx , repo , user )
2019-10-25 16:46:37 +02:00
if err != nil {
return false , err
}
2021-11-28 19:58:28 +08:00
return perm . CanAccessAny ( perm_model . AccessModeWrite , unit . TypeCode , unit . TypeIssues , unit . TypePullRequests ) , nil
2018-11-28 19:26:14 +08:00
}
2021-12-10 09:27:50 +08:00
func hasAccess ( ctx context . Context , userID int64 , repo * repo_model . Repository ) ( bool , error ) {
2021-11-24 17:49:20 +08:00
var user * user_model . User
2018-11-28 19:26:14 +08:00
var err error
if userID > 0 {
2021-12-10 09:27:50 +08:00
user , err = user_model . GetUserByIDEngine ( db . GetEngine ( ctx ) , userID )
2018-11-28 19:26:14 +08:00
if err != nil {
return false , err
}
}
2021-12-10 09:27:50 +08:00
perm , err := getUserRepoPermission ( ctx , repo , user )
2018-11-28 19:26:14 +08:00
if err != nil {
return false , err
}
return perm . HasAccess ( ) , nil
}
// HasAccess returns true if user has access to repo
2021-12-10 09:27:50 +08:00
func HasAccess ( userID int64 , repo * repo_model . Repository ) ( bool , error ) {
return hasAccess ( db . DefaultContext , userID , repo )
2018-11-28 19:26:14 +08:00
}
2020-01-05 02:23:29 +01:00
2021-12-12 23:48:20 +08:00
// GetRepoReaders returns all users that have explicit read access or higher to the repository.
func GetRepoReaders ( repo * repo_model . Repository ) ( _ [ ] * user_model . User , err error ) {
return getUsersWithAccessMode ( db . DefaultContext , repo , perm_model . AccessModeRead )
}
// GetRepoWriters returns all users that have write access to the repository.
func GetRepoWriters ( repo * repo_model . Repository ) ( _ [ ] * user_model . User , err error ) {
return getUsersWithAccessMode ( db . DefaultContext , repo , perm_model . AccessModeWrite )
}
// IsRepoReader returns true if user has explicit read access or higher to the repository.
func IsRepoReader ( repo * repo_model . Repository , userID int64 ) ( bool , error ) {
if repo . OwnerID == userID {
return true , nil
}
return db . GetEngine ( db . DefaultContext ) . Where ( "repo_id = ? AND user_id = ? AND mode >= ?" , repo . ID , userID , perm_model . AccessModeRead ) . Get ( & Access { } )
}