"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
+ "xorm.io/xorm"
)
// ________ .__ __ .__
return opts.Doer == nil || !(opts.IsDoerMember || opts.Doer.IsAdmin)
}
+// applyTeamMatesOnlyFilter make sure restricted users only see public team members and there own team mates
+func (opts FindOrgMembersOpts) applyTeamMatesOnlyFilter(sess *xorm.Session) {
+ if opts.Doer != nil && opts.IsDoerMember && opts.Doer.IsRestricted {
+ teamMates := builder.Select("DISTINCT team_user.uid").
+ From("team_user").
+ Where(builder.In("team_user.team_id", getUserTeamIDsQueryBuilder(opts.OrgID, opts.Doer.ID))).
+ And(builder.Eq{"team_user.org_id": opts.OrgID})
+
+ sess.And(
+ builder.In("org_user.uid", teamMates).
+ Or(builder.Eq{"org_user.is_public": true}),
+ )
+ }
+}
+
// CountOrgMembers counts the organization's members
func CountOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (int64, error) {
sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
if opts.PublicOnly() {
- sess.And("is_public = ?", true)
+ sess = sess.And("is_public = ?", true)
+ } else {
+ opts.applyTeamMatesOnlyFilter(sess)
}
return sess.Count(new(OrgUser))
func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
if opts.PublicOnly() {
- sess.And("is_public = ?", true)
+ sess = sess.And("is_public = ?", true)
+ } else {
+ opts.applyTeamMatesOnlyFilter(sess)
}
if opts.ListOptions.PageSize > 0 {
Find(&teamIDs)
}
+func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder {
+ return builder.Select("team.id").From("team").
+ InnerJoin("team_user", "team_user.team_id = team.id").
+ Where(builder.Eq{
+ "team_user.org_id": orgID,
+ "team_user.uid": userID,
+ })
+}
+
// TeamsWithAccessToRepo returns all teams that have given access level to the repository.
func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) {
return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode)
package organization_test
import (
+ "slices"
"sort"
"testing"
test(unittest.NonexistentID, unittest.NonexistentID, false)
}
+func TestRestrictedUserOrgMembers(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ restrictedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{
+ ID: 29,
+ IsRestricted: true,
+ })
+ if !assert.True(t, restrictedUser.IsRestricted) {
+ return // ensure fixtures return restricted user
+ }
+
+ testCases := []struct {
+ name string
+ opts *organization.FindOrgMembersOpts
+ expectedUIDs []int64
+ }{
+ {
+ name: "restricted user sees public members and teammates",
+ opts: &organization.FindOrgMembersOpts{
+ OrgID: 17, // org17 where user29 is in team9
+ Doer: restrictedUser,
+ IsDoerMember: true,
+ },
+ expectedUIDs: []int64{2, 15, 20, 29}, // Public members (2) + teammates in team9 (15, 20, 29)
+ },
+ {
+ name: "restricted user sees only public members when not member",
+ opts: &organization.FindOrgMembersOpts{
+ OrgID: 3, // org3 where user29 is not a member
+ Doer: restrictedUser,
+ },
+ expectedUIDs: []int64{2, 28}, // Only public members
+ },
+ {
+ name: "non logged in only shows public members",
+ opts: &organization.FindOrgMembersOpts{
+ OrgID: 3,
+ },
+ expectedUIDs: []int64{2, 28}, // Only public members
+ },
+ {
+ name: "non restricted user sees all members",
+ opts: &organization.FindOrgMembersOpts{
+ OrgID: 17,
+ Doer: unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}),
+ IsDoerMember: true,
+ },
+ expectedUIDs: []int64{2, 15, 18, 20, 29}, // All members
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ count, err := organization.CountOrgMembers(db.DefaultContext, tc.opts)
+ assert.NoError(t, err)
+ assert.EqualValues(t, len(tc.expectedUIDs), count)
+
+ members, err := organization.GetOrgUsersByOrgID(db.DefaultContext, tc.opts)
+ assert.NoError(t, err)
+ memberUIDs := make([]int64, 0, len(members))
+ for _, member := range members {
+ memberUIDs = append(memberUIDs, member.UID)
+ }
+ slices.Sort(memberUIDs)
+ assert.EqualValues(t, tc.expectedUIDs, memberUIDs)
+ })
+ }
+}
+
func TestFindOrgs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())