2025-01-09 12:46:36 +01:00
package resources
import (
2025-01-29 15:23:56 +01:00
"context"
2025-01-09 12:46:36 +01:00
"encoding/json"
2025-01-29 15:23:56 +01:00
"errors"
"io"
2025-01-09 12:46:36 +01:00
"net/http"
"slices"
2025-01-09 15:12:13 +01:00
"github.com/gorilla/mux"
2025-01-29 15:23:56 +01:00
"github.com/zitadel/logging"
2025-01-09 15:12:13 +01:00
2025-01-27 13:36:07 +01:00
"github.com/zitadel/zitadel/internal/api/scim/resources/patch"
2025-01-29 15:23:56 +01:00
"github.com/zitadel/zitadel/internal/api/scim/schemas"
2025-01-09 12:46:36 +01:00
"github.com/zitadel/zitadel/internal/api/scim/serrors"
"github.com/zitadel/zitadel/internal/zerrors"
)
2025-01-29 15:23:56 +01:00
// RawResourceHandlerAdapter adapts the ResourceHandler[T] without any generics
type RawResourceHandlerAdapter interface {
2025-01-29 19:11:12 +01:00
Schema ( ) * schemas . ResourceSchema
2025-01-29 15:23:56 +01:00
Create ( ctx context . Context , data io . ReadCloser ) ( ResourceHolder , error )
Replace ( ctx context . Context , resourceID string , data io . ReadCloser ) ( ResourceHolder , error )
Update ( ctx context . Context , resourceID string , data io . ReadCloser ) error
Delete ( ctx context . Context , resourceID string ) error
}
2025-01-09 12:46:36 +01:00
type ResourceHandlerAdapter [ T ResourceHolder ] struct {
handler ResourceHandler [ T ]
}
func NewResourceHandlerAdapter [ T ResourceHolder ] ( handler ResourceHandler [ T ] ) * ResourceHandlerAdapter [ T ] {
return & ResourceHandlerAdapter [ T ] {
handler ,
}
}
2025-01-29 19:11:12 +01:00
func ( adapter * ResourceHandlerAdapter [ T ] ) Schema ( ) * schemas . ResourceSchema {
return adapter . handler . Schema ( )
2025-01-29 15:23:56 +01:00
}
func ( adapter * ResourceHandlerAdapter [ T ] ) CreateFromHttp ( r * http . Request ) ( ResourceHolder , error ) {
return adapter . Create ( r . Context ( ) , r . Body )
}
func ( adapter * ResourceHandlerAdapter [ T ] ) Create ( ctx context . Context , data io . ReadCloser ) ( ResourceHolder , error ) {
entity , err := adapter . readEntity ( data )
2025-01-09 12:46:36 +01:00
if err != nil {
return entity , err
}
2025-01-29 15:23:56 +01:00
return adapter . handler . Create ( ctx , entity )
2025-01-09 12:46:36 +01:00
}
2025-01-29 15:23:56 +01:00
func ( adapter * ResourceHandlerAdapter [ T ] ) ReplaceFromHttp ( r * http . Request ) ( ResourceHolder , error ) {
return adapter . Replace ( r . Context ( ) , mux . Vars ( r ) [ "id" ] , r . Body )
}
func ( adapter * ResourceHandlerAdapter [ T ] ) Replace ( ctx context . Context , resourceID string , data io . ReadCloser ) ( ResourceHolder , error ) {
entity , err := adapter . readEntity ( data )
2025-01-14 15:44:41 +01:00
if err != nil {
return entity , err
}
2025-01-29 15:23:56 +01:00
return adapter . handler . Replace ( ctx , resourceID , entity )
2025-01-14 15:44:41 +01:00
}
2025-01-29 15:23:56 +01:00
func ( adapter * ResourceHandlerAdapter [ T ] ) UpdateFromHttp ( r * http . Request ) error {
return adapter . Update ( r . Context ( ) , mux . Vars ( r ) [ "id" ] , r . Body )
}
2025-01-27 13:36:07 +01:00
2025-01-29 15:23:56 +01:00
func ( adapter * ResourceHandlerAdapter [ T ] ) Update ( ctx context . Context , resourceID string , data io . ReadCloser ) error {
request := new ( patch . OperationRequest )
if err := readSchema ( data , request , schemas . IdPatchOperation ) ; err != nil {
return err
2025-01-27 13:36:07 +01:00
}
2025-01-29 15:23:56 +01:00
if err := request . Validate ( ) ; err != nil {
2025-01-27 13:36:07 +01:00
return err
}
if len ( request . Operations ) == 0 {
return nil
}
2025-01-29 15:23:56 +01:00
return adapter . handler . Update ( ctx , resourceID , request . Operations )
2025-01-27 13:36:07 +01:00
}
2025-01-29 15:23:56 +01:00
func ( adapter * ResourceHandlerAdapter [ T ] ) DeleteFromHttp ( r * http . Request ) error {
return adapter . Delete ( r . Context ( ) , mux . Vars ( r ) [ "id" ] )
}
func ( adapter * ResourceHandlerAdapter [ T ] ) Delete ( ctx context . Context , resourceID string ) error {
return adapter . handler . Delete ( ctx , resourceID )
2025-01-09 15:12:13 +01:00
}
2025-01-29 15:23:56 +01:00
func ( adapter * ResourceHandlerAdapter [ T ] ) ListFromHttp ( r * http . Request ) ( * ListResponse [ T ] , error ) {
request , err := adapter . readListRequest ( r )
2025-01-21 13:31:54 +01:00
if err != nil {
return nil , err
}
return adapter . handler . List ( r . Context ( ) , request )
}
2025-01-29 15:23:56 +01:00
func ( adapter * ResourceHandlerAdapter [ T ] ) GetFromHttp ( r * http . Request ) ( T , error ) {
2025-01-10 12:15:06 +01:00
id := mux . Vars ( r ) [ "id" ]
return adapter . handler . Get ( r . Context ( ) , id )
}
2025-01-29 15:23:56 +01:00
func ( adapter * ResourceHandlerAdapter [ T ] ) readEntity ( data io . ReadCloser ) ( T , error ) {
2025-01-09 12:46:36 +01:00
entity := adapter . handler . NewResource ( )
2025-01-29 19:11:12 +01:00
return entity , readSchema ( data , entity , adapter . handler . Schema ( ) . ID )
2025-01-29 15:23:56 +01:00
}
func readSchema ( data io . ReadCloser , entity SchemasHolder , schema schemas . ScimSchemaType ) error {
defer func ( ) {
err := data . Close ( )
logging . OnError ( err ) . Warn ( "Failed to close http request body" )
} ( )
err := json . NewDecoder ( data ) . Decode ( & entity )
2025-01-09 12:46:36 +01:00
if err != nil {
2025-01-29 15:23:56 +01:00
var maxBytesErr * http . MaxBytesError
if errors . As ( err , & maxBytesErr ) {
return serrors . ThrowPayloadTooLarge ( zerrors . ThrowInvalidArgumentf ( err , "SCIM-hmaxb1" , "Request payload too large, max %d bytes allowed." , maxBytesErr . Limit ) )
2025-01-09 12:46:36 +01:00
}
2025-01-29 15:23:56 +01:00
if serrors . IsScimOrZitadelError ( err ) {
return err
}
2025-01-09 12:46:36 +01:00
2025-01-29 15:23:56 +01:00
return serrors . ThrowInvalidSyntax ( zerrors . ThrowInvalidArgumentf ( err , "SCIM-ucrjson" , "Could not deserialize json" ) )
2025-01-09 12:46:36 +01:00
}
2025-01-29 15:23:56 +01:00
providedSchemas := entity . GetSchemas ( )
if ! slices . Contains ( providedSchemas , schema ) {
return serrors . ThrowInvalidSyntax ( zerrors . ThrowInvalidArgumentf ( nil , "SCIM-xxrschema" , "Expected schema %v is not provided" , schema ) )
2025-01-09 12:46:36 +01:00
}
2025-01-29 15:23:56 +01:00
return nil
2025-01-09 12:46:36 +01:00
}