@@ -20,7 +20,6 @@ import (
20
20
"github.com/google/go-github/v43/github"
21
21
"github.com/google/uuid"
22
22
"github.com/moby/moby/pkg/namesgenerator"
23
- "golang.org/x/exp/slices"
24
23
"golang.org/x/oauth2"
25
24
"golang.org/x/xerrors"
26
25
@@ -659,6 +658,9 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
659
658
AvatarURL : ghUser .GetAvatarURL (),
660
659
Name : normName ,
661
660
DebugContext : OauthDebugContext {},
661
+ GroupSync : idpsync.GroupParams {
662
+ SyncEnabled : false ,
663
+ },
662
664
OrganizationSync : idpsync.OrganizationParams {
663
665
SyncEnabled : false ,
664
666
IncludeDefault : true ,
@@ -1004,11 +1006,6 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
1004
1006
}
1005
1007
1006
1008
ctx = slog .With (ctx , slog .F ("email" , email ), slog .F ("username" , username ), slog .F ("name" , name ))
1007
- usingGroups , groups , groupErr := api .oidcGroups (ctx , mergedClaims )
1008
- if groupErr != nil {
1009
- groupErr .Write (rw , r )
1010
- return
1011
- }
1012
1009
1013
1010
roles , roleErr := api .oidcRoles (ctx , mergedClaims )
1014
1011
if roleErr != nil {
@@ -1032,30 +1029,33 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
1032
1029
return
1033
1030
}
1034
1031
1032
+ groupSync , groupSyncErr := api .IDPSync .ParseGroupClaims (ctx , mergedClaims )
1033
+ if groupSyncErr != nil {
1034
+ groupSyncErr .Write (rw , r )
1035
+ return
1036
+ }
1037
+
1035
1038
// If a new user is authenticating for the first time
1036
1039
// the audit action is 'register', not 'login'
1037
1040
if user .ID == uuid .Nil {
1038
1041
aReq .Action = database .AuditActionRegister
1039
1042
}
1040
1043
1041
1044
params := (& oauthLoginParams {
1042
- User : user ,
1043
- Link : link ,
1044
- State : state ,
1045
- LinkedID : oidcLinkedID (idToken ),
1046
- LoginType : database .LoginTypeOIDC ,
1047
- AllowSignups : api .OIDCConfig .AllowSignups ,
1048
- Email : email ,
1049
- Username : username ,
1050
- Name : name ,
1051
- AvatarURL : picture ,
1052
- UsingRoles : api .OIDCConfig .RoleSyncEnabled (),
1053
- Roles : roles ,
1054
- UsingGroups : usingGroups ,
1055
- Groups : groups ,
1056
- OrganizationSync : orgSync ,
1057
- CreateMissingGroups : api .OIDCConfig .CreateMissingGroups ,
1058
- GroupFilter : api .OIDCConfig .GroupFilter ,
1045
+ User : user ,
1046
+ Link : link ,
1047
+ State : state ,
1048
+ LinkedID : oidcLinkedID (idToken ),
1049
+ LoginType : database .LoginTypeOIDC ,
1050
+ AllowSignups : api .OIDCConfig .AllowSignups ,
1051
+ Email : email ,
1052
+ Username : username ,
1053
+ Name : name ,
1054
+ AvatarURL : picture ,
1055
+ UsingRoles : api .OIDCConfig .RoleSyncEnabled (),
1056
+ Roles : roles ,
1057
+ OrganizationSync : orgSync ,
1058
+ GroupSync : groupSync ,
1059
1059
DebugContext : OauthDebugContext {
1060
1060
IDTokenClaims : idtokenClaims ,
1061
1061
UserInfoClaims : userInfoClaims ,
@@ -1091,79 +1091,6 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
1091
1091
http .Redirect (rw , r , redirect , http .StatusTemporaryRedirect )
1092
1092
}
1093
1093
1094
- // oidcGroups returns the groups for the user from the OIDC claims.
1095
- func (api * API ) oidcGroups (ctx context.Context , mergedClaims map [string ]interface {}) (bool , []string , * idpsync.HTTPError ) {
1096
- logger := api .Logger .Named (userAuthLoggerName )
1097
- usingGroups := false
1098
- var groups []string
1099
-
1100
- // If the GroupField is the empty string, then groups from OIDC are not used.
1101
- // This is so we can support manual group assignment.
1102
- if api .OIDCConfig .GroupField != "" {
1103
- // If the allow list is empty, then the user is allowed to log in.
1104
- // Otherwise, they must belong to at least 1 group in the allow list.
1105
- inAllowList := len (api .OIDCConfig .GroupAllowList ) == 0
1106
-
1107
- usingGroups = true
1108
- groupsRaw , ok := mergedClaims [api .OIDCConfig .GroupField ]
1109
- if ok {
1110
- parsedGroups , err := idpsync .ParseStringSliceClaim (groupsRaw )
1111
- if err != nil {
1112
- api .Logger .Debug (ctx , "groups field was an unknown type in oidc claims" ,
1113
- slog .F ("type" , fmt .Sprintf ("%T" , groupsRaw )),
1114
- slog .Error (err ),
1115
- )
1116
- return false , nil , & idpsync.HTTPError {
1117
- Code : http .StatusBadRequest ,
1118
- Msg : "Failed to sync groups from OIDC claims" ,
1119
- Detail : err .Error (),
1120
- RenderStaticPage : false ,
1121
- }
1122
- }
1123
-
1124
- api .Logger .Debug (ctx , "groups returned in oidc claims" ,
1125
- slog .F ("len" , len (parsedGroups )),
1126
- slog .F ("groups" , parsedGroups ),
1127
- )
1128
-
1129
- for _ , group := range parsedGroups {
1130
- if mappedGroup , ok := api .OIDCConfig .GroupMapping [group ]; ok {
1131
- group = mappedGroup
1132
- }
1133
- if _ , ok := api .OIDCConfig .GroupAllowList [group ]; ok {
1134
- inAllowList = true
1135
- }
1136
- groups = append (groups , group )
1137
- }
1138
- }
1139
-
1140
- if ! inAllowList {
1141
- logger .Debug (ctx , "oidc group claim not in allow list, rejecting login" ,
1142
- slog .F ("allow_list_count" , len (api .OIDCConfig .GroupAllowList )),
1143
- slog .F ("user_group_count" , len (groups )),
1144
- )
1145
- detail := "Ask an administrator to add one of your groups to the allow list"
1146
- if len (groups ) == 0 {
1147
- detail = "You are currently not a member of any groups! Ask an administrator to add you to an authorized group to login."
1148
- }
1149
- return usingGroups , groups , & idpsync.HTTPError {
1150
- Code : http .StatusForbidden ,
1151
- Msg : "Not a member of an allowed group" ,
1152
- Detail : detail ,
1153
- RenderStaticPage : true ,
1154
- }
1155
- }
1156
- }
1157
-
1158
- // This conditional is purely to warn the user they might have misconfigured their OIDC
1159
- // configuration.
1160
- if _ , groupClaimExists := mergedClaims ["groups" ]; ! usingGroups && groupClaimExists {
1161
- logger .Debug (ctx , "claim 'groups' was returned, but 'oidc-group-field' is not set, check your coder oidc settings" )
1162
- }
1163
-
1164
- return usingGroups , groups , nil
1165
- }
1166
-
1167
1094
// oidcRoles returns the roles for the user from the OIDC claims.
1168
1095
// If the function returns false, then the caller should return early.
1169
1096
// All writes to the response writer are handled by this function.
@@ -1278,14 +1205,7 @@ type oauthLoginParams struct {
1278
1205
AvatarURL string
1279
1206
// OrganizationSync has the organizations that the user will be assigned to.
1280
1207
OrganizationSync idpsync.OrganizationParams
1281
- // Is UsingGroups is true, then the user will be assigned
1282
- // to the Groups provided.
1283
- UsingGroups bool
1284
- CreateMissingGroups bool
1285
- // These are the group names from the IDP. Internally, they will map to
1286
- // some organization groups.
1287
- Groups []string
1288
- GroupFilter * regexp.Regexp
1208
+ GroupSync idpsync.GroupParams
1289
1209
// Is UsingRoles is true, then the user will be assigned
1290
1210
// the roles provided.
1291
1211
UsingRoles bool
@@ -1491,53 +1411,9 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C
1491
1411
return xerrors .Errorf ("sync organizations: %w" , err )
1492
1412
}
1493
1413
1494
- // Ensure groups are correct.
1495
- // This places all groups into the default organization.
1496
- // To go multi-org, we need to add a mapping feature here to know which
1497
- // groups go to which orgs.
1498
- if params .UsingGroups {
1499
- filtered := params .Groups
1500
- if params .GroupFilter != nil {
1501
- filtered = make ([]string , 0 , len (params .Groups ))
1502
- for _ , group := range params .Groups {
1503
- if params .GroupFilter .MatchString (group ) {
1504
- filtered = append (filtered , group )
1505
- }
1506
- }
1507
- }
1508
-
1509
- //nolint:gocritic // No user present in the context.
1510
- defaultOrganization , err := tx .GetDefaultOrganization (dbauthz .AsSystemRestricted (ctx ))
1511
- if err != nil {
1512
- // If there is no default org, then we can't assign groups.
1513
- // By default, we assume all groups belong to the default org.
1514
- return xerrors .Errorf ("get default organization: %w" , err )
1515
- }
1516
-
1517
- //nolint:gocritic // No user present in the context.
1518
- memberships , err := tx .OrganizationMembers (dbauthz .AsSystemRestricted (ctx ), database.OrganizationMembersParams {
1519
- UserID : user .ID ,
1520
- OrganizationID : uuid .Nil ,
1521
- })
1522
- if err != nil {
1523
- return xerrors .Errorf ("get organization memberships: %w" , err )
1524
- }
1525
-
1526
- // If the user is not in the default organization, then we can't assign groups.
1527
- // A user cannot be in groups to an org they are not a member of.
1528
- if ! slices .ContainsFunc (memberships , func (member database.OrganizationMembersRow ) bool {
1529
- return member .OrganizationMember .OrganizationID == defaultOrganization .ID
1530
- }) {
1531
- return xerrors .Errorf ("user %s is not a member of the default organization, cannot assign to groups in the org" , user .ID )
1532
- }
1533
-
1534
- //nolint:gocritic
1535
- err = api .Options .SetUserGroups (dbauthz .AsSystemRestricted (ctx ), logger , tx , user .ID , map [uuid.UUID ][]string {
1536
- defaultOrganization .ID : filtered ,
1537
- }, params .CreateMissingGroups )
1538
- if err != nil {
1539
- return xerrors .Errorf ("set user groups: %w" , err )
1540
- }
1414
+ err = api .IDPSync .SyncGroups (ctx , tx , user , params .GroupSync )
1415
+ if err != nil {
1416
+ return xerrors .Errorf ("sync groups: %w" , err )
1541
1417
}
1542
1418
1543
1419
// Ensure roles are correct.
0 commit comments