mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-06 19:36:41 +00:00
fix: configure default url templates (#10416)
# Which Problems Are Solved
Emails are still send only with URLs to login v1.
# How the Problems Are Solved
Add configuration for URLs as URL templates, so that links can point at
Login v2.
# Additional Changes
None
# Additional Context
Closes #10236
---------
Co-authored-by: Marco A. <marco@zitadel.com>
(cherry picked from commit 0a14c01412)
This commit is contained in:
committed by
Livio Spring
parent
e06df6e161
commit
1625e5f7bc
@@ -52,7 +52,43 @@ type Config struct {
|
||||
AssetCache middleware.CacheConfig
|
||||
|
||||
// LoginV2
|
||||
DefaultOTPEmailURLV2 string
|
||||
DefaultPaths *DefaultPaths
|
||||
}
|
||||
|
||||
type DefaultPaths struct {
|
||||
BasePath string
|
||||
PasswordSetPath string
|
||||
EmailCodePath string
|
||||
OTPEmailPath string
|
||||
}
|
||||
|
||||
func (c *Config) defaultBaseURL(ctx context.Context) string {
|
||||
loginV2 := authz.GetInstance(ctx).Features().LoginV2
|
||||
if loginV2.Required {
|
||||
// use the origin as default
|
||||
baseURI := http_utils.DomainContext(ctx).Origin()
|
||||
// use custom base URI if defined
|
||||
if loginV2.BaseURI != nil && loginV2.BaseURI.String() != "" {
|
||||
baseURI = loginV2.BaseURI.String()
|
||||
}
|
||||
return baseURI + c.DefaultPaths.BasePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Config) DefaultEmailCodeURLTemplate(ctx context.Context) string {
|
||||
basePath := c.defaultBaseURL(ctx)
|
||||
if basePath == "" {
|
||||
return ""
|
||||
}
|
||||
return basePath + c.DefaultPaths.EmailCodePath
|
||||
}
|
||||
func (c *Config) DefaultPasswordSetURLTemplate(ctx context.Context) string {
|
||||
basePath := c.defaultBaseURL(ctx)
|
||||
if basePath == "" {
|
||||
return ""
|
||||
}
|
||||
return c.defaultBaseURL(ctx) + c.DefaultPaths.PasswordSetPath
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
178
internal/api/ui/login/login_test.go
Normal file
178
internal/api/ui/login/login_test.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
)
|
||||
|
||||
func TestConfig_defaultBaseURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
config := &Config{
|
||||
DefaultPaths: &DefaultPaths{BasePath: "/basepath"},
|
||||
}
|
||||
|
||||
baseCustomURI, err := url.Parse("https://custom")
|
||||
require.Nil(t, err)
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
inputCtx context.Context
|
||||
http.DomainCtx
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "LoginV2 not required",
|
||||
inputCtx: authz.NewMockContext("instance1", "org1", "user1"),
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "LoginV2 required, no custom BaseURI",
|
||||
inputCtx: http.WithDomainContext(
|
||||
authz.NewMockContext("instance1", "org1", "user1",
|
||||
authz.WithMockFeatures(feature.Features{LoginV2: feature.LoginV2{Required: true}}),
|
||||
),
|
||||
&http.DomainCtx{Protocol: "https", PublicHost: "origin"},
|
||||
),
|
||||
expected: "https://origin/basepath",
|
||||
},
|
||||
{
|
||||
name: "LoginV2 required, custom BaseURI",
|
||||
inputCtx: http.WithDomainContext(
|
||||
authz.NewMockContext("instance1", "org1", "user1",
|
||||
authz.WithMockFeatures(feature.Features{LoginV2: feature.LoginV2{Required: true, BaseURI: baseCustomURI}}),
|
||||
),
|
||||
&http.DomainCtx{Protocol: "https", PublicHost: "origin"},
|
||||
),
|
||||
|
||||
expected: "https://custom/basepath",
|
||||
},
|
||||
{
|
||||
name: "LoginV2 required, custom BaseURI empty string",
|
||||
inputCtx: http.WithDomainContext(
|
||||
authz.NewMockContext("instance1", "org1", "user1",
|
||||
authz.WithMockFeatures(feature.Features{LoginV2: feature.LoginV2{Required: true, BaseURI: &url.URL{}}}),
|
||||
),
|
||||
&http.DomainCtx{Protocol: "https", PublicHost: "origin"},
|
||||
),
|
||||
expected: "https://origin/basepath",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := config.defaultBaseURL(tc.inputCtx)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_DefaultEmailCodeURLTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
testName string
|
||||
inputCtx context.Context
|
||||
expectedEmailURLTemplate string
|
||||
}{
|
||||
{
|
||||
testName: "when base path is empty should return empty email url template",
|
||||
inputCtx: http.WithDomainContext(
|
||||
authz.NewMockContext("instance1", "org1", "user1",
|
||||
authz.WithMockFeatures(feature.Features{LoginV2: feature.LoginV2{Required: false, BaseURI: &url.URL{}}}),
|
||||
),
|
||||
&http.DomainCtx{Protocol: "https", PublicHost: "origin"},
|
||||
),
|
||||
expectedEmailURLTemplate: "",
|
||||
},
|
||||
{
|
||||
testName: "when base path is not empty should return expected url template",
|
||||
inputCtx: http.WithDomainContext(
|
||||
authz.NewMockContext("instance1", "org1", "user1",
|
||||
authz.WithMockFeatures(feature.Features{LoginV2: feature.LoginV2{Required: true, BaseURI: &url.URL{}}}),
|
||||
),
|
||||
&http.DomainCtx{Protocol: "https", PublicHost: "origin"},
|
||||
),
|
||||
expectedEmailURLTemplate: "https://origin/basepath/email-code-path",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given
|
||||
c := &Config{
|
||||
DefaultPaths: &DefaultPaths{
|
||||
BasePath: "/basepath",
|
||||
EmailCodePath: "/email-code-path"},
|
||||
}
|
||||
|
||||
// Test
|
||||
res := c.DefaultEmailCodeURLTemplate(tc.inputCtx)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, tc.expectedEmailURLTemplate, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_DefaultPasswordSetURLTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
testName string
|
||||
inputCtx context.Context
|
||||
expectedEmailURLTemplate string
|
||||
}{
|
||||
{
|
||||
testName: "when base path is empty should return empty email url template",
|
||||
inputCtx: http.WithDomainContext(
|
||||
authz.NewMockContext("instance1", "org1", "user1",
|
||||
authz.WithMockFeatures(feature.Features{LoginV2: feature.LoginV2{Required: false, BaseURI: &url.URL{}}}),
|
||||
),
|
||||
&http.DomainCtx{Protocol: "https", PublicHost: "origin"},
|
||||
),
|
||||
expectedEmailURLTemplate: "",
|
||||
},
|
||||
{
|
||||
testName: "when base path is not empty should return expected url template",
|
||||
inputCtx: http.WithDomainContext(
|
||||
authz.NewMockContext("instance1", "org1", "user1",
|
||||
authz.WithMockFeatures(feature.Features{LoginV2: feature.LoginV2{Required: true, BaseURI: &url.URL{}}}),
|
||||
),
|
||||
&http.DomainCtx{Protocol: "https", PublicHost: "origin"},
|
||||
),
|
||||
expectedEmailURLTemplate: "https://origin/basepath/password-set-path",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given
|
||||
c := &Config{
|
||||
DefaultPaths: &DefaultPaths{
|
||||
BasePath: "/basepath",
|
||||
PasswordSetPath: "/password-set-path",
|
||||
},
|
||||
}
|
||||
|
||||
// Test
|
||||
res := c.DefaultPasswordSetURLTemplate(tc.inputCtx)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, tc.expectedEmailURLTemplate, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user