Skip to content

Commit 99c97c2

Browse files
committed
wip
1 parent cb9d40f commit 99c97c2

File tree

7 files changed

+147
-25
lines changed

7 files changed

+147
-25
lines changed

coderd/coderd.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,6 @@ func New(options *Options) *API {
276276
if options.Entitlements == nil {
277277
options.Entitlements = entitlements.New()
278278
}
279-
if options.IDPSync == nil {
280-
options.IDPSync = idpsync.NewAGPLSync(options.Logger, idpsync.SyncSettings{
281-
OrganizationField: options.DeploymentValues.OIDC.OrganizationField.Value(),
282-
OrganizationMapping: options.DeploymentValues.OIDC.OrganizationMapping.Value,
283-
OrganizationAssignDefault: options.DeploymentValues.OIDC.OrganizationAssignDefault.Value(),
284-
})
285-
}
286279
if options.NewTicker == nil {
287280
options.NewTicker = func(duration time.Duration) (tick <-chan time.Time, done func()) {
288281
ticker := time.NewTicker(duration)
@@ -318,6 +311,14 @@ func New(options *Options) *API {
318311
options.AccessControlStore,
319312
)
320313

314+
if options.IDPSync == nil {
315+
options.IDPSync = idpsync.NewAGPLSync(options.Logger, idpsync.SyncSettings{
316+
OrganizationField: options.DeploymentValues.OIDC.OrganizationField.Value(),
317+
OrganizationMapping: options.DeploymentValues.OIDC.OrganizationMapping.Value,
318+
OrganizationAssignDefault: options.DeploymentValues.OIDC.OrganizationAssignDefault.Value(),
319+
})
320+
}
321+
321322
experiments := ReadExperiments(
322323
options.Logger, options.DeploymentValues.Experiments.Value(),
323324
)

coderd/idpsync/group.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package idpsync
2+
3+
import (
4+
"context"
5+
6+
"github.com/golang-jwt/jwt/v4"
7+
"github.com/google/uuid"
8+
"golang.org/x/xerrors"
9+
10+
"github.com/coder/coder/v2/coderd/database"
11+
"github.com/coder/coder/v2/coderd/database/dbauthz"
12+
)
13+
14+
type GroupParams struct {
15+
// SyncEnabled if false will skip syncing the user's groups
16+
SyncEnabled bool
17+
MergedClaims jwt.MapClaims
18+
}
19+
20+
func (AGPLIDPSync) GroupSyncEnabled() bool {
21+
// AGPL does not support syncing groups.
22+
return false
23+
}
24+
25+
func (s AGPLIDPSync) ParseGroupClaims(_ context.Context, _ jwt.MapClaims) (GroupParams, *HTTPError) {
26+
return GroupParams{
27+
SyncEnabled: s.GroupSyncEnabled(),
28+
}, nil
29+
}
30+
31+
// TODO: Group allowlist behavior should probably happen at this step.
32+
func (s AGPLIDPSync) SyncGroups(ctx context.Context, db database.Store, user database.User, params GroupParams) error {
33+
// Nothing happens if sync is not enabled
34+
if !params.SyncEnabled {
35+
return nil
36+
}
37+
38+
// nolint:gocritic // all syncing is done as a system user
39+
ctx = dbauthz.AsSystemRestricted(ctx)
40+
41+
db.InTx(func(tx database.Store) error {
42+
userGroups, err := db.GetGroups(ctx, database.GetGroupsParams{
43+
HasMemberID: user.ID,
44+
})
45+
if err != nil {
46+
return xerrors.Errorf("get user groups: %w", err)
47+
}
48+
49+
// Figure out which organizations the user is a member of.
50+
userOrgs := make(map[uuid.UUID][]database.GetGroupsRow)
51+
for _, g := range userGroups {
52+
g := g
53+
userOrgs[g.Group.OrganizationID] = append(userOrgs[g.Group.OrganizationID], g)
54+
}
55+
56+
// Force each organization, we sync the groups.
57+
db.RemoveUserFromAllGroups(ctx, user.ID)
58+
59+
return nil
60+
}, nil)
61+
62+
//
63+
//tx.InTx(func(tx database.Store) error {
64+
// // When setting the user's groups, it's easier to just clear their groups and re-add them.
65+
// // This ensures that the user's groups are always in sync with the auth provider.
66+
// err := tx.RemoveUserFromAllGroups(ctx, user.ID)
67+
// if err != nil {
68+
// return err
69+
// }
70+
//
71+
// for _, org := range userOrgs {
72+
//
73+
// }
74+
//
75+
// return nil
76+
//}, nil)
77+
78+
return nil
79+
}

coderd/idpsync/idpsync.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package idpsync
33
import (
44
"context"
55
"net/http"
6+
"regexp"
67
"strings"
78

89
"github.com/golang-jwt/jwt/v4"
@@ -29,6 +30,11 @@ type IDPSync interface {
2930
// SyncOrganizations assigns and removed users from organizations based on the
3031
// provided params.
3132
SyncOrganizations(ctx context.Context, tx database.Store, user database.User, params OrganizationParams) error
33+
34+
GroupSyncEnabled() bool
35+
// ParseGroupClaims takes claims from an OIDC provider, and returns the
36+
// group sync params for assigning users into groups.
37+
ParseGroupClaims(ctx context.Context, _ jwt.MapClaims) (GroupParams, *HTTPError)
3238
}
3339

3440
// AGPLIDPSync is the configuration for syncing user information from an external
@@ -50,17 +56,13 @@ type SyncSettings struct {
5056
// placed into the default organization. This is mostly a hack to support
5157
// legacy deployments.
5258
OrganizationAssignDefault bool
53-
}
5459

55-
type OrganizationParams struct {
56-
// SyncEnabled if false will skip syncing the user's organizations.
57-
SyncEnabled bool
58-
// IncludeDefault is primarily for single org deployments. It will ensure
59-
// a user is always inserted into the default org.
60-
IncludeDefault bool
61-
// Organizations is the list of organizations the user should be a member of
62-
// assuming syncing is turned on.
63-
Organizations []uuid.UUID
60+
// Group options here are set by the deployment config and only apply to
61+
// the default organization.
62+
GroupField string
63+
CreateMissingGroups bool
64+
GroupMapping map[string]string
65+
GroupFilter *regexp.Regexp
6466
}
6567

6668
func NewAGPLSync(logger slog.Logger, settings SyncSettings) *AGPLIDPSync {

coderd/idpsync/organization.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ import (
1616
"github.com/coder/coder/v2/coderd/util/slice"
1717
)
1818

19+
type OrganizationParams struct {
20+
// SyncEnabled if false will skip syncing the user's organizations.
21+
SyncEnabled bool
22+
// IncludeDefault is primarily for single org deployments. It will ensure
23+
// a user is always inserted into the default org.
24+
IncludeDefault bool
25+
// Organizations is the list of organizations the user should be a member of
26+
// assuming syncing is turned on.
27+
Organizations []uuid.UUID
28+
}
29+
1930
func (AGPLIDPSync) OrganizationSyncEnabled() bool {
2031
// AGPL does not support syncing organizations.
2132
return false

enterprise/coderd/coderd.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,6 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
8080
if options.Entitlements == nil {
8181
options.Entitlements = entitlements.New()
8282
}
83-
if options.IDPSync == nil {
84-
options.IDPSync = enidpsync.NewSync(options.Logger, options.Entitlements, idpsync.SyncSettings{
85-
OrganizationField: options.DeploymentValues.OIDC.OrganizationField.Value(),
86-
OrganizationMapping: options.DeploymentValues.OIDC.OrganizationMapping.Value,
87-
OrganizationAssignDefault: options.DeploymentValues.OIDC.OrganizationAssignDefault.Value(),
88-
})
89-
}
9083

9184
ctx, cancelFunc := context.WithCancel(ctx)
9285

@@ -118,6 +111,15 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
118111
}
119112

120113
options.Database = cryptDB
114+
115+
if options.IDPSync == nil {
116+
options.IDPSync = enidpsync.NewSync(options.Logger, options.Entitlements, idpsync.SyncSettings{
117+
OrganizationField: options.DeploymentValues.OIDC.OrganizationField.Value(),
118+
OrganizationMapping: options.DeploymentValues.OIDC.OrganizationMapping.Value,
119+
OrganizationAssignDefault: options.DeploymentValues.OIDC.OrganizationAssignDefault.Value(),
120+
})
121+
}
122+
121123
api := &API{
122124
ctx: ctx,
123125
cancel: cancelFunc,

enterprise/coderd/enidpsync/enidpsync.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package enidpsync
22

33
import (
44
"cdr.dev/slog"
5-
65
"github.com/coder/coder/v2/coderd/entitlements"
76
"github.com/coder/coder/v2/coderd/idpsync"
87
)

enterprise/coderd/enidpsync/groups.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package enidpsync
2+
3+
import (
4+
"context"
5+
6+
"github.com/golang-jwt/jwt/v4"
7+
8+
"github.com/coder/coder/v2/coderd/idpsync"
9+
"github.com/coder/coder/v2/codersdk"
10+
)
11+
12+
func (e EnterpriseIDPSync) GroupSyncEnabled() bool {
13+
return e.entitlements.Enabled(codersdk.FeatureTemplateRBAC)
14+
15+
}
16+
17+
// ParseGroupClaims returns the groups from the external IDP.
18+
// TODO: Implement group allow_list behavior here since that is deployment wide.
19+
func (e EnterpriseIDPSync) ParseGroupClaims(ctx context.Context, mergedClaims jwt.MapClaims) (idpsync.GroupParams, *idpsync.HTTPError) {
20+
if !e.GroupSyncEnabled() {
21+
return e.AGPLIDPSync.ParseGroupClaims(ctx, mergedClaims)
22+
}
23+
24+
return idpsync.GroupParams{
25+
SyncEnabled: e.OrganizationSyncEnabled(),
26+
MergedClaims: mergedClaims,
27+
}, nil
28+
}

0 commit comments

Comments
 (0)