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:
Silvan 2022-03-28 10:05:09 +02:00 committed by GitHub
parent 9d4f296c62
commit c5b99274d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
175 changed files with 5213 additions and 2212 deletions

View File

@ -26,6 +26,7 @@ func New() *cobra.Command {
initialise.New(),
setup.New(),
start.New(),
start.NewStartFromInit(),
key.New(),
)

View File

@ -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

View File

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

View File

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

View File

@ -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")
},
}
}

View File

@ -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")
},
}
}

View File

@ -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")
},
}
}

View File

@ -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

View File

@ -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 (

View File

@ -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 (

View File

@ -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 (

View File

@ -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
View 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"
}

View File

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

View File

@ -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
View 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
}

View File

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

View File

@ -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

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

View File

@ -35,15 +35,14 @@ AdminUser:
Key: ""
Projections:
Config:
RequeueEvery: 10s
RetryFailedAfter: 1s
MaxFailureCount: 5
BulkLimit: 200
MaxIterators: 1
Customizations:
projects:
BulkLimit: 2000
RequeueEvery: 10s
RetryFailedAfter: 1s
MaxFailureCount: 5
BulkLimit: 200
MaxIterators: 1
Customizations:
projects:
BulkLimit: 2000
AuthZ:
Repository:

View File

@ -4865,7 +4865,7 @@ This is an empty request
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| policy | zitadel.policy.v1.DomainPolicy | - | |
| policy | zitadel.policy.v1.OrgIAMPolicy | - | |

View File

@ -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

View File

@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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().

View File

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

View File

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

View File

@ -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),

View File

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

View File

@ -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(

View File

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

View File

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

View File

@ -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),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,

View File

@ -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,

View File

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

View File

@ -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,
),

View File

@ -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,
),

View File

@ -317,8 +317,7 @@ type HumanU2FLoginReadModel struct {
Challenge string
AllowedCredentialIDs [][]byte
UserVerification domain.UserVerificationRequirement
User
State domain.UserState
State domain.UserState
}
func NewHumanU2FLoginReadModel(userID, authReqID, resourceOwner string) *HumanU2FLoginReadModel {

View File

@ -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

View File

@ -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,
),

View File

@ -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")

View File

@ -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,
),

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View 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