Skip to content

Commit 234c01d

Browse files
reapply changes wiped out by conflict resolution
1 parent fe19586 commit 234c01d

File tree

1 file changed

+102
-68
lines changed

1 file changed

+102
-68
lines changed

models/perm/access/repo_permission.go

Lines changed: 102 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import (
99
"slices"
1010

1111
"code.gitea.io/gitea/models/db"
12-
group_model "code.gitea.io/gitea/models/group"
1312
"code.gitea.io/gitea/models/organization"
1413
perm_model "code.gitea.io/gitea/models/perm"
1514
repo_model "code.gitea.io/gitea/models/repo"
1615
"code.gitea.io/gitea/models/unit"
1716
user_model "code.gitea.io/gitea/models/user"
1817
"code.gitea.io/gitea/modules/log"
18+
"code.gitea.io/gitea/modules/setting"
1919
"code.gitea.io/gitea/modules/util"
2020
)
2121

@@ -26,7 +26,8 @@ type Permission struct {
2626
units []*repo_model.RepoUnit
2727
unitsMode map[unit.Type]perm_model.AccessMode
2828

29-
everyoneAccessMode map[unit.Type]perm_model.AccessMode
29+
everyoneAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for every signed-in user
30+
anonymousAccessMode map[unit.Type]perm_model.AccessMode // the unit's minimal access mode for anonymous (non-signed-in) user
3031
}
3132

3233
// IsOwner returns true if current user is the owner of repository.
@@ -40,7 +41,8 @@ func (p *Permission) IsAdmin() bool {
4041
}
4142

4243
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
43-
// It doesn't count the "everyone access mode".
44+
// It doesn't count the "public(anonymous/everyone) access mode".
45+
// TODO: most calls to this function should be replaced with `HasAnyUnitAccessOrPublicAccess`
4446
func (p *Permission) HasAnyUnitAccess() bool {
4547
for _, v := range p.unitsMode {
4648
if v >= perm_model.AccessModeRead {
@@ -50,13 +52,22 @@ func (p *Permission) HasAnyUnitAccess() bool {
5052
return p.AccessMode >= perm_model.AccessModeRead
5153
}
5254

53-
func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
55+
func (p *Permission) HasAnyUnitPublicAccess() bool {
56+
for _, v := range p.anonymousAccessMode {
57+
if v >= perm_model.AccessModeRead {
58+
return true
59+
}
60+
}
5461
for _, v := range p.everyoneAccessMode {
5562
if v >= perm_model.AccessModeRead {
5663
return true
5764
}
5865
}
59-
return p.HasAnyUnitAccess()
66+
return false
67+
}
68+
69+
func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
70+
return p.HasAnyUnitPublicAccess() || p.HasAnyUnitAccess()
6071
}
6172

6273
// HasUnits returns true if the permission contains attached units
@@ -74,14 +85,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
7485
}
7586

7687
// UnitAccessMode returns current user access mode to the specify unit of the repository
77-
// It also considers "everyone access mode"
88+
// It also considers "public (anonymous/everyone) access mode"
7889
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
7990
// if the units map contains the access mode, use it, but admin/owner mode could override it
8091
if m, ok := p.unitsMode[unitType]; ok {
8192
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
8293
}
8394
// if the units map does not contain the access mode, return the default access mode if the unit exists
84-
unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
95+
unitDefaultAccessMode := p.AccessMode
96+
unitDefaultAccessMode = max(unitDefaultAccessMode, p.anonymousAccessMode[unitType])
97+
unitDefaultAccessMode = max(unitDefaultAccessMode, p.everyoneAccessMode[unitType])
8598
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
8699
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
87100
}
@@ -153,7 +166,7 @@ func (p *Permission) ReadableUnitTypes() []unit.Type {
153166
}
154167

155168
func (p *Permission) LogString() string {
156-
format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): [ "
169+
format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): ["
157170
args := []any{p.AccessMode.ToString(), len(p.units), len(p.unitsMode)}
158171

159172
for i, u := range p.units {
@@ -165,27 +178,77 @@ func (p *Permission) LogString() string {
165178
config = err.Error()
166179
}
167180
}
168-
format += "\nUnits[%d]: ID: %d RepoID: %d Type: %s Config: %s"
181+
format += "\n\tunits[%d]: ID=%d RepoID=%d Type=%s Config=%s"
169182
args = append(args, i, u.ID, u.RepoID, u.Type.LogString(), config)
170183
}
171184
for key, value := range p.unitsMode {
172-
format += "\nUnitMode[%-v]: %-v"
185+
format += "\n\tunitsMode[%-v]: %-v"
173186
args = append(args, key.LogString(), value.LogString())
174187
}
175-
format += " ]>"
188+
format += "\n\tanonymousAccessMode: %-v"
189+
args = append(args, p.anonymousAccessMode)
190+
format += "\n\teveryoneAccessMode: %-v"
191+
args = append(args, p.everyoneAccessMode)
192+
format += "\n\t]>"
176193
return fmt.Sprintf(format, args...)
177194
}
178195

179-
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
196+
func applyPublicAccessPermission(unitType unit.Type, accessMode perm_model.AccessMode, modeMap *map[unit.Type]perm_model.AccessMode) {
197+
if setting.Repository.ForcePrivate {
198+
return
199+
}
200+
if accessMode >= perm_model.AccessModeRead && accessMode > (*modeMap)[unitType] {
201+
if *modeMap == nil {
202+
*modeMap = make(map[unit.Type]perm_model.AccessMode)
203+
}
204+
(*modeMap)[unitType] = accessMode
205+
}
206+
}
207+
208+
func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
209+
// apply public (anonymous) access permissions
210+
for _, u := range perm.units {
211+
applyPublicAccessPermission(u.Type, u.AnonymousAccessMode, &perm.anonymousAccessMode)
212+
}
213+
180214
if user == nil || user.ID <= 0 {
215+
// for anonymous access, it could be:
216+
// AccessMode is None or Read, units has repo units, unitModes is nil
181217
return
182218
}
219+
220+
// apply public (everyone) access permissions
183221
for _, u := range perm.units {
184-
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
185-
if perm.everyoneAccessMode == nil {
186-
perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
222+
applyPublicAccessPermission(u.Type, u.EveryoneAccessMode, &perm.everyoneAccessMode)
223+
}
224+
225+
if perm.unitsMode == nil {
226+
// if unitsMode is not set, then it means that the default p.AccessMode applies to all units
227+
return
228+
}
229+
230+
// remove no permission units
231+
origPermUnits := perm.units
232+
perm.units = make([]*repo_model.RepoUnit, 0, len(perm.units))
233+
for _, u := range origPermUnits {
234+
shouldKeep := false
235+
for t := range perm.unitsMode {
236+
if shouldKeep = u.Type == t; shouldKeep {
237+
break
238+
}
239+
}
240+
for t := range perm.anonymousAccessMode {
241+
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
242+
break
187243
}
188-
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
244+
}
245+
for t := range perm.everyoneAccessMode {
246+
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
247+
break
248+
}
249+
}
250+
if shouldKeep {
251+
perm.units = append(perm.units, u)
189252
}
190253
}
191254
}
@@ -194,11 +257,9 @@ func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
194257
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
195258
defer func() {
196259
if err == nil {
197-
applyEveryoneRepoPermission(user, &perm)
198-
}
199-
if log.IsTrace() {
200-
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
260+
finalProcessRepoUnitPermission(user, &perm)
201261
}
262+
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
202263
}()
203264

204265
if err = repo.LoadUnits(ctx); err != nil {
@@ -207,7 +268,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
207268
perm.units = repo.Units
208269

209270
// anonymous user visit private repo.
210-
// TODO: anonymous user visit public unit of private repo???
211271
if user == nil && repo.IsPrivate {
212272
perm.AccessMode = perm_model.AccessModeNone
213273
return perm, nil
@@ -226,7 +286,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
226286
}
227287

228288
// Prevent strangers from checking out public repo of private organization/users
229-
// 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
289+
// 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
290+
// TODO: rename it to "IsOwnerVisibleToDoer"
230291
if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator {
231292
perm.AccessMode = perm_model.AccessModeNone
232293
return perm, nil
@@ -244,7 +305,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
244305
return perm, nil
245306
}
246307

247-
// plain user
308+
// plain user TODO: this check should be replaced, only need to check collaborator access mode
248309
perm.AccessMode, err = accessLevel(ctx, user, repo)
249310
if err != nil {
250311
return perm, err
@@ -254,6 +315,19 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
254315
return perm, nil
255316
}
256317

318+
// now: the owner is visible to doer, if the repo is public, then the min access mode is read
319+
minAccessMode := util.Iif(!repo.IsPrivate && !user.IsRestricted, perm_model.AccessModeRead, perm_model.AccessModeNone)
320+
perm.AccessMode = max(perm.AccessMode, minAccessMode)
321+
322+
// get units mode from teams
323+
teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
324+
if err != nil {
325+
return perm, err
326+
}
327+
if len(teams) == 0 {
328+
return perm, nil
329+
}
330+
257331
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
258332

259333
// Collaborators on organization
@@ -263,61 +337,22 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
263337
}
264338
}
265339

266-
// get units mode from teams
267-
teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
268-
if err != nil {
269-
return perm, err
270-
}
271-
272340
// if user in an owner team
273341
for _, team := range teams {
274-
if team.AccessMode >= perm_model.AccessModeAdmin {
275-
perm.AccessMode = perm_model.AccessModeOwner
276-
perm.unitsMode = nil
277-
return perm, nil
278-
}
279-
}
280-
groupTeams, err := group_model.FindGroupTeams(ctx, repo.GroupID)
281-
for _, team := range groupTeams {
282-
if team.AccessMode >= perm_model.AccessModeAdmin {
342+
if team.HasAdminAccess() {
283343
perm.AccessMode = perm_model.AccessModeOwner
284344
perm.unitsMode = nil
285345
return perm, nil
286346
}
287347
}
288348

289349
for _, u := range repo.Units {
290-
var found bool
291-
for _, team := range groupTeams {
350+
for _, team := range teams {
351+
unitAccessMode := minAccessMode
292352
if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
293-
perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
294-
found = true
295-
}
296-
}
297-
if !found {
298-
for _, team := range teams {
299-
if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
300-
perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
301-
found = true
302-
}
303-
}
304-
}
305-
306-
// for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
307-
if !found && !repo.IsPrivate && !user.IsRestricted {
308-
if _, ok := perm.unitsMode[u.Type]; !ok {
309-
perm.unitsMode[u.Type] = perm_model.AccessModeRead
310-
}
311-
}
312-
}
313-
314-
// remove no permission units
315-
perm.units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
316-
for t := range perm.unitsMode {
317-
for _, u := range repo.Units {
318-
if u.Type == t {
319-
perm.units = append(perm.units, u)
353+
unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode)
320354
}
355+
perm.unitsMode[u.Type] = unitAccessMode
321356
}
322357
}
323358

@@ -358,7 +393,6 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
358393
if mode >= perm_model.AccessModeAdmin {
359394
return true, nil
360395
}
361-
362396
groupTeams, err := organization.GetUserGroupTeams(ctx, repo.GroupID, user.ID)
363397
if err != nil {
364398
return false, err

0 commit comments

Comments
 (0)