mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-02 14:12:27 +00:00
# Which Problems Are Solved
As part of our efforts to simplify the structure and versions of our
APIs, were moving all existing v2beta endpoints to v2 and deprecate
them. They will be removed in Zitadel V5.
# How the Problems Are Solved
- This PR moves instance v2beta service and its endpoints to a
corresponding v2 version. The v2beta service and endpoints are
deprecated.
- The docs are moved to the new GA service and its endpoints. The v2beta
is not displayed anymore.
- The comments and have been improved and, where not already done, moved
from swagger annotations to proto.
- All required fields have been marked with (google.api.field_behavior)
= REQUIRED and validation rules have been added where missing
- `Domain` has been renamed to `CustomDomain` to align with naming
conventions
- `..Query` has been renamed to `..Filter` to align with other services
- The `instance_id` parameter can now passed on all endpoints and is
properly used, but requires `system` permissions. It can be omitted to
use the own instance (identified by context as any other service).
- The following endpoints are affected:
- GetInstance
- UpdateInstance
- ListCustomDomains
- AddTrustedDomain
- RemoveTrustedDomain
- ListTrustedDomains
- InstanceService has been added the InstanceInterceptor's
`explicitInstanceIdServices` to allow passing the id
- If the instance is not found by id, the error is not directly returned
to prevent enumeration.
- Permissions are checked in the API instead of the interceptor for
these calls.
- Setting the same instance name in the update no longer returns an
error, but the previous change date.
# Additional Changes
none
# Additional Context
- part of https://github.com/zitadel/zitadel/issues/10772
- requires backport to v4.x
1853 lines
60 KiB
Go
1853 lines
60 KiB
Go
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"
|
|
|
|
"github.com/zitadel/zitadel/internal/api/authz"
|
|
"github.com/zitadel/zitadel/internal/cache/connector/noop"
|
|
"github.com/zitadel/zitadel/internal/command/preparation"
|
|
"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"
|
|
"github.com/zitadel/zitadel/internal/repository/milestone"
|
|
"github.com/zitadel/zitadel/internal/repository/org"
|
|
"github.com/zitadel/zitadel/internal/repository/project"
|
|
"github.com/zitadel/zitadel/internal/repository/user"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
func instanceSetupZitadelIDs() ZitadelConfig {
|
|
return ZitadelConfig{
|
|
instanceID: "INSTANCE",
|
|
orgID: "ORG",
|
|
projectID: "PROJECT",
|
|
consoleAppID: "console-id",
|
|
authAppID: "auth-id",
|
|
mgmtAppID: "mgmt-id",
|
|
adminAppID: "admin-id",
|
|
}
|
|
}
|
|
|
|
func projectAddedEvents(ctx context.Context, instanceID, orgID, id, owner string, externalSecure bool) []eventstore.Command {
|
|
events := []eventstore.Command{
|
|
project.NewProjectAddedEvent(ctx,
|
|
&project.NewAggregate(id, orgID).Aggregate,
|
|
"ZITADEL",
|
|
false,
|
|
false,
|
|
false,
|
|
domain.PrivateLabelingSettingUnspecified,
|
|
),
|
|
instance.NewIAMProjectSetEvent(ctx,
|
|
&instance.NewAggregate(instanceID).Aggregate,
|
|
id,
|
|
),
|
|
}
|
|
events = append(events, apiAppEvents(ctx, orgID, id, "mgmt-id", "Management-API")...)
|
|
events = append(events, apiAppEvents(ctx, orgID, id, "admin-id", "Admin-API")...)
|
|
events = append(events, apiAppEvents(ctx, orgID, id, "auth-id", "Auth-API")...)
|
|
|
|
consoleAppID := "console-id"
|
|
consoleClientID := "clientID"
|
|
events = append(events, oidcAppEvents(ctx, orgID, id, consoleAppID, "Console", consoleClientID, externalSecure)...)
|
|
events = append(events,
|
|
instance.NewIAMConsoleSetEvent(ctx,
|
|
&instance.NewAggregate(instanceID).Aggregate,
|
|
&consoleClientID,
|
|
&consoleAppID,
|
|
),
|
|
)
|
|
return events
|
|
}
|
|
|
|
func projectClientIDs() []string {
|
|
return []string{"clientID", "clientID", "clientID", "clientID"}
|
|
}
|
|
|
|
func apiAppEvents(ctx context.Context, orgID, projectID, id, name string) []eventstore.Command {
|
|
return []eventstore.Command{
|
|
project.NewApplicationAddedEvent(
|
|
ctx,
|
|
&project.NewAggregate(projectID, orgID).Aggregate,
|
|
id,
|
|
name,
|
|
),
|
|
project.NewAPIConfigAddedEvent(ctx,
|
|
&project.NewAggregate(projectID, orgID).Aggregate,
|
|
id,
|
|
"clientID",
|
|
"",
|
|
domain.APIAuthMethodTypePrivateKeyJWT,
|
|
),
|
|
}
|
|
}
|
|
|
|
func oidcAppEvents(ctx context.Context, orgID, projectID, id, name, clientID string, externalSecure bool) []eventstore.Command {
|
|
return []eventstore.Command{
|
|
project.NewApplicationAddedEvent(
|
|
ctx,
|
|
&project.NewAggregate(projectID, orgID).Aggregate,
|
|
id,
|
|
name,
|
|
),
|
|
project.NewOIDCConfigAddedEvent(ctx,
|
|
&project.NewAggregate(projectID, orgID).Aggregate,
|
|
domain.OIDCVersionV1,
|
|
id,
|
|
clientID,
|
|
"",
|
|
[]string{},
|
|
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
|
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
|
domain.OIDCApplicationTypeUserAgent,
|
|
domain.OIDCAuthMethodTypeNone,
|
|
[]string{},
|
|
!externalSecure,
|
|
domain.OIDCTokenTypeBearer,
|
|
false,
|
|
false,
|
|
false,
|
|
0,
|
|
nil,
|
|
false,
|
|
"",
|
|
domain.LoginVersionUnspecified,
|
|
"",
|
|
),
|
|
}
|
|
}
|
|
|
|
func orgFilters(orgID string, machine, human, loginClient bool) []expect {
|
|
filters := []expect{
|
|
expectFilter(),
|
|
expectFilter(
|
|
org.NewOrgAddedEvent(context.Background(), &org.NewAggregate(orgID).Aggregate, ""),
|
|
),
|
|
}
|
|
if machine {
|
|
filters = append(filters, machineFilters(orgID, true)...)
|
|
filters = append(filters, adminMemberFilters(orgID, "USER-MACHINE")...)
|
|
}
|
|
if human {
|
|
filters = append(filters, humanFilters(orgID)...)
|
|
filters = append(filters, adminMemberFilters(orgID, "USER")...)
|
|
}
|
|
if loginClient {
|
|
filters = append(filters, loginClientFilters(orgID, true)...)
|
|
filters = append(filters, instanceMemberFilters(orgID, "USER-LOGIN-CLIENT")...)
|
|
}
|
|
|
|
return append(filters,
|
|
projectFilters()...,
|
|
)
|
|
}
|
|
|
|
func orgEvents(ctx context.Context, instanceID, orgID, name, projectID, defaultDomain string, externalSecure bool, machine, human, loginClient bool) []eventstore.Command {
|
|
instanceAgg := instance.NewAggregate(instanceID)
|
|
orgAgg := org.NewAggregate(orgID)
|
|
domain := strings.ToLower(name + "." + defaultDomain)
|
|
events := []eventstore.Command{
|
|
org.NewOrgAddedEvent(ctx, &orgAgg.Aggregate, name),
|
|
org.NewDomainAddedEvent(ctx, &orgAgg.Aggregate, domain),
|
|
org.NewDomainVerifiedEvent(ctx, &orgAgg.Aggregate, domain),
|
|
org.NewDomainPrimarySetEvent(ctx, &orgAgg.Aggregate, domain),
|
|
instance.NewDefaultOrgSetEventEvent(ctx, &instanceAgg.Aggregate, orgID),
|
|
}
|
|
|
|
owner := ""
|
|
if machine {
|
|
machineID := "USER-MACHINE"
|
|
events = append(events, machineEvents(ctx, instanceID, orgID, machineID, "PAT")...)
|
|
owner = machineID
|
|
}
|
|
if human {
|
|
userID := "USER"
|
|
events = append(events, humanEvents(ctx, instanceID, orgID, userID)...)
|
|
owner = userID
|
|
}
|
|
if loginClient {
|
|
userID := "USER-LOGIN-CLIENT"
|
|
events = append(events, loginClientEvents(ctx, instanceID, orgID, userID, "LOGIN-CLIENT-PAT")...)
|
|
}
|
|
|
|
events = append(events, projectAddedEvents(ctx, instanceID, orgID, projectID, owner, externalSecure)...)
|
|
return events
|
|
}
|
|
|
|
func orgIDs() []string {
|
|
return slices.Concat([]string{"USER-MACHINE", "PAT", "USER", "USER-LOGIN-CLIENT", "LOGIN-CLIENT-PAT"}, projectClientIDs())
|
|
}
|
|
|
|
func instancePoliciesFilters(instanceID string) []expect {
|
|
return []expect{
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
}
|
|
}
|
|
|
|
func instancePoliciesEvents(ctx context.Context, instanceID string) []eventstore.Command {
|
|
instanceAgg := instance.NewAggregate(instanceID)
|
|
return []eventstore.Command{
|
|
instance.NewPasswordComplexityPolicyAddedEvent(ctx, &instanceAgg.Aggregate, 8, true, true, true, true),
|
|
instance.NewPasswordAgePolicyAddedEvent(ctx, &instanceAgg.Aggregate, 0, 0),
|
|
instance.NewDomainPolicyAddedEvent(ctx, &instanceAgg.Aggregate, false, false, false),
|
|
instance.NewLoginPolicyAddedEvent(ctx, &instanceAgg.Aggregate, true, true, true, false, false, false, false, true, false, false, domain.PasswordlessTypeAllowed, "", 240*time.Hour, 240*time.Hour, 720*time.Hour, 18*time.Hour, 12*time.Hour),
|
|
instance.NewLoginPolicySecondFactorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecondFactorTypeTOTP),
|
|
instance.NewLoginPolicySecondFactorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecondFactorTypeU2F),
|
|
instance.NewLoginPolicyMultiFactorAddedEvent(ctx, &instanceAgg.Aggregate, domain.MultiFactorTypeU2FWithPIN),
|
|
instance.NewPrivacyPolicyAddedEvent(ctx, &instanceAgg.Aggregate, "", "", "", "", "", "", ""),
|
|
instance.NewNotificationPolicyAddedEvent(ctx, &instanceAgg.Aggregate, true),
|
|
instance.NewLockoutPolicyAddedEvent(ctx, &instanceAgg.Aggregate, 0, 0, true),
|
|
instance.NewLabelPolicyAddedEvent(ctx, &instanceAgg.Aggregate, "#5469d4", "#fafafa", "#cd3d56", "#000000", "#2073c4", "#111827", "#ff3b5b", "#ffffff", false, false, false, domain.LabelPolicyThemeAuto),
|
|
instance.NewLabelPolicyActivatedEvent(ctx, &instanceAgg.Aggregate),
|
|
}
|
|
}
|
|
|
|
func instanceSetupPoliciesConfig() *InstanceSetup {
|
|
return &InstanceSetup{
|
|
PasswordComplexityPolicy: struct {
|
|
MinLength uint64
|
|
HasLowercase bool
|
|
HasUppercase bool
|
|
HasNumber bool
|
|
HasSymbol bool
|
|
}{8, true, true, true, true},
|
|
PasswordAgePolicy: struct {
|
|
ExpireWarnDays uint64
|
|
MaxAgeDays uint64
|
|
}{0, 0},
|
|
DomainPolicy: struct {
|
|
UserLoginMustBeDomain bool
|
|
ValidateOrgDomains bool
|
|
SMTPSenderAddressMatchesInstanceDomain bool
|
|
}{false, false, false},
|
|
LoginPolicy: struct {
|
|
AllowUsernamePassword bool
|
|
AllowRegister bool
|
|
AllowExternalIDP bool
|
|
ForceMFA bool
|
|
ForceMFALocalOnly bool
|
|
HidePasswordReset bool
|
|
IgnoreUnknownUsername bool
|
|
AllowDomainDiscovery bool
|
|
DisableLoginWithEmail bool
|
|
DisableLoginWithPhone bool
|
|
PasswordlessType domain.PasswordlessType
|
|
DefaultRedirectURI string
|
|
PasswordCheckLifetime time.Duration
|
|
ExternalLoginCheckLifetime time.Duration
|
|
MfaInitSkipLifetime time.Duration
|
|
SecondFactorCheckLifetime time.Duration
|
|
MultiFactorCheckLifetime time.Duration
|
|
}{true, true, true, false, false, false, false, true, false, false, domain.PasswordlessTypeAllowed, "", 240 * time.Hour, 240 * time.Hour, 720 * time.Hour, 18 * time.Hour, 12 * time.Hour},
|
|
NotificationPolicy: struct {
|
|
PasswordChange bool
|
|
}{true},
|
|
PrivacyPolicy: struct {
|
|
TOSLink string
|
|
PrivacyLink string
|
|
HelpLink string
|
|
SupportEmail domain.EmailAddress
|
|
DocsLink string
|
|
CustomLink string
|
|
CustomLinkText string
|
|
}{"", "", "", "", "", "", ""},
|
|
LabelPolicy: struct {
|
|
PrimaryColor string
|
|
BackgroundColor string
|
|
WarnColor string
|
|
FontColor string
|
|
PrimaryColorDark string
|
|
BackgroundColorDark string
|
|
WarnColorDark string
|
|
FontColorDark string
|
|
HideLoginNameSuffix bool
|
|
ErrorMsgPopup bool
|
|
DisableWatermark bool
|
|
ThemeMode domain.LabelPolicyThemeMode
|
|
}{"#5469d4", "#fafafa", "#cd3d56", "#000000", "#2073c4", "#111827", "#ff3b5b", "#ffffff", false, false, false, domain.LabelPolicyThemeAuto},
|
|
LockoutPolicy: struct {
|
|
MaxPasswordAttempts uint64
|
|
MaxOTPAttempts uint64
|
|
ShouldShowLockoutFailure bool
|
|
}{0, 0, true},
|
|
}
|
|
}
|
|
|
|
func setupInstanceElementsFilters(instanceID string) []expect {
|
|
return slices.Concat(
|
|
instanceElementsFilters(),
|
|
instancePoliciesFilters(instanceID),
|
|
// email template
|
|
[]expect{expectFilter()},
|
|
)
|
|
}
|
|
|
|
func setupInstanceElementsConfig() *InstanceSetup {
|
|
conf := instanceSetupPoliciesConfig()
|
|
conf.InstanceName = "ZITADEL"
|
|
conf.DefaultLanguage = language.English
|
|
conf.zitadel = instanceSetupZitadelIDs()
|
|
conf.SecretGenerators = instanceElementsConfig()
|
|
conf.EmailTemplate = []byte("something")
|
|
return conf
|
|
}
|
|
|
|
func setupInstanceElementsEvents(ctx context.Context, instanceID, instanceName string, defaultLanguage language.Tag) []eventstore.Command {
|
|
instanceAgg := instance.NewAggregate(instanceID)
|
|
return slices.Concat(
|
|
instanceElementsEvents(ctx, instanceID, instanceName, defaultLanguage),
|
|
instancePoliciesEvents(ctx, instanceID),
|
|
[]eventstore.Command{instance.NewMailTemplateAddedEvent(ctx, &instanceAgg.Aggregate, []byte("something"))},
|
|
)
|
|
}
|
|
|
|
func instanceElementsFilters() []expect {
|
|
return []expect{
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
}
|
|
}
|
|
|
|
func instanceElementsEvents(ctx context.Context, instanceID, instanceName string, defaultLanguage language.Tag) []eventstore.Command {
|
|
instanceAgg := instance.NewAggregate(instanceID)
|
|
return []eventstore.Command{
|
|
instance.NewInstanceAddedEvent(ctx, &instanceAgg.Aggregate, instanceName),
|
|
instance.NewDefaultLanguageSetEvent(ctx, &instanceAgg.Aggregate, defaultLanguage),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypeAppSecret, 64, 0, true, true, true, false),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypeInitCode, 6, 72*time.Hour, false, true, true, false),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypeVerifyEmailCode, 6, time.Hour, false, true, true, false),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypeVerifyPhoneCode, 6, time.Hour, false, true, true, false),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypePasswordResetCode, 6, time.Hour, false, true, true, false),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypePasswordlessInitCode, 12, time.Hour, true, true, true, false),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypeVerifyDomain, 32, 0, true, true, true, false),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypeOTPSMS, 8, 5*time.Minute, false, false, true, false),
|
|
instance.NewSecretGeneratorAddedEvent(ctx, &instanceAgg.Aggregate, domain.SecretGeneratorTypeOTPEmail, 8, 5*time.Minute, false, false, true, false),
|
|
}
|
|
}
|
|
|
|
func instanceElementsConfig() *SecretGenerators {
|
|
return &SecretGenerators{
|
|
ClientSecret: &crypto.GeneratorConfig{Length: 64, IncludeLowerLetters: true, IncludeUpperLetters: true, IncludeDigits: true},
|
|
InitializeUserCode: &crypto.GeneratorConfig{Length: 6, Expiry: 72 * time.Hour, IncludeUpperLetters: true, IncludeDigits: true},
|
|
EmailVerificationCode: &crypto.GeneratorConfig{Length: 6, Expiry: time.Hour, IncludeUpperLetters: true, IncludeDigits: true},
|
|
PhoneVerificationCode: &crypto.GeneratorConfig{Length: 6, Expiry: time.Hour, IncludeUpperLetters: true, IncludeDigits: true},
|
|
PasswordVerificationCode: &crypto.GeneratorConfig{Length: 6, Expiry: time.Hour, IncludeUpperLetters: true, IncludeDigits: true},
|
|
PasswordlessInitCode: &crypto.GeneratorConfig{Length: 12, Expiry: time.Hour, IncludeLowerLetters: true, IncludeUpperLetters: true, IncludeDigits: true},
|
|
DomainVerification: &crypto.GeneratorConfig{Length: 32, IncludeLowerLetters: true, IncludeUpperLetters: true, IncludeDigits: true},
|
|
OTPSMS: &crypto.GeneratorConfig{Length: 8, Expiry: 5 * time.Minute, IncludeDigits: true},
|
|
OTPEmail: &crypto.GeneratorConfig{Length: 8, Expiry: 5 * time.Minute, IncludeDigits: true},
|
|
}
|
|
}
|
|
|
|
func setupInstanceFilters(instanceID, orgID, projectID, appID, domain string) []expect {
|
|
return slices.Concat(
|
|
setupInstanceElementsFilters(instanceID),
|
|
orgFilters(orgID, true, true, true),
|
|
generatedDomainFilters(instanceID, orgID, projectID, appID, domain),
|
|
)
|
|
}
|
|
|
|
func setupInstanceEvents(ctx context.Context, instanceID, orgID, projectID, appID, instanceName, orgName string, defaultLanguage language.Tag, domain string, externalSecure bool) []eventstore.Command {
|
|
return slices.Concat(
|
|
setupInstanceElementsEvents(ctx, instanceID, instanceName, defaultLanguage),
|
|
orgEvents(ctx, instanceID, orgID, orgName, projectID, domain, externalSecure, true, true, true),
|
|
generatedDomainEvents(ctx, instanceID, orgID, projectID, appID, domain),
|
|
instanceCreatedMilestoneEvent(ctx, instanceID),
|
|
)
|
|
}
|
|
|
|
func setupInstanceConfig() *InstanceSetup {
|
|
conf := setupInstanceElementsConfig()
|
|
conf.Org = InstanceOrgSetup{
|
|
Name: "ZITADEL",
|
|
Machine: instanceSetupMachineConfig(),
|
|
Human: instanceSetupHumanConfig(),
|
|
LoginClient: instanceSetupLoginClientConfig(),
|
|
}
|
|
conf.CustomDomain = ""
|
|
return conf
|
|
}
|
|
|
|
func generatedDomainEvents(ctx context.Context, instanceID, orgID, projectID, appID, defaultDomain string) []eventstore.Command {
|
|
instanceAgg := instance.NewAggregate(instanceID)
|
|
changed, _ := project.NewOIDCConfigChangedEvent(ctx, &project.NewAggregate(projectID, orgID).Aggregate, appID,
|
|
[]project.OIDCConfigChanges{
|
|
project.ChangeRedirectURIs([]string{"http://" + defaultDomain + "/ui/console/auth/callback"}),
|
|
project.ChangePostLogoutRedirectURIs([]string{"http://" + defaultDomain + "/ui/console/signedout"}),
|
|
},
|
|
)
|
|
return []eventstore.Command{
|
|
instance.NewDomainAddedEvent(ctx, &instanceAgg.Aggregate, defaultDomain, true),
|
|
changed,
|
|
instance.NewDomainPrimarySetEvent(ctx, &instanceAgg.Aggregate, defaultDomain),
|
|
}
|
|
}
|
|
|
|
func instanceCreatedMilestoneEvent(ctx context.Context, instanceID string) []eventstore.Command {
|
|
return []eventstore.Command{
|
|
milestone.NewReachedEvent(ctx, milestone.NewInstanceAggregate(instanceID), milestone.InstanceCreated),
|
|
}
|
|
}
|
|
|
|
func generatedDomainFilters(instanceID, orgID, projectID, appID, generatedDomain string) []expect {
|
|
return []expect{
|
|
expectFilter(),
|
|
expectFilter(
|
|
project.NewApplicationAddedEvent(context.Background(),
|
|
&project.NewAggregate(projectID, orgID).Aggregate,
|
|
appID,
|
|
"console",
|
|
),
|
|
project.NewOIDCConfigAddedEvent(context.Background(),
|
|
&project.NewAggregate(projectID, orgID).Aggregate,
|
|
domain.OIDCVersionV1,
|
|
appID,
|
|
"clientID",
|
|
"",
|
|
[]string{},
|
|
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
|
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
|
domain.OIDCApplicationTypeUserAgent,
|
|
domain.OIDCAuthMethodTypeNone,
|
|
[]string{},
|
|
true,
|
|
domain.OIDCTokenTypeBearer,
|
|
false,
|
|
false,
|
|
false,
|
|
0,
|
|
nil,
|
|
false,
|
|
"",
|
|
domain.LoginVersionUnspecified,
|
|
"",
|
|
),
|
|
),
|
|
expectFilter(
|
|
func() eventstore.Event {
|
|
event := instance.NewDomainAddedEvent(context.Background(),
|
|
&instance.NewAggregate(instanceID).Aggregate,
|
|
generatedDomain,
|
|
true,
|
|
)
|
|
event.Data, _ = json.Marshal(event)
|
|
return event
|
|
}(),
|
|
),
|
|
}
|
|
}
|
|
|
|
func humanFilters(orgID string) []expect {
|
|
return []expect{
|
|
expectFilter(),
|
|
expectFilter(
|
|
org.NewDomainPolicyAddedEvent(
|
|
context.Background(),
|
|
&org.NewAggregate(orgID).Aggregate,
|
|
true,
|
|
true,
|
|
true,
|
|
),
|
|
),
|
|
expectFilterOrganizationSettings("org1", false, false),
|
|
expectFilter(
|
|
org.NewPasswordComplexityPolicyAddedEvent(
|
|
context.Background(),
|
|
&org.NewAggregate(orgID).Aggregate,
|
|
2,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
),
|
|
),
|
|
}
|
|
}
|
|
|
|
func instanceSetupHumanConfig() *AddHuman {
|
|
return &AddHuman{
|
|
Username: "zitadel-admin",
|
|
FirstName: "ZITADEL",
|
|
LastName: "Admin",
|
|
Email: Email{
|
|
Address: domain.EmailAddress("admin@zitadel.test"),
|
|
Verified: true,
|
|
},
|
|
PreferredLanguage: language.English,
|
|
Password: "password",
|
|
PasswordChangeRequired: false,
|
|
}
|
|
}
|
|
|
|
func machineFilters(orgID string, pat bool) []expect {
|
|
filters := []expect{
|
|
expectFilter(),
|
|
expectFilter(
|
|
org.NewDomainPolicyAddedEvent(
|
|
context.Background(),
|
|
&org.NewAggregate(orgID).Aggregate,
|
|
true,
|
|
true,
|
|
true,
|
|
),
|
|
),
|
|
expectFilterOrganizationSettings("org1", false, false),
|
|
}
|
|
if pat {
|
|
filters = append(filters,
|
|
expectFilter(),
|
|
expectFilter(),
|
|
)
|
|
}
|
|
return filters
|
|
}
|
|
|
|
func instanceSetupMachineConfig() *AddMachine {
|
|
return &AddMachine{
|
|
Machine: &Machine{
|
|
Username: "zitadel-admin-machine",
|
|
Name: "ZITADEL-machine",
|
|
Description: "Admin",
|
|
AccessTokenType: domain.OIDCTokenTypeBearer,
|
|
},
|
|
Pat: &AddPat{
|
|
ExpirationDate: time.Time{},
|
|
Scopes: nil,
|
|
},
|
|
/* not predictable with the key value in the events
|
|
MachineKey: &AddMachineKey{
|
|
Type: domain.AuthNKeyTypeJSON,
|
|
ExpirationDate: time.Time{},
|
|
},
|
|
*/
|
|
}
|
|
}
|
|
|
|
func loginClientFilters(orgID string, pat bool) []expect {
|
|
filters := []expect{
|
|
expectFilter(),
|
|
expectFilter(
|
|
org.NewDomainPolicyAddedEvent(
|
|
context.Background(),
|
|
&org.NewAggregate(orgID).Aggregate,
|
|
true,
|
|
true,
|
|
true,
|
|
),
|
|
),
|
|
expectFilterOrganizationSettings("org1", false, false),
|
|
}
|
|
if pat {
|
|
filters = append(filters,
|
|
expectFilter(),
|
|
expectFilter(),
|
|
)
|
|
}
|
|
return filters
|
|
}
|
|
|
|
func instanceSetupLoginClientConfig() *AddLoginClient {
|
|
return &AddLoginClient{
|
|
Machine: &Machine{
|
|
Username: "zitadel-login-client",
|
|
Name: "ZITADEL-login-client",
|
|
Description: "Login Client",
|
|
AccessTokenType: domain.OIDCTokenTypeBearer,
|
|
},
|
|
Pat: &AddPat{
|
|
ExpirationDate: time.Time{},
|
|
Scopes: nil,
|
|
},
|
|
}
|
|
}
|
|
|
|
func projectFilters() []expect {
|
|
return []expect{
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
expectFilter(),
|
|
}
|
|
}
|
|
|
|
func adminMemberFilters(orgID, userID string) []expect {
|
|
filters := append(
|
|
orgMemberFilters(orgID, userID),
|
|
instanceMemberFilters(orgID, userID)...,
|
|
)
|
|
return filters
|
|
}
|
|
func orgMemberFilters(orgID, userID string) []expect {
|
|
return []expect{
|
|
expectFilter(
|
|
addHumanEvent(context.Background(), orgID, userID),
|
|
),
|
|
expectFilter(),
|
|
}
|
|
}
|
|
|
|
func instanceMemberFilters(orgID, userID string) []expect {
|
|
return []expect{
|
|
expectFilter(
|
|
addHumanEvent(context.Background(), orgID, userID),
|
|
),
|
|
expectFilter(),
|
|
}
|
|
}
|
|
|
|
func humanEvents(ctx context.Context, instanceID, orgID, userID string) []eventstore.Command {
|
|
agg := user.NewAggregate(userID, orgID)
|
|
instanceAgg := instance.NewAggregate(instanceID)
|
|
orgAgg := org.NewAggregate(orgID)
|
|
return []eventstore.Command{
|
|
addHumanEvent(ctx, orgID, userID),
|
|
user.NewHumanEmailVerifiedEvent(ctx, &agg.Aggregate),
|
|
org.NewMemberAddedEvent(ctx, &orgAgg.Aggregate, userID, domain.RoleOrgOwner),
|
|
instance.NewMemberAddedEvent(ctx, &instanceAgg.Aggregate, userID, domain.RoleIAMOwner),
|
|
}
|
|
}
|
|
|
|
func addHumanEvent(ctx context.Context, orgID, userID string) *user.HumanAddedEvent {
|
|
agg := user.NewAggregate(userID, orgID)
|
|
return func() *user.HumanAddedEvent {
|
|
event := user.NewHumanAddedEvent(
|
|
ctx,
|
|
&agg.Aggregate,
|
|
"zitadel-admin",
|
|
"ZITADEL",
|
|
"Admin",
|
|
"",
|
|
"ZITADEL Admin",
|
|
language.English,
|
|
0,
|
|
"admin@zitadel.test",
|
|
false,
|
|
)
|
|
event.AddPasswordData("$plain$x$password", false)
|
|
return event
|
|
}()
|
|
}
|
|
|
|
// machineEvents all events from setup to create the machine user, machinekey can't be tested here, as the public key is not provided and as such the value in the event can't be expected
|
|
func machineEvents(ctx context.Context, instanceID, orgID, userID, patID string) []eventstore.Command {
|
|
agg := user.NewAggregate(userID, orgID)
|
|
instanceAgg := instance.NewAggregate(instanceID)
|
|
orgAgg := org.NewAggregate(orgID)
|
|
events := []eventstore.Command{addMachineEvent(ctx, orgID, userID)}
|
|
if patID != "" {
|
|
events = append(events,
|
|
user.NewPersonalAccessTokenAddedEvent(
|
|
ctx,
|
|
&agg.Aggregate,
|
|
patID,
|
|
time.Date(9999, time.December, 31, 23, 59, 59, 0, time.UTC),
|
|
nil,
|
|
),
|
|
)
|
|
}
|
|
return append(events,
|
|
org.NewMemberAddedEvent(ctx, &orgAgg.Aggregate, userID, domain.RoleOrgOwner),
|
|
instance.NewMemberAddedEvent(ctx, &instanceAgg.Aggregate, userID, domain.RoleIAMOwner),
|
|
)
|
|
}
|
|
|
|
func addMachineEvent(ctx context.Context, orgID, userID string) *user.MachineAddedEvent {
|
|
agg := user.NewAggregate(userID, orgID)
|
|
return user.NewMachineAddedEvent(ctx,
|
|
&agg.Aggregate,
|
|
"zitadel-admin-machine",
|
|
"ZITADEL-machine",
|
|
"Admin",
|
|
false,
|
|
domain.OIDCTokenTypeBearer,
|
|
)
|
|
}
|
|
|
|
// loginClientEvents all events from setup to create the login client user
|
|
func loginClientEvents(ctx context.Context, instanceID, orgID, userID, patID string) []eventstore.Command {
|
|
agg := user.NewAggregate(userID, orgID)
|
|
instanceAgg := instance.NewAggregate(instanceID)
|
|
events := []eventstore.Command{
|
|
addLoginClientEvent(ctx, orgID, userID),
|
|
instance.NewMemberAddedEvent(ctx, &instanceAgg.Aggregate, userID, domain.RoleIAMLoginClient),
|
|
}
|
|
if patID != "" {
|
|
events = append(events,
|
|
user.NewPersonalAccessTokenAddedEvent(
|
|
ctx,
|
|
&agg.Aggregate,
|
|
patID,
|
|
time.Date(9999, time.December, 31, 23, 59, 59, 0, time.UTC),
|
|
nil,
|
|
),
|
|
)
|
|
}
|
|
return events
|
|
}
|
|
|
|
func addLoginClientEvent(ctx context.Context, orgID, userID string) *user.MachineAddedEvent {
|
|
agg := user.NewAggregate(userID, orgID)
|
|
return user.NewMachineAddedEvent(ctx,
|
|
&agg.Aggregate,
|
|
"zitadel-login-client",
|
|
"ZITADEL-login-client",
|
|
"Login Client",
|
|
false,
|
|
domain.OIDCTokenTypeBearer,
|
|
)
|
|
}
|
|
|
|
func testSetup(ctx context.Context, c *Commands, validations []preparation.Validation) error {
|
|
//nolint:staticcheck
|
|
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validations...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = c.eventstore.Push(ctx, cmds...)
|
|
return err
|
|
}
|
|
|
|
func TestCommandSide_setupMinimalInterfaces(t *testing.T) {
|
|
type fields struct {
|
|
eventstore func(t *testing.T) *eventstore.Eventstore
|
|
idGenerator id.Generator
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
instanceAgg *instance.Aggregate
|
|
orgAgg *org.Aggregate
|
|
owner string
|
|
ids ZitadelConfig
|
|
}
|
|
type res struct {
|
|
err func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
res res
|
|
}{
|
|
{
|
|
name: "create, ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
slices.Concat(
|
|
projectFilters(),
|
|
[]expect{
|
|
expectPush(
|
|
projectAddedEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"PROJECT",
|
|
"owner",
|
|
false,
|
|
)...,
|
|
),
|
|
},
|
|
)...,
|
|
),
|
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, projectClientIDs()...),
|
|
},
|
|
args: args{
|
|
ctx: contextWithInstanceSetupInfo(context.Background(), "INSTANCE", "PROJECT", "console-id", "DOMAIN", language.Dutch),
|
|
instanceAgg: instance.NewAggregate("INSTANCE"),
|
|
orgAgg: org.NewAggregate("ORG"),
|
|
owner: "owner",
|
|
ids: instanceSetupZitadelIDs(),
|
|
},
|
|
res: res{
|
|
err: nil,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := &Commands{
|
|
eventstore: tt.fields.eventstore(t),
|
|
idGenerator: tt.fields.idGenerator,
|
|
}
|
|
validations := make([]preparation.Validation, 0)
|
|
setupMinimalInterfaces(r, &validations, tt.args.instanceAgg, tt.args.orgAgg, tt.args.owner, tt.args.ids)
|
|
|
|
err := testSetup(tt.args.ctx, r, validations)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
func validZitadelRoles() []authz.RoleMapping {
|
|
return []authz.RoleMapping{
|
|
{Role: domain.RoleOrgOwner, Permissions: []string{""}},
|
|
{Role: domain.RoleIAMOwner, Permissions: []string{""}},
|
|
{Role: domain.RoleIAMLoginClient, Permissions: []string{""}},
|
|
}
|
|
}
|
|
|
|
func TestCommandSide_setupAdmins(t *testing.T) {
|
|
type fields struct {
|
|
eventstore func(t *testing.T) *eventstore.Eventstore
|
|
idGenerator id.Generator
|
|
userPasswordHasher *crypto.Hasher
|
|
roles []authz.RoleMapping
|
|
keyAlgorithm crypto.EncryptionAlgorithm
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
instanceAgg *instance.Aggregate
|
|
orgAgg *org.Aggregate
|
|
machine *AddMachine
|
|
human *AddHuman
|
|
loginClient *AddLoginClient
|
|
}
|
|
type res struct {
|
|
owner string
|
|
pat bool
|
|
machineKey bool
|
|
loginClientPat bool
|
|
err func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
res res
|
|
}{
|
|
{
|
|
name: "human, ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
slices.Concat(
|
|
humanFilters("ORG"),
|
|
adminMemberFilters("ORG", "USER"),
|
|
[]expect{
|
|
expectPush(
|
|
humanEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"USER",
|
|
)...,
|
|
),
|
|
},
|
|
)...,
|
|
),
|
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "USER"),
|
|
userPasswordHasher: mockPasswordHasher("x"),
|
|
roles: validZitadelRoles(),
|
|
},
|
|
args: args{
|
|
ctx: contextWithInstanceSetupInfo(context.Background(), "INSTANCE", "PROJECT", "console-id", "DOMAIN", language.Dutch),
|
|
instanceAgg: instance.NewAggregate("INSTANCE"),
|
|
orgAgg: org.NewAggregate("ORG"),
|
|
human: instanceSetupHumanConfig(),
|
|
},
|
|
res: res{
|
|
owner: "USER",
|
|
pat: false,
|
|
machineKey: false,
|
|
err: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "machine, ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
slices.Concat(
|
|
machineFilters("ORG", true),
|
|
adminMemberFilters("ORG", "USER-MACHINE"),
|
|
[]expect{
|
|
expectPush(
|
|
machineEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"USER-MACHINE",
|
|
"PAT",
|
|
)...,
|
|
),
|
|
},
|
|
)...,
|
|
),
|
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "USER-MACHINE", "PAT"),
|
|
roles: validZitadelRoles(),
|
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
|
},
|
|
args: args{
|
|
ctx: contextWithInstanceSetupInfo(context.Background(), "INSTANCE", "PROJECT", "console-id", "DOMAIN", language.Dutch),
|
|
instanceAgg: instance.NewAggregate("INSTANCE"),
|
|
orgAgg: org.NewAggregate("ORG"),
|
|
machine: instanceSetupMachineConfig(),
|
|
},
|
|
res: res{
|
|
owner: "USER-MACHINE",
|
|
pat: true,
|
|
machineKey: false,
|
|
err: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "human and machine, ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
slices.Concat(
|
|
machineFilters("ORG", true),
|
|
adminMemberFilters("ORG", "USER-MACHINE"),
|
|
humanFilters("ORG"),
|
|
adminMemberFilters("ORG", "USER"),
|
|
[]expect{
|
|
expectPush(
|
|
slices.Concat(
|
|
machineEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"USER-MACHINE",
|
|
"PAT",
|
|
),
|
|
humanEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"USER",
|
|
),
|
|
)...,
|
|
),
|
|
},
|
|
)...,
|
|
),
|
|
userPasswordHasher: mockPasswordHasher("x"),
|
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "USER-MACHINE", "PAT", "USER"),
|
|
roles: validZitadelRoles(),
|
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
|
},
|
|
args: args{
|
|
ctx: contextWithInstanceSetupInfo(context.Background(), "INSTANCE", "PROJECT", "console-id", "DOMAIN", language.Dutch),
|
|
instanceAgg: instance.NewAggregate("INSTANCE"),
|
|
orgAgg: org.NewAggregate("ORG"),
|
|
machine: instanceSetupMachineConfig(),
|
|
human: instanceSetupHumanConfig(),
|
|
},
|
|
res: res{
|
|
owner: "USER",
|
|
pat: true,
|
|
machineKey: false,
|
|
err: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "human, machine and login client, ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
slices.Concat(
|
|
machineFilters("ORG", true),
|
|
adminMemberFilters("ORG", "USER-MACHINE"),
|
|
humanFilters("ORG"),
|
|
adminMemberFilters("ORG", "USER"),
|
|
loginClientFilters("ORG", true),
|
|
instanceMemberFilters("ORG", "USER-LOGIN-CLIENT"),
|
|
[]expect{
|
|
expectPush(
|
|
slices.Concat(
|
|
machineEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"USER-MACHINE",
|
|
"PAT",
|
|
),
|
|
humanEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"USER",
|
|
),
|
|
loginClientEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"USER-LOGIN-CLIENT",
|
|
"LOGIN-CLIENT-PAT",
|
|
),
|
|
)...,
|
|
),
|
|
},
|
|
)...,
|
|
),
|
|
userPasswordHasher: mockPasswordHasher("x"),
|
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "USER-MACHINE", "PAT", "USER", "USER-LOGIN-CLIENT", "LOGIN-CLIENT-PAT"),
|
|
roles: validZitadelRoles(),
|
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
|
},
|
|
args: args{
|
|
ctx: contextWithInstanceSetupInfo(context.Background(), "INSTANCE", "PROJECT", "console-id", "DOMAIN", language.Dutch),
|
|
instanceAgg: instance.NewAggregate("INSTANCE"),
|
|
orgAgg: org.NewAggregate("ORG"),
|
|
machine: instanceSetupMachineConfig(),
|
|
human: instanceSetupHumanConfig(),
|
|
loginClient: instanceSetupLoginClientConfig(),
|
|
},
|
|
res: res{
|
|
owner: "USER",
|
|
pat: true,
|
|
machineKey: false,
|
|
loginClientPat: true,
|
|
err: nil,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := &Commands{
|
|
eventstore: tt.fields.eventstore(t),
|
|
idGenerator: tt.fields.idGenerator,
|
|
zitadelRoles: tt.fields.roles,
|
|
userPasswordHasher: tt.fields.userPasswordHasher,
|
|
keyAlgorithm: tt.fields.keyAlgorithm,
|
|
}
|
|
validations := make([]preparation.Validation, 0)
|
|
owner, pat, mk, loginClientPat, err := setupAdmins(r, &validations, tt.args.instanceAgg, tt.args.orgAgg, tt.args.machine, tt.args.human, tt.args.loginClient)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
|
|
err = testSetup(tt.args.ctx, r, validations)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
|
|
if tt.res.err == nil {
|
|
assert.Equal(t, owner, tt.res.owner)
|
|
if tt.res.pat {
|
|
assert.NotNil(t, pat)
|
|
}
|
|
if tt.res.machineKey {
|
|
assert.NotNil(t, mk)
|
|
}
|
|
if tt.res.loginClientPat {
|
|
assert.NotNil(t, loginClientPat)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCommandSide_setupDefaultOrg(t *testing.T) {
|
|
type fields struct {
|
|
eventstore func(t *testing.T) *eventstore.Eventstore
|
|
idGenerator id.Generator
|
|
userPasswordHasher *crypto.Hasher
|
|
roles []authz.RoleMapping
|
|
keyAlgorithm crypto.EncryptionAlgorithm
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
instanceAgg *instance.Aggregate
|
|
orgName string
|
|
machine *AddMachine
|
|
human *AddHuman
|
|
loginClient *AddLoginClient
|
|
ids ZitadelConfig
|
|
}
|
|
type res struct {
|
|
pat bool
|
|
machineKey bool
|
|
loginClientPat bool
|
|
err func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
res res
|
|
}{
|
|
{
|
|
name: "human, machine and login client, ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
slices.Concat(
|
|
orgFilters(
|
|
"ORG",
|
|
true,
|
|
true,
|
|
true,
|
|
),
|
|
[]expect{
|
|
expectPush(
|
|
slices.Concat(
|
|
orgEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"ZITADEL",
|
|
"PROJECT",
|
|
"DOMAIN",
|
|
false,
|
|
true,
|
|
true,
|
|
true,
|
|
),
|
|
)...,
|
|
),
|
|
},
|
|
)...,
|
|
),
|
|
userPasswordHasher: mockPasswordHasher("x"),
|
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, orgIDs()...),
|
|
roles: validZitadelRoles(),
|
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
|
},
|
|
args: args{
|
|
ctx: contextWithInstanceSetupInfo(context.Background(), "INSTANCE", "PROJECT", "console-id", "DOMAIN", language.Dutch),
|
|
instanceAgg: instance.NewAggregate("INSTANCE"),
|
|
orgName: "ZITADEL",
|
|
machine: &AddMachine{
|
|
Machine: &Machine{
|
|
Username: "zitadel-admin-machine",
|
|
Name: "ZITADEL-machine",
|
|
Description: "Admin",
|
|
AccessTokenType: domain.OIDCTokenTypeBearer,
|
|
},
|
|
Pat: &AddPat{
|
|
ExpirationDate: time.Time{},
|
|
Scopes: nil,
|
|
},
|
|
/* not predictable with the key value in the events
|
|
MachineKey: &AddMachineKey{
|
|
Type: domain.AuthNKeyTypeJSON,
|
|
ExpirationDate: time.Time{},
|
|
},
|
|
*/
|
|
},
|
|
human: &AddHuman{
|
|
Username: "zitadel-admin",
|
|
FirstName: "ZITADEL",
|
|
LastName: "Admin",
|
|
Email: Email{
|
|
Address: domain.EmailAddress("admin@zitadel.test"),
|
|
Verified: true,
|
|
},
|
|
PreferredLanguage: language.English,
|
|
Password: "password",
|
|
PasswordChangeRequired: false,
|
|
},
|
|
loginClient: &AddLoginClient{
|
|
Machine: &Machine{
|
|
Username: "zitadel-login-client",
|
|
Name: "ZITADEL-login-client",
|
|
Description: "Login Client",
|
|
AccessTokenType: domain.OIDCTokenTypeBearer,
|
|
},
|
|
Pat: &AddPat{
|
|
ExpirationDate: time.Time{},
|
|
Scopes: nil,
|
|
},
|
|
},
|
|
ids: ZitadelConfig{
|
|
instanceID: "INSTANCE",
|
|
orgID: "ORG",
|
|
projectID: "PROJECT",
|
|
consoleAppID: "console-id",
|
|
authAppID: "auth-id",
|
|
mgmtAppID: "mgmt-id",
|
|
adminAppID: "admin-id",
|
|
},
|
|
},
|
|
res: res{
|
|
pat: true,
|
|
machineKey: false,
|
|
loginClientPat: true,
|
|
err: nil,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := &Commands{
|
|
eventstore: tt.fields.eventstore(t),
|
|
idGenerator: tt.fields.idGenerator,
|
|
zitadelRoles: tt.fields.roles,
|
|
userPasswordHasher: tt.fields.userPasswordHasher,
|
|
keyAlgorithm: tt.fields.keyAlgorithm,
|
|
}
|
|
validations := make([]preparation.Validation, 0)
|
|
pat, mk, loginClientPat, err := setupDefaultOrg(tt.args.ctx, r, &validations, tt.args.instanceAgg, tt.args.orgName, tt.args.machine, tt.args.human, tt.args.loginClient, tt.args.ids)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
|
|
err = testSetup(context.Background(), r, validations)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
|
|
if tt.res.err == nil {
|
|
if tt.res.pat {
|
|
assert.NotNil(t, pat)
|
|
}
|
|
if tt.res.machineKey {
|
|
assert.NotNil(t, mk)
|
|
}
|
|
if tt.res.loginClientPat {
|
|
assert.NotNil(t, loginClientPat)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCommandSide_setupInstanceElements(t *testing.T) {
|
|
type fields struct {
|
|
eventstore func(t *testing.T) *eventstore.Eventstore
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
instanceAgg *instance.Aggregate
|
|
setup *InstanceSetup
|
|
}
|
|
type res struct {
|
|
err func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
res res
|
|
}{
|
|
{
|
|
name: "ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
slices.Concat(
|
|
setupInstanceElementsFilters("INSTANCE"),
|
|
[]expect{
|
|
expectPush(
|
|
setupInstanceElementsEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ZITADEL",
|
|
language.English,
|
|
)...,
|
|
),
|
|
},
|
|
)...,
|
|
),
|
|
},
|
|
args: args{
|
|
ctx: contextWithInstanceSetupInfo(context.Background(), "INSTANCE", "PROJECT", "console-id", "DOMAIN", language.Dutch),
|
|
instanceAgg: instance.NewAggregate("INSTANCE"),
|
|
setup: setupInstanceElementsConfig(),
|
|
},
|
|
res: res{
|
|
err: nil,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := &Commands{
|
|
eventstore: tt.fields.eventstore(t),
|
|
}
|
|
validations := setupInstanceElements(tt.args.instanceAgg, tt.args.setup)
|
|
|
|
err := testSetup(context.Background(), r, validations)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCommandSide_setUpInstance(t *testing.T) {
|
|
type fields struct {
|
|
eventstore func(t *testing.T) *eventstore.Eventstore
|
|
idGenerator id.Generator
|
|
userPasswordHasher *crypto.Hasher
|
|
roles []authz.RoleMapping
|
|
keyAlgorithm crypto.EncryptionAlgorithm
|
|
generateDomain func(string, string) (string, error)
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
setup *InstanceSetup
|
|
}
|
|
type res struct {
|
|
pat bool
|
|
machineKey bool
|
|
loginClientPat bool
|
|
err func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
res res
|
|
}{
|
|
{
|
|
name: "ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
slices.Concat(
|
|
setupInstanceFilters("INSTANCE", "ORG", "PROJECT", "console-id", "DOMAIN"),
|
|
[]expect{
|
|
expectPush(
|
|
setupInstanceEvents(context.Background(),
|
|
"INSTANCE",
|
|
"ORG",
|
|
"PROJECT",
|
|
"console-id",
|
|
"ZITADEL",
|
|
"ZITADEL",
|
|
language.English,
|
|
"DOMAIN",
|
|
false,
|
|
)...,
|
|
),
|
|
},
|
|
)...,
|
|
),
|
|
userPasswordHasher: mockPasswordHasher("x"),
|
|
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, orgIDs()...),
|
|
roles: validZitadelRoles(),
|
|
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
|
generateDomain: func(string, string) (string, error) {
|
|
return "DOMAIN", nil
|
|
},
|
|
},
|
|
args: args{
|
|
ctx: contextWithInstanceSetupInfo(context.Background(), "INSTANCE", "PROJECT", "console-id", "DOMAIN", language.Dutch),
|
|
setup: setupInstanceConfig(),
|
|
},
|
|
res: res{
|
|
err: nil,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := &Commands{
|
|
eventstore: tt.fields.eventstore(t),
|
|
idGenerator: tt.fields.idGenerator,
|
|
zitadelRoles: tt.fields.roles,
|
|
userPasswordHasher: tt.fields.userPasswordHasher,
|
|
keyAlgorithm: tt.fields.keyAlgorithm,
|
|
GenerateDomain: tt.fields.generateDomain,
|
|
}
|
|
|
|
validations, pat, mk, loginClientPat, err := setUpInstance(tt.args.ctx, r, tt.args.setup)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
|
|
err = testSetup(tt.args.ctx, r, validations)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
|
|
if tt.res.err == nil {
|
|
if tt.res.pat {
|
|
assert.NotNil(t, pat)
|
|
}
|
|
if tt.res.machineKey {
|
|
assert.NotNil(t, mk)
|
|
}
|
|
if tt.res.loginClientPat {
|
|
assert.NotNil(t, loginClientPat)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCommandSide_UpdateInstance(t *testing.T) {
|
|
type fields struct {
|
|
eventstore func(t *testing.T) *eventstore.Eventstore
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
name string
|
|
}
|
|
type res struct {
|
|
want *domain.ObjectDetails
|
|
err func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
res res
|
|
}{
|
|
{
|
|
name: "empty name, invalid error",
|
|
fields: fields{
|
|
eventstore: expectEventstore(),
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
|
name: " ",
|
|
},
|
|
res: res{
|
|
err: zerrors.IsErrorInvalidArgument,
|
|
},
|
|
},
|
|
{
|
|
name: "instance not existing, not found error",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
expectFilter(),
|
|
),
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
|
name: "INSTANCE_CHANGED",
|
|
},
|
|
res: res{
|
|
err: zerrors.IsNotFound,
|
|
},
|
|
},
|
|
{
|
|
name: "instance removed, not found error",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
expectFilter(
|
|
eventFromEventPusher(
|
|
instance.NewInstanceAddedEvent(
|
|
context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE",
|
|
),
|
|
),
|
|
eventFromEventPusher(
|
|
instance.NewInstanceRemovedEvent(context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE",
|
|
nil,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
|
name: "INSTANCE_CHANGED",
|
|
},
|
|
res: res{
|
|
err: zerrors.IsNotFound,
|
|
},
|
|
},
|
|
{
|
|
name: "no changes, precondition error",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
expectFilter(
|
|
eventFromEventPusher(
|
|
instance.NewInstanceAddedEvent(
|
|
context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE",
|
|
),
|
|
),
|
|
),
|
|
),
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
|
name: "INSTANCE",
|
|
},
|
|
res: res{
|
|
want: &domain.ObjectDetails{
|
|
ResourceOwner: "INSTANCE",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "instance change, ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
expectFilter(
|
|
eventFromEventPusherWithInstanceID(
|
|
"INSTANCE",
|
|
instance.NewInstanceAddedEvent(context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE",
|
|
),
|
|
),
|
|
),
|
|
expectPush(
|
|
instance.NewInstanceChangedEvent(context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE_CHANGED",
|
|
),
|
|
),
|
|
),
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
|
name: "INSTANCE_CHANGED",
|
|
},
|
|
res: res{
|
|
want: &domain.ObjectDetails{
|
|
ResourceOwner: "INSTANCE",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := &Commands{
|
|
eventstore: tt.fields.eventstore(t),
|
|
}
|
|
got, err := r.UpdateInstance(tt.args.ctx, tt.args.name)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
if tt.res.err == nil {
|
|
assertObjectDetails(t, tt.res.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCommandSide_RemoveInstance(t *testing.T) {
|
|
type fields struct {
|
|
eventstore func(t *testing.T) *eventstore.Eventstore
|
|
}
|
|
type args struct {
|
|
ctx context.Context
|
|
instanceID string
|
|
}
|
|
type res struct {
|
|
want *domain.ObjectDetails
|
|
err func(error) bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
res res
|
|
}{
|
|
{
|
|
name: "instance empty, invalid argument error",
|
|
fields: fields{
|
|
eventstore: func(t *testing.T) *eventstore.Eventstore { return &eventstore.Eventstore{} },
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), " "),
|
|
instanceID: " ",
|
|
},
|
|
res: res{
|
|
err: zerrors.IsErrorInvalidArgument,
|
|
},
|
|
},
|
|
{
|
|
name: "instance too long, invalid argument error",
|
|
fields: fields{
|
|
eventstore: func(t *testing.T) *eventstore.Eventstore { return &eventstore.Eventstore{} },
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "averylonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonginstance"),
|
|
instanceID: "averylonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonginstance",
|
|
},
|
|
res: res{
|
|
err: zerrors.IsErrorInvalidArgument,
|
|
},
|
|
},
|
|
{
|
|
name: "instance not existing, not found error",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
expectFilter(),
|
|
),
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
|
instanceID: "INSTANCE",
|
|
},
|
|
res: res{
|
|
err: zerrors.IsNotFound,
|
|
},
|
|
},
|
|
{
|
|
name: "instance removed, not found error",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
expectFilter(
|
|
eventFromEventPusher(
|
|
instance.NewInstanceAddedEvent(
|
|
context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE",
|
|
),
|
|
),
|
|
eventFromEventPusher(
|
|
instance.NewInstanceRemovedEvent(context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE",
|
|
nil,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
|
instanceID: "INSTANCE",
|
|
},
|
|
res: res{
|
|
err: zerrors.IsNotFound,
|
|
},
|
|
},
|
|
{
|
|
name: "instance remove, ok",
|
|
fields: fields{
|
|
eventstore: expectEventstore(
|
|
expectFilter(
|
|
eventFromEventPusherWithInstanceID(
|
|
"INSTANCE",
|
|
instance.NewInstanceAddedEvent(context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE",
|
|
),
|
|
),
|
|
eventFromEventPusherWithInstanceID(
|
|
"INSTANCE",
|
|
instance.NewDomainAddedEvent(context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"instance.domain",
|
|
true,
|
|
),
|
|
),
|
|
eventFromEventPusherWithInstanceID(
|
|
"INSTANCE",
|
|
instance.NewDomainAddedEvent(context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"custom.domain",
|
|
false,
|
|
),
|
|
),
|
|
),
|
|
expectPush(
|
|
instance.NewInstanceRemovedEvent(context.Background(),
|
|
&instance.NewAggregate("INSTANCE").Aggregate,
|
|
"INSTANCE",
|
|
[]string{
|
|
"instance.domain",
|
|
"custom.domain",
|
|
},
|
|
),
|
|
milestone.NewReachedEvent(context.Background(),
|
|
milestone.NewInstanceAggregate("INSTANCE"),
|
|
milestone.InstanceDeleted,
|
|
),
|
|
),
|
|
),
|
|
},
|
|
args: args{
|
|
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
|
instanceID: "INSTANCE",
|
|
},
|
|
res: res{
|
|
want: &domain.ObjectDetails{
|
|
ResourceOwner: "INSTANCE",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := &Commands{
|
|
eventstore: tt.fields.eventstore(t),
|
|
caches: &Caches{
|
|
milestones: noop.NewCache[milestoneIndex, string, *MilestonesReached](),
|
|
},
|
|
}
|
|
got, err := r.RemoveInstance(tt.args.ctx, tt.args.instanceID)
|
|
if tt.res.err == nil {
|
|
assert.NoError(t, err)
|
|
}
|
|
if tt.res.err != nil && !tt.res.err(err) {
|
|
t.Errorf("got wrong err: %v ", err)
|
|
}
|
|
if tt.res.err == nil {
|
|
assertObjectDetails(t, tt.res.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInstanceSetupFeatures_ToInstanceFeatures(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type fields struct {
|
|
LoginDefaultOrg *bool
|
|
UserSchema *bool
|
|
TokenExchange *bool
|
|
ImprovedPerformance []feature.ImprovedPerformanceType
|
|
DebugOIDCParentError *bool
|
|
OIDCSingleV1SessionTermination *bool
|
|
EnableBackChannelLogout *bool
|
|
LoginV2 *InstanceSetupFeatureLoginV2
|
|
PermissionCheckV2 *bool
|
|
ConsoleUseV2UserApi *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),
|
|
TokenExchange: gu.Ptr(true),
|
|
ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgDomainVerified},
|
|
DebugOIDCParentError: gu.Ptr(true),
|
|
OIDCSingleV1SessionTermination: gu.Ptr(false),
|
|
EnableBackChannelLogout: gu.Ptr(true),
|
|
PermissionCheckV2: gu.Ptr(true),
|
|
ConsoleUseV2UserApi: gu.Ptr(false),
|
|
EnableRelationalTables: gu.Ptr(true),
|
|
},
|
|
want: &InstanceFeatures{
|
|
LoginDefaultOrg: gu.Ptr(true),
|
|
UserSchema: gu.Ptr(false),
|
|
TokenExchange: gu.Ptr(true),
|
|
ImprovedPerformance: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgDomainVerified},
|
|
DebugOIDCParentError: gu.Ptr(true),
|
|
OIDCSingleV1SessionTermination: gu.Ptr(false),
|
|
EnableBackChannelLogout: gu.Ptr(true),
|
|
LoginV2: nil,
|
|
PermissionCheckV2: gu.Ptr(true),
|
|
ConsoleUseV2UserApi: 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,
|
|
TokenExchange: tc.fields.TokenExchange,
|
|
ImprovedPerformance: tc.fields.ImprovedPerformance,
|
|
DebugOIDCParentError: tc.fields.DebugOIDCParentError,
|
|
OIDCSingleV1SessionTermination: tc.fields.OIDCSingleV1SessionTermination,
|
|
EnableBackChannelLogout: tc.fields.EnableBackChannelLogout,
|
|
LoginV2: tc.fields.LoginV2,
|
|
PermissionCheckV2: tc.fields.PermissionCheckV2,
|
|
ConsoleUseV2UserApi: tc.fields.ConsoleUseV2UserApi,
|
|
EnableRelationalTables: tc.fields.EnableRelationalTables,
|
|
}
|
|
got, err := f.ToInstanceFeatures()
|
|
|
|
require.Equal(t, tc.wantErr, err != nil)
|
|
assert.Equal(t, tc.want, got)
|
|
})
|
|
}
|
|
}
|