feat: assign users to groups returned by OIDC provider (#5965)

This commit is contained in:
Colin Adler
2023-02-02 13:53:48 -06:00
committed by GitHub
parent 026b1cd2a4
commit 496138b086
11 changed files with 477 additions and 133 deletions

View File

@ -311,6 +311,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
if ok {
username, _ = usernameRaw.(string)
}
emailRaw, ok := claims["email"]
if !ok {
// Email is an optional claim in OIDC and
@ -326,6 +327,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
}
emailRaw = username
}
email, ok := emailRaw.(string)
if !ok {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@ -333,6 +335,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
})
return
}
verifiedRaw, ok := claims["email_verified"]
if ok {
verified, ok := verifiedRaw.(bool)
@ -346,6 +349,26 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
api.Logger.Warn(ctx, "allowing unverified oidc email %q")
}
}
var groups []string
groupsRaw, ok := claims["groups"]
if ok {
// Convert the []interface{} we get to a []string.
groupsInterface, ok := groupsRaw.([]interface{})
if ok {
for _, groupInterface := range groupsInterface {
group, ok := groupInterface.(string)
if !ok {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: fmt.Sprintf("Invalid group type. Expected string, got: %t", emailRaw),
})
return
}
groups = append(groups, group)
}
}
}
// The username is a required property in Coder. We make a best-effort
// attempt at using what the claims provide, but if that fails we will
// generate a random username.
@ -359,6 +382,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
}
username = httpapi.UsernameFrom(username)
}
if len(api.OIDCConfig.EmailDomain) > 0 {
ok = false
for _, domain := range api.OIDCConfig.EmailDomain {
@ -374,6 +398,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
return
}
}
var picture string
pictureRaw, ok := claims["picture"]
if ok {
@ -388,6 +413,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
Email: email,
Username: username,
AvatarURL: picture,
Groups: groups,
})
var httpErr httpError
if xerrors.As(err, &httpErr) {
@ -425,6 +451,7 @@ type oauthLoginParams struct {
Email string
Username string
AvatarURL string
Groups []string
}
type httpError struct {
@ -546,22 +573,6 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
}
}
// LEGACY: Remove 10/2022.
// We started tracking linked IDs later so it's possible for a user to be a
// pre-existing OAuth user and not have a linked ID.
// The migration that added the user_links table could not populate
// the 'linked_id' field since it requires fields off the access token.
if link.LinkedID == "" {
link, err = tx.UpdateUserLinkedID(ctx, database.UpdateUserLinkedIDParams{
UserID: user.ID,
LoginType: params.LoginType,
LinkedID: params.LinkedID,
})
if err != nil {
return xerrors.Errorf("update user linked ID: %w", err)
}
}
if link.UserID != uuid.Nil {
link, err = tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
UserID: user.ID,
@ -575,6 +586,14 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
}
}
// Ensure groups are correct.
if len(params.Groups) > 0 {
err := api.Options.SetUserGroups(ctx, tx, user.ID, params.Groups)
if err != nil {
return xerrors.Errorf("set user groups: %w", err)
}
}
needsUpdate := false
if user.AvatarURL.String != params.AvatarURL {
user.AvatarURL = sql.NullString{