Skip to content

Commit 5b1e809

Browse files
authored
fix: support oidc group allowlist in oss (#19430)
## Summary In this pull request we're adding support for OIDC allowed groups in the OSS version as part of work for #17027. ### Changes - Restored support for parsing group allow list in OSS code ### Testing - Added tests for OSS code - Tested allowed/prohibited group OIDC flows in premium and OSS
1 parent a19dfa9 commit 5b1e809

File tree

3 files changed

+80
-50
lines changed

3 files changed

+80
-50
lines changed

coderd/idpsync/group.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"net/http"
78

89
"github.com/golang-jwt/jwt/v4"
910
"github.com/google/uuid"
@@ -71,9 +72,49 @@ func (s AGPLIDPSync) GroupSyncSettings(ctx context.Context, orgID uuid.UUID, db
7172
return settings, nil
7273
}
7374

74-
func (s AGPLIDPSync) ParseGroupClaims(_ context.Context, _ jwt.MapClaims) (GroupParams, *HTTPError) {
75+
func (s AGPLIDPSync) ParseGroupClaims(_ context.Context, mergedClaims jwt.MapClaims) (GroupParams, *HTTPError) {
76+
if s.GroupField != "" && len(s.GroupAllowList) > 0 {
77+
groupsRaw, ok := mergedClaims[s.GroupField]
78+
if !ok {
79+
return GroupParams{}, &HTTPError{
80+
Code: http.StatusForbidden,
81+
Msg: "Not a member of an allowed group",
82+
Detail: "You have no groups in your claims!",
83+
RenderStaticPage: true,
84+
}
85+
}
86+
parsedGroups, err := ParseStringSliceClaim(groupsRaw)
87+
if err != nil {
88+
return GroupParams{}, &HTTPError{
89+
Code: http.StatusBadRequest,
90+
Msg: "Failed read groups from claims for allow list check. Ask an administrator for help.",
91+
Detail: err.Error(),
92+
RenderStaticPage: true,
93+
}
94+
}
95+
96+
inAllowList := false
97+
AllowListCheckLoop:
98+
for _, group := range parsedGroups {
99+
if _, ok := s.GroupAllowList[group]; ok {
100+
inAllowList = true
101+
break AllowListCheckLoop
102+
}
103+
}
104+
105+
if !inAllowList {
106+
return GroupParams{}, &HTTPError{
107+
Code: http.StatusForbidden,
108+
Msg: "Not a member of an allowed group",
109+
Detail: "Ask an administrator to add one of your groups to the allow list.",
110+
RenderStaticPage: true,
111+
}
112+
}
113+
}
114+
75115
return GroupParams{
76116
SyncEntitled: s.GroupSyncEntitled(),
117+
MergedClaims: mergedClaims,
77118
}, nil
78119
}
79120

coderd/idpsync/group_test.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ func TestParseGroupClaims(t *testing.T) {
4444
require.False(t, params.SyncEntitled)
4545
})
4646

47-
// AllowList has no effect in AGPL
48-
t.Run("AllowList", func(t *testing.T) {
47+
t.Run("NotInAllowList", func(t *testing.T) {
4948
t.Parallel()
5049

5150
s := idpsync.NewAGPLSync(slogtest.Make(t, &slogtest.Options{}),
@@ -59,9 +58,39 @@ func TestParseGroupClaims(t *testing.T) {
5958

6059
ctx := testutil.Context(t, testutil.WaitMedium)
6160

62-
params, err := s.ParseGroupClaims(ctx, jwt.MapClaims{})
61+
// Invalid group
62+
_, err := s.ParseGroupClaims(ctx, jwt.MapClaims{
63+
"groups": []string{"bar"},
64+
})
65+
require.NotNil(t, err)
66+
require.Equal(t, 403, err.Code)
67+
68+
// No groups
69+
_, err = s.ParseGroupClaims(ctx, jwt.MapClaims{})
70+
require.NotNil(t, err)
71+
require.Equal(t, 403, err.Code)
72+
})
73+
74+
t.Run("InAllowList", func(t *testing.T) {
75+
t.Parallel()
76+
77+
s := idpsync.NewAGPLSync(slogtest.Make(t, &slogtest.Options{}),
78+
runtimeconfig.NewManager(),
79+
idpsync.DeploymentSyncSettings{
80+
GroupField: "groups",
81+
GroupAllowList: map[string]struct{}{
82+
"foo": {},
83+
},
84+
})
85+
86+
ctx := testutil.Context(t, testutil.WaitMedium)
87+
88+
claims := jwt.MapClaims{
89+
"groups": []string{"foo", "bar"},
90+
}
91+
params, err := s.ParseGroupClaims(ctx, claims)
6392
require.Nil(t, err)
64-
require.False(t, params.SyncEntitled)
93+
require.Equal(t, claims, params.MergedClaims)
6594
})
6695
}
6796

enterprise/coderd/enidpsync/groups.go

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package enidpsync
22

33
import (
44
"context"
5-
"net/http"
65

76
"github.com/golang-jwt/jwt/v4"
87

@@ -20,51 +19,12 @@ func (e EnterpriseIDPSync) GroupSyncEntitled() bool {
2019
// GroupAllowList is implemented here to prevent login by unauthorized users.
2120
// TODO: GroupAllowList overlaps with the default organization group sync settings.
2221
func (e EnterpriseIDPSync) ParseGroupClaims(ctx context.Context, mergedClaims jwt.MapClaims) (idpsync.GroupParams, *idpsync.HTTPError) {
23-
if !e.GroupSyncEntitled() {
24-
return e.AGPLIDPSync.ParseGroupClaims(ctx, mergedClaims)
22+
resp, err := e.AGPLIDPSync.ParseGroupClaims(ctx, mergedClaims)
23+
if err != nil {
24+
return idpsync.GroupParams{}, err
2525
}
26-
27-
if e.GroupField != "" && len(e.GroupAllowList) > 0 {
28-
groupsRaw, ok := mergedClaims[e.GroupField]
29-
if !ok {
30-
return idpsync.GroupParams{}, &idpsync.HTTPError{
31-
Code: http.StatusForbidden,
32-
Msg: "Not a member of an allowed group",
33-
Detail: "You have no groups in your claims!",
34-
RenderStaticPage: true,
35-
}
36-
}
37-
parsedGroups, err := idpsync.ParseStringSliceClaim(groupsRaw)
38-
if err != nil {
39-
return idpsync.GroupParams{}, &idpsync.HTTPError{
40-
Code: http.StatusBadRequest,
41-
Msg: "Failed read groups from claims for allow list check. Ask an administrator for help.",
42-
Detail: err.Error(),
43-
RenderStaticPage: true,
44-
}
45-
}
46-
47-
inAllowList := false
48-
AllowListCheckLoop:
49-
for _, group := range parsedGroups {
50-
if _, ok := e.GroupAllowList[group]; ok {
51-
inAllowList = true
52-
break AllowListCheckLoop
53-
}
54-
}
55-
56-
if !inAllowList {
57-
return idpsync.GroupParams{}, &idpsync.HTTPError{
58-
Code: http.StatusForbidden,
59-
Msg: "Not a member of an allowed group",
60-
Detail: "Ask an administrator to add one of your groups to the allow list.",
61-
RenderStaticPage: true,
62-
}
63-
}
64-
}
65-
6626
return idpsync.GroupParams{
67-
SyncEntitled: true,
68-
MergedClaims: mergedClaims,
27+
SyncEntitled: e.GroupSyncEntitled(),
28+
MergedClaims: resp.MergedClaims,
6929
}, nil
7030
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy