1
1
package coderd
2
2
3
3
import (
4
+ "bytes"
4
5
"crypto/subtle"
5
6
"database/sql"
6
7
"encoding/json"
@@ -26,16 +27,21 @@ import (
26
27
)
27
28
28
29
func (api * API ) scimVerifyAuthHeader (r * http.Request ) bool {
29
- bearer := []byte ("Bearer " )
30
+ bearer := []byte ("bearer " )
30
31
hdr := []byte (r .Header .Get ("Authorization" ))
31
32
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 {
33
35
hdr = hdr [len (bearer ):]
34
36
}
35
37
36
38
return len (api .SCIMAPIKey ) != 0 && subtle .ConstantTimeCompare (hdr , api .SCIMAPIKey ) == 1
37
39
}
38
40
41
+ func scimUnauthorized (rw http.ResponseWriter ) {
42
+ _ = handlerutil .WriteError (rw , scim .NewHTTPError (http .StatusUnauthorized , "invalidAuthorization" , xerrors .New ("invalid authorization" )))
43
+ }
44
+
39
45
// scimServiceProviderConfig returns a static SCIM service provider configuration.
40
46
//
41
47
// @Summary SCIM 2.0: Service Provider Config
@@ -114,7 +120,7 @@ func (api *API) scimServiceProviderConfig(rw http.ResponseWriter, _ *http.Reques
114
120
//nolint:revive
115
121
func (api * API ) scimGetUsers (rw http.ResponseWriter , r * http.Request ) {
116
122
if ! api .scimVerifyAuthHeader (r ) {
117
- _ = handlerutil . WriteError (rw , spec. Error { Status : http . StatusUnauthorized , Type : "invalidAuthorization" } )
123
+ scimUnauthorized (rw )
118
124
return
119
125
}
120
126
@@ -142,11 +148,11 @@ func (api *API) scimGetUsers(rw http.ResponseWriter, r *http.Request) {
142
148
//nolint:revive
143
149
func (api * API ) scimGetUser (rw http.ResponseWriter , r * http.Request ) {
144
150
if ! api .scimVerifyAuthHeader (r ) {
145
- _ = handlerutil . WriteError (rw , spec. Error { Status : http . StatusUnauthorized , Type : "invalidAuthorization" } )
151
+ scimUnauthorized (rw )
146
152
return
147
153
}
148
154
149
- _ = handlerutil .WriteError (rw , spec .ErrNotFound )
155
+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusNotFound , spec .ErrNotFound . Type , xerrors . New ( "endpoint will always return 404" )) )
150
156
}
151
157
152
158
// We currently use our own struct instead of using the SCIM package. This was
@@ -192,7 +198,7 @@ var SCIMAuditAdditionalFields = map[string]string{
192
198
func (api * API ) scimPostUser (rw http.ResponseWriter , r * http.Request ) {
193
199
ctx := r .Context ()
194
200
if ! api .scimVerifyAuthHeader (r ) {
195
- _ = handlerutil . WriteError (rw , spec. Error { Status : http . StatusUnauthorized , Type : "invalidAuthorization" } )
201
+ scimUnauthorized (rw )
196
202
return
197
203
}
198
204
@@ -209,7 +215,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
209
215
var sUser SCIMUser
210
216
err := json .NewDecoder (r .Body ).Decode (& sUser )
211
217
if err != nil {
212
- _ = handlerutil .WriteError (rw , err )
218
+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusBadRequest , "invalidRequest" , err ) )
213
219
return
214
220
}
215
221
@@ -222,7 +228,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
222
228
}
223
229
224
230
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" )) )
226
232
return
227
233
}
228
234
@@ -232,7 +238,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
232
238
Username : sUser .UserName ,
233
239
})
234
240
if err != nil && ! xerrors .Is (err , sql .ErrNoRows ) {
235
- _ = handlerutil .WriteError (rw , err )
241
+ _ = handlerutil .WriteError (rw , err ) // internal error
236
242
return
237
243
}
238
244
if err == nil {
@@ -248,7 +254,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
248
254
UpdatedAt : dbtime .Now (),
249
255
})
250
256
if err != nil {
251
- _ = handlerutil .WriteError (rw , err )
257
+ _ = handlerutil .WriteError (rw , err ) // internal error
252
258
return
253
259
}
254
260
aReq .New = newUser
@@ -284,14 +290,14 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
284
290
//nolint:gocritic // SCIM operations are a system user
285
291
orgSync , err := api .IDPSync .OrganizationSyncSettings (dbauthz .AsSystemRestricted (ctx ), api .Database )
286
292
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 ) ))
288
294
return
289
295
}
290
296
if orgSync .AssignDefault {
291
297
//nolint:gocritic // SCIM operations are a system user
292
298
defaultOrganization , err := api .Database .GetDefaultOrganization (dbauthz .AsSystemRestricted (ctx ))
293
299
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 )) )
295
301
return
296
302
}
297
303
organizations = append (organizations , defaultOrganization .ID )
@@ -309,7 +315,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
309
315
SkipNotifications : true ,
310
316
})
311
317
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 )) )
313
319
return
314
320
}
315
321
aReq .New = dbUser
@@ -335,7 +341,7 @@ func (api *API) scimPostUser(rw http.ResponseWriter, r *http.Request) {
335
341
func (api * API ) scimPatchUser (rw http.ResponseWriter , r * http.Request ) {
336
342
ctx := r .Context ()
337
343
if ! api .scimVerifyAuthHeader (r ) {
338
- _ = handlerutil . WriteError (rw , spec. Error { Status : http . StatusUnauthorized , Type : "invalidAuthorization" } )
344
+ scimUnauthorized (rw )
339
345
return
340
346
}
341
347
@@ -354,21 +360,21 @@ func (api *API) scimPatchUser(rw http.ResponseWriter, r *http.Request) {
354
360
var sUser SCIMUser
355
361
err := json .NewDecoder (r .Body ).Decode (& sUser )
356
362
if err != nil {
357
- _ = handlerutil .WriteError (rw , err )
363
+ _ = handlerutil .WriteError (rw , scim . NewHTTPError ( http . StatusBadRequest , "invalidRequest" , err ) )
358
364
return
359
365
}
360
366
sUser .ID = id
361
367
362
368
uid , err := uuid .Parse (id )
363
369
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 )) )
365
371
return
366
372
}
367
373
368
374
//nolint:gocritic // needed for SCIM
369
375
dbUser , err := api .Database .GetUserByID (dbauthz .AsSystemRestricted (ctx ), uid )
370
376
if err != nil {
371
- _ = handlerutil .WriteError (rw , err )
377
+ _ = handlerutil .WriteError (rw , err ) // internal error
372
378
return
373
379
}
374
380
aReq .Old = dbUser
@@ -400,7 +406,7 @@ func (api *API) scimPatchUser(rw http.ResponseWriter, r *http.Request) {
400
406
UpdatedAt : dbtime .Now (),
401
407
})
402
408
if err != nil {
403
- _ = handlerutil .WriteError (rw , err )
409
+ _ = handlerutil .WriteError (rw , err ) // internal error
404
410
return
405
411
}
406
412
dbUser = userNew
0 commit comments