@@ -3,6 +3,7 @@ package scim
33import (
44 "encoding/json"
55 "net/http"
6+ "strings"
67
78 "github.com/elimity-com/scim/errors"
89 "github.com/elimity-com/scim/schema"
@@ -334,6 +335,132 @@ func (s Server) resourcesGetHandler(w http.ResponseWriter, r *http.Request, reso
334335 }
335336}
336337
338+ // rootResourcesGetHandler receives an HTTP GET request to the server root endpoint to query across all resource types.
339+ func (s Server ) rootResourcesGetHandler (w http.ResponseWriter , r * http.Request ) {
340+ count , startIndex , scimErr := s .parsePaginationParams (r )
341+ if scimErr != nil {
342+ s .errorHandler (w , scimErr )
343+ return
344+ }
345+
346+ params := ListRequestParams {
347+ Count : count ,
348+ Filter : strings .TrimSpace (r .URL .Query ().Get ("filter" )),
349+ StartIndex : startIndex ,
350+ }
351+
352+ page , getError := s .rootQueryHandler .GetAll (r , params )
353+ if getError != nil {
354+ scimErr := errors .CheckScimError (getError , http .MethodGet )
355+ s .errorHandler (w , & scimErr )
356+ return
357+ }
358+
359+ lr := listResponse {
360+ TotalResults : page .TotalResults ,
361+ Resources : page .rawResources (),
362+ StartIndex : params .StartIndex ,
363+ ItemsPerPage : params .Count ,
364+ }
365+ raw , err := json .Marshal (lr )
366+ if err != nil {
367+ s .errorHandler (w , & errors .ScimErrorInternal )
368+ s .log .Error (
369+ "failed marshaling list response" ,
370+ "listResponse" , lr ,
371+ "error" , err ,
372+ )
373+ return
374+ }
375+
376+ _ , err = w .Write (raw )
377+ if err != nil {
378+ s .log .Error (
379+ "failed writing response" ,
380+ "error" , err ,
381+ )
382+ }
383+ }
384+
385+ // rootSearchHandler receives an HTTP POST request to /.search to query across all resource types.
386+ // Per RFC 7644 Section 3.4.3, this is an alternative to GET / with query parameters.
387+ func (s Server ) rootSearchHandler (w http.ResponseWriter , r * http.Request ) {
388+ data , err := readBody (r )
389+ if err != nil {
390+ s .errorHandler (w , & errors .ScimErrorInternal )
391+ return
392+ }
393+
394+ var sr searchRequest
395+ if err := json .Unmarshal (data , & sr ); err != nil {
396+ scimErr := errors.ScimError {
397+ Status : http .StatusBadRequest ,
398+ Detail : "Invalid search request body." ,
399+ }
400+ s .errorHandler (w , & scimErr )
401+ return
402+ }
403+
404+ defaultCount := s .config .getItemsPerPage ()
405+
406+ count := defaultCount
407+ if sr .Count != nil {
408+ count = * sr .Count
409+ }
410+ if count > defaultCount {
411+ count = defaultCount
412+ }
413+ if count < 0 {
414+ count = 0
415+ }
416+
417+ startIndex := defaultStartIndex
418+ if sr .StartIndex != nil {
419+ startIndex = * sr .StartIndex
420+ }
421+ if startIndex < 1 {
422+ startIndex = defaultStartIndex
423+ }
424+
425+ params := ListRequestParams {
426+ Count : count ,
427+ Filter : sr .Filter ,
428+ StartIndex : startIndex ,
429+ }
430+
431+ page , getError := s .rootQueryHandler .GetAll (r , params )
432+ if getError != nil {
433+ scimErr := errors .CheckScimError (getError , http .MethodPost )
434+ s .errorHandler (w , & scimErr )
435+ return
436+ }
437+
438+ lr := listResponse {
439+ TotalResults : page .TotalResults ,
440+ Resources : page .rawResources (),
441+ StartIndex : params .StartIndex ,
442+ ItemsPerPage : params .Count ,
443+ }
444+ raw , err := json .Marshal (lr )
445+ if err != nil {
446+ s .errorHandler (w , & errors .ScimErrorInternal )
447+ s .log .Error (
448+ "failed marshaling list response" ,
449+ "listResponse" , lr ,
450+ "error" , err ,
451+ )
452+ return
453+ }
454+
455+ _ , err = w .Write (raw )
456+ if err != nil {
457+ s .log .Error (
458+ "failed writing response" ,
459+ "error" , err ,
460+ )
461+ }
462+ }
463+
337464// schemaHandler receives an HTTP GET to retrieve individual schema definitions which can be returned by appending the
338465// schema URI to the /Schemas endpoint. For example: "/Schemas/urn:ietf:params:scim:schemas:core:2.0:User".
339466func (s Server ) schemaHandler (w http.ResponseWriter , r * http.Request , id string ) {
@@ -440,3 +567,11 @@ func (s Server) serviceProviderConfigHandler(w http.ResponseWriter, r *http.Requ
440567 )
441568 }
442569}
570+
571+ // searchRequest represents the JSON body of a POST /.search request per RFC 7644 Section 3.4.3.
572+ type searchRequest struct {
573+ Schemas []string `json:"schemas"`
574+ Filter string `json:"filter"`
575+ StartIndex * int `json:"startIndex"`
576+ Count * int `json:"count"`
577+ }
0 commit comments