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
defaults systemdefaults.SystemDefaults
zitadelRoles []authz.RoleMapping
baseURL string
externalSecure bool
externalPort uint16
}
func (mig *DefaultInstance) Execute(ctx context.Context) error {
@ -47,6 +47,8 @@ func (mig *DefaultInstance) Execute(ctx context.Context) error {
nil,
nil,
nil,
mig.externalSecure,
mig.externalPort,
nil,
nil,
nil,
@ -60,7 +62,7 @@ func (mig *DefaultInstance) Execute(ctx context.Context) error {
}
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
}

View File

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

View File

@ -10,7 +10,6 @@ import (
"github.com/spf13/viper"
"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/eventstore"
"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)
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.db = dbClient
steps.S3DefaultInstance.defaults = config.SystemDefaults
steps.S3DefaultInstance.masterKey = masterKey
steps.S3DefaultInstance.domain = config.ExternalDomain
steps.S3DefaultInstance.domain = instanceSetup.CustomDomain
steps.S3DefaultInstance.zitadelRoles = config.InternalAuthZ.RolePermissionMappings
steps.S3DefaultInstance.userEncryptionKey = config.EncryptionKeys.User
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()
err = migration.Migrate(ctx, eventstoreClient, steps.s1ProjectionTable)

View File

@ -28,7 +28,6 @@ type Config struct {
Log *logging.Config
Port uint16
ExternalPort uint16
ExternalDomain string
ExternalSecure bool
HTTP2HostHeader 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/management"
"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/oidc"
"github.com/caos/zitadel/internal/api/ui/console"
@ -112,12 +111,28 @@ func startZitadel(config *Config, masterKey string) error {
DisplayName: config.WebAuthNName,
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 {
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()
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 {
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
}
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
}
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
}
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
}
@ -179,14 +194,13 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
}
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)
if err != nil {
return fmt.Errorf("unable to start console: %w", err)
}
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 {
return fmt.Errorf("unable to start login: %w", err)
}

View File

@ -5,7 +5,6 @@ Log:
Port: 8080
ExternalPort: 8080
ExternalDomain: localhost
ExternalSecure: true
HTTP2HostHeader: ":authority"
HTTP1HostHeader: "host"
@ -97,7 +96,6 @@ Login:
SharedMaxAge: 168h #7d
Console:
ConsoleOverwriteDir: ""
ShortCache:
MaxAge: 5m
SharedMaxAge: 15m
@ -138,25 +136,8 @@ EncryptionKeys:
CSRFCookieKeyID: "csrfCookieKey"
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
SystemDefaults:
# DefaultLanguage: 'en'
Domain: $ZITADEL_DEFAULT_DOMAIN
ZitadelDocs:
Issuer: $ZITADEL_ISSUER
DiscoveryEndpoint: '$ZITADEL_ISSUER/.well-known/openid-configuration'
SecretGenerators:
PasswordSaltCost: 14
MachineKeySize: 2048
@ -172,19 +153,11 @@ SystemDefaults:
IncludeDigits: true
IncludeSymbols: false
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/'
KeyConfig:
Size: 2048
PrivateKeyLifetime: 6h
PublicKeyLifetime: 30h
SigningKeyRotationCheck: 10s
SigningKeyGracefulPeriod: 10m
DefaultInstance:
InstanceName:

View File

@ -2,6 +2,8 @@ package authz
import (
"context"
"golang.org/x/text/language"
)
var (
@ -14,6 +16,8 @@ type Instance interface {
ConsoleClientID() string
ConsoleApplicationID() string
RequestedDomain() string
RequestedHost() string
DefaultLanguage() language.Tag
}
type InstanceVerifier interface {
@ -21,8 +25,11 @@ type InstanceVerifier interface {
}
type instance struct {
ID string
Domain string
ID string
Domain string
projectID string
appID string
clientID string
}
func (i *instance) InstanceID() string {
@ -30,21 +37,29 @@ func (i *instance) InstanceID() string {
}
func (i *instance) ProjectID() string {
return ""
return i.projectID
}
func (i *instance) ConsoleClientID() string {
return ""
return i.clientID
}
func (i *instance) ConsoleApplicationID() string {
return ""
return i.appID
}
func (i *instance) RequestedDomain() string {
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 {
instance, ok := ctx.Value(instanceKey).(Instance)
if !ok {
@ -70,3 +85,15 @@ func WithRequestedDomain(ctx context.Context, domain string) context.Context {
i.Domain = domain
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"
"github.com/stretchr/testify/assert"
"golang.org/x/text/language"
)
func Test_Instance(t *testing.T) {
@ -83,6 +84,14 @@ func (m *mockInstance) ConsoleApplicationID() string {
return "appID"
}
func (m *mockInstance) DefaultLanguage() language.Tag {
return language.English
}
func (m *mockInstance) RequestedDomain() string {
return "zitadel.cloud"
}
func (m *mockInstance) RequestedHost() string {
return "zitadel.cloud:443"
}

View File

@ -3,9 +3,9 @@ package admin
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object"
text_grpc "github.com/caos/zitadel/internal/api/grpc/text"
"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) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.InitCodeMessageType, req.Language)
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.InitCodeMessageType, req.Language)
if err != nil {
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) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.PasswordResetMessageType, req.Language)
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordResetMessageType, req.Language)
if err != nil {
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) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.VerifyEmailMessageType, req.Language)
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.VerifyEmailMessageType, req.Language)
if err != nil {
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) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.VerifyPhoneMessageType, req.Language)
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.VerifyPhoneMessageType, req.Language)
if err != nil {
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) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.DomainClaimedMessageType, req.Language)
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.DomainClaimedMessageType, req.Language)
if err != nil {
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) {
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(domain.PasswordlessRegistrationMessageType, req.Language)
msg, err := s.query.DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx, domain.PasswordlessRegistrationMessageType, req.Language)
if err != nil {
return nil, err
}

View File

@ -5,6 +5,7 @@ import (
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/text"
caos_errors "github.com/caos/zitadel/internal/errors"
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
}
func (s *Server) GetDefaultLanguage(ctx context.Context, req *admin_pb.GetDefaultLanguageRequest) (*admin_pb.GetDefaultLanguageResponse, error) {
return &admin_pb.GetDefaultLanguageResponse{Language: s.query.GetDefaultLanguage(ctx).String()}, nil
func (s *Server) GetDefaultLanguage(ctx context.Context, _ *admin_pb.GetDefaultLanguageRequest) (*admin_pb.GetDefaultLanguageResponse, error) {
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"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object"
org_grpc "github.com/caos/zitadel/internal/api/grpc/org"
"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) {
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 {
return nil, err
}

View File

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

View File

@ -8,6 +8,8 @@ import (
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object"
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/query"
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 {
return nil, err
}
origin := http.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), s.externalSecure)
return &auth_pb.AddMyPasswordlessLinkResponse{
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),
}, nil
}

View File

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

View File

@ -3,6 +3,10 @@ package management
import (
"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"
)
@ -10,9 +14,10 @@ func (s *Server) Healthz(context.Context, *mgmt_pb.HealthzRequest) (*mgmt_pb.Hea
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{
Issuer: s.systemDefaults.ZitadelDocs.Issuer,
DiscoveryEndpoint: s.systemDefaults.ZitadelDocs.DiscoveryEndpoint,
Issuer: issuer,
DiscoveryEndpoint: issuer + oidc.DiscoveryEndpoint,
}, 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) {
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 {
return nil, err
}

View File

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

View File

@ -16,7 +16,9 @@ import (
"github.com/caos/zitadel/internal/api/grpc/metadata"
obj_grpc "github.com/caos/zitadel/internal/api/grpc/object"
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"
"github.com/caos/zitadel/internal/api/ui/login"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/query"
@ -259,8 +261,9 @@ func (s *Server) ImportHumanUser(ctx context.Context, req *mgmt_pb.ImportHumanUs
),
}
if code != nil {
origin := http.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), s.externalSecure)
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),
Expiration: durationpb.New(code.Expiration),
}
@ -654,9 +657,10 @@ func (s *Server) AddPasswordlessRegistration(ctx context.Context, req *mgmt_pb.A
if err != nil {
return nil, err
}
origin := http.BuildOrigin(authz.GetInstance(ctx).RequestedHost(), s.externalSecure)
return &mgmt_pb.AddPasswordlessRegistrationResponse{
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),
}, nil
}

View File

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

View File

@ -3,17 +3,17 @@ package middleware
import (
"context"
"github.com/caos/zitadel/internal/query"
"google.golang.org/grpc"
"github.com/caos/zitadel/internal/api/authz"
_ "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) {
translator := newZitadelTranslator(query.GetDefaultLanguage(context.Background()))
func TranslationHandler() 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)
translator := newZitadelTranslator(authz.GetInstance(ctx).DefaultLanguage())
if loc, ok := resp.(localizers); ok && resp != nil {
translateFields(ctx, loc, translator)
}

View File

@ -48,7 +48,7 @@ func translatorFromNamespace(namespace string, defaultLanguage language.Tag) *i1
dir, err := fs.NewWithNamespace(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")
return translator

View File

@ -1,15 +1,14 @@
package server
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"
"google.golang.org/grpc"
"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/query"
"github.com/caos/zitadel/internal/telemetry/metrics"
)
type Server interface {
@ -33,7 +32,7 @@ func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, querie
//TODO: Handle Ignored Services
middleware.InstanceInterceptor(queries, hostHeaderName, "/zitadel.system.v1.SystemService"),
middleware.AuthorizationInterceptor(verifier, authConfig),
middleware.TranslationHandler(queries),
middleware.TranslationHandler(),
middleware.ValidationHandler(),
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) {
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 {
return nil, err
}

View File

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

View File

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

View File

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

View File

@ -61,12 +61,12 @@ func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, aut
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)
}
func (l *Login) renderChangePasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
var errType, errMessage string
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
import (
"context"
"encoding/base64"
"net/http"
"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) {
provider, err := l.getRPConfig(idpConfig, callbackEndpoint)
provider, err := l.getRPConfig(r.Context(), idpConfig, callbackEndpoint)
if err != nil {
l.renderLogin(w, r, authReq, err)
return
@ -150,7 +151,7 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
return
}
if idpConfig.IsOIDC {
provider, err := l.getRPConfig(idpConfig, EndpointExternalLoginCallback)
provider, err := l.getRPConfig(r.Context(), idpConfig, EndpointExternalLoginCallback)
if err != nil {
l.renderLogin(w, r, authReq, err)
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"))
}
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)
if err != nil {
return nil, err
}
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 == "" {
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,
TokenURL: idpConfig.OAuthTokenEndpoint,
},
RedirectURL: l.baseURL + callbackEndpoint,
RedirectURL: l.baseURL(ctx) + callbackEndpoint,
Scopes: idpConfig.OIDCScopes,
}
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.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)
}

View File

@ -97,7 +97,7 @@ func (l *Login) handleExternalRegisterCallback(w http.ResponseWriter, r *http.Re
l.renderError(w, r, authReq, err)
return
}
provider, err := l.getRPConfig(idpConfig, EndpointExternalRegisterCallback)
provider, err := l.getRPConfig(r.Context(), idpConfig, EndpointExternalRegisterCallback)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
@ -192,7 +192,7 @@ func (l *Login) renderExternalRegisterOverview(w http.ResponseWriter, r *http.Re
data.ExternalPhone = human.PhoneNumber
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)
}

View File

@ -1,6 +1,7 @@
package login
import (
"fmt"
"net/http"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
@ -38,6 +39,10 @@ type initPasswordData struct {
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) {
userID := r.FormValue(queryInitPWUserID)
code := r.FormValue(queryInitPWCode)
@ -142,11 +147,11 @@ func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authR
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)
}
func (l *Login) renderInitPasswordDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
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
import (
"fmt"
"net/http"
"strconv"
@ -41,6 +42,10 @@ type initUserData struct {
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) {
userID := r.FormValue(queryInitUserUserID)
code := r.FormValue(queryInitUserCode)
@ -132,11 +137,11 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *
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)
}
func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
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
}
}
redirect, err := l.redirectToJWTCallback(authReq)
redirect, err := l.redirectToJWTCallback(r.Context(), authReq)
if err != nil {
l.renderError(w, r, nil, err)
return
@ -153,7 +153,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
l.renderError(w, r, authReq, err)
return
}
redirect, err := l.redirectToJWTCallback(authReq)
redirect, err := l.redirectToJWTCallback(r.Context(), authReq)
if err != nil {
l.renderError(w, r, nil, err)
return
@ -174,8 +174,8 @@ func (l *Login) appendUserGrants(ctx context.Context, userGrants []*domain.UserG
return nil
}
func (l *Login) redirectToJWTCallback(authReq *domain.AuthRequest) (string, error) {
redirect, err := url.Parse(l.baseURL + EndpointJWTCallback)
func (l *Login) redirectToJWTCallback(ctx context.Context, authReq *domain.AuthRequest) (string, error) {
redirect, err := url.Parse(l.baseURL(ctx) + EndpointJWTCallback)
if err != nil {
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) {
var errType, errMessage string
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"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/form"
@ -33,12 +32,11 @@ type Login struct {
query *query.Queries
staticStorage static.Storage
authRepo auth_repository.Repository
baseURL string
externalSecure bool
consolePath string
oidcAuthCallbackURL func(context.Context, string) string
idpConfigAlg crypto.EncryptionAlgorithm
userCodeAlg crypto.EncryptionAlgorithm
iamDomain string
}
type Config struct {
@ -58,10 +56,7 @@ func CreateLogin(config Config,
query *query.Queries,
authRepo *eventsourcing.EsRepository,
staticStorage static.Storage,
systemDefaults systemdefaults.SystemDefaults,
consolePath,
domain,
baseURL string,
consolePath string,
oidcAuthCallbackURL func(context.Context, string) string,
externalSecure bool,
userAgentCookie,
@ -74,13 +69,12 @@ func CreateLogin(config Config,
login := &Login{
oidcAuthCallbackURL: oidcAuthCallbackURL,
baseURL: baseURL + HandlerPrefix,
externalSecure: externalSecure,
consolePath: consolePath,
command: command,
query: query,
staticStorage: staticStorage,
authRepo: authRepo,
iamDomain: domain,
idpConfigAlg: idpConfigAlg,
userCodeAlg: userCodeAlg,
}
@ -100,7 +94,7 @@ func CreateLogin(config Config,
security := middleware.SecurityHeaders(csp(), login.cspErrorHandler)
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()
return login, nil
}
@ -128,7 +122,7 @@ func (l *Login) Handler() http.Handler {
}
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 {
return nil, err
}
@ -150,3 +144,7 @@ func setContext(ctx context.Context, resourceOwner string) context.Context {
}
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"`
}
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) {
authReq, err := l.getAuthRequest(r)
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
},
}
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 {
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) {

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) {
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
import (
"fmt"
"net/http"
"github.com/caos/zitadel/internal/domain"
@ -26,6 +27,10 @@ type mailVerificationData struct {
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) {
userID := r.FormValue(queryUserID)
code := r.FormValue(queryCode)
@ -92,7 +97,7 @@ func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, a
UserID: userID,
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)
}
@ -101,6 +106,6 @@ func (l *Login) renderMailVerified(w http.ResponseWriter, r *http.Request, authR
baseData: l.getBaseData(r, authReq, "Mail Verified", "", ""),
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)
}

View File

@ -17,6 +17,6 @@ func (l *Login) renderMFAInitDone(w http.ResponseWriter, r *http.Request, authRe
var errType, errMessage string
data.baseData = l.getBaseData(r, authReq, "MFA Init Done", errType, errMessage)
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)
}

View File

@ -36,7 +36,7 @@ func (l *Login) renderRegisterU2F(w http.ResponseWriter, r *http.Request, authRe
},
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) {

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)
}

View File

@ -74,7 +74,7 @@ func (l *Login) renderMFAPrompt(w http.ResponseWriter, r *http.Request, authReq
l.handleMFACreation(w, r, authReq, data)
return
}
translator := l.getTranslator(authReq)
translator := l.getTranslator(r.Context(), authReq)
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)
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 {

View File

@ -45,7 +45,7 @@ func (l *Login) renderU2FVerification(w http.ResponseWriter, r *http.Request, au
MFAProviders: providers,
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) {

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) {
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)
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 {

View File

@ -28,7 +28,7 @@ func (l *Login) renderPassword(w http.ResponseWriter, r *http.Request, authReq *
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) {

View File

@ -42,5 +42,5 @@ func (l *Login) renderPasswordResetDone(w http.ResponseWriter, r *http.Request,
errID, errMessage = l.getErrorMessage(r, err)
}
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,
}
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) {

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),
}
translator := l.getTranslator(authReq)
translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplPasswordlessPrompt], data, nil)
}

View File

@ -5,6 +5,7 @@ import (
"net/http"
"github.com/caos/logging"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore/v1/models"
@ -110,13 +111,13 @@ func (l *Login) renderPasswordlessRegistration(w http.ResponseWriter, r *http.Re
requestedPlatformType,
disabled,
}
translator := l.getTranslator(authReq)
translator := l.getTranslator(r.Context(), authReq)
if authReq == nil {
policy, err := l.query.ActiveLabelPolicyByOrg(r.Context(), orgID)
logging.Log("HANDL-XjWKE").OnError(err).Error("unable to get active label policy")
data.LabelPolicy = labelPolicyToDomain(policy)
translator, err = l.renderer.NewTranslator()
translator, err = l.renderer.NewTranslator(r.Context())
if err == nil {
texts, err := l.authRepo.GetLoginText(r.Context(), orgID)
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),
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 {
errID, errMessage = l.getErrorMessage(r, err)
}
translator := l.getTranslator(authRequest)
translator := l.getTranslator(r.Context(), authRequest)
if formData == nil {
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
},
}
translator := l.getTranslator(authReq)
translator := l.getTranslator(r.Context(), authReq)
l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplRegisterOption], data, funcs)
}

View File

@ -3,6 +3,7 @@ package login
import (
"net/http"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/domain"
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)
if orgPolicy != nil {
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)
}

View File

@ -1,6 +1,7 @@
package login
import (
"context"
"errors"
"fmt"
"html/template"
@ -9,10 +10,9 @@ import (
"strings"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/api/authz"
"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"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
@ -36,7 +36,7 @@ type LanguageData struct {
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{
pathPrefix: pathPrefix,
staticStorage: staticStorage,
@ -219,7 +219,7 @@ func CreateRenderer(pathPrefix string, staticDir http.FileSystem, staticStorage
r.Renderer, err = renderer.NewRenderer(
staticDir,
tmplMapping, funcs,
i18n.TranslatorConfig{DefaultLanguage: defaultLanguage, CookieName: cookieName},
cookieName,
)
logging.New().OnError(err).WithError(err).Panic("error creating renderer")
return r
@ -317,7 +317,7 @@ func (l *Login) renderInternalError(w http.ResponseWriter, r *http.Request, auth
_, msg = l.getErrorMessage(r, err)
}
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 {
@ -337,7 +337,7 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
ErrID: errType,
ErrMessage: errMessage,
},
Lang: l.renderer.ReqLang(l.getTranslator(authReq), r).String(),
Lang: l.renderer.ReqLang(l.getTranslator(r.Context(), authReq), r).String(),
Title: title,
Theme: l.getTheme(r),
ThemeMode: l.getThemeMode(r),
@ -371,8 +371,8 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
return baseData
}
func (l *Login) getTranslator(authReq *domain.AuthRequest) *i18n.Translator {
translator, _ := l.renderer.NewTranslator()
func (l *Login) getTranslator(ctx context.Context, authReq *domain.AuthRequest) *i18n.Translator {
translator, _ := l.renderer.NewTranslator(ctx)
if authReq != nil {
l.addLoginTranslations(translator, authReq.DefaultTranslations)
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) {
caosErr := new(caos_errs.CaosError)
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
}

View File

@ -22,7 +22,7 @@ func (l *Login) renderUserSelection(w http.ResponseWriter, r *http.Request, auth
Users: selectionData.Users,
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)
}

View File

@ -21,7 +21,7 @@ func (l *Login) renderChangeUsername(w http.ResponseWriter, r *http.Request, aut
errID, errMessage = l.getErrorMessage(r, err)
}
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) {
@ -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) {
var errType, errMessage string
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 {
eventstore *eventstore.Eventstore
static static.Storage
idGenerator id.Generator
iamDomain string
zitadelRoles []authz.RoleMapping
eventstore *eventstore.Eventstore
static static.Storage
idGenerator id.Generator
zitadelRoles []authz.RoleMapping
externalSecure bool
externalPort uint16
idpConfigEncryption crypto.EncryptionAlgorithm
smtpEncryption crypto.EncryptionAlgorithm
@ -61,6 +62,8 @@ func StartCommands(es *eventstore.Eventstore,
staticStore static.Storage,
authZRepo authz_repo.Repository,
webAuthN *webauthn_helper.Config,
externalSecure bool,
externalPort uint16,
idpConfigEncryption,
otpEncryption,
smtpEncryption,
@ -73,8 +76,9 @@ func StartCommands(es *eventstore.Eventstore,
eventstore: es,
static: staticStore,
idGenerator: id.SonyFlakeGenerator,
iamDomain: defaults.Domain,
zitadelRoles: zitadelRoles,
externalSecure: externalSecure,
externalPort: externalPort,
keySize: defaults.KeyConfig.Size,
privateKeyLifetime: defaults.KeyConfig.PrivateKeyLifetime,
publicKeyLifetime: defaults.KeyConfig.PublicKeyLifetime,

View File

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

View File

@ -5,6 +5,7 @@ import (
"strings"
"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/domain"
"github.com/caos/zitadel/internal/errors"
@ -15,7 +16,7 @@ import (
func (c *Commands) AddInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) {
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)
if err != nil {
return nil, err
@ -67,12 +68,12 @@ func (c *Commands) RemoveInstanceDomain(ctx context.Context, instanceDomain stri
}, nil
}
func (c *Commands) addGeneratedInstanceDomain(a *instance.Aggregate, instanceName string) preparation.Validation {
domain := domain.NewGeneratedInstanceDomain(instanceName, c.iamDomain)
return addInstanceDomain(a, domain, true)
func (c *Commands) addGeneratedInstanceDomain(ctx context.Context, a *instance.Aggregate, instanceName string) preparation.Validation {
domain := domain.NewGeneratedInstanceDomain(instanceName, authz.GetInstance(ctx).RequestedDomain())
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) {
if instanceDomain = strings.TrimSpace(instanceDomain); instanceDomain == "" {
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
}
if appWriteModel.State.Exists() {
redirectUrls := append(appWriteModel.RedirectUris, instanceDomain+consoleRedirectPath)
logoutUrls := append(appWriteModel.PostLogoutRedirectUris, instanceDomain+consolePostLogoutPath)
redirectUrls := append(appWriteModel.RedirectUris, http.BuildHTTP(instanceDomain, c.externalPort, c.externalSecure)+consoleRedirectPath)
logoutUrls := append(appWriteModel.PostLogoutRedirectUris, http.BuildOrigin(instanceDomain, c.externalSecure)+consolePostLogoutPath)
consoleChangeEvent, err := project.NewOIDCConfigChangedEvent(
ctx,
ProjectAggregateFromWriteModel(&appWriteModel.WriteModel),

View File

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

View File

@ -7,6 +7,7 @@ import (
"time"
"github.com/golang/mock/gomock"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/crypto"
@ -275,6 +276,14 @@ func (m *mockInstance) ConsoleApplicationID() string {
return "consoleApplicationID"
}
func (m *mockInstance) DefaultLanguage() language.Tag {
return language.English
}
func (m *mockInstance) RequestedDomain() string {
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 {
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)
orgAgg := OrgAggregateFromWriteModel(&addedOrg.WriteModel)

View File

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

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/assert"
"golang.org/x/text/language"
"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/crypto"
@ -1090,10 +1091,9 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
domainVerificationGenerator: tt.fields.secretGenerator,
domainVerificationAlg: tt.fields.alg,
domainVerificationValidator: tt.fields.domainValidationFunc,
iamDomain: "zitadel.ch",
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 {
assert.NoError(t, err)
}

View File

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

View File

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

View File

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

View File

@ -25,7 +25,6 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
iamDomain string
keyAlgorithm crypto.EncryptionAlgorithm
}
type args struct {
@ -285,7 +284,6 @@ func TestCommands_AddAccessAndRefreshToken(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator,
iamDomain: tt.fields.iamDomain,
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,

View File

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

View File

@ -41,15 +41,15 @@ type Message struct {
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)
var err error
t.bundle, err = newBundle(dir, config.DefaultLanguage)
t.bundle, err = newBundle(dir, defaultLanguage)
if err != nil {
return nil, err
}
t.cookieHandler = http_util.NewCookieHandler()
t.cookieName = config.CookieName
t.cookieName = cookieName
return t, nil
}

View File

@ -19,15 +19,12 @@ import (
"github.com/caos/zitadel/internal/notification/messages"
)
func InitFSChannel(path string, config FSConfig) (channels.NotificationChannel, error) {
if path == "" {
return nil, nil
}
if err := os.MkdirAll(path, os.ModePerm); err != nil {
func InitFSChannel(config FSConfig) (channels.NotificationChannel, error) {
if err := os.MkdirAll(config.Path, os.ModePerm); err != nil {
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 {
@ -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 ioutil.WriteFile(filepath.Join(path, fileName), []byte(content), 0666)
return ioutil.WriteFile(filepath.Join(config.Path, fileName), []byte(content), 0666)
}), nil
}

View File

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

View File

@ -9,7 +9,6 @@ import (
"github.com/caos/zitadel/internal/crypto"
"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/statik"
"github.com/caos/zitadel/internal/query"
@ -20,7 +19,8 @@ type Config struct {
}
func Start(config Config,
systemDefaults sd.SystemDefaults,
externalPort uint16,
externalSecure bool,
command *command.Commands,
queries *query.Queries,
dbClient *sql.DB,
@ -32,6 +32,6 @@ func Start(config Config,
statikFS, err := fs.NewWithNamespace("notification")
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")
}

View File

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

View File

@ -9,8 +9,8 @@ import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/api/authz"
http_utils "github.com/caos/zitadel/internal/api/http"
"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/domain"
"github.com/caos/zitadel/internal/errors"
@ -40,7 +40,7 @@ const (
type Notification struct {
handler
command *command.Commands
systemDefaults sd.SystemDefaults
fileSystemPath string
statikDir http.FileSystem
subscription *v1.Subscription
assetsPrefix string
@ -48,13 +48,16 @@ type Notification struct {
userDataCrypto crypto.EncryptionAlgorithm
smtpPasswordCrypto crypto.EncryptionAlgorithm
smsTokenCrypto crypto.EncryptionAlgorithm
externalPort uint16
externalSecure bool
}
func newNotification(
handler handler,
command *command.Commands,
query *query.Queries,
defaults sd.SystemDefaults,
externalPort uint16,
externalSecure bool,
statikDir http.FileSystem,
assetsPrefix string,
userEncryption crypto.EncryptionAlgorithm,
@ -64,13 +67,14 @@ func newNotification(
h := &Notification{
handler: handler,
command: command,
systemDefaults: defaults,
statikDir: statikDir,
assetsPrefix: assetsPrefix,
queries: query,
userDataCrypto: userEncryption,
smtpPasswordCrypto: smtpEncryption,
smsTokenCrypto: smsEncryption,
externalSecure: externalSecure,
externalPort: externalPort,
}
h.subscribe()
@ -190,7 +194,11 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
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 {
return err
}
@ -228,7 +236,12 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
if err != nil {
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 {
return err
}
@ -267,7 +280,11 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err 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 {
return err
}
@ -294,7 +311,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
if err != nil {
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 {
return err
}
@ -334,7 +351,11 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
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 {
return err
}
@ -381,7 +402,11 @@ func (n *Notification) handlePasswordlessRegistrationLink(event *models.Event) (
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 {
return err
}
@ -492,6 +517,7 @@ func (n *Notification) getFileSystemProvider(ctx context.Context) (*fs.FSConfig,
}
return &fs.FSConfig{
Compact: config.Compact,
Path: n.fileSystemPath,
}, 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) {
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 {
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) {
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"
"github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
es_spol "github.com/caos/zitadel/internal/eventstore/v1/spooler"
@ -24,7 +23,8 @@ type EsRepository struct {
func Start(conf Config,
dir http.FileSystem,
systemDefaults sd.SystemDefaults,
externalPort uint16,
externalSecure bool,
command *command.Commands,
queries *query.Queries,
dbClient *sql.DB,
@ -43,7 +43,7 @@ func Start(conf Config,
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{
spool,

View File

@ -5,7 +5,6 @@ import (
"net/http"
"github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
@ -27,7 +26,8 @@ func StartSpooler(c SpoolerConfig,
sql *sql.DB,
command *command.Commands,
queries *query.Queries,
systemDefaults sd.SystemDefaults,
externalPort uint16,
externalSecure bool,
dir http.FileSystem,
assetsPrefix string,
userEncryption crypto.EncryptionAlgorithm,
@ -38,7 +38,7 @@ func StartSpooler(c SpoolerConfig,
Eventstore: es,
Locker: &locker{dbClient: sql},
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.Start()

View File

@ -3,19 +3,18 @@ package senders
import (
"context"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels"
"github.com/caos/zitadel/internal/notification/channels/fs"
"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 (
providers []channels.NotificationChannel
)
if fsProvider, err := getFileSystemProvider(ctx); err == nil {
p, err := fs.InitFSChannel(config.FileSystemPath, *fsProvider)
p, err := fs.InitFSChannel(*fsProvider)
if err == nil {
providers = append(providers, p)
}

View File

@ -4,18 +4,18 @@ import (
"context"
"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/log"
"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)
if err == nil {
chain.channels = append(chain.channels, p)
}
chain, err = debugChannels(ctx, config, getFileSystemProvider, getLogProvider)
chain, err = debugChannels(ctx, getFileSystemProvider, getLogProvider)
if err != nil {
logging.New().Info("Error in creating debug channels")
}

View File

@ -3,17 +3,16 @@ package senders
import (
"context"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"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 {
chain.channels = append(chain.channels, twilio.InitTwilioChannel(*twilioConfig))
}
chain, err = debugChannels(ctx, config, getFileSystemProvider, getLogProvider)
chain, err = debugChannels(ctx, getFileSystemProvider, getLogProvider)
if err != nil {
return nil, err
}

View File

@ -4,7 +4,7 @@ import (
"context"
"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/i18n"
"github.com/caos/zitadel/internal/notification/channels/fs"
@ -20,11 +20,8 @@ type DomainClaimedData struct {
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 {
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.DomainClaimed, &UrlData{UserID: user.ID})
if err != nil {
return err
}
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 := login.LoginLink(origin)
var args = mapNotifyUserToArgs(user)
args["TempUsername"] = username
args["Domain"] = strings.Split(user.LastEmail, "@")[1]
@ -37,5 +34,5 @@ func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Tr
if err != nil {
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 (
"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/domain"
"github.com/caos/zitadel/internal/i18n"
@ -21,16 +21,12 @@ type EmailVerificationCodeData struct {
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)
if err != nil {
return err
}
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.VerifyEmail, &UrlData{UserID: user.ID, Code: codeString})
if err != nil {
return err
}
url := login.MailVerificationLink(origin, user.ID, codeString)
var args = mapNotifyUserToArgs(user)
args["Code"] = codeString
@ -43,5 +39,5 @@ func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator
if err != nil {
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 (
"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/domain"
"github.com/caos/zitadel/internal/i18n"
@ -27,15 +27,12 @@ type UrlData struct {
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)
if err != nil {
return err
}
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.InitCode, &UrlData{UserID: user.ID, Code: codeString, PasswordSet: user.PasswordSet})
if err != nil {
return err
}
url := login.InitUserLink(origin, user.ID, codeString, user.PasswordSet)
var args = mapNotifyUserToArgs(user)
args["Code"] = codeString
@ -47,5 +44,5 @@ func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Tra
if err != nil {
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 (
"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/domain"
"github.com/caos/zitadel/internal/i18n"
@ -24,15 +24,12 @@ type PasswordCodeData struct {
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)
if err != nil {
return err
}
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.PasswordReset, &UrlData{UserID: user.ID, Code: codeString})
if err != nil {
return err
}
url := login.InitPasswordLink(origin, user.ID, codeString)
var args = mapNotifyUserToArgs(user)
args["Code"] = codeString
@ -47,8 +44,8 @@ func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Tra
return err
}
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 (
"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/domain"
"github.com/caos/zitadel/internal/i18n"
@ -21,12 +21,12 @@ type PasswordlessRegistrationLinkData struct {
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)
if err != nil {
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)
emailCodeData := &PasswordlessRegistrationLinkData{
@ -38,5 +38,5 @@ func SendPasswordlessRegistrationLink(ctx context.Context, mailhtml string, tran
if err != nil {
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"
"fmt"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
@ -20,7 +19,7 @@ type PhoneVerificationCodeData struct {
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)
if err != nil {
return err
@ -35,5 +34,5 @@ func SendPhoneVerificationCode(ctx context.Context, translator *i18n.Translator,
if err != nil {
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/senders"
"github.com/caos/zitadel/internal/config/systemdefaults"
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)
message := &messages.Email{
Recipients: []string{user.VerifiedEmail},
@ -26,7 +25,7 @@ func generateEmail(ctx context.Context, user *view_model.NotifyUser, subject, co
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 {
return err
}

View File

@ -3,7 +3,6 @@ package types
import (
"context"
"github.com/caos/zitadel/internal/config/systemdefaults"
caos_errors "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
@ -13,7 +12,7 @@ import (
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 := ""
twilio, err := getTwilioProvider(ctx)
if err == nil {
@ -28,7 +27,7 @@ func generateSms(ctx context.Context, user *view_model.NotifyUser, content strin
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 {
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) {
contents, err := q.readLoginTranslationFile(lang)
contents, err := q.readLoginTranslationFile(ctx, lang)
if err != nil {
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) {
contents, err := q.readLoginTranslationFile(lang)
contents, err := q.readLoginTranslationFile(ctx, lang)
if err != nil {
return nil, err
}
@ -186,7 +186,7 @@ func (q *Queries) IAMLoginTexts(ctx context.Context, lang string) (*domain.Custo
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()
defer q.mutex.Unlock()
contents, ok := q.LoginTranslationFileContents[lang]
@ -194,7 +194,7 @@ func (q *Queries) readLoginTranslationFile(lang string) ([]byte, error) {
if !ok {
contents, err = q.readTranslationFile(q.LoginDir, fmt.Sprintf("/i18n/%s.yaml", lang))
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 {
return nil, err

View File

@ -76,14 +76,14 @@ type Instance struct {
CreationDate time.Time
Sequence uint64
GlobalOrgID string
IAMProjectID string
ConsoleID string
ConsoleAppID string
DefaultLanguage language.Tag
SetupStarted domain.Step
SetupDone domain.Step
Host string
GlobalOrgID string
IAMProjectID string
ConsoleID string
ConsoleAppID string
DefaultLang language.Tag
SetupStarted domain.Step
SetupDone domain.Step
host string
}
type Instances struct {
@ -108,7 +108,15 @@ func (i *Instance) ConsoleApplicationID() 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 {
@ -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) {
stmt, scan := prepareInstanceDomainQuery(host)
host = strings.Split(host, ":")[0] //remove possible port
query, args, err := stmt.Where(sq.Eq{
InstanceDomainDomainCol.identifier(): strings.Split(host, ":")[0],
InstanceDomainDomainCol.identifier(): host,
}).ToSql()
if err != nil {
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 {
iam, err := q.Instance(ctx)
instance, err := q.Instance(ctx)
if err != nil {
return language.Und
}
return iam.DefaultLanguage
return instance.DefaultLanguage()
}
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),
func(row *sql.Row) (*Instance, error) {
instance := &Instance{Host: host}
instance := &Instance{host: host}
lang := ""
err := row.Scan(
&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")
}
instance.DefaultLanguage = language.Make(lang)
instance.DefaultLang = language.Make(lang)
return instance, nil
}
}
@ -299,7 +308,7 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Row) (
LeftJoin(join(InstanceDomainInstanceIDCol, InstanceColumnID)).
PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*Instance, error) {
instance := &Instance{Host: host}
instance := &Instance{host: host}
lang := ""
err := row.Scan(
&instance.ID,
@ -320,7 +329,7 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Row) (
}
return nil, errors.ThrowInternal(err, "QUERY-d9nw", "Errors.Internal")
}
instance.DefaultLanguage = language.Make(lang)
instance.DefaultLang = language.Make(lang)
return instance, nil
}
}

View File

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

View File

@ -158,8 +158,8 @@ func (q *Queries) DefaultMessageText(ctx context.Context) (*MessageText, error)
return scan(row)
}
func (q *Queries) DefaultMessageTextByTypeAndLanguageFromFileSystem(messageType, language string) (*MessageText, error) {
contents, err := q.readNotificationTextMessages(language)
func (q *Queries) DefaultMessageTextByTypeAndLanguageFromFileSystem(ctx context.Context, messageType, language string) (*MessageText, error) {
contents, err := q.readNotificationTextMessages(ctx, language)
if err != nil {
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) {
contents, err := q.readNotificationTextMessages(language)
contents, err := q.readNotificationTextMessages(ctx, language)
if err != nil {
return nil, err
}
@ -229,7 +229,7 @@ func (q *Queries) IAMMessageTextByTypeAndLanguage(ctx context.Context, messageTy
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()
defer q.mutex.Unlock()
var err error
@ -237,7 +237,7 @@ func (q *Queries) readNotificationTextMessages(language string) ([]byte, error)
if !ok {
contents, err = q.readTranslationFile(q.NotificationDir, fmt.Sprintf("/i18n/%s.yaml", language))
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 {
return nil, err

View File

@ -1,17 +1,18 @@
package renderer
import (
"context"
"io/ioutil"
"net/http"
"os"
"text/template"
"github.com/caos/logging"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/logging"
)
const (
@ -21,16 +22,16 @@ const (
)
type Renderer struct {
Templates map[string]*template.Template
dir http.FileSystem
translatorConfig i18n.TranslatorConfig
Templates map[string]*template.Template
dir http.FileSystem
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
r := &Renderer{
dir: dir,
translatorConfig: translatorConfig,
dir: dir,
cookieName: cookieName,
}
err = r.loadTemplates(dir, nil, tmplMapping, funcs)
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) {
return i18n.NewTranslator(r.dir, r.translatorConfig)
func (r *Renderer) NewTranslator(ctx context.Context) (*i18n.Translator, error) {
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 {

View File

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

View File

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

View File

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