diff --git a/cmd/start/start.go b/cmd/start/start.go index 264ce3004..8434b4476 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -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)) diff --git a/internal/api/api.go b/internal/api/api.go index c6f6401c8..d92532a30 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -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), diff --git a/internal/api/assets/asset.go b/internal/api/assets/asset.go index 8c3c51c6a..6035086c4 100644 --- a/internal/api/assets/asset.go +++ b/internal/api/assets/asset.go @@ -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), diff --git a/internal/api/grpc/server/connect_middleware/instance_interceptor.go b/internal/api/grpc/server/connect_middleware/instance_interceptor.go index 27f59313f..50fe774db 100644 --- a/internal/api/grpc/server/connect_middleware/instance_interceptor.go +++ b/internal/api/grpc/server/connect_middleware/instance_interceptor.go @@ -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...) diff --git a/internal/api/grpc/server/connect_middleware/translation_interceptor.go b/internal/api/grpc/server/connect_middleware/translation_interceptor.go index f01b1c85a..d256d2e98 100644 --- a/internal/api/grpc/server/connect_middleware/translation_interceptor.go +++ b/internal/api/grpc/server/connect_middleware/translation_interceptor.go @@ -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()) } diff --git a/internal/api/grpc/server/middleware/instance_interceptor.go b/internal/api/grpc/server/middleware/instance_interceptor.go index 6232ed4cd..30925a99c 100644 --- a/internal/api/grpc/server/middleware/instance_interceptor.go +++ b/internal/api/grpc/server/middleware/instance_interceptor.go @@ -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...) } diff --git a/internal/api/grpc/server/middleware/translation_interceptor.go b/internal/api/grpc/server/middleware/translation_interceptor.go index 08e154053..8e7c097bf 100644 --- a/internal/api/grpc/server/middleware/translation_interceptor.go +++ b/internal/api/grpc/server/middleware/translation_interceptor.go @@ -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()) } diff --git a/internal/api/grpc/server/server.go b/internal/api/grpc/server/server.go index 8f2353e48..736e948ed 100644 --- a/internal/api/grpc/server/server.go +++ b/internal/api/grpc/server/server.go @@ -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), diff --git a/internal/api/http/middleware/instance_interceptor.go b/internal/api/http/middleware/instance_interceptor.go index 3ae5dfbb8..321eab5cb 100644 --- a/internal/api/http/middleware/instance_interceptor.go +++ b/internal/api/http/middleware/instance_interceptor.go @@ -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 -} diff --git a/internal/api/http/middleware/instance_interceptor_test.go b/internal/api/http/middleware/instance_interceptor_test.go index 55e2da5d3..de0170a5a 100644 --- a/internal/api/http/middleware/instance_interceptor_test.go +++ b/internal/api/http/middleware/instance_interceptor_test.go @@ -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 { diff --git a/internal/api/scim/resources/bulk.go b/internal/api/scim/resources/bulk.go index 5f5cd20fe..292037960 100644 --- a/internal/api/scim/resources/bulk.go +++ b/internal/api/scim/resources/bulk.go @@ -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 } }() diff --git a/internal/api/scim/serrors/errors.go b/internal/api/scim/serrors/errors.go index c6d6f806f..cce24c6b7 100644 --- a/internal/api/scim/serrors/errors.go +++ b/internal/api/scim/serrors/errors.go @@ -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 } diff --git a/internal/api/scim/serrors/errors_test.go b/internal/api/scim/serrors/errors_test.go index 71d801835..96edb842a 100644 --- a/internal/api/scim/serrors/errors_test.go +++ b/internal/api/scim/serrors/errors_test.go @@ -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 != "" { diff --git a/internal/api/scim/server.go b/internal/api/scim/server.go index 85b34da9f..38b7128e8 100644 --- a/internal/api/scim/server.go +++ b/internal/api/scim/server.go @@ -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) } diff --git a/internal/api/ui/login/renderer.go b/internal/api/ui/login/renderer.go index a5773eea9..e1b482d8e 100644 --- a/internal/api/ui/login/renderer.go +++ b/internal/api/ui/login/renderer.go @@ -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) diff --git a/internal/i18n/bundle.go b/internal/i18n/bundle.go index 3eeff7e23..80aea7b3b 100644 --- a/internal/i18n/bundle.go +++ b/internal/i18n/bundle.go @@ -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) { diff --git a/internal/i18n/translator.go b/internal/i18n/translator.go index d9d385576..7c3206fe4 100644 --- a/internal/i18n/translator.go +++ b/internal/i18n/translator.go @@ -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 { diff --git a/internal/notification/handlers/translator.go b/internal/notification/handlers/translator.go index d80598579..6294f8bf7 100644 --- a/internal/notification/handlers/translator.go +++ b/internal/notification/handlers/translator.go @@ -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 { diff --git a/internal/renderer/renderer.go b/internal/renderer/renderer.go index ada6a2191..eee64440c 100644 --- a/internal/renderer/renderer.go +++ b/internal/renderer/renderer.go @@ -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) }