11package coderd
22
33import (
4+ "bytes"
45 "crypto/subtle"
56 "database/sql"
67 "encoding/json"
@@ -26,16 +27,21 @@ import (
2627)
2728
2829func (api * API ) scimVerifyAuthHeader (r * http.Request ) bool {
29- bearer := []byte ("Bearer " )
30+ bearer := []byte ("bearer " )
3031 hdr := []byte (r .Header .Get ("Authorization" ))
3132
32- if len (hdr ) >= len (bearer ) && subtle .ConstantTimeCompare (hdr [:len (bearer )], bearer ) == 1 {
33+ // Use toLower to make the comparison case-insensitive.
34+ if len (hdr ) >= len (bearer ) && subtle .ConstantTimeCompare (bytes .ToLower (hdr [:len (bearer )]), bearer ) == 1 {
3335 hdr = hdr [len (bearer ):]
3436 }
3537
3638 return len (api .SCIMAPIKey ) != 0 && subtle .ConstantTimeCompare (hdr , api .SCIMAPIKey ) == 1
3739}
3840
41+ func scimUnauthorized (rw http.ResponseWriter ) {
42+ _ = handlerutil .WriteError (rw , scim .NewHTTPError (http .StatusUnauthorized , "invalidAuthorization" , xerrors .New ("invalid authorization" )))
43+ }
44+
3945// scimServiceProviderConfig returns a static SCIM service provider configuration.
4046//
4147// @Summary SCIM 2.0: Service Provider Config
@@ -114,7 +120,7 @@ func (api *API) scimServiceProviderConfig(rw http.ResponseWriter, _ *http.Reques
114120//nolint:revive
115121func (api * API ) scimGetUsers (rw http.ResponseWriter , r * http.Request ) {
116122 if ! api .scimVerifyAuthHeader (r ) {
117- _ = handlerutil . WriteError (rw , spec. Error { Status : http . StatusUnauthorized , Type : "invalidAuthorization" } )
123+ scimUnauthorized (rw )
118124 return
119125 }
120126
@@ -142,11 +148,11 @@ func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) {
142148//nolint:revive
143149func (api * API ) scimGetUser (rw http.ResponseWriter , r * http.Request ) {
144150 if ! api .scimVerifyAuthHeader (r ) {
145- _ = handlerutil . WriteError (rw , spec. Error { Status : http . StatusUnauthorized , Type : "invalidAuthorization" } )
151+ scimUnauthorized (rw )
146152 return
147153 }
148154
149- _ = handlerutil .WriteError (rw , spec .ErrNotFound )
155+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusNotFound , spec .ErrNotFound . Type , xerrors . New ( "endpoint will always return 404" )) )
150156}
151157
152158// We currently use our own struct instead of using the SCIM package. This was
@@ -192,7 +198,7 @@ var SCIMAuditAdditionalFields = map[string]string{
192198func (api * API ) scimPostUser (rw http.ResponseWriter , r * http.Request ) {
193199 ctx := r .Context ()
194200 if ! api .scimVerifyAuthHeader (r ) {
195- _ = handlerutil . WriteError (rw , spec. Error { Status : http . StatusUnauthorized , Type : "invalidAuthorization" } )
201+ scimUnauthorized (rw )
196202 return
197203 }
198204
@@ -209,7 +215,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
209215 var sUser SCIMUser
210216 err := json .NewDecoder (r .Body ).Decode (& sUser )
211217 if err != nil {
212- _ = handlerutil .WriteError (rw , err )
218+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusBadRequest , "invalidRequest" , err ) )
213219 return
214220 }
215221
@@ -222,7 +228,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
222228 }
223229
224230 if email == "" {
225- _ = handlerutil .WriteError (rw , spec. Error { Status : http .StatusBadRequest , Type : "invalidEmail" } )
231+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http .StatusBadRequest , "invalidEmail" , xerrors . New ( "no primary email provided" )) )
226232 return
227233 }
228234
@@ -232,7 +238,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
232238 Username : sUser .UserName ,
233239 })
234240 if err != nil && ! xerrors .Is (err , sql .ErrNoRows ) {
235- _ = handlerutil .WriteError (rw , err )
241+ _ = handlerutil .WriteError (rw , err ) // internal error
236242 return
237243 }
238244 if err == nil {
@@ -248,7 +254,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
248254 UpdatedAt : dbtime .Now (),
249255 })
250256 if err != nil {
251- _ = handlerutil .WriteError (rw , err )
257+ _ = handlerutil .WriteError (rw , err ) // internal error
252258 return
253259 }
254260 aReq .New = newUser
@@ -284,14 +290,14 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
284290 //nolint:gocritic // SCIM operations are a system user
285291 orgSync , err := api .IDPSync .OrganizationSyncSettings (dbauthz .AsSystemRestricted (ctx ), api .Database )
286292 if err != nil {
287- _ = handlerutil .WriteError (rw , xerrors .Errorf ("failed to get organization sync settings: %w" , err ))
293+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusInternalServerError , "internalError" , xerrors .Errorf ("failed to get organization sync settings: %w" , err ) ))
288294 return
289295 }
290296 if orgSync .AssignDefault {
291297 //nolint:gocritic // SCIM operations are a system user
292298 defaultOrganization , err := api .Database .GetDefaultOrganization (dbauthz .AsSystemRestricted (ctx ))
293299 if err != nil {
294- _ = handlerutil .WriteError (rw , err )
300+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusInternalServerError , "internalError" , xerrors . Errorf ( "failed to get default organization: %w" , err )) )
295301 return
296302 }
297303 organizations = append (organizations , defaultOrganization .ID )
@@ -309,7 +315,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
309315 SkipNotifications : true ,
310316 })
311317 if err != nil {
312- _ = handlerutil .WriteError (rw , err )
318+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusInternalServerError , "internalError" , xerrors . Errorf ( "failed to create user: %w" , err )) )
313319 return
314320 }
315321 aReq .New = dbUser
@@ -335,7 +341,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
335341func (api * API ) scimPatchUser (rw http.ResponseWriter , r * http.Request ) {
336342 ctx := r .Context ()
337343 if ! api .scimVerifyAuthHeader (r ) {
338- _ = handlerutil . WriteError (rw , spec. Error { Status : http . StatusUnauthorized , Type : "invalidAuthorization" } )
344+ scimUnauthorized (rw )
339345 return
340346 }
341347
@@ -354,21 +360,21 @@ func (api *API) scimPatchUser(rw http.ResponseWriter, r *http.Request) {
354360 var sUser SCIMUser
355361 err := json .NewDecoder (r .Body ).Decode (& sUser )
356362 if err != nil {
357- _ = handlerutil .WriteError (rw , err )
363+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusBadRequest , "invalidRequest" , err ) )
358364 return
359365 }
360366 sUser .ID = id
361367
362368 uid , err := uuid .Parse (id )
363369 if err != nil {
364- _ = handlerutil .WriteError (rw , spec. Error { Status : http .StatusBadRequest , Type : "invalidId" } )
370+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http .StatusBadRequest , "invalidId" , xerrors . Errorf ( "id must be a uuid: %w" , err )) )
365371 return
366372 }
367373
368374 //nolint:gocritic // needed for SCIM
369375 dbUser , err := api .Database .GetUserByID (dbauthz .AsSystemRestricted (ctx ), uid )
370376 if err != nil {
371- _ = handlerutil .WriteError (rw , err )
377+ _ = handlerutil .WriteError (rw , err ) // internal error
372378 return
373379 }
374380 aReq .Old = dbUser
@@ -400,7 +406,7 @@ func (api *API) scimPatchUser(rw http.ResponseWriter, r *http.Request) {
400406 UpdatedAt : dbtime .Now (),
401407 })
402408 if err != nil {
403- _ = handlerutil .WriteError (rw , err )
409+ _ = handlerutil .WriteError (rw , err ) // internal error
404410 return
405411 }
406412 dbUser = userNew
0 commit comments