mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 23:27:23 +00:00
feat(cli): setup (#3267)
* commander * commander * selber! * move to packages * fix(errors): implement Is interface * test: command * test: commands * add init steps * setup tenant * add default step yaml * possibility to set password * merge v2 into v2-commander * fix: rename iam command side to instance * fix: rename iam command side to instance * fix: rename iam command side to instance * fix: rename iam command side to instance * fix: search query builder can filter events in memory * fix: filters for add member * fix(setup): add `ExternalSecure` to config * chore: name iam to instance * fix: matching * remove unsued func * base url * base url * test(command): filter funcs * test: commands * fix: rename orgiampolicy to domain policy * start from init * commands * config * fix indexes and add constraints * fixes * fix: merge conflicts * fix: protos * fix: md files * setup * add deprecated org iam policy again * typo * fix search query * fix filter * Apply suggestions from code review * remove custom org from org setup * add todos for verification * change apps creation * simplify package structure * fix error * move preparation helper for tests * fix unique constraints * fix config mapping in setup * fix error handling in encryption_keys.go * fix projection config * fix query from old views to projection * fix setup of mgmt api * set iam project and fix instance projection * imports Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
parent
9d4f296c62
commit
c5b99274d7
@ -26,6 +26,7 @@ func New() *cobra.Command {
|
||||
initialise.New(),
|
||||
setup.New(),
|
||||
start.New(),
|
||||
start.NewStartFromInit(),
|
||||
key.New(),
|
||||
)
|
||||
|
||||
|
@ -1,13 +1,29 @@
|
||||
package initialise
|
||||
|
||||
import "github.com/caos/zitadel/internal/database"
|
||||
import (
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/database"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Database database.Config
|
||||
AdminUser database.User
|
||||
Log *logging.Config
|
||||
}
|
||||
|
||||
func adminConfig(config Config) database.Config {
|
||||
func MustNewConfig(v *viper.Viper) *Config {
|
||||
config := new(Config)
|
||||
err := v.Unmarshal(config)
|
||||
logging.OnError(err).Fatal("unable to read config")
|
||||
|
||||
err = config.Log.SetLogger()
|
||||
logging.OnError(err).Fatal("unable to set logger")
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func adminConfig(config *Config) database.Config {
|
||||
adminConfig := config.Database
|
||||
adminConfig.Username = config.AdminUser.Username
|
||||
adminConfig.Password = config.AdminUser.Password
|
||||
|
@ -8,9 +8,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
//sql import
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
"github.com/caos/zitadel/internal/database"
|
||||
)
|
||||
|
||||
@ -28,20 +25,10 @@ The user provided by flags needs priviledge to
|
||||
- see other users and create a new one if the user does not exist
|
||||
- grant all rights of the ZITADEL database to the user created if not yet set
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := Config{}
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := initialise(config,
|
||||
VerifyUser(config.Database.User.Username, config.Database.User.Password),
|
||||
VerifyDatabase(config.Database.Database),
|
||||
VerifyGrant(config.Database.Database, config.Database.User.Username),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := MustNewConfig(viper.GetViper())
|
||||
|
||||
return verifyZitadel(config.Database)
|
||||
InitAll(config)
|
||||
},
|
||||
}
|
||||
|
||||
@ -49,7 +36,19 @@ The user provided by flags needs priviledge to
|
||||
return cmd
|
||||
}
|
||||
|
||||
func initialise(config Config, steps ...func(*sql.DB) error) error {
|
||||
func InitAll(config *Config) {
|
||||
err := initialise(config,
|
||||
VerifyUser(config.Database.Username, config.Database.Password),
|
||||
VerifyDatabase(config.Database.Database),
|
||||
VerifyGrant(config.Database.Database, config.Database.Username),
|
||||
)
|
||||
logging.OnError(err).Fatal("unable to initialize the database")
|
||||
|
||||
err = verifyZitadel(config.Database)
|
||||
logging.OnError(err).Fatal("unable to initialize ZITADEL")
|
||||
}
|
||||
|
||||
func initialise(config *Config, steps ...func(*sql.DB) error) error {
|
||||
logging.Info("initialization started")
|
||||
|
||||
db, err := database.Connect(adminConfig(config))
|
||||
|
@ -1,5 +1,6 @@
|
||||
CREATE TABLE eventstore.unique_constraints (
|
||||
instance_id TEXT,
|
||||
unique_type TEXT,
|
||||
unique_field TEXT,
|
||||
PRIMARY KEY (unique_type, unique_field)
|
||||
PRIMARY KEY (instance_id, unique_type, unique_field)
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -30,12 +31,11 @@ The user provided by flags needs priviledge to
|
||||
- see other users and create a new one if the user does not exist
|
||||
- grant all rights of the ZITADEL database to the user created if not yet set
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := Config{}
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
return initialise(config, VerifyDatabase(config.Database.Database))
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := MustNewConfig(viper.New())
|
||||
|
||||
err := initialise(config, VerifyDatabase(config.Database.Database))
|
||||
logging.OnError(err).Fatal("unable to initialize the database")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,11 @@ func newGrant() *cobra.Command {
|
||||
Prereqesits:
|
||||
- cockroachdb
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := Config{}
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
return initialise(config, VerifyGrant(config.Database.Database, config.Database.User.Username))
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := MustNewConfig(viper.New())
|
||||
|
||||
err := initialise(config, VerifyGrant(config.Database.Database, config.Database.User.Username))
|
||||
logging.OnError(err).Fatal("unable to set grant")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +29,11 @@ The user provided by flags needs priviledge to
|
||||
- see other users and create a new one if the user does not exist
|
||||
- grant all rights of the ZITADEL database to the user created if not yet set
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := Config{}
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
return initialise(config, VerifyUser(config.Database.User.Username, config.Database.User.Password))
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := MustNewConfig(viper.New())
|
||||
|
||||
err := initialise(config, VerifyUser(config.Database.Username, config.Database.Password))
|
||||
logging.OnError(err).Fatal("unable to init user")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func VerifyZitadel(db *sql.DB) error {
|
||||
}
|
||||
|
||||
func verifyZitadel(config database.Config) error {
|
||||
logging.WithFields("database", config.Database).Info("verify database")
|
||||
logging.WithFields("database", config.Database).Info("verify zitadel")
|
||||
db, err := database.Connect(config)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -3,27 +3,27 @@ CREATE SCHEMA adminapi;
|
||||
CREATE TABLE adminapi.locks (
|
||||
locker_id TEXT,
|
||||
locked_until TIMESTAMPTZ(3),
|
||||
projection_name TEXT,
|
||||
view_name TEXT,
|
||||
|
||||
PRIMARY KEY (projection_name)
|
||||
PRIMARY KEY (view_name)
|
||||
);
|
||||
|
||||
CREATE TABLE adminapi.current_sequences (
|
||||
projection_name TEXT,
|
||||
aggregate_type TEXT,
|
||||
view_name TEXT,
|
||||
current_sequence BIGINT,
|
||||
timestamp TIMESTAMPTZ,
|
||||
event_timestamp TIMESTAMPTZ,
|
||||
last_successful_spooler_run TIMESTAMPTZ,
|
||||
|
||||
PRIMARY KEY (projection_name, aggregate_type)
|
||||
PRIMARY KEY (view_name)
|
||||
);
|
||||
|
||||
CREATE TABLE adminapi.failed_events (
|
||||
projection_name TEXT,
|
||||
view_name TEXT,
|
||||
failed_sequence BIGINT,
|
||||
failure_count SMALLINT,
|
||||
error TEXT,
|
||||
err_msg TEXT,
|
||||
|
||||
PRIMARY KEY (projection_name, failed_sequence)
|
||||
PRIMARY KEY (view_name, failed_sequence)
|
||||
);
|
||||
|
||||
CREATE TABLE adminapi.styling (
|
||||
|
@ -3,27 +3,27 @@ CREATE SCHEMA auth;
|
||||
CREATE TABLE auth.locks (
|
||||
locker_id TEXT,
|
||||
locked_until TIMESTAMPTZ(3),
|
||||
projection_name TEXT,
|
||||
view_name TEXT,
|
||||
|
||||
PRIMARY KEY (projection_name)
|
||||
PRIMARY KEY (view_name)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.current_sequences (
|
||||
projection_name TEXT,
|
||||
aggregate_type TEXT,
|
||||
view_name TEXT,
|
||||
current_sequence BIGINT,
|
||||
timestamp TIMESTAMPTZ,
|
||||
event_timestamp TIMESTAMPTZ,
|
||||
last_successful_spooler_run TIMESTAMPTZ,
|
||||
|
||||
PRIMARY KEY (projection_name, aggregate_type)
|
||||
PRIMARY KEY (view_name)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.failed_events (
|
||||
projection_name TEXT,
|
||||
view_name TEXT,
|
||||
failed_sequence BIGINT,
|
||||
failure_count SMALLINT,
|
||||
error TEXT,
|
||||
err_msg TEXT,
|
||||
|
||||
PRIMARY KEY (projection_name, failed_sequence)
|
||||
PRIMARY KEY (view_name, failed_sequence)
|
||||
);
|
||||
|
||||
CREATE TABLE auth.users (
|
||||
|
@ -3,27 +3,27 @@ CREATE SCHEMA authz;
|
||||
CREATE TABLE authz.locks (
|
||||
locker_id TEXT,
|
||||
locked_until TIMESTAMPTZ(3),
|
||||
projection_name TEXT,
|
||||
view_name TEXT,
|
||||
|
||||
PRIMARY KEY (projection_name)
|
||||
PRIMARY KEY (view_name)
|
||||
);
|
||||
|
||||
CREATE TABLE authz.current_sequences (
|
||||
projection_name TEXT,
|
||||
aggregate_type TEXT,
|
||||
view_name TEXT,
|
||||
current_sequence BIGINT,
|
||||
timestamp TIMESTAMPTZ,
|
||||
event_timestamp TIMESTAMPTZ,
|
||||
last_successful_spooler_run TIMESTAMPTZ,
|
||||
|
||||
PRIMARY KEY (projection_name, aggregate_type)
|
||||
PRIMARY KEY (view_name)
|
||||
);
|
||||
|
||||
CREATE TABLE authz.failed_events (
|
||||
projection_name TEXT,
|
||||
view_name TEXT,
|
||||
failed_sequence BIGINT,
|
||||
failure_count SMALLINT,
|
||||
error TEXT,
|
||||
err_msg TEXT,
|
||||
|
||||
PRIMARY KEY (projection_name, failed_sequence)
|
||||
PRIMARY KEY (view_name, failed_sequence)
|
||||
);
|
||||
|
||||
CREATE TABLE authz.user_memberships (
|
||||
|
@ -3,27 +3,27 @@ CREATE SCHEMA notification;
|
||||
CREATE TABLE notification.locks (
|
||||
locker_id TEXT,
|
||||
locked_until TIMESTAMPTZ(3),
|
||||
projection_name TEXT,
|
||||
view_name TEXT,
|
||||
|
||||
PRIMARY KEY (projection_name)
|
||||
PRIMARY KEY (view_name)
|
||||
);
|
||||
|
||||
CREATE TABLE notification.current_sequences (
|
||||
projection_name TEXT,
|
||||
aggregate_type TEXT,
|
||||
view_name TEXT,
|
||||
current_sequence BIGINT,
|
||||
timestamp TIMESTAMPTZ,
|
||||
event_timestamp TIMESTAMPTZ,
|
||||
last_successful_spooler_run TIMESTAMPTZ,
|
||||
|
||||
PRIMARY KEY (projection_name, aggregate_type)
|
||||
PRIMARY KEY (view_name)
|
||||
);
|
||||
|
||||
CREATE TABLE notification.failed_events (
|
||||
projection_name TEXT,
|
||||
view_name TEXT,
|
||||
failed_sequence BIGINT,
|
||||
failure_count SMALLINT,
|
||||
error TEXT,
|
||||
err_msg TEXT,
|
||||
|
||||
PRIMARY KEY (projection_name, failed_sequence)
|
||||
PRIMARY KEY (view_name, failed_sequence)
|
||||
);
|
||||
|
||||
CREATE TABLE notification.notify_users (
|
||||
|
22
cmd/admin/setup/02.go
Normal file
22
cmd/admin/setup/02.go
Normal file
@ -0,0 +1,22 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
command "github.com/caos/zitadel/internal/command/v2"
|
||||
)
|
||||
|
||||
type DefaultInstance struct {
|
||||
cmd *command.Command
|
||||
InstanceSetup command.InstanceSetup
|
||||
}
|
||||
|
||||
func (mig *DefaultInstance) Execute(ctx context.Context) error {
|
||||
_, err := mig.cmd.SetUpInstance(ctx, &mig.InstanceSetup)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *DefaultInstance) String() string {
|
||||
return "02_default_instance"
|
||||
}
|
@ -1,13 +1,58 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/config/hook"
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/database"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Database database.Config
|
||||
SystemDefaults systemdefaults.SystemDefaults
|
||||
InternalAuthZ authz.Config
|
||||
ExternalPort uint16
|
||||
ExternalDomain string
|
||||
ExternalSecure bool
|
||||
Log *logging.Config
|
||||
}
|
||||
|
||||
func MustNewConfig(v *viper.Viper) *Config {
|
||||
config := new(Config)
|
||||
err := v.Unmarshal(config)
|
||||
logging.OnError(err).Fatal("unable to read config")
|
||||
|
||||
err = config.Log.SetLogger()
|
||||
logging.OnError(err).Fatal("unable to set logger")
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type Steps struct {
|
||||
S1ProjectionTable *ProjectionTable
|
||||
S2DefaultInstance *DefaultInstance
|
||||
}
|
||||
|
||||
func MustNewSteps(v *viper.Viper) *Steps {
|
||||
v.SetConfigType("yaml")
|
||||
err := v.ReadConfig(bytes.NewBuffer(defaultSteps))
|
||||
logging.OnError(err).Fatal("unable to read setup steps")
|
||||
|
||||
steps := new(Steps)
|
||||
err = v.Unmarshal(steps,
|
||||
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
)),
|
||||
)
|
||||
logging.OnError(err).Fatal("unable to read steps")
|
||||
return steps
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "embed"
|
||||
|
||||
@ -9,6 +8,8 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
http_util "github.com/caos/zitadel/internal/api/http"
|
||||
command "github.com/caos/zitadel/internal/command/v2"
|
||||
"github.com/caos/zitadel/internal/database"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/migration"
|
||||
@ -27,32 +28,30 @@ func New() *cobra.Command {
|
||||
Requirements:
|
||||
- cockroachdb`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := new(Config)
|
||||
err := viper.Unmarshal(config)
|
||||
logging.OnError(err).Fatal("unable to read config")
|
||||
config := MustNewConfig(viper.GetViper())
|
||||
steps := MustNewSteps(viper.New())
|
||||
|
||||
v := viper.New()
|
||||
v.SetConfigType("yaml")
|
||||
err = v.ReadConfig(bytes.NewBuffer(defaultSteps))
|
||||
logging.OnError(err).Fatal("unable to read setup steps")
|
||||
|
||||
steps := new(Steps)
|
||||
err = v.Unmarshal(steps)
|
||||
logging.OnError(err).Fatal("unable to read steps")
|
||||
|
||||
setup(config, steps)
|
||||
Setup(config, steps)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func setup(config *Config, steps *Steps) {
|
||||
func Setup(config *Config, steps *Steps) {
|
||||
dbClient, err := database.Connect(config.Database)
|
||||
logging.OnError(err).Fatal("unable to connect to database")
|
||||
|
||||
eventstoreClient, err := eventstore.Start(dbClient)
|
||||
logging.OnError(err).Fatal("unable to start eventstore")
|
||||
migration.RegisterMappers(eventstoreClient)
|
||||
|
||||
cmd := command.New(eventstoreClient, "localhost", config.SystemDefaults)
|
||||
|
||||
steps.S2DefaultInstance.cmd = cmd
|
||||
steps.S1ProjectionTable = &ProjectionTable{dbClient: dbClient}
|
||||
steps.S2DefaultInstance.InstanceSetup.Zitadel.IsDevMode = !config.ExternalSecure
|
||||
steps.S2DefaultInstance.InstanceSetup.Zitadel.BaseURL = http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||
|
||||
migration.Migrate(context.Background(), eventstoreClient, steps.S1ProjectionTable)
|
||||
ctx := context.Background()
|
||||
migration.Migrate(ctx, eventstoreClient, steps.S1ProjectionTable)
|
||||
migration.Migrate(ctx, eventstoreClient, steps.S2DefaultInstance)
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
66
cmd/admin/start/config.go
Normal file
66
cmd/admin/start/config.go
Normal file
@ -0,0 +1,66 @@
|
||||
package start
|
||||
|
||||
import (
|
||||
"github.com/caos/logging"
|
||||
admin_es "github.com/caos/zitadel/internal/admin/repository/eventsourcing"
|
||||
internal_authz "github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/api/oidc"
|
||||
"github.com/caos/zitadel/internal/api/ui/console"
|
||||
"github.com/caos/zitadel/internal/api/ui/login"
|
||||
auth_es "github.com/caos/zitadel/internal/auth/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/authz"
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/database"
|
||||
"github.com/caos/zitadel/internal/notification"
|
||||
"github.com/caos/zitadel/internal/query/projection"
|
||||
static_config "github.com/caos/zitadel/internal/static/config"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Log *logging.Config
|
||||
Port uint16
|
||||
ExternalPort uint16
|
||||
ExternalDomain string
|
||||
ExternalSecure bool
|
||||
Database database.Config
|
||||
Projections projection.Config
|
||||
AuthZ authz.Config
|
||||
Auth auth_es.Config
|
||||
Admin admin_es.Config
|
||||
UserAgentCookie *middleware.UserAgentCookieConfig
|
||||
OIDC oidc.Config
|
||||
Login login.Config
|
||||
Console console.Config
|
||||
Notification notification.Config
|
||||
AssetStorage static_config.AssetStorageConfig
|
||||
InternalAuthZ internal_authz.Config
|
||||
SystemDefaults systemdefaults.SystemDefaults
|
||||
EncryptionKeys *encryptionKeyConfig
|
||||
}
|
||||
|
||||
func MustNewConfig(v *viper.Viper) *Config {
|
||||
config := new(Config)
|
||||
|
||||
err := v.Unmarshal(config)
|
||||
logging.OnError(err).Fatal("unable to read config")
|
||||
|
||||
err = config.Log.SetLogger()
|
||||
logging.OnError(err).Fatal("unable to set logger")
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type encryptionKeyConfig struct {
|
||||
DomainVerification *crypto.KeyConfig
|
||||
IDPConfig *crypto.KeyConfig
|
||||
OIDC *crypto.KeyConfig
|
||||
OTP *crypto.KeyConfig
|
||||
SMS *crypto.KeyConfig
|
||||
SMTP *crypto.KeyConfig
|
||||
User *crypto.KeyConfig
|
||||
CSRFCookieKeyID string
|
||||
UserAgentCookieKeyID string
|
||||
}
|
@ -35,7 +35,7 @@ type encryptionKeys struct {
|
||||
func ensureEncryptionKeys(keyConfig *encryptionKeyConfig, keyStorage crypto.KeyStorage) (*encryptionKeys, error) {
|
||||
keys, err := keyStorage.ReadKeys()
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
return nil, err
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
if err := createDefaultKeys(keyStorage); err != nil {
|
||||
|
31
cmd/admin/start/flags.go
Normal file
31
cmd/admin/start/flags.go
Normal file
@ -0,0 +1,31 @@
|
||||
package start
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func startFlags(cmd *cobra.Command) {
|
||||
bindUint16Flag(cmd, "port", "port to run ZITADEL on")
|
||||
bindStringFlag(cmd, "externalDomain", "domain ZITADEL will be exposed on")
|
||||
bindStringFlag(cmd, "externalPort", "port ZITADEL will be exposed on")
|
||||
bindBoolFlag(cmd, "externalSecure", "if ZITADEL will be served on HTTPS")
|
||||
|
||||
cmd.PersistentFlags().String(flagMasterKey, "", "masterkey for en/decryption keys")
|
||||
|
||||
}
|
||||
|
||||
func bindStringFlag(cmd *cobra.Command, name, description string) {
|
||||
cmd.PersistentFlags().String(name, viper.GetString(name), description)
|
||||
viper.BindPFlag(name, cmd.PersistentFlags().Lookup(name))
|
||||
}
|
||||
|
||||
func bindUint16Flag(cmd *cobra.Command, name, description string) {
|
||||
cmd.PersistentFlags().Uint16(name, uint16(viper.GetUint(name)), description)
|
||||
viper.BindPFlag(name, cmd.PersistentFlags().Lookup(name))
|
||||
}
|
||||
|
||||
func bindBoolFlag(cmd *cobra.Command, name, description string) {
|
||||
cmd.PersistentFlags().Bool(name, viper.GetBool(name), description)
|
||||
viper.BindPFlag(name, cmd.PersistentFlags().Lookup(name))
|
||||
}
|
@ -15,7 +15,6 @@ import (
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/net/http2"
|
||||
@ -37,17 +36,13 @@ import (
|
||||
"github.com/caos/zitadel/internal/authz"
|
||||
authz_repo "github.com/caos/zitadel/internal/authz/repository"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
cryptoDB "github.com/caos/zitadel/internal/crypto/database"
|
||||
"github.com/caos/zitadel/internal/database"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"github.com/caos/zitadel/internal/notification"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/internal/query/projection"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
static_config "github.com/caos/zitadel/internal/static/config"
|
||||
"github.com/caos/zitadel/internal/webauthn"
|
||||
"github.com/caos/zitadel/openapi"
|
||||
)
|
||||
@ -64,82 +59,19 @@ func New() *cobra.Command {
|
||||
Requirements:
|
||||
- cockroachdb`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := new(startConfig)
|
||||
err := viper.Unmarshal(config, viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToSliceHookFunc(":"),
|
||||
)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = config.Log.SetLogger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
masterKey, _ := cmd.Flags().GetString("masterkey")
|
||||
config := MustNewConfig(viper.GetViper())
|
||||
masterKey, _ := cmd.Flags().GetString(flagMasterKey)
|
||||
|
||||
return startZitadel(config, masterKey)
|
||||
},
|
||||
}
|
||||
bindUint16Flag(start, "port", "port to run ZITADEL on")
|
||||
bindStringFlag(start, "externalDomain", "domain ZITADEL will be exposed on")
|
||||
bindStringFlag(start, "externalPort", "port ZITADEL will be exposed on")
|
||||
bindBoolFlag(start, "externalSecure", "if ZITADEL will be served on HTTPS")
|
||||
|
||||
start.PersistentFlags().String(flagMasterKey, "", "masterkey for en/decryption keys")
|
||||
startFlags(start)
|
||||
|
||||
return start
|
||||
}
|
||||
|
||||
func bindStringFlag(cmd *cobra.Command, name, description string) {
|
||||
cmd.PersistentFlags().String(name, viper.GetString(name), description)
|
||||
viper.BindPFlag(name, cmd.PersistentFlags().Lookup(name))
|
||||
}
|
||||
|
||||
func bindUint16Flag(cmd *cobra.Command, name, description string) {
|
||||
cmd.PersistentFlags().Uint16(name, uint16(viper.GetUint(name)), description)
|
||||
viper.BindPFlag(name, cmd.PersistentFlags().Lookup(name))
|
||||
}
|
||||
|
||||
func bindBoolFlag(cmd *cobra.Command, name, description string) {
|
||||
cmd.PersistentFlags().Bool(name, viper.GetBool(name), description)
|
||||
viper.BindPFlag(name, cmd.PersistentFlags().Lookup(name))
|
||||
}
|
||||
|
||||
type startConfig struct {
|
||||
Log *logging.Config
|
||||
Port uint16
|
||||
ExternalPort uint16
|
||||
ExternalDomain string
|
||||
ExternalSecure bool
|
||||
Database database.Config
|
||||
Projections projection.Config
|
||||
AuthZ authz.Config
|
||||
Auth auth_es.Config
|
||||
Admin admin_es.Config
|
||||
UserAgentCookie *middleware.UserAgentCookieConfig
|
||||
OIDC oidc.Config
|
||||
Login login.Config
|
||||
Console console.Config
|
||||
Notification notification.Config
|
||||
AssetStorage static_config.AssetStorageConfig
|
||||
InternalAuthZ internal_authz.Config
|
||||
SystemDefaults systemdefaults.SystemDefaults
|
||||
EncryptionKeys *encryptionKeyConfig
|
||||
}
|
||||
|
||||
type encryptionKeyConfig struct {
|
||||
DomainVerification *crypto.KeyConfig
|
||||
IDPConfig *crypto.KeyConfig
|
||||
OIDC *crypto.KeyConfig
|
||||
OTP *crypto.KeyConfig
|
||||
SMS *crypto.KeyConfig
|
||||
SMTP *crypto.KeyConfig
|
||||
User *crypto.KeyConfig
|
||||
CSRFCookieKeyID string
|
||||
UserAgentCookieKeyID string
|
||||
}
|
||||
|
||||
func startZitadel(config *startConfig, masterKey string) error {
|
||||
func startZitadel(config *Config, masterKey string) error {
|
||||
ctx := context.Background()
|
||||
keyChan := make(chan interface{})
|
||||
|
||||
@ -197,7 +129,7 @@ func startZitadel(config *startConfig, masterKey string) error {
|
||||
return listen(ctx, router, config.Port)
|
||||
}
|
||||
|
||||
func startAPIs(ctx context.Context, router *mux.Router, commands *command.Commands, queries *query.Queries, eventstore *eventstore.Eventstore, dbClient *sql.DB, keyChan chan interface{}, config *startConfig, store static.Storage, authZRepo authz_repo.Repository, keys *encryptionKeys) error {
|
||||
func startAPIs(ctx context.Context, router *mux.Router, commands *command.Commands, queries *query.Queries, eventstore *eventstore.Eventstore, dbClient *sql.DB, keyChan chan interface{}, config *Config, store static.Storage, authZRepo authz_repo.Repository, keys *encryptionKeys) error {
|
||||
repo := struct {
|
||||
authz_repo.Repository
|
||||
*query.Queries
|
||||
|
40
cmd/admin/start/start_from_init.go
Normal file
40
cmd/admin/start/start_from_init.go
Normal file
@ -0,0 +1,40 @@
|
||||
package start
|
||||
|
||||
import (
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/cmd/admin/initialise"
|
||||
"github.com/caos/zitadel/cmd/admin/setup"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func NewStartFromInit() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "start-from-init",
|
||||
Short: "cold starts zitadel",
|
||||
Long: `cold starts ZITADEL.
|
||||
First the minimum requirements to start ZITADEL are set up.
|
||||
Second the initial events are created.
|
||||
Last ZITADEL starts.
|
||||
|
||||
Requirements:
|
||||
- cockroachdb`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
initialise.InitAll(initialise.MustNewConfig(viper.GetViper()))
|
||||
|
||||
setupConfig := setup.MustNewConfig(viper.GetViper())
|
||||
setupSteps := setup.MustNewSteps(viper.New())
|
||||
setup.Setup(setupConfig, setupSteps)
|
||||
|
||||
startConfig := MustNewConfig(viper.GetViper())
|
||||
startMasterKey, _ := cmd.Flags().GetString(flagMasterKey)
|
||||
|
||||
err := startZitadel(startConfig, startMasterKey)
|
||||
logging.OnError(err).Fatal("unable to start zitadel")
|
||||
},
|
||||
}
|
||||
|
||||
startFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
@ -35,7 +35,6 @@ AdminUser:
|
||||
Key: ""
|
||||
|
||||
Projections:
|
||||
Config:
|
||||
RequeueEvery: 10s
|
||||
RetryFailedAfter: 1s
|
||||
MaxFailureCount: 5
|
||||
|
@ -4865,7 +4865,7 @@ This is an empty request
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| policy | zitadel.policy.v1.DomainPolicy | - | |
|
||||
| policy | zitadel.policy.v1.OrgIAMPolicy | - | |
|
||||
|
||||
|
||||
|
||||
|
@ -121,6 +121,10 @@ func GetInstance(ctx context.Context) Instance {
|
||||
return instance
|
||||
}
|
||||
|
||||
func WithInstance(ctx context.Context, instance Instance) context.Context {
|
||||
return context.WithValue(ctx, instanceKey, instance)
|
||||
}
|
||||
|
||||
func GetRequestPermissionsFromCtx(ctx context.Context) []string {
|
||||
ctxPermission, _ := ctx.Value(requestPermissionsKey).([]string)
|
||||
return ctxPermission
|
||||
|
@ -26,64 +26,6 @@ func (s *Server) GetCustomDomainPolicy(ctx context.Context, req *admin_pb.GetCus
|
||||
return &admin_pb.GetCustomDomainPolicyResponse{Policy: policy_grpc.DomainPolicyToPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddCustomOrgIAMPolicy(ctx context.Context, req *admin_pb.AddCustomOrgIAMPolicyRequest) (*admin_pb.AddCustomOrgIAMPolicyResponse, error) {
|
||||
policy, err := s.command.AddOrgDomainPolicy(ctx, req.OrgId, domainPolicyToDomain(req.UserLoginMustBeDomain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.AddCustomOrgIAMPolicyResponse{
|
||||
Details: object.AddToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.ChangeDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateOrgIAMPolicy(ctx context.Context, req *admin_pb.UpdateOrgIAMPolicyRequest) (*admin_pb.UpdateOrgIAMPolicyResponse, error) {
|
||||
config, err := s.command.ChangeDefaultDomainPolicy(ctx, updateOrgIAMPolicyToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.UpdateOrgIAMPolicyResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
config.Sequence,
|
||||
config.ChangeDate,
|
||||
config.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateCustomOrgIAMPolicy(ctx context.Context, req *admin_pb.UpdateCustomOrgIAMPolicyRequest) (*admin_pb.UpdateCustomOrgIAMPolicyResponse, error) {
|
||||
config, err := s.command.ChangeOrgDomainPolicy(ctx, req.OrgId, updateCustomOrgIAMPolicyToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.UpdateCustomOrgIAMPolicyResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
config.Sequence,
|
||||
config.ChangeDate,
|
||||
config.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetOrgIAMPolicy(ctx context.Context, _ *admin_pb.GetOrgIAMPolicyRequest) (*admin_pb.GetOrgIAMPolicyResponse, error) {
|
||||
policy, err := s.query.DefaultDomainPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetOrgIAMPolicyResponse{Policy: policy_grpc.DomainPolicyToOrgIAMPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomOrgIAMPolicy(ctx context.Context, req *admin_pb.GetCustomOrgIAMPolicyRequest) (*admin_pb.GetCustomOrgIAMPolicyResponse, error) {
|
||||
policy, err := s.query.DomainPolicyByOrg(ctx, req.OrgId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetCustomOrgIAMPolicyResponse{Policy: policy_grpc.DomainPolicyToOrgIAMPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddCustomDomainPolicy(ctx context.Context, req *admin_pb.AddCustomDomainPolicyRequest) (*admin_pb.AddCustomDomainPolicyResponse, error) {
|
||||
policy, err := s.command.AddOrgDomainPolicy(ctx, req.OrgId, domainPolicyToDomain(req.UserLoginMustBeDomain))
|
||||
if err != nil {
|
||||
@ -158,6 +100,64 @@ func updateCustomDomainPolicyToDomain(req *admin_pb.UpdateCustomDomainPolicyRequ
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) AddCustomOrgIAMPolicy(ctx context.Context, req *admin_pb.AddCustomOrgIAMPolicyRequest) (*admin_pb.AddCustomOrgIAMPolicyResponse, error) {
|
||||
policy, err := s.command.AddOrgDomainPolicy(ctx, req.OrgId, domainPolicyToDomain(req.UserLoginMustBeDomain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.AddCustomOrgIAMPolicyResponse{
|
||||
Details: object.AddToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.ChangeDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateOrgIAMPolicy(ctx context.Context, req *admin_pb.UpdateOrgIAMPolicyRequest) (*admin_pb.UpdateOrgIAMPolicyResponse, error) {
|
||||
config, err := s.command.ChangeDefaultDomainPolicy(ctx, updateOrgIAMPolicyToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.UpdateOrgIAMPolicyResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
config.Sequence,
|
||||
config.ChangeDate,
|
||||
config.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateCustomOrgIAMPolicy(ctx context.Context, req *admin_pb.UpdateCustomOrgIAMPolicyRequest) (*admin_pb.UpdateCustomOrgIAMPolicyResponse, error) {
|
||||
config, err := s.command.ChangeOrgDomainPolicy(ctx, req.OrgId, updateCustomOrgIAMPolicyToDomain(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.UpdateCustomOrgIAMPolicyResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
config.Sequence,
|
||||
config.ChangeDate,
|
||||
config.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetOrgIAMPolicy(ctx context.Context, _ *admin_pb.GetOrgIAMPolicyRequest) (*admin_pb.GetOrgIAMPolicyResponse, error) {
|
||||
policy, err := s.query.DefaultDomainPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetOrgIAMPolicyResponse{Policy: policy_grpc.DomainPolicyToOrgIAMPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetCustomOrgIAMPolicy(ctx context.Context, req *admin_pb.GetCustomOrgIAMPolicyRequest) (*admin_pb.GetCustomOrgIAMPolicyResponse, error) {
|
||||
policy, err := s.query.DomainPolicyByOrg(ctx, req.OrgId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetCustomOrgIAMPolicyResponse{Policy: policy_grpc.DomainPolicyToOrgIAMPb(policy)}, nil
|
||||
}
|
||||
|
||||
func updateOrgIAMPolicyToDomain(req *admin_pb.UpdateOrgIAMPolicyRequest) *domain.DomainPolicy {
|
||||
return &domain.DomainPolicy{
|
||||
UserLoginMustBeDomain: req.UserLoginMustBeDomain,
|
||||
|
@ -112,6 +112,16 @@ func (s *Server) GetDomainPolicy(ctx context.Context, req *mgmt_pb.GetDomainPoli
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetOrgIAMPolicy(ctx context.Context, _ *mgmt_pb.GetOrgIAMPolicyRequest) (*mgmt_pb.GetOrgIAMPolicyResponse, error) {
|
||||
policy, err := s.query.DomainPolicyByOrg(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetOrgIAMPolicyResponse{
|
||||
Policy: policy_grpc.DomainPolicyToOrgIAMPb(policy),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ListOrgDomains(ctx context.Context, req *mgmt_pb.ListOrgDomainsRequest) (*mgmt_pb.ListOrgDomainsResponse, error) {
|
||||
queries, err := ListOrgDomainsRequestToModel(req)
|
||||
if err != nil {
|
||||
|
@ -100,7 +100,7 @@ func NewProvider(ctx context.Context, config Config, issuer, defaultLogoutRedire
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "OIDC-DAtg3", "cannot create provider: %w")
|
||||
return nil, caos_errs.ThrowInternal(err, "OIDC-DAtg3", "cannot create provider")
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
v1 "github.com/caos/zitadel/internal/eventstore/v1"
|
||||
@ -69,3 +71,7 @@ func (h *handler) LockDuration() time.Duration {
|
||||
func (h *handler) QueryLimit() uint64 {
|
||||
return h.bulkLimit
|
||||
}
|
||||
|
||||
func withInstanceID(ctx context.Context, instanceID string) context.Context {
|
||||
return authz.WithInstance(ctx, authz.Instance{ID: instanceID})
|
||||
}
|
||||
|
@ -121,9 +121,9 @@ func (i *IDPProvider) processIdpProvider(event *es_models.Event) (err error) {
|
||||
}
|
||||
config := new(query2.IDP)
|
||||
if event.AggregateID == domain.IAMID {
|
||||
config, err = i.getDefaultIDPConfig(context.TODO(), esConfig.IDPConfigID)
|
||||
config, err = i.getDefaultIDPConfig(event.InstanceID, esConfig.IDPConfigID)
|
||||
} else {
|
||||
config, err = i.getOrgIDPConfig(context.TODO(), event.AggregateID, esConfig.IDPConfigID)
|
||||
config, err = i.getOrgIDPConfig(event.InstanceID, event.AggregateID, esConfig.IDPConfigID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@ -146,9 +146,9 @@ func (i *IDPProvider) processIdpProvider(event *es_models.Event) (err error) {
|
||||
func (i *IDPProvider) fillData(provider *iam_view_model.IDPProviderView) (err error) {
|
||||
var config *query2.IDP
|
||||
if provider.IDPProviderType == int32(iam_model.IDPProviderTypeSystem) {
|
||||
config, err = i.getDefaultIDPConfig(context.Background(), provider.IDPConfigID)
|
||||
config, err = i.getDefaultIDPConfig(provider.InstanceID, provider.IDPConfigID)
|
||||
} else {
|
||||
config, err = i.getOrgIDPConfig(context.Background(), provider.AggregateID, provider.IDPConfigID)
|
||||
config, err = i.getOrgIDPConfig(provider.InstanceID, provider.AggregateID, provider.IDPConfigID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@ -186,10 +186,10 @@ func (i *IDPProvider) OnSuccess() error {
|
||||
return spooler.HandleSuccess(i.view.UpdateIDPProviderSpoolerRunTimestamp)
|
||||
}
|
||||
|
||||
func (i *IDPProvider) getOrgIDPConfig(ctx context.Context, aggregateID, idpConfigID string) (*query2.IDP, error) {
|
||||
return i.queries.IDPByIDAndResourceOwner(ctx, idpConfigID, aggregateID)
|
||||
func (i *IDPProvider) getOrgIDPConfig(instanceID, aggregateID, idpConfigID string) (*query2.IDP, error) {
|
||||
return i.queries.IDPByIDAndResourceOwner(withInstanceID(context.Background(), instanceID), idpConfigID, aggregateID)
|
||||
}
|
||||
|
||||
func (u *IDPProvider) getDefaultIDPConfig(ctx context.Context, idpConfigID string) (*query2.IDP, error) {
|
||||
return u.queries.IDPByIDAndResourceOwner(ctx, idpConfigID, domain.IAMID)
|
||||
func (u *IDPProvider) getDefaultIDPConfig(instanceID, idpConfigID string) (*query2.IDP, error) {
|
||||
return u.queries.IDPByIDAndResourceOwner(withInstanceID(context.Background(), instanceID), idpConfigID, domain.IAMID)
|
||||
}
|
||||
|
@ -187,9 +187,9 @@ func (u *User) ProcessOrg(event *es_models.Event) (err error) {
|
||||
switch event.Type {
|
||||
case org_es_model.OrgDomainVerified,
|
||||
org_es_model.OrgDomainRemoved,
|
||||
es_models.EventType(org.OrgDomainPolicyAddedEventType),
|
||||
es_models.EventType(org.OrgDomainPolicyChangedEventType),
|
||||
es_models.EventType(org.OrgDomainPolicyRemovedEventType):
|
||||
es_models.EventType(org.DomainPolicyAddedEventType),
|
||||
es_models.EventType(org.DomainPolicyChangedEventType),
|
||||
es_models.EventType(org.DomainPolicyRemovedEventType):
|
||||
return u.fillLoginNamesOnOrgUsers(event)
|
||||
case org_es_model.OrgDomainPrimarySet:
|
||||
return u.fillPreferredLoginNamesOnOrgUsers(event)
|
||||
@ -268,7 +268,7 @@ func (u *User) loginNameInformation(ctx context.Context, orgID string) (userLogi
|
||||
return false, "", nil, err
|
||||
}
|
||||
if org.DomainPolicy == nil {
|
||||
policy, err := u.queries.DefaultDomainPolicy(ctx)
|
||||
policy, err := u.queries.DefaultDomainPolicy(withInstanceID(ctx, org.InstanceID))
|
||||
if err != nil {
|
||||
return false, "", nil, err
|
||||
}
|
||||
|
@ -4,10 +4,11 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1"
|
||||
v1 "github.com/caos/zitadel/internal/eventstore/v1"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/query"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
|
||||
@ -137,9 +138,9 @@ func (i *ExternalIDP) processIdpConfig(event *es_models.Event) (err error) {
|
||||
return err
|
||||
}
|
||||
if event.AggregateType == iam_es_model.IAMAggregate {
|
||||
config, err = i.getDefaultIDPConfig(context.Background(), configView.IDPConfigID)
|
||||
config, err = i.getDefaultIDPConfig(event.InstanceID, configView.IDPConfigID)
|
||||
} else {
|
||||
config, err = i.getOrgIDPConfig(context.Background(), event.AggregateID, configView.IDPConfigID)
|
||||
config, err = i.getOrgIDPConfig(event.InstanceID, event.AggregateID, configView.IDPConfigID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@ -155,9 +156,9 @@ func (i *ExternalIDP) processIdpConfig(event *es_models.Event) (err error) {
|
||||
}
|
||||
|
||||
func (i *ExternalIDP) fillData(externalIDP *usr_view_model.ExternalIDPView) error {
|
||||
config, err := i.getOrgIDPConfig(context.Background(), externalIDP.ResourceOwner, externalIDP.IDPConfigID)
|
||||
config, err := i.getOrgIDPConfig(externalIDP.InstanceID, externalIDP.ResourceOwner, externalIDP.IDPConfigID)
|
||||
if caos_errs.IsNotFound(err) {
|
||||
config, err = i.getDefaultIDPConfig(context.Background(), externalIDP.IDPConfigID)
|
||||
config, err = i.getDefaultIDPConfig(externalIDP.InstanceID, externalIDP.IDPConfigID)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@ -179,10 +180,10 @@ func (i *ExternalIDP) OnSuccess() error {
|
||||
return spooler.HandleSuccess(i.view.UpdateExternalIDPSpoolerRunTimestamp)
|
||||
}
|
||||
|
||||
func (i *ExternalIDP) getOrgIDPConfig(ctx context.Context, aggregateID, idpConfigID string) (*query2.IDP, error) {
|
||||
return i.queries.IDPByIDAndResourceOwner(ctx, idpConfigID, aggregateID)
|
||||
func (i *ExternalIDP) getOrgIDPConfig(instanceID, aggregateID, idpConfigID string) (*query2.IDP, error) {
|
||||
return i.queries.IDPByIDAndResourceOwner(withInstanceID(context.Background(), instanceID), idpConfigID, aggregateID)
|
||||
}
|
||||
|
||||
func (i *ExternalIDP) getDefaultIDPConfig(ctx context.Context, idpConfigID string) (*query2.IDP, error) {
|
||||
return i.queries.IDPByIDAndResourceOwner(ctx, idpConfigID, domain.IAMID)
|
||||
func (i *ExternalIDP) getDefaultIDPConfig(instanceID, idpConfigID string) (*query2.IDP, error) {
|
||||
return i.queries.IDPByIDAndResourceOwner(withInstanceID(context.Background(), instanceID), idpConfigID, domain.IAMID)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"github.com/caos/zitadel/internal/repository/action"
|
||||
iam_repo "github.com/caos/zitadel/internal/repository/instance"
|
||||
instance_repo "github.com/caos/zitadel/internal/repository/instance"
|
||||
"github.com/caos/zitadel/internal/repository/keypair"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
proj_repo "github.com/caos/zitadel/internal/repository/project"
|
||||
@ -82,7 +82,7 @@ func StartCommands(es *eventstore.Eventstore,
|
||||
domainVerificationAlg: domainVerificationEncryption,
|
||||
keyAlgorithm: oidcEncryption,
|
||||
}
|
||||
iam_repo.RegisterEventMappers(repo.eventstore)
|
||||
instance_repo.RegisterEventMappers(repo.eventstore)
|
||||
org.RegisterEventMappers(repo.eventstore)
|
||||
usr_repo.RegisterEventMappers(repo.eventstore)
|
||||
usr_grant_repo.RegisterEventMappers(repo.eventstore)
|
||||
|
@ -27,7 +27,7 @@ func (c *Commands) SetDefaultMessageText(ctx context.Context, messageText *domai
|
||||
return writeModelToObjectDetails(&existingMessageText.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) setDefaultMessageText(ctx context.Context, instanceAgg *eventstore.Aggregate, msg *domain.CustomMessageText) ([]eventstore.Command, *InstanceCustomMessageTextReadModel, error) {
|
||||
func (c *Commands) setDefaultMessageText(ctx context.Context, instanceAgg *eventstore.Aggregate, msg *domain.CustomMessageText) ([]eventstore.Command, *InstanceCustomMessageTextWriteModel, error) {
|
||||
if !msg.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-kd9fs", "Errors.CustomMessageText.Invalid")
|
||||
}
|
||||
@ -113,7 +113,7 @@ func (c *Commands) RemoveInstanceMessageTexts(ctx context.Context, messageTextTy
|
||||
return writeModelToObjectDetails(&customText.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) defaultCustomMessageTextWriteModelByID(ctx context.Context, messageType string, lang language.Tag) (*InstanceCustomMessageTextReadModel, error) {
|
||||
func (c *Commands) defaultCustomMessageTextWriteModelByID(ctx context.Context, messageType string, lang language.Tag) (*InstanceCustomMessageTextWriteModel, error) {
|
||||
writeModel := NewInstanceCustomMessageTextWriteModel(messageType, lang)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
|
@ -8,12 +8,12 @@ import (
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
type InstanceCustomMessageTextReadModel struct {
|
||||
type InstanceCustomMessageTextWriteModel struct {
|
||||
CustomMessageTextReadModel
|
||||
}
|
||||
|
||||
func NewInstanceCustomMessageTextWriteModel(messageTextType string, lang language.Tag) *InstanceCustomMessageTextReadModel {
|
||||
return &InstanceCustomMessageTextReadModel{
|
||||
func NewInstanceCustomMessageTextWriteModel(messageTextType string, lang language.Tag) *InstanceCustomMessageTextWriteModel {
|
||||
return &InstanceCustomMessageTextWriteModel{
|
||||
CustomMessageTextReadModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: domain.IAMID,
|
||||
@ -25,7 +25,7 @@ func NewInstanceCustomMessageTextWriteModel(messageTextType string, lang languag
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *InstanceCustomMessageTextReadModel) AppendEvents(events ...eventstore.Event) {
|
||||
func (wm *InstanceCustomMessageTextWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *instance.CustomTextSetEvent:
|
||||
@ -38,11 +38,11 @@ func (wm *InstanceCustomMessageTextReadModel) AppendEvents(events ...eventstore.
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *InstanceCustomMessageTextReadModel) Reduce() error {
|
||||
func (wm *InstanceCustomMessageTextWriteModel) Reduce() error {
|
||||
return wm.CustomMessageTextReadModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *InstanceCustomMessageTextReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
func (wm *InstanceCustomMessageTextWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
|
@ -37,7 +37,7 @@ func (c *Commands) addDefaultDomainPolicy(ctx context.Context, instanceAgg *even
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-Lk0dS", "Errors.IAM.DomainPolicy.AlreadyExists")
|
||||
}
|
||||
return iam_repo.NewInstnaceDomainPolicyAddedEvent(ctx, instanceAgg, policy.UserLoginMustBeDomain), nil
|
||||
return iam_repo.NewDomainPolicyAddedEvent(ctx, instanceAgg, policy.UserLoginMustBeDomain), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeDefaultDomainPolicy(ctx context.Context, policy *domain.DomainPolicy) (*domain.DomainPolicy, error) {
|
||||
|
@ -28,9 +28,9 @@ func NewInstanceDomainPolicyWriteModel() *InstanceDomainPolicyWriteModel {
|
||||
func (wm *InstanceDomainPolicyWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *instance.InstanceDomainPolicyAddedEvent:
|
||||
case *instance.DomainPolicyAddedEvent:
|
||||
wm.PolicyDomainWriteModel.AppendEvents(&e.DomainPolicyAddedEvent)
|
||||
case *instance.InstanceDomainPolicyChangedEvent:
|
||||
case *instance.DomainPolicyChangedEvent:
|
||||
wm.PolicyDomainWriteModel.AppendEvents(&e.DomainPolicyChangedEvent)
|
||||
}
|
||||
}
|
||||
@ -47,15 +47,15 @@ func (wm *InstanceDomainPolicyWriteModel) Query() *eventstore.SearchQueryBuilder
|
||||
AggregateTypes(instance.AggregateType).
|
||||
AggregateIDs(wm.PolicyDomainWriteModel.AggregateID).
|
||||
EventTypes(
|
||||
instance.InstanceDomainPolicyAddedEventType,
|
||||
instance.InstanceDomainPolicyChangedEventType).
|
||||
instance.DomainPolicyAddedEventType,
|
||||
instance.DomainPolicyChangedEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *InstanceDomainPolicyWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
userLoginMustBeDomain bool) (*instance.InstanceDomainPolicyChangedEvent, bool) {
|
||||
userLoginMustBeDomain bool) (*instance.DomainPolicyChangedEvent, bool) {
|
||||
changes := make([]policy.OrgPolicyChanges, 0)
|
||||
if wm.UserLoginMustBeDomain != userLoginMustBeDomain {
|
||||
changes = append(changes, policy.ChangeUserLoginMustBeDomain(userLoginMustBeDomain))
|
||||
@ -63,7 +63,7 @@ func (wm *InstanceDomainPolicyWriteModel) NewChangedEvent(
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := instance.NewInstanceDomainPolicyChangedEvent(ctx, aggregate, changes)
|
||||
changedEvent, err := instance.NewDomainPolicyChangedEvent(ctx, aggregate, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
@ -33,13 +33,13 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) {
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "orgiam policy already existing, already exists error",
|
||||
name: "domain policy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate().Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -66,7 +66,7 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate().Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -111,7 +111,7 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_ChangeDefaultOrgIAMPolicy(t *testing.T) {
|
||||
func TestCommandSide_ChangeDefaultDomainPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
@ -130,7 +130,7 @@ func TestCommandSide_ChangeDefaultOrgIAMPolicy(t *testing.T) {
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "orgiampolicy not existing, not found error",
|
||||
name: "domain policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
@ -154,7 +154,7 @@ func TestCommandSide_ChangeDefaultOrgIAMPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate().Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -179,7 +179,7 @@ func TestCommandSide_ChangeDefaultOrgIAMPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate().Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -188,7 +188,7 @@ func TestCommandSide_ChangeDefaultOrgIAMPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultOrgIAMPolicyChangedEvent(context.Background(), false),
|
||||
newDefaultDomainPolicyChangedEvent(context.Background(), false),
|
||||
),
|
||||
},
|
||||
),
|
||||
@ -230,8 +230,8 @@ func TestCommandSide_ChangeDefaultOrgIAMPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func newDefaultOrgIAMPolicyChangedEvent(ctx context.Context, userLoginMustBeDomain bool) *instance.InstanceDomainPolicyChangedEvent {
|
||||
event, _ := instance.NewInstanceDomainPolicyChangedEvent(ctx,
|
||||
func newDefaultDomainPolicyChangedEvent(ctx context.Context, userLoginMustBeDomain bool) *instance.DomainPolicyChangedEvent {
|
||||
event, _ := instance.NewDomainPolicyChangedEvent(ctx,
|
||||
&instance.NewAggregate().Aggregate,
|
||||
[]policy.OrgPolicyChanges{
|
||||
policy.ChangeUserLoginMustBeDomain(userLoginMustBeDomain),
|
||||
|
@ -32,19 +32,20 @@ func (c *Commands) checkOrgExists(ctx context.Context, orgID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator, claimedUserIDs []string, selfregistered bool) (*domain.ObjectDetails, error) {
|
||||
orgIAMPolicy, err := c.getDefaultDomainPolicy(ctx)
|
||||
func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human, initCodeGenerator, phoneCodeGenerator crypto.Generator, claimedUserIDs []string, selfregistered bool) (*domain.ObjectDetails, error) {
|
||||
domainPolicy, err := c.getDefaultDomainPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.IAM.DomainPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.Instance.DomainPolicy.NotFound")
|
||||
}
|
||||
pwPolicy, err := c.getDefaultPasswordComplexityPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.IAM.PasswordComplexity.NotFound")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Instance.PasswordComplexity.NotFound")
|
||||
}
|
||||
_, orgWriteModel, _, _, events, err := c.setUpOrg(ctx, organisation, admin, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, claimedUserIDs, selfregistered)
|
||||
_, orgWriteModel, _, _, events, err := c.setUpOrg(ctx, organisation, admin, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, claimedUserIDs, selfregistered)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -201,7 +202,7 @@ func (c *Commands) setUpOrg(
|
||||
}
|
||||
|
||||
func (c *Commands) addOrg(ctx context.Context, organisation *domain.Org, claimedUserIDs []string) (_ *eventstore.Aggregate, _ *OrgWriteModel, _ []eventstore.Command, err error) {
|
||||
if organisation == nil || !organisation.IsValid() {
|
||||
if !organisation.IsValid() {
|
||||
return nil, nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMM-deLSk", "Errors.Org.Invalid")
|
||||
}
|
||||
|
||||
@ -220,9 +221,8 @@ func (c *Commands) addOrg(ctx context.Context, organisation *domain.Org, claimed
|
||||
orgDomainEvents, err := c.addOrgDomain(ctx, orgAgg, NewOrgDomainWriteModel(orgAgg.ID, orgDomain.Domain), orgDomain, claimedUserIDs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
} else {
|
||||
events = append(events, orgDomainEvents...)
|
||||
}
|
||||
events = append(events, orgDomainEvents...)
|
||||
}
|
||||
return orgAgg, addedOrg, events, nil
|
||||
}
|
||||
|
@ -819,7 +819,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org2", "org2").Aggregate,
|
||||
false))),
|
||||
expectPush(
|
||||
|
@ -39,7 +39,7 @@ func (c *Commands) addOrgDomainPolicy(ctx context.Context, orgAgg *eventstore.Ag
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-1M8ds", "Errors.Org.DomainPolicy.AlreadyExists")
|
||||
}
|
||||
return org.NewOrgDomainPolicyAddedEvent(ctx, orgAgg, policy.UserLoginMustBeDomain), nil
|
||||
return org.NewDomainPolicyAddedEvent(ctx, orgAgg, policy.UserLoginMustBeDomain), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeOrgDomainPolicy(ctx context.Context, resourceOwner string, policy *domain.DomainPolicy) (*domain.DomainPolicy, error) {
|
||||
@ -80,11 +80,11 @@ func (c *Commands) RemoveOrgDomainPolicy(ctx context.Context, orgID string) erro
|
||||
return err
|
||||
}
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "ORG-Dvsh3", "Errors.Org.OrgIAMPolicy.NotFound")
|
||||
return caos_errs.ThrowNotFound(nil, "ORG-Dvsh3", "Errors.Org.DomainPolicy.NotFound")
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PolicyDomainWriteModel.WriteModel)
|
||||
_, err = c.eventstore.Push(ctx, org.NewOrgDomainPolicyRemovedEvent(ctx, orgAgg))
|
||||
_, err = c.eventstore.Push(ctx, org.NewDomainPolicyRemovedEvent(ctx, orgAgg))
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -27,11 +27,11 @@ func NewOrgDomainPolicyWriteModel(orgID string) *OrgDomainPolicyWriteModel {
|
||||
func (wm *OrgDomainPolicyWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.OrgDomainPolicyAddedEvent:
|
||||
case *org.DomainPolicyAddedEvent:
|
||||
wm.PolicyDomainWriteModel.AppendEvents(&e.DomainPolicyAddedEvent)
|
||||
case *org.OrgDomainPolicyChangedEvent:
|
||||
case *org.DomainPolicyChangedEvent:
|
||||
wm.PolicyDomainWriteModel.AppendEvents(&e.DomainPolicyChangedEvent)
|
||||
case *org.OrgDomainPolicyRemovedEvent:
|
||||
case *org.DomainPolicyRemovedEvent:
|
||||
wm.PolicyDomainWriteModel.AppendEvents(&e.DomainPolicyRemovedEvent)
|
||||
}
|
||||
}
|
||||
@ -47,16 +47,16 @@ func (wm *OrgDomainPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
AddQuery().
|
||||
AggregateTypes(org.AggregateType).
|
||||
AggregateIDs(wm.PolicyDomainWriteModel.AggregateID).
|
||||
EventTypes(org.OrgDomainPolicyAddedEventType,
|
||||
org.OrgDomainPolicyChangedEventType,
|
||||
org.OrgDomainPolicyRemovedEventType).
|
||||
EventTypes(org.DomainPolicyAddedEventType,
|
||||
org.DomainPolicyChangedEventType,
|
||||
org.DomainPolicyRemovedEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *OrgDomainPolicyWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
userLoginMustBeDomain bool) (*org.OrgDomainPolicyChangedEvent, bool) {
|
||||
userLoginMustBeDomain bool) (*org.DomainPolicyChangedEvent, bool) {
|
||||
changes := make([]policy.OrgPolicyChanges, 0)
|
||||
if wm.UserLoginMustBeDomain != userLoginMustBeDomain {
|
||||
changes = append(changes, policy.ChangeUserLoginMustBeDomain(userLoginMustBeDomain))
|
||||
@ -64,7 +64,7 @@ func (wm *OrgDomainPolicyWriteModel) NewChangedEvent(
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := org.NewOrgDomainPolicyChangedEvent(ctx, aggregate, changes)
|
||||
changedEvent, err := org.NewDomainPolicyChangedEvent(ctx, aggregate, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func TestCommandSide_AddDomainPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -86,7 +86,7 @@ func TestCommandSide_AddDomainPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -194,7 +194,7 @@ func TestCommandSide_ChangeDomainPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -220,7 +220,7 @@ func TestCommandSide_ChangeDomainPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -229,7 +229,7 @@ func TestCommandSide_ChangeDomainPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newOrgIAMPolicyChangedEvent(context.Background(), "org1", false),
|
||||
newDomainPolicyChangedEvent(context.Background(), "org1", false),
|
||||
),
|
||||
},
|
||||
),
|
||||
@ -272,7 +272,7 @@ func TestCommandSide_ChangeDomainPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_RemoveOrgIAMPolicy(t *testing.T) {
|
||||
func TestCommandSide_RemoveDomainPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
@ -327,7 +327,7 @@ func TestCommandSide_RemoveOrgIAMPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -336,7 +336,7 @@ func TestCommandSide_RemoveOrgIAMPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyRemovedEvent(context.Background(),
|
||||
org.NewDomainPolicyRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate),
|
||||
),
|
||||
},
|
||||
@ -370,8 +370,8 @@ func TestCommandSide_RemoveOrgIAMPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func newOrgIAMPolicyChangedEvent(ctx context.Context, orgID string, userLoginMustBeDomain bool) *org.OrgDomainPolicyChangedEvent {
|
||||
event, _ := org.NewOrgDomainPolicyChangedEvent(ctx,
|
||||
func newDomainPolicyChangedEvent(ctx context.Context, orgID string, userLoginMustBeDomain bool) *org.DomainPolicyChangedEvent {
|
||||
event, _ := org.NewDomainPolicyChangedEvent(ctx,
|
||||
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||
[]policy.OrgPolicyChanges{
|
||||
policy.ChangeUserLoginMustBeDomain(userLoginMustBeDomain),
|
||||
|
@ -1,11 +1,21 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
var (
|
||||
hasStringLowerCase = regexp.MustCompile(`[a-z]`).MatchString
|
||||
hasStringUpperCase = regexp.MustCompile(`[A-Z]`).MatchString
|
||||
hasNumber = regexp.MustCompile(`[0-9]`).MatchString
|
||||
hasSymbol = regexp.MustCompile(`[^A-Za-z0-9]`).MatchString
|
||||
)
|
||||
|
||||
type PasswordComplexityPolicyWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
@ -49,3 +59,26 @@ func (wm *PasswordComplexityPolicyWriteModel) Reduce() error {
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *PasswordComplexityPolicyWriteModel) Validate(password string) error {
|
||||
if wm.MinLength != 0 && uint64(len(password)) < wm.MinLength {
|
||||
return errors.ThrowInvalidArgument(nil, "COMMA-HuJf6", "Errors.User.PasswordComplexityPolicy.MinLength")
|
||||
}
|
||||
|
||||
if wm.HasLowercase && !hasStringLowerCase(password) {
|
||||
return errors.ThrowInvalidArgument(nil, "COMMA-co3Xw", "Errors.User.PasswordComplexityPolicy.HasLower")
|
||||
}
|
||||
|
||||
if wm.HasUppercase && !hasStringUpperCase(password) {
|
||||
return errors.ThrowInvalidArgument(nil, "COMMA-VoaRj", "Errors.User.PasswordComplexityPolicy.HasUpper")
|
||||
}
|
||||
|
||||
if wm.HasNumber && !hasNumber(password) {
|
||||
return errors.ThrowInvalidArgument(nil, "COMMA-ZBv4H", "Errors.User.PasswordComplexityPolicy.HasNumber")
|
||||
}
|
||||
|
||||
if wm.HasSymbol && !hasSymbol(password) {
|
||||
return errors.ThrowInvalidArgument(nil, "COMMA-ZDLwA", "Errors.User.PasswordComplexityPolicy.HasSymbol")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,329 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
iam_repo "github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
const (
|
||||
OIDCResponseTypeCode = "CODE"
|
||||
OIDCResponseTypeIDToken = "ID_TOKEN"
|
||||
OIDCResponseTypeToken = "ID_TOKEN TOKEN"
|
||||
OIDCGrantTypeAuthorizationCode = "AUTHORIZATION_CODE"
|
||||
OIDCGrantTypeImplicit = "IMPLICIT"
|
||||
OIDCGrantTypeRefreshToken = "REFRESH_TOKEN"
|
||||
OIDCApplicationTypeNative = "NATIVE"
|
||||
OIDCApplicationTypeUserAgent = "USER_AGENT"
|
||||
OIDCApplicationTypeWeb = "WEB"
|
||||
AuthMethodTypeNone = "NONE"
|
||||
AuthMethodTypeBasic = "BASIC"
|
||||
AuthMethodTypePost = "POST"
|
||||
AuthMethodTypePrivateKeyJWT = "PRIVATE_KEY_JWT"
|
||||
)
|
||||
|
||||
type Step1 struct {
|
||||
GlobalOrg string
|
||||
IAMProject string
|
||||
DefaultLoginPolicy LoginPolicy
|
||||
Orgs []Org
|
||||
}
|
||||
|
||||
func (s *Step1) Step() domain.Step {
|
||||
return domain.Step1
|
||||
}
|
||||
|
||||
func (s *Step1) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep1(ctx, s)
|
||||
}
|
||||
|
||||
type LoginPolicy struct {
|
||||
AllowRegister bool
|
||||
AllowUsernamePassword bool
|
||||
AllowExternalIdp bool
|
||||
}
|
||||
|
||||
type User struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
UserName string
|
||||
Email string
|
||||
Password string
|
||||
}
|
||||
|
||||
type Org struct {
|
||||
Name string
|
||||
Domain string
|
||||
OrgIamPolicy bool
|
||||
Owner User
|
||||
Projects []Project
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
Name string
|
||||
Users []User
|
||||
Members []string
|
||||
OIDCApps []OIDCApp
|
||||
APIs []API
|
||||
}
|
||||
|
||||
type OIDCApp struct {
|
||||
Name string
|
||||
RedirectUris []string
|
||||
ResponseTypes []string
|
||||
GrantTypes []string
|
||||
ApplicationType string
|
||||
AuthMethodType string
|
||||
PostLogoutRedirectUris []string
|
||||
DevMode bool
|
||||
}
|
||||
|
||||
type API struct {
|
||||
Name string
|
||||
AuthMethodType string
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep1(ctx context.Context, step1 *Step1) error {
|
||||
var events []eventstore.Command
|
||||
iamWriteModel := NewInstanceWriteModel()
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iamWriteModel.WriteModel)
|
||||
//create default login policy
|
||||
loginPolicyEvent, err := c.addDefaultLoginPolicy(ctx, iamAgg, NewInstanceLoginPolicyWriteModel(),
|
||||
&domain.LoginPolicy{
|
||||
AllowUsernamePassword: step1.DefaultLoginPolicy.AllowUsernamePassword,
|
||||
AllowRegister: step1.DefaultLoginPolicy.AllowRegister,
|
||||
AllowExternalIDP: step1.DefaultLoginPolicy.AllowExternalIdp,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, loginPolicyEvent)
|
||||
logging.Log("SETUP-sd2hj").Info("default login policy set up")
|
||||
//create orgs
|
||||
for _, organisation := range step1.Orgs {
|
||||
orgIAMPolicy := &domain.DomainPolicy{UserLoginMustBeDomain: true}
|
||||
if organisation.OrgIamPolicy {
|
||||
orgIAMPolicy.UserLoginMustBeDomain = false
|
||||
}
|
||||
pwPolicy := &domain.PasswordComplexityPolicy{
|
||||
MinLength: 1,
|
||||
}
|
||||
orgAgg, _, humanWriteModel, _, setUpOrgEvents, err := c.setUpOrg(ctx,
|
||||
&domain.Org{
|
||||
Name: organisation.Name,
|
||||
Domains: []*domain.OrgDomain{{Domain: organisation.Domain}},
|
||||
},
|
||||
&domain.Human{
|
||||
Username: organisation.Owner.UserName,
|
||||
Profile: &domain.Profile{
|
||||
FirstName: organisation.Owner.FirstName,
|
||||
LastName: organisation.Owner.LastName,
|
||||
},
|
||||
Password: &domain.Password{
|
||||
SecretString: organisation.Owner.Password,
|
||||
},
|
||||
Email: &domain.Email{
|
||||
EmailAddress: organisation.Owner.Email,
|
||||
IsEmailVerified: true,
|
||||
},
|
||||
}, orgIAMPolicy, pwPolicy,
|
||||
nil, //TODO: Code Generator missing! Should be setuped in step1 create iam
|
||||
nil, //TODO: Code Generator missing! Should be setuped in step1 create iam
|
||||
nil, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, setUpOrgEvents...)
|
||||
logging.LogWithFields("SETUP-Gdsfg", "id", orgAgg.ID, "name", organisation.Name).Info("org set up")
|
||||
|
||||
if organisation.OrgIamPolicy {
|
||||
orgIAMPolicyEvent, err := c.addOrgDomainPolicy(ctx, orgAgg, NewOrgDomainPolicyWriteModel(orgAgg.ID), orgIAMPolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, orgIAMPolicyEvent)
|
||||
}
|
||||
if organisation.Name == step1.GlobalOrg {
|
||||
globalOrgEvent, err := c.setGlobalOrg(ctx, iamAgg, iamWriteModel, orgAgg.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, globalOrgEvent)
|
||||
logging.Log("SETUP-BDn52").Info("global org set")
|
||||
}
|
||||
//projects
|
||||
for _, proj := range organisation.Projects {
|
||||
project := &domain.Project{Name: proj.Name}
|
||||
projectEvents, projectWriteModel, err := c.addProject(ctx, project, orgAgg.ID, humanWriteModel.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, projectEvents...)
|
||||
if project.Name == step1.IAMProject {
|
||||
iamProjectEvent, err := c.setIAMProject(ctx, iamAgg, iamWriteModel, projectWriteModel.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, iamProjectEvent)
|
||||
logging.Log("SETUP-Bdfs1").Info("IAM project set")
|
||||
iamEvent, err := c.addInstanceMember(ctx, iamAgg, NewInstanceMemberWriteModel(humanWriteModel.AggregateID), domain.NewMember(iamAgg.ID, humanWriteModel.AggregateID, domain.RoleIAMOwner))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, iamEvent)
|
||||
logging.Log("SETUP-BSf2h").Info("IAM owner set")
|
||||
}
|
||||
//create applications
|
||||
for _, app := range proj.OIDCApps {
|
||||
//TODO: Add Secret Generator
|
||||
applicationEvents, err := setUpOIDCApplication(ctx, c, projectWriteModel, project, app, orgAgg.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, applicationEvents...)
|
||||
}
|
||||
for _, app := range proj.APIs {
|
||||
//TODO: Add Secret Generator
|
||||
applicationEvents, err := setUpAPI(ctx, c, projectWriteModel, project, app, orgAgg.ID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
events = append(events, applicationEvents...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
events = append(events, iam_repo.NewSetupStepDoneEvent(ctx, iamAgg, domain.Step1))
|
||||
|
||||
_, err = c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-Gr2hh", "Setup Step1 failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setUpOIDCApplication(ctx context.Context, r *Commands, projectWriteModel *ProjectWriteModel, project *domain.Project, oidcApp OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) ([]eventstore.Command, error) {
|
||||
app := &domain.OIDCApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: projectWriteModel.AggregateID,
|
||||
},
|
||||
AppName: oidcApp.Name,
|
||||
RedirectUris: oidcApp.RedirectUris,
|
||||
ResponseTypes: getOIDCResponseTypes(oidcApp.ResponseTypes),
|
||||
GrantTypes: getOIDCGrantTypes(oidcApp.GrantTypes),
|
||||
ApplicationType: getOIDCApplicationType(oidcApp.ApplicationType),
|
||||
AuthMethodType: getOIDCAuthMethod(oidcApp.AuthMethodType),
|
||||
DevMode: oidcApp.DevMode,
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&projectWriteModel.WriteModel)
|
||||
events, _, err := r.addOIDCApplication(ctx, projectAgg, project, app, resourceOwner, appSecretGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.LogWithFields("SETUP-Edgw4", "name", app.AppName, "clientID", app.ClientID).Info("application set up")
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func setUpAPI(ctx context.Context, r *Commands, projectWriteModel *ProjectWriteModel, project *domain.Project, apiApp API, resourceOwner string, appSecretGenerator crypto.Generator) ([]eventstore.Command, error) {
|
||||
app := &domain.APIApp{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: projectWriteModel.AggregateID,
|
||||
},
|
||||
AppName: apiApp.Name,
|
||||
AuthMethodType: getAPIAuthMethod(apiApp.AuthMethodType),
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&projectWriteModel.WriteModel)
|
||||
events, _, err := r.addAPIApplication(ctx, projectAgg, project, app, resourceOwner, appSecretGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.LogWithFields("SETUP-Dbgsf", "name", app.AppName, "clientID", app.ClientID).Info("application set up")
|
||||
return events, nil
|
||||
}
|
||||
|
||||
func getOIDCResponseTypes(responseTypes []string) []domain.OIDCResponseType {
|
||||
types := make([]domain.OIDCResponseType, len(responseTypes))
|
||||
for i, t := range responseTypes {
|
||||
types[i] = getOIDCResponseType(t)
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func getOIDCResponseType(responseType string) domain.OIDCResponseType {
|
||||
switch responseType {
|
||||
case OIDCResponseTypeCode:
|
||||
return domain.OIDCResponseTypeCode
|
||||
case OIDCResponseTypeIDToken:
|
||||
return domain.OIDCResponseTypeIDToken
|
||||
case OIDCResponseTypeToken:
|
||||
return domain.OIDCResponseTypeIDTokenToken
|
||||
}
|
||||
return domain.OIDCResponseTypeCode
|
||||
}
|
||||
|
||||
func getOIDCGrantTypes(grantTypes []string) []domain.OIDCGrantType {
|
||||
types := make([]domain.OIDCGrantType, len(grantTypes))
|
||||
for i, t := range grantTypes {
|
||||
types[i] = getOIDCGrantType(t)
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func getOIDCGrantType(grantTypes string) domain.OIDCGrantType {
|
||||
switch grantTypes {
|
||||
case OIDCGrantTypeAuthorizationCode:
|
||||
return domain.OIDCGrantTypeAuthorizationCode
|
||||
case OIDCGrantTypeImplicit:
|
||||
return domain.OIDCGrantTypeImplicit
|
||||
case OIDCGrantTypeRefreshToken:
|
||||
return domain.OIDCGrantTypeRefreshToken
|
||||
}
|
||||
return domain.OIDCGrantTypeAuthorizationCode
|
||||
}
|
||||
|
||||
func getOIDCApplicationType(appType string) domain.OIDCApplicationType {
|
||||
switch appType {
|
||||
case OIDCApplicationTypeNative:
|
||||
return domain.OIDCApplicationTypeNative
|
||||
case OIDCApplicationTypeUserAgent:
|
||||
return domain.OIDCApplicationTypeUserAgent
|
||||
case OIDCApplicationTypeWeb:
|
||||
return domain.OIDCApplicationTypeWeb
|
||||
}
|
||||
return domain.OIDCApplicationTypeWeb
|
||||
}
|
||||
|
||||
func getOIDCAuthMethod(authMethod string) domain.OIDCAuthMethodType {
|
||||
switch authMethod {
|
||||
case AuthMethodTypeNone:
|
||||
return domain.OIDCAuthMethodTypeNone
|
||||
case AuthMethodTypeBasic:
|
||||
return domain.OIDCAuthMethodTypeBasic
|
||||
case AuthMethodTypePost:
|
||||
return domain.OIDCAuthMethodTypePost
|
||||
case AuthMethodTypePrivateKeyJWT:
|
||||
return domain.OIDCAuthMethodTypePrivateKeyJWT
|
||||
}
|
||||
return domain.OIDCAuthMethodTypeBasic
|
||||
}
|
||||
|
||||
func getAPIAuthMethod(authMethod string) domain.APIAuthMethodType {
|
||||
switch authMethod {
|
||||
case AuthMethodTypeBasic:
|
||||
return domain.APIAuthMethodTypeBasic
|
||||
case AuthMethodTypePrivateKeyJWT:
|
||||
return domain.APIAuthMethodTypePrivateKeyJWT
|
||||
}
|
||||
return domain.APIAuthMethodTypePrivateKeyJWT
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step10 struct {
|
||||
DefaultMailTemplate domain.MailTemplate
|
||||
}
|
||||
|
||||
func (s *Step10) Step() domain.Step {
|
||||
return domain.Step10
|
||||
}
|
||||
|
||||
func (s *Step10) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep10(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep10(ctx context.Context, step *Step10) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
mailTemplateEvent, err := c.addDefaultMailTemplate(ctx, iamAgg, NewInstanceMailTemplateWriteModel(), &step.DefaultMailTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events := []eventstore.Command{
|
||||
mailTemplateEvent,
|
||||
}
|
||||
logging.Log("SETUP-3N9fs").Info("default mail template/text set up")
|
||||
return events, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
iam_repo "github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
type Step11 struct {
|
||||
MigrateV1EventstoreToV2 bool
|
||||
}
|
||||
|
||||
func (s *Step11) Step() domain.Step {
|
||||
return domain.Step11
|
||||
}
|
||||
|
||||
func (s *Step11) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep11(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep11(ctx context.Context, step *Step11) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
var uniqueContraintMigrations []*domain.UniqueConstraintMigration
|
||||
if step.MigrateV1EventstoreToV2 {
|
||||
uniqueConstraints := NewUniqueConstraintReadModel(ctx, c)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, uniqueConstraints)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uniqueContraintMigrations = uniqueConstraints.UniqueConstraints
|
||||
}
|
||||
logging.Log("SETUP-M9fsd").Info("migrate v1 eventstore to v2")
|
||||
return []eventstore.Command{iam_repo.NewMigrateUniqueConstraintEvent(ctx, iamAgg, uniqueContraintMigrations)}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step12 struct {
|
||||
TierName string
|
||||
TierDescription string
|
||||
AuditLogRetention time.Duration
|
||||
LoginPolicyFactors bool
|
||||
LoginPolicyIDP bool
|
||||
LoginPolicyPasswordless bool
|
||||
LoginPolicyRegistration bool
|
||||
LoginPolicyUsernameLogin bool
|
||||
PasswordComplexityPolicy bool
|
||||
LabelPolicy bool
|
||||
CustomDomain bool
|
||||
}
|
||||
|
||||
func (s *Step12) Step() domain.Step {
|
||||
return domain.Step12
|
||||
}
|
||||
|
||||
func (s *Step12) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep12(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep12(ctx context.Context, step *Step12) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
featuresWriteModel := NewInstanceFeaturesWriteModel()
|
||||
featuresEvent, err := c.setDefaultFeatures(ctx, featuresWriteModel, &domain.Features{
|
||||
TierName: step.TierName,
|
||||
TierDescription: step.TierDescription,
|
||||
State: domain.FeaturesStateActive,
|
||||
AuditLogRetention: step.AuditLogRetention,
|
||||
LoginPolicyFactors: step.LoginPolicyFactors,
|
||||
LoginPolicyIDP: step.LoginPolicyIDP,
|
||||
LoginPolicyPasswordless: step.LoginPolicyPasswordless,
|
||||
LoginPolicyRegistration: step.LoginPolicyRegistration,
|
||||
LoginPolicyUsernameLogin: step.LoginPolicyUsernameLogin,
|
||||
PasswordComplexityPolicy: step.PasswordComplexityPolicy,
|
||||
LabelPolicyPrivateLabel: step.LabelPolicy,
|
||||
CustomDomain: step.CustomDomain,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{featuresEvent}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step13 struct {
|
||||
DefaultMailTemplate domain.MailTemplate
|
||||
}
|
||||
|
||||
func (s *Step13) Step() domain.Step {
|
||||
return domain.Step13
|
||||
}
|
||||
|
||||
func (s *Step13) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep13(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep13(ctx context.Context, step *Step13) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
_, mailTemplateEvent, err := c.changeDefaultMailTemplate(ctx, &step.DefaultMailTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-4insR").Info("default mail template/text set up")
|
||||
return []eventstore.Command{mailTemplateEvent}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
iam_repo "github.com/caos/zitadel/internal/repository/instance"
|
||||
org_repo "github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
type Step14 struct {
|
||||
ActivateExistingLabelPolicies bool
|
||||
}
|
||||
|
||||
func (s *Step14) Step() domain.Step {
|
||||
return domain.Step14
|
||||
}
|
||||
|
||||
func (s *Step14) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep14(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep14(ctx context.Context, step *Step14) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
var events []eventstore.Command
|
||||
if step.ActivateExistingLabelPolicies {
|
||||
existingPolicies := NewExistingLabelPoliciesReadModel(ctx)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, aggID := range existingPolicies.aggregateIDs {
|
||||
if iamAgg.ID == aggID {
|
||||
events = append(events, iam_repo.NewLabelPolicyActivatedEvent(ctx, iamAgg))
|
||||
continue
|
||||
}
|
||||
events = append(events, org_repo.NewLabelPolicyActivatedEvent(ctx, &org_repo.NewAggregate(aggID, aggID).Aggregate))
|
||||
}
|
||||
}
|
||||
logging.Log("SETUP-M9fsd").Info("activate login policies")
|
||||
return events, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step15 struct {
|
||||
DefaultMailTemplate domain.MailTemplate
|
||||
}
|
||||
|
||||
func (s *Step15) Step() domain.Step {
|
||||
return domain.Step15
|
||||
}
|
||||
|
||||
func (s *Step15) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep15(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep15(ctx context.Context, step *Step15) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
_, mailTemplateEvent, err := c.changeDefaultMailTemplate(ctx, &step.DefaultMailTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-2nfsd").Info("default mail template/text set up")
|
||||
return []eventstore.Command{mailTemplateEvent}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step16 struct {
|
||||
DefaultMessageTexts []domain.CustomMessageText
|
||||
}
|
||||
|
||||
func (s *Step16) Step() domain.Step {
|
||||
return domain.Step16
|
||||
}
|
||||
|
||||
func (s *Step16) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep16(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep16(ctx context.Context, step *Step16) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
events := make([]eventstore.Command, 0)
|
||||
|
||||
for _, text := range step.DefaultMessageTexts {
|
||||
mailEvents, _, err := c.setDefaultMessageText(ctx, iamAgg, &text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, mailEvents...)
|
||||
}
|
||||
|
||||
logging.Log("SETUP-4k0LL").Info("default message text set up")
|
||||
return events, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step17 struct {
|
||||
PrivacyPolicy domain.PrivacyPolicy
|
||||
}
|
||||
|
||||
func (s *Step17) Step() domain.Step {
|
||||
return domain.Step17
|
||||
}
|
||||
|
||||
func (s *Step17) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep17(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep17(ctx context.Context, step *Step17) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
addedPolicy := NewInstancePrivacyPolicyWriteModel()
|
||||
events, err := c.addDefaultPrivacyPolicy(ctx, iamAgg, addedPolicy, &step.PrivacyPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logging.Log("SETUP-N9sq2").Info("default privacy policy set up")
|
||||
return []eventstore.Command{events}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step18 struct {
|
||||
LockoutPolicy domain.LockoutPolicy
|
||||
}
|
||||
|
||||
func (s *Step18) Step() domain.Step {
|
||||
return domain.Step18
|
||||
}
|
||||
|
||||
func (s *Step18) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep18(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep18(ctx context.Context, step *Step18) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
addedPolicy := NewInstanceLockoutPolicyWriteModel()
|
||||
events, err := c.addDefaultLockoutPolicy(ctx, iamAgg, addedPolicy, &step.LockoutPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logging.Log("SETUP-3m99ds").Info("default lockout policy set up")
|
||||
return []eventstore.Command{events}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
type Step19 struct{}
|
||||
|
||||
func (s *Step19) Step() domain.Step {
|
||||
return domain.Step19
|
||||
}
|
||||
|
||||
func (s *Step19) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep19(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep19(ctx context.Context, step *Step19) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
events := make([]eventstore.Command, 0)
|
||||
orgs := newOrgsWithUsernameNotDomain()
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, orgs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for orgID, usernameCheck := range orgs.orgs {
|
||||
if !usernameCheck {
|
||||
continue
|
||||
}
|
||||
users := newDomainClaimedUsernames(orgID)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, users); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for userID, username := range users.users {
|
||||
split := strings.Split(username, "@")
|
||||
if len(split) != 2 {
|
||||
continue
|
||||
}
|
||||
domainVerified := NewOrgDomainVerifiedWriteModel(split[1])
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, domainVerified); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if domainVerified.Verified && domainVerified.ResourceOwner != orgID {
|
||||
id, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, user.NewDomainClaimedEvent(
|
||||
ctx,
|
||||
&user.NewAggregate(userID, orgID).Aggregate,
|
||||
fmt.Sprintf("%s@temporary.%s", id, c.iamDomain),
|
||||
username,
|
||||
false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if length := len(events); length > 0 {
|
||||
logging.Log("SETUP-dFG2t").WithField("count", length).Info("domain claimed events created")
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
||||
|
||||
func newOrgsWithUsernameNotDomain() *orgsWithUsernameNotDomain {
|
||||
return &orgsWithUsernameNotDomain{
|
||||
orgEvents: make(map[string][]eventstore.Event),
|
||||
orgs: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
type orgsWithUsernameNotDomain struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
orgEvents map[string][]eventstore.Event
|
||||
orgs map[string]bool
|
||||
}
|
||||
|
||||
func (wm *orgsWithUsernameNotDomain) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.OrgAddedEvent:
|
||||
wm.orgEvents[e.Aggregate().ID] = append(wm.orgEvents[e.Aggregate().ID], e)
|
||||
case *org.OrgRemovedEvent:
|
||||
delete(wm.orgEvents, e.Aggregate().ID)
|
||||
case *org.OrgDomainPolicyAddedEvent:
|
||||
wm.orgEvents[e.Aggregate().ID] = append(wm.orgEvents[e.Aggregate().ID], e)
|
||||
case *org.OrgDomainPolicyChangedEvent:
|
||||
if e.UserLoginMustBeDomain == nil {
|
||||
continue
|
||||
}
|
||||
wm.orgEvents[e.Aggregate().ID] = append(wm.orgEvents[e.Aggregate().ID], e)
|
||||
case *org.OrgDomainPolicyRemovedEvent:
|
||||
delete(wm.orgEvents, e.Aggregate().ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *orgsWithUsernameNotDomain) Reduce() error {
|
||||
for _, events := range wm.orgEvents {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.OrgDomainPolicyAddedEvent:
|
||||
if !e.UserLoginMustBeDomain {
|
||||
wm.orgs[e.Aggregate().ID] = true
|
||||
}
|
||||
case *org.OrgDomainPolicyChangedEvent:
|
||||
if !*e.UserLoginMustBeDomain {
|
||||
wm.orgs[e.Aggregate().ID] = true
|
||||
}
|
||||
delete(wm.orgs, e.Aggregate().ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wm *orgsWithUsernameNotDomain) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
AddQuery().
|
||||
AggregateTypes(org.AggregateType).
|
||||
EventTypes(
|
||||
org.OrgAddedEventType,
|
||||
org.OrgRemovedEventType,
|
||||
org.OrgDomainPolicyAddedEventType,
|
||||
org.OrgDomainPolicyChangedEventType,
|
||||
org.OrgDomainPolicyRemovedEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func newDomainClaimedUsernames(orgID string) *domainClaimedUsernames {
|
||||
return &domainClaimedUsernames{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
ResourceOwner: orgID,
|
||||
},
|
||||
userEvents: make(map[string][]eventstore.Event),
|
||||
users: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
type domainClaimedUsernames struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
userEvents map[string][]eventstore.Event
|
||||
users map[string]string
|
||||
}
|
||||
|
||||
func (wm *domainClaimedUsernames) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
if !strings.Contains(e.UserName, "@") {
|
||||
continue
|
||||
}
|
||||
wm.userEvents[e.Aggregate().ID] = append(wm.userEvents[e.Aggregate().ID], e)
|
||||
case *user.HumanRegisteredEvent:
|
||||
if !strings.Contains(e.UserName, "@") {
|
||||
continue
|
||||
}
|
||||
wm.userEvents[e.Aggregate().ID] = append(wm.userEvents[e.Aggregate().ID], e)
|
||||
case *user.UsernameChangedEvent:
|
||||
if !strings.Contains(e.UserName, "@") {
|
||||
delete(wm.userEvents, e.Aggregate().ID)
|
||||
continue
|
||||
}
|
||||
wm.userEvents[e.Aggregate().ID] = append(wm.userEvents[e.Aggregate().ID], e)
|
||||
case *user.DomainClaimedEvent:
|
||||
delete(wm.userEvents, e.Aggregate().ID)
|
||||
case *user.UserRemovedEvent:
|
||||
delete(wm.userEvents, e.Aggregate().ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *domainClaimedUsernames) Reduce() error {
|
||||
for _, events := range wm.userEvents {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
wm.users[e.Aggregate().ID] = e.UserName
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.users[e.Aggregate().ID] = e.UserName
|
||||
case *user.UsernameChangedEvent:
|
||||
wm.users[e.Aggregate().ID] = e.UserName
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wm *domainClaimedUsernames) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(user.AggregateType).
|
||||
EventTypes(
|
||||
user.UserV1AddedType,
|
||||
user.UserV1RegisteredType,
|
||||
user.HumanAddedType,
|
||||
user.HumanRegisteredType,
|
||||
user.UserUserNameChangedType,
|
||||
user.UserDomainClaimedType,
|
||||
user.UserRemovedType).
|
||||
Builder()
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step2 struct {
|
||||
DefaultPasswordComplexityPolicy domain.PasswordComplexityPolicy
|
||||
}
|
||||
|
||||
func (s *Step2) Step() domain.Step {
|
||||
return domain.Step2
|
||||
}
|
||||
|
||||
func (s *Step2) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep2(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep2(ctx context.Context, step *Step2) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
event, err := c.addDefaultPasswordComplexityPolicy(ctx, iamAgg, NewInstancePasswordComplexityPolicyWriteModel(), &domain.PasswordComplexityPolicy{
|
||||
MinLength: step.DefaultPasswordComplexityPolicy.MinLength,
|
||||
HasLowercase: step.DefaultPasswordComplexityPolicy.HasLowercase,
|
||||
HasUppercase: step.DefaultPasswordComplexityPolicy.HasUppercase,
|
||||
HasNumber: step.DefaultPasswordComplexityPolicy.HasNumber,
|
||||
HasSymbol: step.DefaultPasswordComplexityPolicy.HasSymbol,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-ADgd2").Info("default password complexity policy set up")
|
||||
return []eventstore.Command{event}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step20 struct{}
|
||||
|
||||
func (s *Step20) Step() domain.Step {
|
||||
return domain.Step20
|
||||
}
|
||||
|
||||
func (s *Step20) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep20(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep20(ctx context.Context, step *Step20) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
err := c.eventstore.Step20(ctx, iam.Events[len(iam.Events)-1].Sequence())
|
||||
return nil, err
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
type Step21 struct{}
|
||||
|
||||
func (s *Step21) Step() domain.Step {
|
||||
return domain.Step21
|
||||
}
|
||||
|
||||
func (s *Step21) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep21(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep21(ctx context.Context, step *Step21) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
events := make([]eventstore.Command, 0)
|
||||
globalMembers := newGlobalOrgMemberWriteModel(iam.GlobalOrgID, domain.RoleOrgProjectCreator)
|
||||
orgAgg := OrgAggregateFromWriteModel(&globalMembers.WriteModel)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, globalMembers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for userID, roles := range globalMembers.members {
|
||||
for i, role := range roles {
|
||||
if role == domain.RoleOrgProjectCreator {
|
||||
roles[i] = domain.RoleSelfManagementGlobal
|
||||
}
|
||||
}
|
||||
events = append(events, org.NewMemberChangedEvent(ctx, orgAgg, userID, roles...))
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
||||
|
||||
type globalOrgMembersWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
role string
|
||||
members map[string][]string
|
||||
}
|
||||
|
||||
func newGlobalOrgMemberWriteModel(orgID, role string) *globalOrgMembersWriteModel {
|
||||
return &globalOrgMembersWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
ResourceOwner: orgID,
|
||||
AggregateID: orgID,
|
||||
},
|
||||
role: role,
|
||||
members: make(map[string][]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *globalOrgMembersWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *org.MemberAddedEvent:
|
||||
for _, role := range e.Roles {
|
||||
if wm.role == role {
|
||||
wm.members[e.UserID] = e.Roles
|
||||
break
|
||||
}
|
||||
}
|
||||
case *org.MemberChangedEvent:
|
||||
delete(wm.members, e.UserID)
|
||||
for _, role := range e.Roles {
|
||||
if wm.role == role {
|
||||
wm.members[e.UserID] = e.Roles
|
||||
break
|
||||
}
|
||||
}
|
||||
case *org.MemberRemovedEvent:
|
||||
delete(wm.members, e.UserID)
|
||||
case *org.MemberCascadeRemovedEvent:
|
||||
delete(wm.members, e.UserID)
|
||||
case *user.UserRemovedEvent:
|
||||
delete(wm.members, e.Aggregate().ID)
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *globalOrgMembersWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
AddQuery().
|
||||
AggregateTypes(org.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(
|
||||
org.MemberAddedEventType,
|
||||
org.MemberChangedEventType,
|
||||
org.MemberRemovedEventType,
|
||||
org.MemberCascadeRemovedEventType,
|
||||
).
|
||||
Or().
|
||||
AggregateTypes(user.AggregateType).
|
||||
EventTypes(user.UserRemovedType).
|
||||
Builder()
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step3 struct {
|
||||
DefaultPasswordAgePolicy domain.PasswordAgePolicy
|
||||
}
|
||||
|
||||
func (s *Step3) Step() domain.Step {
|
||||
return domain.Step3
|
||||
}
|
||||
|
||||
func (s *Step3) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep3(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep3(ctx context.Context, step *Step3) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
event, err := c.addDefaultPasswordAgePolicy(ctx, iamAgg, NewInstancePasswordAgePolicyWriteModel(), &domain.PasswordAgePolicy{
|
||||
MaxAgeDays: step.DefaultPasswordAgePolicy.MaxAgeDays,
|
||||
ExpireWarnDays: step.DefaultPasswordAgePolicy.ExpireWarnDays,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-DBqgq").Info("default password age policy set up")
|
||||
return []eventstore.Command{event}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step4 struct {
|
||||
DefaultPasswordLockoutPolicy domain.LockoutPolicy
|
||||
}
|
||||
|
||||
func (s *Step4) Step() domain.Step {
|
||||
return domain.Step4
|
||||
}
|
||||
|
||||
func (s *Step4) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep4(ctx, s)
|
||||
}
|
||||
|
||||
//This step should not be executed when a new instance is setup, because its not used anymore
|
||||
//SetupStep4 is no op in favour of step 18.
|
||||
//Password lockout policy is replaced by lockout policy
|
||||
func (c *Commands) SetupStep4(ctx context.Context, step *Step4) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
return nil, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step5 struct {
|
||||
DefaultOrgIAMPolicy domain.DomainPolicy
|
||||
}
|
||||
|
||||
func (s *Step5) Step() domain.Step {
|
||||
return domain.Step5
|
||||
}
|
||||
|
||||
func (s *Step5) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep5(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep5(ctx context.Context, step *Step5) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
event, err := c.addDefaultDomainPolicy(ctx, iamAgg, NewInstanceDomainPolicyWriteModel(), &domain.DomainPolicy{
|
||||
UserLoginMustBeDomain: step.DefaultOrgIAMPolicy.UserLoginMustBeDomain,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-ADgd2").Info("default org iam policy set up")
|
||||
return []eventstore.Command{event}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step6 struct {
|
||||
DefaultLabelPolicy domain.LabelPolicy
|
||||
}
|
||||
|
||||
func (s *Step6) Step() domain.Step {
|
||||
return domain.Step6
|
||||
}
|
||||
|
||||
func (s *Step6) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep6(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep6(ctx context.Context, step *Step6) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
iamAgg := InstanceAggregateFromWriteModel(&iam.WriteModel)
|
||||
event, err := c.addDefaultLabelPolicy(ctx, iamAgg, NewInstanceLabelPolicyWriteModel(), &step.DefaultLabelPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-ADgd2").Info("default label policy set up")
|
||||
return []eventstore.Command{event}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step7 struct {
|
||||
OTP bool
|
||||
}
|
||||
|
||||
func (s *Step7) Step() domain.Step {
|
||||
return domain.Step7
|
||||
}
|
||||
|
||||
func (s *Step7) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep7(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep7(ctx context.Context, step *Step7) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
secondFactorModel := NewInstanceSecondFactorWriteModel(domain.SecondFactorTypeOTP)
|
||||
iamAgg := InstanceAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
||||
if !step.OTP {
|
||||
return []eventstore.Command{}, nil
|
||||
}
|
||||
event, err := c.addSecondFactorToDefaultLoginPolicy(ctx, iamAgg, secondFactorModel, domain.SecondFactorTypeOTP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-Dggsg").Info("added OTP to 2FA login policy")
|
||||
return []eventstore.Command{event}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step8 struct {
|
||||
U2F bool
|
||||
}
|
||||
|
||||
func (s *Step8) Step() domain.Step {
|
||||
return domain.Step8
|
||||
}
|
||||
|
||||
func (s *Step8) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep8(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep8(ctx context.Context, step *Step8) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
secondFactorModel := NewInstanceSecondFactorWriteModel(domain.SecondFactorTypeU2F)
|
||||
iamAgg := InstanceAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
||||
if !step.U2F {
|
||||
return []eventstore.Command{}, nil
|
||||
}
|
||||
event, err := c.addSecondFactorToDefaultLoginPolicy(ctx, iamAgg, secondFactorModel, domain.SecondFactorTypeU2F)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-BDhne").Info("added U2F to 2FA login policy")
|
||||
return []eventstore.Command{event}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step9 struct {
|
||||
Passwordless bool
|
||||
}
|
||||
|
||||
func (s *Step9) Step() domain.Step {
|
||||
return domain.Step9
|
||||
}
|
||||
|
||||
func (s *Step9) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep9(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep9(ctx context.Context, step *Step9) error {
|
||||
fn := func(iam *InstanceWriteModel) ([]eventstore.Command, error) {
|
||||
multiFactorModel := NewInstanceMultiFactorWriteModel(domain.MultiFactorTypeU2FWithPIN)
|
||||
iamAgg := InstanceAggregateFromWriteModel(&multiFactorModel.MultiFactorWriteModel.WriteModel)
|
||||
if !step.Passwordless {
|
||||
return []eventstore.Command{}, nil
|
||||
}
|
||||
passwordlessEvent, err := setPasswordlessAllowedInPolicy(ctx, c, iamAgg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-AEG2t").Info("allowed passwordless in login policy")
|
||||
multifactorEvent, err := c.addMultiFactorToDefaultLoginPolicy(ctx, iamAgg, multiFactorModel, domain.MultiFactorTypeU2FWithPIN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-ADfng").Info("added passwordless to MFA login policy")
|
||||
return []eventstore.Command{passwordlessEvent, multifactorEvent}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
||||
|
||||
func setPasswordlessAllowedInPolicy(ctx context.Context, c *Commands, iamAgg *eventstore.Aggregate) (eventstore.Command, error) {
|
||||
policy, err := c.getDefaultLoginPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policy.PasswordlessType = domain.PasswordlessTypeAllowed
|
||||
return c.changeDefaultLoginPolicy(ctx, iamAgg, NewInstanceLoginPolicyWriteModel(), policy)
|
||||
}
|
@ -36,18 +36,18 @@ func (c *Commands) ChangeUsername(ctx context.Context, orgID, userID, userName s
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6m9gs", "Errors.User.UsernameNotChanged")
|
||||
}
|
||||
|
||||
orgIAMPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-38fnu", "Errors.Org.DomainPolicy.NotExisting")
|
||||
}
|
||||
|
||||
if err := CheckDomainPolicyForUserName(userName, orgIAMPolicy); err != nil {
|
||||
if err := CheckDomainPolicyForUserName(userName, domainPolicy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
|
||||
pushedEvents, err := c.eventstore.Push(ctx,
|
||||
user.NewUsernameChangedEvent(ctx, userAgg, existingUser.UserName, userName, orgIAMPolicy.UserLoginMustBeDomain))
|
||||
user.NewUsernameChangedEvent(ctx, userAgg, existingUser.UserName, userName, domainPolicy.UserLoginMustBeDomain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -186,13 +186,13 @@ func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string,
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-m9od", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
orgIAMPolicy, err := c.getOrgDomainPolicy(ctx, existingUser.ResourceOwner)
|
||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, existingUser.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.OrgIAMPolicy.NotExisting")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.DomainPolicy.NotExisting")
|
||||
}
|
||||
var events []eventstore.Command
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
events = append(events, user.NewUserRemovedEvent(ctx, userAgg, existingUser.UserName, existingUser.IDPLinks, orgIAMPolicy.UserLoginMustBeDomain))
|
||||
events = append(events, user.NewUserRemovedEvent(ctx, userAgg, existingUser.UserName, existingUser.IDPLinks, domainPolicy.UserLoginMustBeDomain))
|
||||
|
||||
for _, grantID := range cascadingGrantIDs {
|
||||
removeEvent, _, err := c.removeUserGrant(ctx, grantID, "", true)
|
||||
@ -320,7 +320,7 @@ func (c *Commands) userDomainClaimed(ctx context.Context, userID string) (events
|
||||
changedUserGrant := NewUserWriteModel(userID, existingUser.ResourceOwner)
|
||||
userAgg := UserAggregateFromWriteModel(&changedUserGrant.WriteModel)
|
||||
|
||||
orgIAMPolicy, err := c.getOrgDomainPolicy(ctx, existingUser.ResourceOwner)
|
||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, existingUser.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -335,7 +335,7 @@ func (c *Commands) userDomainClaimed(ctx context.Context, userID string) (events
|
||||
userAgg,
|
||||
fmt.Sprintf("%s@temporary.%s", id, c.iamDomain),
|
||||
existingUser.UserName,
|
||||
orgIAMPolicy.UserLoginMustBeDomain),
|
||||
domainPolicy.UserLoginMustBeDomain),
|
||||
}, changedUserGrant, nil
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Hum
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-XYFk9", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
orgIAMPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.Org.DomainPolicy.NotFound")
|
||||
}
|
||||
@ -36,7 +36,7 @@ func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Hum
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexityPolicy.NotFound")
|
||||
}
|
||||
events, addedHuman, err := c.addHuman(ctx, orgID, human, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
events, addedHuman, err := c.addHuman(ctx, orgID, human, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -57,7 +57,7 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
|
||||
if orgID == "" {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-5N8fs", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
orgIAMPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-2N9fs", "Errors.Org.DomainPolicy.NotFound")
|
||||
}
|
||||
@ -65,7 +65,7 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
|
||||
if err != nil {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexityPolicy.NotFound")
|
||||
}
|
||||
events, addedHuman, addedCode, code, err := c.importHuman(ctx, orgID, human, passwordless, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator)
|
||||
events, addedHuman, addedCode, code, err := c.importHuman(ctx, orgID, human, passwordless, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -89,21 +89,21 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
|
||||
return writeModelToHuman(addedHuman), passwordlessCode, nil
|
||||
}
|
||||
|
||||
func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human, orgIAMPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
|
||||
func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
|
||||
if orgID == "" || !human.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-67Ms8", "Errors.User.Invalid")
|
||||
}
|
||||
if human.Password != nil && human.SecretString != "" {
|
||||
human.ChangeRequired = true
|
||||
}
|
||||
return c.createHuman(ctx, orgID, human, nil, false, false, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
return c.createHuman(ctx, orgID, human, nil, false, false, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
}
|
||||
|
||||
func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, orgIAMPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator, passwordlessCodeGenerator crypto.Generator) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) {
|
||||
func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator, passwordlessCodeGenerator crypto.Generator) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) {
|
||||
if orgID == "" || !human.IsValid() {
|
||||
return nil, nil, nil, "", caos_errs.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.User.Invalid")
|
||||
}
|
||||
events, humanWriteModel, err = c.createHuman(ctx, orgID, human, nil, false, passwordless, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
events, humanWriteModel, err = c.createHuman(ctx, orgID, human, nil, false, passwordless, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
if err != nil {
|
||||
return nil, nil, nil, "", err
|
||||
}
|
||||
@ -122,7 +122,7 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-GEdf2", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
orgIAMPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.Org.DomainPolicy.NotFound")
|
||||
}
|
||||
@ -137,7 +137,7 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
|
||||
if !loginPolicy.AllowRegister {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-SAbr3", "Errors.Org.LoginPolicy.RegistrationNotAllowed")
|
||||
}
|
||||
userEvents, registeredHuman, err := c.registerHuman(ctx, orgID, human, link, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
userEvents, registeredHuman, err := c.registerHuman(ctx, orgID, human, link, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -171,7 +171,7 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
|
||||
return writeModelToHuman(registeredHuman), nil
|
||||
}
|
||||
|
||||
func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, orgIAMPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
|
||||
func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
|
||||
if human != nil && human.Username == "" {
|
||||
human.Username = human.EmailAddress
|
||||
}
|
||||
@ -181,14 +181,14 @@ func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domai
|
||||
if human.Password != nil && human.SecretString != "" {
|
||||
human.ChangeRequired = false
|
||||
}
|
||||
return c.createHuman(ctx, orgID, human, link, true, false, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
return c.createHuman(ctx, orgID, human, link, true, false, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
|
||||
}
|
||||
|
||||
func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, selfregister, passwordless bool, orgIAMPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
|
||||
if err := human.CheckDomainPolicy(orgIAMPolicy); err != nil {
|
||||
func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, selfregister, passwordless bool, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
|
||||
if err := human.CheckDomainPolicy(domainPolicy); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !orgIAMPolicy.UserLoginMustBeDomain {
|
||||
if !domainPolicy.UserLoginMustBeDomain {
|
||||
usernameSplit := strings.Split(human.Username, "@")
|
||||
if len(usernameSplit) != 2 {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-Dfd21", "Errors.User.Invalid")
|
||||
@ -219,9 +219,9 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
||||
var events []eventstore.Command
|
||||
|
||||
if selfregister {
|
||||
events = append(events, createRegisterHumanEvent(ctx, userAgg, human, orgIAMPolicy.UserLoginMustBeDomain))
|
||||
events = append(events, createRegisterHumanEvent(ctx, userAgg, human, domainPolicy.UserLoginMustBeDomain))
|
||||
} else {
|
||||
events = append(events, createAddHumanEvent(ctx, userAgg, human, orgIAMPolicy.UserLoginMustBeDomain))
|
||||
events = append(events, createAddHumanEvent(ctx, userAgg, human, domainPolicy.UserLoginMustBeDomain))
|
||||
}
|
||||
|
||||
if link != nil {
|
||||
|
@ -67,7 +67,8 @@ func (wm *HumanInitCodeWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
AddQuery().
|
||||
AggregateTypes(user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(user.UserV1AddedType,
|
||||
EventTypes(
|
||||
user.UserV1AddedType,
|
||||
user.HumanAddedType,
|
||||
user.UserV1RegisteredType,
|
||||
user.HumanRegisteredType,
|
||||
|
@ -109,7 +109,8 @@ func (wm *HumanWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
AddQuery().
|
||||
AggregateTypes(user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(user.HumanAddedType,
|
||||
EventTypes(
|
||||
user.HumanAddedType,
|
||||
user.HumanRegisteredType,
|
||||
user.HumanInitialCodeAddedType,
|
||||
user.HumanInitializedCheckSucceededType,
|
||||
|
@ -28,7 +28,7 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
|
||||
orgPolicy, err := c.getOrgDomainPolicy(ctx, org.AggregateID)
|
||||
if err != nil {
|
||||
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.OrgIAMPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.DomainPolicy.NotFound")
|
||||
}
|
||||
otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
|
@ -176,7 +176,7 @@ func TestCommandSide_AddHumanOTP(t *testing.T) {
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
|
@ -102,7 +102,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -137,7 +137,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -178,7 +178,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -272,7 +272,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -359,7 +359,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -440,7 +440,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -539,7 +539,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -732,7 +732,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -767,7 +767,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -808,7 +808,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -896,7 +896,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -978,7 +978,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1082,7 +1082,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1190,7 +1190,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1294,7 +1294,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1495,7 +1495,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1533,7 +1533,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1579,7 +1579,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1641,7 +1641,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1703,7 +1703,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
false,
|
||||
),
|
||||
@ -1782,7 +1782,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
false,
|
||||
),
|
||||
@ -1919,7 +1919,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -2024,7 +2024,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -2123,7 +2123,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -2244,7 +2244,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
|
@ -317,7 +317,6 @@ type HumanU2FLoginReadModel struct {
|
||||
Challenge string
|
||||
AllowedCredentialIDs [][]byte
|
||||
UserVerification domain.UserVerificationRequirement
|
||||
User
|
||||
State domain.UserState
|
||||
}
|
||||
|
||||
|
@ -13,11 +13,11 @@ func (c *Commands) AddMachine(ctx context.Context, orgID string, machine *domain
|
||||
if !machine.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-bm9Ds", "Errors.User.Invalid")
|
||||
}
|
||||
orgIAMPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.DomainPolicy.NotFound")
|
||||
}
|
||||
if !orgIAMPolicy.UserLoginMustBeDomain {
|
||||
if !domainPolicy.UserLoginMustBeDomain {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.Invalid")
|
||||
}
|
||||
userID, err := c.idGenerator.Next()
|
||||
@ -33,7 +33,7 @@ func (c *Commands) AddMachine(ctx context.Context, orgID string, machine *domain
|
||||
machine.Username,
|
||||
machine.Name,
|
||||
machine.Description,
|
||||
orgIAMPolicy.UserLoginMustBeDomain,
|
||||
domainPolicy.UserLoginMustBeDomain,
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -83,7 +83,7 @@ func TestCommandSide_AddMachine(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
false,
|
||||
),
|
||||
@ -110,7 +110,7 @@ func TestCommandSide_AddMachine(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewOrgDomainPolicyAddedEvent(context.Background(),
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
|
@ -128,7 +128,7 @@ func UserAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregat
|
||||
|
||||
func CheckDomainPolicyForUserName(userName string, policy *domain.DomainPolicy) error {
|
||||
if policy == nil {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "COMMAND-3Mb9s", "Errors.Users.OrgIamPolicyNil")
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "COMMAND-3Mb9s", "Errors.Users.DomainPolicyNil")
|
||||
}
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(userName, "@") {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "COMMAND-4M9vs", "Errors.User.EmailAsUsernameNotAllowed")
|
||||
|
@ -202,7 +202,7 @@ func TestCommandSide_UsernameChange(t *testing.T) {
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -244,7 +244,7 @@ func TestCommandSide_UsernameChange(t *testing.T) {
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1026,7 +1026,7 @@ func TestCommandSide_RemoveUser(t *testing.T) {
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1090,7 +1090,7 @@ func TestCommandSide_RemoveUser(t *testing.T) {
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
@ -1147,7 +1147,7 @@ func TestCommandSide_RemoveUser(t *testing.T) {
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewInstnaceDomainPolicyAddedEvent(context.Background(),
|
||||
instance.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
true,
|
||||
),
|
||||
|
36
internal/command/v2/command.go
Normal file
36
internal/command/v2/command.go
Normal file
@ -0,0 +1,36 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/action"
|
||||
iam_repo "github.com/caos/zitadel/internal/repository/instance"
|
||||
"github.com/caos/zitadel/internal/repository/keypair"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
proj_repo "github.com/caos/zitadel/internal/repository/project"
|
||||
usr_repo "github.com/caos/zitadel/internal/repository/user"
|
||||
usr_grant_repo "github.com/caos/zitadel/internal/repository/usergrant"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
es *eventstore.Eventstore
|
||||
userPasswordAlg crypto.HashAlgorithm
|
||||
iamDomain string
|
||||
}
|
||||
|
||||
func New(es *eventstore.Eventstore, iamDomain string, defaults sd.SystemDefaults) *Command {
|
||||
iam_repo.RegisterEventMappers(es)
|
||||
org.RegisterEventMappers(es)
|
||||
usr_repo.RegisterEventMappers(es)
|
||||
usr_grant_repo.RegisterEventMappers(es)
|
||||
proj_repo.RegisterEventMappers(es)
|
||||
keypair.RegisterEventMappers(es)
|
||||
action.RegisterEventMappers(es)
|
||||
|
||||
return &Command{
|
||||
es: es,
|
||||
iamDomain: iamDomain,
|
||||
userPasswordAlg: crypto.NewBCrypt(defaults.SecretGenerators.PasswordSaltCost),
|
||||
}
|
||||
}
|
290
internal/command/v2/instance.go
Normal file
290
internal/command/v2/instance.go
Normal file
@ -0,0 +1,290 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/ui/console"
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
const (
|
||||
zitadelProjectName = "ZITADEL"
|
||||
mgmtAppName = "Management-API"
|
||||
adminAppName = "Admin-API"
|
||||
authAppName = "Auth-API"
|
||||
consoleAppName = "Console"
|
||||
consoleRedirectPath = console.HandlerPrefix + "/auth/callback"
|
||||
consolePostLogoutPath = console.HandlerPrefix + "/signedout"
|
||||
)
|
||||
|
||||
type InstanceSetup struct {
|
||||
Org OrgSetup
|
||||
Zitadel ZitadelConfig
|
||||
PasswordComplexityPolicy struct {
|
||||
MinLength uint64
|
||||
HasLowercase bool
|
||||
HasUppercase bool
|
||||
HasNumber bool
|
||||
HasSymbol bool
|
||||
}
|
||||
PasswordAgePolicy struct {
|
||||
ExpireWarnDays uint64
|
||||
MaxAgeDays uint64
|
||||
}
|
||||
DomainPolicy struct {
|
||||
UserLoginMustBeDomain bool
|
||||
}
|
||||
LoginPolicy struct {
|
||||
AllowUsernamePassword bool
|
||||
AllowRegister bool
|
||||
AllowExternalIDP bool
|
||||
ForceMFA bool
|
||||
HidePasswordReset bool
|
||||
PasswordlessType domain.PasswordlessType
|
||||
PasswordCheckLifetime time.Duration
|
||||
ExternalLoginCheckLifetime time.Duration
|
||||
MfaInitSkipLifetime time.Duration
|
||||
SecondFactorCheckLifetime time.Duration
|
||||
MultiFactorCheckLifetime time.Duration
|
||||
}
|
||||
PrivacyPolicy struct {
|
||||
TOSLink string
|
||||
PrivacyLink string
|
||||
HelpLink string
|
||||
}
|
||||
LockoutPolicy struct {
|
||||
MaxAttempts uint64
|
||||
ShouldShowLockoutFailure bool
|
||||
}
|
||||
EmailTemplate []byte
|
||||
MessageTexts []*domain.CustomMessageText
|
||||
}
|
||||
|
||||
type ZitadelConfig struct {
|
||||
IsDevMode bool
|
||||
BaseURL string
|
||||
|
||||
projectID string
|
||||
mgmtID string
|
||||
mgmtClientID string
|
||||
adminID string
|
||||
adminClientID string
|
||||
authID string
|
||||
authClientID string
|
||||
consoleID string
|
||||
consoleClientID string
|
||||
}
|
||||
|
||||
func (s *InstanceSetup) generateIDs() (err error) {
|
||||
s.Zitadel.projectID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Zitadel.mgmtID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Zitadel.mgmtClientID, err = domain.NewClientID(id.SonyFlakeGenerator, zitadelProjectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Zitadel.adminID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Zitadel.adminClientID, err = domain.NewClientID(id.SonyFlakeGenerator, zitadelProjectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Zitadel.authID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Zitadel.authClientID, err = domain.NewClientID(id.SonyFlakeGenerator, zitadelProjectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Zitadel.consoleID, err = id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Zitadel.consoleClientID, err = domain.NewClientID(id.SonyFlakeGenerator, zitadelProjectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (command *Command) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*domain.ObjectDetails, error) {
|
||||
// TODO
|
||||
// instanceID, err := id.SonyFlakeGenerator.Next()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
ctx = authz.SetCtxData(authz.WithInstance(ctx, authz.Instance{ID: "system"}), authz.CtxData{OrgID: domain.IAMID, ResourceOwner: domain.IAMID})
|
||||
|
||||
orgID, err := id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userID, err := id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = setup.generateIDs(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setup.Org.Human.PasswordChangeRequired = true
|
||||
|
||||
instanceAgg := instance.NewAggregate()
|
||||
orgAgg := org.NewAggregate(orgID, orgID)
|
||||
userAgg := user.NewAggregate(userID, orgID)
|
||||
projectAgg := project.NewAggregate(setup.Zitadel.projectID, orgID)
|
||||
|
||||
validations := []preparation.Validation{
|
||||
AddPasswordComplexityPolicy(
|
||||
instanceAgg,
|
||||
setup.PasswordComplexityPolicy.MinLength,
|
||||
setup.PasswordComplexityPolicy.HasLowercase,
|
||||
setup.PasswordComplexityPolicy.HasUppercase,
|
||||
setup.PasswordComplexityPolicy.HasNumber,
|
||||
setup.PasswordComplexityPolicy.HasSymbol,
|
||||
),
|
||||
AddPasswordAgePolicy(
|
||||
instanceAgg,
|
||||
setup.PasswordAgePolicy.ExpireWarnDays,
|
||||
setup.PasswordAgePolicy.MaxAgeDays,
|
||||
),
|
||||
AddDefaultDomainPolicy(
|
||||
instanceAgg,
|
||||
setup.DomainPolicy.UserLoginMustBeDomain,
|
||||
),
|
||||
AddDefaultLoginPolicy(
|
||||
instanceAgg,
|
||||
setup.LoginPolicy.AllowUsernamePassword,
|
||||
setup.LoginPolicy.AllowRegister,
|
||||
setup.LoginPolicy.AllowExternalIDP,
|
||||
setup.LoginPolicy.ForceMFA,
|
||||
setup.LoginPolicy.HidePasswordReset,
|
||||
setup.LoginPolicy.PasswordlessType,
|
||||
setup.LoginPolicy.PasswordCheckLifetime,
|
||||
setup.LoginPolicy.ExternalLoginCheckLifetime,
|
||||
setup.LoginPolicy.MfaInitSkipLifetime,
|
||||
setup.LoginPolicy.SecondFactorCheckLifetime,
|
||||
setup.LoginPolicy.MultiFactorCheckLifetime,
|
||||
),
|
||||
AddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeOTP),
|
||||
AddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeU2F),
|
||||
AddMultiFactorToDefaultLoginPolicy(instanceAgg, domain.MultiFactorTypeU2FWithPIN),
|
||||
|
||||
AddPrivacyPolicy(instanceAgg, setup.PrivacyPolicy.TOSLink, setup.PrivacyPolicy.PrivacyLink, setup.PrivacyPolicy.HelpLink),
|
||||
AddDefaultLockoutPolicy(instanceAgg, setup.LockoutPolicy.MaxAttempts, setup.LockoutPolicy.ShouldShowLockoutFailure),
|
||||
|
||||
AddEmailTemplate(instanceAgg, setup.EmailTemplate),
|
||||
}
|
||||
|
||||
for _, msg := range setup.MessageTexts {
|
||||
validations = append(validations, SetInstanceCustomTexts(instanceAgg, msg))
|
||||
}
|
||||
|
||||
validations = append(validations,
|
||||
AddOrg(orgAgg, setup.Org.Name, command.iamDomain),
|
||||
AddHumanCommand(userAgg, &setup.Org.Human, command.userPasswordAlg),
|
||||
AddOrgMember(orgAgg, userID, domain.RoleOrgOwner),
|
||||
|
||||
AddProject(projectAgg, zitadelProjectName, userID, false, false, false, domain.PrivateLabelingSettingUnspecified),
|
||||
|
||||
SetIAMProject(instanceAgg, projectAgg.ID),
|
||||
|
||||
AddAPIApp(
|
||||
*projectAgg,
|
||||
setup.Zitadel.mgmtID,
|
||||
mgmtAppName,
|
||||
setup.Zitadel.mgmtClientID,
|
||||
nil,
|
||||
domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
),
|
||||
|
||||
AddAPIApp(
|
||||
*projectAgg,
|
||||
setup.Zitadel.adminID,
|
||||
adminAppName,
|
||||
setup.Zitadel.adminClientID,
|
||||
nil,
|
||||
domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
),
|
||||
|
||||
AddAPIApp(
|
||||
*projectAgg,
|
||||
setup.Zitadel.authID,
|
||||
authAppName,
|
||||
setup.Zitadel.authClientID,
|
||||
nil,
|
||||
domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
),
|
||||
|
||||
AddOIDCApp(
|
||||
*projectAgg,
|
||||
domain.OIDCVersionV1,
|
||||
setup.Zitadel.consoleID,
|
||||
consoleAppName,
|
||||
setup.Zitadel.consoleClientID,
|
||||
nil,
|
||||
[]string{setup.Zitadel.BaseURL + consoleRedirectPath},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeUserAgent,
|
||||
domain.OIDCAuthMethodTypeNone,
|
||||
[]string{setup.Zitadel.BaseURL + consolePostLogoutPath},
|
||||
setup.Zitadel.IsDevMode,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
nil,
|
||||
),
|
||||
)
|
||||
|
||||
cmds, err := preparation.PrepareCommands(ctx, command.es.Filter, validations...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, err := command.es.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &domain.ObjectDetails{
|
||||
Sequence: events[len(events)-1].Sequence(),
|
||||
EventDate: events[len(events)-1].CreationDate(),
|
||||
ResourceOwner: orgID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//SetIAMProject defines the commands to set the id of the IAM project onto the instance
|
||||
func SetIAMProject(a *instance.Aggregate, projectID string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{
|
||||
instance.NewIAMProjectSetEvent(ctx, &a.Aggregate, projectID),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
25
internal/command/v2/instance_domain_policy.go
Normal file
25
internal/command/v2/instance_domain_policy.go
Normal file
@ -0,0 +1,25 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func AddDefaultDomainPolicy(
|
||||
a *instance.Aggregate,
|
||||
userLoginMustBeDomain bool,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewDomainPolicyAddedEvent(ctx, &a.Aggregate,
|
||||
userLoginMustBeDomain,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
107
internal/command/v2/instance_email_template.go
Normal file
107
internal/command/v2/instance_email_template.go
Normal file
@ -0,0 +1,107 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func AddEmailTemplate(
|
||||
a *instance.Aggregate,
|
||||
tempalte []byte,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewMailTemplateAddedEvent(ctx, &a.Aggregate,
|
||||
tempalte,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func SetInstanceCustomTexts(
|
||||
a *instance.Aggregate,
|
||||
msg *domain.CustomMessageText,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
existing, err := existingInstanceCustomMessageText(ctx, filter, msg.MessageTextType, msg.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmds := make([]eventstore.Command, 0, 7)
|
||||
if existing.Greeting != msg.Greeting {
|
||||
if msg.Greeting != "" {
|
||||
cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageGreeting, msg.Greeting, msg.Language))
|
||||
} else {
|
||||
cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageGreeting, msg.Language))
|
||||
}
|
||||
}
|
||||
if existing.Subject != msg.Subject {
|
||||
if msg.Subject != "" {
|
||||
cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageSubject, msg.Subject, msg.Language))
|
||||
} else {
|
||||
cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageSubject, msg.Language))
|
||||
}
|
||||
}
|
||||
if existing.Title != msg.Title {
|
||||
if msg.Title != "" {
|
||||
cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageTitle, msg.Title, msg.Language))
|
||||
} else {
|
||||
cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageTitle, msg.Language))
|
||||
}
|
||||
}
|
||||
if existing.PreHeader != msg.PreHeader {
|
||||
if msg.PreHeader != "" {
|
||||
cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessagePreHeader, msg.PreHeader, msg.Language))
|
||||
} else {
|
||||
cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessagePreHeader, msg.Language))
|
||||
}
|
||||
}
|
||||
if existing.Text != msg.Text {
|
||||
if msg.Text != "" {
|
||||
cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageText, msg.Text, msg.Language))
|
||||
} else {
|
||||
cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageText, msg.Language))
|
||||
}
|
||||
}
|
||||
if existing.ButtonText != msg.ButtonText {
|
||||
if msg.ButtonText != "" {
|
||||
cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageButtonText, msg.ButtonText, msg.Language))
|
||||
} else {
|
||||
cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageButtonText, msg.Language))
|
||||
}
|
||||
}
|
||||
if existing.FooterText != msg.FooterText {
|
||||
if msg.FooterText != "" {
|
||||
cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageFooterText, msg.FooterText, msg.Language))
|
||||
} else {
|
||||
cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageFooterText, msg.Language))
|
||||
}
|
||||
}
|
||||
// TODO: what if no text changed? len(events) == 0
|
||||
return cmds, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func existingInstanceCustomMessageText(ctx context.Context, filter preparation.FilterToQueryReducer, textType string, lang language.Tag) (*command.InstanceCustomMessageTextWriteModel, error) {
|
||||
writeModel := command.NewInstanceCustomMessageTextWriteModel(textType, lang)
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
writeModel.Reduce()
|
||||
return writeModel, nil
|
||||
}
|
45
internal/command/v2/instance_label_policy.go
Normal file
45
internal/command/v2/instance_label_policy.go
Normal file
@ -0,0 +1,45 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func AddDefaultLabelPolicy(
|
||||
a *instance.Aggregate,
|
||||
primaryColor,
|
||||
backgroundColor,
|
||||
warnColor,
|
||||
fontColor,
|
||||
primaryColorDark,
|
||||
backgroundColorDark,
|
||||
warnColorDark,
|
||||
fontColorDark string,
|
||||
hideLoginNameSuffix,
|
||||
errorMsgPopup,
|
||||
disableWatermark bool,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewLabelPolicyAddedEvent(ctx, &a.Aggregate,
|
||||
primaryColor,
|
||||
backgroundColor,
|
||||
warnColor,
|
||||
fontColor,
|
||||
primaryColorDark,
|
||||
backgroundColorDark,
|
||||
warnColorDark,
|
||||
fontColorDark,
|
||||
hideLoginNameSuffix,
|
||||
errorMsgPopup,
|
||||
disableWatermark,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
24
internal/command/v2/instance_lockout_policy.go
Normal file
24
internal/command/v2/instance_lockout_policy.go
Normal file
@ -0,0 +1,24 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func AddDefaultLockoutPolicy(
|
||||
a *instance.Aggregate,
|
||||
maxAttempts uint64,
|
||||
showLockoutFailure bool,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewLockoutPolicyAddedEvent(ctx, &a.Aggregate, maxAttempts, showLockoutFailure),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
69
internal/command/v2/instance_login_policy.go
Normal file
69
internal/command/v2/instance_login_policy.go
Normal file
@ -0,0 +1,69 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func AddDefaultLoginPolicy(
|
||||
a *instance.Aggregate,
|
||||
allowUsernamePassword bool,
|
||||
allowRegister bool,
|
||||
allowExternalIDP bool,
|
||||
forceMFA bool,
|
||||
hidePasswordReset bool,
|
||||
passwordlessType domain.PasswordlessType,
|
||||
passwordCheckLifetime time.Duration,
|
||||
externalLoginCheckLifetime time.Duration,
|
||||
mfaInitSkipLifetime time.Duration,
|
||||
secondFactorCheckLifetime time.Duration,
|
||||
multiFactorCheckLifetime time.Duration,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewLoginPolicyAddedEvent(ctx, &a.Aggregate,
|
||||
allowUsernamePassword,
|
||||
allowRegister,
|
||||
allowExternalIDP,
|
||||
forceMFA,
|
||||
hidePasswordReset,
|
||||
passwordlessType,
|
||||
passwordCheckLifetime,
|
||||
externalLoginCheckLifetime,
|
||||
mfaInitSkipLifetime,
|
||||
secondFactorCheckLifetime,
|
||||
multiFactorCheckLifetime,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func AddSecondFactorToDefaultLoginPolicy(a *instance.Aggregate, factor domain.SecondFactorType) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewLoginPolicySecondFactorAddedEvent(ctx, &a.Aggregate, factor),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func AddMultiFactorToDefaultLoginPolicy(a *instance.Aggregate, factor domain.MultiFactorType) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewLoginPolicyMultiFactorAddedEvent(ctx, &a.Aggregate, factor),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
27
internal/command/v2/instance_password_age_policy.go
Normal file
27
internal/command/v2/instance_password_age_policy.go
Normal file
@ -0,0 +1,27 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func AddPasswordAgePolicy(
|
||||
a *instance.Aggregate,
|
||||
expireWarnDays,
|
||||
maxAgeDays uint64,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewPasswordAgePolicyAddedEvent(ctx, &a.Aggregate,
|
||||
expireWarnDays,
|
||||
maxAgeDays,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
33
internal/command/v2/instance_password_complexity_policy.go
Normal file
33
internal/command/v2/instance_password_complexity_policy.go
Normal file
@ -0,0 +1,33 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func AddPasswordComplexityPolicy(
|
||||
a *instance.Aggregate,
|
||||
minLength uint64,
|
||||
hasLowercase,
|
||||
hasUppercase,
|
||||
hasNumber,
|
||||
hasSymbol bool,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewPasswordComplexityPolicyAddedEvent(ctx, &a.Aggregate,
|
||||
minLength,
|
||||
hasLowercase,
|
||||
hasUppercase,
|
||||
hasNumber,
|
||||
hasSymbol,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
25
internal/command/v2/instance_privacy_policy.go
Normal file
25
internal/command/v2/instance_privacy_policy.go
Normal file
@ -0,0 +1,25 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func AddPrivacyPolicy(
|
||||
a *instance.Aggregate,
|
||||
tosLink,
|
||||
privacyLink,
|
||||
helpLink string,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{
|
||||
instance.NewPrivacyPolicyAddedEvent(ctx, &a.Aggregate, tosLink, privacyLink, helpLink),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
72
internal/command/v2/org.go
Normal file
72
internal/command/v2/org.go
Normal file
@ -0,0 +1,72 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
user_repo "github.com/caos/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
type OrgSetup struct {
|
||||
Name string
|
||||
Human AddHuman
|
||||
}
|
||||
|
||||
func (command *Command) SetUpOrg(ctx context.Context, o *OrgSetup) (*domain.ObjectDetails, error) {
|
||||
orgID, err := id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userID, err := id.SonyFlakeGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgAgg := org.NewAggregate(orgID, orgID)
|
||||
userAgg := user_repo.NewAggregate(userID, orgID)
|
||||
|
||||
cmds, err := preparation.PrepareCommands(ctx, command.es.Filter,
|
||||
AddOrg(orgAgg, o.Name, command.iamDomain),
|
||||
AddHumanCommand(userAgg, &o.Human, command.userPasswordAlg),
|
||||
AddOrgMember(orgAgg, userID, domain.RoleOrgOwner),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, err := command.es.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &domain.ObjectDetails{
|
||||
Sequence: events[len(events)-1].Sequence(),
|
||||
EventDate: events[len(events)-1].CreationDate(),
|
||||
ResourceOwner: orgID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//AddOrg defines the commands to create a new org,
|
||||
// this includes the verified default domain
|
||||
func AddOrg(a *org.Aggregate, name, iamDomain string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if name = strings.TrimSpace(name); name == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-mruNY", "Errors.Invalid.Argument")
|
||||
}
|
||||
defaultDomain := domain.NewIAMDomainName(name, iamDomain)
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{
|
||||
org.NewOrgAddedEvent(ctx, &a.Aggregate, name),
|
||||
org.NewDomainAddedEvent(ctx, &a.Aggregate, defaultDomain),
|
||||
org.NewDomainVerifiedEvent(ctx, &a.Aggregate, defaultDomain),
|
||||
org.NewDomainPrimarySetEvent(ctx, &a.Aggregate, defaultDomain),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
46
internal/command/v2/org_domain.go
Normal file
46
internal/command/v2/org_domain.go
Normal file
@ -0,0 +1,46 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func AddOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if domain = strings.TrimSpace(domain); domain == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-r3h4J", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{org.NewDomainAddedEvent(ctx, &a.Aggregate, domain)}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func VerifyOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if domain = strings.TrimSpace(domain); domain == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-yqlVQ", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists
|
||||
return []eventstore.Command{org.NewDomainVerifiedEvent(ctx, &a.Aggregate, domain)}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func SetPrimaryOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if domain = strings.TrimSpace(domain); domain == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-gmNqY", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
//TODO: check if already exists and verified
|
||||
return []eventstore.Command{org.NewDomainPrimarySetEvent(ctx, &a.Aggregate, domain)}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
133
internal/command/v2/org_domain_test.go
Normal file
133
internal/command/v2/org_domain_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func TestAddDomain(t *testing.T) {
|
||||
type args struct {
|
||||
a *org.Aggregate
|
||||
domain string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "invalid domain",
|
||||
args: args{
|
||||
a: org.NewAggregate("test", "test"),
|
||||
domain: "",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "ORG-r3h4J", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: org.NewAggregate("test", "test"),
|
||||
domain: "domain",
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
org.NewDomainAddedEvent(context.Background(), &org.NewAggregate("test", "test").Aggregate, "domain"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, AddOrgDomain(tt.args.a, tt.args.domain), nil, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyDomain(t *testing.T) {
|
||||
type args struct {
|
||||
a *org.Aggregate
|
||||
domain string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "invalid domain",
|
||||
args: args{
|
||||
a: org.NewAggregate("test", "test"),
|
||||
domain: "",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "ORG-yqlVQ", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: org.NewAggregate("test", "test"),
|
||||
domain: "domain",
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
org.NewDomainVerifiedEvent(context.Background(), &org.NewAggregate("test", "test").Aggregate, "domain"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, VerifyOrgDomain(tt.args.a, tt.args.domain), nil, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDomainPrimary(t *testing.T) {
|
||||
type args struct {
|
||||
a *org.Aggregate
|
||||
domain string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "invalid domain",
|
||||
args: args{
|
||||
a: org.NewAggregate("test", "test"),
|
||||
domain: "",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "ORG-gmNqY", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: org.NewAggregate("test", "test"),
|
||||
domain: "domain",
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
org.NewDomainPrimarySetEvent(context.Background(), &org.NewAggregate("test", "test").Aggregate, "domain"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, SetPrimaryOrgDomain(tt.args.a, tt.args.domain), nil, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
65
internal/command/v2/org_member.go
Normal file
65
internal/command/v2/org_member.go
Normal file
@ -0,0 +1,65 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func AddOrgMember(a *org.Aggregate, userID string, roles ...string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if userID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "ORG-4Mlfs", "Errors.Invalid.Argument")
|
||||
}
|
||||
// TODO: check roles
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
if exists, err := ExistsUser(ctx, filter, userID, a.ID); err != nil || !exists {
|
||||
return nil, errors.ThrowNotFound(err, "ORG-GoXOn", "Errors.User.NotFound")
|
||||
}
|
||||
if isMember, err := IsOrgMember(ctx, filter, a.ID, userID); err != nil || isMember {
|
||||
return nil, errors.ThrowAlreadyExists(err, "ORG-poWwe", "Errors.Org.Member.AlreadyExists")
|
||||
}
|
||||
return []eventstore.Command{org.NewMemberAddedEvent(ctx, &a.Aggregate, userID, roles...)}, nil
|
||||
},
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
func IsOrgMember(ctx context.Context, filter preparation.FilterToQueryReducer, orgID, userID string) (isMember bool, err error) {
|
||||
events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(orgID).
|
||||
OrderAsc().
|
||||
AddQuery().
|
||||
AggregateIDs(orgID).
|
||||
AggregateTypes(org.AggregateType).
|
||||
EventTypes(
|
||||
org.MemberAddedEventType,
|
||||
org.MemberRemovedEventType,
|
||||
org.MemberCascadeRemovedEventType,
|
||||
).Builder())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.MemberAddedEvent:
|
||||
if e.UserID == userID {
|
||||
isMember = true
|
||||
}
|
||||
case *org.MemberRemovedEvent:
|
||||
if e.UserID == userID {
|
||||
isMember = false
|
||||
}
|
||||
case *org.MemberCascadeRemovedEvent:
|
||||
if e.UserID == userID {
|
||||
isMember = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isMember, nil
|
||||
}
|
249
internal/command/v2/org_member_test.go
Normal file
249
internal/command/v2/org_member_test.go
Normal file
@ -0,0 +1,249 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
func TestAddMember(t *testing.T) {
|
||||
type args struct {
|
||||
a *org.Aggregate
|
||||
userID string
|
||||
roles []string
|
||||
filter preparation.FilterToQueryReducer
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
agg := org.NewAggregate("test", "test")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "no user id",
|
||||
args: args{
|
||||
a: agg,
|
||||
userID: "",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "ORG-4Mlfs", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
// {
|
||||
// name: "TODO: invalid roles",
|
||||
// args: args{
|
||||
// a: agg,
|
||||
// userID: "",
|
||||
// roles: []string{""},
|
||||
// },
|
||||
// want: preparation.Want{
|
||||
// ValidationErr: errors.ThrowInvalidArgument(nil, "ORG-4Mlfs", "Errors.Invalid.Argument"),
|
||||
// },
|
||||
// },
|
||||
{
|
||||
name: "user not exists",
|
||||
args: args{
|
||||
a: agg,
|
||||
userID: "userID",
|
||||
filter: NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return nil, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
CreateErr: errors.ThrowNotFound(nil, "ORG-GoXOn", "Errors.User.NotFound"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "already member",
|
||||
args: args{
|
||||
a: agg,
|
||||
userID: "userID",
|
||||
filter: NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
user.NewMachineAddedEvent(
|
||||
ctx,
|
||||
&user.NewAggregate("id", "ro").Aggregate,
|
||||
"userName",
|
||||
"name",
|
||||
"description",
|
||||
true,
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
org.NewMemberAddedEvent(
|
||||
ctx,
|
||||
&org.NewAggregate("id", "ro").Aggregate,
|
||||
"userID",
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
CreateErr: errors.ThrowAlreadyExists(nil, "ORG-poWwe", "Errors.Org.Member.AlreadyExists"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: agg,
|
||||
userID: "userID",
|
||||
filter: NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
user.NewMachineAddedEvent(
|
||||
ctx,
|
||||
&user.NewAggregate("id", "ro").Aggregate,
|
||||
"userName",
|
||||
"name",
|
||||
"description",
|
||||
true,
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return nil, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
org.NewMemberAddedEvent(ctx, &agg.Aggregate, "userID"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, AddOrgMember(tt.args.a, tt.args.userID, tt.args.roles...), tt.args.filter, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsMember(t *testing.T) {
|
||||
type args struct {
|
||||
filter preparation.FilterToQueryReducer
|
||||
orgID string
|
||||
userID string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantExists bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no events",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{}, nil
|
||||
},
|
||||
orgID: "orgID",
|
||||
userID: "userID",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "member added",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
org.NewMemberAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("orgID", "ro").Aggregate,
|
||||
"userID",
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
orgID: "orgID",
|
||||
userID: "userID",
|
||||
},
|
||||
wantExists: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "member removed",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
org.NewMemberAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("orgID", "ro").Aggregate,
|
||||
"userID",
|
||||
),
|
||||
org.NewMemberRemovedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("orgID", "ro").Aggregate,
|
||||
"userID",
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
orgID: "orgID",
|
||||
userID: "userID",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "member cascade removed",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
org.NewMemberAddedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("orgID", "ro").Aggregate,
|
||||
"userID",
|
||||
),
|
||||
org.NewMemberCascadeRemovedEvent(
|
||||
context.Background(),
|
||||
&org.NewAggregate("orgID", "ro").Aggregate,
|
||||
"userID",
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
orgID: "orgID",
|
||||
userID: "userID",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "error durring filter",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return nil, errors.ThrowInternal(nil, "PROJE-Op26p", "Errors.Internal")
|
||||
},
|
||||
orgID: "orgID",
|
||||
userID: "userID",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotExists, err := IsOrgMember(context.Background(), tt.args.filter, tt.args.orgID, tt.args.userID)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ExistsUser() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotExists != tt.wantExists {
|
||||
t.Errorf("ExistsUser() = %v, want %v", gotExists, tt.wantExists)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
57
internal/command/v2/org_test.go
Normal file
57
internal/command/v2/org_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func TestAddOrg(t *testing.T) {
|
||||
type args struct {
|
||||
a *org.Aggregate
|
||||
name string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
agg := org.NewAggregate("test", "test")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "invalid domain",
|
||||
args: args{
|
||||
a: agg,
|
||||
name: "",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "ORG-mruNY", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: agg,
|
||||
name: "caos ag",
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
org.NewOrgAddedEvent(ctx, &agg.Aggregate, "caos ag"),
|
||||
org.NewDomainAddedEvent(ctx, &agg.Aggregate, "caos-ag.localhost"),
|
||||
org.NewDomainVerifiedEvent(ctx, &agg.Aggregate, "caos-ag.localhost"),
|
||||
org.NewDomainPrimarySetEvent(ctx, &agg.Aggregate, "caos-ag.localhost"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, AddOrg(tt.args.a, tt.args.name, "localhost"), nil, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
80
internal/command/v2/preparation/command.go
Normal file
80
internal/command/v2/preparation/command.go
Normal file
@ -0,0 +1,80 @@
|
||||
package preparation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
// Validation of the input values of the command and if correct returns
|
||||
// the function to create commands or if not valid an error
|
||||
type Validation func() (CreateCommands, error)
|
||||
|
||||
// CreateCommands builds the commands
|
||||
// the filter param is an extended version of the eventstore filter method
|
||||
// it filters for events including the commands on the current context
|
||||
type CreateCommands func(context.Context, FilterToQueryReducer) ([]eventstore.Command, error)
|
||||
|
||||
// FilterToQueryReducer is an abstraction of the eventstore method
|
||||
type FilterToQueryReducer func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error)
|
||||
|
||||
var (
|
||||
//ErrNotExecutable is thrown if no command creator was created
|
||||
ErrNotExecutable = errors.ThrowInvalidArgument(nil, "PREPA-pH70n", "Errors.Internal")
|
||||
)
|
||||
|
||||
// PrepareCommands checks the passed validations and if ok creates the commands
|
||||
func PrepareCommands(ctx context.Context, filter FilterToQueryReducer, validations ...Validation) (cmds []eventstore.Command, err error) {
|
||||
commanders, err := validate(validations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return create(ctx, filter, commanders)
|
||||
}
|
||||
|
||||
func validate(validations []Validation) ([]CreateCommands, error) {
|
||||
creators := make([]CreateCommands, 0, len(validations))
|
||||
|
||||
for _, validate := range validations {
|
||||
cmds, err := validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
creators = append(creators, cmds)
|
||||
}
|
||||
|
||||
if len(creators) == 0 {
|
||||
return nil, ErrNotExecutable
|
||||
}
|
||||
return creators, nil
|
||||
}
|
||||
|
||||
func create(ctx context.Context, filter FilterToQueryReducer, commanders []CreateCommands) (cmds []eventstore.Command, err error) {
|
||||
for _, command := range commanders {
|
||||
cmd, err := command(ctx, transactionFilter(filter, cmds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmds = append(cmds, cmd...)
|
||||
}
|
||||
|
||||
return cmds, nil
|
||||
}
|
||||
|
||||
func transactionFilter(filter FilterToQueryReducer, commands []eventstore.Command) FilterToQueryReducer {
|
||||
return func(ctx context.Context, query *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
events, err := filter(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, command := range commands {
|
||||
event := command.(eventstore.Event)
|
||||
if !query.Matches(event, len(events)) {
|
||||
continue
|
||||
}
|
||||
events = append(events, event)
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
}
|
176
internal/command/v2/preparation/command_test.go
Normal file
176
internal/command/v2/preparation/command_test.go
Normal file
@ -0,0 +1,176 @@
|
||||
package preparation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
var errTest = errors.New("test")
|
||||
|
||||
func Test_validate(t *testing.T) {
|
||||
type args struct {
|
||||
validations []Validation
|
||||
}
|
||||
type want struct {
|
||||
len int
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "no validations",
|
||||
args: args{},
|
||||
want: want{
|
||||
err: ErrNotExecutable,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error in validation",
|
||||
args: args{
|
||||
validations: []Validation{
|
||||
func() (CreateCommands, error) {
|
||||
return nil, errTest
|
||||
},
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
err: errTest,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
validations: []Validation{
|
||||
func() (CreateCommands, error) {
|
||||
return func(_ context.Context, _ FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return nil, nil
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
len: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := validate(tt.args.validations)
|
||||
if !errors.Is(err, tt.want.err) {
|
||||
t.Errorf("validate() error = %v, wantErr %v", err, tt.want.err)
|
||||
return
|
||||
}
|
||||
if len(got) != tt.want.len {
|
||||
t.Errorf("validate() len = %v, want %v", len(got), tt.want.len)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_create(t *testing.T) {
|
||||
type args struct {
|
||||
commanders []CreateCommands
|
||||
}
|
||||
type want struct {
|
||||
err error
|
||||
len int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "error in command",
|
||||
want: want{
|
||||
err: errTest,
|
||||
},
|
||||
args: args{
|
||||
commanders: []CreateCommands{
|
||||
func(_ context.Context, _ FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return nil, errTest
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no commands",
|
||||
want: want{},
|
||||
args: args{
|
||||
commanders: []CreateCommands{
|
||||
func(_ context.Context, _ FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple commands",
|
||||
want: want{
|
||||
len: 3,
|
||||
},
|
||||
args: args{
|
||||
commanders: []CreateCommands{
|
||||
func(_ context.Context, _ FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{new(testCommand), new(testCommand)}, nil
|
||||
},
|
||||
func(_ context.Context, _ FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{new(testCommand)}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotCmds, err := create(context.Background(), nil, tt.args.commanders)
|
||||
if !errors.Is(err, tt.want.err) {
|
||||
t.Errorf("create() error = %v, wantErr %v", err, tt.want.err)
|
||||
return
|
||||
}
|
||||
if len(gotCmds) != tt.want.len {
|
||||
t.Errorf("create() len = %d, want %d", len(gotCmds), tt.want.len)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_transactionFilter(t *testing.T) {
|
||||
type args struct {
|
||||
filter FilterToQueryReducer
|
||||
commands []eventstore.Command
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want FilterToQueryReducer
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := transactionFilter(tt.args.filter, tt.args.commands); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("transactionFilter() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testCommand struct {
|
||||
eventstore.BaseEvent
|
||||
}
|
||||
|
||||
func (c *testCommand) Data() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *testCommand) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
81
internal/command/v2/preparation_test.go
Normal file
81
internal/command/v2/preparation_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
// this is a helper file for tests
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
//Want represents the expected values for each step
|
||||
type Want struct {
|
||||
ValidationErr error
|
||||
CreateErr error
|
||||
Commands []eventstore.Command
|
||||
}
|
||||
|
||||
//AssertValidation checks if the validation works as inteded
|
||||
func AssertValidation(t *testing.T, validation preparation.Validation, filter preparation.FilterToQueryReducer, want Want) {
|
||||
t.Helper()
|
||||
|
||||
creates, err := validation()
|
||||
if !errors.Is(err, want.ValidationErr) {
|
||||
t.Errorf("wrong validation err = %v, want %v", err, want.ValidationErr)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cmds, err := creates(context.Background(), filter)
|
||||
if !errors.Is(err, want.CreateErr) {
|
||||
t.Errorf("wrong create err = %v, want %v", err, want.CreateErr)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(cmds) != len(want.Commands) {
|
||||
t.Errorf("wrong length of commands = %v, want %v", eventTypes(cmds), eventTypes(want.Commands))
|
||||
return
|
||||
}
|
||||
|
||||
for i, cmd := range want.Commands {
|
||||
if !reflect.DeepEqual(cmd, cmds[i]) {
|
||||
t.Errorf("unexpected command: = %v, want %v", cmds[i], cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func eventTypes(cmds []eventstore.Command) []eventstore.EventType {
|
||||
types := make([]eventstore.EventType, len(cmds))
|
||||
for i, cmd := range cmds {
|
||||
types[i] = cmd.Type()
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
type MultiFilter struct {
|
||||
count int
|
||||
filters []preparation.FilterToQueryReducer
|
||||
}
|
||||
|
||||
func NewMultiFilter() *MultiFilter {
|
||||
return new(MultiFilter)
|
||||
}
|
||||
|
||||
func (mf *MultiFilter) Append(filter preparation.FilterToQueryReducer) *MultiFilter {
|
||||
mf.filters = append(mf.filters, filter)
|
||||
return mf
|
||||
}
|
||||
|
||||
func (mf *MultiFilter) Filter() preparation.FilterToQueryReducer {
|
||||
return func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
mf.count++
|
||||
return mf.filters[mf.count-1](ctx, queryFactory)
|
||||
}
|
||||
}
|
75
internal/command/v2/project.go
Normal file
75
internal/command/v2/project.go
Normal file
@ -0,0 +1,75 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
)
|
||||
|
||||
func AddProject(
|
||||
a *project.Aggregate,
|
||||
name string,
|
||||
owner string,
|
||||
projectRoleAssertion bool,
|
||||
projectRoleCheck bool,
|
||||
hasProjectCheck bool,
|
||||
privateLabelingSetting domain.PrivateLabelingSetting,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if name = strings.TrimSpace(name); name == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJE-C01yo", "Errors.Invalid.Argument")
|
||||
}
|
||||
if !privateLabelingSetting.Valid() {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJE-AO52V", "Errors.Invalid.Argument")
|
||||
}
|
||||
if owner == "" {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "PROJE-hzxwo", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{
|
||||
project.NewProjectAddedEvent(ctx, &a.Aggregate,
|
||||
name,
|
||||
projectRoleAssertion,
|
||||
projectRoleCheck,
|
||||
hasProjectCheck,
|
||||
privateLabelingSetting,
|
||||
),
|
||||
project.NewProjectMemberAddedEvent(ctx, &a.Aggregate,
|
||||
owner,
|
||||
domain.RoleProjectOwner),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func ExistsProject(ctx context.Context, filter preparation.FilterToQueryReducer, projectID, resourceOwner string) (exists bool, err error) {
|
||||
events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(resourceOwner).
|
||||
OrderAsc().
|
||||
AddQuery().
|
||||
AggregateTypes(project.AggregateType).
|
||||
AggregateIDs(projectID).
|
||||
EventTypes(
|
||||
project.ProjectAddedType,
|
||||
project.ProjectRemovedType,
|
||||
).Builder())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
switch event.(type) {
|
||||
case *project.ProjectAddedEvent:
|
||||
exists = true
|
||||
case *project.ProjectRemovedEvent:
|
||||
exists = false
|
||||
}
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
}
|
155
internal/command/v2/project_app.go
Normal file
155
internal/command/v2/project_app.go
Normal file
@ -0,0 +1,155 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
)
|
||||
|
||||
func AddOIDCApp(
|
||||
a project.Aggregate,
|
||||
version domain.OIDCVersion,
|
||||
appID,
|
||||
name,
|
||||
clientID string,
|
||||
clientSecret *crypto.CryptoValue,
|
||||
redirectUris []string,
|
||||
responseTypes []domain.OIDCResponseType,
|
||||
grantTypes []domain.OIDCGrantType,
|
||||
applicationType domain.OIDCApplicationType,
|
||||
authMethodType domain.OIDCAuthMethodType,
|
||||
postLogoutRedirectUris []string,
|
||||
devMode bool,
|
||||
accessTokenType domain.OIDCTokenType,
|
||||
accessTokenRoleAssertion bool,
|
||||
idTokenRoleAssertion bool,
|
||||
idTokenUserinfoAssertion bool,
|
||||
clockSkew time.Duration,
|
||||
additionalOrigins []string,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if appID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJE-NnavI", "Errors.Invalid.Argument")
|
||||
}
|
||||
if name = strings.TrimSpace(name); name == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJE-Fef31", "Errors.Invalid.Argument")
|
||||
}
|
||||
if clientID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJE-ghTsJ", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
if exists, err := ExistsProject(ctx, filter, a.ID, a.ResourceOwner); !exists || err != nil {
|
||||
return nil, errors.ThrowNotFound(err, "PROJE-5LQ0U", "Errors.Project.NotFound")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
project.NewApplicationAddedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
appID,
|
||||
name,
|
||||
),
|
||||
project.NewOIDCConfigAddedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
version,
|
||||
appID,
|
||||
clientID,
|
||||
clientSecret,
|
||||
redirectUris,
|
||||
responseTypes,
|
||||
grantTypes,
|
||||
applicationType,
|
||||
authMethodType,
|
||||
postLogoutRedirectUris,
|
||||
devMode,
|
||||
accessTokenType,
|
||||
accessTokenRoleAssertion,
|
||||
idTokenRoleAssertion,
|
||||
idTokenUserinfoAssertion,
|
||||
clockSkew,
|
||||
additionalOrigins,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func AddAPIApp(
|
||||
a project.Aggregate,
|
||||
appID,
|
||||
name,
|
||||
clientID string,
|
||||
clientSecret *crypto.CryptoValue,
|
||||
authMethodType domain.APIAuthMethodType,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if appID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJE-XHsKt", "Errors.Invalid.Argument")
|
||||
}
|
||||
if name = strings.TrimSpace(name); name == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJE-F7g21", "Errors.Invalid.Argument")
|
||||
}
|
||||
if clientID == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "PROJE-XXED5", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
if exists, err := ExistsProject(ctx, filter, a.ID, a.ResourceOwner); !exists || err != nil {
|
||||
return nil, errors.ThrowNotFound(err, "PROJE-Sf2gb", "Errors.Project.NotFound")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
project.NewApplicationAddedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
appID,
|
||||
name,
|
||||
),
|
||||
project.NewAPIConfigAddedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
appID,
|
||||
clientID,
|
||||
clientSecret,
|
||||
authMethodType,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func ExistsApp(ctx context.Context, filter preparation.FilterToQueryReducer, projectID, appID, resourceOwner string) (exists bool, err error) {
|
||||
events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(resourceOwner).
|
||||
OrderAsc().
|
||||
AddQuery().
|
||||
AggregateTypes(project.AggregateType).
|
||||
AggregateIDs(projectID).
|
||||
EventTypes(
|
||||
project.ApplicationAddedType,
|
||||
project.ApplicationRemovedType,
|
||||
).Builder())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *project.ApplicationAddedEvent:
|
||||
if e.AppID == appID {
|
||||
exists = true
|
||||
}
|
||||
case *project.ApplicationRemovedEvent:
|
||||
if e.AppID == appID {
|
||||
exists = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
}
|
386
internal/command/v2/project_app_test.go
Normal file
386
internal/command/v2/project_app_test.go
Normal file
@ -0,0 +1,386 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
)
|
||||
|
||||
func TestAddOIDCApp(t *testing.T) {
|
||||
type args struct {
|
||||
a *project.Aggregate
|
||||
appID string
|
||||
name string
|
||||
clientID string
|
||||
filter preparation.FilterToQueryReducer
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
agg := project.NewAggregate("test", "test")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "invalid appID",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "",
|
||||
name: "name",
|
||||
clientID: "clientID",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "PROJE-NnavI", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid name",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "appID",
|
||||
name: "",
|
||||
clientID: "clientID",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "PROJE-Fef31", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid clientID",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "appID",
|
||||
name: "name",
|
||||
clientID: "",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "PROJE-ghTsJ", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project not exists",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "id",
|
||||
name: "name",
|
||||
clientID: "clientID",
|
||||
filter: NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return nil, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
CreateErr: errors.ThrowNotFound(nil, "PROJE-5LQ0U", "Errors.Project.NotFound"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "appID",
|
||||
name: "name",
|
||||
clientID: "clientID",
|
||||
filter: NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
project.NewProjectAddedEvent(
|
||||
ctx,
|
||||
&agg.Aggregate,
|
||||
"project",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PrivateLabelingSettingUnspecified,
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
project.NewApplicationAddedEvent(ctx, &agg.Aggregate,
|
||||
"appID",
|
||||
"name",
|
||||
),
|
||||
project.NewOIDCConfigAddedEvent(ctx, &agg.Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"appID",
|
||||
"clientID",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypeBasic,
|
||||
nil,
|
||||
false,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
nil,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t,
|
||||
AddOIDCApp(*tt.args.a,
|
||||
domain.OIDCVersionV1,
|
||||
tt.args.appID,
|
||||
tt.args.name,
|
||||
tt.args.clientID,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypeBasic,
|
||||
nil,
|
||||
false,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
nil,
|
||||
), tt.args.filter, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAPIConfig(t *testing.T) {
|
||||
type args struct {
|
||||
a *project.Aggregate
|
||||
appID string
|
||||
name string
|
||||
clientID string
|
||||
filter preparation.FilterToQueryReducer
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
agg := project.NewAggregate("test", "test")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "invalid appID",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "",
|
||||
name: "name",
|
||||
clientID: "clientID",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "PROJE-XHsKt", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid name",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "appID",
|
||||
name: "",
|
||||
clientID: "clientID",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "PROJE-F7g21", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid clientID",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "appID",
|
||||
name: "name",
|
||||
clientID: "",
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "PROJE-XXED5", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project not exists",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "id",
|
||||
name: "name",
|
||||
clientID: "clientID",
|
||||
filter: NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return nil, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
CreateErr: errors.ThrowNotFound(nil, "PROJE-Sf2gb", "Errors.Project.NotFound"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: agg,
|
||||
appID: "appID",
|
||||
name: "name",
|
||||
clientID: "clientID",
|
||||
filter: NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
project.NewProjectAddedEvent(
|
||||
ctx,
|
||||
&agg.Aggregate,
|
||||
"project",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PrivateLabelingSettingUnspecified,
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
project.NewApplicationAddedEvent(
|
||||
ctx,
|
||||
&agg.Aggregate,
|
||||
"appID",
|
||||
"name",
|
||||
),
|
||||
project.NewAPIConfigAddedEvent(ctx, &agg.Aggregate,
|
||||
"appID",
|
||||
"clientID",
|
||||
nil,
|
||||
domain.APIAuthMethodTypeBasic,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t,
|
||||
AddAPIApp(*tt.args.a,
|
||||
tt.args.appID,
|
||||
tt.args.name,
|
||||
tt.args.clientID,
|
||||
nil,
|
||||
domain.APIAuthMethodTypeBasic,
|
||||
), tt.args.filter, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExistsApp(t *testing.T) {
|
||||
type args struct {
|
||||
filter preparation.FilterToQueryReducer
|
||||
appID string
|
||||
projectID string
|
||||
resourceOwner string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantExists bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no events",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{}, nil
|
||||
},
|
||||
appID: "appID",
|
||||
projectID: "projectID",
|
||||
resourceOwner: "ro",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "app added",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
project.NewApplicationAddedEvent(
|
||||
context.Background(),
|
||||
&project.NewAggregate("id", "ro").Aggregate,
|
||||
"appID",
|
||||
"name",
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
appID: "appID",
|
||||
projectID: "projectID",
|
||||
resourceOwner: "ro",
|
||||
},
|
||||
wantExists: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "app removed",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
project.NewApplicationAddedEvent(
|
||||
context.Background(),
|
||||
&project.NewAggregate("id", "ro").Aggregate,
|
||||
"appID",
|
||||
"name",
|
||||
),
|
||||
project.NewApplicationRemovedEvent(
|
||||
context.Background(),
|
||||
&project.NewAggregate("id", "ro").Aggregate,
|
||||
"appID",
|
||||
"name",
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
appID: "appID",
|
||||
projectID: "projectID",
|
||||
resourceOwner: "ro",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "error durring filter",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return nil, errors.ThrowInternal(nil, "PROJE-Op26p", "Errors.Internal")
|
||||
},
|
||||
appID: "appID",
|
||||
projectID: "projectID",
|
||||
resourceOwner: "ro",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotExists, err := ExistsApp(context.Background(), tt.args.filter, tt.args.projectID, tt.args.appID, tt.args.resourceOwner)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ExistsUser() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotExists != tt.wantExists {
|
||||
t.Errorf("ExistsUser() = %v, want %v", gotExists, tt.wantExists)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
195
internal/command/v2/project_test.go
Normal file
195
internal/command/v2/project_test.go
Normal file
@ -0,0 +1,195 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/project"
|
||||
)
|
||||
|
||||
func TestAddProject(t *testing.T) {
|
||||
type args struct {
|
||||
a *project.Aggregate
|
||||
name string
|
||||
owner string
|
||||
privateLabelingSetting domain.PrivateLabelingSetting
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
agg := project.NewAggregate("test", "test")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "invalid name",
|
||||
args: args{
|
||||
a: agg,
|
||||
name: "",
|
||||
owner: "owner",
|
||||
privateLabelingSetting: domain.PrivateLabelingSettingAllowLoginUserResourceOwnerPolicy,
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "PROJE-C01yo", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid private labeling setting",
|
||||
args: args{
|
||||
a: agg,
|
||||
name: "name",
|
||||
owner: "owner",
|
||||
privateLabelingSetting: -1,
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "PROJE-AO52V", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid owner",
|
||||
args: args{
|
||||
a: agg,
|
||||
name: "name",
|
||||
owner: "",
|
||||
privateLabelingSetting: domain.PrivateLabelingSettingAllowLoginUserResourceOwnerPolicy,
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowPreconditionFailed(nil, "PROJE-hzxwo", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: agg,
|
||||
name: "ZITADEL",
|
||||
owner: "CAOS AG",
|
||||
privateLabelingSetting: domain.PrivateLabelingSettingAllowLoginUserResourceOwnerPolicy,
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
project.NewProjectAddedEvent(ctx, &agg.Aggregate,
|
||||
"ZITADEL",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PrivateLabelingSettingAllowLoginUserResourceOwnerPolicy,
|
||||
),
|
||||
project.NewProjectMemberAddedEvent(ctx, &agg.Aggregate,
|
||||
"CAOS AG",
|
||||
domain.RoleProjectOwner),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, AddProject(tt.args.a, tt.args.name, tt.args.owner, false, false, false, tt.args.privateLabelingSetting), nil, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExistsProject(t *testing.T) {
|
||||
type args struct {
|
||||
filter preparation.FilterToQueryReducer
|
||||
id string
|
||||
resourceOwner string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantExists bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no events",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{}, nil
|
||||
},
|
||||
id: "id",
|
||||
resourceOwner: "ro",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "project added",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
project.NewProjectAddedEvent(
|
||||
context.Background(),
|
||||
&project.NewAggregate("id", "ro").Aggregate,
|
||||
"name",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PrivateLabelingSettingEnforceProjectResourceOwnerPolicy,
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
id: "id",
|
||||
resourceOwner: "ro",
|
||||
},
|
||||
wantExists: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "project removed",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
project.NewProjectAddedEvent(
|
||||
context.Background(),
|
||||
&project.NewAggregate("id", "ro").Aggregate,
|
||||
"name",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PrivateLabelingSettingEnforceProjectResourceOwnerPolicy,
|
||||
),
|
||||
project.NewProjectRemovedEvent(
|
||||
context.Background(),
|
||||
&project.NewAggregate("id", "ro").Aggregate,
|
||||
"name",
|
||||
),
|
||||
}, nil
|
||||
},
|
||||
id: "id",
|
||||
resourceOwner: "ro",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "error durring filter",
|
||||
args: args{
|
||||
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return nil, errors.ThrowInternal(nil, "PROJE-Op26p", "Errors.Internal")
|
||||
},
|
||||
id: "id",
|
||||
resourceOwner: "ro",
|
||||
},
|
||||
wantExists: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotExists, err := ExistsProject(context.Background(), tt.args.filter, tt.args.id, tt.args.resourceOwner)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ExistsUser() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotExists != tt.wantExists {
|
||||
t.Errorf("ExistsUser() = %v, want %v", gotExists, tt.wantExists)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user