@@ -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` 
4446func  (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" 
7889func  (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
155168func  (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  +=  "\n  Units [%d]: ID:  %d RepoID:  %d Type:  %s Config:  %s" 
181+ 		format  +=  "\n  \t units [%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  +=  "\n  UnitMode [%-v]: %-v" 
185+ 		format  +=  "\n  \t unitsMode [%-v]: %-v" 
173186		args  =  append (args , key .LogString (), value .LogString ())
174187	}
175- 	format  +=  " ]>" 
188+ 	format  +=  "\n \t anonymousAccessMode: %-v" 
189+ 	args  =  append (args , p .anonymousAccessMode )
190+ 	format  +=  "\n \t everyoneAccessMode: %-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) {
194257func  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