Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 341bfe7

Browse files
authored
allow site admins to list org members & view user/org settings on dotcom (#63963)
Followup from https://github.com/sourcegraph/sourcegraph/pull/63941. I forgot to make it so site admins could list org members in that PR. As for the new ability for site admins to see user and org settings: Site admins could already add themselves to orgs as members and see settings that way. I considered still keeping it stricter, but it is valuable for site admins to be able to view settings to help users troubleshoot. ## Test plan In dotcom mode, as a site admin, view a user or org (that the site admin is not a member of). Confirm that the settings can be viewed.
1 parent 492ddef commit 341bfe7

File tree

10 files changed

+56
-70
lines changed

10 files changed

+56
-70
lines changed

cmd/frontend/graphqlbackend/feature_flags.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/graph-gophers/graphql-go"
77
"github.com/graph-gophers/graphql-go/relay"
8+
89
"github.com/sourcegraph/sourcegraph/internal/actor"
910
"github.com/sourcegraph/sourcegraph/internal/gqlutil"
1011

@@ -172,7 +173,7 @@ func (r *schemaResolver) OrganizationFeatureFlagValue(ctx context.Context, args
172173
return false, err
173174
}
174175
// same behavior as if the flag does not exist
175-
if err := auth.CheckOrgAccess(ctx, r.db, org); err != nil {
176+
if err := auth.CheckOrgAccessOrSiteAdmin(ctx, r.db, org); err != nil {
176177
return false, nil
177178
}
178179

cmd/frontend/graphqlbackend/org.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func (o *OrgResolver) Members(ctx context.Context, args struct {
105105
) (*graphqlutil.ConnectionResolver[*UserResolver], error) {
106106
// 🚨 SECURITY: On dotcom, only an org's members can list its members.
107107
if dotcom.SourcegraphDotComMode() {
108-
if err := auth.CheckOrgAccess(ctx, o.db, o.org.ID); err != nil {
108+
if err := auth.CheckOrgAccessOrSiteAdmin(ctx, o.db, o.org.ID); err != nil {
109109
return nil, err
110110
}
111111
}

cmd/frontend/graphqlbackend/org_test.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,8 @@ func TestOrganizationMembers(t *testing.T) {
123123
})
124124

125125
t.Run("non-members", func(t *testing.T) {
126-
users.GetByCurrentAuthUserFunc.SetDefaultReturn(&types.User{Username: "xavier", ID: 10}, nil)
127-
128126
t.Run("can list members on non-dotcom", func(t *testing.T) {
127+
users.GetByCurrentAuthUserFunc.SetDefaultReturn(&types.User{Username: "xavier", ID: 10}, nil)
129128
dotcom.MockSourcegraphDotComMode(t, false)
130129
RunTests(t, []*Test{
131130
{
@@ -152,7 +151,36 @@ func TestOrganizationMembers(t *testing.T) {
152151
})
153152
})
154153

155-
t.Run("cannot list members on dotcom", func(t *testing.T) {
154+
t.Run("who are site admin can list members on non-dotcom", func(t *testing.T) {
155+
users.GetByCurrentAuthUserFunc.SetDefaultReturn(&types.User{Username: "xavier", ID: 10, SiteAdmin: true}, nil)
156+
dotcom.MockSourcegraphDotComMode(t, true)
157+
RunTests(t, []*Test{
158+
{
159+
Schema: mustParseGraphQLSchema(t, db),
160+
Query: `
161+
{
162+
organization(name: "acme") {
163+
members {
164+
nodes { username }
165+
}
166+
}
167+
}
168+
`,
169+
ExpectedResult: `
170+
{
171+
"organization": {
172+
"members": {
173+
"nodes": [{"username": "alice"}, {"username": "bob"}]
174+
}
175+
}
176+
}
177+
`,
178+
},
179+
})
180+
})
181+
182+
t.Run("who are not site admin cannot list members on dotcom", func(t *testing.T) {
183+
users.GetByCurrentAuthUserFunc.SetDefaultReturn(&types.User{Username: "xavier", ID: 10}, nil)
156184
dotcom.MockSourcegraphDotComMode(t, true)
157185
RunTests(t, []*Test{
158186
{

cmd/frontend/graphqlbackend/settings_mutation_test.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/sourcegraph/sourcegraph/internal/actor"
1111
"github.com/sourcegraph/sourcegraph/internal/api"
12+
"github.com/sourcegraph/sourcegraph/internal/auth"
1213
"github.com/sourcegraph/sourcegraph/internal/database/dbmocks"
1314
"github.com/sourcegraph/sourcegraph/internal/dotcom"
1415
"github.com/sourcegraph/sourcegraph/internal/gitserver"
@@ -142,15 +143,6 @@ func TestSettingsMutation(t *testing.T) {
142143
})
143144
},
144145
},
145-
{
146-
name: "site admin",
147-
ctx: actor.WithActor(context.Background(), &actor.Actor{UID: 2}),
148-
setup: func() {
149-
users.GetByIDFunc.SetDefaultHook(func(ctx context.Context, id int32) (*types.User, error) {
150-
return &types.User{ID: id, SiteAdmin: true}, nil
151-
})
152-
},
153-
},
154146
}
155147
for _, test := range tests {
156148
t.Run(test.name, func(t *testing.T) {
@@ -165,7 +157,7 @@ func TestSettingsMutation(t *testing.T) {
165157
},
166158
)
167159
got := fmt.Sprintf("%v", err)
168-
want := "must be authenticated as user with id 1"
160+
want := auth.ErrMustBeSiteAdminOrSameUser.Error()
169161
assert.Equal(t, want, got)
170162
})
171163
}

cmd/frontend/graphqlbackend/settings_subject.go

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/sourcegraph/sourcegraph/internal/api"
1111
"github.com/sourcegraph/sourcegraph/internal/auth"
1212
"github.com/sourcegraph/sourcegraph/internal/database"
13-
"github.com/sourcegraph/sourcegraph/internal/dotcom"
1413
"github.com/sourcegraph/sourcegraph/lib/errors"
1514
)
1615

@@ -102,31 +101,16 @@ func settingsSubjectForNodeAndCheckAccess(ctx context.Context, n Node) (*setting
102101
subject.site = s
103102

104103
case *UserResolver:
105-
// 🚨 SECURITY: Only the authenticated user can view their settings on
106-
// Sourcegraph.com.
107-
if dotcom.SourcegraphDotComMode() {
108-
if err := auth.CheckSameUser(ctx, s.user.ID); err != nil {
109-
return nil, err
110-
}
111-
} else {
112-
// 🚨 SECURITY: The user and site admins are allowed to view the user's settings otherwise.
113-
if err := auth.CheckSiteAdminOrSameUser(ctx, s.db, s.user.ID); err != nil {
114-
return nil, err
115-
}
104+
// 🚨 SECURITY: The user and site admins are allowed to view the user's settings otherwise.
105+
if err := auth.CheckSiteAdminOrSameUser(ctx, s.db, s.user.ID); err != nil {
106+
return nil, err
116107
}
117108
subject.user = s
118109

119110
case *OrgResolver:
120-
if dotcom.SourcegraphDotComMode() {
121-
// 🚨 SECURITY: Only org members (not any site admin) can view org settings on Sourcegraph.com.
122-
if err := auth.CheckOrgAccess(ctx, s.db, s.org.ID); err != nil {
123-
return nil, err
124-
}
125-
} else {
126-
// 🚨 SECURITY: Org members or site admins can view the org settings otherwise.
127-
if err := auth.CheckOrgAccessOrSiteAdmin(ctx, s.db, s.org.ID); err != nil {
128-
return nil, err
129-
}
111+
// 🚨 SECURITY: Only org members or site admins can view the org settings.
112+
if err := auth.CheckOrgAccessOrSiteAdmin(ctx, s.db, s.org.ID); err != nil {
113+
return nil, err
130114
}
131115
subject.org = s
132116

cmd/frontend/graphqlbackend/settings_subject_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ func TestSettingsSubjectForNodeAndCheckAccess(t *testing.T) {
6767
wantSubject: &settingsSubjectResolver{user: &UserResolver{user: &types.User{ID: userID}, db: db}},
6868
},
6969
{
70-
name: "user settings - site admin on dotcom",
71-
node: &UserResolver{user: &types.User{ID: userID}, db: db},
72-
actor: actor.FromActualUser(&types.User{ID: otherUserID, SiteAdmin: true}),
73-
isDotcom: true,
74-
wantError: &auth.InsufficientAuthorizationError{},
70+
name: "user settings - site admin on dotcom",
71+
node: &UserResolver{user: &types.User{ID: userID}, db: db},
72+
actor: actor.FromActualUser(&types.User{ID: otherUserID, SiteAdmin: true}),
73+
isDotcom: true,
74+
wantSubject: &settingsSubjectResolver{user: &UserResolver{user: &types.User{ID: userID}, db: db}},
7575
},
7676
{
7777
name: "user settings - different user",
@@ -98,11 +98,11 @@ func TestSettingsSubjectForNodeAndCheckAccess(t *testing.T) {
9898
wantSubject: &settingsSubjectResolver{org: &OrgResolver{org: &types.Org{ID: orgID}, db: db}},
9999
},
100100
{
101-
name: "org settings - non-member site admin on dotcom",
102-
node: &OrgResolver{org: &types.Org{ID: orgID}, db: db},
103-
actor: actor.FromActualUser(&types.User{ID: otherUserID, SiteAdmin: true}),
104-
isDotcom: true,
105-
wantError: auth.ErrNotAnOrgMember,
101+
name: "org settings - non-member site admin on dotcom",
102+
node: &OrgResolver{org: &types.Org{ID: orgID}, db: db},
103+
actor: actor.FromActualUser(&types.User{ID: otherUserID, SiteAdmin: true}),
104+
isDotcom: true,
105+
wantSubject: &settingsSubjectResolver{org: &OrgResolver{org: &types.Org{ID: orgID}, db: db}},
106106
},
107107
{
108108
name: "unknown node type",

cmd/frontend/graphqlbackend/user.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ func (r *schemaResolver) ChangeCodyPlan(ctx context.Context, args *changeCodyPla
516516
}
517517

518518
// 🚨 SECURITY: Only the authenticated user can update their properties.
519-
if err := auth.CheckSameUser(ctx, userID); err != nil {
519+
if err := auth.CheckSiteAdminOrSameUser(ctx, r.db, userID); err != nil {
520520
return nil, err
521521
}
522522

cmd/frontend/graphqlbackend/user_test.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -251,17 +251,6 @@ func TestUser_LatestSettings(t *testing.T) {
251251
})
252252
},
253253
},
254-
{
255-
name: "site admin on dotcom",
256-
isDotcom: true,
257-
ctx: actor.WithActor(context.Background(), &actor.Actor{UID: 2}),
258-
wantErr: "must be authenticated as user with id 1",
259-
setup: func() {
260-
users.GetByIDFunc.SetDefaultHook(func(ctx context.Context, id int32) (*types.User, error) {
261-
return &types.User{ID: id, SiteAdmin: true}, nil
262-
})
263-
},
264-
},
265254
}
266255
for _, test := range tests {
267256
t.Run(test.name, func(t *testing.T) {

internal/auth/orgs.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,6 @@ func CheckOrgAccessOrSiteAdmin(ctx context.Context, db database.DB, orgID int32)
2121
return checkOrgAccess(ctx, db, orgID, true)
2222
}
2323

24-
// CheckOrgAccess returns an error if the user is not a member of the
25-
// organization with the specified ID.
26-
//
27-
// It is used when an action on an org can be performed by the organization's
28-
// members, but nobody else.
29-
func CheckOrgAccess(ctx context.Context, db database.DB, orgID int32) error {
30-
return checkOrgAccess(ctx, db, orgID, false)
31-
}
32-
3324
// checkOrgAccess is a helper method used above which allows optionally allowing
3425
// site admins to access all organizations.
3526
func checkOrgAccess(ctx context.Context, db database.DB, orgID int32, allowAdmin bool) error {

internal/auth/site_admin.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ func CheckSiteAdminOrSameUser(ctx context.Context, db database.DB, subjectUserID
7272
return CheckSiteAdminOrSameUserFromActor(a, db, subjectUserID)
7373
}
7474

75-
// CheckSameUser returns an error if the user is not the user specified by
76-
// subjectUserID.
75+
// CheckSameUser returns an error if the user is not the user specified by subjectUserID. It is used
76+
// for actions that can ONLY be performed by that user. In most cases, site admins should also be
77+
// able to perform the action, and you should use CheckSiteAdminOrSameUser instead.
7778
func CheckSameUser(ctx context.Context, subjectUserID int32) error {
7879
a := actor.FromContext(ctx)
7980
if a.IsInternal() || (a.IsAuthenticated() && a.UID == subjectUserID) {

0 commit comments

Comments
 (0)