Skip to content

Commit 4c547f7

Browse files
reapply changes wiped out by conflict resolution
1 parent fe19586 commit 4c547f7

File tree

1 file changed

+102
-60
lines changed

1 file changed

+102
-60
lines changed

models/perm/access/repo_permission.go

Lines changed: 102 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,19 @@
44
package access
55

66
import (
7+
group_model "code.gitea.io/gitea/models/group"
78
"context"
89
"fmt"
910
"slices"
1011

1112
"code.gitea.io/gitea/models/db"
12-
group_model "code.gitea.io/gitea/models/group"
1313
"code.gitea.io/gitea/models/organization"
1414
perm_model "code.gitea.io/gitea/models/perm"
1515
repo_model "code.gitea.io/gitea/models/repo"
1616
"code.gitea.io/gitea/models/unit"
1717
user_model "code.gitea.io/gitea/models/user"
1818
"code.gitea.io/gitea/modules/log"
19+
"code.gitea.io/gitea/modules/setting"
1920
"code.gitea.io/gitea/modules/util"
2021
)
2122

@@ -26,7 +27,8 @@ type Permission struct {
2627
units []*repo_model.RepoUnit
2728
unitsMode map[unit.Type]perm_model.AccessMode
2829

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

3234
// IsOwner returns true if current user is the owner of repository.
@@ -40,7 +42,8 @@ func (p *Permission) IsAdmin() bool {
4042
}
4143

4244
// 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".
45+
// It doesn't count the "public(anonymous/everyone) access mode".
46+
// TODO: most calls to this function should be replaced with `HasAnyUnitAccessOrPublicAccess`
4447
func (p *Permission) HasAnyUnitAccess() bool {
4548
for _, v := range p.unitsMode {
4649
if v >= perm_model.AccessModeRead {
@@ -50,13 +53,22 @@ func (p *Permission) HasAnyUnitAccess() bool {
5053
return p.AccessMode >= perm_model.AccessModeRead
5154
}
5255

53-
func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
56+
func (p *Permission) HasAnyUnitPublicAccess() bool {
57+
for _, v := range p.anonymousAccessMode {
58+
if v >= perm_model.AccessModeRead {
59+
return true
60+
}
61+
}
5462
for _, v := range p.everyoneAccessMode {
5563
if v >= perm_model.AccessModeRead {
5664
return true
5765
}
5866
}
59-
return p.HasAnyUnitAccess()
67+
return false
68+
}
69+
70+
func (p *Permission) HasAnyUnitAccessOrPublicAccess() bool {
71+
return p.HasAnyUnitPublicAccess() || p.HasAnyUnitAccess()
6072
}
6173

6274
// HasUnits returns true if the permission contains attached units
@@ -74,14 +86,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
7486
}
7587

7688
// UnitAccessMode returns current user access mode to the specify unit of the repository
77-
// It also considers "everyone access mode"
89+
// It also considers "public (anonymous/everyone) access mode"
7890
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
7991
// if the units map contains the access mode, use it, but admin/owner mode could override it
8092
if m, ok := p.unitsMode[unitType]; ok {
8193
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
8294
}
8395
// 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])
96+
unitDefaultAccessMode := p.AccessMode
97+
unitDefaultAccessMode = max(unitDefaultAccessMode, p.anonymousAccessMode[unitType])
98+
unitDefaultAccessMode = max(unitDefaultAccessMode, p.everyoneAccessMode[unitType])
8599
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
86100
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
87101
}
@@ -153,7 +167,7 @@ func (p *Permission) ReadableUnitTypes() []unit.Type {
153167
}
154168

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

159173
for i, u := range p.units {
@@ -165,27 +179,77 @@ func (p *Permission) LogString() string {
165179
config = err.Error()
166180
}
167181
}
168-
format += "\nUnits[%d]: ID: %d RepoID: %d Type: %s Config: %s"
182+
format += "\n\tunits[%d]: ID=%d RepoID=%d Type=%s Config=%s"
169183
args = append(args, i, u.ID, u.RepoID, u.Type.LogString(), config)
170184
}
171185
for key, value := range p.unitsMode {
172-
format += "\nUnitMode[%-v]: %-v"
186+
format += "\n\tunitsMode[%-v]: %-v"
173187
args = append(args, key.LogString(), value.LogString())
174188
}
175-
format += " ]>"
189+
format += "\n\tanonymousAccessMode: %-v"
190+
args = append(args, p.anonymousAccessMode)
191+
format += "\n\teveryoneAccessMode: %-v"
192+
args = append(args, p.everyoneAccessMode)
193+
format += "\n\t]>"
176194
return fmt.Sprintf(format, args...)
177195
}
178196

179-
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
197+
func applyPublicAccessPermission(unitType unit.Type, accessMode perm_model.AccessMode, modeMap *map[unit.Type]perm_model.AccessMode) {
198+
if setting.Repository.ForcePrivate {
199+
return
200+
}
201+
if accessMode >= perm_model.AccessModeRead && accessMode > (*modeMap)[unitType] {
202+
if *modeMap == nil {
203+
*modeMap = make(map[unit.Type]perm_model.AccessMode)
204+
}
205+
(*modeMap)[unitType] = accessMode
206+
}
207+
}
208+
209+
func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
210+
// apply public (anonymous) access permissions
211+
for _, u := range perm.units {
212+
applyPublicAccessPermission(u.Type, u.AnonymousAccessMode, &perm.anonymousAccessMode)
213+
}
214+
180215
if user == nil || user.ID <= 0 {
216+
// for anonymous access, it could be:
217+
// AccessMode is None or Read, units has repo units, unitModes is nil
181218
return
182219
}
220+
221+
// apply public (everyone) access permissions
183222
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)
223+
applyPublicAccessPermission(u.Type, u.EveryoneAccessMode, &perm.everyoneAccessMode)
224+
}
225+
226+
if perm.unitsMode == nil {
227+
// if unitsMode is not set, then it means that the default p.AccessMode applies to all units
228+
return
229+
}
230+
231+
// remove no permission units
232+
origPermUnits := perm.units
233+
perm.units = make([]*repo_model.RepoUnit, 0, len(perm.units))
234+
for _, u := range origPermUnits {
235+
shouldKeep := false
236+
for t := range perm.unitsMode {
237+
if shouldKeep = u.Type == t; shouldKeep {
238+
break
187239
}
188-
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
240+
}
241+
for t := range perm.anonymousAccessMode {
242+
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
243+
break
244+
}
245+
}
246+
for t := range perm.everyoneAccessMode {
247+
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
248+
break
249+
}
250+
}
251+
if shouldKeep {
252+
perm.units = append(perm.units, u)
189253
}
190254
}
191255
}
@@ -194,11 +258,9 @@ func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
194258
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
195259
defer func() {
196260
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)
261+
finalProcessRepoUnitPermission(user, &perm)
201262
}
263+
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
202264
}()
203265

204266
if err = repo.LoadUnits(ctx); err != nil {
@@ -207,7 +269,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
207269
perm.units = repo.Units
208270

209271
// anonymous user visit private repo.
210-
// TODO: anonymous user visit public unit of private repo???
211272
if user == nil && repo.IsPrivate {
212273
perm.AccessMode = perm_model.AccessModeNone
213274
return perm, nil
@@ -226,7 +287,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
226287
}
227288

228289
// 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
290+
// 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
291+
// TODO: rename it to "IsOwnerVisibleToDoer"
230292
if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator {
231293
perm.AccessMode = perm_model.AccessModeNone
232294
return perm, nil
@@ -244,7 +306,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
244306
return perm, nil
245307
}
246308

247-
// plain user
309+
// plain user TODO: this check should be replaced, only need to check collaborator access mode
248310
perm.AccessMode, err = accessLevel(ctx, user, repo)
249311
if err != nil {
250312
return perm, err
@@ -254,6 +316,19 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
254316
return perm, nil
255317
}
256318

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

259334
// Collaborators on organization
@@ -263,15 +338,9 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
263338
}
264339
}
265340

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-
272341
// if user in an owner team
273342
for _, team := range teams {
274-
if team.AccessMode >= perm_model.AccessModeAdmin {
343+
if team.HasAdminAccess() {
275344
perm.AccessMode = perm_model.AccessModeOwner
276345
perm.unitsMode = nil
277346
return perm, nil
@@ -285,7 +354,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
285354
return perm, nil
286355
}
287356
}
288-
289357
for _, u := range repo.Units {
290358
var found bool
291359
for _, team := range groupTeams {
@@ -296,27 +364,12 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
296364
}
297365
if !found {
298366
for _, team := range teams {
367+
unitAccessMode := minAccessMode
299368
if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
300-
perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
369+
unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode)
301370
found = true
302371
}
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)
372+
perm.unitsMode[u.Type] = unitAccessMode
320373
}
321374
}
322375
}
@@ -359,17 +412,6 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
359412
return true, nil
360413
}
361414

362-
groupTeams, err := organization.GetUserGroupTeams(ctx, repo.GroupID, user.ID)
363-
if err != nil {
364-
return false, err
365-
}
366-
367-
for _, team := range groupTeams {
368-
if team.AccessMode >= perm_model.AccessModeAdmin {
369-
return true, nil
370-
}
371-
}
372-
373415
teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
374416
if err != nil {
375417
return false, err

0 commit comments

Comments
 (0)