feat: complete dynamic domain handling (#3482)

* feat: dynamic issuer

* feat: default language from context

* remove zitadel docs from defaults

* remove ConsoleOverwriteDir

* remove notification endpoints from defaults

* custom domains in emails

* remove (external) domain

* external domain completely removed, console handling fixed

* fix test

* fix defaults.yaml
This commit is contained in:
Livio Amstutz 2022-04-25 11:16:36 +02:00 committed by GitHub
parent 75ec73ca4a
commit 2c4799c223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 478 additions and 381 deletions

View File

@ -23,8 +23,8 @@ type DefaultInstance struct {
domain string domain string
defaults systemdefaults.SystemDefaults defaults systemdefaults.SystemDefaults
zitadelRoles []authz.RoleMapping zitadelRoles []authz.RoleMapping
baseURL string
externalSecure bool externalSecure bool
externalPort uint16
} }
func (mig *DefaultInstance) Execute(ctx context.Context) error { func (mig *DefaultInstance) Execute(ctx context.Context) error {
@ -47,6 +47,8 @@ func (mig *DefaultInstance) Execute(ctx context.Context) error {
nil, nil,
nil, nil,
nil, nil,
mig.externalSecure,
mig.externalPort,
nil, nil,
nil, nil,
nil, nil,
@ -60,7 +62,7 @@ func (mig *DefaultInstance) Execute(ctx context.Context) error {
} }
ctx = authz.WithRequestedDomain(ctx, mig.domain) ctx = authz.WithRequestedDomain(ctx, mig.domain)
_, _, err = cmd.SetUpInstance(ctx, &mig.InstanceSetup, mig.externalSecure, mig.baseURL) _, _, err = cmd.SetUpInstance(ctx, &mig.InstanceSetup, mig.externalSecure)
return err return err
} }

View File

@ -4,11 +4,11 @@ import (
"bytes" "bytes"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/command"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/config/hook" "github.com/caos/zitadel/internal/config/hook"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
@ -20,7 +20,6 @@ type Config struct {
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
InternalAuthZ authz.Config InternalAuthZ authz.Config
ExternalPort uint16 ExternalPort uint16
ExternalDomain string
ExternalSecure bool ExternalSecure bool
Log *logging.Config Log *logging.Config
EncryptionKeys *encryptionKeyConfig EncryptionKeys *encryptionKeyConfig

View File

@ -10,7 +10,6 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/caos/zitadel/cmd/admin/key" "github.com/caos/zitadel/cmd/admin/key"
http_util "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/database" "github.com/caos/zitadel/internal/database"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/migration" "github.com/caos/zitadel/internal/migration"
@ -59,18 +58,18 @@ func Setup(config *Config, steps *Steps, masterKey string) {
steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address = strings.TrimSpace(steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address) steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address = strings.TrimSpace(steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address)
if steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address == "" { if steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address == "" {
steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address = "admin@" + config.ExternalDomain steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address = "admin@" + instanceSetup.CustomDomain
} }
steps.S3DefaultInstance.es = eventstoreClient steps.S3DefaultInstance.es = eventstoreClient
steps.S3DefaultInstance.db = dbClient steps.S3DefaultInstance.db = dbClient
steps.S3DefaultInstance.defaults = config.SystemDefaults steps.S3DefaultInstance.defaults = config.SystemDefaults
steps.S3DefaultInstance.masterKey = masterKey steps.S3DefaultInstance.masterKey = masterKey
steps.S3DefaultInstance.domain = config.ExternalDomain steps.S3DefaultInstance.domain = instanceSetup.CustomDomain
steps.S3DefaultInstance.zitadelRoles = config.InternalAuthZ.RolePermissionMappings steps.S3DefaultInstance.zitadelRoles = config.InternalAuthZ.RolePermissionMappings
steps.S3DefaultInstance.userEncryptionKey = config.EncryptionKeys.User steps.S3DefaultInstance.userEncryptionKey = config.EncryptionKeys.User
steps.S3DefaultInstance.externalSecure = config.ExternalSecure steps.S3DefaultInstance.externalSecure = config.ExternalSecure
steps.S3DefaultInstance.baseURL = http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure) steps.S3DefaultInstance.externalPort = config.ExternalPort
ctx := context.Background() ctx := context.Background()
err = migration.Migrate(ctx, eventstoreClient, steps.s1ProjectionTable) err = migration.Migrate(ctx, eventstoreClient, steps.s1ProjectionTable)

View File

@ -28,7 +28,6 @@ type Config struct {
Log *logging.Config Log *logging.Config
Port uint16 Port uint16
ExternalPort uint16 ExternalPort uint16
ExternalDomain string
ExternalSecure bool ExternalSecure bool
HTTP2HostHeader string HTTP2HostHeader string
HTTP1HostHeader string HTTP1HostHeader string

View File

@ -29,7 +29,6 @@ import (
"github.com/caos/zitadel/internal/api/grpc/auth" "github.com/caos/zitadel/internal/api/grpc/auth"
"github.com/caos/zitadel/internal/api/grpc/management" "github.com/caos/zitadel/internal/api/grpc/management"
"github.com/caos/zitadel/internal/api/grpc/system" "github.com/caos/zitadel/internal/api/grpc/system"
http_util "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/api/http/middleware" "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/api/oidc" "github.com/caos/zitadel/internal/api/oidc"
"github.com/caos/zitadel/internal/api/ui/console" "github.com/caos/zitadel/internal/api/ui/console"
@ -112,12 +111,28 @@ func startZitadel(config *Config, masterKey string) error {
DisplayName: config.WebAuthNName, DisplayName: config.WebAuthNName,
ExternalSecure: config.ExternalSecure, ExternalSecure: config.ExternalSecure,
} }
commands, err := command.StartCommands(eventstoreClient, config.SystemDefaults, config.InternalAuthZ.RolePermissionMappings, storage, authZRepo, webAuthNConfig, keys.IDPConfig, keys.OTP, keys.SMTP, keys.SMS, keys.User, keys.DomainVerification, keys.OIDC) commands, err := command.StartCommands(
eventstoreClient,
config.SystemDefaults,
config.InternalAuthZ.RolePermissionMappings,
storage,
authZRepo,
webAuthNConfig,
config.ExternalSecure,
config.ExternalPort,
keys.IDPConfig,
keys.OTP,
keys.SMTP,
keys.SMS,
keys.User,
keys.DomainVerification,
keys.OIDC,
)
if err != nil { if err != nil {
return fmt.Errorf("cannot start commands: %w", err) return fmt.Errorf("cannot start commands: %w", err)
} }
notification.Start(config.Notification, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, keys.User, keys.SMTP, keys.SMS) notification.Start(config.Notification, config.ExternalPort, config.ExternalSecure, commands, queries, dbClient, assets.HandlerPrefix, keys.User, keys.SMTP, keys.SMS)
router := mux.NewRouter() router := mux.NewRouter()
err = startAPIs(ctx, router, commands, queries, eventstoreClient, dbClient, config, storage, authZRepo, keys) err = startAPIs(ctx, router, commands, queries, eventstoreClient, dbClient, config, storage, authZRepo, keys)
@ -146,16 +161,16 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
if err != nil { if err != nil {
return fmt.Errorf("error starting admin repo: %w", err) return fmt.Errorf("error starting admin repo: %w", err)
} }
if err := authenticatedAPIs.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.DefaultInstance, config.ExternalPort, config.ExternalDomain, config.ExternalSecure)); err != nil { if err := authenticatedAPIs.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.DefaultInstance, config.ExternalSecure)); err != nil {
return err return err
} }
if err := authenticatedAPIs.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.SystemDefaults.Domain, assets.HandlerPrefix, keys.User)); err != nil { if err := authenticatedAPIs.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, assets.HandlerPrefix, keys.User)); err != nil {
return err return err
} }
if err := authenticatedAPIs.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, keys.User)); err != nil { if err := authenticatedAPIs.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, keys.User, config.ExternalSecure, oidc.HandlerPrefix)); err != nil {
return err return err
} }
if err := authenticatedAPIs.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, keys.User)); err != nil { if err := authenticatedAPIs.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, keys.User, config.ExternalSecure)); err != nil {
return err return err
} }
@ -179,14 +194,13 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
} }
authenticatedAPIs.RegisterHandler(openapi.HandlerPrefix, openAPIHandler) authenticatedAPIs.RegisterHandler(openapi.HandlerPrefix, openAPIHandler)
baseURL := http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
c, err := console.Start(config.Console, config.ExternalSecure, oidcProvider.IssuerFromRequest, instanceInterceptor.Handler) c, err := console.Start(config.Console, config.ExternalSecure, oidcProvider.IssuerFromRequest, instanceInterceptor.Handler)
if err != nil { if err != nil {
return fmt.Errorf("unable to start console: %w", err) return fmt.Errorf("unable to start console: %w", err)
} }
authenticatedAPIs.RegisterHandler(console.HandlerPrefix, c) authenticatedAPIs.RegisterHandler(console.HandlerPrefix, c)
l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, config.SystemDefaults, console.HandlerPrefix+"/", config.ExternalDomain, baseURL, op.AuthCallbackURL(oidcProvider), config.ExternalSecure, userAgentInterceptor, op.NewIssuerInterceptor(oidcProvider.IssuerFromRequest).Handler, instanceInterceptor.Handler, keys.User, keys.IDPConfig, keys.CSRFCookieKey) l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, console.HandlerPrefix+"/", op.AuthCallbackURL(oidcProvider), config.ExternalSecure, userAgentInterceptor, op.NewIssuerInterceptor(oidcProvider.IssuerFromRequest).Handler, instanceInterceptor.Handler, keys.User, keys.IDPConfig, keys.CSRFCookieKey)
if err != nil { if err != nil {
return fmt.Errorf("unable to start login: %w", err) return fmt.Errorf("unable to start login: %w", err)
} }

View File

@ -5,7 +5,6 @@ Log:
Port: 8080 Port: 8080
ExternalPort: 8080 ExternalPort: 8080
ExternalDomain: localhost
ExternalSecure: true ExternalSecure: true
HTTP2HostHeader: ":authority" HTTP2HostHeader: ":authority"
HTTP1HostHeader: "host" HTTP1HostHeader: "host"
@ -97,7 +96,6 @@ Login:
SharedMaxAge: 168h #7d SharedMaxAge: 168h #7d
Console: Console:
ConsoleOverwriteDir: ""
ShortCache: ShortCache:
MaxAge: 5m MaxAge: 5m
SharedMaxAge: 15m SharedMaxAge: 15m
@ -138,25 +136,8 @@ EncryptionKeys:
CSRFCookieKeyID: "csrfCookieKey" CSRFCookieKeyID: "csrfCookieKey"
UserAgentCookieKeyID: "userAgentCookieKey" UserAgentCookieKeyID: "userAgentCookieKey"
#TODO: configure as soon as possible
#AssetStorage:
# Type: $ZITADEL_ASSET_STORAGE_TYPE
# Config:
# Endpoint: $ZITADEL_ASSET_STORAGE_ENDPOINT
# AccessKeyID: $ZITADEL_ASSET_STORAGE_ACCESS_KEY_ID
# SecretAccessKey: $ZITADEL_ASSET_STORAGE_SECRET_ACCESS_KEY
# SSL: $ZITADEL_ASSET_STORAGE_SSL
# Location: $ZITADEL_ASSET_STORAGE_LOCATION
# BucketPrefix: $ZITADEL_ASSET_STORAGE_BUCKET_PREFIX
# MultiDelete: $ZITADEL_ASSET_STORAGE_MULTI_DELETE
#TODO: remove as soon as possible #TODO: remove as soon as possible
SystemDefaults: SystemDefaults:
# DefaultLanguage: 'en'
Domain: $ZITADEL_DEFAULT_DOMAIN
ZitadelDocs:
Issuer: $ZITADEL_ISSUER
DiscoveryEndpoint: '$ZITADEL_ISSUER/.well-known/openid-configuration'
SecretGenerators: SecretGenerators:
PasswordSaltCost: 14 PasswordSaltCost: 14
MachineKeySize: 2048 MachineKeySize: 2048
@ -172,19 +153,11 @@ SystemDefaults:
IncludeDigits: true IncludeDigits: true
IncludeSymbols: false IncludeSymbols: false
Notifications: Notifications:
Endpoints:
InitCode: '$ZITADEL_ACCOUNTS/user/init?userID={{.UserID}}&code={{.Code}}&passwordset={{.PasswordSet}}'
PasswordReset: '$ZITADEL_ACCOUNTS/password/init?userID={{.UserID}}&code={{.Code}}'
VerifyEmail: '$ZITADEL_ACCOUNTS/mail/verification?userID={{.UserID}}&code={{.Code}}'
DomainClaimed: '$ZITADEL_ACCOUNTS/login'
PasswordlessRegistration: '$ZITADEL_ACCOUNTS/login/passwordless/init'
FileSystemPath: '.notifications/' FileSystemPath: '.notifications/'
KeyConfig: KeyConfig:
Size: 2048 Size: 2048
PrivateKeyLifetime: 6h PrivateKeyLifetime: 6h
PublicKeyLifetime: 30h PublicKeyLifetime: 30h
SigningKeyRotationCheck: 10s
SigningKeyGracefulPeriod: 10m
DefaultInstance: DefaultInstance:
InstanceName: InstanceName:

View File

@ -2,6 +2,8 @@ package authz
import ( import (
"context" "context"
"golang.org/x/text/language"
) )
var ( var (
@ -14,6 +16,8 @@ type Instance interface {
ConsoleClientID() string ConsoleClientID() string
ConsoleApplicationID() string ConsoleApplicationID() string
RequestedDomain() string RequestedDomain() string
RequestedHost() string
DefaultLanguage() language.Tag
} }
type InstanceVerifier interface { type InstanceVerifier interface {
@ -21,8 +25,11 @@ type InstanceVerifier interface {
} }
type instance struct { type instance struct {
ID string ID string
Domain string Domain string
projectID string
appID string
clientID string
} }
func (i *instance) InstanceID() string { func (i *instance) InstanceID() string {
@ -30,21 +37,29 @@ func (i *instance) InstanceID() string {
} }
func (i *instance) ProjectID() string { func (i *instance) ProjectID() string {
return "" return i.projectID
} }
func (i *instance) ConsoleClientID() string { func (i *instance) ConsoleClientID() string {
return "" return i.clientID
} }
func (i *instance) ConsoleApplicationID() string { func (i *instance) ConsoleApplicationID() string {
return "" return i.appID
} }
func (i *instance) RequestedDomain() string { func (i *instance) RequestedDomain() string {
return i.Domain return i.Domain
} }
func (i *instance) RequestedHost() string {
return i.Domain
}
func (i *instance) DefaultLanguage() language.Tag {
return language.Und
}
func GetInstance(ctx context.Context) Instance { func GetInstance(ctx context.Context) Instance {
instance, ok := ctx.Value(instanceKey).(Instance) instance, ok := ctx.Value(instanceKey).(Instance)
if !ok { if !ok {
@ -70,3 +85,15 @@ func WithRequestedDomain(ctx context.Context, domain string) context.Context {
i.Domain = domain i.Domain = domain
return context.WithValue(ctx, instanceKey, i) return context.WithValue(ctx, instanceKey, i)
} }
func WithConsole(ctx context.Context, projectID, appID string) context.Context {
i, ok := ctx.Value(instanceKey).(*instance)
if !ok {
i = new(instance)
}
i.projectID = projectID
i.appID = appID
//i.clientID = clientID
return context.WithValue(ctx, instanceKey, i)
}

View File

@ -5,6 +5,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"golang.org/x/text/language"
) )
func Test_Instance(t *testing.T) { func Test_Instance(t *testing.T) {
@ -83,6 +84,14 @@ func (m *mockInstance) ConsoleApplicationID() string {
return "appID" return "appID"
} }
func (m *mockInstance) DefaultLanguage() language.Tag {
return language.English
}
func (m *mockInstance) RequestedDomain() string { func (m *mockInstance) RequestedDomain() string {
return "zitadel.cloud" return "zitadel.cloud"
} }
func (m *mockInstance) RequestedHost() string {
return "zitadel.cloud:443"
}

View File

@ -3,9 +3,9 @@ package admin
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/api/authz"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object" "github.com/caos/zitadel/internal/api/grpc/object"
text_grpc "github.com/caos/zitadel/internal/api/grpc/text" text_grpc "github.com/caos/zitadel/internal/api/grpc/text"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
@ -13,7 +13,7 @@ import (
) )
func (s *Server) GetDefaultInitMessageText(ctx context.Context, req *admin_pb.GetDefaultInitMessageTextRequest) (*admin_pb.GetDefaultInitMessageTextResponse, error) { func (s *Server) GetDefaultInitMessageText(ctx context.Context, req *admin_pb.GetDefaultInitMessageTextRequest) (*admin_pb.GetDefaultInitMessageTextResponse, error) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.InitCodeMessageType, req.Language) msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.InitCodeMessageType, req.Language)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -61,7 +61,7 @@ func (s *Server) ResetCustomInitMessageTextToDefault(ctx context.Context, req *a
} }
func (s *Server) GetDefaultPasswordResetMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordResetMessageTextRequest) (*admin_pb.GetDefaultPasswordResetMessageTextResponse, error) { func (s *Server) GetDefaultPasswordResetMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordResetMessageTextRequest) (*admin_pb.GetDefaultPasswordResetMessageTextResponse, error) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.PasswordResetMessageType, req.Language) msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordResetMessageType, req.Language)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -109,7 +109,7 @@ func (s *Server) ResetCustomPasswordResetMessageTextToDefault(ctx context.Contex
} }
func (s *Server) GetDefaultVerifyEmailMessageText(ctx context.Context, req *admin_pb.GetDefaultVerifyEmailMessageTextRequest) (*admin_pb.GetDefaultVerifyEmailMessageTextResponse, error) { func (s *Server) GetDefaultVerifyEmailMessageText(ctx context.Context, req *admin_pb.GetDefaultVerifyEmailMessageTextRequest) (*admin_pb.GetDefaultVerifyEmailMessageTextResponse, error) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.VerifyEmailMessageType, req.Language) msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.VerifyEmailMessageType, req.Language)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -157,7 +157,7 @@ func (s *Server) ResetCustomVerifyEmailMessageTextToDefault(ctx context.Context,
} }
func (s *Server) GetDefaultVerifyPhoneMessageText(ctx context.Context, req *admin_pb.GetDefaultVerifyPhoneMessageTextRequest) (*admin_pb.GetDefaultVerifyPhoneMessageTextResponse, error) { func (s *Server) GetDefaultVerifyPhoneMessageText(ctx context.Context, req *admin_pb.GetDefaultVerifyPhoneMessageTextRequest) (*admin_pb.GetDefaultVerifyPhoneMessageTextResponse, error) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.VerifyPhoneMessageType, req.Language) msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.VerifyPhoneMessageType, req.Language)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -205,7 +205,7 @@ func (s *Server) ResetCustomVerifyPhoneMessageTextToDefault(ctx context.Context,
} }
func (s *Server) GetDefaultDomainClaimedMessageText(ctx context.Context, req *admin_pb.GetDefaultDomainClaimedMessageTextRequest) (*admin_pb.GetDefaultDomainClaimedMessageTextResponse, error) { func (s *Server) GetDefaultDomainClaimedMessageText(ctx context.Context, req *admin_pb.GetDefaultDomainClaimedMessageTextRequest) (*admin_pb.GetDefaultDomainClaimedMessageTextResponse, error) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.DomainClaimedMessageType, req.Language) msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.DomainClaimedMessageType, req.Language)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -253,7 +253,7 @@ func (s *Server) ResetCustomDomainClaimedMessageTextToDefault(ctx context.Contex
} }
func (s *Server) GetDefaultPasswordlessRegistrationMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordlessRegistrationMessageTextRequest) (*admin_pb.GetDefaultPasswordlessRegistrationMessageTextResponse, error) { func (s *Server) GetDefaultPasswordlessRegistrationMessageText(ctx context.Context, req *admin_pb.GetDefaultPasswordlessRegistrationMessageTextRequest) (*admin_pb.GetDefaultPasswordlessRegistrationMessageTextResponse, error) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.PasswordlessRegistrationMessageType, req.Language) msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordlessRegistrationMessageType, req.Language)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -5,6 +5,7 @@ import (
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/text" "github.com/caos/zitadel/internal/api/grpc/text"
caos_errors "github.com/caos/zitadel/internal/errors" caos_errors "github.com/caos/zitadel/internal/errors"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin" admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
@ -34,6 +35,6 @@ func (s *Server) SetDefaultLanguage(ctx context.Context, req *admin_pb.SetDefaul
return nil, nil return nil, nil
} }
func (s *Server) GetDefaultLanguage(ctx context.Context, req *admin_pb.GetDefaultLanguageRequest) (*admin_pb.GetDefaultLanguageResponse, error) { func (s *Server) GetDefaultLanguage(ctx context.Context, _ *admin_pb.GetDefaultLanguageRequest) (*admin_pb.GetDefaultLanguageResponse, error) {
return &admin_pb.GetDefaultLanguageResponse{Language: s.query.GetDefaultLanguage(ctx).String()}, nil return &admin_pb.GetDefaultLanguageResponse{Language: authz.GetInstance(ctx).DefaultLanguage().String()}, nil
} }

View File

@ -5,6 +5,7 @@ import (
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object" "github.com/caos/zitadel/internal/api/grpc/object"
org_grpc "github.com/caos/zitadel/internal/api/grpc/org" org_grpc "github.com/caos/zitadel/internal/api/grpc/org"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
@ -47,7 +48,7 @@ func (s *Server) ListOrgs(ctx context.Context, req *admin_pb.ListOrgsRequest) (*
} }
func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (*admin_pb.SetUpOrgResponse, error) { func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (*admin_pb.SetUpOrgResponse, error) {
userIDs, err := s.getClaimedUserIDsOfOrgDomain(ctx, domain.NewIAMDomainName(req.Org.Name, s.iamDomain)) userIDs, err := s.getClaimedUserIDsOfOrgDomain(ctx, domain.NewIAMDomainName(req.Org.Name, authz.GetInstance(ctx).RequestedDomain()))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -24,7 +24,6 @@ type Server struct {
command *command.Commands command *command.Commands
query *query.Queries query *query.Queries
administrator repository.AdministratorRepository administrator repository.AdministratorRepository
iamDomain string
assetsAPIDomain string assetsAPIDomain string
userCodeAlg crypto.EncryptionAlgorithm userCodeAlg crypto.EncryptionAlgorithm
} }
@ -36,7 +35,6 @@ type Config struct {
func CreateServer(command *command.Commands, func CreateServer(command *command.Commands,
query *query.Queries, query *query.Queries,
repo repository.Repository, repo repository.Repository,
iamDomain,
assetsAPIDomain string, assetsAPIDomain string,
userCodeAlg crypto.EncryptionAlgorithm, userCodeAlg crypto.EncryptionAlgorithm,
) *Server { ) *Server {
@ -44,7 +42,6 @@ func CreateServer(command *command.Commands,
command: command, command: command,
query: query, query: query,
administrator: repo, administrator: repo,
iamDomain: iamDomain,
assetsAPIDomain: assetsAPIDomain, assetsAPIDomain: assetsAPIDomain,
userCodeAlg: userCodeAlg, userCodeAlg: userCodeAlg,
} }

View File

@ -8,6 +8,8 @@ import (
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object" "github.com/caos/zitadel/internal/api/grpc/object"
user_grpc "github.com/caos/zitadel/internal/api/grpc/user" user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
"github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/api/ui/login"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/internal/query"
auth_pb "github.com/caos/zitadel/pkg/grpc/auth" auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
@ -65,9 +67,10 @@ func (s *Server) AddMyPasswordlessLink(ctx context.Context, _ *auth_pb.AddMyPass
if err != nil { if err != nil {
return nil, err return nil, err
} }
origin := http.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), s.externalSecure)
return &auth_pb.AddMyPasswordlessLinkResponse{ return &auth_pb.AddMyPasswordlessLinkResponse{
Details: object.AddToDetailsPb(initCode.Sequence, initCode.ChangeDate, initCode.ResourceOwner), Details: object.AddToDetailsPb(initCode.Sequence, initCode.ChangeDate, initCode.ResourceOwner),
Link: initCode.Link(s.defaults.Notifications.Endpoints.PasswordlessRegistration), Link: initCode.Link(origin + login.HandlerPrefix + login.EndpointPasswordlessRegistration),
Expiration: durationpb.New(initCode.Expiration), Expiration: durationpb.New(initCode.Expiration),
}, nil }, nil
} }

View File

@ -28,6 +28,7 @@ type Server struct {
defaults systemdefaults.SystemDefaults defaults systemdefaults.SystemDefaults
assetsAPIDomain string assetsAPIDomain string
userCodeAlg crypto.EncryptionAlgorithm userCodeAlg crypto.EncryptionAlgorithm
externalSecure bool
} }
type Config struct { type Config struct {
@ -40,6 +41,7 @@ func CreateServer(command *command.Commands,
defaults systemdefaults.SystemDefaults, defaults systemdefaults.SystemDefaults,
assetsAPIDomain string, assetsAPIDomain string,
userCodeAlg crypto.EncryptionAlgorithm, userCodeAlg crypto.EncryptionAlgorithm,
externalSecure bool,
) *Server { ) *Server {
return &Server{ return &Server{
command: command, command: command,
@ -48,6 +50,7 @@ func CreateServer(command *command.Commands,
defaults: defaults, defaults: defaults,
assetsAPIDomain: assetsAPIDomain, assetsAPIDomain: assetsAPIDomain,
userCodeAlg: userCodeAlg, userCodeAlg: userCodeAlg,
externalSecure: externalSecure,
} }
} }

View File

@ -3,6 +3,10 @@ package management
import ( import (
"context" "context"
"github.com/caos/oidc/v2/pkg/oidc"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/http"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management" mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
) )
@ -10,9 +14,10 @@ func (s *Server) Healthz(context.Context, *mgmt_pb.HealthzRequest) (*mgmt_pb.Hea
return &mgmt_pb.HealthzResponse{}, nil return &mgmt_pb.HealthzResponse{}, nil
} }
func (s *Server) GetOIDCInformation(ctx context.Context, req *mgmt_pb.GetOIDCInformationRequest) (*mgmt_pb.GetOIDCInformationResponse, error) { func (s *Server) GetOIDCInformation(ctx context.Context, _ *mgmt_pb.GetOIDCInformationRequest) (*mgmt_pb.GetOIDCInformationResponse, error) {
issuer := http.BuildOrigin(authz.GetInstance(ctx).RequestedDomain(), s.externalSecure) + s.issuerPath
return &mgmt_pb.GetOIDCInformationResponse{ return &mgmt_pb.GetOIDCInformationResponse{
Issuer: s.systemDefaults.ZitadelDocs.Issuer, Issuer: issuer,
DiscoveryEndpoint: s.systemDefaults.ZitadelDocs.DiscoveryEndpoint, DiscoveryEndpoint: issuer + oidc.DiscoveryEndpoint,
}, nil }, nil
} }

View File

@ -48,7 +48,7 @@ func (s *Server) ListOrgChanges(ctx context.Context, req *mgmt_pb.ListOrgChanges
} }
func (s *Server) AddOrg(ctx context.Context, req *mgmt_pb.AddOrgRequest) (*mgmt_pb.AddOrgResponse, error) { func (s *Server) AddOrg(ctx context.Context, req *mgmt_pb.AddOrgRequest) (*mgmt_pb.AddOrgResponse, error) {
userIDs, err := s.getClaimedUserIDsOfOrgDomain(ctx, domain.NewIAMDomainName(req.Name, s.systemDefaults.Domain), "") userIDs, err := s.getClaimedUserIDsOfOrgDomain(ctx, domain.NewIAMDomainName(req.Name, authz.GetInstance(ctx).RequestedDomain()), "")
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -26,6 +26,8 @@ type Server struct {
assetAPIPrefix string assetAPIPrefix string
passwordHashAlg crypto.HashAlgorithm passwordHashAlg crypto.HashAlgorithm
userCodeAlg crypto.EncryptionAlgorithm userCodeAlg crypto.EncryptionAlgorithm
externalSecure bool
issuerPath string
} }
func CreateServer( func CreateServer(
@ -34,6 +36,8 @@ func CreateServer(
sd systemdefaults.SystemDefaults, sd systemdefaults.SystemDefaults,
assetAPIPrefix string, assetAPIPrefix string,
userCodeAlg crypto.EncryptionAlgorithm, userCodeAlg crypto.EncryptionAlgorithm,
externalSecure bool,
issuerPath string,
) *Server { ) *Server {
return &Server{ return &Server{
command: command, command: command,
@ -42,6 +46,8 @@ func CreateServer(
assetAPIPrefix: assetAPIPrefix, assetAPIPrefix: assetAPIPrefix,
passwordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost), passwordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost),
userCodeAlg: userCodeAlg, userCodeAlg: userCodeAlg,
externalSecure: externalSecure,
issuerPath: issuerPath,
} }
} }

View File

@ -16,7 +16,9 @@ import (
"github.com/caos/zitadel/internal/api/grpc/metadata" "github.com/caos/zitadel/internal/api/grpc/metadata"
obj_grpc "github.com/caos/zitadel/internal/api/grpc/object" obj_grpc "github.com/caos/zitadel/internal/api/grpc/object"
user_grpc "github.com/caos/zitadel/internal/api/grpc/user" user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
"github.com/caos/zitadel/internal/api/http"
z_oidc "github.com/caos/zitadel/internal/api/oidc" z_oidc "github.com/caos/zitadel/internal/api/oidc"
"github.com/caos/zitadel/internal/api/ui/login"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/internal/query"
@ -259,8 +261,9 @@ func (s *Server) ImportHumanUser(ctx context.Context, req *mgmt_pb.ImportHumanUs
), ),
} }
if code != nil { if code != nil {
origin := http.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), s.externalSecure)
resp.PasswordlessRegistration = &mgmt_pb.ImportHumanUserResponse_PasswordlessRegistration{ resp.PasswordlessRegistration = &mgmt_pb.ImportHumanUserResponse_PasswordlessRegistration{
Link: code.Link(s.systemDefaults.Notifications.Endpoints.PasswordlessRegistration), Link: code.Link(origin + login.HandlerPrefix + login.EndpointPasswordlessRegistration),
Lifetime: durationpb.New(code.Expiration), Lifetime: durationpb.New(code.Expiration),
Expiration: durationpb.New(code.Expiration), Expiration: durationpb.New(code.Expiration),
} }
@ -654,9 +657,10 @@ func (s *Server) AddPasswordlessRegistration(ctx context.Context, req *mgmt_pb.A
if err != nil { if err != nil {
return nil, err return nil, err
} }
origin := http.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), s.externalSecure)
return &mgmt_pb.AddPasswordlessRegistrationResponse{ return &mgmt_pb.AddPasswordlessRegistrationResponse{
Details: obj_grpc.AddToDetailsPb(initCode.Sequence, initCode.ChangeDate, initCode.ResourceOwner), Details: obj_grpc.AddToDetailsPb(initCode.Sequence, initCode.ChangeDate, initCode.ResourceOwner),
Link: initCode.Link(s.systemDefaults.Notifications.Endpoints.PasswordlessRegistration), Link: initCode.Link(origin + login.HandlerPrefix + login.EndpointPasswordlessRegistration),
Expiration: durationpb.New(initCode.Expiration), Expiration: durationpb.New(initCode.Expiration),
}, nil }, nil
} }

View File

@ -6,6 +6,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"golang.org/x/text/language"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
@ -177,6 +178,14 @@ func (m *mockInstance) ConsoleApplicationID() string {
return "consoleApplicationID" return "consoleApplicationID"
} }
func (m *mockInstance) DefaultLanguage() language.Tag {
return language.English
}
func (m *mockInstance) RequestedDomain() string { func (m *mockInstance) RequestedDomain() string {
return "localhost" return "localhost"
} }
func (m *mockInstance) RequestedHost() string {
return "localhost:8080"
}

View File

@ -3,17 +3,17 @@ package middleware
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/query"
"google.golang.org/grpc" "google.golang.org/grpc"
"github.com/caos/zitadel/internal/api/authz"
_ "github.com/caos/zitadel/internal/statik" _ "github.com/caos/zitadel/internal/statik"
) )
func TranslationHandler(query *query.Queries) func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { func TranslationHandler() func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
translator := newZitadelTranslator(query.GetDefaultLanguage(context.Background()))
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
resp, err := handler(ctx, req) resp, err := handler(ctx, req)
translator := newZitadelTranslator(authz.GetInstance(ctx).DefaultLanguage())
if loc, ok := resp.(localizers); ok && resp != nil { if loc, ok := resp.(localizers); ok && resp != nil {
translateFields(ctx, loc, translator) translateFields(ctx, loc, translator)
} }

View File

@ -48,7 +48,7 @@ func translatorFromNamespace(namespace string, defaultLanguage language.Tag) *i1
dir, err := fs.NewWithNamespace(namespace) dir, err := fs.NewWithNamespace(namespace)
logging.LogWithFields("ERROR-7usEW", "namespace", namespace).OnError(err).Panic("unable to get namespace") logging.LogWithFields("ERROR-7usEW", "namespace", namespace).OnError(err).Panic("unable to get namespace")
translator, err := i18n.NewTranslator(dir, i18n.TranslatorConfig{DefaultLanguage: defaultLanguage}) translator, err := i18n.NewTranslator(dir, defaultLanguage, "")
logging.Log("ERROR-Sk8sf").OnError(err).Panic("unable to get i18n translator") logging.Log("ERROR-Sk8sf").OnError(err).Panic("unable to get i18n translator")
return translator return translator

View File

@ -1,15 +1,14 @@
package server package server
import ( import (
grpc_api "github.com/caos/zitadel/internal/api/grpc"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/telemetry/metrics"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"google.golang.org/grpc" "google.golang.org/grpc"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
grpc_api "github.com/caos/zitadel/internal/api/grpc"
"github.com/caos/zitadel/internal/api/grpc/server/middleware" "github.com/caos/zitadel/internal/api/grpc/server/middleware"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/telemetry/metrics"
) )
type Server interface { type Server interface {
@ -33,7 +32,7 @@ func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, querie
//TODO: Handle Ignored Services //TODO: Handle Ignored Services
middleware.InstanceInterceptor(queries, hostHeaderName, "/zitadel.system.v1.SystemService"), middleware.InstanceInterceptor(queries, hostHeaderName, "/zitadel.system.v1.SystemService"),
middleware.AuthorizationInterceptor(verifier, authConfig), middleware.AuthorizationInterceptor(verifier, authConfig),
middleware.TranslationHandler(queries), middleware.TranslationHandler(),
middleware.ValidationHandler(), middleware.ValidationHandler(),
middleware.ServiceHandler(), middleware.ServiceHandler(),
), ),

View File

@ -40,7 +40,7 @@ func (s *Server) GetInstance(ctx context.Context, req *system_pb.GetInstanceRequ
} }
func (s *Server) AddInstance(ctx context.Context, req *system_pb.AddInstanceRequest) (*system_pb.AddInstanceResponse, error) { func (s *Server) AddInstance(ctx context.Context, req *system_pb.AddInstanceRequest) (*system_pb.AddInstanceResponse, error) {
id, details, err := s.command.SetUpInstance(ctx, AddInstancePbToSetupInstance(req, s.DefaultInstance), s.ExternalSecure, s.BaseURL) id, details, err := s.command.SetUpInstance(ctx, AddInstancePbToSetupInstance(req, s.DefaultInstance), s.ExternalSecure)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,10 +1,10 @@
package system package system
import ( import (
"github.com/caos/zitadel/internal/admin/repository"
http_util "github.com/caos/zitadel/internal/api/http"
"google.golang.org/grpc" "google.golang.org/grpc"
"github.com/caos/zitadel/internal/admin/repository"
"github.com/caos/zitadel/internal/admin/repository/eventsourcing" "github.com/caos/zitadel/internal/admin/repository/eventsourcing"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/server" "github.com/caos/zitadel/internal/api/grpc/server"
@ -26,7 +26,6 @@ type Server struct {
administrator repository.AdministratorRepository administrator repository.AdministratorRepository
DefaultInstance command.InstanceSetup DefaultInstance command.InstanceSetup
ExternalSecure bool ExternalSecure bool
BaseURL string
} }
type Config struct { type Config struct {
@ -37,8 +36,6 @@ func CreateServer(command *command.Commands,
query *query.Queries, query *query.Queries,
repo repository.Repository, repo repository.Repository,
defaultInstance command.InstanceSetup, defaultInstance command.InstanceSetup,
externalPort uint16,
externalDomain string,
externalSecure bool) *Server { externalSecure bool) *Server {
return &Server{ return &Server{
command: command, command: command,
@ -46,7 +43,6 @@ func CreateServer(command *command.Commands,
administrator: repo, administrator: repo,
DefaultInstance: defaultInstance, DefaultInstance: defaultInstance,
ExternalSecure: externalSecure, ExternalSecure: externalSecure,
BaseURL: http_util.BuildHTTP(externalDomain, externalPort, externalSecure),
} }
} }

View File

@ -9,6 +9,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
) )
@ -261,6 +262,14 @@ func (m *mockInstance) ConsoleApplicationID() string {
return "consoleApplicationID" return "consoleApplicationID"
} }
func (m *mockInstance) DefaultLanguage() language.Tag {
return language.English
}
func (m *mockInstance) RequestedDomain() string { func (m *mockInstance) RequestedDomain() string {
return "zitadel.cloud" return "zitadel.cloud"
} }
func (m *mockInstance) RequestedHost() string {
return "zitadel.cloud:443"
}

View File

@ -32,7 +32,11 @@ func IsOrigin(rawOrigin string) bool {
} }
func BuildHTTP(hostname string, externalPort uint16, secure bool) string { func BuildHTTP(hostname string, externalPort uint16, secure bool) string {
return BuildOrigin(fmt.Sprintf("%s:%d", hostname, externalPort), secure) host := hostname
if externalPort != 0 {
host = fmt.Sprintf("%s:%d", hostname, externalPort)
}
return BuildOrigin(host, secure)
} }
func BuildOrigin(host string, secure bool) string { func BuildOrigin(host string, secure bool) string {

View File

@ -19,9 +19,8 @@ import (
) )
type Config struct { type Config struct {
ConsoleOverwriteDir string ShortCache middleware.CacheConfig
ShortCache middleware.CacheConfig LongCache middleware.CacheConfig
LongCache middleware.CacheConfig
} }
type spaHandler struct { type spaHandler struct {

View File

@ -61,12 +61,12 @@ func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, aut
data.HasNumber = NumberRegex data.HasNumber = NumberRegex
} }
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplChangePassword], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplChangePassword], data, nil)
} }
func (l *Login) renderChangePasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) renderChangePasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
var errType, errMessage string var errType, errMessage string
data := l.getUserData(r, authReq, "Password Change Done", errType, errMessage) data := l.getUserData(r, authReq, "Password Change Done", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplChangePasswordDone], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplChangePasswordDone], data, nil)
} }

View File

@ -1,6 +1,7 @@
package login package login
import ( import (
"context"
"encoding/base64" "encoding/base64"
"net/http" "net/http"
"net/url" "net/url"
@ -100,7 +101,7 @@ func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *domai
} }
func (l *Login) handleOIDCAuthorize(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) { func (l *Login) handleOIDCAuthorize(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) {
provider, err := l.getRPConfig(idpConfig, callbackEndpoint) provider, err := l.getRPConfig(r.Context(), idpConfig, callbackEndpoint)
if err != nil { if err != nil {
l.renderLogin(w, r, authReq, err) l.renderLogin(w, r, authReq, err)
return return
@ -150,7 +151,7 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
return return
} }
if idpConfig.IsOIDC { if idpConfig.IsOIDC {
provider, err := l.getRPConfig(idpConfig, EndpointExternalLoginCallback) provider, err := l.getRPConfig(r.Context(), idpConfig, EndpointExternalLoginCallback)
if err != nil { if err != nil {
l.renderLogin(w, r, authReq, err) l.renderLogin(w, r, authReq, err)
return return
@ -166,13 +167,13 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
l.renderError(w, r, authReq, caos_errors.ThrowPreconditionFailed(nil, "RP-asff2", "Errors.ExternalIDP.IDPTypeNotImplemented")) l.renderError(w, r, authReq, caos_errors.ThrowPreconditionFailed(nil, "RP-asff2", "Errors.ExternalIDP.IDPTypeNotImplemented"))
} }
func (l *Login) getRPConfig(idpConfig *iam_model.IDPConfigView, callbackEndpoint string) (rp.RelyingParty, error) { func (l *Login) getRPConfig(ctx context.Context, idpConfig *iam_model.IDPConfigView, callbackEndpoint string) (rp.RelyingParty, error) {
oidcClientSecret, err := crypto.DecryptString(idpConfig.OIDCClientSecret, l.idpConfigAlg) oidcClientSecret, err := crypto.DecryptString(idpConfig.OIDCClientSecret, l.idpConfigAlg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if idpConfig.OIDCIssuer != "" { if idpConfig.OIDCIssuer != "" {
return rp.NewRelyingPartyOIDC(idpConfig.OIDCIssuer, idpConfig.OIDCClientID, oidcClientSecret, l.baseURL+callbackEndpoint, idpConfig.OIDCScopes, rp.WithVerifierOpts(rp.WithIssuedAtOffset(3*time.Second))) return rp.NewRelyingPartyOIDC(idpConfig.OIDCIssuer, idpConfig.OIDCClientID, oidcClientSecret, l.baseURL(ctx)+callbackEndpoint, idpConfig.OIDCScopes, rp.WithVerifierOpts(rp.WithIssuedAtOffset(3*time.Second)))
} }
if idpConfig.OAuthAuthorizationEndpoint == "" || idpConfig.OAuthTokenEndpoint == "" { if idpConfig.OAuthAuthorizationEndpoint == "" || idpConfig.OAuthTokenEndpoint == "" {
return nil, caos_errors.ThrowPreconditionFailed(nil, "RP-4n0fs", "Errors.IdentityProvider.InvalidConfig") return nil, caos_errors.ThrowPreconditionFailed(nil, "RP-4n0fs", "Errors.IdentityProvider.InvalidConfig")
@ -184,7 +185,7 @@ func (l *Login) getRPConfig(idpConfig *iam_model.IDPConfigView, callbackEndpoint
AuthURL: idpConfig.OAuthAuthorizationEndpoint, AuthURL: idpConfig.OAuthAuthorizationEndpoint,
TokenURL: idpConfig.OAuthTokenEndpoint, TokenURL: idpConfig.OAuthTokenEndpoint,
}, },
RedirectURL: l.baseURL + callbackEndpoint, RedirectURL: l.baseURL(ctx) + callbackEndpoint,
Scopes: idpConfig.OIDCScopes, Scopes: idpConfig.OIDCScopes,
} }
return rp.NewRelyingPartyOAuth(oauth2Config, rp.WithVerifierOpts(rp.WithIssuedAtOffset(3*time.Second))) return rp.NewRelyingPartyOAuth(oauth2Config, rp.WithVerifierOpts(rp.WithIssuedAtOffset(3*time.Second)))
@ -308,7 +309,7 @@ func (l *Login) renderExternalNotFoundOption(w http.ResponseWriter, r *http.Requ
data.ExternalPhone = human.PhoneNumber data.ExternalPhone = human.PhoneNumber
data.ExternalPhoneVerified = human.IsPhoneVerified data.ExternalPhoneVerified = human.IsPhoneVerified
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplExternalNotFoundOption], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplExternalNotFoundOption], data, nil)
} }

View File

@ -97,7 +97,7 @@ func (l *Login) handleExternalRegisterCallback(w http.ResponseWriter, r *http.Re
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
provider, err := l.getRPConfig(idpConfig, EndpointExternalRegisterCallback) provider, err := l.getRPConfig(r.Context(), idpConfig, EndpointExternalRegisterCallback)
if err != nil { if err != nil {
l.renderRegisterOption(w, r, authReq, err) l.renderRegisterOption(w, r, authReq, err)
return return
@ -192,7 +192,7 @@ func (l *Login) renderExternalRegisterOverview(w http.ResponseWriter, r *http.Re
data.ExternalPhone = human.PhoneNumber data.ExternalPhone = human.PhoneNumber
data.ExternalPhoneVerified = human.IsPhoneVerified data.ExternalPhoneVerified = human.IsPhoneVerified
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplExternalRegisterOverview], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplExternalRegisterOverview], data, nil)
} }

View File

@ -1,6 +1,7 @@
package login package login
import ( import (
"fmt"
"net/http" "net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
@ -38,6 +39,10 @@ type initPasswordData struct {
HasSymbol string HasSymbol string
} }
func InitPasswordLink(origin, userID, code string) string {
return fmt.Sprintf("%s%s?userID=%s&code=%s", externalLink(origin), EndpointInitPassword, userID, code)
}
func (l *Login) handleInitPassword(w http.ResponseWriter, r *http.Request) { func (l *Login) handleInitPassword(w http.ResponseWriter, r *http.Request) {
userID := r.FormValue(queryInitPWUserID) userID := r.FormValue(queryInitPWUserID)
code := r.FormValue(queryInitPWCode) code := r.FormValue(queryInitPWCode)
@ -142,11 +147,11 @@ func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authR
data.HasNumber = NumberRegex data.HasNumber = NumberRegex
} }
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitPassword], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitPassword], data, nil)
} }
func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
data := l.getUserData(r, authReq, "Password Init Done", "", "") data := l.getUserData(r, authReq, "Password Init Done", "", "")
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplInitPasswordDone], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplInitPasswordDone], data, nil)
} }

View File

@ -1,6 +1,7 @@
package login package login
import ( import (
"fmt"
"net/http" "net/http"
"strconv" "strconv"
@ -41,6 +42,10 @@ type initUserData struct {
HasSymbol string HasSymbol string
} }
func InitUserLink(origin, userID, code string, passwordSet bool) string {
return fmt.Sprintf("%s%s?userID=%s&code=%s&passwordset=%t", externalLink(origin), EndpointInitUser, userID, code, passwordSet)
}
func (l *Login) handleInitUser(w http.ResponseWriter, r *http.Request) { func (l *Login) handleInitUser(w http.ResponseWriter, r *http.Request) {
userID := r.FormValue(queryInitUserUserID) userID := r.FormValue(queryInitUserUserID)
code := r.FormValue(queryInitUserCode) code := r.FormValue(queryInitUserCode)
@ -132,11 +137,11 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *
data.HasNumber = NumberRegex data.HasNumber = NumberRegex
} }
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitUser], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplInitUser], data, nil)
} }
func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
data := l.getUserData(r, authReq, "User Init Done", "", "") data := l.getUserData(r, authReq, "User Init Done", "", "")
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplInitUserDone], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplInitUserDone], data, nil)
} }

View File

@ -99,7 +99,7 @@ func (l *Login) handleJWTExtraction(w http.ResponseWriter, r *http.Request, auth
return return
} }
} }
redirect, err := l.redirectToJWTCallback(authReq) redirect, err := l.redirectToJWTCallback(r.Context(), authReq)
if err != nil { if err != nil {
l.renderError(w, r, nil, err) l.renderError(w, r, nil, err)
return return
@ -153,7 +153,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
redirect, err := l.redirectToJWTCallback(authReq) redirect, err := l.redirectToJWTCallback(r.Context(), authReq)
if err != nil { if err != nil {
l.renderError(w, r, nil, err) l.renderError(w, r, nil, err)
return return
@ -174,8 +174,8 @@ func (l *Login) appendUserGrants(ctx context.Context, userGrants []*domain.UserG
return nil return nil
} }
func (l *Login) redirectToJWTCallback(authReq *domain.AuthRequest) (string, error) { func (l *Login) redirectToJWTCallback(ctx context.Context, authReq *domain.AuthRequest) (string, error) {
redirect, err := url.Parse(l.baseURL + EndpointJWTCallback) redirect, err := url.Parse(l.baseURL(ctx) + EndpointJWTCallback)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -20,5 +20,5 @@ func (l *Login) linkUsers(w http.ResponseWriter, r *http.Request, authReq *domai
func (l *Login) renderLinkUsersDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) { func (l *Login) renderLinkUsersDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, err error) {
var errType, errMessage string var errType, errMessage string
data := l.getUserData(r, authReq, "Linking Users Done", errType, errMessage) data := l.getUserData(r, authReq, "Linking Users Done", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplLinkUsersDone], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplLinkUsersDone], data, nil)
} }

View File

@ -16,7 +16,6 @@ import (
auth_repository "github.com/caos/zitadel/internal/auth/repository" auth_repository "github.com/caos/zitadel/internal/auth/repository"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing" "github.com/caos/zitadel/internal/auth/repository/eventsourcing"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/form" "github.com/caos/zitadel/internal/form"
@ -33,12 +32,11 @@ type Login struct {
query *query.Queries query *query.Queries
staticStorage static.Storage staticStorage static.Storage
authRepo auth_repository.Repository authRepo auth_repository.Repository
baseURL string externalSecure bool
consolePath string consolePath string
oidcAuthCallbackURL func(context.Context, string) string oidcAuthCallbackURL func(context.Context, string) string
idpConfigAlg crypto.EncryptionAlgorithm idpConfigAlg crypto.EncryptionAlgorithm
userCodeAlg crypto.EncryptionAlgorithm userCodeAlg crypto.EncryptionAlgorithm
iamDomain string
} }
type Config struct { type Config struct {
@ -58,10 +56,7 @@ func CreateLogin(config Config,
query *query.Queries, query *query.Queries,
authRepo *eventsourcing.EsRepository, authRepo *eventsourcing.EsRepository,
staticStorage static.Storage, staticStorage static.Storage,
systemDefaults systemdefaults.SystemDefaults, consolePath string,
consolePath,
domain,
baseURL string,
oidcAuthCallbackURL func(context.Context, string) string, oidcAuthCallbackURL func(context.Context, string) string,
externalSecure bool, externalSecure bool,
userAgentCookie, userAgentCookie,
@ -74,13 +69,12 @@ func CreateLogin(config Config,
login := &Login{ login := &Login{
oidcAuthCallbackURL: oidcAuthCallbackURL, oidcAuthCallbackURL: oidcAuthCallbackURL,
baseURL: baseURL + HandlerPrefix, externalSecure: externalSecure,
consolePath: consolePath, consolePath: consolePath,
command: command, command: command,
query: query, query: query,
staticStorage: staticStorage, staticStorage: staticStorage,
authRepo: authRepo, authRepo: authRepo,
iamDomain: domain,
idpConfigAlg: idpConfigAlg, idpConfigAlg: idpConfigAlg,
userCodeAlg: userCodeAlg, userCodeAlg: userCodeAlg,
} }
@ -100,7 +94,7 @@ func CreateLogin(config Config,
security := middleware.SecurityHeaders(csp(), login.cspErrorHandler) security := middleware.SecurityHeaders(csp(), login.cspErrorHandler)
login.router = CreateRouter(login, statikFS, instanceHandler, csrfInterceptor, cacheInterceptor, security, userAgentCookie, middleware.TelemetryHandler(EndpointResources), issuerInterceptor) login.router = CreateRouter(login, statikFS, instanceHandler, csrfInterceptor, cacheInterceptor, security, userAgentCookie, middleware.TelemetryHandler(EndpointResources), issuerInterceptor)
login.renderer = CreateRenderer(HandlerPrefix, statikFS, staticStorage, config.LanguageCookieName, systemDefaults.DefaultLanguage) login.renderer = CreateRenderer(HandlerPrefix, statikFS, staticStorage, config.LanguageCookieName)
login.parser = form.NewParser() login.parser = form.NewParser()
return login, nil return login, nil
} }
@ -128,7 +122,7 @@ func (l *Login) Handler() http.Handler {
} }
func (l *Login) getClaimedUserIDsOfOrgDomain(ctx context.Context, orgName string) ([]string, error) { func (l *Login) getClaimedUserIDsOfOrgDomain(ctx context.Context, orgName string) ([]string, error) {
loginName, err := query.NewUserPreferredLoginNameSearchQuery("@"+domain.NewIAMDomainName(orgName, l.iamDomain), query.TextEndsWithIgnoreCase) loginName, err := query.NewUserPreferredLoginNameSearchQuery("@"+domain.NewIAMDomainName(orgName, authz.GetInstance(ctx).RequestedDomain()), query.TextEndsWithIgnoreCase)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -150,3 +144,7 @@ func setContext(ctx context.Context, resourceOwner string) context.Context {
} }
return authz.SetCtxData(ctx, data) return authz.SetCtxData(ctx, data)
} }
func (l *Login) baseURL(ctx context.Context) string {
return http_utils.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), l.externalSecure) + HandlerPrefix
}

View File

@ -17,6 +17,14 @@ type loginData struct {
Register bool `schema:"register"` Register bool `schema:"register"`
} }
func LoginLink(origin string) string {
return externalLink(origin) + EndpointLogin
}
func externalLink(origin string) string {
return origin + HandlerPrefix
}
func (l *Login) handleLogin(w http.ResponseWriter, r *http.Request) { func (l *Login) handleLogin(w http.ResponseWriter, r *http.Request) {
authReq, err := l.getAuthRequest(r) authReq, err := l.getAuthRequest(r)
if err != nil { if err != nil {
@ -82,5 +90,5 @@ func (l *Login) renderLogin(w http.ResponseWriter, r *http.Request, authReq *dom
return authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowExternalIDP && authReq.AllowedExternalIDPs != nil && len(authReq.AllowedExternalIDPs) > 0 return authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowExternalIDP && authReq.AllowedExternalIDPs != nil && len(authReq.AllowedExternalIDPs) > 0
}, },
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplLogin], data, funcs) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplLogin], data, funcs)
} }

View File

@ -45,7 +45,7 @@ func (l *Login) renderSuccessAndCallback(w http.ResponseWriter, r *http.Request,
if authReq != nil { if authReq != nil {
data.RedirectURI = l.oidcAuthCallbackURL(r.Context(), "") //the id will be set via the html (maybe change this with the login refactoring) data.RedirectURI = l.oidcAuthCallbackURL(r.Context(), "") //the id will be set via the html (maybe change this with the login refactoring)
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplLoginSuccess], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplLoginSuccess], data, nil)
} }
func (l *Login) redirectToCallback(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) redirectToCallback(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {

View File

@ -14,5 +14,5 @@ func (l *Login) handleLogoutDone(w http.ResponseWriter, r *http.Request) {
func (l *Login) renderLogoutDone(w http.ResponseWriter, r *http.Request) { func (l *Login) renderLogoutDone(w http.ResponseWriter, r *http.Request) {
data := l.getUserData(r, nil, "Logout Done", "", "") data := l.getUserData(r, nil, "Logout Done", "", "")
l.renderer.RenderTemplate(w, r, l.getTranslator(nil), l.renderer.Templates[tmplLogoutDone], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), nil), l.renderer.Templates[tmplLogoutDone], data, nil)
} }

View File

@ -1,6 +1,7 @@
package login package login
import ( import (
"fmt"
"net/http" "net/http"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
@ -26,6 +27,10 @@ type mailVerificationData struct {
UserID string UserID string
} }
func MailVerificationLink(origin, userID, code string) string {
return fmt.Sprintf("%s%s?userID=%s&code=%s", externalLink(origin), EndpointMailVerification, userID, code)
}
func (l *Login) handleMailVerification(w http.ResponseWriter, r *http.Request) { func (l *Login) handleMailVerification(w http.ResponseWriter, r *http.Request) {
userID := r.FormValue(queryUserID) userID := r.FormValue(queryUserID)
code := r.FormValue(queryCode) code := r.FormValue(queryCode)
@ -92,7 +97,7 @@ func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, a
UserID: userID, UserID: userID,
profileData: l.getProfileData(authReq), profileData: l.getProfileData(authReq),
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMailVerification], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMailVerification], data, nil)
} }
@ -101,6 +106,6 @@ func (l *Login) renderMailVerified(w http.ResponseWriter, r *http.Request, authR
baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""), baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""),
profileData: l.getProfileData(authReq), profileData: l.getProfileData(authReq),
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMailVerified], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMailVerified], data, nil)
} }

View File

@ -17,6 +17,6 @@ func (l *Login) renderMFAInitDone(w http.ResponseWriter, r *http.Request, authRe
var errType, errMessage string var errType, errMessage string
data.baseData = l.getBaseData(r, authReq, "MFA Init Done", errType, errMessage) data.baseData = l.getBaseData(r, authReq, "MFA Init Done", errType, errMessage)
data.profileData = l.getProfileData(authReq) data.profileData = l.getProfileData(authReq)
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMFAInitDone], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMFAInitDone], data, nil)
} }

View File

@ -36,7 +36,7 @@ func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authRe
}, },
MFAType: domain.MFATypeU2F, MFAType: domain.MFATypeU2F,
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplMFAU2FInit], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplMFAU2FInit], data, nil)
} }
func (l *Login) handleRegisterU2F(w http.ResponseWriter, r *http.Request) { func (l *Login) handleRegisterU2F(w http.ResponseWriter, r *http.Request) {

View File

@ -79,7 +79,7 @@ func (l *Login) renderMFAInitVerify(w http.ResponseWriter, r *http.Request, auth
} }
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMFAInitVerify], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMFAInitVerify], data, nil)
} }

View File

@ -74,7 +74,7 @@ func (l *Login) renderMFAPrompt(w http.ResponseWriter, r *http.Request, authReq
l.handleMFACreation(w, r, authReq, data) l.handleMFACreation(w, r, authReq, data)
return return
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMFAPrompt], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplMFAPrompt], data, nil)
} }

View File

@ -74,7 +74,7 @@ func (l *Login) renderMFAVerifySelected(w http.ResponseWriter, r *http.Request,
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplMFAVerify], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplMFAVerify], data, nil)
} }
func removeSelectedProviderFromList(providers []domain.MFAType, selected domain.MFAType) []domain.MFAType { func removeSelectedProviderFromList(providers []domain.MFAType, selected domain.MFAType) []domain.MFAType {

View File

@ -45,7 +45,7 @@ func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, au
MFAProviders: providers, MFAProviders: providers,
SelectedProvider: -1, SelectedProvider: -1,
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplU2FVerification], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplU2FVerification], data, nil)
} }
func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) { func (l *Login) handleU2FVerification(w http.ResponseWriter, r *http.Request) {

View File

@ -47,7 +47,7 @@ func (l *Login) getPasswordComplexityPolicyByUserID(r *http.Request, authReq *do
func (l *Login) generatePolicyDescription(r *http.Request, authReq *domain.AuthRequest, policy *iam_model.PasswordComplexityPolicyView) (string, error) { func (l *Login) generatePolicyDescription(r *http.Request, authReq *domain.AuthRequest, policy *iam_model.PasswordComplexityPolicyView) (string, error) {
description := "<ul class=\"lgn-no-dots lgn-policy\" id=\"passwordcomplexity\">" description := "<ul class=\"lgn-no-dots lgn-policy\" id=\"passwordcomplexity\">"
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
minLength := l.renderer.LocalizeFromRequest(translator, r, "Password.MinLength", nil) minLength := l.renderer.LocalizeFromRequest(translator, r, "Password.MinLength", nil)
description += "<li id=\"minlength\" class=\"invalid\"><i class=\"lgn-icon-times-solid lgn-warn\"></i><span>" + minLength + " " + strconv.Itoa(int(policy.MinLength)) + "</span></li>" description += "<li id=\"minlength\" class=\"invalid\"><i class=\"lgn-icon-times-solid lgn-warn\"></i><span>" + minLength + " " + strconv.Itoa(int(policy.MinLength)) + "</span></li>"
if policy.HasUppercase { if policy.HasUppercase {

View File

@ -28,7 +28,7 @@ func (l *Login) renderPassword(w http.ResponseWriter, r *http.Request, authReq *
return true return true
}, },
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplPassword], data, funcs) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplPassword], data, funcs)
} }
func (l *Login) handlePasswordCheck(w http.ResponseWriter, r *http.Request) { func (l *Login) handlePasswordCheck(w http.ResponseWriter, r *http.Request) {

View File

@ -42,5 +42,5 @@ func (l *Login) renderPasswordResetDone(w http.ResponseWriter, r *http.Request,
errID, errMessage = l.getErrorMessage(r, err) errID, errMessage = l.getErrorMessage(r, err)
} }
data := l.getUserData(r, authReq, "Password Reset Done", errID, errMessage) data := l.getUserData(r, authReq, "Password Reset Done", errID, errMessage)
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplPasswordResetDone], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplPasswordResetDone], data, nil)
} }

View File

@ -43,7 +43,7 @@ func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Re
}, },
passwordSet, passwordSet,
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplPasswordlessVerification], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplPasswordlessVerification], data, nil)
} }
func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Request) { func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Request) {

View File

@ -35,6 +35,6 @@ func (l *Login) renderPasswordlessPrompt(w http.ResponseWriter, r *http.Request,
userData: l.getUserData(r, authReq, "Passwordless Prompt", errID, errMessage), userData: l.getUserData(r, authReq, "Passwordless Prompt", errID, errMessage),
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplPasswordlessPrompt], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplPasswordlessPrompt], data, nil)
} }

View File

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"github.com/caos/logging" "github.com/caos/logging"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
@ -110,13 +111,13 @@ func (l *Login) renderPasswordlessRegistration(w http.ResponseWriter, r *http.Re
requestedPlatformType, requestedPlatformType,
disabled, disabled,
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
if authReq == nil { if authReq == nil {
policy, err := l.query.ActiveLabelPolicyByOrg(r.Context(), orgID) policy, err := l.query.ActiveLabelPolicyByOrg(r.Context(), orgID)
logging.Log("HANDL-XjWKE").OnError(err).Error("unable to get active label policy") logging.Log("HANDL-XjWKE").OnError(err).Error("unable to get active label policy")
data.LabelPolicy = labelPolicyToDomain(policy) data.LabelPolicy = labelPolicyToDomain(policy)
translator, err = l.renderer.NewTranslator() translator, err = l.renderer.NewTranslator(r.Context())
if err == nil { if err == nil {
texts, err := l.authRepo.GetLoginText(r.Context(), orgID) texts, err := l.authRepo.GetLoginText(r.Context(), orgID)
logging.Log("LOGIN-HJK4t").OnError(err).Warn("could not get custom texts") logging.Log("LOGIN-HJK4t").OnError(err).Warn("could not get custom texts")
@ -194,5 +195,5 @@ func (l *Login) renderPasswordlessRegistrationDone(w http.ResponseWriter, r *htt
userData: l.getUserData(r, authReq, "Passwordless Registration Done", errID, errMessage), userData: l.getUserData(r, authReq, "Passwordless Registration Done", errID, errMessage),
HideNextButton: authReq == nil, HideNextButton: authReq == nil,
} }
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplPasswordlessRegistrationDone], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplPasswordlessRegistrationDone], data, nil)
} }

View File

@ -107,7 +107,7 @@ func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authReque
if err != nil { if err != nil {
errID, errMessage = l.getErrorMessage(r, err) errID, errMessage = l.getErrorMessage(r, err)
} }
translator := l.getTranslator(authRequest) translator := l.getTranslator(r.Context(), authRequest)
if formData == nil { if formData == nil {
formData = new(registerFormData) formData = new(registerFormData)
} }

View File

@ -41,7 +41,7 @@ func (l *Login) renderRegisterOption(w http.ResponseWriter, r *http.Request, aut
return authReq.LoginPolicy.AllowExternalIDP && authReq.AllowedExternalIDPs != nil && len(authReq.AllowedExternalIDPs) > 0 return authReq.LoginPolicy.AllowExternalIDP && authReq.AllowedExternalIDPs != nil && len(authReq.AllowedExternalIDPs) > 0
}, },
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegisterOption], data, funcs) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegisterOption], data, funcs)
} }

View File

@ -3,6 +3,7 @@ package login
import ( import (
"net/http" "net/http"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
@ -110,10 +111,10 @@ func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRe
orgPolicy, _ := l.getDefaultDomainPolicy(r) orgPolicy, _ := l.getDefaultDomainPolicy(r)
if orgPolicy != nil { if orgPolicy != nil {
data.UserLoginMustBeDomain = orgPolicy.UserLoginMustBeDomain data.UserLoginMustBeDomain = orgPolicy.UserLoginMustBeDomain
data.IamDomain = l.iamDomain data.IamDomain = authz.GetInstance(r.Context()).RequestedDomain()
} }
translator := l.getTranslator(authRequest) translator := l.getTranslator(r.Context(), authRequest)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegisterOrg], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegisterOrg], data, nil)
} }

View File

@ -1,6 +1,7 @@
package login package login
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
@ -9,10 +10,9 @@ import (
"strings" "strings"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/api/authz"
"github.com/gorilla/csrf" "github.com/gorilla/csrf"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
http_mw "github.com/caos/zitadel/internal/api/http/middleware" http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
@ -36,7 +36,7 @@ type LanguageData struct {
Lang string Lang string
} }
func CreateRenderer(pathPrefix string, staticDir http.FileSystem, staticStorage static.Storage, cookieName string, defaultLanguage language.Tag) *Renderer { func CreateRenderer(pathPrefix string, staticDir http.FileSystem, staticStorage static.Storage, cookieName string) *Renderer {
r := &Renderer{ r := &Renderer{
pathPrefix: pathPrefix, pathPrefix: pathPrefix,
staticStorage: staticStorage, staticStorage: staticStorage,
@ -219,7 +219,7 @@ func CreateRenderer(pathPrefix string, staticDir http.FileSystem, staticStorage
r.Renderer, err = renderer.NewRenderer( r.Renderer, err = renderer.NewRenderer(
staticDir, staticDir,
tmplMapping, funcs, tmplMapping, funcs,
i18n.TranslatorConfig{DefaultLanguage: defaultLanguage, CookieName: cookieName}, cookieName,
) )
logging.New().OnError(err).WithError(err).Panic("error creating renderer") logging.New().OnError(err).WithError(err).Panic("error creating renderer")
return r return r
@ -317,7 +317,7 @@ func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, auth
_, msg = l.getErrorMessage(r, err) _, msg = l.getErrorMessage(r, err)
} }
data := l.getBaseData(r, authReq, "Error", "Internal", msg) data := l.getBaseData(r, authReq, "Error", "Internal", msg)
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplError], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplError], data, nil)
} }
func (l *Login) getUserData(r *http.Request, authReq *domain.AuthRequest, title string, errType, errMessage string) userData { func (l *Login) getUserData(r *http.Request, authReq *domain.AuthRequest, title string, errType, errMessage string) userData {
@ -337,7 +337,7 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
ErrID: errType, ErrID: errType,
ErrMessage: errMessage, ErrMessage: errMessage,
}, },
Lang: l.renderer.ReqLang(l.getTranslator(authReq), r).String(), Lang: l.renderer.ReqLang(l.getTranslator(r.Context(), authReq), r).String(),
Title: title, Title: title,
Theme: l.getTheme(r), Theme: l.getTheme(r),
ThemeMode: l.getThemeMode(r), ThemeMode: l.getThemeMode(r),
@ -371,8 +371,8 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
return baseData return baseData
} }
func (l *Login) getTranslator(authReq *domain.AuthRequest) *i18n.Translator { func (l *Login) getTranslator(ctx context.Context, authReq *domain.AuthRequest) *i18n.Translator {
translator, _ := l.renderer.NewTranslator() translator, _ := l.renderer.NewTranslator(ctx)
if authReq != nil { if authReq != nil {
l.addLoginTranslations(translator, authReq.DefaultTranslations) l.addLoginTranslations(translator, authReq.DefaultTranslations)
l.addLoginTranslations(translator, authReq.OrgTranslations) l.addLoginTranslations(translator, authReq.OrgTranslations)
@ -420,7 +420,7 @@ func (l *Login) setLinksOnBaseData(baseData baseData, privacyPolicy *domain.Priv
func (l *Login) getErrorMessage(r *http.Request, err error) (errID, errMsg string) { func (l *Login) getErrorMessage(r *http.Request, err error) (errID, errMsg string) {
caosErr := new(caos_errs.CaosError) caosErr := new(caos_errs.CaosError)
if errors.As(err, &caosErr) { if errors.As(err, &caosErr) {
localized := l.renderer.LocalizeFromRequest(l.getTranslator(nil), r, caosErr.Message, nil) localized := l.renderer.LocalizeFromRequest(l.getTranslator(r.Context(), nil), r, caosErr.Message, nil)
return caosErr.ID, localized return caosErr.ID, localized
} }

View File

@ -22,7 +22,7 @@ func (l *Login) renderUserSelection(w http.ResponseWriter, r *http.Request, auth
Users: selectionData.Users, Users: selectionData.Users,
Linking: len(authReq.LinkingUsers) > 0, Linking: len(authReq.LinkingUsers) > 0,
} }
translator := l.getTranslator(authReq) translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplUserSelection], data, nil) l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplUserSelection], data, nil)
} }

View File

@ -21,7 +21,7 @@ func (l *Login) renderChangeUsername(w http.ResponseWriter, r *http.Request, aut
errID, errMessage = l.getErrorMessage(r, err) errID, errMessage = l.getErrorMessage(r, err)
} }
data := l.getUserData(r, authReq, "Change Username", errID, errMessage) data := l.getUserData(r, authReq, "Change Username", errID, errMessage)
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplChangeUsername], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplChangeUsername], data, nil)
} }
func (l *Login) handleChangeUsername(w http.ResponseWriter, r *http.Request) { func (l *Login) handleChangeUsername(w http.ResponseWriter, r *http.Request) {
@ -42,5 +42,5 @@ func (l *Login) handleChangeUsername(w http.ResponseWriter, r *http.Request) {
func (l *Login) renderChangeUsernameDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { func (l *Login) renderChangeUsernameDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
var errType, errMessage string var errType, errMessage string
data := l.getUserData(r, authReq, "Username Change Done", errType, errMessage) data := l.getUserData(r, authReq, "Username Change Done", errType, errMessage)
l.renderer.RenderTemplate(w, r, l.getTranslator(authReq), l.renderer.Templates[tmplChangeUsernameDone], data, nil) l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplChangeUsernameDone], data, nil)
} }

View File

@ -24,11 +24,12 @@ import (
) )
type Commands struct { type Commands struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
static static.Storage static static.Storage
idGenerator id.Generator idGenerator id.Generator
iamDomain string zitadelRoles []authz.RoleMapping
zitadelRoles []authz.RoleMapping externalSecure bool
externalPort uint16
idpConfigEncryption crypto.EncryptionAlgorithm idpConfigEncryption crypto.EncryptionAlgorithm
smtpEncryption crypto.EncryptionAlgorithm smtpEncryption crypto.EncryptionAlgorithm
@ -61,6 +62,8 @@ func StartCommands(es *eventstore.Eventstore,
staticStore static.Storage, staticStore static.Storage,
authZRepo authz_repo.Repository, authZRepo authz_repo.Repository,
webAuthN *webauthn_helper.Config, webAuthN *webauthn_helper.Config,
externalSecure bool,
externalPort uint16,
idpConfigEncryption, idpConfigEncryption,
otpEncryption, otpEncryption,
smtpEncryption, smtpEncryption,
@ -73,8 +76,9 @@ func StartCommands(es *eventstore.Eventstore,
eventstore: es, eventstore: es,
static: staticStore, static: staticStore,
idGenerator: id.SonyFlakeGenerator, idGenerator: id.SonyFlakeGenerator,
iamDomain: defaults.Domain,
zitadelRoles: zitadelRoles, zitadelRoles: zitadelRoles,
externalSecure: externalSecure,
externalPort: externalPort,
keySize: defaults.KeyConfig.Size, keySize: defaults.KeyConfig.Size,
privateKeyLifetime: defaults.KeyConfig.PrivateKeyLifetime, privateKeyLifetime: defaults.KeyConfig.PrivateKeyLifetime,
publicKeyLifetime: defaults.KeyConfig.PublicKeyLifetime, publicKeyLifetime: defaults.KeyConfig.PublicKeyLifetime,

View File

@ -157,7 +157,7 @@ func (s *InstanceSetup) generateIDs() (err error) {
return nil return nil
} }
func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, externalSecure bool, baseURL string) (string, *domain.ObjectDetails, error) { func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, externalSecure bool) (string, *domain.ObjectDetails, error) {
instanceID, err := id.SonyFlakeGenerator.Next() instanceID, err := id.SonyFlakeGenerator.Next()
if err != nil { if err != nil {
return "", nil, err return "", nil, err
@ -167,7 +167,6 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, exte
return "", nil, err return "", nil, err
} }
ctx = authz.SetCtxData(authz.WithInstanceID(ctx, instanceID), authz.CtxData{OrgID: instanceID, ResourceOwner: instanceID})
requestedDomain := authz.GetInstance(ctx).RequestedDomain() requestedDomain := authz.GetInstance(ctx).RequestedDomain()
ctx = authz.SetCtxData(authz.WithRequestedDomain(authz.WithInstanceID(ctx, instanceID), requestedDomain), authz.CtxData{OrgID: instanceID, ResourceOwner: instanceID}) ctx = authz.SetCtxData(authz.WithRequestedDomain(authz.WithInstanceID(ctx, instanceID), requestedDomain), authz.CtxData{OrgID: instanceID, ResourceOwner: instanceID})
@ -184,6 +183,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, exte
if err = setup.generateIDs(); err != nil { if err = setup.generateIDs(); err != nil {
return "", nil, err return "", nil, err
} }
ctx = authz.WithConsole(ctx, setup.zitadel.projectID, setup.zitadel.consoleAppID)
setup.Org.Human.PasswordChangeRequired = true setup.Org.Human.PasswordChangeRequired = true
@ -194,7 +194,6 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, exte
validations := []preparation.Validation{ validations := []preparation.Validation{
addInstance(instanceAgg, setup.InstanceName), addInstance(instanceAgg, setup.InstanceName),
c.addGeneratedInstanceDomain(instanceAgg, setup.InstanceName),
SetDefaultFeatures( SetDefaultFeatures(
instanceAgg, instanceAgg,
setup.Features.TierName, setup.Features.TierName,
@ -290,10 +289,6 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, exte
validations = append(validations, SetInstanceCustomTexts(instanceAgg, msg)) validations = append(validations, SetInstanceCustomTexts(instanceAgg, msg))
} }
if setup.CustomDomain != "" {
validations = append(validations, addInstanceDomain(instanceAgg, setup.CustomDomain, false))
}
console := &addOIDCApp{ console := &addOIDCApp{
AddApp: AddApp{ AddApp: AddApp{
Aggregate: *projectAgg, Aggregate: *projectAgg,
@ -301,12 +296,12 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, exte
Name: consoleAppName, Name: consoleAppName,
}, },
Version: domain.OIDCVersionV1, Version: domain.OIDCVersionV1,
RedirectUris: []string{baseURL + consoleRedirectPath}, RedirectUris: []string{},
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode}, ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode}, GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
ApplicationType: domain.OIDCApplicationTypeUserAgent, ApplicationType: domain.OIDCApplicationTypeUserAgent,
AuthMethodType: domain.OIDCAuthMethodTypeNone, AuthMethodType: domain.OIDCAuthMethodTypeNone,
PostLogoutRedirectUris: []string{baseURL + consolePostLogoutPath}, PostLogoutRedirectUris: []string{},
DevMode: !externalSecure, DevMode: !externalSecure,
AccessTokenType: domain.OIDCTokenTypeBearer, AccessTokenType: domain.OIDCTokenTypeBearer,
AccessTokenRoleAssertion: false, AccessTokenRoleAssertion: false,
@ -362,7 +357,11 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, exte
AddOIDCAppCommand(console, nil), AddOIDCAppCommand(console, nil),
SetIAMConsoleID(instanceAgg, &console.ClientID, &setup.zitadel.consoleAppID), SetIAMConsoleID(instanceAgg, &console.ClientID, &setup.zitadel.consoleAppID),
c.addGeneratedInstanceDomain(ctx, instanceAgg, setup.InstanceName),
) )
if setup.CustomDomain != "" {
validations = append(validations, c.addInstanceDomain(instanceAgg, setup.CustomDomain, false))
}
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validations...) cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validations...)
if err != nil { if err != nil {

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/command/preparation" "github.com/caos/zitadel/internal/command/preparation"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
@ -15,7 +16,7 @@ import (
func (c *Commands) AddInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) { func (c *Commands) AddInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
validation := addInstanceDomain(instanceAgg, instanceDomain, false) validation := c.addInstanceDomain(instanceAgg, instanceDomain, false)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation) cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
if err != nil { if err != nil {
return nil, err return nil, err
@ -67,12 +68,12 @@ func (c *Commands) RemoveInstanceDomain(ctx context.Context, instanceDomain stri
}, nil }, nil
} }
func (c *Commands) addGeneratedInstanceDomain(a *instance.Aggregate, instanceName string) preparation.Validation { func (c *Commands) addGeneratedInstanceDomain(ctx context.Context, a *instance.Aggregate, instanceName string) preparation.Validation {
domain := domain.NewGeneratedInstanceDomain(instanceName, c.iamDomain) domain := domain.NewGeneratedInstanceDomain(instanceName, authz.GetInstance(ctx).RequestedDomain())
return addInstanceDomain(a, domain, true) return c.addInstanceDomain(a, domain, true)
} }
func addInstanceDomain(a *instance.Aggregate, instanceDomain string, generated bool) preparation.Validation { func (c *Commands) addInstanceDomain(a *instance.Aggregate, instanceDomain string, generated bool) preparation.Validation {
return func() (preparation.CreateCommands, error) { return func() (preparation.CreateCommands, error) {
if instanceDomain = strings.TrimSpace(instanceDomain); instanceDomain == "" { if instanceDomain = strings.TrimSpace(instanceDomain); instanceDomain == "" {
return nil, errors.ThrowInvalidArgument(nil, "INST-28nlD", "Errors.Invalid.Argument") return nil, errors.ThrowInvalidArgument(nil, "INST-28nlD", "Errors.Invalid.Argument")
@ -93,8 +94,8 @@ func addInstanceDomain(a *instance.Aggregate, instanceDomain string, generated b
return nil, err return nil, err
} }
if appWriteModel.State.Exists() { if appWriteModel.State.Exists() {
redirectUrls := append(appWriteModel.RedirectUris, instanceDomain+consoleRedirectPath) redirectUrls := append(appWriteModel.RedirectUris, http.BuildHTTP(instanceDomain, c.externalPort, c.externalSecure)+consoleRedirectPath)
logoutUrls := append(appWriteModel.PostLogoutRedirectUris, instanceDomain+consolePostLogoutPath) logoutUrls := append(appWriteModel.PostLogoutRedirectUris, http.BuildOrigin(instanceDomain, c.externalSecure)+consolePostLogoutPath)
consoleChangeEvent, err := project.NewOIDCConfigChangedEvent( consoleChangeEvent, err := project.NewOIDCConfigChangedEvent(
ctx, ctx,
ProjectAggregateFromWriteModel(&appWriteModel.WriteModel), ProjectAggregateFromWriteModel(&appWriteModel.WriteModel),

View File

@ -5,10 +5,11 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/repository/project" "github.com/caos/zitadel/internal/repository/project"
"github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
@ -19,7 +20,8 @@ import (
func TestCommandSide_AddInstanceDomain(t *testing.T) { func TestCommandSide_AddInstanceDomain(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
externalSecure bool
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
@ -134,6 +136,7 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) {
uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", instance.NewAddInstanceDomainUniqueConstraint("domain.ch")), uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", instance.NewAddInstanceDomainUniqueConstraint("domain.ch")),
), ),
), ),
externalSecure: true,
}, },
args: args{ args: args{
ctx: authz.WithInstance(context.Background(), new(mockInstance)), ctx: authz.WithInstance(context.Background(), new(mockInstance)),
@ -149,7 +152,8 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
externalSecure: tt.fields.externalSecure,
} }
got, err := r.AddInstanceDomain(tt.args.ctx, tt.args.domain) got, err := r.AddInstanceDomain(tt.args.ctx, tt.args.domain)
if tt.res.err == nil { if tt.res.err == nil {
@ -404,8 +408,8 @@ func TestCommandSide_RemoveInstanceDomain(t *testing.T) {
func newOIDCAppChangedEventInstanceDomain(ctx context.Context, appID, projectID, resourceOwner string) *project.OIDCConfigChangedEvent { func newOIDCAppChangedEventInstanceDomain(ctx context.Context, appID, projectID, resourceOwner string) *project.OIDCConfigChangedEvent {
changes := []project.OIDCConfigChanges{ changes := []project.OIDCConfigChanges{
project.ChangeRedirectURIs([]string{"https://test.ch", "domain.ch/ui/console/auth/callback"}), project.ChangeRedirectURIs([]string{"https://test.ch", "https://domain.ch/ui/console/auth/callback"}),
project.ChangePostLogoutRedirectURIs([]string{"https://test.ch/logout", "domain.ch/ui/console/signedout"}), project.ChangePostLogoutRedirectURIs([]string{"https://test.ch/logout", "https://domain.ch/ui/console/signedout"}),
} }
event, _ := project.NewOIDCConfigChangedEvent(ctx, event, _ := project.NewOIDCConfigChangedEvent(ctx,
&project.NewAggregate(projectID, resourceOwner).Aggregate, &project.NewAggregate(projectID, resourceOwner).Aggregate,

View File

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
@ -275,6 +276,14 @@ func (m *mockInstance) ConsoleApplicationID() string {
return "consoleApplicationID" return "consoleApplicationID"
} }
func (m *mockInstance) DefaultLanguage() language.Tag {
return language.English
}
func (m *mockInstance) RequestedDomain() string { func (m *mockInstance) RequestedDomain() string {
return "zitadel.cloud" return "zitadel.cloud"
} }
func (m *mockInstance) RequestedHost() string {
return "zitadel.cloud:443"
}

View File

@ -249,7 +249,7 @@ func (c *Commands) addOrg(ctx context.Context, organisation *domain.Org, claimed
if err != nil { if err != nil {
return nil, nil, nil, caos_errs.ThrowInternal(err, "COMMA-OwciI", "Errors.Internal") return nil, nil, nil, caos_errs.ThrowInternal(err, "COMMA-OwciI", "Errors.Internal")
} }
organisation.AddIAMDomain(c.iamDomain) organisation.AddIAMDomain(authz.GetInstance(ctx).RequestedDomain())
addedOrg := NewOrgWriteModel(organisation.AggregateID) addedOrg := NewOrgWriteModel(organisation.AggregateID)
orgAgg := OrgAggregateFromWriteModel(&addedOrg.WriteModel) orgAgg := OrgAggregateFromWriteModel(&addedOrg.WriteModel)

View File

@ -7,6 +7,7 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/api/authz"
http_utils "github.com/caos/zitadel/internal/api/http" http_utils "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/command/preparation" "github.com/caos/zitadel/internal/command/preparation"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
@ -292,13 +293,14 @@ func (c *Commands) changeDefaultDomain(ctx context.Context, orgID, newName strin
if err != nil { if err != nil {
return nil, err return nil, err
} }
defaultDomain := domain.NewIAMDomainName(orgDomains.OrgName, c.iamDomain) iamDomain := authz.GetInstance(ctx).RequestedDomain()
defaultDomain := domain.NewIAMDomainName(orgDomains.OrgName, iamDomain)
isPrimary := defaultDomain == orgDomains.PrimaryDomain isPrimary := defaultDomain == orgDomains.PrimaryDomain
orgAgg := OrgAggregateFromWriteModel(&orgDomains.WriteModel) orgAgg := OrgAggregateFromWriteModel(&orgDomains.WriteModel)
for _, orgDomain := range orgDomains.Domains { for _, orgDomain := range orgDomains.Domains {
if orgDomain.State == domain.OrgDomainStateActive { if orgDomain.State == domain.OrgDomainStateActive {
if orgDomain.Domain == defaultDomain { if orgDomain.Domain == defaultDomain {
newDefaultDomain := domain.NewIAMDomainName(newName, c.iamDomain) newDefaultDomain := domain.NewIAMDomainName(newName, iamDomain)
events := []eventstore.Command{ events := []eventstore.Command{
org.NewDomainAddedEvent(ctx, orgAgg, newDefaultDomain), org.NewDomainAddedEvent(ctx, orgAgg, newDefaultDomain),
org.NewDomainVerifiedEvent(ctx, orgAgg, newDefaultDomain), org.NewDomainVerifiedEvent(ctx, orgAgg, newDefaultDomain),
@ -321,7 +323,7 @@ func (c *Commands) removeCustomDomains(ctx context.Context, orgID string) ([]eve
return nil, err return nil, err
} }
hasDefault := false hasDefault := false
defaultDomain := domain.NewIAMDomainName(orgDomains.OrgName, c.iamDomain) defaultDomain := domain.NewIAMDomainName(orgDomains.OrgName, authz.GetInstance(ctx).RequestedDomain())
isPrimary := defaultDomain == orgDomains.PrimaryDomain isPrimary := defaultDomain == orgDomains.PrimaryDomain
orgAgg := OrgAggregateFromWriteModel(&orgDomains.WriteModel) orgAgg := OrgAggregateFromWriteModel(&orgDomains.WriteModel)
events := make([]eventstore.Command, 0, len(orgDomains.Domains)) events := make([]eventstore.Command, 0, len(orgDomains.Domains))

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/http" "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/command/preparation" "github.com/caos/zitadel/internal/command/preparation"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
@ -1090,10 +1091,9 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
domainVerificationGenerator: tt.fields.secretGenerator, domainVerificationGenerator: tt.fields.secretGenerator,
domainVerificationAlg: tt.fields.alg, domainVerificationAlg: tt.fields.alg,
domainVerificationValidator: tt.fields.domainValidationFunc, domainVerificationValidator: tt.fields.domainValidationFunc,
iamDomain: "zitadel.ch",
idGenerator: tt.fields.idGenerator, idGenerator: tt.fields.idGenerator,
} }
got, err := r.ValidateOrgDomain(tt.args.ctx, tt.args.domain, tt.args.claimedUserIDs) got, err := r.ValidateOrgDomain(authz.WithRequestedDomain(tt.args.ctx, "zitadel.ch"), tt.args.domain, tt.args.claimedUserIDs)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@ -9,9 +9,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/caos/zitadel/internal/repository/user" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/static/mock"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -19,13 +17,14 @@ import (
"github.com/caos/zitadel/internal/repository/features" "github.com/caos/zitadel/internal/repository/features"
"github.com/caos/zitadel/internal/repository/instance" "github.com/caos/zitadel/internal/repository/instance"
"github.com/caos/zitadel/internal/repository/org" "github.com/caos/zitadel/internal/repository/org"
"github.com/caos/zitadel/internal/repository/user"
"github.com/caos/zitadel/internal/static" "github.com/caos/zitadel/internal/static"
"github.com/caos/zitadel/internal/static/mock"
) )
func TestCommandSide_SetOrgFeatures(t *testing.T) { func TestCommandSide_SetOrgFeatures(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
iamDomain string
static static.Storage static static.Storage
} }
type args struct { type args struct {
@ -291,10 +290,9 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
}, },
), ),
), ),
iamDomain: "iam-domain",
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
resourceOwner: "org1", resourceOwner: "org1",
features: &domain.Features{ features: &domain.Features{
TierName: "Test", TierName: "Test",
@ -503,10 +501,9 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("test1")), uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("test1")),
), ),
), ),
iamDomain: "iam-domain",
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
resourceOwner: "org1", resourceOwner: "org1",
features: &domain.Features{ features: &domain.Features{
TierName: "Test", TierName: "Test",
@ -723,10 +720,9 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("test1")), uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("test1")),
), ),
), ),
iamDomain: "iam-domain",
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
resourceOwner: "org1", resourceOwner: "org1",
features: &domain.Features{ features: &domain.Features{
TierName: "Test", TierName: "Test",
@ -953,10 +949,9 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("test1")), uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("test1")),
), ),
), ),
iamDomain: "iam-domain",
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
resourceOwner: "org1", resourceOwner: "org1",
features: &domain.Features{ features: &domain.Features{
TierName: "Test", TierName: "Test",
@ -1268,11 +1263,10 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
}, },
), ),
), ),
iamDomain: "iam-domain", static: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectsNoError(),
static: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectsNoError(),
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
resourceOwner: "org1", resourceOwner: "org1",
features: &domain.Features{ features: &domain.Features{
TierName: "Test", TierName: "Test",
@ -1462,10 +1456,9 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
}, },
), ),
), ),
iamDomain: "iam-domain",
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
resourceOwner: "org1", resourceOwner: "org1",
features: &domain.Features{ features: &domain.Features{
TierName: "Test", TierName: "Test",
@ -1500,7 +1493,6 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
iamDomain: tt.fields.iamDomain,
static: tt.fields.static, static: tt.fields.static,
} }
got, err := r.SetOrgFeatures(tt.args.ctx, tt.args.resourceOwner, tt.args.features) got, err := r.SetOrgFeatures(tt.args.ctx, tt.args.resourceOwner, tt.args.features)
@ -1520,7 +1512,6 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
func TestCommandSide_RemoveOrgFeatures(t *testing.T) { func TestCommandSide_RemoveOrgFeatures(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
iamDomain string
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
@ -1715,10 +1706,9 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) {
}, },
), ),
), ),
iamDomain: "iam-domain",
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
resourceOwner: "org1", resourceOwner: "org1",
}, },
res: res{ res: res{
@ -1732,7 +1722,6 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
iamDomain: tt.fields.iamDomain,
} }
got, err := r.RemoveOrgFeatures(tt.args.ctx, tt.args.resourceOwner) got, err := r.RemoveOrgFeatures(tt.args.ctx, tt.args.resourceOwner)
if tt.res.err == nil { if tt.res.err == nil {

View File

@ -71,7 +71,6 @@ func TestCommandSide_AddOrg(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
idGenerator id.Generator idGenerator id.Generator
iamDomain string
zitadelRoles []authz.RoleMapping zitadelRoles []authz.RoleMapping
} }
type args struct { type args struct {
@ -203,7 +202,6 @@ func TestCommandSide_AddOrg(t *testing.T) {
), ),
), ),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"),
iamDomain: "iam-domain",
zitadelRoles: []authz.RoleMapping{ zitadelRoles: []authz.RoleMapping{
{ {
Role: "ORG_OWNER", Role: "ORG_OWNER",
@ -211,7 +209,7 @@ func TestCommandSide_AddOrg(t *testing.T) {
}, },
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
name: "Org", name: "Org",
userID: "user1", userID: "user1",
resourceOwner: "org1", resourceOwner: "org1",
@ -272,7 +270,6 @@ func TestCommandSide_AddOrg(t *testing.T) {
), ),
), ),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"),
iamDomain: "iam-domain",
zitadelRoles: []authz.RoleMapping{ zitadelRoles: []authz.RoleMapping{
{ {
Role: "ORG_OWNER", Role: "ORG_OWNER",
@ -280,7 +277,7 @@ func TestCommandSide_AddOrg(t *testing.T) {
}, },
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
name: "Org", name: "Org",
userID: "user1", userID: "user1",
resourceOwner: "org1", resourceOwner: "org1",
@ -341,7 +338,6 @@ func TestCommandSide_AddOrg(t *testing.T) {
), ),
), ),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "org2"),
iamDomain: "iam-domain",
zitadelRoles: []authz.RoleMapping{ zitadelRoles: []authz.RoleMapping{
{ {
Role: "ORG_OWNER", Role: "ORG_OWNER",
@ -349,7 +345,7 @@ func TestCommandSide_AddOrg(t *testing.T) {
}, },
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
name: "Org", name: "Org",
userID: "user1", userID: "user1",
resourceOwner: "org1", resourceOwner: "org1",
@ -372,7 +368,6 @@ func TestCommandSide_AddOrg(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator, idGenerator: tt.fields.idGenerator,
iamDomain: tt.fields.iamDomain,
zitadelRoles: tt.fields.zitadelRoles, zitadelRoles: tt.fields.zitadelRoles,
} }
got, err := r.AddOrg(tt.args.ctx, tt.args.name, tt.args.userID, tt.args.resourceOwner, tt.args.claimedUserIDs) got, err := r.AddOrg(tt.args.ctx, tt.args.name, tt.args.userID, tt.args.resourceOwner, tt.args.claimedUserIDs)
@ -392,7 +387,6 @@ func TestCommandSide_AddOrg(t *testing.T) {
func TestCommandSide_ChangeOrg(t *testing.T) { func TestCommandSide_ChangeOrg(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
iamDomain string
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
@ -444,7 +438,6 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
{ {
name: "push failed, error", name: "push failed, error",
fields: fields{ fields: fields{
iamDomain: "zitadel.ch",
eventstore: eventstoreExpect( eventstore: eventstoreExpect(
t, t,
expectFilter( expectFilter(
@ -467,7 +460,7 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
), ),
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "zitadel.ch"),
orgID: "org1", orgID: "org1",
name: "neworg", name: "neworg",
}, },
@ -478,7 +471,6 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
{ {
name: "change org name verified, not primary", name: "change org name verified, not primary",
fields: fields{ fields: fields{
iamDomain: "zitadel.ch",
eventstore: eventstoreExpect( eventstore: eventstoreExpect(
t, t,
expectFilter( expectFilter(
@ -524,7 +516,7 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
), ),
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "zitadel.ch"),
orgID: "org1", orgID: "org1",
name: "neworg", name: "neworg",
}, },
@ -533,7 +525,6 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
{ {
name: "change org name verified, with primary", name: "change org name verified, with primary",
fields: fields{ fields: fields{
iamDomain: "zitadel.ch",
eventstore: eventstoreExpect( eventstore: eventstoreExpect(
t, t,
expectFilter( expectFilter(
@ -586,7 +577,7 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
), ),
}, },
args: args{ args: args{
ctx: context.Background(), ctx: authz.WithRequestedDomain(context.Background(), "zitadel.ch"),
orgID: "org1", orgID: "org1",
name: "neworg", name: "neworg",
}, },
@ -597,7 +588,6 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
r := &Commands{ r := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
iamDomain: tt.fields.iamDomain,
} }
_, err := r.ChangeOrg(tt.args.ctx, tt.args.orgID, tt.args.name) _, err := r.ChangeOrg(tt.args.ctx, tt.args.orgID, tt.args.name)
if tt.res.err == nil { if tt.res.err == nil {

View File

@ -7,6 +7,7 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/command/preparation" "github.com/caos/zitadel/internal/command/preparation"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
@ -333,7 +334,7 @@ func (c *Commands) userDomainClaimed(ctx context.Context, userID string) (events
user.NewDomainClaimedEvent( user.NewDomainClaimedEvent(
ctx, ctx,
userAgg, userAgg,
fmt.Sprintf("%s@temporary.%s", id, c.iamDomain), fmt.Sprintf("%s@temporary.%s", id, authz.GetInstance(ctx).RequestedDomain()),
existingUser.UserName, existingUser.UserName,
domainPolicy.UserLoginMustBeDomain), domainPolicy.UserLoginMustBeDomain),
}, changedUserGrant, nil }, changedUserGrant, nil

View File

@ -25,7 +25,6 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
idGenerator id.Generator idGenerator id.Generator
iamDomain string
keyAlgorithm crypto.EncryptionAlgorithm keyAlgorithm crypto.EncryptionAlgorithm
} }
type args struct { type args struct {
@ -285,7 +284,6 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
c := &Commands{ c := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator, idGenerator: tt.fields.idGenerator,
iamDomain: tt.fields.iamDomain,
keyAlgorithm: tt.fields.keyAlgorithm, keyAlgorithm: tt.fields.keyAlgorithm,
} }
got, gotRefresh, err := c.AddAccessAndRefreshToken(tt.args.ctx, tt.args.orgID, tt.args.agentID, tt.args.clientID, tt.args.userID, tt.args.refreshToken, got, gotRefresh, err := c.AddAccessAndRefreshToken(tt.args.ctx, tt.args.orgID, tt.args.agentID, tt.args.clientID, tt.args.userID, tt.args.refreshToken,

View File

@ -3,15 +3,10 @@ package systemdefaults
import ( import (
"time" "time"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
) )
type SystemDefaults struct { type SystemDefaults struct {
DefaultLanguage language.Tag
Domain string
ZitadelDocs ZitadelDocs
SecretGenerators SecretGenerators SecretGenerators SecretGenerators
Multifactors MultifactorConfig Multifactors MultifactorConfig
DomainVerification DomainVerification DomainVerification DomainVerification
@ -19,11 +14,6 @@ type SystemDefaults struct {
KeyConfig KeyConfig KeyConfig KeyConfig
} }
type ZitadelDocs struct {
Issuer string
DiscoveryEndpoint string
}
type SecretGenerators struct { type SecretGenerators struct {
PasswordSaltCost int PasswordSaltCost int
MachineKeySize uint32 MachineKeySize uint32
@ -43,18 +33,9 @@ type DomainVerification struct {
} }
type Notifications struct { type Notifications struct {
Endpoints Endpoints
FileSystemPath string FileSystemPath string
} }
type Endpoints struct {
InitCode string
PasswordReset string
VerifyEmail string
DomainClaimed string
PasswordlessRegistration string
}
type KeyConfig struct { type KeyConfig struct {
Size int Size int
PrivateKeyLifetime time.Duration PrivateKeyLifetime time.Duration

View File

@ -41,15 +41,15 @@ type Message struct {
Text string Text string
} }
func NewTranslator(dir http.FileSystem, config TranslatorConfig) (*Translator, error) { func NewTranslator(dir http.FileSystem, defaultLanguage language.Tag, cookieName string) (*Translator, error) {
t := new(Translator) t := new(Translator)
var err error var err error
t.bundle, err = newBundle(dir, config.DefaultLanguage) t.bundle, err = newBundle(dir, defaultLanguage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
t.cookieHandler = http_util.NewCookieHandler() t.cookieHandler = http_util.NewCookieHandler()
t.cookieName = config.CookieName t.cookieName = cookieName
return t, nil return t, nil
} }

View File

@ -19,15 +19,12 @@ import (
"github.com/caos/zitadel/internal/notification/messages" "github.com/caos/zitadel/internal/notification/messages"
) )
func InitFSChannel(path string, config FSConfig) (channels.NotificationChannel, error) { func InitFSChannel(config FSConfig) (channels.NotificationChannel, error) {
if path == "" { if err := os.MkdirAll(config.Path, os.ModePerm); err != nil {
return nil, nil
}
if err := os.MkdirAll(path, os.ModePerm); err != nil {
return nil, err return nil, err
} }
logging.Log("NOTIF-kSvPp").Debug("successfully initialized filesystem email and sms channel") logging.Debug("successfully initialized filesystem email and sms channel")
return channels.HandleMessageFunc(func(message channels.Message) error { return channels.HandleMessageFunc(func(message channels.Message) error {
@ -48,6 +45,6 @@ func InitFSChannel(path string, config FSConfig) (channels.NotificationChannel,
return caos_errors.ThrowUnimplementedf(nil, "NOTIF-6f9a1", "filesystem provider doesn't support message type %T", message) return caos_errors.ThrowUnimplementedf(nil, "NOTIF-6f9a1", "filesystem provider doesn't support message type %T", message)
} }
return ioutil.WriteFile(filepath.Join(path, fileName), []byte(content), 0666) return ioutil.WriteFile(filepath.Join(config.Path, fileName), []byte(content), 0666)
}), nil }), nil
} }

View File

@ -3,4 +3,5 @@ package fs
type FSConfig struct { type FSConfig struct {
Enabled bool Enabled bool
Compact bool Compact bool
Path string
} }

View File

@ -9,7 +9,6 @@ import (
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing" "github.com/caos/zitadel/internal/notification/repository/eventsourcing"
_ "github.com/caos/zitadel/internal/notification/statik" _ "github.com/caos/zitadel/internal/notification/statik"
"github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/internal/query"
@ -20,7 +19,8 @@ type Config struct {
} }
func Start(config Config, func Start(config Config,
systemDefaults sd.SystemDefaults, externalPort uint16,
externalSecure bool,
command *command.Commands, command *command.Commands,
queries *query.Queries, queries *query.Queries,
dbClient *sql.DB, dbClient *sql.DB,
@ -32,6 +32,6 @@ func Start(config Config,
statikFS, err := fs.NewWithNamespace("notification") statikFS, err := fs.NewWithNamespace("notification")
logging.OnError(err).Panic("unable to start listener") logging.OnError(err).Panic("unable to start listener")
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command, queries, dbClient, assetsPrefix, userEncryption, smtpEncryption, smsEncryption) _, err = eventsourcing.Start(config.Repository, statikFS, externalPort, externalSecure, command, queries, dbClient, assetsPrefix, userEncryption, smtpEncryption, smsEncryption)
logging.OnError(err).Panic("unable to start app") logging.OnError(err).Panic("unable to start app")
} }

View File

@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
v1 "github.com/caos/zitadel/internal/eventstore/v1" v1 "github.com/caos/zitadel/internal/eventstore/v1"
queryv1 "github.com/caos/zitadel/internal/eventstore/v1/query" queryv1 "github.com/caos/zitadel/internal/eventstore/v1/query"
@ -39,7 +38,8 @@ func Register(configs Configs,
es v1.Eventstore, es v1.Eventstore,
command *command.Commands, command *command.Commands,
queries *query.Queries, queries *query.Queries,
systemDefaults sd.SystemDefaults, externalPort uint16,
externalSecure bool,
dir http.FileSystem, dir http.FileSystem,
assetsPrefix string, assetsPrefix string,
userEncryption crypto.EncryptionAlgorithm, userEncryption crypto.EncryptionAlgorithm,
@ -55,7 +55,8 @@ func Register(configs Configs,
handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount, es}, handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount, es},
command, command,
queries, queries,
systemDefaults, externalPort,
externalSecure,
dir, dir,
assetsPrefix, assetsPrefix,
userEncryption, userEncryption,

View File

@ -9,8 +9,8 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
http_utils "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
@ -40,7 +40,7 @@ const (
type Notification struct { type Notification struct {
handler handler
command *command.Commands command *command.Commands
systemDefaults sd.SystemDefaults fileSystemPath string
statikDir http.FileSystem statikDir http.FileSystem
subscription *v1.Subscription subscription *v1.Subscription
assetsPrefix string assetsPrefix string
@ -48,13 +48,16 @@ type Notification struct {
userDataCrypto crypto.EncryptionAlgorithm userDataCrypto crypto.EncryptionAlgorithm
smtpPasswordCrypto crypto.EncryptionAlgorithm smtpPasswordCrypto crypto.EncryptionAlgorithm
smsTokenCrypto crypto.EncryptionAlgorithm smsTokenCrypto crypto.EncryptionAlgorithm
externalPort uint16
externalSecure bool
} }
func newNotification( func newNotification(
handler handler, handler handler,
command *command.Commands, command *command.Commands,
query *query.Queries, query *query.Queries,
defaults sd.SystemDefaults, externalPort uint16,
externalSecure bool,
statikDir http.FileSystem, statikDir http.FileSystem,
assetsPrefix string, assetsPrefix string,
userEncryption crypto.EncryptionAlgorithm, userEncryption crypto.EncryptionAlgorithm,
@ -64,13 +67,14 @@ func newNotification(
h := &Notification{ h := &Notification{
handler: handler, handler: handler,
command: command, command: command,
systemDefaults: defaults,
statikDir: statikDir, statikDir: statikDir,
assetsPrefix: assetsPrefix, assetsPrefix: assetsPrefix,
queries: query, queries: query,
userDataCrypto: userEncryption, userDataCrypto: userEncryption,
smtpPasswordCrypto: smtpEncryption, smtpPasswordCrypto: smtpEncryption,
smsTokenCrypto: smsEncryption, smsTokenCrypto: smsEncryption,
externalSecure: externalSecure,
externalPort: externalPort,
} }
h.subscribe() h.subscribe()
@ -190,7 +194,11 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
return err return err
} }
err = types.SendUserInitCode(ctx, string(template.Template), translator, user, initCode, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix) origin, err := n.origin(ctx)
if err != nil {
return err
}
err = types.SendUserInitCode(ctx, string(template.Template), translator, user, initCode, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix, origin)
if err != nil { if err != nil {
return err return err
} }
@ -228,7 +236,12 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
if err != nil { if err != nil {
return err return err
} }
err = types.SendPasswordCode(ctx, string(template.Template), translator, user, pwCode, n.systemDefaults, n.getSMTPConfig, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix)
origin, err := n.origin(ctx)
if err != nil {
return err
}
err = types.SendPasswordCode(ctx, string(template.Template), translator, user, pwCode, n.getSMTPConfig, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix, origin)
if err != nil { if err != nil {
return err return err
} }
@ -267,7 +280,11 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
return err return err
} }
err = types.SendEmailVerificationCode(ctx, string(template.Template), translator, user, emailCode, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix) origin, err := n.origin(ctx)
if err != nil {
return err
}
err = types.SendEmailVerificationCode(ctx, string(template.Template), translator, user, emailCode, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix, origin)
if err != nil { if err != nil {
return err return err
} }
@ -294,7 +311,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
if err != nil { if err != nil {
return err return err
} }
err = types.SendPhoneVerificationCode(ctx, translator, user, phoneCode, n.systemDefaults, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto) err = types.SendPhoneVerificationCode(ctx, translator, user, phoneCode, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto)
if err != nil { if err != nil {
return err return err
} }
@ -334,7 +351,11 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
return err return err
} }
err = types.SendDomainClaimed(ctx, string(template.Template), translator, user, data["userName"], n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, colors, n.assetsPrefix) origin, err := n.origin(ctx)
if err != nil {
return err
}
err = types.SendDomainClaimed(ctx, string(template.Template), translator, user, data["userName"], n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, colors, n.assetsPrefix, origin)
if err != nil { if err != nil {
return err return err
} }
@ -381,7 +402,11 @@ func (n *Notification) handlePasswordlessRegistrationLink(event *models.Event) (
return err return err
} }
err = types.SendPasswordlessRegistrationLink(ctx, string(template.Template), translator, user, addedEvent, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix) origin, err := n.origin(ctx)
if err != nil {
return err
}
err = types.SendPasswordlessRegistrationLink(ctx, string(template.Template), translator, user, addedEvent, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix, origin)
if err != nil { if err != nil {
return err return err
} }
@ -492,6 +517,7 @@ func (n *Notification) getFileSystemProvider(ctx context.Context) (*fs.FSConfig,
} }
return &fs.FSConfig{ return &fs.FSConfig{
Compact: config.Compact, Compact: config.Compact,
Path: n.fileSystemPath,
}, nil }, nil
} }
@ -507,7 +533,7 @@ func (n *Notification) getLogProvider(ctx context.Context) (*log.LogConfig, erro
} }
func (n *Notification) getTranslatorWithOrgTexts(ctx context.Context, orgID, textType string) (*i18n.Translator, error) { func (n *Notification) getTranslatorWithOrgTexts(ctx context.Context, orgID, textType string) (*i18n.Translator, error) {
translator, err := i18n.NewTranslator(n.statikDir, i18n.TranslatorConfig{DefaultLanguage: n.queries.GetDefaultLanguage(ctx)}) translator, err := i18n.NewTranslator(n.statikDir, n.queries.GetDefaultLanguage(ctx), "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -535,3 +561,17 @@ func (n *Notification) getTranslatorWithOrgTexts(ctx context.Context, orgID, tex
func (n *Notification) getUserByID(userID, instanceID string) (*model.NotifyUser, error) { func (n *Notification) getUserByID(userID, instanceID string) (*model.NotifyUser, error) {
return n.view.NotifyUserByID(userID, instanceID) return n.view.NotifyUserByID(userID, instanceID)
} }
func (n *Notification) origin(ctx context.Context) (string, error) {
primary, err := query.NewInstanceDomainPrimarySearchQuery(true)
domains, err := n.queries.SearchInstanceDomains(ctx, &query.InstanceDomainSearchQueries{
Queries: []query.SearchQuery{primary},
})
if err != nil {
return "", err
}
if len(domains.Domains) < 1 {
return "", errors.ThrowInternal(nil, "NOTIF-Ef3r1", "Errors.Notification.NoDomain")
}
return http_utils.BuildHTTP(domains.Domains[0].Domain, n.externalPort, n.externalSecure), nil
}

View File

@ -5,7 +5,6 @@ import (
"net/http" "net/http"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
v1 "github.com/caos/zitadel/internal/eventstore/v1" v1 "github.com/caos/zitadel/internal/eventstore/v1"
es_spol "github.com/caos/zitadel/internal/eventstore/v1/spooler" es_spol "github.com/caos/zitadel/internal/eventstore/v1/spooler"
@ -24,7 +23,8 @@ type EsRepository struct {
func Start(conf Config, func Start(conf Config,
dir http.FileSystem, dir http.FileSystem,
systemDefaults sd.SystemDefaults, externalPort uint16,
externalSecure bool,
command *command.Commands, command *command.Commands,
queries *query.Queries, queries *query.Queries,
dbClient *sql.DB, dbClient *sql.DB,
@ -43,7 +43,7 @@ func Start(conf Config,
return nil, err return nil, err
} }
spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, command, queries, systemDefaults, dir, assetsPrefix, userEncryption, smtpEncryption, smsEncryption) spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, command, queries, externalPort, externalSecure, dir, assetsPrefix, userEncryption, smtpEncryption, smsEncryption)
return &EsRepository{ return &EsRepository{
spool, spool,

View File

@ -5,7 +5,6 @@ import (
"net/http" "net/http"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
v1 "github.com/caos/zitadel/internal/eventstore/v1" v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/spooler" "github.com/caos/zitadel/internal/eventstore/v1/spooler"
@ -27,7 +26,8 @@ func StartSpooler(c SpoolerConfig,
sql *sql.DB, sql *sql.DB,
command *command.Commands, command *command.Commands,
queries *query.Queries, queries *query.Queries,
systemDefaults sd.SystemDefaults, externalPort uint16,
externalSecure bool,
dir http.FileSystem, dir http.FileSystem,
assetsPrefix string, assetsPrefix string,
userEncryption crypto.EncryptionAlgorithm, userEncryption crypto.EncryptionAlgorithm,
@ -38,7 +38,7 @@ func StartSpooler(c SpoolerConfig,
Eventstore: es, Eventstore: es,
Locker: &locker{dbClient: sql}, Locker: &locker{dbClient: sql},
ConcurrentWorkers: c.ConcurrentWorkers, ConcurrentWorkers: c.ConcurrentWorkers,
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, queries, systemDefaults, dir, assetsPrefix, userEncryption, smtpEncryption, smsEncryption), ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, queries, externalPort, externalSecure, dir, assetsPrefix, userEncryption, smtpEncryption, smsEncryption),
} }
spool := spoolerConfig.New() spool := spoolerConfig.New()
spool.Start() spool.Start()

View File

@ -3,19 +3,18 @@ package senders
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels" "github.com/caos/zitadel/internal/notification/channels"
"github.com/caos/zitadel/internal/notification/channels/fs" "github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log" "github.com/caos/zitadel/internal/notification/channels/log"
) )
func debugChannels(ctx context.Context, config systemdefaults.Notifications, getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (*Chain, error) { func debugChannels(ctx context.Context, getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (*Chain, error) {
var ( var (
providers []channels.NotificationChannel providers []channels.NotificationChannel
) )
if fsProvider, err := getFileSystemProvider(ctx); err == nil { if fsProvider, err := getFileSystemProvider(ctx); err == nil {
p, err := fs.InitFSChannel(config.FileSystemPath, *fsProvider) p, err := fs.InitFSChannel(*fsProvider)
if err == nil { if err == nil {
providers = append(providers, p) providers = append(providers, p)
} }

View File

@ -4,18 +4,18 @@ import (
"context" "context"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels/fs" "github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log" "github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/smtp" "github.com/caos/zitadel/internal/notification/channels/smtp"
) )
func EmailChannels(ctx context.Context, config systemdefaults.Notifications, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (chain *Chain, err error) { func EmailChannels(ctx context.Context, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (chain *Chain, err error) {
p, err := smtp.InitSMTPChannel(ctx, emailConfig) p, err := smtp.InitSMTPChannel(ctx, emailConfig)
if err == nil { if err == nil {
chain.channels = append(chain.channels, p) chain.channels = append(chain.channels, p)
} }
chain, err = debugChannels(ctx, config, getFileSystemProvider, getLogProvider) chain, err = debugChannels(ctx, getFileSystemProvider, getLogProvider)
if err != nil { if err != nil {
logging.New().Info("Error in creating debug channels") logging.New().Info("Error in creating debug channels")
} }

View File

@ -3,17 +3,16 @@ package senders
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels/fs" "github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log" "github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/twilio" "github.com/caos/zitadel/internal/notification/channels/twilio"
) )
func SMSChannels(ctx context.Context, config systemdefaults.Notifications, twilioConfig *twilio.TwilioConfig, getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (chain *Chain, err error) { func SMSChannels(ctx context.Context, twilioConfig *twilio.TwilioConfig, getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (chain *Chain, err error) {
if twilioConfig != nil { if twilioConfig != nil {
chain.channels = append(chain.channels, twilio.InitTwilioChannel(*twilioConfig)) chain.channels = append(chain.channels, twilio.InitTwilioChannel(*twilioConfig))
} }
chain, err = debugChannels(ctx, config, getFileSystemProvider, getLogProvider) chain, err = debugChannels(ctx, getFileSystemProvider, getLogProvider)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"strings" "strings"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/api/ui/login"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/channels/fs" "github.com/caos/zitadel/internal/notification/channels/fs"
@ -20,11 +20,8 @@ type DomainClaimedData struct {
URL string URL string
} }
func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, username string, systemDefaults systemdefaults.SystemDefaults, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), colors *query.LabelPolicy, assetsPrefix string) error { func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, username string, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), colors *query.LabelPolicy, assetsPrefix string, origin string) error {
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.DomainClaimed, &UrlData{UserID: user.ID}) url := login.LoginLink(origin)
if err != nil {
return err
}
var args = mapNotifyUserToArgs(user) var args = mapNotifyUserToArgs(user)
args["TempUsername"] = username args["TempUsername"] = username
args["Domain"] = strings.Split(user.LastEmail, "@")[1] args["Domain"] = strings.Split(user.LastEmail, "@")[1]
@ -37,5 +34,5 @@ func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Tr
if err != nil { if err != nil {
return err return err
} }
return generateEmail(ctx, user, domainClaimedData.Subject, template, systemDefaults.Notifications, emailConfig, getFileSystemProvider, getLogProvider, true) return generateEmail(ctx, user, domainClaimedData.Subject, template, emailConfig, getFileSystemProvider, getLogProvider, true)
} }

View File

@ -3,7 +3,7 @@ package types
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/api/ui/login"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/i18n"
@ -21,16 +21,12 @@ type EmailVerificationCodeData struct {
URL string URL string
} }
func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error { func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string, origin string) error {
codeString, err := crypto.DecryptString(code.Code, alg) codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil { if err != nil {
return err return err
} }
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.VerifyEmail, &UrlData{UserID: user.ID, Code: codeString}) url := login.MailVerificationLink(origin, user.ID, codeString)
if err != nil {
return err
}
var args = mapNotifyUserToArgs(user) var args = mapNotifyUserToArgs(user)
args["Code"] = codeString args["Code"] = codeString
@ -43,5 +39,5 @@ func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator
if err != nil { if err != nil {
return err return err
} }
return generateEmail(ctx, user, emailCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, getFileSystemProvider, getLogProvider, true) return generateEmail(ctx, user, emailCodeData.Subject, template, smtpConfig, getFileSystemProvider, getLogProvider, true)
} }

View File

@ -3,7 +3,7 @@ package types
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/api/ui/login"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/i18n"
@ -27,15 +27,12 @@ type UrlData struct {
PasswordSet bool PasswordSet bool
} }
func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error { func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix, origin string) error {
codeString, err := crypto.DecryptString(code.Code, alg) codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil { if err != nil {
return err return err
} }
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.InitCode, &UrlData{UserID: user.ID, Code: codeString, PasswordSet: user.PasswordSet}) url := login.InitUserLink(origin, user.ID, codeString, user.PasswordSet)
if err != nil {
return err
}
var args = mapNotifyUserToArgs(user) var args = mapNotifyUserToArgs(user)
args["Code"] = codeString args["Code"] = codeString
@ -47,5 +44,5 @@ func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Tra
if err != nil { if err != nil {
return err return err
} }
return generateEmail(ctx, user, initCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, getFileSystemProvider, getLogProvider, true) return generateEmail(ctx, user, initCodeData.Subject, template, smtpConfig, getFileSystemProvider, getLogProvider, true)
} }

View File

@ -3,7 +3,7 @@ package types
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/api/ui/login"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/i18n"
@ -24,15 +24,12 @@ type PasswordCodeData struct {
URL string URL string
} }
func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getTwilioConfig func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error { func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getTwilioConfig func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string, origin string) error {
codeString, err := crypto.DecryptString(code.Code, alg) codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil { if err != nil {
return err return err
} }
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.PasswordReset, &UrlData{UserID: user.ID, Code: codeString}) url := login.InitPasswordLink(origin, user.ID, codeString)
if err != nil {
return err
}
var args = mapNotifyUserToArgs(user) var args = mapNotifyUserToArgs(user)
args["Code"] = codeString args["Code"] = codeString
@ -47,8 +44,8 @@ func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Tra
return err return err
} }
if code.NotificationType == int32(domain.NotificationTypeSms) { if code.NotificationType == int32(domain.NotificationTypeSms) {
return generateSms(ctx, user, passwordResetData.Text, systemDefaults.Notifications, getTwilioConfig, getFileSystemProvider, getLogProvider, false) return generateSms(ctx, user, passwordResetData.Text, getTwilioConfig, getFileSystemProvider, getLogProvider, false)
} }
return generateEmail(ctx, user, passwordResetData.Subject, template, systemDefaults.Notifications, smtpConfig, getFileSystemProvider, getLogProvider, true) return generateEmail(ctx, user, passwordResetData.Subject, template, smtpConfig, getFileSystemProvider, getLogProvider, true)
} }

View File

@ -3,7 +3,7 @@ package types
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/api/ui/login"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/i18n"
@ -21,12 +21,12 @@ type PasswordlessRegistrationLinkData struct {
URL string URL string
} }
func SendPasswordlessRegistrationLink(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *user.HumanPasswordlessInitCodeRequestedEvent, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error { func SendPasswordlessRegistrationLink(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *user.HumanPasswordlessInitCodeRequestedEvent, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string, origin string) error {
codeString, err := crypto.DecryptString(code.Code, alg) codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil { if err != nil {
return err return err
} }
url := domain.PasswordlessInitCodeLink(systemDefaults.Notifications.Endpoints.PasswordlessRegistration, user.ID, user.ResourceOwner, code.ID, codeString) url := domain.PasswordlessInitCodeLink(origin+login.HandlerPrefix+login.EndpointPasswordlessRegistration, user.ID, user.ResourceOwner, code.ID, codeString)
var args = mapNotifyUserToArgs(user) var args = mapNotifyUserToArgs(user)
emailCodeData := &PasswordlessRegistrationLinkData{ emailCodeData := &PasswordlessRegistrationLinkData{
@ -38,5 +38,5 @@ func SendPasswordlessRegistrationLink(ctx context.Context, mailhtml string, tran
if err != nil { if err != nil {
return err return err
} }
return generateEmail(ctx, user, emailCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, getFileSystemProvider, getLogProvider, true) return generateEmail(ctx, user, emailCodeData.Subject, template, smtpConfig, getFileSystemProvider, getLogProvider, true)
} }

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/i18n"
@ -20,7 +19,7 @@ type PhoneVerificationCodeData struct {
UserID string UserID string
} }
func SendPhoneVerificationCode(ctx context.Context, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PhoneCode, systemDefaults systemdefaults.SystemDefaults, getTwilioConfig func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm) error { func SendPhoneVerificationCode(ctx context.Context, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PhoneCode, getTwilioConfig func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm) error {
codeString, err := crypto.DecryptString(code.Code, alg) codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil { if err != nil {
return err return err
@ -35,5 +34,5 @@ func SendPhoneVerificationCode(ctx context.Context, translator *i18n.Translator,
if err != nil { if err != nil {
return err return err
} }
return generateSms(ctx, user, template, systemDefaults.Notifications, getTwilioConfig, getFileSystemProvider, getLogProvider, true) return generateSms(ctx, user, template, getTwilioConfig, getFileSystemProvider, getLogProvider, true)
} }

View File

@ -11,11 +11,10 @@ import (
"github.com/caos/zitadel/internal/notification/messages" "github.com/caos/zitadel/internal/notification/messages"
"github.com/caos/zitadel/internal/notification/senders" "github.com/caos/zitadel/internal/notification/senders"
"github.com/caos/zitadel/internal/config/systemdefaults"
view_model "github.com/caos/zitadel/internal/user/repository/view/model" view_model "github.com/caos/zitadel/internal/user/repository/view/model"
) )
func generateEmail(ctx context.Context, user *view_model.NotifyUser, subject, content string, config systemdefaults.Notifications, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), lastEmail bool) error { func generateEmail(ctx context.Context, user *view_model.NotifyUser, subject, content string, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), lastEmail bool) error {
content = html.UnescapeString(content) content = html.UnescapeString(content)
message := &messages.Email{ message := &messages.Email{
Recipients: []string{user.VerifiedEmail}, Recipients: []string{user.VerifiedEmail},
@ -26,7 +25,7 @@ func generateEmail(ctx context.Context, user *view_model.NotifyUser, subject, co
message.Recipients = []string{user.LastEmail} message.Recipients = []string{user.LastEmail}
} }
channelChain, err := senders.EmailChannels(ctx, config, smtpConfig, getFileSystemProvider, getLogProvider) channelChain, err := senders.EmailChannels(ctx, smtpConfig, getFileSystemProvider, getLogProvider)
if err != nil { if err != nil {
return err return err
} }

View File

@ -3,7 +3,6 @@ package types
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/config/systemdefaults"
caos_errors "github.com/caos/zitadel/internal/errors" caos_errors "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/notification/channels/fs" "github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log" "github.com/caos/zitadel/internal/notification/channels/log"
@ -13,7 +12,7 @@ import (
view_model "github.com/caos/zitadel/internal/user/repository/view/model" view_model "github.com/caos/zitadel/internal/user/repository/view/model"
) )
func generateSms(ctx context.Context, user *view_model.NotifyUser, content string, config systemdefaults.Notifications, getTwilioProvider func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), lastPhone bool) error { func generateSms(ctx context.Context, user *view_model.NotifyUser, content string, getTwilioProvider func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), lastPhone bool) error {
number := "" number := ""
twilio, err := getTwilioProvider(ctx) twilio, err := getTwilioProvider(ctx)
if err == nil { if err == nil {
@ -28,7 +27,7 @@ func generateSms(ctx context.Context, user *view_model.NotifyUser, content strin
message.RecipientPhoneNumber = user.LastPhone message.RecipientPhoneNumber = user.LastPhone
} }
channelChain, err := senders.SMSChannels(ctx, config, twilio, getFileSystemProvider, getLogProvider) channelChain, err := senders.SMSChannels(ctx, twilio, getFileSystemProvider, getLogProvider)
if channelChain.Len() == 0 { if channelChain.Len() == 0 {
return caos_errors.ThrowPreconditionFailed(nil, "PHONE-w8nfow", "Errors.Notification.Channels.NotPresent") return caos_errors.ThrowPreconditionFailed(nil, "PHONE-w8nfow", "Errors.Notification.Channels.NotPresent")

View File

@ -131,7 +131,7 @@ func (q *Queries) CustomTextListByTemplate(ctx context.Context, aggregateID, tem
} }
func (q *Queries) GetDefaultLoginTexts(ctx context.Context, lang string) (*domain.CustomLoginText, error) { func (q *Queries) GetDefaultLoginTexts(ctx context.Context, lang string) (*domain.CustomLoginText, error) {
contents, err := q.readLoginTranslationFile(lang) contents, err := q.readLoginTranslationFile(ctx, lang)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -153,7 +153,7 @@ func (q *Queries) GetCustomLoginTexts(ctx context.Context, aggregateID, lang str
} }
func (q *Queries) IAMLoginTexts(ctx context.Context, lang string) (*domain.CustomLoginText, error) { func (q *Queries) IAMLoginTexts(ctx context.Context, lang string) (*domain.CustomLoginText, error) {
contents, err := q.readLoginTranslationFile(lang) contents, err := q.readLoginTranslationFile(ctx, lang)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -186,7 +186,7 @@ func (q *Queries) IAMLoginTexts(ctx context.Context, lang string) (*domain.Custo
return loginText, nil return loginText, nil
} }
func (q *Queries) readLoginTranslationFile(lang string) ([]byte, error) { func (q *Queries) readLoginTranslationFile(ctx context.Context, lang string) ([]byte, error) {
q.mutex.Lock() q.mutex.Lock()
defer q.mutex.Unlock() defer q.mutex.Unlock()
contents, ok := q.LoginTranslationFileContents[lang] contents, ok := q.LoginTranslationFileContents[lang]
@ -194,7 +194,7 @@ func (q *Queries) readLoginTranslationFile(lang string) ([]byte, error) {
if !ok { if !ok {
contents, err = q.readTranslationFile(q.LoginDir, fmt.Sprintf("/i18n/%s.yaml", lang)) contents, err = q.readTranslationFile(q.LoginDir, fmt.Sprintf("/i18n/%s.yaml", lang))
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
contents, err = q.readTranslationFile(q.LoginDir, fmt.Sprintf("/i18n/%s.yaml", q.GetDefaultLanguage(context.Background()).String())) contents, err = q.readTranslationFile(q.LoginDir, fmt.Sprintf("/i18n/%s.yaml", authz.GetInstance(ctx).DefaultLanguage().String()))
} }
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -76,14 +76,14 @@ type Instance struct {
CreationDate time.Time CreationDate time.Time
Sequence uint64 Sequence uint64
GlobalOrgID string GlobalOrgID string
IAMProjectID string IAMProjectID string
ConsoleID string ConsoleID string
ConsoleAppID string ConsoleAppID string
DefaultLanguage language.Tag DefaultLang language.Tag
SetupStarted domain.Step SetupStarted domain.Step
SetupDone domain.Step SetupDone domain.Step
Host string host string
} }
type Instances struct { type Instances struct {
@ -108,7 +108,15 @@ func (i *Instance) ConsoleApplicationID() string {
} }
func (i *Instance) RequestedDomain() string { func (i *Instance) RequestedDomain() string {
return i.Host return strings.Split(i.host, ":")[0]
}
func (i *Instance) RequestedHost() string {
return i.host
}
func (i *Instance) DefaultLanguage() language.Tag {
return i.DefaultLang
} }
type InstanceSearchQueries struct { type InstanceSearchQueries struct {
@ -165,8 +173,9 @@ func (q *Queries) Instance(ctx context.Context) (*Instance, error) {
func (q *Queries) InstanceByHost(ctx context.Context, host string) (authz.Instance, error) { func (q *Queries) InstanceByHost(ctx context.Context, host string) (authz.Instance, error) {
stmt, scan := prepareInstanceDomainQuery(host) stmt, scan := prepareInstanceDomainQuery(host)
host = strings.Split(host, ":")[0] //remove possible port
query, args, err := stmt.Where(sq.Eq{ query, args, err := stmt.Where(sq.Eq{
InstanceDomainDomainCol.identifier(): strings.Split(host, ":")[0], InstanceDomainDomainCol.identifier(): host,
}).ToSql() }).ToSql()
if err != nil { if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-SAfg2", "Errors.Query.SQLStatement") return nil, errors.ThrowInternal(err, "QUERY-SAfg2", "Errors.Query.SQLStatement")
@ -177,11 +186,11 @@ func (q *Queries) InstanceByHost(ctx context.Context, host string) (authz.Instan
} }
func (q *Queries) GetDefaultLanguage(ctx context.Context) language.Tag { func (q *Queries) GetDefaultLanguage(ctx context.Context) language.Tag {
iam, err := q.Instance(ctx) instance, err := q.Instance(ctx)
if err != nil { if err != nil {
return language.Und return language.Und
} }
return iam.DefaultLanguage return instance.DefaultLanguage()
} }
func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Instance, error)) { func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Instance, error)) {
@ -200,7 +209,7 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta
). ).
From(instanceTable.identifier()).PlaceholderFormat(sq.Dollar), From(instanceTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*Instance, error) { func(row *sql.Row) (*Instance, error) {
instance := &Instance{Host: host} instance := &Instance{host: host}
lang := "" lang := ""
err := row.Scan( err := row.Scan(
&instance.ID, &instance.ID,
@ -221,7 +230,7 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta
} }
return nil, errors.ThrowInternal(err, "QUERY-d9nw", "Errors.Internal") return nil, errors.ThrowInternal(err, "QUERY-d9nw", "Errors.Internal")
} }
instance.DefaultLanguage = language.Make(lang) instance.DefaultLang = language.Make(lang)
return instance, nil return instance, nil
} }
} }
@ -299,7 +308,7 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Row) (
LeftJoin(join(InstanceDomainInstanceIDCol, InstanceColumnID)). LeftJoin(join(InstanceDomainInstanceIDCol, InstanceColumnID)).
PlaceholderFormat(sq.Dollar), PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*Instance, error) { func(row *sql.Row) (*Instance, error) {
instance := &Instance{Host: host} instance := &Instance{host: host}
lang := "" lang := ""
err := row.Scan( err := row.Scan(
&instance.ID, &instance.ID,
@ -320,7 +329,7 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Row) (
} }
return nil, errors.ThrowInternal(err, "QUERY-d9nw", "Errors.Internal") return nil, errors.ThrowInternal(err, "QUERY-d9nw", "Errors.Internal")
} }
instance.DefaultLanguage = language.Make(lang) instance.DefaultLang = language.Make(lang)
return instance, nil return instance, nil
} }
} }

View File

@ -8,9 +8,9 @@ import (
"regexp" "regexp"
"testing" "testing"
sq "github.com/Masterminds/squirrel"
"golang.org/x/text/language" "golang.org/x/text/language"
sq "github.com/Masterminds/squirrel"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
errs "github.com/caos/zitadel/internal/errors" errs "github.com/caos/zitadel/internal/errors"
) )
@ -105,17 +105,17 @@ func Test_InstancePrepares(t *testing.T) {
), ),
}, },
object: &Instance{ object: &Instance{
ID: "id", ID: "id",
CreationDate: testNow, CreationDate: testNow,
ChangeDate: testNow, ChangeDate: testNow,
Sequence: 20211108, Sequence: 20211108,
GlobalOrgID: "global-org-id", GlobalOrgID: "global-org-id",
IAMProjectID: "project-id", IAMProjectID: "project-id",
ConsoleID: "client-id", ConsoleID: "client-id",
ConsoleAppID: "app-id", ConsoleAppID: "app-id",
SetupStarted: domain.Step2, SetupStarted: domain.Step2,
SetupDone: domain.Step1, SetupDone: domain.Step1,
DefaultLanguage: language.English, DefaultLang: language.English,
}, },
}, },
{ {

View File

@ -158,8 +158,8 @@ func (q *Queries) DefaultMessageText(ctx context.Context) (*MessageText, error)
return scan(row) return scan(row)
} }
func (q *Queries) DefaultMessageTextByTypeAndLanguageFromFileSystem(messageType, language string) (*MessageText, error) { func (q *Queries) DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx context.Context, messageType, language string) (*MessageText, error) {
contents, err := q.readNotificationTextMessages(language) contents, err := q.readNotificationTextMessages(ctx, language)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -195,7 +195,7 @@ func (q *Queries) CustomMessageTextByTypeAndLanguage(ctx context.Context, aggreg
} }
func (q *Queries) IAMMessageTextByTypeAndLanguage(ctx context.Context, messageType, language string) (*MessageText, error) { func (q *Queries) IAMMessageTextByTypeAndLanguage(ctx context.Context, messageType, language string) (*MessageText, error) {
contents, err := q.readNotificationTextMessages(language) contents, err := q.readNotificationTextMessages(ctx, language)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -229,7 +229,7 @@ func (q *Queries) IAMMessageTextByTypeAndLanguage(ctx context.Context, messageTy
return result, nil return result, nil
} }
func (q *Queries) readNotificationTextMessages(language string) ([]byte, error) { func (q *Queries) readNotificationTextMessages(ctx context.Context, language string) ([]byte, error) {
q.mutex.Lock() q.mutex.Lock()
defer q.mutex.Unlock() defer q.mutex.Unlock()
var err error var err error
@ -237,7 +237,7 @@ func (q *Queries) readNotificationTextMessages(language string) ([]byte, error)
if !ok { if !ok {
contents, err = q.readTranslationFile(q.NotificationDir, fmt.Sprintf("/i18n/%s.yaml", language)) contents, err = q.readTranslationFile(q.NotificationDir, fmt.Sprintf("/i18n/%s.yaml", language))
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
contents, err = q.readTranslationFile(q.NotificationDir, fmt.Sprintf("/i18n/%s.yaml", q.GetDefaultLanguage(context.Background()).String())) contents, err = q.readTranslationFile(q.NotificationDir, fmt.Sprintf("/i18n/%s.yaml", authz.GetInstance(ctx).DefaultLanguage().String()))
} }
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -1,17 +1,18 @@
package renderer package renderer
import ( import (
"context"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"text/template" "text/template"
"github.com/caos/logging"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/i18n"
"github.com/caos/logging"
) )
const ( const (
@ -21,16 +22,16 @@ const (
) )
type Renderer struct { type Renderer struct {
Templates map[string]*template.Template Templates map[string]*template.Template
dir http.FileSystem dir http.FileSystem
translatorConfig i18n.TranslatorConfig cookieName string
} }
func NewRenderer(dir http.FileSystem, tmplMapping map[string]string, funcs map[string]interface{}, translatorConfig i18n.TranslatorConfig) (*Renderer, error) { func NewRenderer(dir http.FileSystem, tmplMapping map[string]string, funcs map[string]interface{}, cookieName string) (*Renderer, error) {
var err error var err error
r := &Renderer{ r := &Renderer{
dir: dir, dir: dir,
translatorConfig: translatorConfig, cookieName: cookieName,
} }
err = r.loadTemplates(dir, nil, tmplMapping, funcs) err = r.loadTemplates(dir, nil, tmplMapping, funcs)
if err != nil { if err != nil {
@ -46,8 +47,8 @@ func (r *Renderer) RenderTemplate(w http.ResponseWriter, req *http.Request, tran
} }
} }
func (r *Renderer) NewTranslator() (*i18n.Translator, error) { func (r *Renderer) NewTranslator(ctx context.Context) (*i18n.Translator, error) {
return i18n.NewTranslator(r.dir, r.translatorConfig) return i18n.NewTranslator(r.dir, authz.GetInstance(ctx).DefaultLanguage(), r.cookieName)
} }
func (r *Renderer) Localize(translator *i18n.Translator, id string, args map[string]interface{}) string { func (r *Renderer) Localize(translator *i18n.Translator, id string, args map[string]interface{}) string {

View File

@ -44,6 +44,8 @@ Errors:
SMTPConfig: SMTPConfig:
NotFound: SMTP Konfiguration nicht gefunden NotFound: SMTP Konfiguration nicht gefunden
AlreadyExists: SMTP Konfiguration existiert bereits AlreadyExists: SMTP Konfiguration existiert bereits
Notification:
NoDomain: Keine Domäne für Nachricht gefunden
User: User:
NotFound: Benutzer konnte nicht gefunden werden NotFound: Benutzer konnte nicht gefunden werden
AlreadyExists: Benutzer existierts bereits AlreadyExists: Benutzer existierts bereits

View File

@ -44,6 +44,8 @@ Errors:
SMTPConfig: SMTPConfig:
NotFound: SMTP configuration not found NotFound: SMTP configuration not found
AlreadyExists: SMTP configuration already exists AlreadyExists: SMTP configuration already exists
Notification:
NoDomain: No Domain found for message
User: User:
NotFound: User could not be found NotFound: User could not be found
AlreadyExists: User already exists AlreadyExists: User already exists

View File

@ -44,6 +44,8 @@ Errors:
SMTPConfig: SMTPConfig:
NotFound: Configurazione SMTP non trovata NotFound: Configurazione SMTP non trovata
AlreadyExists: La configurazione SMTP esiste già AlreadyExists: La configurazione SMTP esiste già
Notification:
NoDomain: Nessun dominio trovato per il messaggio
User: User:
NotFound: L'utente non è stato trovato NotFound: L'utente non è stato trovato
AlreadyExists: L'utente già esistente AlreadyExists: L'utente già esistente