mirror of
https://github.com/zitadel/zitadel.git
synced 2025-11-01 00:46:23 +00:00
fix: use a single translator for middleware (#10633)
# Which Problems Are Solved Comparing the v3 and v4 deployments we noticed an increase in memory usage. A first analysis revealed that it might be related to the (multiple) initialization of the `i18n.Translator`, partially related # How the Problems Are Solved Initialize the tranlator once (apart from the translator interceptor, which uses context / request specific information) and pass it to all necessary middleware. # Additional Changes Removed unnecessary error return parameter from the translator initialization. # Additional Context - noticed internally - backport to v4.x
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/zitadel/saml/pkg/provider"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/cmd/build"
|
||||
"github.com/zitadel/zitadel/cmd/encryption"
|
||||
@@ -438,7 +439,22 @@ func startAPIs(
|
||||
http_util.WithMaxAge(int(math.Floor(config.Quotas.Access.ExhaustedCookieMaxAge.Seconds()))),
|
||||
)
|
||||
limitingAccessInterceptor := middleware.NewAccessInterceptor(accessSvc, exhaustedCookieHandler, &config.Quotas.Access.AccessConfig)
|
||||
apis, err := api.New(ctx, config.Port, router, queries, verifier, config.SystemAuthZ, config.InternalAuthZ, tlsConfig, config.ExternalDomain, append(config.InstanceHostHeaders, config.PublicHostHeaders...), limitingAccessInterceptor, keys.Target)
|
||||
translator := i18n.NewZitadelTranslator(language.English)
|
||||
apis, err := api.New(
|
||||
ctx,
|
||||
config.Port,
|
||||
router,
|
||||
queries,
|
||||
verifier,
|
||||
config.SystemAuthZ,
|
||||
config.InternalAuthZ,
|
||||
tlsConfig,
|
||||
config.ExternalDomain,
|
||||
append(config.InstanceHostHeaders, config.PublicHostHeaders...),
|
||||
limitingAccessInterceptor,
|
||||
keys.Target,
|
||||
translator,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating api %w", err)
|
||||
}
|
||||
@@ -541,9 +557,22 @@ func startAPIs(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceInterceptor := middleware.InstanceInterceptor(queries, config.ExternalDomain, login.IgnoreInstanceEndpoints...)
|
||||
instanceInterceptor := middleware.InstanceInterceptor(queries, config.ExternalDomain, translator, login.IgnoreInstanceEndpoints...)
|
||||
assetsCache := middleware.AssetsCacheInterceptor(config.AssetStorage.Cache.MaxAge, config.AssetStorage.Cache.SharedMaxAge)
|
||||
apis.RegisterHandlerOnPrefix(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.SystemAuthZ, config.InternalAuthZ, id.SonyFlakeGenerator(), store, queries, middleware.CallDurationHandler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.Handle))
|
||||
apis.RegisterHandlerOnPrefix(assets.HandlerPrefix, assets.NewHandler(
|
||||
commands,
|
||||
verifier,
|
||||
config.SystemAuthZ,
|
||||
config.InternalAuthZ,
|
||||
id.SonyFlakeGenerator(),
|
||||
store,
|
||||
queries,
|
||||
middleware.CallDurationHandler,
|
||||
instanceInterceptor.Handler,
|
||||
assetsCache.Handler,
|
||||
limitingAccessInterceptor.Handle,
|
||||
translator,
|
||||
))
|
||||
|
||||
federatedLogoutsCache, err := connector.StartCache[federatedlogout.Index, string, *federatedlogout.FederatedLogout](ctx, []federatedlogout.Index{federatedlogout.IndexRequestID}, cache.PurposeFederatedLogout, cacheConnectors.Config.FederatedLogouts, cacheConnectors)
|
||||
if err != nil {
|
||||
@@ -609,6 +638,7 @@ func startAPIs(
|
||||
verifier,
|
||||
keys.User,
|
||||
&config.SCIM,
|
||||
translator,
|
||||
instanceInterceptor.HandlerFuncWithError,
|
||||
middleware.AuthorizationInterceptor(verifier, config.SystemAuthZ, config.InternalAuthZ).HandlerFuncWithError))
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
http_mw "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||
"github.com/zitadel/zitadel/internal/api/ui/login"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/metrics"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
@@ -51,6 +52,7 @@ type API struct {
|
||||
connectServices map[string][]string
|
||||
|
||||
targetEncryptionAlgorithm crypto.EncryptionAlgorithm
|
||||
translator *i18n.Translator
|
||||
}
|
||||
|
||||
func (a *API) ListGrpcServices() []string {
|
||||
@@ -103,6 +105,7 @@ func New(
|
||||
hostHeaders []string,
|
||||
accessInterceptor *http_mw.AccessInterceptor,
|
||||
targetEncryptionAlgorithm crypto.EncryptionAlgorithm,
|
||||
translator *i18n.Translator,
|
||||
) (_ *API, err error) {
|
||||
api := &API{
|
||||
port: port,
|
||||
@@ -117,9 +120,10 @@ func New(
|
||||
systemAuthZ: systemAuthz,
|
||||
connectServices: make(map[string][]string),
|
||||
targetEncryptionAlgorithm: targetEncryptionAlgorithm,
|
||||
translator: translator,
|
||||
}
|
||||
|
||||
api.grpcServer = server.CreateServer(api.verifier, systemAuthz, authZ, queries, externalDomain, tlsConfig, accessInterceptor.AccessService(), targetEncryptionAlgorithm)
|
||||
api.grpcServer = server.CreateServer(api.verifier, systemAuthz, authZ, queries, externalDomain, tlsConfig, accessInterceptor.AccessService(), targetEncryptionAlgorithm, api.translator)
|
||||
api.grpcGateway, err = server.CreateGateway(ctx, port, hostHeaders, accessInterceptor, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -188,7 +192,7 @@ func (a *API) registerConnectServer(service server.ConnectServer) {
|
||||
connect_middleware.CallDurationHandler(),
|
||||
connect_middleware.MetricsHandler(metricTypes, grpc_api.Probes...),
|
||||
connect_middleware.NoCacheInterceptor(),
|
||||
connect_middleware.InstanceInterceptor(a.queries, a.externalDomain, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
|
||||
connect_middleware.InstanceInterceptor(a.queries, a.externalDomain, a.translator, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
|
||||
connect_middleware.AccessStorageInterceptor(a.accessInterceptor.AccessService()),
|
||||
connect_middleware.ErrorHandler(),
|
||||
connect_middleware.LimitsInterceptor(system_pb.SystemService_ServiceDesc.ServiceName),
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||
@@ -94,9 +93,17 @@ func DefaultErrorHandler(translator *i18n.Translator) func(w http.ResponseWriter
|
||||
}
|
||||
}
|
||||
|
||||
func NewHandler(commands *command.Commands, verifier authz.APITokenVerifier, systemAuthCOnfig authz.Config, authConfig authz.Config, idGenerator id.Generator, storage static.Storage, queries *query.Queries, callDurationInterceptor, instanceInterceptor, assetCacheInterceptor, accessInterceptor func(handler http.Handler) http.Handler) http.Handler {
|
||||
translator, err := i18n.NewZitadelTranslator(language.English)
|
||||
logging.OnError(err).Panic("unable to get translator")
|
||||
func NewHandler(
|
||||
commands *command.Commands,
|
||||
verifier authz.APITokenVerifier,
|
||||
systemAuthCOnfig authz.Config,
|
||||
authConfig authz.Config,
|
||||
idGenerator id.Generator,
|
||||
storage static.Storage,
|
||||
queries *query.Queries,
|
||||
callDurationInterceptor, instanceInterceptor, assetCacheInterceptor, accessInterceptor func(handler http.Handler) http.Handler,
|
||||
translator *i18n.Translator,
|
||||
) http.Handler {
|
||||
h := &Handler{
|
||||
commands: commands,
|
||||
errorHandler: DefaultErrorHandler(translator),
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
zitadel_http "github.com/zitadel/zitadel/internal/api/http"
|
||||
@@ -18,9 +17,7 @@ import (
|
||||
object_v3 "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
|
||||
)
|
||||
|
||||
func InstanceInterceptor(verifier authz.InstanceVerifier, externalDomain string, explicitInstanceIdServices ...string) connect.UnaryInterceptorFunc {
|
||||
translator, err := i18n.NewZitadelTranslator(language.English)
|
||||
logging.OnError(err).Panic("unable to get translator")
|
||||
func InstanceInterceptor(verifier authz.InstanceVerifier, externalDomain string, translator *i18n.Translator, explicitInstanceIdServices ...string) connect.UnaryInterceptorFunc {
|
||||
return func(handler connect.UnaryFunc) connect.UnaryFunc {
|
||||
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
|
||||
return setInstance(ctx, req, handler, verifier, externalDomain, translator, explicitInstanceIdServices...)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
@@ -21,17 +20,11 @@ func TranslationHandler() connect.UnaryInterceptorFunc {
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if err != nil {
|
||||
translator, translatorError := getTranslator(ctx)
|
||||
if translatorError != nil {
|
||||
return resp, err
|
||||
}
|
||||
translator := getTranslator(ctx)
|
||||
return resp, translateError(ctx, err, translator)
|
||||
}
|
||||
if loc, ok := resp.Any().(localizers); ok {
|
||||
translator, translatorError := getTranslator(ctx)
|
||||
if translatorError != nil {
|
||||
return resp, err
|
||||
}
|
||||
translator := getTranslator(ctx)
|
||||
translateFields(ctx, loc, translator)
|
||||
}
|
||||
return resp, nil
|
||||
@@ -39,10 +32,6 @@ func TranslationHandler() connect.UnaryInterceptorFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func getTranslator(ctx context.Context) (*i18n.Translator, error) {
|
||||
translator, err := i18n.NewZitadelTranslator(authz.GetInstance(ctx).DefaultLanguage())
|
||||
if err != nil {
|
||||
logging.New().WithError(err).Error("could not load translator")
|
||||
}
|
||||
return translator, err
|
||||
func getTranslator(ctx context.Context) *i18n.Translator {
|
||||
return i18n.NewZitadelTranslator(authz.GetInstance(ctx).DefaultLanguage())
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/text/language"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -24,9 +23,7 @@ const (
|
||||
HTTP1Host = "x-zitadel-http1-host"
|
||||
)
|
||||
|
||||
func InstanceInterceptor(verifier authz.InstanceVerifier, externalDomain string, explicitInstanceIdServices ...string) grpc.UnaryServerInterceptor {
|
||||
translator, err := i18n.NewZitadelTranslator(language.English)
|
||||
logging.OnError(err).Panic("unable to get translator")
|
||||
func InstanceInterceptor(verifier authz.InstanceVerifier, externalDomain string, translator *i18n.Translator, explicitInstanceIdServices ...string) grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
return setInstance(ctx, req, info, handler, verifier, externalDomain, translator, explicitInstanceIdServices...)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package middleware
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
@@ -19,27 +18,17 @@ func TranslationHandler() func(ctx context.Context, req interface{}, info *grpc.
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if loc, ok := resp.(localizers); ok && resp != nil {
|
||||
translator, translatorError := getTranslator(ctx)
|
||||
if translatorError != nil {
|
||||
return resp, err
|
||||
}
|
||||
translator := getTranslator(ctx)
|
||||
translateFields(ctx, loc, translator)
|
||||
}
|
||||
if err != nil {
|
||||
translator, translatorError := getTranslator(ctx)
|
||||
if translatorError != nil {
|
||||
return resp, err
|
||||
}
|
||||
translator := getTranslator(ctx)
|
||||
err = translateError(ctx, err, translator)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
func getTranslator(ctx context.Context) (*i18n.Translator, error) {
|
||||
translator, err := i18n.NewZitadelTranslator(authz.GetInstance(ctx).DefaultLanguage())
|
||||
if err != nil {
|
||||
logging.New().WithError(err).Error("could not load translator")
|
||||
}
|
||||
return translator, err
|
||||
func getTranslator(ctx context.Context) *i18n.Translator {
|
||||
return i18n.NewZitadelTranslator(authz.GetInstance(ctx).DefaultLanguage())
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
grpc_api "github.com/zitadel/zitadel/internal/api/grpc"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server/middleware"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
"github.com/zitadel/zitadel/internal/logstore"
|
||||
"github.com/zitadel/zitadel/internal/logstore/record"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
@@ -62,6 +63,7 @@ func CreateServer(
|
||||
tlsConfig *tls.Config,
|
||||
accessSvc *logstore.Service[*record.AccessLog],
|
||||
targetEncAlg crypto.EncryptionAlgorithm,
|
||||
translator *i18n.Translator,
|
||||
) *grpc.Server {
|
||||
metricTypes := []metrics.MetricType{metrics.MetricTypeTotalCount, metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode}
|
||||
serverOptions := []grpc.ServerOption{
|
||||
@@ -70,7 +72,7 @@ func CreateServer(
|
||||
middleware.CallDurationHandler(),
|
||||
middleware.MetricsHandler(metricTypes, grpc_api.Probes...),
|
||||
middleware.NoCacheInterceptor(),
|
||||
middleware.InstanceInterceptor(queries, externalDomain, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
|
||||
middleware.InstanceInterceptor(queries, externalDomain, translator, system_pb.SystemService_ServiceDesc.ServiceName, healthpb.Health_ServiceDesc.ServiceName),
|
||||
middleware.AccessStorageInterceptor(accessSvc),
|
||||
middleware.ErrorHandler(),
|
||||
middleware.LimitsInterceptor(system_pb.SystemService_ServiceDesc.ServiceName),
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
zitadel_http "github.com/zitadel/zitadel/internal/api/http"
|
||||
@@ -24,12 +23,12 @@ type instanceInterceptor struct {
|
||||
translator *i18n.Translator
|
||||
}
|
||||
|
||||
func InstanceInterceptor(verifier authz.InstanceVerifier, externalDomain string, ignoredPrefixes ...string) *instanceInterceptor {
|
||||
func InstanceInterceptor(verifier authz.InstanceVerifier, externalDomain string, translator *i18n.Translator, ignoredPrefixes ...string) *instanceInterceptor {
|
||||
return &instanceInterceptor{
|
||||
verifier: verifier,
|
||||
externalDomain: externalDomain,
|
||||
ignoredPrefixes: ignoredPrefixes,
|
||||
translator: newZitadelTranslator(),
|
||||
translator: translator,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +98,3 @@ func setInstance(ctx context.Context, verifier authz.InstanceVerifier) (_ contex
|
||||
span.End()
|
||||
return authz.WithInstance(ctx, instance), nil
|
||||
}
|
||||
|
||||
func newZitadelTranslator() *i18n.Translator {
|
||||
translator, err := i18n.NewZitadelTranslator(language.English)
|
||||
logging.OnError(err).Panic("unable to get translator")
|
||||
return translator
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
zitadel_http "github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/execution/target"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
)
|
||||
|
||||
func Test_instanceInterceptor_Handler(t *testing.T) {
|
||||
@@ -70,7 +71,7 @@ func Test_instanceInterceptor_Handler(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &instanceInterceptor{
|
||||
verifier: tt.fields.verifier,
|
||||
translator: newZitadelTranslator(),
|
||||
translator: i18n.NewZitadelTranslator(language.English),
|
||||
}
|
||||
next := &testHandler{}
|
||||
got := a.HandlerFunc(next)
|
||||
@@ -134,7 +135,7 @@ func Test_instanceInterceptor_HandlerFunc(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &instanceInterceptor{
|
||||
verifier: tt.fields.verifier,
|
||||
translator: newZitadelTranslator(),
|
||||
translator: i18n.NewZitadelTranslator(language.English),
|
||||
}
|
||||
next := &testHandler{}
|
||||
got := a.HandlerFunc(next)
|
||||
@@ -197,7 +198,7 @@ func Test_instanceInterceptor_HandlerFuncWithError(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &instanceInterceptor{
|
||||
verifier: tt.fields.verifier,
|
||||
translator: newZitadelTranslator(),
|
||||
translator: i18n.NewZitadelTranslator(language.English),
|
||||
}
|
||||
var ctx context.Context
|
||||
got := a.HandlerFuncWithError(func(w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
@@ -16,12 +16,14 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/scim/metadata"
|
||||
"github.com/zitadel/zitadel/internal/api/scim/schemas"
|
||||
"github.com/zitadel/zitadel/internal/api/scim/serrors"
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type BulkHandler struct {
|
||||
cfg *scim_config.BulkConfig
|
||||
handlersByPluralResourceName map[schemas.ScimResourceTypePlural]RawResourceHandlerAdapter
|
||||
translator *i18n.Translator
|
||||
}
|
||||
|
||||
type BulkRequest struct {
|
||||
@@ -56,6 +58,7 @@ func (r *BulkRequest) GetSchemas() []schemas.ScimSchemaType {
|
||||
|
||||
func NewBulkHandler(
|
||||
cfg scim_config.BulkConfig,
|
||||
translator *i18n.Translator,
|
||||
handlers ...RawResourceHandlerAdapter,
|
||||
) *BulkHandler {
|
||||
handlersByPluralResourceName := make(map[schemas.ScimResourceTypePlural]RawResourceHandlerAdapter, len(handlers))
|
||||
@@ -66,6 +69,7 @@ func NewBulkHandler(
|
||||
return &BulkHandler{
|
||||
&cfg,
|
||||
handlersByPluralResourceName,
|
||||
translator,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +144,7 @@ func (h *BulkHandler) processOperation(ctx context.Context, op *BulkRequestOpera
|
||||
|
||||
opResp.Status = strconv.Itoa(statusCode)
|
||||
if err != nil {
|
||||
opResp.Error = serrors.MapToScimError(ctx, err)
|
||||
opResp.Error = serrors.MapToScimError(ctx, h.translator, err)
|
||||
opResp.Status = opResp.Error.Status
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||
zhttp_middleware "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||||
@@ -67,24 +66,22 @@ const (
|
||||
ScimTypeUniqueness scimErrorType = "uniqueness"
|
||||
)
|
||||
|
||||
var translator *i18n.Translator
|
||||
func ErrorHandler(translator *i18n.Translator) func(next zhttp_middleware.HandlerFuncWithError) http.Handler {
|
||||
return func(next zhttp_middleware.HandlerFuncWithError) http.Handler {
|
||||
var err error
|
||||
|
||||
func ErrorHandler(next zhttp_middleware.HandlerFuncWithError) http.Handler {
|
||||
var err error
|
||||
translator, err = i18n.NewZitadelTranslator(language.English)
|
||||
logging.OnError(err).Panic("unable to get translator")
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if err = next(w, r); err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if err = next(w, r); err == nil {
|
||||
return
|
||||
}
|
||||
scimErr := MapToScimError(r.Context(), translator, err)
|
||||
w.WriteHeader(scimErr.StatusCode)
|
||||
|
||||
scimErr := MapToScimError(r.Context(), err)
|
||||
w.WriteHeader(scimErr.StatusCode)
|
||||
|
||||
jsonErr := json.NewEncoder(w).Encode(scimErr)
|
||||
logging.OnError(jsonErr).Warn("Failed to marshal scim error response")
|
||||
})
|
||||
jsonErr := json.NewEncoder(w).Encode(scimErr)
|
||||
logging.OnError(jsonErr).Warn("Failed to marshal scim error response")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ThrowInvalidValue(parent error) error {
|
||||
@@ -146,7 +143,7 @@ func (err *wrappedScimError) Error() string {
|
||||
return fmt.Sprintf("SCIM Error: %s: %s", err.ScimType, err.Parent.Error())
|
||||
}
|
||||
|
||||
func MapToScimError(ctx context.Context, err error) *ScimError {
|
||||
func MapToScimError(ctx context.Context, translator *i18n.Translator, err error) *ScimError {
|
||||
scimError := new(ScimError)
|
||||
if ok := errors.As(err, &scimError); ok {
|
||||
return scimError
|
||||
@@ -154,7 +151,7 @@ func MapToScimError(ctx context.Context, err error) *ScimError {
|
||||
|
||||
scimWrappedError := new(wrappedScimError)
|
||||
if ok := errors.As(err, &scimWrappedError); ok {
|
||||
mappedErr := MapToScimError(ctx, scimWrappedError.Parent)
|
||||
mappedErr := MapToScimError(ctx, translator, scimWrappedError.Parent)
|
||||
if scimWrappedError.ScimType != "" {
|
||||
mappedErr.ScimType = scimWrappedError.ScimType
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@@ -97,9 +98,10 @@ func TestErrorHandler(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
ErrorHandler(func(http.ResponseWriter, *http.Request) error {
|
||||
return tt.err
|
||||
}).ServeHTTP(recorder, req)
|
||||
ErrorHandler(i18n.NewZitadelTranslator(language.English))(
|
||||
func(http.ResponseWriter, *http.Request) error {
|
||||
return tt.err
|
||||
}).ServeHTTP(recorder, req)
|
||||
assert.Equal(t, tt.wantStatus, recorder.Code)
|
||||
|
||||
if tt.wantBody != "" {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/scim/serrors"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
@@ -27,10 +28,11 @@ func NewServer(
|
||||
verifier *authz.ApiTokenVerifier,
|
||||
userCodeAlg crypto.EncryptionAlgorithm,
|
||||
config *sconfig.Config,
|
||||
translator *i18n.Translator,
|
||||
middlewares ...zhttp_middlware.MiddlewareWithErrorFunc,
|
||||
) http.Handler {
|
||||
verifier.RegisterServer("SCIM-V2", schemas.HandlerPrefix, AuthMapping)
|
||||
return buildHandler(command, query, userCodeAlg, config, middlewares...)
|
||||
return buildHandler(command, query, userCodeAlg, config, translator, middlewares...)
|
||||
}
|
||||
|
||||
func buildHandler(
|
||||
@@ -38,16 +40,17 @@ func buildHandler(
|
||||
query *query.Queries,
|
||||
userCodeAlg crypto.EncryptionAlgorithm,
|
||||
cfg *sconfig.Config,
|
||||
translator *i18n.Translator,
|
||||
middlewares ...zhttp_middlware.MiddlewareWithErrorFunc,
|
||||
) http.Handler {
|
||||
|
||||
router := mux.NewRouter()
|
||||
middleware := buildMiddleware(cfg, query, middlewares)
|
||||
middleware := buildMiddleware(cfg, query, translator, middlewares)
|
||||
|
||||
usersHandler := sresources.NewResourceHandlerAdapter(sresources.NewUsersHandler(command, query, userCodeAlg, cfg))
|
||||
mapResource(router, middleware, usersHandler)
|
||||
|
||||
bulkHandler := sresources.NewBulkHandler(cfg.Bulk, usersHandler)
|
||||
bulkHandler := sresources.NewBulkHandler(cfg.Bulk, translator, usersHandler)
|
||||
router.Handle("/"+zhttp.OrgIdInPathVariable+"/Bulk", middleware(handleJsonResponse(bulkHandler.BulkFromHttp))).Methods(http.MethodPost)
|
||||
|
||||
serviceProviderHandler := newServiceProviderHandler(cfg, usersHandler)
|
||||
@@ -60,11 +63,16 @@ func buildHandler(
|
||||
return router
|
||||
}
|
||||
|
||||
func buildMiddleware(cfg *sconfig.Config, query *query.Queries, middlewares []zhttp_middlware.MiddlewareWithErrorFunc) zhttp_middlware.ErrorHandlerFunc {
|
||||
func buildMiddleware(
|
||||
cfg *sconfig.Config,
|
||||
query *query.Queries,
|
||||
translator *i18n.Translator,
|
||||
middlewares []zhttp_middlware.MiddlewareWithErrorFunc,
|
||||
) zhttp_middlware.ErrorHandlerFunc {
|
||||
// content type middleware needs to run at the very beginning to correctly set content types of errors
|
||||
middlewares = append([]zhttp_middlware.MiddlewareWithErrorFunc{smiddleware.ContentTypeMiddleware}, middlewares...)
|
||||
middlewares = append(middlewares, smiddleware.ScimContextMiddleware(query))
|
||||
scimMiddleware := zhttp_middlware.ChainedWithErrorHandler(serrors.ErrorHandler, middlewares...)
|
||||
scimMiddleware := zhttp_middlware.ChainedWithErrorHandler(serrors.ErrorHandler(translator), middlewares...)
|
||||
return func(handler zhttp_middlware.HandlerFuncWithError) http.Handler {
|
||||
return http.MaxBytesHandler(scimMiddleware(handler), cfg.MaxRequestBodySize)
|
||||
}
|
||||
|
||||
@@ -437,8 +437,7 @@ func (l *Login) getTranslator(ctx context.Context, authReq *domain.AuthRequest)
|
||||
if err != nil {
|
||||
logging.OnError(err).Warn("cannot load instance restrictions to retrieve allowed languages for creating the translator")
|
||||
}
|
||||
translator, err := l.renderer.NewTranslator(ctx, restrictions.AllowedLanguages)
|
||||
logging.OnError(err).Warn("cannot load translator")
|
||||
translator := l.renderer.NewTranslator(ctx, restrictions.AllowedLanguages)
|
||||
if authReq != nil {
|
||||
l.addLoginTranslations(translator, authReq.DefaultTranslations)
|
||||
l.addLoginTranslations(translator, authReq.OrgTranslations)
|
||||
|
||||
@@ -31,7 +31,7 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func newBundle(ns Namespace, defaultLanguage language.Tag, allowedLanguages []language.Tag) (*i18n.Bundle, error) {
|
||||
func newBundle(ns Namespace, defaultLanguage language.Tag, allowedLanguages []language.Tag) *i18n.Bundle {
|
||||
bundle := i18n.NewBundle(defaultLanguage)
|
||||
|
||||
for lang, file := range translationMessages[ns] {
|
||||
@@ -41,7 +41,7 @@ func newBundle(ns Namespace, defaultLanguage language.Tag, allowedLanguages []la
|
||||
bundle.MustAddMessages(lang, file.Messages...)
|
||||
}
|
||||
|
||||
return bundle, nil
|
||||
return bundle
|
||||
}
|
||||
|
||||
func loadTranslationsFromNamespace(ns Namespace) {
|
||||
|
||||
@@ -32,32 +32,28 @@ type Message struct {
|
||||
}
|
||||
|
||||
// NewZitadelTranslator translates to all supported languages, as the ZITADEL texts are not customizable.
|
||||
func NewZitadelTranslator(defaultLanguage language.Tag) (*Translator, error) {
|
||||
func NewZitadelTranslator(defaultLanguage language.Tag) *Translator {
|
||||
return newTranslator(ZITADEL, defaultLanguage, SupportedLanguages(), "")
|
||||
}
|
||||
|
||||
func NewNotificationTranslator(defaultLanguage language.Tag, allowedLanguages []language.Tag) (*Translator, error) {
|
||||
func NewNotificationTranslator(defaultLanguage language.Tag, allowedLanguages []language.Tag) *Translator {
|
||||
return newTranslator(NOTIFICATION, defaultLanguage, allowedLanguages, "")
|
||||
}
|
||||
|
||||
func NewLoginTranslator(defaultLanguage language.Tag, allowedLanguages []language.Tag, cookieName string) (*Translator, error) {
|
||||
func NewLoginTranslator(defaultLanguage language.Tag, allowedLanguages []language.Tag, cookieName string) *Translator {
|
||||
return newTranslator(LOGIN, defaultLanguage, allowedLanguages, cookieName)
|
||||
}
|
||||
|
||||
func newTranslator(ns Namespace, defaultLanguage language.Tag, allowedLanguages []language.Tag, cookieName string) (*Translator, error) {
|
||||
func newTranslator(ns Namespace, defaultLanguage language.Tag, allowedLanguages []language.Tag, cookieName string) *Translator {
|
||||
t := new(Translator)
|
||||
var err error
|
||||
t.allowedLanguages = allowedLanguages
|
||||
if len(t.allowedLanguages) == 0 {
|
||||
t.allowedLanguages = SupportedLanguages()
|
||||
}
|
||||
t.bundle, err = newBundle(ns, defaultLanguage, t.allowedLanguages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.bundle = newBundle(ns, defaultLanguage, t.allowedLanguages)
|
||||
t.cookieHandler = http_util.NewCookieHandler()
|
||||
t.cookieName = cookieName
|
||||
return t, nil
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Translator) SupportedLanguages() []language.Tag {
|
||||
|
||||
@@ -14,10 +14,7 @@ func (n *NotificationQueries) GetTranslatorWithOrgTexts(ctx context.Context, org
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
translator, err := i18n.NewNotificationTranslator(n.GetDefaultLanguage(ctx), restrictions.AllowedLanguages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
translator := i18n.NewNotificationTranslator(n.GetDefaultLanguage(ctx), restrictions.AllowedLanguages)
|
||||
|
||||
allCustomTexts, err := n.CustomTextListByTemplate(ctx, authz.GetInstance(ctx).InstanceID(), textType, false)
|
||||
if err != nil {
|
||||
|
||||
@@ -43,7 +43,7 @@ func (r *Renderer) RenderTemplate(w http.ResponseWriter, req *http.Request, tran
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Renderer) NewTranslator(ctx context.Context, allowedLanguages []language.Tag) (*i18n.Translator, error) {
|
||||
func (r *Renderer) NewTranslator(ctx context.Context, allowedLanguages []language.Tag) *i18n.Translator {
|
||||
return i18n.NewLoginTranslator(authz.GetInstance(ctx).DefaultLanguage(), allowedLanguages, r.cookieName)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user