aboutsummaryrefslogtreecommitdiffstats
path: root/models/perm/access/repo_permission.go
diff options
context:
space:
mode:
Diffstat (limited to 'models/perm/access/repo_permission.go')
-rw-r--r--models/perm/access/repo_permission.go109
1 files changed, 73 insertions, 36 deletions
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index 5e7ecb31ea..7de43ecd07 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
@@ -25,7 +26,8 @@ type Permission struct {
units []*repo_model.RepoUnit
unitsMode map[unit.Type]perm_model.AccessMode
- everyoneAccessMode map[unit.Type]perm_model.AccessMode
+ everyoneAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for every signed-in user
+ anonymousAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for anonymous (non-signed-in) user
}
// IsOwner returns true if current user is the owner of repository.
@@ -39,7 +41,8 @@ func (p *Permission) IsAdmin() bool {
}
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
-// It doesn't count the "everyone access mode".
+// It doesn't count the "public(anonymous/everyone) access mode".
+// TODO: most calls to this function should be replaced with `HasAnyUnitAccessOrPublicAccess`
func (p *Permission) HasAnyUnitAccess() bool {
for _, v := range p.unitsMode {
if v >= perm_model.AccessModeRead {
@@ -49,13 +52,22 @@ func (p *Permission) HasAnyUnitAccess() bool {
return p.AccessMode >= perm_model.AccessModeRead
}
-func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
+func (p *Permission) HasAnyUnitPublicAccess() bool {
+ for _, v := range p.anonymousAccessMode {
+ if v >= perm_model.AccessModeRead {
+ return true
+ }
+ }
for _, v := range p.everyoneAccessMode {
if v >= perm_model.AccessModeRead {
return true
}
}
- return p.HasAnyUnitAccess()
+ return false
+}
+
+func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
+ return p.HasAnyUnitPublicAccess() || p.HasAnyUnitAccess()
}
// HasUnits returns true if the permission contains attached units
@@ -73,14 +85,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
}
// UnitAccessMode returns current user access mode to the specify unit of the repository
-// It also considers "everyone access mode"
+// It also considers "public (anonymous/everyone) access mode"
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
// if the units map contains the access mode, use it, but admin/owner mode could override it
if m, ok := p.unitsMode[unitType]; ok {
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
}
// if the units map does not contain the access mode, return the default access mode if the unit exists
- unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
+ unitDefaultAccessMode := p.AccessMode
+ unitDefaultAccessMode = max(unitDefaultAccessMode, p.anonymousAccessMode[unitType])
+ unitDefaultAccessMode = max(unitDefaultAccessMode, p.everyoneAccessMode[unitType])
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
}
@@ -171,27 +185,41 @@ func (p *Permission) LogString() string {
format += "\n\tunitsMode[%-v]: %-v"
args = append(args, key.LogString(), value.LogString())
}
+ format += "\n\tanonymousAccessMode: %-v"
+ args = append(args, p.anonymousAccessMode)
format += "\n\teveryoneAccessMode: %-v"
args = append(args, p.everyoneAccessMode)
format += "\n\t]>"
return fmt.Sprintf(format, args...)
}
+func applyPublicAccessPermission(unitType unit.Type, accessMode perm_model.AccessMode, modeMap *map[unit.Type]perm_model.AccessMode) {
+ if setting.Repository.ForcePrivate {
+ return
+ }
+ if accessMode >= perm_model.AccessModeRead && accessMode > (*modeMap)[unitType] {
+ if *modeMap == nil {
+ *modeMap = make(map[unit.Type]perm_model.AccessMode)
+ }
+ (*modeMap)[unitType] = accessMode
+ }
+}
+
func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
+ // apply public (anonymous) access permissions
+ for _, u := range perm.units {
+ applyPublicAccessPermission(u.Type, u.AnonymousAccessMode, &perm.anonymousAccessMode)
+ }
+
if user == nil || user.ID <= 0 {
// for anonymous access, it could be:
// AccessMode is None or Read, units has repo units, unitModes is nil
return
}
- // apply everyone access permissions
+ // apply public (everyone) access permissions
for _, u := range perm.units {
- if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
- if perm.everyoneAccessMode == nil {
- perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
- }
- perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
- }
+ applyPublicAccessPermission(u.Type, u.EveryoneAccessMode, &perm.everyoneAccessMode)
}
if perm.unitsMode == nil {
@@ -209,6 +237,11 @@ func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
break
}
}
+ for t := range perm.anonymousAccessMode {
+ if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
+ break
+ }
+ }
for t := range perm.everyoneAccessMode {
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
break
@@ -235,7 +268,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
perm.units = repo.Units
// anonymous user visit private repo.
- // TODO: anonymous user visit public unit of private repo???
if user == nil && repo.IsPrivate {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -254,7 +286,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
// Prevent strangers from checking out public repo of private organization/users
- // 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
+ // Allow user if they are a collaborator of a repo within a private user or a private organization but not a member of the organization itself
+ // TODO: rename it to "IsOwnerVisibleToDoer"
if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -272,7 +305,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
- // plain user
+ // plain user TODO: this check should be replaced, only need to check collaborator access mode
perm.AccessMode, err = accessLevel(ctx, user, repo)
if err != nil {
return perm, err
@@ -282,6 +315,19 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
+ // now: the owner is visible to doer, if the repo is public, then the min access mode is read
+ minAccessMode := util.Iif(!repo.IsPrivate && !user.IsRestricted, perm_model.AccessModeRead, perm_model.AccessModeNone)
+ perm.AccessMode = max(perm.AccessMode, minAccessMode)
+
+ // get units mode from teams
+ teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
+ if err != nil {
+ return perm, err
+ }
+ if len(teams) == 0 {
+ return perm, nil
+ }
+
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
// Collaborators on organization
@@ -291,15 +337,9 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
}
- // get units mode from teams
- teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
- if err != nil {
- return perm, err
- }
-
// if user in an owner team
for _, team := range teams {
- if team.AccessMode >= perm_model.AccessModeAdmin {
+ if team.HasAdminAccess() {
perm.AccessMode = perm_model.AccessModeOwner
perm.unitsMode = nil
return perm, nil
@@ -307,19 +347,12 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
for _, u := range repo.Units {
- var found bool
for _, team := range teams {
+ unitAccessMode := minAccessMode
if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
- perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
- found = true
- }
- }
-
- // 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 {
- if _, ok := perm.unitsMode[u.Type]; !ok {
- perm.unitsMode[u.Type] = perm_model.AccessModeRead
+ unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode)
}
+ perm.unitsMode[u.Type] = unitAccessMode
}
}
@@ -367,7 +400,7 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
}
for _, team := range teams {
- if team.AccessMode >= perm_model.AccessModeAdmin {
+ if team.HasAdminAccess() {
return true, nil
}
}
@@ -376,13 +409,13 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access.
-func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint
+func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint:revive // export stutter
return AccessLevelUnit(ctx, user, repo, unit.TypeCode)
}
// AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the
// user does not have access.
-func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint
+func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint:revive // export stutter
perm, err := GetUserRepoPermission(ctx, repo, user)
if err != nil {
return perm_model.AccessModeNone, err
@@ -490,3 +523,7 @@ func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *u
return perm.CanRead(unitType)
}
+
+func PermissionNoAccess() Permission {
+ return Permission{AccessMode: perm_model.AccessModeNone}
+}