mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
chore: make scim auth header case insensitive for 'bearer' (#15538)
Fixes status codes to return more than 500. The way we were using the package, it always returned a status code 500
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package coderd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
@ -26,16 +27,21 @@ import (
|
||||
)
|
||||
|
||||
func (api *API) scimVerifyAuthHeader(r *http.Request) bool {
|
||||
bearer := []byte("Bearer ")
|
||||
bearer := []byte("bearer ")
|
||||
hdr := []byte(r.Header.Get("Authorization"))
|
||||
|
||||
if len(hdr) >= len(bearer) && subtle.ConstantTimeCompare(hdr[:len(bearer)], bearer) == 1 {
|
||||
// Use toLower to make the comparison case-insensitive.
|
||||
if len(hdr) >= len(bearer) && subtle.ConstantTimeCompare(bytes.ToLower(hdr[:len(bearer)]), bearer) == 1 {
|
||||
hdr = hdr[len(bearer):]
|
||||
}
|
||||
|
||||
return len(api.SCIMAPIKey) != 0 && subtle.ConstantTimeCompare(hdr, api.SCIMAPIKey) == 1
|
||||
}
|
||||
|
||||
func scimUnauthorized(rw http.ResponseWriter) {
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusUnauthorized, "invalidAuthorization", xerrors.New("invalid authorization")))
|
||||
}
|
||||
|
||||
// scimServiceProviderConfig returns a static SCIM service provider configuration.
|
||||
//
|
||||
// @Summary SCIM 2.0: Service Provider Config
|
||||
@ -114,7 +120,7 @@ func (api *API) scimServiceProviderConfig(rw http.ResponseWriter, _ *http.Reques
|
||||
//nolint:revive
|
||||
func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) {
|
||||
if !api.scimVerifyAuthHeader(r) {
|
||||
_ = handlerutil.WriteError(rw, spec.Error{Status: http.StatusUnauthorized, Type: "invalidAuthorization"})
|
||||
scimUnauthorized(rw)
|
||||
return
|
||||
}
|
||||
|
||||
@ -142,11 +148,11 @@ func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) {
|
||||
//nolint:revive
|
||||
func (api *API) scimGetUser(rw http.ResponseWriter, r *http.Request) {
|
||||
if !api.scimVerifyAuthHeader(r) {
|
||||
_ = handlerutil.WriteError(rw, spec.Error{Status: http.StatusUnauthorized, Type: "invalidAuthorization"})
|
||||
scimUnauthorized(rw)
|
||||
return
|
||||
}
|
||||
|
||||
_ = handlerutil.WriteError(rw, spec.ErrNotFound)
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusNotFound, spec.ErrNotFound.Type, xerrors.New("endpoint will always return 404")))
|
||||
}
|
||||
|
||||
// We currently use our own struct instead of using the SCIM package. This was
|
||||
@ -192,7 +198,7 @@ var SCIMAuditAdditionalFields = map[string]string{
|
||||
func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if !api.scimVerifyAuthHeader(r) {
|
||||
_ = handlerutil.WriteError(rw, spec.Error{Status: http.StatusUnauthorized, Type: "invalidAuthorization"})
|
||||
scimUnauthorized(rw)
|
||||
return
|
||||
}
|
||||
|
||||
@ -209,7 +215,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
|
||||
var sUser SCIMUser
|
||||
err := json.NewDecoder(r.Body).Decode(&sUser)
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, err)
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusBadRequest, "invalidRequest", err))
|
||||
return
|
||||
}
|
||||
|
||||
@ -222,7 +228,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if email == "" {
|
||||
_ = handlerutil.WriteError(rw, spec.Error{Status: http.StatusBadRequest, Type: "invalidEmail"})
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusBadRequest, "invalidEmail", xerrors.New("no primary email provided")))
|
||||
return
|
||||
}
|
||||
|
||||
@ -232,7 +238,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
|
||||
Username: sUser.UserName,
|
||||
})
|
||||
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
|
||||
_ = handlerutil.WriteError(rw, err)
|
||||
_ = handlerutil.WriteError(rw, err) // internal error
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
@ -248,7 +254,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
|
||||
UpdatedAt: dbtime.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, err)
|
||||
_ = handlerutil.WriteError(rw, err) // internal error
|
||||
return
|
||||
}
|
||||
aReq.New = newUser
|
||||
@ -284,14 +290,14 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
|
||||
//nolint:gocritic // SCIM operations are a system user
|
||||
orgSync, err := api.IDPSync.OrganizationSyncSettings(dbauthz.AsSystemRestricted(ctx), api.Database)
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, xerrors.Errorf("failed to get organization sync settings: %w", err))
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusInternalServerError, "internalError", xerrors.Errorf("failed to get organization sync settings: %w", err)))
|
||||
return
|
||||
}
|
||||
if orgSync.AssignDefault {
|
||||
//nolint:gocritic // SCIM operations are a system user
|
||||
defaultOrganization, err := api.Database.GetDefaultOrganization(dbauthz.AsSystemRestricted(ctx))
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, err)
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusInternalServerError, "internalError", xerrors.Errorf("failed to get default organization: %w", err)))
|
||||
return
|
||||
}
|
||||
organizations = append(organizations, defaultOrganization.ID)
|
||||
@ -309,7 +315,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
|
||||
SkipNotifications: true,
|
||||
})
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, err)
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusInternalServerError, "internalError", xerrors.Errorf("failed to create user: %w", err)))
|
||||
return
|
||||
}
|
||||
aReq.New = dbUser
|
||||
@ -335,7 +341,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) scimPatchUser(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if !api.scimVerifyAuthHeader(r) {
|
||||
_ = handlerutil.WriteError(rw, spec.Error{Status: http.StatusUnauthorized, Type: "invalidAuthorization"})
|
||||
scimUnauthorized(rw)
|
||||
return
|
||||
}
|
||||
|
||||
@ -354,21 +360,21 @@ func (api *API) scimPatchUser(rw http.ResponseWriter, r *http.Request) {
|
||||
var sUser SCIMUser
|
||||
err := json.NewDecoder(r.Body).Decode(&sUser)
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, err)
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusBadRequest, "invalidRequest", err))
|
||||
return
|
||||
}
|
||||
sUser.ID = id
|
||||
|
||||
uid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, spec.Error{Status: http.StatusBadRequest, Type: "invalidId"})
|
||||
_ = handlerutil.WriteError(rw, scim.NewHTTPError(http.StatusBadRequest, "invalidId", xerrors.Errorf("id must be a uuid: %w", err)))
|
||||
return
|
||||
}
|
||||
|
||||
//nolint:gocritic // needed for SCIM
|
||||
dbUser, err := api.Database.GetUserByID(dbauthz.AsSystemRestricted(ctx), uid)
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, err)
|
||||
_ = handlerutil.WriteError(rw, err) // internal error
|
||||
return
|
||||
}
|
||||
aReq.Old = dbUser
|
||||
@ -400,7 +406,7 @@ func (api *API) scimPatchUser(rw http.ResponseWriter, r *http.Request) {
|
||||
UpdatedAt: dbtime.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
_ = handlerutil.WriteError(rw, err)
|
||||
_ = handlerutil.WriteError(rw, err) // internal error
|
||||
return
|
||||
}
|
||||
dbUser = userNew
|
||||
|
Reference in New Issue
Block a user