mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
fix: correctly send links to login v2 in email notifications (#10711)
# Which Problems Are Solved There were still some emails (passkey registration and domain claimed) sent with links pointing to login v1 even when the login v2 was enabled for the instance. Also while looking into the issue, it was discovered that some links pointing to login V2 were not correctly generated. # How the Problems Are Solved - Added default paths for passkey registration and domain claimed notifications - Fixed the existing paths to properly handle concatenation (resp. use `url.ResolveReference`) - Change their go types (from string) to `*url.URL` - Added a mapstructure hook for string to url - Removed unnecessary `InstanceSetupFeatures` and corresponding conversions - Refactored the methods on the `login.DefaultPaths` struct and added an interface to the `Commands` to only need to pass a single config (and not every method) - Added an `OriginURL` method to the `DomainCtx` to prevent going from url to string and back - Added the use of the templates in case of enabled login v2 for passkey registration and domain claimed) # Additional Changes None # Additional Context closes #10643 --------- Co-authored-by: Max Peintner <[email protected]> Co-authored-by: Livio Spring <[email protected]> Co-authored-by: Livio Spring <[email protected]> Co-authored-by: Max Peintner <[email protected]> Co-authored-by: Gayathri Vijayan <[email protected]>
This commit is contained in:
co-authored by
Max Peintner
Livio Spring
Livio Spring
Max Peintner
Gayathri Vijayan
parent
382aec2d61
commit
2a2d5392a3
@@ -36,7 +36,7 @@ services:
|
||||
# Activate the login v2 on an installation from scratch.
|
||||
# To activate the login v2 on an existing installation, read the "What's next" section.
|
||||
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: true # To use the login v1, set this to false.
|
||||
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI: http://localhost:3000/ui/v2/login
|
||||
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI: http://localhost:3000/ui/v2/login/
|
||||
# Configure the redirection paths to the login v2.
|
||||
ZITADEL_OIDC_DEFAULTLOGINURLV2: http://localhost:3000/ui/v2/login/login?authRequest=
|
||||
ZITADEL_OIDC_DEFAULTLOGOUTURLV2: http://localhost:3000/ui/v2/login/logout?post_logout_redirect=
|
||||
|
||||
@@ -109,6 +109,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
|
||||
requestId={requestId}
|
||||
code={code}
|
||||
codeId={codeId}
|
||||
loginName={loginName ?? session?.factors?.user?.loginName ?? user?.preferredLoginName}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -21,9 +21,19 @@ type Props = {
|
||||
organization?: string;
|
||||
code?: string;
|
||||
codeId?: string;
|
||||
loginName?: string;
|
||||
};
|
||||
|
||||
export function RegisterPasskey({ sessionId, userId, isPrompt, organization, requestId, code, codeId }: Props) {
|
||||
export function RegisterPasskey({
|
||||
sessionId,
|
||||
userId,
|
||||
isPrompt,
|
||||
organization,
|
||||
requestId,
|
||||
code,
|
||||
codeId,
|
||||
loginName: initialLoginName,
|
||||
}: Props) {
|
||||
const { handleSubmit, formState } = useForm<Inputs>({
|
||||
mode: "onChange",
|
||||
});
|
||||
@@ -155,7 +165,7 @@ export function RegisterPasskey({ sessionId, userId, isPrompt, organization, req
|
||||
return;
|
||||
}
|
||||
|
||||
continueAndLogin();
|
||||
continueAndLogin(verificationResponse.loginName);
|
||||
}, [sessionId, userId, code]);
|
||||
|
||||
// Auto-submit when code is provided (similar to VerifyForm)
|
||||
@@ -165,7 +175,7 @@ export function RegisterPasskey({ sessionId, userId, isPrompt, organization, req
|
||||
}
|
||||
}, [code, submitRegisterAndContinue]);
|
||||
|
||||
function continueAndLogin() {
|
||||
function continueAndLogin(loginName?: string) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (organization) {
|
||||
@@ -184,6 +194,10 @@ export function RegisterPasskey({ sessionId, userId, isPrompt, organization, req
|
||||
params.set("userId", userId);
|
||||
}
|
||||
|
||||
if (loginName ?? initialLoginName) {
|
||||
params.set("loginName", loginName ?? initialLoginName ?? "");
|
||||
}
|
||||
|
||||
router.push("/passkey?" + params);
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,7 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
|
||||
}${os.name}${os.name ? ", " : ""}${browser.name}`;
|
||||
}
|
||||
|
||||
let loginName: string | undefined;
|
||||
let currentUserId: string;
|
||||
|
||||
if (command.sessionId) {
|
||||
@@ -216,6 +217,7 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
|
||||
}
|
||||
|
||||
currentUserId = userId;
|
||||
loginName = session?.session?.factors?.user?.loginName;
|
||||
} else {
|
||||
// UserId-based flow
|
||||
currentUserId = command.userId!;
|
||||
@@ -226,9 +228,11 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
|
||||
if (!userResponse || !userResponse.user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
loginName = userResponse.user.preferredLoginName;
|
||||
}
|
||||
|
||||
return zitadelVerifyPasskeyRegistration({
|
||||
const response = await zitadelVerifyPasskeyRegistration({
|
||||
serviceConfig,
|
||||
request: create(VerifyPasskeyRegistrationRequestSchema, {
|
||||
passkeyId: command.passkeyId,
|
||||
@@ -237,6 +241,8 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
|
||||
userId: currentUserId,
|
||||
}),
|
||||
});
|
||||
|
||||
return { ...response, loginName };
|
||||
}
|
||||
|
||||
type SendPasskeyCommand = {
|
||||
|
||||
+11
-4
@@ -708,10 +708,17 @@ Login:
|
||||
# 168h is 7 days, one week
|
||||
SharedMaxAge: 168h # ZITADEL_LOGIN_CACHE_SHAREDMAXAGE
|
||||
DefaultPaths:
|
||||
BasePath: "/ui/v2/login"
|
||||
PasswordSetPath: "/password/set?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}"
|
||||
EmailCodePath: "/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}"
|
||||
OTPEmailPath: "/otp/verify?code={{.Code}}&userID={{.UserID}}&sessionId={{.SessionID}}"
|
||||
BasePath: "/ui/v2/login/"
|
||||
# PasswordSetPath is used to create the link for setting a password during user registration or password reset
|
||||
PasswordSetPath: "password/set?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}" # ZITADEL_LOGIN_DEFAULTPATHS_PASSWORDSETPATH
|
||||
# EmailCodePath is used to create the link for verifying the user's email address
|
||||
EmailCodePath: "verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}"
|
||||
# OTPEmailPath is used to create the link for verifying the user's OTP during login
|
||||
OTPEmailPath: "otp/email?code={{.Code}}&userId={{.UserID}}&sessionId={{.SessionID}}"
|
||||
# PasskeySetPath is used to create the link for adding a passkey to the user's account though the login UI
|
||||
PasskeySetPath: "passkey/set?codeId={{.CodeID}}&code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}"
|
||||
# DomainClaimedPath is used to create the link to the login UI after an organization has claimed a domain and the user's loginname was updated
|
||||
DomainClaimedPath: "loginname?loginName={{.TempUsername}}&organization={{.OrgID}}"
|
||||
|
||||
Console:
|
||||
ShortCache:
|
||||
|
||||
@@ -94,6 +94,7 @@ func newConfig(v *viper.Viper, config any) error {
|
||||
hooks.MapHTTPHeaderStringDecode,
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
hook.StringToURLHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
|
||||
@@ -214,8 +214,7 @@ func projections(
|
||||
config.OIDC.DefaultRefreshTokenExpiration,
|
||||
config.OIDC.DefaultRefreshTokenIdleExpiration,
|
||||
config.DefaultInstance.SecretGenerators,
|
||||
nil,
|
||||
nil,
|
||||
config.Login.DefaultPaths,
|
||||
)
|
||||
logging.OnError(ctx, err).Fatal("unable to start commands")
|
||||
|
||||
@@ -239,7 +238,7 @@ func projections(
|
||||
commands,
|
||||
queries,
|
||||
es,
|
||||
config.Login.DefaultPaths.OTPEmailPath,
|
||||
config.Login.DefaultPaths.DefaultOTPEmailURLTemplate,
|
||||
config.SystemDefaults.Notifications.FileSystemPath,
|
||||
keys.User,
|
||||
keys.SMTP,
|
||||
|
||||
+3
-2
@@ -9,6 +9,7 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/ui/login"
|
||||
"github.com/zitadel/zitadel/internal/cache/connector"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
@@ -43,6 +44,7 @@ type FirstInstance struct {
|
||||
externalSecure bool
|
||||
externalPort uint16
|
||||
domain string
|
||||
defaultPaths *login.DefaultPaths
|
||||
}
|
||||
|
||||
func (mig *FirstInstance) Execute(ctx context.Context, _ eventstore.Event) error {
|
||||
@@ -92,8 +94,7 @@ func (mig *FirstInstance) Execute(ctx context.Context, _ eventstore.Event) error
|
||||
0,
|
||||
0,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
mig.defaultPaths,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -85,6 +85,7 @@ func NewConfig(cmd *cobra.Command, v *viper.Viper) (*Config, instrumentation.Shu
|
||||
hook.EnumHookFunc(authz.MemberTypeString),
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
hook.StringToURLHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
@@ -205,6 +206,7 @@ func NewSteps(ctx context.Context, v *viper.Viper) (*Steps, error) {
|
||||
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
hook.StringToURLHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
|
||||
@@ -3,6 +3,7 @@ package setup
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/ui/login"
|
||||
"github.com/zitadel/zitadel/internal/cache/connector"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
@@ -58,8 +59,7 @@ func (mig *externalConfigChange) Execute(ctx context.Context, _ eventstore.Event
|
||||
0,
|
||||
0,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
&login.DefaultPaths{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
)
|
||||
|
||||
func TestMustNewConfig(t *testing.T) {
|
||||
@@ -48,12 +50,12 @@ Actions:
|
||||
DenyList: []
|
||||
`},
|
||||
want: func(t *testing.T, config *Config) {
|
||||
assert.Equal(t, config.DefaultInstance.Features, &command.InstanceSetupFeatures{
|
||||
assert.Equal(t, config.DefaultInstance.Features, &command.InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
LoginV2: &command.InstanceSetupFeatureLoginV2{
|
||||
LoginV2: &feature.LoginV2{
|
||||
Required: true,
|
||||
BaseURI: gu.Ptr("http://zitadel:8080"),
|
||||
BaseURI: &url.URL{Scheme: "http", Host: "zitadel:8080"},
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
+3
-4
@@ -186,6 +186,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string)
|
||||
steps.FirstInstance.externalDomain = config.ExternalDomain
|
||||
steps.FirstInstance.externalSecure = config.ExternalSecure
|
||||
steps.FirstInstance.externalPort = config.ExternalPort
|
||||
steps.FirstInstance.defaultPaths = config.Login.DefaultPaths
|
||||
|
||||
steps.s5LastFailed = &LastFailed{dbClient: dbClient.DB}
|
||||
steps.s6OwnerRemoveColumns = &OwnerRemoveColumns{dbClient: dbClient.DB}
|
||||
@@ -573,9 +574,7 @@ func startCommandsQueries(
|
||||
config.OIDC.DefaultRefreshTokenExpiration,
|
||||
config.OIDC.DefaultRefreshTokenIdleExpiration,
|
||||
config.DefaultInstance.SecretGenerators,
|
||||
|
||||
nil,
|
||||
nil,
|
||||
config.Login.DefaultPaths,
|
||||
)
|
||||
logging.OnError(ctx, err).Fatal("unable to start commands")
|
||||
|
||||
@@ -599,7 +598,7 @@ func startCommandsQueries(
|
||||
commands,
|
||||
queries,
|
||||
eventstoreClient,
|
||||
config.Login.DefaultPaths.OTPEmailPath,
|
||||
config.Login.DefaultPaths.DefaultOTPEmailURLTemplate,
|
||||
config.SystemDefaults.Notifications.FileSystemPath,
|
||||
keys.User,
|
||||
keys.SMTP,
|
||||
|
||||
@@ -151,6 +151,7 @@ func readConfig(v *viper.Viper) (*Config, error) {
|
||||
hooks.SliceTypeStringDecode[*command.SetQuota],
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
hook.StringToURLHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
)
|
||||
|
||||
func Test_readConfig(t *testing.T) {
|
||||
@@ -84,12 +86,12 @@ Actions:
|
||||
DenyList: []
|
||||
`},
|
||||
want: func(t *testing.T, config *Config) {
|
||||
assert.Equal(t, config.DefaultInstance.Features, &command.InstanceSetupFeatures{
|
||||
assert.Equal(t, config.DefaultInstance.Features, &command.InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
LoginV2: &command.InstanceSetupFeatureLoginV2{
|
||||
LoginV2: &feature.LoginV2{
|
||||
Required: true,
|
||||
BaseURI: gu.Ptr("http://zitadel:8080"),
|
||||
BaseURI: &url.URL{Scheme: "http", Host: "zitadel:8080"},
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
+2
-3
@@ -288,8 +288,7 @@ func startZitadel(ctx context.Context, config *Config, masterKey string, server
|
||||
config.OIDC.DefaultRefreshTokenExpiration,
|
||||
config.OIDC.DefaultRefreshTokenIdleExpiration,
|
||||
config.DefaultInstance.SecretGenerators,
|
||||
config.Login.DefaultEmailCodeURLTemplate,
|
||||
config.Login.DefaultPasswordSetURLTemplate,
|
||||
config.Login.DefaultPaths,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start commands: %w", err)
|
||||
@@ -329,7 +328,7 @@ func startZitadel(ctx context.Context, config *Config, masterKey string, server
|
||||
commands,
|
||||
queries,
|
||||
eventstoreClient,
|
||||
config.Login.DefaultPaths.OTPEmailPath,
|
||||
config.Login.DefaultPaths.DefaultOTPEmailURLTemplate,
|
||||
config.SystemDefaults.Notifications.FileSystemPath,
|
||||
keys.User,
|
||||
keys.SMTP,
|
||||
|
||||
@@ -61,11 +61,16 @@ func (r *DomainCtx) RequestedDomain() string {
|
||||
// Origin returns the origin (protocol://hostname[:port]) for which the request was handled.
|
||||
// The instance host is used if no public host was set.
|
||||
func (r *DomainCtx) Origin() string {
|
||||
host := r.PublicHost
|
||||
if host == "" {
|
||||
host = r.InstanceHost
|
||||
return fmt.Sprintf("%s://%s", r.Protocol, r.RequestedHost())
|
||||
}
|
||||
|
||||
// OriginURL returns the origin (protocol://hostname[:port]) for which the request was handled as [*url.URL].
|
||||
// The instance host is used if no public host was set.
|
||||
func (r *DomainCtx) OriginURL() *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: r.Protocol,
|
||||
Host: r.RequestedHost(),
|
||||
}
|
||||
return fmt.Sprintf("%s://%s", r.Protocol, host)
|
||||
}
|
||||
|
||||
var _ slog.LogValuer = (*DomainCtx)(nil)
|
||||
|
||||
+108
-21
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -56,39 +57,125 @@ type Config struct {
|
||||
}
|
||||
|
||||
type DefaultPaths struct {
|
||||
BasePath string
|
||||
PasswordSetPath string
|
||||
EmailCodePath string
|
||||
OTPEmailPath string
|
||||
BasePath *url.URL
|
||||
PasswordSetPath *url.URL
|
||||
EmailCodePath *url.URL
|
||||
OTPEmailPath *url.URL
|
||||
PasskeySetPath *url.URL
|
||||
DomainClaimedPath *url.URL
|
||||
}
|
||||
|
||||
func (c *Config) defaultBaseURL(ctx context.Context) string {
|
||||
func (c *DefaultPaths) defaultBaseURL(ctx context.Context) *url.URL {
|
||||
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
|
||||
// In case login v1 is still active, we don't want to return a base URL, as the templates will not be used
|
||||
if !loginV2.Required {
|
||||
return nil
|
||||
}
|
||||
return ""
|
||||
origin := http_utils.DomainContext(ctx).OriginURL()
|
||||
if loginV2.BaseURI == nil || loginV2.BaseURI.String() == "" {
|
||||
// In case the login v2 is enabled without a custom BaseURI,
|
||||
// we use the request origin plus the default base path as the base URL for the templates.
|
||||
if c.BasePath == nil {
|
||||
return origin
|
||||
}
|
||||
return origin.ResolveReference(c.BasePath)
|
||||
}
|
||||
// In case a custom BaseURI is set for login v2, we use it as the base URL for the templates.
|
||||
if loginV2.BaseURI.IsAbs() {
|
||||
return loginV2.BaseURI
|
||||
}
|
||||
// If the custom BaseURI is a relative URL, we join the request origin with the custom BaseURI to form the base URL for the templates.
|
||||
return origin.ResolveReference(loginV2.BaseURI)
|
||||
}
|
||||
|
||||
func (c *Config) DefaultEmailCodeURLTemplate(ctx context.Context) string {
|
||||
basePath := c.defaultBaseURL(ctx)
|
||||
if basePath == "" {
|
||||
// mergeURLs will merge two URLs, where the path is joined and query parameters are combined.
|
||||
// It uses (*url.URL).JoinPath on the base URL and the second URL's Path field to build the resulting path,
|
||||
// and then merges the query parameters from both URLs using [mergeQueries], preserving existing values and placeholders.
|
||||
// Fragments are not modified or combined by this function.
|
||||
// For example, merging "https://example.com/base" with a URL whose path is "/path/to/resource" will result in "https://example.com/base/path/to/resource".
|
||||
func mergeURLs(base, path *url.URL) string {
|
||||
if base == nil && path == nil {
|
||||
return ""
|
||||
}
|
||||
return basePath + c.DefaultPaths.EmailCodePath
|
||||
if base == nil {
|
||||
return path.String()
|
||||
}
|
||||
if path == nil {
|
||||
return base.String()
|
||||
}
|
||||
u := base.JoinPath(path.Path)
|
||||
u.RawQuery = mergeQueries(u.Query(), path.Query())
|
||||
return u.String()
|
||||
}
|
||||
func (c *Config) DefaultPasswordSetURLTemplate(ctx context.Context) string {
|
||||
|
||||
// mergeQueries will merge two sets of query parameters, preserving existing values and placeholders.
|
||||
// It takes two url.Values, where the first one is considered the base and the second one is merged into it.
|
||||
// The resulting query string will contain all unique key-value pairs from both sets of query parameters.
|
||||
// If a value contains placeholders in the format "{{placeholder}}", they are preserved in the final query string without being URL-encoded.
|
||||
func mergeQueries(base, path url.Values) string {
|
||||
placeholders := map[string]string{}
|
||||
for _, value := range base {
|
||||
for _, v := range value {
|
||||
if strings.Contains(v, "{{") && strings.Contains(v, "}}") {
|
||||
placeholders[url.QueryEscape(v)] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
for key, value := range path {
|
||||
baseValues, ok := base[key]
|
||||
for _, v := range value {
|
||||
if !ok || !slices.Contains(baseValues, v) {
|
||||
base.Add(key, v)
|
||||
}
|
||||
if strings.Contains(v, "{{") && strings.Contains(v, "}}") {
|
||||
placeholders[url.QueryEscape(v)] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
raw := base.Encode()
|
||||
for esc, orig := range placeholders {
|
||||
raw = strings.ReplaceAll(raw, esc, orig)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func (c *DefaultPaths) DefaultEmailCodeURLTemplate(ctx context.Context) string {
|
||||
basePath := c.defaultBaseURL(ctx)
|
||||
if basePath == "" {
|
||||
if basePath == nil {
|
||||
return ""
|
||||
}
|
||||
return c.defaultBaseURL(ctx) + c.DefaultPaths.PasswordSetPath
|
||||
return mergeURLs(basePath, c.EmailCodePath)
|
||||
}
|
||||
|
||||
func (c *DefaultPaths) DefaultPasswordSetURLTemplate(ctx context.Context) string {
|
||||
basePath := c.defaultBaseURL(ctx)
|
||||
if basePath == nil {
|
||||
return ""
|
||||
}
|
||||
return mergeURLs(basePath, c.PasswordSetPath)
|
||||
}
|
||||
|
||||
func (c *DefaultPaths) DefaultPasskeySetURLTemplate(ctx context.Context) string {
|
||||
basePath := c.defaultBaseURL(ctx)
|
||||
if basePath == nil {
|
||||
return ""
|
||||
}
|
||||
return mergeURLs(basePath, c.PasskeySetPath)
|
||||
}
|
||||
|
||||
func (c *DefaultPaths) DefaultDomainClaimedURLTemplate(ctx context.Context) string {
|
||||
basePath := c.defaultBaseURL(ctx)
|
||||
if basePath == nil {
|
||||
return ""
|
||||
}
|
||||
return mergeURLs(basePath, c.DomainClaimedPath)
|
||||
}
|
||||
|
||||
func (c *DefaultPaths) DefaultOTPEmailURLTemplate(origin *url.URL) string {
|
||||
if c.BasePath == nil {
|
||||
return mergeURLs(origin, c.OTPEmailPath)
|
||||
}
|
||||
return mergeURLs(origin.ResolveReference(c.BasePath), c.OTPEmailPath)
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -16,12 +16,10 @@ import (
|
||||
func TestConfig_defaultBaseURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
config := &Config{
|
||||
DefaultPaths: &DefaultPaths{BasePath: "/basepath"},
|
||||
}
|
||||
config := &DefaultPaths{BasePath: &url.URL{Path: "/basepath"}}
|
||||
|
||||
baseCustomURI, err := url.Parse("https://custom")
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
@@ -45,15 +43,14 @@ func TestConfig_defaultBaseURL(t *testing.T) {
|
||||
expected: "https://origin/basepath",
|
||||
},
|
||||
{
|
||||
name: "LoginV2 required, custom BaseURI",
|
||||
name: "LoginV2 required, absolute 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",
|
||||
expected: "https://custom",
|
||||
},
|
||||
{
|
||||
name: "LoginV2 required, custom BaseURI empty string",
|
||||
@@ -65,13 +62,27 @@ func TestConfig_defaultBaseURL(t *testing.T) {
|
||||
),
|
||||
expected: "https://origin/basepath",
|
||||
},
|
||||
{
|
||||
name: "LoginV2 required, relative custom BaseURI",
|
||||
inputCtx: http.WithDomainContext(
|
||||
authz.NewMockContext("instance1", "org1", "user1",
|
||||
authz.WithMockFeatures(feature.Features{LoginV2: feature.LoginV2{Required: true, BaseURI: &url.URL{Path: "/custom"}}}),
|
||||
),
|
||||
&http.DomainCtx{Protocol: "https", PublicHost: "origin"},
|
||||
),
|
||||
expected: "https://origin/custom",
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
if tc.expected == "" {
|
||||
assert.Nil(t, result)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tc.expected, result.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -111,10 +122,9 @@ func TestConfig_DefaultEmailCodeURLTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given
|
||||
c := &Config{
|
||||
DefaultPaths: &DefaultPaths{
|
||||
BasePath: "/basepath",
|
||||
EmailCodePath: "/email-code-path"},
|
||||
c := &DefaultPaths{
|
||||
BasePath: &url.URL{Path: "/basepath"},
|
||||
EmailCodePath: &url.URL{Path: "/email-code-path"},
|
||||
}
|
||||
|
||||
// Test
|
||||
@@ -161,11 +171,9 @@ func TestConfig_DefaultPasswordSetURLTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given
|
||||
c := &Config{
|
||||
DefaultPaths: &DefaultPaths{
|
||||
BasePath: "/basepath",
|
||||
PasswordSetPath: "/password-set-path",
|
||||
},
|
||||
c := &DefaultPaths{
|
||||
BasePath: &url.URL{Path: "/basepath"},
|
||||
PasswordSetPath: &url.URL{Path: "/password-set-path"},
|
||||
}
|
||||
|
||||
// Test
|
||||
@@ -176,3 +184,220 @@ func TestConfig_DefaultPasswordSetURLTemplate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_DefaultPasskeySetURLTemplate(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/passkey-set-path",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given
|
||||
c := &DefaultPaths{
|
||||
BasePath: &url.URL{Path: "/basepath"},
|
||||
PasskeySetPath: &url.URL{Path: "/passkey-set-path"},
|
||||
}
|
||||
|
||||
// Test
|
||||
res := c.DefaultPasskeySetURLTemplate(tc.inputCtx)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, tc.expectedEmailURLTemplate, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_DefaultDomainClaimedURLTemplate(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/loginname",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given
|
||||
c := &DefaultPaths{
|
||||
BasePath: &url.URL{Path: "/basepath"},
|
||||
DomainClaimedPath: &url.URL{Path: "/loginname"},
|
||||
}
|
||||
|
||||
// Test
|
||||
res := c.DefaultDomainClaimedURLTemplate(tc.inputCtx)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, tc.expectedEmailURLTemplate, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_mergeURLs(t *testing.T) {
|
||||
type args struct {
|
||||
base *url.URL
|
||||
path *url.URL
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "both base and path are nil, return empty string",
|
||||
args: args{
|
||||
base: nil,
|
||||
path: nil,
|
||||
},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "base is nil, path is set, return path",
|
||||
args: args{
|
||||
base: nil,
|
||||
path: &url.URL{Path: "/path"},
|
||||
},
|
||||
want: "/path",
|
||||
},
|
||||
{
|
||||
name: "base is set, path is nil, return base",
|
||||
args: args{
|
||||
base: &url.URL{Path: "/base"},
|
||||
path: nil,
|
||||
},
|
||||
want: "/base",
|
||||
},
|
||||
{
|
||||
name: "both base and path are set, return merged path",
|
||||
args: args{
|
||||
base: &url.URL{Path: "/base"},
|
||||
path: &url.URL{Path: "/path"},
|
||||
},
|
||||
want: "/base/path",
|
||||
},
|
||||
{
|
||||
name: "both base and path are set incl. placeholders, return merged path",
|
||||
args: args{
|
||||
base: &url.URL{Path: "/base", RawQuery: "query={{.Placeholder}}"},
|
||||
path: &url.URL{Path: "/path", RawQuery: "query={{.Placeholder1}}&query2={{.Placeholder2}}"},
|
||||
},
|
||||
want: "/base/path?query={{.Placeholder}}&query={{.Placeholder1}}&query2={{.Placeholder2}}",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := mergeURLs(tt.args.base, tt.args.path)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_mergeQueries(t *testing.T) {
|
||||
type args struct {
|
||||
base url.Values
|
||||
path url.Values
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "both base and path are empty, return empty string",
|
||||
args: args{
|
||||
base: map[string][]string{},
|
||||
path: map[string][]string{},
|
||||
},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "base is empty, path is set, return path",
|
||||
args: args{
|
||||
base: map[string][]string{},
|
||||
path: map[string][]string{
|
||||
"query": {"{{.Placeholder1}}", "{{.Placeholder2}}"},
|
||||
},
|
||||
},
|
||||
want: "query={{.Placeholder1}}&query={{.Placeholder2}}",
|
||||
},
|
||||
{
|
||||
name: "base is set, path is empty, return base",
|
||||
args: args{
|
||||
base: map[string][]string{
|
||||
"query": {"{{.Placeholder}}"},
|
||||
},
|
||||
path: map[string][]string{},
|
||||
},
|
||||
want: "query={{.Placeholder}}",
|
||||
},
|
||||
{
|
||||
name: "both base and path are set, return merged query",
|
||||
args: args{
|
||||
base: map[string][]string{
|
||||
"query": {"{{.Placeholder}}"},
|
||||
"query2": {"{{.Placeholder}}"},
|
||||
},
|
||||
path: map[string][]string{
|
||||
"query": {"{{.Placeholder1}}"},
|
||||
"query2": {"{{.Placeholder}}", "{{.Placeholder2}}"},
|
||||
},
|
||||
},
|
||||
want: "query={{.Placeholder}}&query={{.Placeholder1}}&query2={{.Placeholder}}&query2={{.Placeholder2}}",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, mergeQueries(tt.args.base, tt.args.path), "mergeQueries(%v, %v)", tt.args.base, tt.args.path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+15
-12
@@ -98,11 +98,18 @@ type Commands struct {
|
||||
caches *Caches
|
||||
// Store instance IDs where all milestones are reached (except InstanceDeleted).
|
||||
// These instance's milestones never need to be invalidated,
|
||||
// so the query and cache overhead can completely eliminated.
|
||||
// so the query and cache overhead can be completely eliminated.
|
||||
milestonesCompleted sync.Map
|
||||
|
||||
defaultEmailCodeURLTemplate func(ctx context.Context) string
|
||||
defaultPasswordSetURLTemplate func(ctx context.Context) string
|
||||
loginPaths LoginPaths
|
||||
}
|
||||
|
||||
//go:generate mockgen -package command -destination ./mock_login_paths.go . LoginPaths
|
||||
type LoginPaths interface {
|
||||
DefaultEmailCodeURLTemplate(ctx context.Context) string
|
||||
DefaultPasswordSetURLTemplate(ctx context.Context) string
|
||||
DefaultPasskeySetURLTemplate(ctx context.Context) string
|
||||
DefaultDomainClaimedURLTemplate(ctx context.Context) string
|
||||
}
|
||||
|
||||
func StartCommands(
|
||||
@@ -120,12 +127,9 @@ func StartCommands(
|
||||
httpClient *http.Client,
|
||||
permissionCheck domain.PermissionCheck,
|
||||
sessionTokenVerifier func(ctx context.Context, sessionToken string, sessionID string, tokenID string) (err error),
|
||||
defaultAccessTokenLifetime,
|
||||
defaultRefreshTokenLifetime,
|
||||
defaultRefreshTokenIdleLifetime time.Duration,
|
||||
defaultAccessTokenLifetime, defaultRefreshTokenLifetime, defaultRefreshTokenIdleLifetime time.Duration,
|
||||
defaultSecretGenerators *SecretGenerators,
|
||||
defaultEmailCodeURLTemplate func(ctx context.Context) string,
|
||||
defaultPasswordSetURLTemplate func(ctx context.Context) string,
|
||||
loginPaths LoginPaths,
|
||||
) (repo *Commands, err error) {
|
||||
if externalDomain == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Df21s", "no external domain specified")
|
||||
@@ -211,10 +215,9 @@ func StartCommands(
|
||||
WithHyphen: defaults.Multifactors.RecoveryCodes.WithHyphen,
|
||||
},
|
||||
},
|
||||
GenerateDomain: domain.NewGeneratedInstanceDomain,
|
||||
caches: caches,
|
||||
defaultEmailCodeURLTemplate: defaultEmailCodeURLTemplate,
|
||||
defaultPasswordSetURLTemplate: defaultPasswordSetURLTemplate,
|
||||
GenerateDomain: domain.NewGeneratedInstanceDomain,
|
||||
caches: caches,
|
||||
loginPaths: loginPaths,
|
||||
}
|
||||
|
||||
if defaultSecretGenerators != nil && defaultSecretGenerators.ClientSecret != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
@@ -144,3 +145,39 @@ func TestCommands_asyncPush(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func expectLoginPathsNoCall(t *testing.T) LoginPaths {
|
||||
return NewMockLoginPaths(gomock.NewController(t))
|
||||
}
|
||||
|
||||
func expectLoginPathsDefaultEmailCodeURLTemplate(tmpl string) func(t *testing.T) LoginPaths {
|
||||
return func(t *testing.T) LoginPaths {
|
||||
m := NewMockLoginPaths(gomock.NewController(t))
|
||||
m.EXPECT().DefaultEmailCodeURLTemplate(gomock.Any()).Return(tmpl)
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
func expectLoginPathsDefaultDomainClaimedURLTemplate(tmpl string) func(t *testing.T) LoginPaths {
|
||||
return func(t *testing.T) LoginPaths {
|
||||
m := NewMockLoginPaths(gomock.NewController(t))
|
||||
m.EXPECT().DefaultDomainClaimedURLTemplate(gomock.Any()).Return(tmpl)
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
func expectLoginPathsDefaultPasswordSetURLTemplate(tmpl string) func(t *testing.T) LoginPaths {
|
||||
return func(t *testing.T) LoginPaths {
|
||||
m := NewMockLoginPaths(gomock.NewController(t))
|
||||
m.EXPECT().DefaultPasswordSetURLTemplate(gomock.Any()).Return(tmpl)
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
func expectLoginPathsDefaultPasskeySetURLTemplate(tmpl string) func(t *testing.T) LoginPaths {
|
||||
return func(t *testing.T) LoginPaths {
|
||||
m := NewMockLoginPaths(gomock.NewController(t))
|
||||
m.EXPECT().DefaultPasskeySetURLTemplate(gomock.Any()).Return(tmpl)
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
|
||||
@@ -124,57 +122,12 @@ type InstanceSetup struct {
|
||||
SMTPConfiguration *SMTPConfiguration
|
||||
OIDCSettings *OIDCSettings
|
||||
Quotas *SetQuotas
|
||||
Features *InstanceSetupFeatures
|
||||
Features *InstanceFeatures
|
||||
Limits *SetLimits
|
||||
Restrictions *SetRestrictions
|
||||
RolePermissionMappings []authz.RoleMapping
|
||||
}
|
||||
|
||||
type InstanceSetupFeatures struct {
|
||||
LoginDefaultOrg *bool
|
||||
UserSchema *bool
|
||||
ImprovedPerformance []feature.ImprovedPerformanceType
|
||||
DebugOIDCParentError *bool
|
||||
OIDCSingleV1SessionTermination *bool
|
||||
LoginV2 *InstanceSetupFeatureLoginV2
|
||||
PermissionCheckV2 *bool
|
||||
ManagementConsoleUseV2UserApi *bool
|
||||
EnableRelationalTables *bool
|
||||
}
|
||||
|
||||
type InstanceSetupFeatureLoginV2 struct {
|
||||
Required bool `json:"required,omitempty"`
|
||||
BaseURI *string `json:"base_uri,omitempty"`
|
||||
}
|
||||
|
||||
func (f *InstanceSetupFeatures) ToInstanceFeatures() (_ *InstanceFeatures, err error) {
|
||||
if f == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var loginV2 *feature.LoginV2
|
||||
if f.LoginV2 != nil {
|
||||
loginV2 = &feature.LoginV2{Required: f.LoginV2.Required}
|
||||
if f.LoginV2.BaseURI != nil {
|
||||
loginV2.BaseURI, err = url.Parse(*f.LoginV2.BaseURI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &InstanceFeatures{
|
||||
LoginDefaultOrg: f.LoginDefaultOrg,
|
||||
UserSchema: f.UserSchema,
|
||||
ImprovedPerformance: f.ImprovedPerformance,
|
||||
DebugOIDCParentError: f.DebugOIDCParentError,
|
||||
OIDCSingleV1SessionTermination: f.OIDCSingleV1SessionTermination,
|
||||
LoginV2: loginV2,
|
||||
PermissionCheckV2: f.PermissionCheckV2,
|
||||
ManagementConsoleUseV2UserApi: f.ManagementConsoleUseV2UserApi,
|
||||
EnableRelationalTables: f.EnableRelationalTables,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type SMTPConfiguration struct {
|
||||
Description string
|
||||
SMTP smtp.SMTP
|
||||
@@ -468,7 +421,7 @@ func setupQuotas(commands *Commands, validations *[]preparation.Validation, setQ
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupFeatures(validations *[]preparation.Validation, features *InstanceSetupFeatures, instanceID string) {
|
||||
func setupFeatures(validations *[]preparation.Validation, features *InstanceFeatures, instanceID string) {
|
||||
if features != nil {
|
||||
*validations = append(*validations, prepareSetFeatures(instanceID, features))
|
||||
}
|
||||
|
||||
@@ -56,16 +56,11 @@ func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures)
|
||||
return pushedEventsToObjectDetails(events), nil
|
||||
}
|
||||
|
||||
func prepareSetFeatures(instanceID string, f *InstanceSetupFeatures) preparation.Validation {
|
||||
func prepareSetFeatures(instanceID string, f *InstanceFeatures) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
features, err := f.ToInstanceFeatures()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(ctx context.Context, _ preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
wm := NewInstanceFeaturesWriteModel(instanceID)
|
||||
return wm.setCommands(ctx, features), nil
|
||||
return wm.setCommands(ctx, f), nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,12 @@ package command
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
@@ -21,7 +18,6 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/feature"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
id_mock "github.com/zitadel/zitadel/internal/id/mock"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
@@ -1769,118 +1765,3 @@ func TestCommandSide_RemoveInstance(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstanceSetupFeatures_ToInstanceFeatures(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type fields struct {
|
||||
LoginDefaultOrg *bool
|
||||
UserSchema *bool
|
||||
ImprovedPerformance []feature.ImprovedPerformanceType
|
||||
DebugOIDCParentError *bool
|
||||
OIDCSingleV1SessionTermination *bool
|
||||
LoginV2 *InstanceSetupFeatureLoginV2
|
||||
PermissionCheckV2 *bool
|
||||
ManagementConsoleUseV2UserApi *bool
|
||||
EnableRelationalTables *bool
|
||||
}
|
||||
|
||||
correctlyParsedURI, err := url.Parse("https://example.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want *InstanceFeatures
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "nil features returns nil",
|
||||
fields: fields{},
|
||||
want: &InstanceFeatures{},
|
||||
},
|
||||
{
|
||||
name: "all fields no login v2",
|
||||
fields: fields{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(false),
|
||||
ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgDomainVerified},
|
||||
DebugOIDCParentError: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(false),
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
ManagementConsoleUseV2UserApi: gu.Ptr(false),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
},
|
||||
want: &InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(false),
|
||||
ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgDomainVerified},
|
||||
DebugOIDCParentError: gu.Ptr(true),
|
||||
OIDCSingleV1SessionTermination: gu.Ptr(false),
|
||||
LoginV2: nil,
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
ManagementConsoleUseV2UserApi: gu.Ptr(false),
|
||||
EnableRelationalTables: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with login v2 no base uri",
|
||||
fields: fields{
|
||||
LoginV2: &InstanceSetupFeatureLoginV2{
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
want: &InstanceFeatures{
|
||||
LoginV2: &feature.LoginV2{
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with login v2 valid base uri",
|
||||
fields: fields{
|
||||
LoginV2: &InstanceSetupFeatureLoginV2{
|
||||
Required: true,
|
||||
BaseURI: gu.Ptr("https://example.com"),
|
||||
},
|
||||
},
|
||||
want: &InstanceFeatures{
|
||||
LoginV2: &feature.LoginV2{
|
||||
Required: true,
|
||||
BaseURI: correctlyParsedURI,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with login v2 invalid base uri",
|
||||
fields: fields{
|
||||
LoginV2: &InstanceSetupFeatureLoginV2{
|
||||
Required: true,
|
||||
BaseURI: gu.Ptr("://invalid"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := &InstanceSetupFeatures{
|
||||
LoginDefaultOrg: tc.fields.LoginDefaultOrg,
|
||||
UserSchema: tc.fields.UserSchema,
|
||||
ImprovedPerformance: tc.fields.ImprovedPerformance,
|
||||
DebugOIDCParentError: tc.fields.DebugOIDCParentError,
|
||||
OIDCSingleV1SessionTermination: tc.fields.OIDCSingleV1SessionTermination,
|
||||
LoginV2: tc.fields.LoginV2,
|
||||
PermissionCheckV2: tc.fields.PermissionCheckV2,
|
||||
ManagementConsoleUseV2UserApi: tc.fields.ManagementConsoleUseV2UserApi,
|
||||
EnableRelationalTables: tc.fields.EnableRelationalTables,
|
||||
}
|
||||
got, err := f.ToInstanceFeatures()
|
||||
|
||||
require.Equal(t, tc.wantErr, err != nil)
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/zitadel/zitadel/internal/command (interfaces: LoginPaths)
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -package command -destination ./mock_login_paths.go . LoginPaths
|
||||
//
|
||||
|
||||
// Package command is a generated GoMock package.
|
||||
package command
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockLoginPaths is a mock of LoginPaths interface.
|
||||
type MockLoginPaths struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockLoginPathsMockRecorder
|
||||
}
|
||||
|
||||
// MockLoginPathsMockRecorder is the mock recorder for MockLoginPaths.
|
||||
type MockLoginPathsMockRecorder struct {
|
||||
mock *MockLoginPaths
|
||||
}
|
||||
|
||||
// NewMockLoginPaths creates a new mock instance.
|
||||
func NewMockLoginPaths(ctrl *gomock.Controller) *MockLoginPaths {
|
||||
mock := &MockLoginPaths{ctrl: ctrl}
|
||||
mock.recorder = &MockLoginPathsMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockLoginPaths) EXPECT() *MockLoginPathsMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// DefaultDomainClaimedURLTemplate mocks base method.
|
||||
func (m *MockLoginPaths) DefaultDomainClaimedURLTemplate(arg0 context.Context) string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DefaultDomainClaimedURLTemplate", arg0)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DefaultDomainClaimedURLTemplate indicates an expected call of DefaultDomainClaimedURLTemplate.
|
||||
func (mr *MockLoginPathsMockRecorder) DefaultDomainClaimedURLTemplate(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultDomainClaimedURLTemplate", reflect.TypeOf((*MockLoginPaths)(nil).DefaultDomainClaimedURLTemplate), arg0)
|
||||
}
|
||||
|
||||
// DefaultEmailCodeURLTemplate mocks base method.
|
||||
func (m *MockLoginPaths) DefaultEmailCodeURLTemplate(arg0 context.Context) string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DefaultEmailCodeURLTemplate", arg0)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DefaultEmailCodeURLTemplate indicates an expected call of DefaultEmailCodeURLTemplate.
|
||||
func (mr *MockLoginPathsMockRecorder) DefaultEmailCodeURLTemplate(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultEmailCodeURLTemplate", reflect.TypeOf((*MockLoginPaths)(nil).DefaultEmailCodeURLTemplate), arg0)
|
||||
}
|
||||
|
||||
// DefaultPasskeySetURLTemplate mocks base method.
|
||||
func (m *MockLoginPaths) DefaultPasskeySetURLTemplate(arg0 context.Context) string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DefaultPasskeySetURLTemplate", arg0)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DefaultPasskeySetURLTemplate indicates an expected call of DefaultPasskeySetURLTemplate.
|
||||
func (mr *MockLoginPathsMockRecorder) DefaultPasskeySetURLTemplate(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultPasskeySetURLTemplate", reflect.TypeOf((*MockLoginPaths)(nil).DefaultPasskeySetURLTemplate), arg0)
|
||||
}
|
||||
|
||||
// DefaultPasswordSetURLTemplate mocks base method.
|
||||
func (m *MockLoginPaths) DefaultPasswordSetURLTemplate(arg0 context.Context) string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DefaultPasswordSetURLTemplate", arg0)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DefaultPasswordSetURLTemplate indicates an expected call of DefaultPasswordSetURLTemplate.
|
||||
func (mr *MockLoginPathsMockRecorder) DefaultPasswordSetURLTemplate(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultPasswordSetURLTemplate", reflect.TypeOf((*MockLoginPaths)(nil).DefaultPasswordSetURLTemplate), arg0)
|
||||
}
|
||||
@@ -29,6 +29,7 @@ func TestAddDomain(t *testing.T) {
|
||||
idGenerator id.Generator
|
||||
filter preparation.FilterToQueryReducer
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
permissionCheck OrganizationPermissionCheck
|
||||
}
|
||||
|
||||
@@ -45,6 +46,7 @@ func TestAddDomain(t *testing.T) {
|
||||
a: agg,
|
||||
domain: "",
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: zerrors.ThrowInvalidArgument(nil, "ORG-r3h4J", "Errors.Invalid.Argument"),
|
||||
@@ -69,6 +71,7 @@ func TestAddDomain(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
@@ -118,12 +121,13 @@ func TestAddDomain(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsDefaultDomainClaimedURLTemplate(""),
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
org.NewDomainAddedEvent(context.Background(), &agg.Aggregate, "domain"),
|
||||
org.NewDomainVerifiedEvent(context.Background(), &agg.Aggregate, "domain"),
|
||||
user.NewDomainClaimedEvent(http.WithRequestedHost(context.Background(), "domain"), &user.NewAggregate("userID1", "org2").Aggregate, "[email protected]", "username", false),
|
||||
user.NewDomainClaimedEvent(http.WithRequestedHost(context.Background(), "domain"), &user.NewAggregate("userID1", "org2").Aggregate, "[email protected]", "username", false, ""),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -148,6 +152,7 @@ func TestAddDomain(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
want: Want{
|
||||
CreateErr: zerrors.ThrowAlreadyExists(nil, "", ""),
|
||||
@@ -161,6 +166,7 @@ func TestAddDomain(t *testing.T) {
|
||||
claimedUserIDs: []string{"userID1"},
|
||||
filter: nil,
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
|
||||
},
|
||||
want: Want{
|
||||
@@ -186,6 +192,7 @@ func TestAddDomain(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
|
||||
},
|
||||
want: Want{
|
||||
@@ -200,7 +207,7 @@ func TestAddDomain(t *testing.T) {
|
||||
AssertValidation(
|
||||
t,
|
||||
http.WithRequestedHost(context.Background(), "domain"),
|
||||
(&Commands{idGenerator: tt.args.idGenerator, eventstore: tt.args.eventstore(t)}).prepareAddOrgDomain(tt.args.a, tt.args.domain, tt.args.claimedUserIDs, tt.args.permissionCheck),
|
||||
(&Commands{idGenerator: tt.args.idGenerator, eventstore: tt.args.eventstore(t), loginPaths: tt.args.loginPaths(t)}).prepareAddOrgDomain(tt.args.a, tt.args.domain, tt.args.claimedUserIDs, tt.args.permissionCheck),
|
||||
tt.args.filter,
|
||||
tt.want,
|
||||
)
|
||||
@@ -879,6 +886,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
secretGenerator crypto.Generator
|
||||
alg crypto.EncryptionAlgorithm
|
||||
domainValidationFunc func(domain, token, verifier string, checkType http.CheckType) error
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -900,6 +908,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
name: "invalid domain, error",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -917,6 +926,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
name: "missing aggregateid, error",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -941,6 +951,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -981,6 +992,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1015,6 +1027,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1070,6 +1083,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
domainValidationFunc: invalidDomainVerification,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1125,6 +1139,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
domainValidationFunc: validDomainVerification,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1183,6 +1198,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
domainValidationFunc: validDomainVerification,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1264,12 +1280,14 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
"[email protected]",
|
||||
"[email protected]",
|
||||
false,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
domainValidationFunc: validDomainVerification,
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "tempid"),
|
||||
loginPaths: expectLoginPathsDefaultDomainClaimedURLTemplate(""),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1351,12 +1369,14 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
"[email protected]",
|
||||
"[email protected]",
|
||||
true,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
domainValidationFunc: validDomainVerification,
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "tempid"),
|
||||
loginPaths: expectLoginPathsDefaultDomainClaimedURLTemplate(""),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1415,6 +1435,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
domainValidationFunc: validDomainVerification,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1439,6 +1460,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
eventstore: expectEventstore(),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
domainValidationFunc: validDomainVerification,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1464,6 +1486,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
domainVerificationAlg: tt.fields.alg,
|
||||
domainVerificationValidator: tt.fields.domainValidationFunc,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := r.ValidateOrgDomain(http.WithRequestedHost(tt.args.ctx, "zitadel.ch"), tt.args.domain, tt.args.claimedUserIDs, tt.args.permissionCheck)
|
||||
if tt.res.err == nil {
|
||||
|
||||
@@ -303,6 +303,7 @@ func (c *Commands) userDomainClaimed(ctx context.Context, userID string) (events
|
||||
fmt.Sprintf("%s@temporary.%s", id, http_util.DomainContext(ctx).RequestedDomain()),
|
||||
existingUser.UserName,
|
||||
domainPolicy.UserLoginMustBeDomain || organizationScopedUsername,
|
||||
c.loginPaths.DefaultDomainClaimedURLTemplate(ctx),
|
||||
),
|
||||
}, changedUserGrant, nil
|
||||
}
|
||||
@@ -338,6 +339,7 @@ func (c *Commands) prepareUserDomainClaimed(ctx context.Context, filter preparat
|
||||
fmt.Sprintf("%s@temporary.%s", id, http_util.DomainContext(ctx).RequestedDomain()),
|
||||
userWriteModel.UserName,
|
||||
domainPolicy.UserLoginMustBeDomain || organizationScopedUsername,
|
||||
c.loginPaths.DefaultDomainClaimedURLTemplate(ctx),
|
||||
), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -306,10 +306,10 @@ func (c *Commands) addHumanCommandEmail(ctx context.Context, filter preparation.
|
||||
}
|
||||
|
||||
if human.Email.URLTemplate == "" {
|
||||
human.Email.URLTemplate = c.defaultEmailCodeURLTemplate(ctx)
|
||||
human.Email.URLTemplate = c.loginPaths.DefaultEmailCodeURLTemplate(ctx)
|
||||
}
|
||||
|
||||
return append(cmds, user.NewHumanEmailCodeAddedEventV2(ctx, &a.Aggregate, emailCode.Crypted, emailCode.Expiry, human.Email.URLTemplate, human.Email.ReturnCode, human.AuthRequestID)), nil
|
||||
return append(cmds, user.NewHumanEmailCodeAddedEvent(ctx, &a.Aggregate, emailCode.Crypted, emailCode.Expiry, human.Email.URLTemplate, human.Email.ReturnCode, human.AuthRequestID)), nil
|
||||
}
|
||||
return cmds, nil
|
||||
}
|
||||
@@ -599,7 +599,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, ""))
|
||||
events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, c.loginPaths.DefaultEmailCodeURLTemplate(ctx), false, ""))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email, em
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, ""))
|
||||
events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, c.loginPaths.DefaultEmailCodeURLTemplate(ctx), false, ""))
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
@@ -134,7 +134,7 @@ func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID,
|
||||
if authRequestID == "" {
|
||||
authRequestID = existingEmail.AuthRequestID
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, authRequestID))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, c.loginPaths.DefaultEmailCodeURLTemplate(ctx), false, authRequestID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -41,6 +42,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
name: "invalid email, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -61,6 +63,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -104,6 +107,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -140,6 +144,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -185,6 +190,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -234,6 +240,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -283,6 +290,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -341,9 +349,75 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
false,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate(""),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
email: &domain.Email{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "user1",
|
||||
},
|
||||
EmailAddress: "[email protected]",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Email{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "user1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
EmailAddress: "[email protected]",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "email changed with code, ok",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"[email protected]",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"[email protected]",
|
||||
),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"http://example.com/{{.user}}/email/{{.code}}",
|
||||
false,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -371,6 +445,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := r.ChangeHumanEmail(tt.args.ctx, tt.args.email, tt.args.secretGenerator)
|
||||
if tt.res.err == nil {
|
||||
@@ -389,6 +464,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
|
||||
func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
userPasswordHasher *crypto.Hasher
|
||||
}
|
||||
type args struct {
|
||||
@@ -414,6 +490,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
name: "userid missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -428,6 +505,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
name: "code missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -444,6 +522,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -476,6 +555,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -517,6 +597,8 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
false,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -526,6 +608,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -568,6 +651,8 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
false,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -577,6 +662,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -621,6 +707,8 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
false,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -649,6 +737,84 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
code: "a",
|
||||
resourceOwner: "org1",
|
||||
optionalPassword: "password",
|
||||
optionalUserAgentID: "userAgentID",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid code (with password and user agent), url template, ok",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"[email protected]",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithCreationDateNow(
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"http://example.com/{{.user}}/email/{{.code}}",
|
||||
false,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
),
|
||||
user.NewHumanPasswordChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"$plain$x$password",
|
||||
false,
|
||||
"userAgentID",
|
||||
),
|
||||
),
|
||||
),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -671,6 +837,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := r.VerifyHumanEmail(
|
||||
tt.args.ctx,
|
||||
@@ -697,6 +864,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
|
||||
func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -719,6 +887,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||
name: "userid missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -734,6 +903,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -772,6 +942,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -808,6 +979,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -860,9 +1032,12 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
false,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate(""),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -917,10 +1092,75 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
false,
|
||||
"authRequestID",
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate(""),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
authRequestID: "authRequestID",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new code with authRequestID, url template, ok",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"[email protected]",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"[email protected]",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"http://example.com/{{.user}}/email/{{.code}}",
|
||||
false,
|
||||
"authRequestID",
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -940,6 +1180,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := r.CreateHumanEmailVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.secretGenerator, tt.args.authRequestID)
|
||||
if tt.res.err == nil {
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
newCode encrypedCodeFunc
|
||||
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
|
||||
defaultSecretGenerators *SecretGenerators
|
||||
defaultEmailCodeURLTemplate func(ctx context.Context) string
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -73,6 +73,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
name: "orgid missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -97,6 +98,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
name: "user invalid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -123,6 +125,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -155,6 +158,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
expectFilter(),
|
||||
expectFilter(),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -196,6 +200,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
expectFilter(),
|
||||
expectFilter(),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -264,6 +269,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -333,6 +339,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -403,6 +410,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -473,6 +481,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -541,11 +550,11 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
defaultEmailCodeURLTemplate: func(_ context.Context) string { return "" },
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -600,7 +609,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
expectPush(
|
||||
newAddHumanEvent("$plain$x$password", false, true, "", AllowedLanguage),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -619,6 +628,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -674,7 +684,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
expectPush(
|
||||
newAddHumanEvent("$plain$x$password", false, true, "", AllowedLanguage),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -689,11 +699,11 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
defaultEmailCodeURLTemplate: func(_ context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -755,6 +765,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -819,6 +830,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -883,6 +895,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -947,6 +960,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1000,6 +1014,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1086,6 +1101,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1195,6 +1211,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("phonecode", time.Hour),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1262,6 +1279,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1371,6 +1389,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("phoneCode", time.Hour),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1441,6 +1460,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1481,7 +1501,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
|
||||
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
|
||||
defaultEmailCodeURLTemplate: tt.fields.defaultEmailCodeURLTemplate,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
err := r.AddHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.allowInitMail)
|
||||
if tt.res.err == nil {
|
||||
|
||||
@@ -156,7 +156,7 @@ func (c *Commands) changeUserEmailWithGeneratorEvents(ctx context.Context, userI
|
||||
}
|
||||
|
||||
if urlTmpl == "" {
|
||||
urlTmpl = c.defaultEmailCodeURLTemplate(ctx)
|
||||
urlTmpl = c.loginPaths.DefaultEmailCodeURLTemplate(ctx)
|
||||
}
|
||||
|
||||
if err = cmd.AddGeneratedCode(ctx, gen, urlTmpl, returnCode); err != nil {
|
||||
@@ -178,7 +178,7 @@ func (c *Commands) sendUserEmailCodeWithGeneratorEvents(ctx context.Context, use
|
||||
}
|
||||
|
||||
if urlTmpl == "" {
|
||||
urlTmpl = c.defaultEmailCodeURLTemplate(ctx)
|
||||
urlTmpl = c.loginPaths.DefaultEmailCodeURLTemplate(ctx)
|
||||
}
|
||||
|
||||
if err = cmd.AddGeneratedCode(ctx, gen, urlTmpl, returnCode); err != nil {
|
||||
@@ -286,7 +286,7 @@ func generateCodeCommand(ctx context.Context, agg *eventstore.Aggregate, gen cry
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
cmd := user.NewHumanEmailCodeAddedEventV2(ctx, agg, value, gen.Expiry(), urlTmpl, returnCode, "")
|
||||
cmd := user.NewHumanEmailCodeAddedEvent(ctx, agg, value, gen.Expiry(), urlTmpl, returnCode, "")
|
||||
if returnCode {
|
||||
return cmd, plain, nil
|
||||
}
|
||||
|
||||
@@ -439,7 +439,7 @@ func TestCommands_ResendUserEmailCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -556,7 +556,7 @@ func TestCommands_SendUserEmailCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -647,7 +647,7 @@ func TestCommands_ResendUserEmailCodeURLTemplate(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -778,7 +778,7 @@ func TestCommands_SendUserEmailCodeURLTemplate(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -858,7 +858,7 @@ func TestCommands_ResendUserEmailReturnCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -975,7 +975,7 @@ func TestCommands_SendUserEmailReturnCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1160,9 +1160,9 @@ func TestCommands_ChangeUserEmailVerified(t *testing.T) {
|
||||
|
||||
func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
defaultEmailCodeURLTemplate func(ctx context.Context) string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
@@ -1180,7 +1180,8 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
{
|
||||
name: "missing user",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "",
|
||||
@@ -1193,8 +1194,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
{
|
||||
name: "missing permission",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -1213,6 +1213,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1225,8 +1226,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
{
|
||||
name: "missing email",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -1245,6 +1245,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1257,8 +1258,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
{
|
||||
name: "not changed",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -1277,6 +1277,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1289,8 +1290,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
{
|
||||
name: "email changed",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -1312,7 +1312,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"[email protected]",
|
||||
),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1326,8 +1326,8 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1347,8 +1347,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
{
|
||||
name: "email changed, return code",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -1370,7 +1369,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"[email protected]",
|
||||
),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1383,8 +1382,8 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code2}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code2}}"),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1405,8 +1404,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
{
|
||||
name: "email changed, URL template",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -1428,7 +1426,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"[email protected]",
|
||||
),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1442,6 +1440,7 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1462,9 +1461,9 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
defaultEmailCodeURLTemplate: tt.fields.defaultEmailCodeURLTemplate,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := c.changeUserEmailWithGenerator(context.Background(), tt.args.userID, tt.args.email, GetMockSecretGenerator(t), tt.args.returnCode, tt.args.urlTmpl)
|
||||
require.ErrorIs(t, tt.wantErr, err)
|
||||
@@ -1475,9 +1474,9 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
|
||||
func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
defaultEmailCodeURLTemplate func(ctx context.Context) string
|
||||
eventstore *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
@@ -1496,6 +1495,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
name: "missing user",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "",
|
||||
@@ -1525,7 +1525,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1540,6 +1540,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1570,7 +1571,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1583,8 +1584,8 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1622,7 +1623,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1636,7 +1637,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1649,8 +1650,8 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code2}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code2}}"),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1690,6 +1691,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1721,7 +1723,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1734,8 +1736,8 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1775,7 +1777,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -1789,6 +1791,7 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -1809,9 +1812,9 @@ func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
defaultEmailCodeURLTemplate: tt.fields.defaultEmailCodeURLTemplate,
|
||||
eventstore: tt.fields.eventstore,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := c.sendUserEmailCodeWithGenerator(context.Background(), tt.args.userID, GetMockSecretGenerator(t), tt.args.returnCode, tt.args.urlTmpl, tt.args.checkExisting)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
@@ -1924,7 +1927,7 @@ func TestCommands_VerifyUserEmail(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -2039,7 +2042,7 @@ func TestCommands_verifyUserEmailWithGenerator(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -2086,7 +2089,7 @@ func TestCommands_verifyUserEmailWithGenerator(t *testing.T) {
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithCreationDateNow(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
|
||||
@@ -413,10 +413,10 @@ func (c *Commands) changeUserEmail(ctx context.Context, cmds []eventstore.Comman
|
||||
return cmds, code, err
|
||||
}
|
||||
if email.URLTemplate == "" {
|
||||
email.URLTemplate = c.defaultEmailCodeURLTemplate(ctx)
|
||||
email.URLTemplate = c.loginPaths.DefaultEmailCodeURLTemplate(ctx)
|
||||
}
|
||||
|
||||
cmds = append(cmds, user.NewHumanEmailCodeAddedEventV2(ctx, &wm.Aggregate().Aggregate, cryptoCode.Crypted, cryptoCode.Expiry, email.URLTemplate, email.ReturnCode, ""))
|
||||
cmds = append(cmds, user.NewHumanEmailCodeAddedEvent(ctx, &wm.Aggregate().Aggregate, cryptoCode.Crypted, cryptoCode.Expiry, email.URLTemplate, email.ReturnCode, ""))
|
||||
if email.ReturnCode {
|
||||
code = &cryptoCode.Plain
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
|
||||
checkPermission domain.PermissionCheck
|
||||
defaultSecretGenerators *SecretGenerators
|
||||
defaultEmailCodeURLTemplate func(ctx context.Context) string
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -79,6 +79,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -104,6 +105,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -131,6 +133,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -163,6 +166,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
expectFilter(),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -205,6 +209,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
expectFilter(),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -274,6 +279,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -310,6 +316,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -376,6 +383,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -449,6 +457,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -492,7 +501,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectPush(
|
||||
newAddHumanEvent("", false, true, "", language.English),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -507,10 +516,10 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("emailverify", time.Hour),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("emailverify", time.Hour),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -565,7 +574,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
expectPush(
|
||||
newAddHumanEvent("$plain$x$password", false, true, "", language.English),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -584,6 +593,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -640,7 +650,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
expectPush(
|
||||
newAddHumanEvent("$plain$x$password", false, true, "", language.English),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -655,11 +665,11 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -725,6 +735,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -790,6 +801,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -855,6 +867,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -909,6 +922,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -996,6 +1010,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1106,6 +1121,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("phonecode", time.Hour),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1212,6 +1228,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("phonecode", time.Hour),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1280,6 +1297,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1390,6 +1408,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("phoneCode", time.Hour),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1461,6 +1480,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1522,7 +1542,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
expectPush(
|
||||
newRegisterHumanEvent("[email protected]", "", false, true, "", language.English),
|
||||
user.NewHumanEmailCodeAddedEventV2(
|
||||
user.NewHumanEmailCodeAddedEvent(
|
||||
context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
@@ -1545,10 +1565,10 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("mailVerify", time.Hour),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("mailVerify", time.Hour),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1629,6 +1649,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("mailVerify", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1717,6 +1738,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1801,6 +1823,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1858,6 +1881,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -1938,6 +1962,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2028,6 +2053,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2122,6 +2148,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2168,7 +2195,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
CryptoMFA: cryptoAlg,
|
||||
},
|
||||
},
|
||||
defaultEmailCodeURLTemplate: tt.fields.defaultEmailCodeURLTemplate,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
err := r.AddUserHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.allowInitMail, tt.args.codeAlg)
|
||||
if tt.res.err == nil {
|
||||
@@ -2206,7 +2233,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
|
||||
checkPermission domain.PermissionCheck
|
||||
defaultSecretGenerators *SecretGenerators
|
||||
defaultEmailCodeURLTemplate func(ctx context.Context) string
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
tarpit Tarpit
|
||||
}
|
||||
type args struct {
|
||||
@@ -2245,6 +2272,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2271,6 +2299,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2293,6 +2322,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2339,6 +2369,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2387,6 +2418,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2415,6 +2447,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2443,6 +2476,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2492,6 +2526,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2527,6 +2562,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2564,7 +2600,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
&userAgg.Aggregate,
|
||||
"[email protected]",
|
||||
),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -2579,10 +2615,10 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
tarpit: expectTarpit(0),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
tarpit: expectTarpit(0),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2614,6 +2650,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2644,6 +2681,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2682,6 +2720,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2718,6 +2757,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2750,7 +2790,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
&userAgg.Aggregate,
|
||||
"[email protected]",
|
||||
),
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -2765,10 +2805,10 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
defaultEmailCodeURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/email/{{.code}}" },
|
||||
tarpit: expectTarpit(0),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
loginPaths: expectLoginPathsDefaultEmailCodeURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
tarpit: expectTarpit(0),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2852,6 +2892,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("phoneCode", time.Hour),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2928,6 +2969,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("phoneCode", time.Hour),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2959,6 +3001,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -2997,6 +3040,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3033,6 +3077,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3113,6 +3158,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("phoneCode", time.Hour),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3141,6 +3187,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3175,6 +3222,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3224,6 +3272,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3249,6 +3298,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3284,6 +3334,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3339,6 +3390,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3397,6 +3449,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3452,6 +3505,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(1),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3524,6 +3578,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3579,6 +3634,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3644,6 +3700,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3703,6 +3760,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3778,6 +3836,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
tarpit: expectTarpit(0),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -3810,7 +3869,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
|
||||
userEncryption: tt.args.codeAlg,
|
||||
defaultEmailCodeURLTemplate: tt.fields.defaultEmailCodeURLTemplate,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
tarpit: tt.fields.tarpit.tarpit,
|
||||
}
|
||||
err := r.ChangeUserHuman(tt.args.ctx, tt.args.human, tt.args.codeAlg)
|
||||
|
||||
@@ -662,7 +662,7 @@ func TestCommandSide_userHumanWriteModel_email(t *testing.T) {
|
||||
newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -724,7 +724,7 @@ func TestCommandSide_userHumanWriteModel_email(t *testing.T) {
|
||||
newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -783,7 +783,7 @@ func TestCommandSide_userHumanWriteModel_email(t *testing.T) {
|
||||
newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
@@ -851,7 +851,7 @@ func TestCommandSide_userHumanWriteModel_email(t *testing.T) {
|
||||
newRegisterHumanEvent("username", "$plain$x$password", true, true, "", language.English),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanEmailCodeAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
|
||||
@@ -143,6 +143,10 @@ func (c *Commands) addUserPasskeyCode(ctx context.Context, userID, resourceOwner
|
||||
}
|
||||
agg := UserAggregateFromWriteModel(&wm.WriteModel)
|
||||
|
||||
if urlTmpl == "" {
|
||||
urlTmpl = c.loginPaths.DefaultPasskeySetURLTemplate(ctx)
|
||||
}
|
||||
|
||||
cmd := user.NewHumanPasswordlessInitCodeRequestedEvent(ctx, agg, codeID, code.Crypted, code.Expiry, urlTmpl, returnCode)
|
||||
err = c.pushAppendAndReduce(ctx, wm, cmd)
|
||||
if err != nil {
|
||||
|
||||
@@ -142,9 +142,9 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
||||
ExternalSecure: true,
|
||||
}
|
||||
alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t))
|
||||
es := eventstoreExpect(t,
|
||||
es := expectEventstore(
|
||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
||||
)
|
||||
)(t)
|
||||
code, err := newEncryptedCode(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) //nolint:staticcheck
|
||||
require.NoError(t, err)
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
@@ -238,9 +238,9 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
||||
func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
||||
ctx := http_util.WithRequestedHost(context.Background(), "example.com")
|
||||
alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t))
|
||||
es := eventstoreExpect(t,
|
||||
es := expectEventstore(
|
||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
||||
)
|
||||
)(t)
|
||||
code, err := newEncryptedCode(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) //nolint:staticcheck
|
||||
require.NoError(t, err)
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
@@ -436,14 +436,14 @@ func TestCommands_pushUserPasskey(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: eventstoreExpect(t, prep...),
|
||||
eventstore: expectEventstore(prep...)(t),
|
||||
webauthnConfig: webauthnConfig,
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
}
|
||||
wm, userAgg, webAuthN, err := c.createUserPasskey(ctx, "user1", "org1", "rpID", domain.AuthenticatorAttachmentCrossPlattform)
|
||||
require.NoError(t, err)
|
||||
|
||||
c.eventstore = eventstoreExpect(t, tt.expectPush(webAuthN.Challenge))
|
||||
c.eventstore = expectEventstore(tt.expectPush(webAuthN.Challenge))(t)
|
||||
|
||||
got, err := c.pushUserPasskey(ctx, wm, userAgg, webAuthN, tt.args.events...)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
@@ -463,6 +463,7 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
|
||||
newCode encrypedCodeFunc
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
@@ -481,6 +482,7 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
|
||||
newCode: mockEncryptedCode("passkey1", time.Hour),
|
||||
eventstore: expectEventstore(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -519,6 +521,49 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsDefaultPasskeySetURLTemplate(""),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success, url template",
|
||||
fields: fields{
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
userAgg,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"[email protected]",
|
||||
true,
|
||||
),
|
||||
)),
|
||||
expectPush(
|
||||
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
||||
userAgg,
|
||||
"123", &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("passkey1"),
|
||||
}, time.Minute, "http://example.com/{{.user}}/email/{{.code}}", false,
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsDefaultPasskeySetURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
},
|
||||
args: args{
|
||||
@@ -536,6 +581,7 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := c.AddUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
@@ -550,7 +596,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
|
||||
type fields struct {
|
||||
newCode encrypedCodeFunc
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
type args struct {
|
||||
@@ -569,7 +615,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
name: "template error",
|
||||
fields: fields{
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -582,7 +628,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
name: "id generator error",
|
||||
fields: fields{
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t),
|
||||
eventstore: expectEventstore(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe),
|
||||
},
|
||||
args: args{
|
||||
@@ -596,7 +642,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
name: "success",
|
||||
fields: fields{
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
userAgg,
|
||||
@@ -642,7 +688,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
got, err := c.AddUserPasskeyCodeURLTemplate(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, tt.args.urlTmpl)
|
||||
@@ -657,8 +703,9 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
type fields struct {
|
||||
newCode encrypedCodeFunc
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
@@ -675,8 +722,9 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
name: "id generator error",
|
||||
fields: fields{
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t),
|
||||
eventstore: expectEventstore(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -688,7 +736,7 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
name: "success",
|
||||
fields: fields{
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
userAgg,
|
||||
@@ -716,6 +764,53 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
loginPaths: expectLoginPathsDefaultPasskeySetURLTemplate(""),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
want: &domain.PasskeyCodeDetails{
|
||||
ObjectDetails: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
CodeID: "123",
|
||||
Code: "passkey1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success, url template",
|
||||
fields: fields{
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
userAgg,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"[email protected]",
|
||||
true,
|
||||
),
|
||||
)),
|
||||
expectPush(
|
||||
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
||||
userAgg,
|
||||
"123", &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("passkey1"),
|
||||
}, time.Minute, "http://example.com/{{.user}}/email/{{.code}}", true,
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
loginPaths: expectLoginPathsDefaultPasskeySetURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -734,8 +829,9 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := c.AddUserPasskeyCodeReturn(context.Background(), tt.args.userID, tt.args.resourceOwner, alg)
|
||||
if tt.wantErr != nil {
|
||||
@@ -754,8 +850,9 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
type fields struct {
|
||||
newCode encrypedCodeFunc
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
@@ -772,8 +869,9 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
name: "id generator error",
|
||||
fields: fields{
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t),
|
||||
eventstore: expectEventstore(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -785,8 +883,9 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
name: "crypto error",
|
||||
fields: fields{
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)),
|
||||
eventstore: expectEventstore(expectFilterError(io.ErrClosedPipe)),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -798,11 +897,12 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
name: "filter query error",
|
||||
fields: fields{
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
||||
expectFilterError(io.ErrClosedPipe),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -814,7 +914,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
name: "push error",
|
||||
fields: fields{
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
userAgg,
|
||||
@@ -842,6 +942,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
loginPaths: expectLoginPathsDefaultPasskeySetURLTemplate(""),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -853,7 +954,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
name: "success",
|
||||
fields: fields{
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: eventstoreExpect(t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
userAgg,
|
||||
@@ -881,6 +982,52 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
loginPaths: expectLoginPathsDefaultPasskeySetURLTemplate(""),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
want: &domain.PasskeyCodeDetails{
|
||||
ObjectDetails: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
CodeID: "123",
|
||||
Code: "passkey1",
|
||||
},
|
||||
}, {
|
||||
name: "success, url template",
|
||||
fields: fields{
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
userAgg,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"[email protected]",
|
||||
true,
|
||||
),
|
||||
)),
|
||||
expectPush(
|
||||
user.NewHumanPasswordlessInitCodeRequestedEvent(context.Background(),
|
||||
userAgg,
|
||||
"123", &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("passkey1"),
|
||||
}, time.Minute, "http://example.com/{{.user}}/email/{{.code}}", false,
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
loginPaths: expectLoginPathsDefaultPasskeySetURLTemplate("http://example.com/{{.user}}/email/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
@@ -899,8 +1046,9 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
got, err := c.addUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, "", false)
|
||||
if tt.wantErr != nil {
|
||||
|
||||
@@ -63,7 +63,7 @@ func (c *Commands) requestPasswordReset(ctx context.Context, userID string, retu
|
||||
return nil, nil, err
|
||||
}
|
||||
if urlTmpl == "" {
|
||||
urlTmpl = c.defaultPasswordSetURLTemplate(ctx)
|
||||
urlTmpl = c.loginPaths.DefaultPasswordSetURLTemplate(ctx)
|
||||
}
|
||||
cmd := user.NewHumanPasswordCodeAddedEventV2(ctx, UserAggregateFromWriteModelCtx(ctx, &model.WriteModel), passwordCode.CryptedCode(), passwordCode.CodeExpiry(), notificationType, urlTmpl, returnCode, generatorID)
|
||||
|
||||
|
||||
@@ -338,13 +338,13 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
},
|
||||
}
|
||||
type fields struct {
|
||||
checkPermission domain.PermissionCheck
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
userEncryption crypto.EncryptionAlgorithm
|
||||
newCode encrypedCodeFunc
|
||||
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
|
||||
defaultSecretGenerators *SecretGenerators
|
||||
defaultPasswordSetURLTemplate func(ctx context.Context) string
|
||||
checkPermission domain.PermissionCheck
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
userEncryption crypto.EncryptionAlgorithm
|
||||
newCode encrypedCodeFunc
|
||||
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
|
||||
defaultSecretGenerators *SecretGenerators
|
||||
loginPaths func(*testing.T) LoginPaths
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -368,6 +368,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
name: "missing userID",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -383,6 +384,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -408,6 +410,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -430,6 +433,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -466,9 +470,9 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
defaultPasswordSetURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/password/{{.code}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
loginPaths: expectLoginPathsDefaultPasswordSetURLTemplate("http://example.com/{{.user}}/password/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -510,6 +514,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -583,6 +588,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("code", 10*time.Minute),
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -652,6 +658,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
loginPaths: expectLoginPathsNoCall,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -693,9 +700,9 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
defaultPasswordSetURLTemplate: func(ctx context.Context) string { return "http://example.com/{{.user}}/password/{{.code}}" },
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
loginPaths: expectLoginPathsDefaultPasswordSetURLTemplate("http://example.com/{{.user}}/password/{{.code}}"),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -713,13 +720,13 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userEncryption: tt.fields.userEncryption,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
|
||||
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
|
||||
defaultPasswordSetURLTemplate: tt.fields.defaultPasswordSetURLTemplate,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userEncryption: tt.fields.userEncryption,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
|
||||
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
|
||||
loginPaths: tt.fields.loginPaths(t),
|
||||
}
|
||||
|
||||
got, gotPlainCode, err := c.requestPasswordReset(tt.args.ctx, tt.args.userID, tt.args.returnCode, tt.args.urlTmpl, tt.args.notificationType)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func StringToURLHookFunc() mapstructure.DecodeHookFuncType {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{},
|
||||
) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if t != reflect.TypeOf(&url.URL{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
u, err := url.Parse(data.(string))
|
||||
return u, err
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||
@@ -89,7 +90,7 @@ const (
|
||||
|
||||
type userNotifier struct {
|
||||
queries *NotificationQueries
|
||||
otpEmailTmpl string
|
||||
otpEmailTmpl func(origin *url.URL) string
|
||||
|
||||
queue Queue
|
||||
maxAttempts uint8
|
||||
@@ -101,7 +102,7 @@ func NewUserNotifier(
|
||||
commands Commands,
|
||||
queries *NotificationQueries,
|
||||
channels types.ChannelChains,
|
||||
otpEmailTmpl string,
|
||||
otpEmailTmpl func(origin *url.URL) string,
|
||||
workerConfig WorkerConfig,
|
||||
queue Queue,
|
||||
) *handler.Handler {
|
||||
@@ -530,7 +531,7 @@ func (u *userNotifier) reduceSessionOTPEmailChallenged(event eventstore.Event) (
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
origin := http_util.DomainContext(ctx).Origin()
|
||||
origin := http_util.DomainContext(ctx).OriginURL()
|
||||
|
||||
args := otpArgs(ctx, e.Expiry)
|
||||
args.SessionID = e.Aggregate().ID
|
||||
@@ -539,7 +540,7 @@ func (u *userNotifier) reduceSessionOTPEmailChallenged(event eventstore.Event) (
|
||||
Aggregate: e.Aggregate(),
|
||||
UserID: s.UserFactor.UserID,
|
||||
UserResourceOwner: s.UserFactor.ResourceOwner,
|
||||
TriggeredAtOrigin: origin,
|
||||
TriggeredAtOrigin: origin.String(),
|
||||
EventType: e.EventType,
|
||||
NotificationType: domain.NotificationTypeEmail,
|
||||
MessageType: domain.VerifyEmailOTPMessageType,
|
||||
@@ -555,11 +556,11 @@ func (u *userNotifier) reduceSessionOTPEmailChallenged(event eventstore.Event) (
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (u *userNotifier) otpEmailTemplate(origin string, e *session.OTPEmailChallengedEvent) string {
|
||||
func (u *userNotifier) otpEmailTemplate(origin *url.URL, e *session.OTPEmailChallengedEvent) string {
|
||||
if e.URLTmpl != "" {
|
||||
return e.URLTmpl
|
||||
}
|
||||
return origin + u.otpEmailTmpl
|
||||
return u.otpEmailTmpl(origin)
|
||||
}
|
||||
|
||||
func otpArgs(ctx context.Context, expiry time.Duration) *domain.NotificationArguments {
|
||||
|
||||
@@ -3,6 +3,7 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -25,7 +26,7 @@ type userNotifierLegacy struct {
|
||||
commands Commands
|
||||
queries *NotificationQueries
|
||||
channels types.ChannelChains
|
||||
otpEmailTmpl string
|
||||
otpEmailTmpl func(origin *url.URL) string
|
||||
}
|
||||
|
||||
func NewUserNotifierLegacy(
|
||||
@@ -34,7 +35,7 @@ func NewUserNotifierLegacy(
|
||||
commands Commands,
|
||||
queries *NotificationQueries,
|
||||
channels types.ChannelChains,
|
||||
otpEmailTmpl string,
|
||||
otpEmailTmpl func(origin *url.URL) string,
|
||||
) *handler.Handler {
|
||||
return handler.NewHandler(ctx, &config, &userNotifierLegacy{
|
||||
commands: commands,
|
||||
@@ -421,8 +422,8 @@ func (u *userNotifierLegacy) reduceOTPEmailCodeAdded(event eventstore.Event) (*h
|
||||
if e.AuthRequestInfo != nil {
|
||||
authRequestID = e.AuthRequestInfo.ID
|
||||
}
|
||||
url := func(code, origin string, _ *query.NotifyUser) (string, error) {
|
||||
return login.OTPLink(origin, authRequestID, code, domain.MFATypeOTPEmail), nil
|
||||
url := func(code string, origin *url.URL, _ *query.NotifyUser) (string, error) {
|
||||
return login.OTPLink(origin.String(), authRequestID, code, domain.MFATypeOTPEmail), nil
|
||||
}
|
||||
return u.reduceOTPEmail(
|
||||
e,
|
||||
@@ -450,11 +451,11 @@ func (u *userNotifierLegacy) reduceSessionOTPEmailChallenged(event eventstore.Ev
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url := func(code, origin string, user *query.NotifyUser) (string, error) {
|
||||
link := func(code string, origin *url.URL, user *query.NotifyUser) (string, error) {
|
||||
var buf strings.Builder
|
||||
urlTmpl := origin + u.otpEmailTmpl
|
||||
if e.URLTmpl != "" {
|
||||
urlTmpl = e.URLTmpl
|
||||
urlTmpl := e.URLTmpl
|
||||
if urlTmpl == "" {
|
||||
urlTmpl = u.otpEmailTmpl(origin)
|
||||
}
|
||||
if err := domain.RenderOTPEmailURLTemplate(&buf, urlTmpl, code, user.ID, user.PreferredLoginName, user.DisplayName, e.Aggregate().ID, user.PreferredLanguage); err != nil {
|
||||
return "", err
|
||||
@@ -467,7 +468,7 @@ func (u *userNotifierLegacy) reduceSessionOTPEmailChallenged(event eventstore.Ev
|
||||
e.Expiry,
|
||||
s.UserFactor.UserID,
|
||||
s.UserFactor.ResourceOwner,
|
||||
url,
|
||||
link,
|
||||
u.commands.OTPEmailSent,
|
||||
user.HumanOTPEmailCodeAddedType,
|
||||
user.HumanOTPEmailCodeSentType,
|
||||
@@ -480,7 +481,7 @@ func (u *userNotifierLegacy) reduceOTPEmail(
|
||||
expiry time.Duration,
|
||||
userID,
|
||||
resourceOwner string,
|
||||
urlTmpl func(code, origin string, user *query.NotifyUser) (string, error),
|
||||
urlTmpl func(code string, origin *url.URL, user *query.NotifyUser) (string, error),
|
||||
sentCommand func(ctx context.Context, userID string, resourceOwner string) (err error),
|
||||
eventTypes ...eventstore.EventType,
|
||||
) (*handler.Statement, error) {
|
||||
@@ -518,12 +519,12 @@ func (u *userNotifierLegacy) reduceOTPEmail(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url, err := urlTmpl(plainCode, http_util.DomainContext(ctx).Origin(), notifyUser)
|
||||
link, err := urlTmpl(plainCode, http_util.DomainContext(ctx).OriginURL(), notifyUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notify := types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, event.Type())
|
||||
err = notify.SendOTPEmailCode(ctx, url, plainCode, expiry)
|
||||
err = notify.SendOTPEmailCode(ctx, link, plainCode, expiry)
|
||||
if err != nil {
|
||||
if errors.Is(err, &channels.CancelError{}) {
|
||||
// if the notification was canceled, we don't want to return the error, so there is no retry
|
||||
@@ -577,7 +578,7 @@ func (u *userNotifierLegacy) reduceDomainClaimed(event eventstore.Event) (*handl
|
||||
return err
|
||||
}
|
||||
err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, event.Type()).
|
||||
SendDomainClaimed(ctx, notifyUser, e.UserName)
|
||||
SendDomainClaimed(ctx, notifyUser, e.UserName, e.URLTemplate)
|
||||
if err != nil {
|
||||
if errors.Is(err, &channels.CancelError{}) {
|
||||
// if the notification was canceled, we don't want to return the error, so there is no retry
|
||||
|
||||
@@ -3,6 +3,7 @@ package handlers
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -1972,7 +1973,9 @@ func newUserNotifierLegacy(t *testing.T, ctrl *gomock.Controller, queries *mock.
|
||||
smtpAlg,
|
||||
f.SMSTokenCrypto,
|
||||
),
|
||||
otpEmailTmpl: defaultOTPEmailTemplate,
|
||||
otpEmailTmpl: func(origin *url.URL) string {
|
||||
return origin.String() + defaultOTPEmailTemplate
|
||||
},
|
||||
channels: ¬ificationChannels{
|
||||
Chain: *senders.ChainChannels(channel),
|
||||
EmailConfig: &email.Config{
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -1936,7 +1937,9 @@ func newUserNotifier(t *testing.T, ctrl *gomock.Controller, queries *mock.MockQu
|
||||
smtpAlg,
|
||||
f.SMSTokenCrypto,
|
||||
),
|
||||
otpEmailTmpl: defaultOTPEmailTemplate,
|
||||
otpEmailTmpl: func(origin *url.URL) string {
|
||||
return origin.String() + defaultOTPEmailTemplate
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package notification
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
@@ -35,7 +36,8 @@ func Register(
|
||||
commands *command.Commands,
|
||||
queries *query.Queries,
|
||||
es *eventstore.Eventstore,
|
||||
otpEmailTmpl, fileSystemPath string,
|
||||
otpEmailTmpl func(origin *url.URL) string,
|
||||
fileSystemPath string,
|
||||
userEncryption, smtpEncryption, smsEncryption crypto.EncryptionAlgorithm,
|
||||
queue *queue.Queue,
|
||||
) {
|
||||
|
||||
@@ -10,11 +10,31 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
func (notify Notify) SendDomainClaimed(ctx context.Context, user *query.NotifyUser, username string) error {
|
||||
url := login.LoginLink(http_utils.DomainContext(ctx).Origin(), user.ResourceOwner)
|
||||
func (notify Notify) SendDomainClaimed(ctx context.Context, user *query.NotifyUser, username, urlTemplate string) error {
|
||||
index := strings.LastIndex(user.LastEmail, "@")
|
||||
domainSuffix := user.LastEmail[index+1:]
|
||||
var url string
|
||||
if urlTemplate == "" {
|
||||
url = login.LoginLink(http_utils.DomainContext(ctx).Origin(), user.ResourceOwner)
|
||||
} else {
|
||||
var buf strings.Builder
|
||||
if err := domain.RenderURLTemplate(&buf, urlTemplate, &DomainClaimedData{
|
||||
Domain: domainSuffix,
|
||||
TempUsername: username,
|
||||
OrgID: user.ResourceOwner,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
url = buf.String()
|
||||
}
|
||||
args := make(map[string]interface{})
|
||||
args["TempUsername"] = username
|
||||
args["Domain"] = user.LastEmail[index+1:]
|
||||
args["Domain"] = domainSuffix
|
||||
return notify(url, args, domain.DomainClaimedMessageType, true)
|
||||
}
|
||||
|
||||
type DomainClaimedData struct {
|
||||
TempUsername string
|
||||
Domain string
|
||||
OrgID string
|
||||
}
|
||||
|
||||
@@ -143,16 +143,6 @@ func (e *HumanEmailCodeAddedEvent) TriggerOrigin() string {
|
||||
}
|
||||
|
||||
func NewHumanEmailCodeAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
code *crypto.CryptoValue,
|
||||
expiry time.Duration,
|
||||
authRequestID string,
|
||||
) *HumanEmailCodeAddedEvent {
|
||||
return NewHumanEmailCodeAddedEventV2(ctx, aggregate, code, expiry, "", false, authRequestID)
|
||||
}
|
||||
|
||||
func NewHumanEmailCodeAddedEventV2(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
code *crypto.CryptoValue,
|
||||
|
||||
@@ -372,6 +372,7 @@ type DomainClaimedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
UserName string `json:"userName"`
|
||||
URLTemplate string `json:"urlTemplate,omitempty"`
|
||||
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
||||
oldUserName string
|
||||
orgScopedUsername bool
|
||||
@@ -398,6 +399,7 @@ func NewDomainClaimedEvent(
|
||||
userName,
|
||||
oldUserName string,
|
||||
orgScopedUsername bool,
|
||||
urlTemplate string,
|
||||
) *DomainClaimedEvent {
|
||||
return &DomainClaimedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
@@ -406,6 +408,7 @@ func NewDomainClaimedEvent(
|
||||
UserDomainClaimedType,
|
||||
),
|
||||
UserName: userName,
|
||||
URLTemplate: urlTemplate,
|
||||
oldUserName: oldUserName,
|
||||
orgScopedUsername: orgScopedUsername,
|
||||
TriggeredAtOrigin: http.DomainContext(ctx).Origin(),
|
||||
|
||||
Reference in New Issue
Block a user