feat: create user scim v2 endpoint (#9132)

# Which Problems Are Solved
- Adds infrastructure code (basic implementation, error handling,
middlewares, ...) to implement the SCIM v2 interface
- Adds support for the user create SCIM v2 endpoint

# How the Problems Are Solved
- Adds support for the user create SCIM v2 endpoint under `POST
/scim/v2/{orgID}/Users`

# Additional Context

Part of #8140
This commit is contained in:
Lars
2025-01-09 12:46:36 +01:00
committed by GitHub
parent 829f4543da
commit e621224ab2
44 changed files with 2412 additions and 48 deletions

View File

@@ -2,12 +2,15 @@ package middleware
import (
"context"
"errors"
"net/http"
"strings"
"github.com/gorilla/mux"
"github.com/zitadel/zitadel/internal/api/authz"
http_util "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
)
type AuthInterceptor struct {
@@ -23,34 +26,40 @@ func AuthorizationInterceptor(verifier authz.APITokenVerifier, authConfig authz.
}
func (a *AuthInterceptor) Handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx, err := authorize(r, a.verifier, a.authConfig)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
})
return a.HandlerFunc(next)
}
func (a *AuthInterceptor) HandlerFunc(next http.HandlerFunc) http.HandlerFunc {
func (a *AuthInterceptor) HandlerFunc(next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx, err := authorize(r, a.verifier, a.authConfig)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
}
}
func (a *AuthInterceptor) HandlerFuncWithError(next HandlerFuncWithError) HandlerFuncWithError {
return func(w http.ResponseWriter, r *http.Request) error {
ctx, err := authorize(r, a.verifier, a.authConfig)
if err != nil {
return err
}
r = r.WithContext(ctx)
return next(w, r)
}
}
type httpReq struct{}
func authorize(r *http.Request, verifier authz.APITokenVerifier, authConfig authz.Config) (_ context.Context, err error) {
ctx := r.Context()
authOpt, needsToken := verifier.CheckAuthMethod(r.Method + ":" + r.RequestURI)
authOpt, needsToken := checkAuthMethod(r, verifier)
if !needsToken {
return ctx, nil
}
@@ -59,7 +68,7 @@ func authorize(r *http.Request, verifier authz.APITokenVerifier, authConfig auth
authToken := http_util.GetAuthorization(r)
if authToken == "" {
return nil, errors.New("auth header missing")
return nil, zerrors.ThrowUnauthenticated(nil, "AUT-1179", "auth header missing")
}
ctxSetter, err := authz.CheckUserAuthorization(authCtx, &httpReq{}, authToken, http_util.GetOrgID(r), "", verifier, authConfig, authOpt, r.RequestURI)
@@ -69,3 +78,30 @@ func authorize(r *http.Request, verifier authz.APITokenVerifier, authConfig auth
span.End()
return ctxSetter(ctx), nil
}
func checkAuthMethod(r *http.Request, verifier authz.APITokenVerifier) (authz.Option, bool) {
authOpt, needsToken := verifier.CheckAuthMethod(r.Method + ":" + r.RequestURI)
if needsToken {
return authOpt, true
}
route := mux.CurrentRoute(r)
if route == nil {
return authOpt, false
}
pathTemplate, err := route.GetPathTemplate()
if err != nil || pathTemplate == "" {
return authOpt, false
}
// the path prefix is usually handled in a router in upper layer
// trim the query and the path of the url to get the correct path prefix
pathPrefix := r.RequestURI
if i := strings.Index(pathPrefix, "?"); i != -1 {
pathPrefix = pathPrefix[0:i]
}
pathPrefix = strings.TrimSuffix(pathPrefix, r.URL.Path)
return verifier.CheckAuthMethod(r.Method + ":" + pathPrefix + pathTemplate)
}