mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-10 09:23:40 +00:00
test(e2e): migrate e2e setup
This commit is contained in:
parent
9fbfe97d2c
commit
2b7600e6e8
118
cmd/e2e-setup/config.go
Normal file
118
cmd/e2e-setup/config.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/id"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/database"
|
||||||
|
|
||||||
|
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
|
||||||
|
|
||||||
|
static_config "github.com/zitadel/zitadel/internal/static/config"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
"github.com/zitadel/zitadel/internal/config/hook"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
E2E *E2EConfig
|
||||||
|
Log *logging.Config
|
||||||
|
ExternalPort uint16
|
||||||
|
ExternalDomain string
|
||||||
|
ExternalSecure bool
|
||||||
|
Database database.Config
|
||||||
|
AssetStorage static_config.AssetStorageConfig
|
||||||
|
WebAuthNName string
|
||||||
|
InternalAuthZ internal_authz.Config
|
||||||
|
Machine *id.Config
|
||||||
|
SystemDefaults systemdefaults.SystemDefaults
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Validate() error {
|
||||||
|
if c.E2E == nil {
|
||||||
|
return errors.New("no e2e config found")
|
||||||
|
}
|
||||||
|
return c.E2E.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
type E2EConfig struct {
|
||||||
|
Org string
|
||||||
|
MachineKeyPath string
|
||||||
|
ZitadelProjectResourceID string
|
||||||
|
APIURL string
|
||||||
|
IssuerURL string
|
||||||
|
Audience string
|
||||||
|
OrgOwnerPassword string
|
||||||
|
OrgOwnerViewerPassword string
|
||||||
|
OrgProjectCreatorPassword string
|
||||||
|
PasswordComplexityUserPassword string
|
||||||
|
LoginPolicyUserPassword string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e E2EConfig) Validate() (err error) {
|
||||||
|
if e.Org == "" {
|
||||||
|
return errors.New("field Org is empty")
|
||||||
|
}
|
||||||
|
if e.MachineKeyPath == "" {
|
||||||
|
return errors.New("field MachineKeyPath is empty")
|
||||||
|
}
|
||||||
|
if e.ZitadelProjectResourceID == "" {
|
||||||
|
return errors.New("field ZitadelProjectResourceID is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
audPattern := "number-[0-9]{17}"
|
||||||
|
matched, err := regexp.MatchString("number-[0-9]{17}", e.ZitadelProjectResourceID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("validating ZitadelProjectResourceID failed: %w", err)
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
return fmt.Errorf("ZitadelProjectResourceID doesn't match regular expression %s", audPattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.APIURL == "" {
|
||||||
|
return errors.New("field APIURL is empty")
|
||||||
|
}
|
||||||
|
if e.IssuerURL == "" {
|
||||||
|
return errors.New("field IssuerURL is empty")
|
||||||
|
}
|
||||||
|
if e.OrgOwnerPassword == "" {
|
||||||
|
return errors.New("field OrgOwnerPassword is empty")
|
||||||
|
}
|
||||||
|
if e.OrgOwnerViewerPassword == "" {
|
||||||
|
return errors.New("field OrgOwnerViewerPassword is empty")
|
||||||
|
}
|
||||||
|
if e.OrgProjectCreatorPassword == "" {
|
||||||
|
return errors.New("field OrgProjectCreatorPassword is empty")
|
||||||
|
}
|
||||||
|
if e.PasswordComplexityUserPassword == "" {
|
||||||
|
return errors.New("field PasswordComplexityUserPassword is empty")
|
||||||
|
}
|
||||||
|
if e.LoginPolicyUserPassword == "" {
|
||||||
|
return errors.New("field LoginPolicyUserPassword is empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustNewConfig(v *viper.Viper) *Config {
|
||||||
|
config := new(Config)
|
||||||
|
|
||||||
|
err := v.Unmarshal(config,
|
||||||
|
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
||||||
|
hook.Base64ToBytesHookFunc(),
|
||||||
|
hook.TagToLanguageHookFunc(),
|
||||||
|
mapstructure.StringToTimeDurationHookFunc(),
|
||||||
|
mapstructure.StringToSliceHookFunc(","),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
err = config.Log.SetLogger()
|
||||||
|
logging.OnError(err).Fatal("unable to set logger")
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
65
cmd/e2e-setup/consistency.go
Normal file
65
cmd/e2e-setup/consistency.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func awaitConsistency(ctx context.Context, cfg E2EConfig, expectUsers []userData) (err error) {
|
||||||
|
|
||||||
|
retry := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
// trigger first check
|
||||||
|
retry <- struct{}{}
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-retry:
|
||||||
|
err = checkCondition(ctx, cfg, expectUsers)
|
||||||
|
if err == nil {
|
||||||
|
logging.Log("AWAIT-QIOOJ").Info("setup is consistent")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
logging.Log("AWAIT-VRk3Y").Info("setup is not consistent yet, retrying in a second: ", err)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
go func() {
|
||||||
|
retry <- struct{}{}
|
||||||
|
}()
|
||||||
|
case <-ctx.Done():
|
||||||
|
return fmt.Errorf("setup failed to come to a consistent state: %s: %w", ctx.Err(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCondition(ctx context.Context, cfg E2EConfig, expectUsers []userData) error {
|
||||||
|
token, err := newToken(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
foundUsers, err := listUsers(ctx, cfg.APIURL, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var awaitingUsers []string
|
||||||
|
expectLoop:
|
||||||
|
for i := range expectUsers {
|
||||||
|
expectUser := expectUsers[i]
|
||||||
|
for j := range foundUsers {
|
||||||
|
foundUser := foundUsers[j]
|
||||||
|
if expectUser.desc+"_user_name" == foundUser {
|
||||||
|
continue expectLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
awaitingUsers = append(awaitingUsers, expectUser.desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(awaitingUsers) > 0 {
|
||||||
|
return fmt.Errorf("users %v are not consistent yet", awaitingUsers)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
110
cmd/e2e-setup/execute.go
Normal file
110
cmd/e2e-setup/execute.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func execute(ctx context.Context, cmd *command.Commands, cfg E2EConfig, users []userData) error {
|
||||||
|
|
||||||
|
orgOwner := newHuman(users[0])
|
||||||
|
|
||||||
|
orgOwnerID, org, err := cmd.SetUpOrg(ctx, &command.OrgSetup{
|
||||||
|
Name: cfg.Org,
|
||||||
|
CustomDomain: "localhost",
|
||||||
|
Human: *orgOwner,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoids the MFA nudge
|
||||||
|
if _, err = cmd.AddLoginPolicy(ctx, org.ResourceOwner, &domain.LoginPolicy{
|
||||||
|
AllowUsernamePassword: true,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoids the change password screen
|
||||||
|
if _, err = cmd.ChangePassword(ctx, org.ResourceOwner, orgOwnerID, cfg.OrgOwnerPassword, cfg.OrgOwnerPassword, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sa, err := cmd.AddMachine(ctx, org.ResourceOwner, &domain.Machine{
|
||||||
|
Username: "e2e",
|
||||||
|
Name: "e2e",
|
||||||
|
Description: "User who calls the ZITADEL API for preparing end-to-end tests",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = cmd.AddOrgMember(ctx, org.ResourceOwner, sa.AggregateID, domain.RoleOrgOwner); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := cmd.AddUserMachineKey(ctx, &domain.MachineKey{
|
||||||
|
ObjectRoot: models.ObjectRoot{
|
||||||
|
AggregateID: sa.AggregateID,
|
||||||
|
},
|
||||||
|
ExpirationDate: time.Now().Add(30 * 24 * time.Hour),
|
||||||
|
Type: domain.AuthNKeyTypeJSON,
|
||||||
|
}, org.ResourceOwner)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
json, err := key.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.MkdirAll(filepath.Dir(cfg.MachineKeyPath), 0700); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ioutil.WriteFile(cfg.MachineKeyPath, json, 0600); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx := range users[1:] {
|
||||||
|
user := users[idx]
|
||||||
|
|
||||||
|
createdHuman, err := cmd.AddHuman(ctx, org.ResourceOwner, newHuman(user))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoids the change password screen
|
||||||
|
if _, err = cmd.ChangePassword(ctx, org.ResourceOwner, createdHuman.ID, user.pw, user.pw, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.role != "" {
|
||||||
|
if _, err = cmd.AddOrgMember(ctx, org.ResourceOwner, createdHuman.ID, user.role); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHuman(u userData) *command.AddHuman {
|
||||||
|
return &command.AddHuman{
|
||||||
|
Username: u.desc + "_user_name",
|
||||||
|
FirstName: u.desc + "_first_name",
|
||||||
|
LastName: u.desc + "_last_name",
|
||||||
|
Password: u.pw,
|
||||||
|
Email: command.Email{
|
||||||
|
Address: u.desc + ".e2e@zitadel.com",
|
||||||
|
Verified: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
139
cmd/e2e-setup/main.go
Normal file
139
cmd/e2e-setup/main.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/id"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/config/options"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
|
"github.com/zitadel/zitadel/internal/database"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
|
"github.com/zitadel/zitadel/internal/webauthn"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
type userData struct {
|
||||||
|
desc, role, pw string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// masterkey := flag.String("materkey", "MasterkeyNeedsToHave32Characters", "the ZITADEL installations masterkey")
|
||||||
|
debug := flag.Bool("debug", false, "print information that is helpful for debugging")
|
||||||
|
|
||||||
|
err := options.InitViper()
|
||||||
|
logging.OnError(err).Fatalf("unable to initialize config: %s", err)
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
viper.
|
||||||
|
fmt.Println(x)
|
||||||
|
conf := MustNewConfig(viper.GetViper())
|
||||||
|
|
||||||
|
if *debug {
|
||||||
|
printConfig("config", conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.New().OnError(err).Fatal("validating e2e config failed")
|
||||||
|
|
||||||
|
startE2ESetup(conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func startE2ESetup(conf *Config /*, masterkey string*/) {
|
||||||
|
|
||||||
|
id.Configure(conf.Machine)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
dbClient, err := database.Connect(conf.Database)
|
||||||
|
logging.New().OnError(err).Fatalf("cannot start client for projection: %s", err)
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyStorage, err := cryptoDB.NewKeyStorage(dbClient, masterkey)
|
||||||
|
logging.New().OnError(err).Fatalf("cannot start key storage: %s", err)
|
||||||
|
|
||||||
|
keys, err := ensureEncryptionKeys(conf.EncryptionKeys, keyStorage)
|
||||||
|
logging.New().OnError(err).Fatalf("failed ensuring encryption keys: %s", err)
|
||||||
|
*/
|
||||||
|
|
||||||
|
eventstoreClient, err := eventstore.Start(dbClient)
|
||||||
|
logging.New().OnError(err).Fatalf("cannot start eventstore for queries: %s", err)
|
||||||
|
|
||||||
|
storage, err := conf.AssetStorage.NewStorage(dbClient)
|
||||||
|
logging.New().OnError(err).Fatalf("cannot start asset storage client: %s", err)
|
||||||
|
|
||||||
|
webAuthNConfig := &webauthn.Config{
|
||||||
|
DisplayName: conf.WebAuthNName,
|
||||||
|
ExternalSecure: conf.ExternalSecure,
|
||||||
|
}
|
||||||
|
|
||||||
|
commands, err := command.StartCommands(
|
||||||
|
eventstoreClient,
|
||||||
|
conf.SystemDefaults,
|
||||||
|
conf.InternalAuthZ.RolePermissionMappings,
|
||||||
|
storage,
|
||||||
|
webAuthNConfig,
|
||||||
|
conf.ExternalDomain,
|
||||||
|
conf.ExternalSecure,
|
||||||
|
conf.ExternalPort,
|
||||||
|
nil, //keys.IDPConfig,
|
||||||
|
nil, //keys.OTP,
|
||||||
|
nil, //keys.SMTP,
|
||||||
|
nil, //keys.SMS,
|
||||||
|
nil, //keys.User,
|
||||||
|
nil, //keys.DomainVerification,
|
||||||
|
nil, //keys.OIDC,
|
||||||
|
)
|
||||||
|
logging.New().OnError(err).Errorf("cannot start commands: %s", err)
|
||||||
|
|
||||||
|
users := []userData{{
|
||||||
|
desc: "org_owner",
|
||||||
|
pw: conf.E2E.OrgOwnerPassword,
|
||||||
|
role: domain.RoleOrgOwner,
|
||||||
|
}, {
|
||||||
|
desc: "org_owner_viewer",
|
||||||
|
pw: conf.E2E.OrgOwnerViewerPassword,
|
||||||
|
role: domain.RoleOrgOwner,
|
||||||
|
}, {
|
||||||
|
desc: "org_project_creator",
|
||||||
|
pw: conf.E2E.OrgProjectCreatorPassword,
|
||||||
|
role: domain.RoleOrgProjectCreator,
|
||||||
|
}, {
|
||||||
|
desc: "login_policy_user",
|
||||||
|
pw: conf.E2E.LoginPolicyUserPassword,
|
||||||
|
}, {
|
||||||
|
desc: "password_complexity_user",
|
||||||
|
pw: conf.E2E.PasswordComplexityUserPassword,
|
||||||
|
}}
|
||||||
|
|
||||||
|
err = execute(ctx, commands, *conf.E2E, users)
|
||||||
|
logging.New().OnError(err).Errorf("failed to execute commands steps")
|
||||||
|
|
||||||
|
eventualConsistencyCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
err = awaitConsistency(
|
||||||
|
eventualConsistencyCtx,
|
||||||
|
*conf.E2E,
|
||||||
|
users,
|
||||||
|
)
|
||||||
|
logging.New().OnError(err).Fatal("failed to await consistency")
|
||||||
|
}
|
||||||
|
|
||||||
|
func printConfig(desc string, cfg interface{}) {
|
||||||
|
bytes, err := yaml.Marshal(cfg)
|
||||||
|
logging.New().OnError(err).Fatal("cannot marshal config")
|
||||||
|
|
||||||
|
logging.New().Info("got the following ", desc, " config")
|
||||||
|
fmt.Println(string(bytes))
|
||||||
|
}
|
85
cmd/e2e-setup/token.go
Normal file
85
cmd/e2e-setup/token.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newToken(cfg E2EConfig) (string, error) {
|
||||||
|
|
||||||
|
keyBytes, err := os.ReadFile(cfg.MachineKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
key := struct{ UserId, KeyId, Key string }{}
|
||||||
|
if err := json.Unmarshal(keyBytes, &key); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if key.KeyId == "" ||
|
||||||
|
key.UserId == "" ||
|
||||||
|
key.Key == "" {
|
||||||
|
return "", fmt.Errorf("key is incomplete: %+v", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
iat := now.Unix()
|
||||||
|
exp := now.Add(55 * time.Minute).Unix()
|
||||||
|
|
||||||
|
audience := cfg.Audience
|
||||||
|
if audience == "" {
|
||||||
|
audience = cfg.IssuerURL
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
|
||||||
|
"iss": key.UserId,
|
||||||
|
"sub": key.UserId,
|
||||||
|
"aud": audience,
|
||||||
|
"iat": iat,
|
||||||
|
"exp": exp,
|
||||||
|
})
|
||||||
|
|
||||||
|
token.Header["alg"] = "RS256"
|
||||||
|
token.Header["kid"] = key.KeyId
|
||||||
|
|
||||||
|
rsaKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(key.Key))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenString, err := token.SignedString(rsaKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.PostForm(fmt.Sprintf("%s/oauth/v2/token", cfg.APIURL), map[string][]string{
|
||||||
|
"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"},
|
||||||
|
"scope": {fmt.Sprintf("openid urn:zitadel:iam:org:project:id:%s:aud", strings.TrimPrefix(cfg.ZitadelProjectResourceID, "bignumber-"))},
|
||||||
|
"assertion": {tokenString},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode > 399 {
|
||||||
|
return "", fmt.Errorf("getting token returned status code %d and body %s", resp.StatusCode, string(tokenBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenResp := struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
return tokenResp.AccessToken, json.Unmarshal(tokenBody, &tokenResp)
|
||||||
|
}
|
52
cmd/e2e-setup/users.go
Normal file
52
cmd/e2e-setup/users.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func listUsers(ctx context.Context, apiUrl, token string) ([]string, error) {
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiUrl+"/management/v1/users/_search", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", "Bearer "+token)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode > 399 {
|
||||||
|
return nil, fmt.Errorf("listing users returned status code %d and body %s", resp.StatusCode, string(bodyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalledResp := struct {
|
||||||
|
Result []struct {
|
||||||
|
UserName string `json:"userName"`
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
|
||||||
|
if err = json.Unmarshal(bodyBytes, &unmarshalledResp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users := make([]string, len(unmarshalledResp.Result))
|
||||||
|
for i := range unmarshalledResp.Result {
|
||||||
|
users[i] = unmarshalledResp.Result[i].UserName
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
package start
|
package start
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"github.com/zitadel/zitadel/internal/config/hook"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
admin_es "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing"
|
admin_es "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing"
|
||||||
@ -15,7 +16,6 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/api/ui/login"
|
"github.com/zitadel/zitadel/internal/api/ui/login"
|
||||||
auth_es "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing"
|
auth_es "github.com/zitadel/zitadel/internal/auth/repository/eventsourcing"
|
||||||
"github.com/zitadel/zitadel/internal/command"
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
"github.com/zitadel/zitadel/internal/config/hook"
|
|
||||||
"github.com/zitadel/zitadel/internal/config/network"
|
"github.com/zitadel/zitadel/internal/config/network"
|
||||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
@ -54,6 +54,18 @@ type Config struct {
|
|||||||
CustomerPortal string
|
CustomerPortal string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 MustNewConfig(v *viper.Viper) *Config {
|
func MustNewConfig(v *viper.Viper) *Config {
|
||||||
config := new(Config)
|
config := new(Config)
|
||||||
|
|
||||||
@ -73,15 +85,3 @@ func MustNewConfig(v *viper.Viper) *Config {
|
|||||||
|
|
||||||
return config
|
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
|
|
||||||
}
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
"github.com/zitadel/zitadel/internal/config/options"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/cmd/initialise"
|
"github.com/zitadel/zitadel/cmd/initialise"
|
||||||
"github.com/zitadel/zitadel/cmd/key"
|
"github.com/zitadel/zitadel/cmd/key"
|
||||||
@ -35,7 +36,7 @@ Requirements:
|
|||||||
setupSteps := setup.MustNewSteps(viper.New())
|
setupSteps := setup.MustNewSteps(viper.New())
|
||||||
setup.Setup(setupConfig, setupSteps, masterKey)
|
setup.Setup(setupConfig, setupSteps, masterKey)
|
||||||
|
|
||||||
startConfig := MustNewConfig(viper.GetViper())
|
startConfig := options.MustNewConfig(viper.GetViper())
|
||||||
|
|
||||||
err = startZitadel(startConfig, masterKey)
|
err = startZitadel(startConfig, masterKey)
|
||||||
logging.OnError(err).Fatal("unable to start zitadel")
|
logging.OnError(err).Fatal("unable to start zitadel")
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
|
||||||
|
"github.com/zitadel/zitadel/internal/config/options"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/cmd/admin"
|
"github.com/zitadel/zitadel/cmd/admin"
|
||||||
@ -20,9 +19,6 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
configFiles []string
|
configFiles []string
|
||||||
|
|
||||||
//go:embed defaults.yaml
|
|
||||||
defaultConfig []byte
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(out io.Writer, in io.Reader, args []string) *cobra.Command {
|
func New(out io.Writer, in io.Reader, args []string) *cobra.Command {
|
||||||
@ -35,12 +31,8 @@ func New(out io.Writer, in io.Reader, args []string) *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
viper.AutomaticEnv()
|
err := options.InitViper()
|
||||||
viper.SetEnvPrefix("ZITADEL")
|
logging.OnError(err).Fatalf("unable initialize config: %s", err)
|
||||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
||||||
viper.SetConfigType("yaml")
|
|
||||||
err := viper.ReadConfig(bytes.NewBuffer(defaultConfig))
|
|
||||||
logging.OnError(err).Fatal("unable to read default config")
|
|
||||||
|
|
||||||
cobra.OnInitialize(initConfig)
|
cobra.OnInitialize(initConfig)
|
||||||
cmd.PersistentFlags().StringArrayVar(&configFiles, "config", nil, "path to config file to overwrite system defaults")
|
cmd.PersistentFlags().StringArrayVar(&configFiles, "config", nil, "path to config file to overwrite system defaults")
|
||||||
@ -58,9 +50,5 @@ func New(out io.Writer, in io.Reader, args []string) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initConfig() {
|
func initConfig() {
|
||||||
for _, file := range configFiles {
|
options.MergeToViper(configFiles...)
|
||||||
viper.SetConfigFile(file)
|
|
||||||
err := viper.MergeInConfig()
|
|
||||||
logging.WithFields("file", file).OnError(err).Warn("unable to read config file")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
E2E_CYPRESS_PORT=5003
|
|
||||||
E2E_ORG=e2e-tests
|
|
||||||
E2E_ORG_OWNER_PW=Password1!
|
|
||||||
E2E_ORG_OWNER_VIEWER_PW=Password1!
|
|
||||||
E2E_ORG_PROJECT_CREATOR_PW=Password1!
|
|
||||||
E2E_PASSWORD_COMPLEXITY_USER_PW=Password1!
|
|
||||||
E2E_LOGIN_POLICY_USER_PW=Password1!
|
|
||||||
E2E_SERVICEACCOUNT_KEY_PATH="${projectRoot}/.keys/e2e.json"
|
|
||||||
E2E_CONSOLE_URL="http://localhost:4200"
|
|
||||||
E2E_API_URL="http://localhost:50002"
|
|
||||||
E2E_ACCOUNTS_URL="http://localhost:50003"
|
|
||||||
E2E_ISSUER_URL="http://localhost:50002/oauth/v2"
|
|
||||||
E2E_OTHER_ZITADEL_IDP_INSTANCE=false
|
|
||||||
E2E_ZITADEL_PROJECT_RESOURCE_ID="bignumber-$(echo -n $(docker compose -f ${projectRoot}/build/local/docker-compose-local.yml exec --no-TTY db cockroach sql --insecure --execute "select aggregate_id from eventstore.events where event_type = 'project.added' and event_data = '{\"name\": \"Zitadel\"}';" --format tsv) | cut -d " " -f 2)"
|
|
@ -16,7 +16,7 @@ This guide assumes you are already familiar with [running ZITADEL with the most
|
|||||||
## Configuration Files
|
## Configuration Files
|
||||||
|
|
||||||
### Runtime Configuration
|
### Runtime Configuration
|
||||||
See a description of all possible _runtime configuration_ options with their defaults [in the source code](https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/defaults.yaml).
|
See a description of all possible _runtime configuration_ options with their defaults [in the source code](https://github.com/zitadel/zitadel/blob/v2-alpha/internal/config/options/defaults.yaml).
|
||||||
The `zitadel` binary expects the `--config` flag for this configuration.
|
The `zitadel` binary expects the `--config` flag for this configuration.
|
||||||
|
|
||||||
### Database Initialization
|
### Database Initialization
|
||||||
@ -67,7 +67,7 @@ This is the IAM admin users login according to your configuration in the [exampl
|
|||||||
|
|
||||||
- Read more about [the login process](../../manuals/user-login).
|
- Read more about [the login process](../../manuals/user-login).
|
||||||
- If you want to run ZITADEL in production, you most certainly need to [customize your own domain](./custom-domain).
|
- If you want to run ZITADEL in production, you most certainly need to [customize your own domain](./custom-domain).
|
||||||
- Check out all possible [runtime configuration properties and their defaults in the source code](https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/defaults.yaml)
|
- Check out all possible [runtime configuration properties and their defaults in the source code](https://github.com/zitadel/zitadel/blob/v2-alpha/internal/config/options/defaults.yaml)
|
||||||
- Check out all possible [setup step configuration properties and their defaults in the source code](https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/setup/steps.yaml)
|
- Check out all possible [setup step configuration properties and their defaults in the source code](https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/setup/steps.yaml)
|
||||||
|
|
||||||
:::caution
|
:::caution
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/defaults.yaml
|
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/internal/config/options/defaults.yaml
|
||||||
Log:
|
Log:
|
||||||
Level: 'info'
|
Level: 'info'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/defaults.yaml
|
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/internal/config/options/defaults.yaml
|
||||||
|
|
||||||
# If not using the docker compose example, adjust these values for connecting ZITADEL to your CockroachDB
|
# If not using the docker compose example, adjust these values for connecting ZITADEL to your CockroachDB
|
||||||
Database:
|
Database:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/defaults.yaml
|
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/internal/config/options/defaults.yaml
|
||||||
zitadel:
|
zitadel:
|
||||||
|
|
||||||
masterkey: 'MasterkeyNeedsToHave32Characters'
|
masterkey: 'MasterkeyNeedsToHave32Characters'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/defaults.yaml
|
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/internal/config/options/defaults.yaml
|
||||||
zitadel:
|
zitadel:
|
||||||
configmapConfig:
|
configmapConfig:
|
||||||
Log:
|
Log:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/defaults.yaml
|
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/internal/config/options/defaults.yaml
|
||||||
Log:
|
Log:
|
||||||
Level: 'info'
|
Level: 'info'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/cmd/defaults.yaml
|
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/v2-alpha/internal/config/options/defaults.yaml
|
||||||
|
|
||||||
# If not using the docker compose example, adjust these values for connecting ZITADEL to your CockroachDB
|
# If not using the docker compose example, adjust these values for connecting ZITADEL to your CockroachDB
|
||||||
Database:
|
Database:
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
services:
|
services:
|
||||||
zitadel:
|
zitadel:
|
||||||
image: 'zitadel:${BUILD_DATE}'
|
image: 'zitadel:${BUILD_DATE}'
|
||||||
|
|
||||||
|
3
e2e/docker-compose.sh
Executable file
3
e2e/docker-compose.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
COMPOSE_DOCKER_CLI_BUILD=1 docker compose --file ./docs/docs/guides/installation/run/docker-compose.yaml --file ./e2e/docker-compose-overwrite.yaml "$@"
|
11
e2e/e2e-setup-debug.sh
Executable file
11
e2e/e2e-setup-debug.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
projectRoot="."
|
||||||
|
|
||||||
|
set -a
|
||||||
|
source ./e2e/local.env
|
||||||
|
set +a
|
||||||
|
|
||||||
|
dlv debug --api-version 2 --headless --listen 127.0.0.1:2345 ./cmd/e2e-setup/*.go "$@"
|
@ -4,9 +4,8 @@ set -e
|
|||||||
|
|
||||||
DO_BUILD=1
|
DO_BUILD=1
|
||||||
DO_DEPLOY=1
|
DO_DEPLOY=1
|
||||||
DO_TEST=1
|
|
||||||
|
|
||||||
while getopts 'bdt:' OPTION; do
|
while getopts 'bd:' OPTION; do
|
||||||
case "$OPTION" in
|
case "$OPTION" in
|
||||||
b)
|
b)
|
||||||
echo "skipping build"
|
echo "skipping build"
|
||||||
@ -16,15 +15,10 @@ while getopts 'bdt:' OPTION; do
|
|||||||
echo "skipping deployment"
|
echo "skipping deployment"
|
||||||
DO_DEPLOY=0
|
DO_DEPLOY=0
|
||||||
;;
|
;;
|
||||||
s)
|
|
||||||
echo "skipping tests"
|
|
||||||
DO_TEST=0
|
|
||||||
;;
|
|
||||||
?)
|
?)
|
||||||
echo "script usage: $(basename \$0) [-b] [-d] [-t]" >&2
|
echo "script usage: $(basename \$0) [-b] [-d] [-t]" >&2
|
||||||
echo "-b skip build"
|
echo "-b skip build"
|
||||||
echo "-d skip deployment"
|
echo "-d skip deployment"
|
||||||
echo "-t skip tests"
|
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@ -54,10 +48,10 @@ if [ "$DO_BUILD" -eq "1" ]; then
|
|||||||
BUILD_VERSION="$(extract_metadata metadata.json '.version')"
|
BUILD_VERSION="$(extract_metadata metadata.json '.version')"
|
||||||
|
|
||||||
# build the docker image
|
# build the docker image
|
||||||
docker build --file ./build/Dockerfile --tag zitadel:latest --tag zitadel:$BUILD_VERSION --tag zitadel:$BUILD_DATE $BUILD_PATH
|
DOCKER_BUILDKIT=1 docker build --file ./build/Dockerfile --tag zitadel:latest --tag zitadel:$BUILD_VERSION --tag zitadel:$BUILD_DATE $BUILD_PATH
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DO_DEPLOY" -eq "1" ]; then
|
if [ "$DO_DEPLOY" -eq "1" ]; then
|
||||||
# run cockroach and zitadel
|
# run cockroach and zitadel
|
||||||
docker compose --file ./docs/docs/guides/installation/run/docker-compose.yaml --file ./e2e/docker-compose-overwrite.yaml up --detach
|
./e2e/docker-compose.sh up --detach
|
||||||
fi
|
fi
|
14
e2e/local.env
Normal file
14
e2e/local.env
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
ZITADEL_E2E_CYPRESS_PORT=5000
|
||||||
|
ZITADEL_E2E_ORG=e2e-tests
|
||||||
|
ZITADEL_E2E_ORG_OWNER_PW=Password1!
|
||||||
|
ZITADEL_E2E_ORG_OWNER_VIEWER_PW=Password1!
|
||||||
|
ZITADEL_E2E_ORG_PROJECT_CREATOR_PW=Password1!
|
||||||
|
ZITADEL_E2E_PASSWORD_COMPLEXITY_USER_PW=Password1!
|
||||||
|
ZITADEL_E2E_LOGIN_POLICY_USER_PW=Password1!
|
||||||
|
ZITADEL_E2E_MACHINE_KEY_PATH="${projectRoot}/.keys/e2e.json"
|
||||||
|
ZITADEL_E2E_CONSOLE_URL="http://localhost:8080"
|
||||||
|
ZITADEL_E2E_API_URL="http://localhost:8080"
|
||||||
|
ZITADEL_E2E_ACCOUNTS_URL="http://localhost:8080"
|
||||||
|
ZITADEL_E2E_ISSUER_URL="http://localhost:8080/oauth/v2"
|
||||||
|
ZITADEL_E2E_OTHER_ZITADEL_IDP_INSTANCE=false
|
||||||
|
ZITADEL_E2E_ZITADEL_PROJECT_RESOURCE_ID="bignumber-$(echo -n $(./e2e/docker-compose.sh exec --no-TTY db cockroach sql --database zitadel --insecure --execute "select aggregate_id from eventstore.events where event_type = 'project.added' and event_data = '{\"name\": \"ZITADEL\"}';" --format tsv) | cut -d " " -f 2)"
|
8
internal/config/options/defaults.go
Normal file
8
internal/config/options/defaults.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed defaults.yaml
|
||||||
|
var defaultConfig []byte
|
30
internal/config/options/viper.go
Normal file
30
internal/config/options/viper.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/zitadel/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitViper() error {
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
viper.SetEnvPrefix("ZITADEL")
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
err := viper.ReadConfig(bytes.NewBuffer(defaultConfig))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read default config: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeToViper(configFiles ...string) {
|
||||||
|
for _, file := range configFiles {
|
||||||
|
viper.SetConfigFile(file)
|
||||||
|
err := viper.MergeInConfig()
|
||||||
|
logging.WithFields("file", file).OnError(err).Warn("unable to read config file")
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user