mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-08 16:37:41 +00:00
feat: encryption keys in database (#3265)
* enable overwrite of adminUser fields in defaults.yaml * create schema and table * cli: create keys * cli: create keys * read encryptionkey from db * merge v2 * file names * cleanup defaults.yaml * remove custom errors * load encryptionKeys on start * cleanup * fix merge * update system defaults * fix error message
This commit is contained in:
parent
7899a0b851
commit
5463244376
@ -4,10 +4,12 @@ import (
|
|||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/caos/zitadel/cmd/admin/initialise"
|
"github.com/caos/zitadel/cmd/admin/initialise"
|
||||||
|
"github.com/caos/zitadel/cmd/admin/key"
|
||||||
"github.com/caos/zitadel/cmd/admin/setup"
|
"github.com/caos/zitadel/cmd/admin/setup"
|
||||||
"github.com/caos/zitadel/cmd/admin/start"
|
"github.com/caos/zitadel/cmd/admin/start"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func New() *cobra.Command {
|
func New() *cobra.Command {
|
||||||
@ -24,6 +26,7 @@ func New() *cobra.Command {
|
|||||||
initialise.New(),
|
initialise.New(),
|
||||||
setup.New(),
|
setup.New(),
|
||||||
start.New(),
|
start.New(),
|
||||||
|
key.New(),
|
||||||
)
|
)
|
||||||
|
|
||||||
return adminCMD
|
return adminCMD
|
||||||
|
1
cmd/admin/initialise/sql/06_system.sql
Normal file
1
cmd/admin/initialise/sql/06_system.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
CREATE SCHEMA system;
|
6
cmd/admin/initialise/sql/07_encryption_keys_table.sql
Normal file
6
cmd/admin/initialise/sql/07_encryption_keys_table.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE system.encryption_keys (
|
||||||
|
id TEXT NOT NULL
|
||||||
|
, key TEXT NOT NULL
|
||||||
|
|
||||||
|
, PRIMARY KEY (id)
|
||||||
|
);
|
@ -9,6 +9,8 @@ The sql-files in this folder initialize the ZITADEL database and user. These obj
|
|||||||
- 03_grant_user.sql: grants the user created before to have full access to its database. The user needs full access to the database because zitadel makes ddl/dml on runtime
|
- 03_grant_user.sql: grants the user created before to have full access to its database. The user needs full access to the database because zitadel makes ddl/dml on runtime
|
||||||
- 04_eventstore.sql: creates the schema needed for eventsourcing
|
- 04_eventstore.sql: creates the schema needed for eventsourcing
|
||||||
- 05_projections.sql: creates the schema needed to read the data
|
- 05_projections.sql: creates the schema needed to read the data
|
||||||
- files 06_enable_hash_sharded_indexes.sql and 07_events_table.sql must run in the same session
|
- 06_system.sql: creates the schema needed for ZITADEL itself
|
||||||
- 06_enable_hash_sharded_indexes.sql enables the [hash sharded index](https://www.cockroachlabs.com/docs/stable/hash-sharded-indexes.html) feature for this session
|
- 07_encryption_keys_table.sql: creates the table for encryption keys (for event data)
|
||||||
- 07_events_table.sql creates the table for eventsourcing
|
- files 08_enable_hash_sharded_indexes.sql and 09_events_table.sql must run in the same session
|
||||||
|
- 08_enable_hash_sharded_indexes.sql enables the [hash sharded index](https://www.cockroachlabs.com/docs/stable/hash-sharded-indexes.html) feature for this session
|
||||||
|
- 09_events_table.sql creates the table for eventsourcing
|
||||||
|
@ -4,27 +4,36 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/database"
|
"github.com/caos/logging"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
eventstoreSchema = "eventstore"
|
eventstoreSchema = "eventstore"
|
||||||
projectionsSchema = "projections"
|
eventsTable = "events"
|
||||||
|
projectionsSchema = "projections"
|
||||||
|
systemSchema = "system"
|
||||||
|
encryptionKeysTable = "encryption_key"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
searchEventsTable = "SELECT table_name FROM [SHOW TABLES] WHERE table_name = 'events'"
|
searchTable = "SELECT table_name FROM [SHOW TABLES] WHERE table_name = $1"
|
||||||
searchSchema = "SELECT schema_name FROM [SHOW SCHEMAS] WHERE schema_name = $1"
|
searchSchema = "SELECT schema_name FROM [SHOW SCHEMAS] WHERE schema_name = $1"
|
||||||
//go:embed sql/06_enable_hash_sharded_indexes.sql
|
|
||||||
enableHashShardedIdx string
|
|
||||||
//go:embed sql/07_events_table.sql
|
|
||||||
createEventsStmt string
|
|
||||||
//go:embed sql/05_projections.sql
|
|
||||||
createProjectionsStmt string
|
|
||||||
//go:embed sql/04_eventstore.sql
|
//go:embed sql/04_eventstore.sql
|
||||||
createEventstoreStmt string
|
createEventstoreStmt string
|
||||||
|
//go:embed sql/05_projections.sql
|
||||||
|
createProjectionsStmt string
|
||||||
|
//go:embed sql/06_system.sql
|
||||||
|
createSystemStmt string
|
||||||
|
//go:embed sql/07_encryption_keys_table.sql
|
||||||
|
createEncryptionKeysStmt string
|
||||||
|
//go:embed sql/08_enable_hash_sharded_indexes.sql
|
||||||
|
enableHashShardedIdx string
|
||||||
|
//go:embed sql/09_events_table.sql
|
||||||
|
createEventsStmt string
|
||||||
)
|
)
|
||||||
|
|
||||||
func newZitadel() *cobra.Command {
|
func newZitadel() *cobra.Command {
|
||||||
@ -47,11 +56,20 @@ Prereqesits:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func verifyZitadel(config database.Config) error {
|
func verifyZitadel(config database.Config) error {
|
||||||
|
logging.WithFields("database", config.Database).Info("verify database")
|
||||||
db, err := database.Connect(config)
|
db, err := database.Connect(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := verify(db, exists(searchSchema, systemSchema), exec(createSystemStmt)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := verify(db, exists(searchTable, encryptionKeysTable), createEncryptionKeys); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := verify(db, exists(searchSchema, projectionsSchema), exec(createProjectionsStmt)); err != nil {
|
if err := verify(db, exists(searchSchema, projectionsSchema), exec(createProjectionsStmt)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -60,13 +78,26 @@ func verifyZitadel(config database.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := verify(db, exists(searchSchema, projectionsSchema), createEvents); err != nil {
|
if err := verify(db, exists(searchTable, eventsTable), createEvents); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.Close()
|
return db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createEncryptionKeys(db *sql.DB) error {
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = tx.Exec(createEncryptionKeysStmt); err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
func createEvents(db *sql.DB) error {
|
func createEvents(db *sql.DB) error {
|
||||||
tx, err := db.Begin()
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -71,3 +71,56 @@ func Test_verifyEvents(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_verifyEncryptionKeys(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
db db
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
targetErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "unable to begin",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectBegin(sql.ErrConnDone),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrConnDone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create table fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectBegin(nil),
|
||||||
|
expectExec(createEncryptionKeysStmt, sql.ErrNoRows),
|
||||||
|
expectRollback(nil),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrNoRows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "correct",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectBegin(nil),
|
||||||
|
expectExec(createEncryptionKeysStmt, nil),
|
||||||
|
expectCommit(nil),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := createEncryptionKeys(tt.args.db.db); !errors.Is(err, tt.targetErr) {
|
||||||
|
t.Errorf("createEvents() error = %v, want: %v", err, tt.targetErr)
|
||||||
|
}
|
||||||
|
if err := tt.args.db.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
130
cmd/admin/key/key.go
Normal file
130
cmd/admin/key/key.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package key
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
cryptoDB "github.com/caos/zitadel/internal/crypto/database"
|
||||||
|
"github.com/caos/zitadel/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagMasterKey = "masterkey"
|
||||||
|
flagKeyFile = "file"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Database database.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "keys",
|
||||||
|
Short: "manage encryption keys",
|
||||||
|
}
|
||||||
|
cmd.PersistentFlags().String(flagMasterKey, "", "masterkey for en/decryption keys")
|
||||||
|
cmd.AddCommand(newKey())
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func newKey() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "new [keyID=key]... [-f file]",
|
||||||
|
Short: "create new encryption key(s)",
|
||||||
|
Long: `create new encryption key(s) (encrypted by the provided master key)
|
||||||
|
provide key(s) by YAML file and/or by argument
|
||||||
|
Requirements:
|
||||||
|
- cockroachdb`,
|
||||||
|
Example: `new -f keys.yaml
|
||||||
|
new key1=somekey key2=anotherkey
|
||||||
|
new -f keys.yaml key2=anotherkey`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
keys, err := keysFromArgs(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
filePath, _ := cmd.Flags().GetString(flagKeyFile)
|
||||||
|
if filePath != "" {
|
||||||
|
file, err := openFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
yamlKeys, err := keysFromYAML(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
keys = append(keys, yamlKeys...)
|
||||||
|
}
|
||||||
|
config := new(Config)
|
||||||
|
if err := viper.Unmarshal(config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
masterKey, _ := cmd.Flags().GetString(flagMasterKey)
|
||||||
|
storage, err := keyStorage(config.Database, masterKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storage.CreateKeys(keys...)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.PersistentFlags().StringP(flagKeyFile, "f", "", "path to keys file")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func keysFromArgs(args []string) ([]*crypto.Key, error) {
|
||||||
|
keys := make([]*crypto.Key, len(args))
|
||||||
|
for i, arg := range args {
|
||||||
|
key := strings.Split(arg, "=")
|
||||||
|
if len(key) != 2 {
|
||||||
|
return nil, caos_errs.ThrowInternal(nil, "KEY-JKd82", "argument is not in the valid format [keyID=key]")
|
||||||
|
}
|
||||||
|
keys[i] = &crypto.Key{
|
||||||
|
ID: key[0],
|
||||||
|
Value: key[1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func keysFromYAML(file io.Reader) ([]*crypto.Key, error) {
|
||||||
|
data, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "KEY-ajGFr", "unable to extract keys from file")
|
||||||
|
}
|
||||||
|
keysYAML := make(map[string]string)
|
||||||
|
if err = yaml.Unmarshal(data, &keysYAML); err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "KEY-sd34K", "unable to extract keys from file")
|
||||||
|
}
|
||||||
|
keys := make([]*crypto.Key, 0, len(keysYAML))
|
||||||
|
for id, key := range keysYAML {
|
||||||
|
keys = append(keys, &crypto.Key{
|
||||||
|
ID: id,
|
||||||
|
Value: key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func openFile(fileName string) (*os.File, error) {
|
||||||
|
file, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternalf(err, "KEY-asGr2", "failed to open file: %s", fileName)
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyStorage(config database.Config, masterKey string) (crypto.KeyStorage, error) {
|
||||||
|
db, err := database.Connect(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cryptoDB.NewKeyStorage(db, masterKey)
|
||||||
|
}
|
161
cmd/admin/key/key_test.go
Normal file
161
cmd/admin/key/key_test.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package key
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_keysFromArgs(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
args []string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
keys []*crypto.Key
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"no args",
|
||||||
|
args{},
|
||||||
|
res{
|
||||||
|
keys: []*crypto.Key{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"invalid arg",
|
||||||
|
args{
|
||||||
|
args: []string{"keyID", "value"},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: caos_errors.IsInternal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"single arg",
|
||||||
|
args{
|
||||||
|
args: []string{"keyID=value"},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
keys: []*crypto.Key{
|
||||||
|
{
|
||||||
|
ID: "keyID",
|
||||||
|
Value: "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multiple args",
|
||||||
|
args{
|
||||||
|
args: []string{"keyID=value", "keyID2=value2"},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
keys: []*crypto.Key{
|
||||||
|
{
|
||||||
|
ID: "keyID",
|
||||||
|
Value: "value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "keyID2",
|
||||||
|
Value: "value2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := keysFromArgs(tt.args.args)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.res.keys) {
|
||||||
|
t.Errorf("keysFromArgs() got = %v, want %v", got, tt.res.keys)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_keysFromYAML(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
file io.Reader
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
keys []*crypto.Key
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"invalid yaml",
|
||||||
|
args{
|
||||||
|
file: bytes.NewReader([]byte("keyID=ds")),
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: caos_errors.IsInternal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"single key",
|
||||||
|
args{
|
||||||
|
file: bytes.NewReader([]byte("keyID: value")),
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
keys: []*crypto.Key{
|
||||||
|
{
|
||||||
|
ID: "keyID",
|
||||||
|
Value: "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multiple keys",
|
||||||
|
args{
|
||||||
|
file: bytes.NewReader([]byte("keyID: value\nkeyID2: value2")),
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
keys: []*crypto.Key{
|
||||||
|
{
|
||||||
|
ID: "keyID",
|
||||||
|
Value: "value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "keyID2",
|
||||||
|
Value: "value2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := keysFromYAML(tt.args.file)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
}
|
||||||
|
assert.EqualValues(t, got, tt.res.keys)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
105
cmd/admin/start/encryption_keys.go
Normal file
105
cmd/admin/start/encryption_keys.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package start
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultKeyIDs = []string{
|
||||||
|
"domainVerificationKey",
|
||||||
|
"idpConfigKey",
|
||||||
|
"oidcKey",
|
||||||
|
"otpKey",
|
||||||
|
"smsKey",
|
||||||
|
"smtpKey",
|
||||||
|
"userKey",
|
||||||
|
"csrfCookieKey",
|
||||||
|
"userAgentCookieKey",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type encryptionKeys struct {
|
||||||
|
DomainVerification crypto.EncryptionAlgorithm
|
||||||
|
IDPConfig crypto.EncryptionAlgorithm
|
||||||
|
OIDC crypto.EncryptionAlgorithm
|
||||||
|
OTP crypto.EncryptionAlgorithm
|
||||||
|
SMS crypto.EncryptionAlgorithm
|
||||||
|
SMTP crypto.EncryptionAlgorithm
|
||||||
|
User crypto.EncryptionAlgorithm
|
||||||
|
CSRFCookieKey []byte
|
||||||
|
UserAgentCookieKey []byte
|
||||||
|
OIDCKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureEncryptionKeys(keyConfig *encryptionKeyConfig, keyStorage crypto.KeyStorage) (*encryptionKeys, error) {
|
||||||
|
keys, err := keyStorage.ReadKeys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
if err := createDefaultKeys(keyStorage); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encryptionKeys := new(encryptionKeys)
|
||||||
|
encryptionKeys.DomainVerification, err = crypto.NewAESCrypto(keyConfig.DomainVerification, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptionKeys.IDPConfig, err = crypto.NewAESCrypto(keyConfig.IDPConfig, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptionKeys.OIDC, err = crypto.NewAESCrypto(keyConfig.OIDC, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key, err := crypto.LoadKey(keyConfig.OIDC.EncryptionKeyID, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptionKeys.OIDCKey = []byte(key)
|
||||||
|
encryptionKeys.OTP, err = crypto.NewAESCrypto(keyConfig.OTP, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptionKeys.SMS, err = crypto.NewAESCrypto(keyConfig.SMS, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptionKeys.SMTP, err = crypto.NewAESCrypto(keyConfig.SMTP, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptionKeys.User, err = crypto.NewAESCrypto(keyConfig.User, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key, err = crypto.LoadKey(keyConfig.CSRFCookieKeyID, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptionKeys.CSRFCookieKey = []byte(key)
|
||||||
|
key, err = crypto.LoadKey(keyConfig.UserAgentCookieKeyID, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encryptionKeys.UserAgentCookieKey = []byte(key)
|
||||||
|
return encryptionKeys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDefaultKeys(keyStorage crypto.KeyStorage) error {
|
||||||
|
keys := make([]*crypto.Key, len(defaultKeyIDs))
|
||||||
|
for i, keyID := range defaultKeyIDs {
|
||||||
|
key, err := crypto.NewKey(keyID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
keys[i] = key
|
||||||
|
}
|
||||||
|
if err := keyStorage.CreateKeys(keys...); err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "START-aGBq2", "cannot create default keys")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -39,6 +39,7 @@ import (
|
|||||||
"github.com/caos/zitadel/internal/command"
|
"github.com/caos/zitadel/internal/command"
|
||||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
"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/database"
|
||||||
"github.com/caos/zitadel/internal/domain"
|
"github.com/caos/zitadel/internal/domain"
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
@ -52,6 +53,10 @@ import (
|
|||||||
"github.com/caos/zitadel/openapi"
|
"github.com/caos/zitadel/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagMasterKey = "masterkey"
|
||||||
|
)
|
||||||
|
|
||||||
func New() *cobra.Command {
|
func New() *cobra.Command {
|
||||||
start := &cobra.Command{
|
start := &cobra.Command{
|
||||||
Use: "start",
|
Use: "start",
|
||||||
@ -72,7 +77,8 @@ Requirements:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return startZitadel(config)
|
masterKey, _ := cmd.Flags().GetString("masterkey")
|
||||||
|
return startZitadel(config, masterKey)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
bindUint16Flag(start, "port", "port to run ZITADEL on")
|
bindUint16Flag(start, "port", "port to run ZITADEL on")
|
||||||
@ -80,6 +86,8 @@ Requirements:
|
|||||||
bindStringFlag(start, "externalPort", "port ZITADEL will be exposed on")
|
bindStringFlag(start, "externalPort", "port ZITADEL will be exposed on")
|
||||||
bindBoolFlag(start, "externalSecure", "if ZITADEL will be served on HTTPS")
|
bindBoolFlag(start, "externalSecure", "if ZITADEL will be served on HTTPS")
|
||||||
|
|
||||||
|
start.PersistentFlags().String(flagMasterKey, "", "masterkey for en/decryption keys")
|
||||||
|
|
||||||
return start
|
return start
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +113,7 @@ type startConfig struct {
|
|||||||
ExternalDomain string
|
ExternalDomain string
|
||||||
ExternalSecure bool
|
ExternalSecure bool
|
||||||
Database database.Config
|
Database database.Config
|
||||||
Projections projectionConfig
|
Projections projection.Config
|
||||||
AuthZ authz.Config
|
AuthZ authz.Config
|
||||||
Auth auth_es.Config
|
Auth auth_es.Config
|
||||||
Admin admin_es.Config
|
Admin admin_es.Config
|
||||||
@ -117,14 +125,22 @@ type startConfig struct {
|
|||||||
AssetStorage static_config.AssetStorageConfig
|
AssetStorage static_config.AssetStorageConfig
|
||||||
InternalAuthZ internal_authz.Config
|
InternalAuthZ internal_authz.Config
|
||||||
SystemDefaults systemdefaults.SystemDefaults
|
SystemDefaults systemdefaults.SystemDefaults
|
||||||
|
EncryptionKeys *encryptionKeyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type projectionConfig struct {
|
type encryptionKeyConfig struct {
|
||||||
projection.Config
|
DomainVerification *crypto.KeyConfig
|
||||||
KeyConfig *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) error {
|
func startZitadel(config *startConfig, masterKey string) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
keyChan := make(chan interface{})
|
keyChan := make(chan interface{})
|
||||||
|
|
||||||
@ -132,6 +148,16 @@ func startZitadel(config *startConfig) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot start client for projection: %w", err)
|
return fmt.Errorf("cannot start client for projection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keyStorage, err := cryptoDB.NewKeyStorage(dbClient, masterKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot start key storage: %w", err)
|
||||||
|
}
|
||||||
|
keys, err := ensureEncryptionKeys(config.EncryptionKeys, keyStorage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var storage static.Storage
|
var storage static.Storage
|
||||||
//TODO: enable when storage is implemented again
|
//TODO: enable when storage is implemented again
|
||||||
//if *assetsEnabled {
|
//if *assetsEnabled {
|
||||||
@ -142,22 +168,13 @@ func startZitadel(config *startConfig) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot start eventstore for queries: %w", err)
|
return fmt.Errorf("cannot start eventstore for queries: %w", err)
|
||||||
}
|
}
|
||||||
smtpPasswordCrypto, err := crypto.NewAESCrypto(config.SystemDefaults.SMTPPasswordVerificationKey)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot create smtp crypto: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
smsCrypto, err := crypto.NewAESCrypto(config.SystemDefaults.SMSVerificationKey)
|
queries, err := query.StartQueries(ctx, eventstoreClient, dbClient, config.Projections, keys.OIDC, keyChan, config.InternalAuthZ.RolePermissionMappings)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot create smtp crypto: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
queries, err := query.StartQueries(ctx, eventstoreClient, dbClient, config.Projections.Config, config.SystemDefaults, config.Projections.KeyConfig, keyChan, config.InternalAuthZ.RolePermissionMappings)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot start queries: %w", err)
|
return fmt.Errorf("cannot start queries: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
authZRepo, err := authz.Start(config.AuthZ, config.SystemDefaults, queries, dbClient, config.OIDC.KeyConfig)
|
authZRepo, err := authz.Start(config.AuthZ, config.SystemDefaults, queries, dbClient, keys.OIDC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting authz repo: %w", err)
|
return fmt.Errorf("error starting authz repo: %w", err)
|
||||||
}
|
}
|
||||||
@ -166,22 +183,22 @@ func startZitadel(config *startConfig) error {
|
|||||||
Origin: http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure),
|
Origin: http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure),
|
||||||
DisplayName: "ZITADEL",
|
DisplayName: "ZITADEL",
|
||||||
}
|
}
|
||||||
commands, err := command.StartCommands(eventstoreClient, config.SystemDefaults, config.InternalAuthZ, storage, authZRepo, config.OIDC.KeyConfig, webAuthNConfig, smtpPasswordCrypto, smsCrypto)
|
commands, err := command.StartCommands(eventstoreClient, config.SystemDefaults, config.InternalAuthZ, storage, authZRepo, webAuthNConfig, keys.IDPConfig, keys.OTP, keys.SMTP, keys.SMS, keys.DomainVerification, keys.OIDC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot start commands: %w", err)
|
return fmt.Errorf("cannot start commands: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.Start(config.Notification, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, smtpPasswordCrypto, smsCrypto)
|
notification.Start(config.Notification, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, keys.User, keys.SMTP, keys.SMS)
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
err = startAPIs(ctx, router, commands, queries, eventstoreClient, dbClient, keyChan, config, storage, authZRepo)
|
err = startAPIs(ctx, router, commands, queries, eventstoreClient, dbClient, keyChan, config, storage, authZRepo, keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return listen(ctx, router, config.Port)
|
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) 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 *startConfig, store static.Storage, authZRepo authz_repo.Repository, keys *encryptionKeys) error {
|
||||||
repo := struct {
|
repo := struct {
|
||||||
authz_repo.Repository
|
authz_repo.Repository
|
||||||
*query.Queries
|
*query.Queries
|
||||||
@ -192,11 +209,7 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
|
|||||||
verifier := internal_authz.Start(repo)
|
verifier := internal_authz.Start(repo)
|
||||||
|
|
||||||
apis := api.New(config.Port, router, &repo, config.InternalAuthZ, config.ExternalSecure)
|
apis := api.New(config.Port, router, &repo, config.InternalAuthZ, config.ExternalSecure)
|
||||||
userEncryptionAlgorithm, err := crypto.NewAESCrypto(config.SystemDefaults.UserVerificationKey)
|
authRepo, err := auth_es.Start(config.Auth, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, keys.OIDC, keys.User)
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
authRepo, err := auth_es.Start(config.Auth, config.SystemDefaults, commands, queries, dbClient, config.OIDC.KeyConfig, assets.HandlerPrefix, userEncryptionAlgorithm)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting auth repo: %w", err)
|
return fmt.Errorf("error starting auth repo: %w", err)
|
||||||
}
|
}
|
||||||
@ -204,25 +217,25 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting admin repo: %w", err)
|
return fmt.Errorf("error starting admin repo: %w", err)
|
||||||
}
|
}
|
||||||
if err := apis.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.SystemDefaults.Domain, assets.HandlerPrefix, userEncryptionAlgorithm)); err != nil {
|
if err := apis.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.SystemDefaults.Domain, assets.HandlerPrefix, keys.User)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, userEncryptionAlgorithm)); err != nil {
|
if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, keys.User)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, userEncryptionAlgorithm)); err != nil {
|
if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, keys.User)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
apis.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator, store, queries))
|
apis.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator, store, queries))
|
||||||
|
|
||||||
userAgentInterceptor, err := middleware.NewUserAgentHandler(config.UserAgentCookie, config.ExternalDomain, id.SonyFlakeGenerator, config.ExternalSecure)
|
userAgentInterceptor, err := middleware.NewUserAgentHandler(config.UserAgentCookie, keys.UserAgentCookieKey, config.ExternalDomain, id.SonyFlakeGenerator, config.ExternalSecure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
issuer := oidc.Issuer(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
issuer := oidc.Issuer(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||||
oidcProvider, err := oidc.NewProvider(ctx, config.OIDC, issuer, login.DefaultLoggedOutPath, commands, queries, authRepo, config.SystemDefaults.KeyConfig, eventstore, dbClient, keyChan, userAgentInterceptor)
|
oidcProvider, err := oidc.NewProvider(ctx, config.OIDC, issuer, login.DefaultLoggedOutPath, commands, queries, authRepo, config.SystemDefaults.KeyConfig, keys.OIDC, keys.OIDCKey, eventstore, dbClient, keyChan, userAgentInterceptor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to start oidc provider: %w", err)
|
return fmt.Errorf("unable to start oidc provider: %w", err)
|
||||||
}
|
}
|
||||||
@ -238,13 +251,14 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to get client_id for console: %w", err)
|
return fmt.Errorf("unable to get client_id for console: %w", err)
|
||||||
}
|
}
|
||||||
c, err := console.Start(config.Console, config.ExternalDomain, http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure), issuer, consoleID)
|
baseURL := http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||||
|
c, err := console.Start(config.Console, config.ExternalDomain, baseURL, issuer, consoleID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to start console: %w", err)
|
return fmt.Errorf("unable to start console: %w", err)
|
||||||
}
|
}
|
||||||
apis.RegisterHandler(console.HandlerPrefix, c)
|
apis.RegisterHandler(console.HandlerPrefix, c)
|
||||||
|
|
||||||
l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, config.SystemDefaults, console.HandlerPrefix, config.ExternalDomain, oidc.AuthCallback, config.ExternalSecure, userAgentInterceptor, userEncryptionAlgorithm)
|
l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, config.SystemDefaults, console.HandlerPrefix, config.ExternalDomain, baseURL, oidc.AuthCallback, config.ExternalSecure, userAgentInterceptor, keys.User, keys.IDPConfig, keys.CSRFCookieKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to start login: %w", err)
|
return fmt.Errorf("unable to start login: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,19 @@ Database:
|
|||||||
Username: zitadel
|
Username: zitadel
|
||||||
Password: ""
|
Password: ""
|
||||||
SSL:
|
SSL:
|
||||||
Mode: diabled
|
Mode: disable
|
||||||
RootCert: ""
|
RootCert: ""
|
||||||
Cert: ""
|
Cert: ""
|
||||||
Key: ""
|
Key: ""
|
||||||
|
|
||||||
AdminUser:
|
AdminUser:
|
||||||
Username: root
|
Username: root
|
||||||
|
Password: ""
|
||||||
|
SSL:
|
||||||
|
Mode: disable
|
||||||
|
RootCert: ""
|
||||||
|
Cert: ""
|
||||||
|
Key: ""
|
||||||
|
|
||||||
Projections:
|
Projections:
|
||||||
Config:
|
Config:
|
||||||
@ -38,10 +44,6 @@ Projections:
|
|||||||
Customizations:
|
Customizations:
|
||||||
projects:
|
projects:
|
||||||
BulkLimit: 2000
|
BulkLimit: 2000
|
||||||
KeyConfig:
|
|
||||||
# We don't need an EncryptionKey but DecryptionKeys (and load them via env)
|
|
||||||
DecryptionKeyIDs:
|
|
||||||
Path: ""
|
|
||||||
|
|
||||||
AuthZ:
|
AuthZ:
|
||||||
Repository:
|
Repository:
|
||||||
@ -66,8 +68,6 @@ Admin:
|
|||||||
|
|
||||||
UserAgentCookie:
|
UserAgentCookie:
|
||||||
Name: zitadel.useragent
|
Name: zitadel.useragent
|
||||||
Key:
|
|
||||||
EncryptionKeyID:
|
|
||||||
MaxAge: 8760h #365*24h (1 year)
|
MaxAge: 8760h #365*24h (1 year)
|
||||||
|
|
||||||
OIDC:
|
OIDC:
|
||||||
@ -84,19 +84,11 @@ OIDC:
|
|||||||
Cache:
|
Cache:
|
||||||
MaxAge: 12h
|
MaxAge: 12h
|
||||||
SharedMaxAge: 168h #7d
|
SharedMaxAge: 168h #7d
|
||||||
KeyConfig:
|
|
||||||
EncryptionKeyID: ""
|
|
||||||
DecryptionKeyIDs:
|
|
||||||
Path: ""
|
|
||||||
CustomEndpoints:
|
CustomEndpoints:
|
||||||
|
|
||||||
Login:
|
Login:
|
||||||
LanguageCookieName: zitadel.login.lang
|
LanguageCookieName: zitadel.login.lang
|
||||||
CSRF:
|
CSRFCookieName: zitadel.login.csrf
|
||||||
CookieName: zitadel.login.csrf
|
|
||||||
Development: true
|
|
||||||
Key:
|
|
||||||
EncryptionKeyID:
|
|
||||||
Cache:
|
Cache:
|
||||||
MaxAge: 12h
|
MaxAge: 12h
|
||||||
SharedMaxAge: 168h #7d
|
SharedMaxAge: 168h #7d
|
||||||
@ -118,6 +110,31 @@ Notification:
|
|||||||
FailureCountUntilSkip: 5
|
FailureCountUntilSkip: 5
|
||||||
Handlers:
|
Handlers:
|
||||||
|
|
||||||
|
EncryptionKeys:
|
||||||
|
DomainVerification:
|
||||||
|
EncryptionKeyID: "domainVerificationKey"
|
||||||
|
DecryptionKeyIDs:
|
||||||
|
IDPConfig:
|
||||||
|
EncryptionKeyID: "idpConfigKey"
|
||||||
|
DecryptionKeyIDs:
|
||||||
|
OIDC:
|
||||||
|
EncryptionKeyID: "oidcKey"
|
||||||
|
DecryptionKeyIDs:
|
||||||
|
OTP:
|
||||||
|
EncryptionKeyID: "otpKey"
|
||||||
|
DecryptionKeyIDs:
|
||||||
|
SMS:
|
||||||
|
EncryptionKeyID: "smsKey"
|
||||||
|
DecryptionKeyIDs:
|
||||||
|
SMTP:
|
||||||
|
EncryptionKeyID: "smtpKey"
|
||||||
|
DecryptionKeyIDs:
|
||||||
|
User:
|
||||||
|
EncryptionKeyID: "userKey"
|
||||||
|
DecryptionKeyIDs:
|
||||||
|
CSRFCookieKeyID: "csrfCookieKey"
|
||||||
|
UserAgentCookieKeyID: "userAgentCookieKey"
|
||||||
|
|
||||||
#TODO: configure as soon as possible
|
#TODO: configure as soon as possible
|
||||||
#AssetStorage:
|
#AssetStorage:
|
||||||
# Type: $ZITADEL_ASSET_STORAGE_TYPE
|
# Type: $ZITADEL_ASSET_STORAGE_TYPE
|
||||||
@ -137,73 +154,14 @@ SystemDefaults:
|
|||||||
ZitadelDocs:
|
ZitadelDocs:
|
||||||
Issuer: $ZITADEL_ISSUER
|
Issuer: $ZITADEL_ISSUER
|
||||||
DiscoveryEndpoint: '$ZITADEL_ISSUER/.well-known/openid-configuration'
|
DiscoveryEndpoint: '$ZITADEL_ISSUER/.well-known/openid-configuration'
|
||||||
UserVerificationKey:
|
|
||||||
EncryptionKeyID: $ZITADEL_USER_VERIFICATION_KEY
|
|
||||||
IDPConfigVerificationKey:
|
|
||||||
EncryptionKeyID: $ZITADEL_IDP_CONFIG_VERIFICATION_KEY
|
|
||||||
SMTPPasswordVerificationKey:
|
|
||||||
EncryptionKeyID: $ZITADEL_SMTP_PASSWORD_VERIFICATION_KEY
|
|
||||||
SMSVerificationKey:
|
|
||||||
EncryptionKeyID: $ZITADEL_SMS_VERIFICATION_KEY
|
|
||||||
SecretGenerators:
|
SecretGenerators:
|
||||||
PasswordSaltCost: 14
|
PasswordSaltCost: 14
|
||||||
ClientSecretGenerator:
|
|
||||||
Length: 64
|
|
||||||
IncludeLowerLetters: true
|
|
||||||
IncludeUpperLetters: true
|
|
||||||
IncludeDigits: true
|
|
||||||
IncludeSymbols: false
|
|
||||||
InitializeUserCode:
|
|
||||||
Length: 6
|
|
||||||
Expiry: '72h'
|
|
||||||
IncludeLowerLetters: false
|
|
||||||
IncludeUpperLetters: true
|
|
||||||
IncludeDigits: true
|
|
||||||
IncludeSymbols: false
|
|
||||||
EmailVerificationCode:
|
|
||||||
Length: 6
|
|
||||||
Expiry: '1h'
|
|
||||||
IncludeLowerLetters: false
|
|
||||||
IncludeUpperLetters: true
|
|
||||||
IncludeDigits: true
|
|
||||||
IncludeSymbols: false
|
|
||||||
PhoneVerificationCode:
|
|
||||||
Length: 6
|
|
||||||
Expiry: '1h'
|
|
||||||
IncludeLowerLetters: false
|
|
||||||
IncludeUpperLetters: true
|
|
||||||
IncludeDigits: true
|
|
||||||
IncludeSymbols: false
|
|
||||||
PasswordVerificationCode:
|
|
||||||
Length: 6
|
|
||||||
Expiry: '1h'
|
|
||||||
IncludeLowerLetters: false
|
|
||||||
IncludeUpperLetters: true
|
|
||||||
IncludeDigits: true
|
|
||||||
IncludeSymbols: false
|
|
||||||
PasswordlessInitCode:
|
|
||||||
Length: 12
|
|
||||||
Expiry: '1h'
|
|
||||||
IncludeLowerLetters: true
|
|
||||||
IncludeUpperLetters: true
|
|
||||||
IncludeDigits: true
|
|
||||||
IncludeSymbols: false
|
|
||||||
MachineKeySize: 2048
|
MachineKeySize: 2048
|
||||||
ApplicationKeySize: 2048
|
ApplicationKeySize: 2048
|
||||||
Multifactors:
|
Multifactors:
|
||||||
OTP:
|
OTP:
|
||||||
Issuer: 'ZITADEL'
|
Issuer: 'ZITADEL'
|
||||||
VerificationKey:
|
|
||||||
EncryptionKeyID: $ZITADEL_OTP_VERIFICATION_KEY
|
|
||||||
VerificationLifetimes:
|
|
||||||
PasswordCheck: 240h #10d
|
|
||||||
ExternalLoginCheck: 240h #10d
|
|
||||||
MFAInitSkip: 720h #30d
|
|
||||||
SecondFactorCheck: 18h
|
|
||||||
MultiFactorCheck: 12h
|
|
||||||
DomainVerification:
|
DomainVerification:
|
||||||
VerificationKey:
|
|
||||||
EncryptionKeyID: $ZITADEL_DOMAIN_VERIFICATION_KEY
|
|
||||||
VerificationGenerator:
|
VerificationGenerator:
|
||||||
Length: 32
|
Length: 32
|
||||||
IncludeLowerLetters: true
|
IncludeLowerLetters: true
|
||||||
@ -211,38 +169,13 @@ SystemDefaults:
|
|||||||
IncludeDigits: true
|
IncludeDigits: true
|
||||||
IncludeSymbols: false
|
IncludeSymbols: false
|
||||||
Notifications:
|
Notifications:
|
||||||
# DebugMode: $DEBUG_MODE
|
|
||||||
Endpoints:
|
Endpoints:
|
||||||
InitCode: '$ZITADEL_ACCOUNTS/user/init?userID={{.UserID}}&code={{.Code}}&passwordset={{.PasswordSet}}'
|
InitCode: '$ZITADEL_ACCOUNTS/user/init?userID={{.UserID}}&code={{.Code}}&passwordset={{.PasswordSet}}'
|
||||||
PasswordReset: '$ZITADEL_ACCOUNTS/password/init?userID={{.UserID}}&code={{.Code}}'
|
PasswordReset: '$ZITADEL_ACCOUNTS/password/init?userID={{.UserID}}&code={{.Code}}'
|
||||||
VerifyEmail: '$ZITADEL_ACCOUNTS/mail/verification?userID={{.UserID}}&code={{.Code}}'
|
VerifyEmail: '$ZITADEL_ACCOUNTS/mail/verification?userID={{.UserID}}&code={{.Code}}'
|
||||||
DomainClaimed: '$ZITADEL_ACCOUNTS/login'
|
DomainClaimed: '$ZITADEL_ACCOUNTS/login'
|
||||||
PasswordlessRegistration: '$ZITADEL_ACCOUNTS/login/passwordless/init'
|
PasswordlessRegistration: '$ZITADEL_ACCOUNTS/login/passwordless/init'
|
||||||
Providers:
|
FileSystemPath: '.notifications/'
|
||||||
Email:
|
|
||||||
SMTP:
|
|
||||||
Host: $SMTP_HOST
|
|
||||||
User: $SMTP_USER
|
|
||||||
Password: $SMTP_PASSWORD
|
|
||||||
From: $EMAIL_SENDER_ADDRESS
|
|
||||||
FromName: $EMAIL_SENDER_NAME
|
|
||||||
# Tls: $SMTP_TLS
|
|
||||||
Twilio:
|
|
||||||
SID: $TWILIO_SERVICE_SID
|
|
||||||
Token: $TWILIO_TOKEN
|
|
||||||
From: $TWILIO_SENDER_NAME
|
|
||||||
FileSystem:
|
|
||||||
# Enabled: $FS_NOTIFICATIONS_ENABLED
|
|
||||||
Path: $FS_NOTIFICATIONS_PATH
|
|
||||||
# Compact: $FS_NOTIFICATIONS_COMPACT
|
|
||||||
Log:
|
|
||||||
# Enabled: $LOG_NOTIFICATIONS_ENABLED
|
|
||||||
# Compact: $LOG_NOTIFICATIONS_COMPACT
|
|
||||||
Chat:
|
|
||||||
# Enabled: $CHAT_ENABLED
|
|
||||||
Url: $CHAT_URL
|
|
||||||
# Compact: $CHAT_COMPACT
|
|
||||||
SplitCount: 4000
|
|
||||||
KeyConfig:
|
KeyConfig:
|
||||||
Size: 2048
|
Size: 2048
|
||||||
PrivateKeyLifetime: 6h
|
PrivateKeyLifetime: 6h
|
||||||
|
@ -188,30 +188,6 @@ Update twilio sms provider token
|
|||||||
PUT: /sms/twilio/{id}/token
|
PUT: /sms/twilio/{id}/token
|
||||||
|
|
||||||
|
|
||||||
### GetFileSystemNotificationProvider
|
|
||||||
|
|
||||||
> **rpc** GetFileSystemNotificationProvider([GetFileSystemNotificationProviderRequest](#getfilesystemnotificationproviderrequest))
|
|
||||||
[GetFileSystemNotificationProviderResponse](#getfilesystemnotificationproviderresponse)
|
|
||||||
|
|
||||||
Get file system notification provider
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GET: /notification/provider/file
|
|
||||||
|
|
||||||
|
|
||||||
### GetLogNotificationProvider
|
|
||||||
|
|
||||||
> **rpc** GetLogNotificationProvider([GetLogNotificationProviderRequest](#getlognotificationproviderrequest))
|
|
||||||
[GetLogNotificationProviderResponse](#getlognotificationproviderresponse)
|
|
||||||
|
|
||||||
Get log notification provider
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GET: /notification/provider/log
|
|
||||||
|
|
||||||
|
|
||||||
### GetOIDCSettings
|
### GetOIDCSettings
|
||||||
|
|
||||||
> **rpc** GetOIDCSettings([GetOIDCSettingsRequest](#getoidcsettingsrequest))
|
> **rpc** GetOIDCSettings([GetOIDCSettingsRequest](#getoidcsettingsrequest))
|
||||||
@ -236,6 +212,30 @@ Update oidc settings (e.g token lifetimes, etc)
|
|||||||
PUT: /settings/oidc
|
PUT: /settings/oidc
|
||||||
|
|
||||||
|
|
||||||
|
### GetFileSystemNotificationProvider
|
||||||
|
|
||||||
|
> **rpc** GetFileSystemNotificationProvider([GetFileSystemNotificationProviderRequest](#getfilesystemnotificationproviderrequest))
|
||||||
|
[GetFileSystemNotificationProviderResponse](#getfilesystemnotificationproviderresponse)
|
||||||
|
|
||||||
|
Get file system notification provider
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GET: /notification/provider/file
|
||||||
|
|
||||||
|
|
||||||
|
### GetLogNotificationProvider
|
||||||
|
|
||||||
|
> **rpc** GetLogNotificationProvider([GetLogNotificationProviderRequest](#getlognotificationproviderrequest))
|
||||||
|
[GetLogNotificationProviderResponse](#getlognotificationproviderresponse)
|
||||||
|
|
||||||
|
Get log notification provider
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GET: /notification/provider/log
|
||||||
|
|
||||||
|
|
||||||
### GetOrgByID
|
### GetOrgByID
|
||||||
|
|
||||||
> **rpc** GetOrgByID([GetOrgByIDRequest](#getorgbyidrequest))
|
> **rpc** GetOrgByID([GetOrgByIDRequest](#getorgbyidrequest))
|
||||||
|
@ -53,11 +53,11 @@ func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (*
|
|||||||
human := setUpOrgHumanToDomain(req.User.(*admin_pb.SetUpOrgRequest_Human_).Human) //TODO: handle machine
|
human := setUpOrgHumanToDomain(req.User.(*admin_pb.SetUpOrgRequest_Human_).Human) //TODO: handle machine
|
||||||
org := setUpOrgOrgToDomain(req.Org)
|
org := setUpOrgOrgToDomain(req.Org)
|
||||||
|
|
||||||
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.UserCodeAlg)
|
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
|
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/admin/repository"
|
"github.com/caos/zitadel/internal/admin/repository"
|
||||||
@ -9,6 +8,7 @@ import (
|
|||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/server"
|
"github.com/caos/zitadel/internal/api/grpc/server"
|
||||||
"github.com/caos/zitadel/internal/command"
|
"github.com/caos/zitadel/internal/command"
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
"github.com/caos/zitadel/internal/query"
|
"github.com/caos/zitadel/internal/query"
|
||||||
"github.com/caos/zitadel/pkg/grpc/admin"
|
"github.com/caos/zitadel/pkg/grpc/admin"
|
||||||
)
|
)
|
||||||
@ -26,22 +26,27 @@ type Server struct {
|
|||||||
administrator repository.AdministratorRepository
|
administrator repository.AdministratorRepository
|
||||||
iamDomain string
|
iamDomain string
|
||||||
assetsAPIDomain string
|
assetsAPIDomain string
|
||||||
|
userCodeAlg crypto.EncryptionAlgorithm
|
||||||
UserCodeAlg crypto.EncryptionAlgorithm
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Repository eventsourcing.Config
|
Repository eventsourcing.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateServer(command *command.Commands, query *query.Queries, repo repository.Repository, iamDomain, assetsAPIDomain string, userCrypto *crypto.AESCrypto) *Server {
|
func CreateServer(command *command.Commands,
|
||||||
|
query *query.Queries,
|
||||||
|
repo repository.Repository,
|
||||||
|
iamDomain,
|
||||||
|
assetsAPIDomain string,
|
||||||
|
userCodeAlg crypto.EncryptionAlgorithm,
|
||||||
|
) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
command: command,
|
command: command,
|
||||||
query: query,
|
query: query,
|
||||||
administrator: repo,
|
administrator: repo,
|
||||||
iamDomain: iamDomain,
|
iamDomain: iamDomain,
|
||||||
assetsAPIDomain: assetsAPIDomain,
|
assetsAPIDomain: assetsAPIDomain,
|
||||||
UserCodeAlg: userCrypto,
|
userCodeAlg: userCodeAlg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ func (s *Server) GetMyEmail(ctx context.Context, _ *auth_pb.GetMyEmailRequest) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) SetMyEmail(ctx context.Context, req *auth_pb.SetMyEmailRequest) (*auth_pb.SetMyEmailResponse, error) {
|
func (s *Server) SetMyEmail(ctx context.Context, req *auth_pb.SetMyEmailRequest) (*auth_pb.SetMyEmailResponse, error) {
|
||||||
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
|
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ func (s *Server) SetMyEmail(ctx context.Context, req *auth_pb.SetMyEmailRequest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) VerifyMyEmail(ctx context.Context, req *auth_pb.VerifyMyEmailRequest) (*auth_pb.VerifyMyEmailResponse, error) {
|
func (s *Server) VerifyMyEmail(ctx context.Context, req *auth_pb.VerifyMyEmailRequest) (*auth_pb.VerifyMyEmailResponse, error) {
|
||||||
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
|
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ func (s *Server) VerifyMyEmail(ctx context.Context, req *auth_pb.VerifyMyEmailRe
|
|||||||
|
|
||||||
func (s *Server) ResendMyEmailVerification(ctx context.Context, _ *auth_pb.ResendMyEmailVerificationRequest) (*auth_pb.ResendMyEmailVerificationResponse, error) {
|
func (s *Server) ResendMyEmailVerification(ctx context.Context, _ *auth_pb.ResendMyEmailVerificationRequest) (*auth_pb.ResendMyEmailVerificationResponse, error) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
ctxData := authz.GetCtxData(ctx)
|
||||||
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
|
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/query"
|
|
||||||
"google.golang.org/protobuf/types/known/durationpb"
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||||
user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
|
user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
|
||||||
"github.com/caos/zitadel/internal/domain"
|
"github.com/caos/zitadel/internal/domain"
|
||||||
|
"github.com/caos/zitadel/internal/query"
|
||||||
auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
|
auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
|
||||||
user_pb "github.com/caos/zitadel/pkg/grpc/user"
|
user_pb "github.com/caos/zitadel/pkg/grpc/user"
|
||||||
)
|
)
|
||||||
@ -57,7 +57,7 @@ func (s *Server) AddMyPasswordless(ctx context.Context, _ *auth_pb.AddMyPassword
|
|||||||
|
|
||||||
func (s *Server) AddMyPasswordlessLink(ctx context.Context, _ *auth_pb.AddMyPasswordlessLinkRequest) (*auth_pb.AddMyPasswordlessLinkResponse, error) {
|
func (s *Server) AddMyPasswordlessLink(ctx context.Context, _ *auth_pb.AddMyPasswordlessLinkRequest) (*auth_pb.AddMyPasswordlessLinkResponse, error) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
ctxData := authz.GetCtxData(ctx)
|
||||||
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
|
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ func (s *Server) AddMyPasswordlessLink(ctx context.Context, _ *auth_pb.AddMyPass
|
|||||||
|
|
||||||
func (s *Server) SendMyPasswordlessLink(ctx context.Context, _ *auth_pb.SendMyPasswordlessLinkRequest) (*auth_pb.SendMyPasswordlessLinkResponse, error) {
|
func (s *Server) SendMyPasswordlessLink(ctx context.Context, _ *auth_pb.SendMyPasswordlessLinkRequest) (*auth_pb.SendMyPasswordlessLinkResponse, error) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
ctxData := authz.GetCtxData(ctx)
|
||||||
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
|
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ func (s *Server) GetMyPhone(ctx context.Context, _ *auth_pb.GetMyPhoneRequest) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) SetMyPhone(ctx context.Context, req *auth_pb.SetMyPhoneRequest) (*auth_pb.SetMyPhoneResponse, error) {
|
func (s *Server) SetMyPhone(ctx context.Context, req *auth_pb.SetMyPhoneRequest) (*auth_pb.SetMyPhoneResponse, error) {
|
||||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
|
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ func (s *Server) SetMyPhone(ctx context.Context, req *auth_pb.SetMyPhoneRequest)
|
|||||||
|
|
||||||
func (s *Server) VerifyMyPhone(ctx context.Context, req *auth_pb.VerifyMyPhoneRequest) (*auth_pb.VerifyMyPhoneResponse, error) {
|
func (s *Server) VerifyMyPhone(ctx context.Context, req *auth_pb.VerifyMyPhoneRequest) (*auth_pb.VerifyMyPhoneResponse, error) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
ctxData := authz.GetCtxData(ctx)
|
||||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
|
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ func (s *Server) VerifyMyPhone(ctx context.Context, req *auth_pb.VerifyMyPhoneRe
|
|||||||
|
|
||||||
func (s *Server) ResendMyPhoneVerification(ctx context.Context, _ *auth_pb.ResendMyPhoneVerificationRequest) (*auth_pb.ResendMyPhoneVerificationResponse, error) {
|
func (s *Server) ResendMyPhoneVerification(ctx context.Context, _ *auth_pb.ResendMyPhoneVerificationRequest) (*auth_pb.ResendMyPhoneVerificationResponse, error) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
ctxData := authz.GetCtxData(ctx)
|
||||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
|
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
@ -10,6 +9,7 @@ import (
|
|||||||
"github.com/caos/zitadel/internal/auth/repository/eventsourcing"
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing"
|
||||||
"github.com/caos/zitadel/internal/command"
|
"github.com/caos/zitadel/internal/command"
|
||||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
"github.com/caos/zitadel/internal/query"
|
"github.com/caos/zitadel/internal/query"
|
||||||
"github.com/caos/zitadel/pkg/grpc/auth"
|
"github.com/caos/zitadel/pkg/grpc/auth"
|
||||||
)
|
)
|
||||||
@ -27,21 +27,27 @@ type Server struct {
|
|||||||
repo repository.Repository
|
repo repository.Repository
|
||||||
defaults systemdefaults.SystemDefaults
|
defaults systemdefaults.SystemDefaults
|
||||||
assetsAPIDomain string
|
assetsAPIDomain string
|
||||||
UserCodeAlg crypto.EncryptionAlgorithm
|
userCodeAlg crypto.EncryptionAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Repository eventsourcing.Config
|
Repository eventsourcing.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateServer(command *command.Commands, query *query.Queries, authRepo repository.Repository, defaults systemdefaults.SystemDefaults, assetsAPIDomain string, userCrypto *crypto.AESCrypto) *Server {
|
func CreateServer(command *command.Commands,
|
||||||
|
query *query.Queries,
|
||||||
|
authRepo repository.Repository,
|
||||||
|
defaults systemdefaults.SystemDefaults,
|
||||||
|
assetsAPIDomain string,
|
||||||
|
userCodeAlg crypto.EncryptionAlgorithm,
|
||||||
|
) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
command: command,
|
command: command,
|
||||||
query: query,
|
query: query,
|
||||||
repo: authRepo,
|
repo: authRepo,
|
||||||
defaults: defaults,
|
defaults: defaults,
|
||||||
assetsAPIDomain: assetsAPIDomain,
|
assetsAPIDomain: assetsAPIDomain,
|
||||||
UserCodeAlg: userCrypto,
|
userCodeAlg: userCodeAlg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ func (s *Server) ListAppChanges(ctx context.Context, req *mgmt_pb.ListAppChanges
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) AddOIDCApp(ctx context.Context, req *mgmt_pb.AddOIDCAppRequest) (*mgmt_pb.AddOIDCAppResponse, error) {
|
func (s *Server) AddOIDCApp(ctx context.Context, req *mgmt_pb.AddOIDCAppRequest) (*mgmt_pb.AddOIDCAppResponse, error) {
|
||||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.PasswordHashAlg)
|
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ func (s *Server) AddOIDCApp(ctx context.Context, req *mgmt_pb.AddOIDCAppRequest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) AddAPIApp(ctx context.Context, req *mgmt_pb.AddAPIAppRequest) (*mgmt_pb.AddAPIAppResponse, error) {
|
func (s *Server) AddAPIApp(ctx context.Context, req *mgmt_pb.AddAPIAppRequest) (*mgmt_pb.AddAPIAppResponse, error) {
|
||||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.PasswordHashAlg)
|
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ func (s *Server) RemoveApp(ctx context.Context, req *mgmt_pb.RemoveAppRequest) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, req *mgmt_pb.RegenerateOIDCClientSecretRequest) (*mgmt_pb.RegenerateOIDCClientSecretResponse, error) {
|
func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, req *mgmt_pb.RegenerateOIDCClientSecretRequest) (*mgmt_pb.RegenerateOIDCClientSecretResponse, error) {
|
||||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.PasswordHashAlg)
|
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -181,7 +181,7 @@ func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, req *mgmt_pb.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) RegenerateAPIClientSecret(ctx context.Context, req *mgmt_pb.RegenerateAPIClientSecretRequest) (*mgmt_pb.RegenerateAPIClientSecretResponse, error) {
|
func (s *Server) RegenerateAPIClientSecret(ctx context.Context, req *mgmt_pb.RegenerateAPIClientSecretRequest) (*mgmt_pb.RegenerateAPIClientSecretResponse, error) {
|
||||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.PasswordHashAlg)
|
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package management
|
package management
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/server"
|
"github.com/caos/zitadel/internal/api/grpc/server"
|
||||||
"github.com/caos/zitadel/internal/command"
|
"github.com/caos/zitadel/internal/command"
|
||||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
"github.com/caos/zitadel/internal/query"
|
"github.com/caos/zitadel/internal/query"
|
||||||
"github.com/caos/zitadel/pkg/grpc/management"
|
"github.com/caos/zitadel/pkg/grpc/management"
|
||||||
)
|
)
|
||||||
@ -24,18 +24,23 @@ type Server struct {
|
|||||||
query *query.Queries
|
query *query.Queries
|
||||||
systemDefaults systemdefaults.SystemDefaults
|
systemDefaults systemdefaults.SystemDefaults
|
||||||
assetAPIPrefix string
|
assetAPIPrefix string
|
||||||
PasswordHashAlg crypto.HashAlgorithm
|
passwordHashAlg crypto.HashAlgorithm
|
||||||
UserCodeAlg crypto.EncryptionAlgorithm
|
userCodeAlg crypto.EncryptionAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateServer(command *command.Commands, query *query.Queries, sd systemdefaults.SystemDefaults, assetAPIPrefix string, userCrypto *crypto.AESCrypto) *Server {
|
func CreateServer(command *command.Commands,
|
||||||
|
query *query.Queries,
|
||||||
|
sd systemdefaults.SystemDefaults,
|
||||||
|
assetAPIPrefix string,
|
||||||
|
userCodeAlg crypto.EncryptionAlgorithm,
|
||||||
|
) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
command: command,
|
command: command,
|
||||||
query: query,
|
query: query,
|
||||||
systemDefaults: sd,
|
systemDefaults: sd,
|
||||||
assetAPIPrefix: assetAPIPrefix,
|
assetAPIPrefix: assetAPIPrefix,
|
||||||
PasswordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost),
|
passwordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost),
|
||||||
UserCodeAlg: userCrypto,
|
userCodeAlg: userCodeAlg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,11 +192,11 @@ func (s *Server) BulkRemoveUserMetadata(ctx context.Context, req *mgmt_pb.BulkRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) AddHumanUser(ctx context.Context, req *mgmt_pb.AddHumanUserRequest) (*mgmt_pb.AddHumanUserResponse, error) {
|
func (s *Server) AddHumanUser(ctx context.Context, req *mgmt_pb.AddHumanUserRequest) (*mgmt_pb.AddHumanUserResponse, error) {
|
||||||
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.UserCodeAlg)
|
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
|
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -216,15 +216,15 @@ func (s *Server) AddHumanUser(ctx context.Context, req *mgmt_pb.AddHumanUserRequ
|
|||||||
|
|
||||||
func (s *Server) ImportHumanUser(ctx context.Context, req *mgmt_pb.ImportHumanUserRequest) (*mgmt_pb.ImportHumanUserResponse, error) {
|
func (s *Server) ImportHumanUser(ctx context.Context, req *mgmt_pb.ImportHumanUserRequest) (*mgmt_pb.ImportHumanUserResponse, error) {
|
||||||
human, passwordless := ImportHumanUserRequestToDomain(req)
|
human, passwordless := ImportHumanUserRequestToDomain(req)
|
||||||
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.UserCodeAlg)
|
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
|
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
|
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -408,7 +408,7 @@ func (s *Server) GetHumanEmail(ctx context.Context, req *mgmt_pb.GetHumanEmailRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) UpdateHumanEmail(ctx context.Context, req *mgmt_pb.UpdateHumanEmailRequest) (*mgmt_pb.UpdateHumanEmailResponse, error) {
|
func (s *Server) UpdateHumanEmail(ctx context.Context, req *mgmt_pb.UpdateHumanEmailRequest) (*mgmt_pb.UpdateHumanEmailResponse, error) {
|
||||||
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
|
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -426,7 +426,7 @@ func (s *Server) UpdateHumanEmail(ctx context.Context, req *mgmt_pb.UpdateHumanE
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.ResendHumanInitializationRequest) (*mgmt_pb.ResendHumanInitializationResponse, error) {
|
func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.ResendHumanInitializationRequest) (*mgmt_pb.ResendHumanInitializationResponse, error) {
|
||||||
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.UserCodeAlg)
|
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -440,7 +440,7 @@ func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ResendHumanEmailVerification(ctx context.Context, req *mgmt_pb.ResendHumanEmailVerificationRequest) (*mgmt_pb.ResendHumanEmailVerificationResponse, error) {
|
func (s *Server) ResendHumanEmailVerification(ctx context.Context, req *mgmt_pb.ResendHumanEmailVerificationRequest) (*mgmt_pb.ResendHumanEmailVerificationResponse, error) {
|
||||||
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
|
emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -474,7 +474,7 @@ func (s *Server) GetHumanPhone(ctx context.Context, req *mgmt_pb.GetHumanPhoneRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) UpdateHumanPhone(ctx context.Context, req *mgmt_pb.UpdateHumanPhoneRequest) (*mgmt_pb.UpdateHumanPhoneResponse, error) {
|
func (s *Server) UpdateHumanPhone(ctx context.Context, req *mgmt_pb.UpdateHumanPhoneRequest) (*mgmt_pb.UpdateHumanPhoneResponse, error) {
|
||||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
|
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -502,7 +502,7 @@ func (s *Server) RemoveHumanPhone(ctx context.Context, req *mgmt_pb.RemoveHumanP
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ResendHumanPhoneVerification(ctx context.Context, req *mgmt_pb.ResendHumanPhoneVerificationRequest) (*mgmt_pb.ResendHumanPhoneVerificationResponse, error) {
|
func (s *Server) ResendHumanPhoneVerification(ctx context.Context, req *mgmt_pb.ResendHumanPhoneVerificationRequest) (*mgmt_pb.ResendHumanPhoneVerificationResponse, error) {
|
||||||
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
|
phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -547,7 +547,7 @@ func (s *Server) SetHumanPassword(ctx context.Context, req *mgmt_pb.SetHumanPass
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) SendHumanResetPasswordNotification(ctx context.Context, req *mgmt_pb.SendHumanResetPasswordNotificationRequest) (*mgmt_pb.SendHumanResetPasswordNotificationResponse, error) {
|
func (s *Server) SendHumanResetPasswordNotification(ctx context.Context, req *mgmt_pb.SendHumanResetPasswordNotificationRequest) (*mgmt_pb.SendHumanResetPasswordNotificationResponse, error) {
|
||||||
passwordCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordResetCode, s.UserCodeAlg)
|
passwordCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordResetCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -628,7 +628,7 @@ func (s *Server) ListHumanPasswordless(ctx context.Context, req *mgmt_pb.ListHum
|
|||||||
|
|
||||||
func (s *Server) AddPasswordlessRegistration(ctx context.Context, req *mgmt_pb.AddPasswordlessRegistrationRequest) (*mgmt_pb.AddPasswordlessRegistrationResponse, error) {
|
func (s *Server) AddPasswordlessRegistration(ctx context.Context, req *mgmt_pb.AddPasswordlessRegistrationRequest) (*mgmt_pb.AddPasswordlessRegistrationResponse, error) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
ctxData := authz.GetCtxData(ctx)
|
||||||
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
|
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -645,7 +645,7 @@ func (s *Server) AddPasswordlessRegistration(ctx context.Context, req *mgmt_pb.A
|
|||||||
|
|
||||||
func (s *Server) SendPasswordlessRegistration(ctx context.Context, req *mgmt_pb.SendPasswordlessRegistrationRequest) (*mgmt_pb.SendPasswordlessRegistrationResponse, error) {
|
func (s *Server) SendPasswordlessRegistration(ctx context.Context, req *mgmt_pb.SendPasswordlessRegistrationRequest) (*mgmt_pb.SendPasswordlessRegistrationResponse, error) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
ctxData := authz.GetCtxData(ctx)
|
||||||
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
|
passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
http_utils "github.com/caos/zitadel/internal/api/http"
|
http_utils "github.com/caos/zitadel/internal/api/http"
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
|
||||||
"github.com/caos/zitadel/internal/errors"
|
"github.com/caos/zitadel/internal/errors"
|
||||||
"github.com/caos/zitadel/internal/id"
|
"github.com/caos/zitadel/internal/id"
|
||||||
)
|
)
|
||||||
@ -35,16 +34,10 @@ type userAgentHandler struct {
|
|||||||
|
|
||||||
type UserAgentCookieConfig struct {
|
type UserAgentCookieConfig struct {
|
||||||
Name string
|
Name string
|
||||||
Key *crypto.KeyConfig
|
|
||||||
MaxAge time.Duration
|
MaxAge time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserAgentHandler(config *UserAgentCookieConfig, domain string, idGenerator id.Generator, externalSecure bool) (func(http.Handler) http.Handler, error) {
|
func NewUserAgentHandler(config *UserAgentCookieConfig, cookieKey []byte, domain string, idGenerator id.Generator, externalSecure bool) (func(http.Handler) http.Handler, error) {
|
||||||
key, err := crypto.LoadKey(config.Key, config.Key.EncryptionKeyID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cookieKey := []byte(key)
|
|
||||||
opts := []http_utils.CookieHandlerOpt{
|
opts := []http_utils.CookieHandlerOpt{
|
||||||
http_utils.WithEncryption(cookieKey, cookieKey),
|
http_utils.WithEncryption(cookieKey, cookieKey),
|
||||||
http_utils.WithDomain(domain),
|
http_utils.WithDomain(domain),
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/caos/zitadel/internal/command"
|
"github.com/caos/zitadel/internal/command"
|
||||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
"github.com/caos/zitadel/internal/eventstore/handler/crdb"
|
"github.com/caos/zitadel/internal/eventstore/handler/crdb"
|
||||||
"github.com/caos/zitadel/internal/i18n"
|
"github.com/caos/zitadel/internal/i18n"
|
||||||
@ -44,7 +45,6 @@ type Config struct {
|
|||||||
DefaultRefreshTokenExpiration time.Duration
|
DefaultRefreshTokenExpiration time.Duration
|
||||||
UserAgentCookieConfig *middleware.UserAgentCookieConfig
|
UserAgentCookieConfig *middleware.UserAgentCookieConfig
|
||||||
Cache *middleware.CacheConfig
|
Cache *middleware.CacheConfig
|
||||||
KeyConfig *crypto.KeyConfig
|
|
||||||
CustomEndpoints *EndpointConfig
|
CustomEndpoints *EndpointConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,18 +83,15 @@ type OPStorage struct {
|
|||||||
assetAPIPrefix string
|
assetAPIPrefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProvider(ctx context.Context, config Config, issuer, defaultLogoutRedirectURI string, command *command.Commands, query *query.Queries, repo repository.Repository, keyConfig systemdefaults.KeyConfig, es *eventstore.Eventstore, projections *sql.DB, keyChan <-chan interface{}, userAgentCookie func(http.Handler) http.Handler) (op.OpenIDProvider, error) {
|
func NewProvider(ctx context.Context, config Config, issuer, defaultLogoutRedirectURI string, command *command.Commands, query *query.Queries, repo repository.Repository, keyConfig systemdefaults.KeyConfig, encryptionAlg crypto.EncryptionAlgorithm, cryptoKey []byte, es *eventstore.Eventstore, projections *sql.DB, keyChan <-chan interface{}, userAgentCookie func(http.Handler) http.Handler) (op.OpenIDProvider, error) {
|
||||||
opConfig, err := createOPConfig(config, issuer, defaultLogoutRedirectURI)
|
opConfig, err := createOPConfig(config, issuer, defaultLogoutRedirectURI, cryptoKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create op config: %w", err)
|
return nil, caos_errs.ThrowInternal(err, "OIDC-EGrqd", "cannot create op config: %w")
|
||||||
}
|
|
||||||
storage, err := newStorage(config, command, query, repo, keyConfig, config.KeyConfig, es, projections, keyChan)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cannot create storage: %w", err)
|
|
||||||
}
|
}
|
||||||
|
storage := newStorage(config, command, query, repo, keyConfig, encryptionAlg, es, projections, keyChan)
|
||||||
options, err := createOptions(config, userAgentCookie)
|
options, err := createOptions(config, userAgentCookie)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create options: %w", err)
|
return nil, caos_errs.ThrowInternal(err, "OIDC-D3gq1", "cannot create options: %w")
|
||||||
}
|
}
|
||||||
provider, err := op.NewOpenIDProvider(
|
provider, err := op.NewOpenIDProvider(
|
||||||
ctx,
|
ctx,
|
||||||
@ -103,7 +100,7 @@ func NewProvider(ctx context.Context, config Config, issuer, defaultLogoutRedire
|
|||||||
options...,
|
options...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create provider: %w", err)
|
return nil, caos_errs.ThrowInternal(err, "OIDC-DAtg3", "cannot create provider: %w")
|
||||||
}
|
}
|
||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
@ -112,7 +109,7 @@ func Issuer(domain string, port uint16, externalSecure bool) string {
|
|||||||
return http_utils.BuildHTTP(domain, port, externalSecure) + HandlerPrefix
|
return http_utils.BuildHTTP(domain, port, externalSecure) + HandlerPrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
func createOPConfig(config Config, issuer, defaultLogoutRedirectURI string) (*op.Config, error) {
|
func createOPConfig(config Config, issuer, defaultLogoutRedirectURI string, cryptoKey []byte) (*op.Config, error) {
|
||||||
supportedLanguages, err := getSupportedLanguages()
|
supportedLanguages, err := getSupportedLanguages()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -127,25 +124,13 @@ func createOPConfig(config Config, issuer, defaultLogoutRedirectURI string) (*op
|
|||||||
RequestObjectSupported: config.RequestObjectSupported,
|
RequestObjectSupported: config.RequestObjectSupported,
|
||||||
SupportedUILocales: supportedLanguages,
|
SupportedUILocales: supportedLanguages,
|
||||||
}
|
}
|
||||||
if err := cryptoKey(opConfig, config.KeyConfig); err != nil {
|
if cryptoLength := len(cryptoKey); cryptoLength != 32 {
|
||||||
return nil, err
|
return nil, caos_errs.ThrowInternalf(nil, "OIDC-D43gf", "crypto key must be 32 bytes, but is %d", cryptoLength)
|
||||||
}
|
}
|
||||||
|
copy(opConfig.CryptoKey[:], cryptoKey)
|
||||||
return opConfig, nil
|
return opConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cryptoKey(config *op.Config, keyConfig *crypto.KeyConfig) error {
|
|
||||||
tokenKey, err := crypto.LoadKey(keyConfig, keyConfig.EncryptionKeyID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot load OP crypto key: %w", err)
|
|
||||||
}
|
|
||||||
cryptoKey := []byte(tokenKey)
|
|
||||||
if len(cryptoKey) != 32 {
|
|
||||||
return fmt.Errorf("OP crypto key must be exactly 32 bytes")
|
|
||||||
}
|
|
||||||
copy(config.CryptoKey[:], cryptoKey)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createOptions(config Config, userAgentCookie func(http.Handler) http.Handler) ([]op.Option, error) {
|
func createOptions(config Config, userAgentCookie func(http.Handler) http.Handler) ([]op.Option, error) {
|
||||||
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
|
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
|
||||||
interceptor := op.WithHttpInterceptors(
|
interceptor := op.WithHttpInterceptors(
|
||||||
@ -191,11 +176,7 @@ func customEndpoints(endpointConfig *EndpointConfig) []op.Option {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStorage(config Config, command *command.Commands, query *query.Queries, repo repository.Repository, keyConfig systemdefaults.KeyConfig, c *crypto.KeyConfig, es *eventstore.Eventstore, projections *sql.DB, keyChan <-chan interface{}) (*OPStorage, error) {
|
func newStorage(config Config, command *command.Commands, query *query.Queries, repo repository.Repository, keyConfig systemdefaults.KeyConfig, encAlg crypto.EncryptionAlgorithm, es *eventstore.Eventstore, projections *sql.DB, keyChan <-chan interface{}) *OPStorage {
|
||||||
encAlg, err := crypto.NewAESCrypto(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &OPStorage{
|
return &OPStorage{
|
||||||
repo: repo,
|
repo: repo,
|
||||||
command: command,
|
command: command,
|
||||||
@ -213,7 +194,7 @@ func newStorage(config Config, command *command.Commands, query *query.Queries,
|
|||||||
locker: crdb.NewLocker(projections, locksTable, signingKey),
|
locker: crdb.NewLocker(projections, locksTable, signingKey),
|
||||||
keyChan: keyChan,
|
keyChan: keyChan,
|
||||||
assetAPIPrefix: assets.HandlerPrefix,
|
assetAPIPrefix: assets.HandlerPrefix,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OPStorage) Health(ctx context.Context) error {
|
func (o *OPStorage) Health(ctx context.Context) error {
|
||||||
|
@ -74,7 +74,7 @@ func (l *Login) handleExternalLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
http.Redirect(w, r, l.zitadelURL, http.StatusFound)
|
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.handleIDP(w, r, authReq, data.IDPConfigID)
|
l.handleIDP(w, r, authReq, data.IDPConfigID)
|
||||||
@ -121,7 +121,7 @@ func (l *Login) handleJWTAuthorize(w http.ResponseWriter, r *http.Request, authR
|
|||||||
l.renderLogin(w, r, authReq, caos_errors.ThrowPreconditionFailed(nil, "LOGIN-dsgg3", "Errors.AuthRequest.UserAgentNotFound"))
|
l.renderLogin(w, r, authReq, caos_errors.ThrowPreconditionFailed(nil, "LOGIN-dsgg3", "Errors.AuthRequest.UserAgentNotFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nonce, err := l.IDPConfigAesCrypto.Encrypt([]byte(userAgentID))
|
nonce, err := l.idpConfigAlg.Encrypt([]byte(userAgentID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderLogin(w, r, authReq, err)
|
l.renderLogin(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
@ -167,7 +167,7 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *Login) getRPConfig(idpConfig *iam_model.IDPConfigView, callbackEndpoint string) (rp.RelyingParty, error) {
|
func (l *Login) getRPConfig(idpConfig *iam_model.IDPConfigView, callbackEndpoint string) (rp.RelyingParty, error) {
|
||||||
oidcClientSecret, err := crypto.DecryptString(idpConfig.OIDCClientSecret, l.IDPConfigAesCrypto)
|
oidcClientSecret, err := crypto.DecryptString(idpConfig.OIDCClientSecret, l.idpConfigAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func (l *Login) handleExternalRegister(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
http.Redirect(w, r, l.zitadelURL, http.StatusFound)
|
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
idpConfig, err := l.getIDPConfigByID(r, data.IDPConfigID)
|
idpConfig, err := l.getIDPConfigByID(r, data.IDPConfigID)
|
||||||
@ -145,12 +145,12 @@ func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, aut
|
|||||||
memberRoles = nil
|
memberRoles = nil
|
||||||
resourceOwner = authReq.RequestedOrgID
|
resourceOwner = authReq.RequestedOrgID
|
||||||
}
|
}
|
||||||
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
|
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegisterOption(w, r, authReq, err)
|
l.renderRegisterOption(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.UserCodeAlg)
|
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegisterOption(w, r, authReq, err)
|
l.renderRegisterOption(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
@ -226,12 +226,12 @@ func (l *Login) handleExternalRegisterCheck(w http.ResponseWriter, r *http.Reque
|
|||||||
l.renderRegisterOption(w, r, authReq, err)
|
l.renderRegisterOption(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
|
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegisterOption(w, r, authReq, err)
|
l.renderRegisterOption(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.UserCodeAlg)
|
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegisterOption(w, r, authReq, err)
|
l.renderRegisterOption(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
|
@ -70,7 +70,7 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *dom
|
|||||||
userOrg = authReq.UserOrgID
|
userOrg = authReq.UserOrgID
|
||||||
}
|
}
|
||||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||||
passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.UserCodeAlg)
|
passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderInitPassword(w, r, authReq, data.UserID, "", err)
|
l.renderInitPassword(w, r, authReq, data.UserID, "", err)
|
||||||
return
|
return
|
||||||
@ -97,7 +97,7 @@ func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authRe
|
|||||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.UserCodeAlg)
|
passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||||
return
|
return
|
||||||
|
@ -73,7 +73,7 @@ func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authRe
|
|||||||
if authReq != nil {
|
if authReq != nil {
|
||||||
userOrgID = authReq.UserOrgID
|
userOrgID = authReq.UserOrgID
|
||||||
}
|
}
|
||||||
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
|
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err)
|
l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err)
|
||||||
return
|
return
|
||||||
@ -91,7 +91,7 @@ func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *
|
|||||||
if authReq != nil {
|
if authReq != nil {
|
||||||
userOrgID = authReq.UserOrgID
|
userOrgID = authReq.UserOrgID
|
||||||
}
|
}
|
||||||
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
|
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderInitUser(w, r, authReq, userID, "", showPassword, err)
|
l.renderInitUser(w, r, authReq, userID, "", showPassword, err)
|
||||||
return
|
return
|
||||||
|
@ -39,7 +39,7 @@ func (l *Login) handleJWTRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.renderError(w, r, nil, err)
|
l.renderError(w, r, nil, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userAgentID, err := l.IDPConfigAesCrypto.DecryptString(id, l.IDPConfigAesCrypto.EncryptionKeyID())
|
userAgentID, err := l.idpConfigAlg.DecryptString(id, l.idpConfigAlg.EncryptionKeyID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderError(w, r, nil, err)
|
l.renderError(w, r, nil, err)
|
||||||
return
|
return
|
||||||
@ -181,7 +181,7 @@ func (l *Login) redirectToJWTCallback(authReq *domain.AuthRequest) (string, erro
|
|||||||
}
|
}
|
||||||
q := redirect.Query()
|
q := redirect.Query()
|
||||||
q.Set(QueryAuthRequestID, authReq.ID)
|
q.Set(QueryAuthRequestID, authReq.ID)
|
||||||
nonce, err := l.IDPConfigAesCrypto.Encrypt([]byte(authReq.AgentID))
|
nonce, err := l.idpConfigAlg.Encrypt([]byte(authReq.AgentID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -202,7 +202,7 @@ func (l *Login) handleJWTCallback(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.renderError(w, r, nil, err)
|
l.renderError(w, r, nil, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userAgentID, err := l.IDPConfigAesCrypto.DecryptString(id, l.IDPConfigAesCrypto.EncryptionKeyID())
|
userAgentID, err := l.idpConfigAlg.DecryptString(id, l.idpConfigAlg.EncryptionKeyID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderError(w, r, nil, err)
|
l.renderError(w, r, nil, err)
|
||||||
return
|
return
|
||||||
|
@ -35,47 +35,54 @@ type Login struct {
|
|||||||
//staticCache cache.Cache //TODO: enable when storage is implemented again
|
//staticCache cache.Cache //TODO: enable when storage is implemented again
|
||||||
authRepo auth_repository.Repository
|
authRepo auth_repository.Repository
|
||||||
baseURL string
|
baseURL string
|
||||||
zitadelURL string
|
consolePath string
|
||||||
oidcAuthCallbackURL string
|
oidcAuthCallbackURL string
|
||||||
IDPConfigAesCrypto crypto.EncryptionAlgorithm
|
idpConfigAlg crypto.EncryptionAlgorithm
|
||||||
UserCodeAlg crypto.EncryptionAlgorithm
|
userCodeAlg crypto.EncryptionAlgorithm
|
||||||
iamDomain string
|
iamDomain string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
LanguageCookieName string
|
LanguageCookieName string
|
||||||
CSRF CSRF
|
CSRFCookieName string
|
||||||
Cache middleware.CacheConfig
|
Cache middleware.CacheConfig
|
||||||
//StaticCache cache_config.CacheConfig //TODO: enable when storage is implemented again
|
//StaticCache cache_config.CacheConfig //TODO: enable when storage is implemented again
|
||||||
}
|
}
|
||||||
|
|
||||||
type CSRF struct {
|
|
||||||
CookieName string
|
|
||||||
Key *crypto.KeyConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
login = "LOGIN"
|
login = "LOGIN"
|
||||||
HandlerPrefix = "/ui/login"
|
HandlerPrefix = "/ui/login"
|
||||||
DefaultLoggedOutPath = HandlerPrefix + EndpointLogoutDone
|
DefaultLoggedOutPath = HandlerPrefix + EndpointLogoutDone
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateLogin(config Config, command *command.Commands, query *query.Queries, authRepo *eventsourcing.EsRepository, staticStorage static.Storage, systemDefaults systemdefaults.SystemDefaults, zitadelURL, domain, oidcAuthCallbackURL string, externalSecure bool, userAgentCookie mux.MiddlewareFunc, userCrypto *crypto.AESCrypto) (*Login, error) {
|
func CreateLogin(config Config,
|
||||||
aesCrypto, err := crypto.NewAESCrypto(systemDefaults.IDPConfigVerificationKey)
|
command *command.Commands,
|
||||||
if err != nil {
|
query *query.Queries,
|
||||||
return nil, fmt.Errorf("error create new aes crypto: %w", err)
|
authRepo *eventsourcing.EsRepository,
|
||||||
}
|
staticStorage static.Storage,
|
||||||
|
systemDefaults systemdefaults.SystemDefaults,
|
||||||
|
consolePath,
|
||||||
|
domain,
|
||||||
|
baseURL,
|
||||||
|
oidcAuthCallbackURL string,
|
||||||
|
externalSecure bool,
|
||||||
|
userAgentCookie mux.MiddlewareFunc,
|
||||||
|
userCodeAlg crypto.EncryptionAlgorithm,
|
||||||
|
idpConfigAlg crypto.EncryptionAlgorithm,
|
||||||
|
csrfCookieKey []byte,
|
||||||
|
) (*Login, error) {
|
||||||
|
|
||||||
login := &Login{
|
login := &Login{
|
||||||
oidcAuthCallbackURL: oidcAuthCallbackURL,
|
oidcAuthCallbackURL: oidcAuthCallbackURL,
|
||||||
baseURL: HandlerPrefix,
|
baseURL: baseURL + HandlerPrefix,
|
||||||
zitadelURL: zitadelURL,
|
consolePath: consolePath,
|
||||||
command: command,
|
command: command,
|
||||||
query: query,
|
query: query,
|
||||||
staticStorage: staticStorage,
|
staticStorage: staticStorage,
|
||||||
authRepo: authRepo,
|
authRepo: authRepo,
|
||||||
IDPConfigAesCrypto: aesCrypto,
|
|
||||||
iamDomain: domain,
|
iamDomain: domain,
|
||||||
UserCodeAlg: userCrypto,
|
idpConfigAlg: idpConfigAlg,
|
||||||
|
userCodeAlg: userCodeAlg,
|
||||||
}
|
}
|
||||||
//TODO: enable when storage is implemented again
|
//TODO: enable when storage is implemented again
|
||||||
//login.staticCache, err = config.StaticCache.Config.NewCache()
|
//login.staticCache, err = config.StaticCache.Config.NewCache()
|
||||||
@ -88,7 +95,7 @@ func CreateLogin(config Config, command *command.Commands, query *query.Queries,
|
|||||||
return nil, fmt.Errorf("unable to create filesystem: %w", err)
|
return nil, fmt.Errorf("unable to create filesystem: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
csrfInterceptor, err := createCSRFInterceptor(config.CSRF, externalSecure, login.csrfErrorHandler())
|
csrfInterceptor, err := createCSRFInterceptor(config.CSRFCookieName, csrfCookieKey, externalSecure, login.csrfErrorHandler())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to create csrfInterceptor: %w", err)
|
return nil, fmt.Errorf("unable to create csrfInterceptor: %w", err)
|
||||||
}
|
}
|
||||||
@ -111,15 +118,11 @@ func csp() *middleware.CSP {
|
|||||||
return &csp
|
return &csp
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCSRFInterceptor(config CSRF, externalSecure bool, errorHandler http.Handler) (func(http.Handler) http.Handler, error) {
|
func createCSRFInterceptor(cookieName string, csrfCookieKey []byte, externalSecure bool, errorHandler http.Handler) (func(http.Handler) http.Handler, error) {
|
||||||
csrfKey, err := crypto.LoadKey(config.Key, config.Key.EncryptionKeyID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
path := "/"
|
path := "/"
|
||||||
return csrf.Protect([]byte(csrfKey),
|
return csrf.Protect(csrfCookieKey,
|
||||||
csrf.Secure(externalSecure),
|
csrf.Secure(externalSecure),
|
||||||
csrf.CookieName(http_utils.SetCookiePrefix(config.CookieName, "", path, externalSecure)),
|
csrf.CookieName(http_utils.SetCookiePrefix(cookieName, "", path, externalSecure)),
|
||||||
csrf.Path(path),
|
csrf.Path(path),
|
||||||
csrf.ErrorHandler(errorHandler),
|
csrf.ErrorHandler(errorHandler),
|
||||||
), nil
|
), nil
|
||||||
|
@ -24,7 +24,7 @@ func (l *Login) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
http.Redirect(w, r, l.zitadelURL, http.StatusFound)
|
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.renderNextStep(w, r, authReq)
|
l.renderNextStep(w, r, authReq)
|
||||||
|
@ -51,7 +51,7 @@ func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Reque
|
|||||||
if authReq != nil {
|
if authReq != nil {
|
||||||
userOrg = authReq.UserOrgID
|
userOrg = authReq.UserOrgID
|
||||||
}
|
}
|
||||||
emailCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyEmailCode, l.UserCodeAlg)
|
emailCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyEmailCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.checkMailCode(w, r, authReq, data.UserID, data.Code)
|
l.checkMailCode(w, r, authReq, data.UserID, data.Code)
|
||||||
return
|
return
|
||||||
@ -66,7 +66,7 @@ func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *d
|
|||||||
userID = authReq.UserID
|
userID = authReq.UserID
|
||||||
userOrg = authReq.UserOrgID
|
userOrg = authReq.UserOrgID
|
||||||
}
|
}
|
||||||
emailCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyEmailCode, l.UserCodeAlg)
|
emailCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyEmailCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderMailVerification(w, r, authReq, userID, err)
|
l.renderMailVerification(w, r, authReq, userID, err)
|
||||||
return
|
return
|
||||||
|
@ -27,7 +27,7 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.renderPasswordResetDone(w, r, authReq, err)
|
l.renderPasswordResetDone(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.UserCodeAlg)
|
passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderPasswordResetDone(w, r, authReq, err)
|
l.renderPasswordResetDone(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
|
@ -74,12 +74,12 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
memberRoles = nil
|
memberRoles = nil
|
||||||
resourceOwner = authRequest.RequestedOrgID
|
resourceOwner = authRequest.RequestedOrgID
|
||||||
}
|
}
|
||||||
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
|
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegister(w, r, authRequest, data, err)
|
l.renderRegister(w, r, authRequest, data, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.UserCodeAlg)
|
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegister(w, r, authRequest, data, err)
|
l.renderRegister(w, r, authRequest, data, err)
|
||||||
return
|
return
|
||||||
@ -90,7 +90,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authRequest == nil {
|
if authRequest == nil {
|
||||||
http.Redirect(w, r, l.zitadelURL, http.StatusFound)
|
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||||
|
@ -65,12 +65,12 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.renderRegisterOrg(w, r, authRequest, data, err)
|
l.renderRegisterOrg(w, r, authRequest, data, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordlessInitCode, l.UserCodeAlg)
|
initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordlessInitCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegisterOrg(w, r, authRequest, data, err)
|
l.renderRegisterOrg(w, r, authRequest, data, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.UserCodeAlg)
|
phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.userCodeAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.renderRegisterOrg(w, r, authRequest, data, err)
|
l.renderRegisterOrg(w, r, authRequest, data, err)
|
||||||
return
|
return
|
||||||
@ -81,7 +81,7 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if authRequest == nil {
|
if authRequest == nil {
|
||||||
http.Redirect(w, r, l.zitadelURL, http.StatusFound)
|
http.Redirect(w, r, l.consolePath, http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l.renderNextStep(w, r, authRequest)
|
l.renderNextStep(w, r, authRequest)
|
||||||
|
@ -5,13 +5,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
"github.com/caos/zitadel/internal/auth_request/model"
|
"github.com/caos/zitadel/internal/auth_request/model"
|
||||||
cache "github.com/caos/zitadel/internal/auth_request/repository"
|
cache "github.com/caos/zitadel/internal/auth_request/repository"
|
||||||
"github.com/caos/zitadel/internal/command"
|
"github.com/caos/zitadel/internal/command"
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
"github.com/caos/zitadel/internal/domain"
|
"github.com/caos/zitadel/internal/domain"
|
||||||
"github.com/caos/zitadel/internal/errors"
|
"github.com/caos/zitadel/internal/errors"
|
||||||
v1 "github.com/caos/zitadel/internal/eventstore/v1"
|
v1 "github.com/caos/zitadel/internal/eventstore/v1"
|
||||||
@ -666,15 +666,20 @@ func queryLoginPolicyToDomain(policy *query.LoginPolicy) *domain.LoginPolicy {
|
|||||||
CreationDate: policy.CreationDate,
|
CreationDate: policy.CreationDate,
|
||||||
ChangeDate: policy.ChangeDate,
|
ChangeDate: policy.ChangeDate,
|
||||||
},
|
},
|
||||||
Default: policy.IsDefault,
|
Default: policy.IsDefault,
|
||||||
AllowUsernamePassword: policy.AllowUsernamePassword,
|
AllowUsernamePassword: policy.AllowUsernamePassword,
|
||||||
AllowRegister: policy.AllowRegister,
|
AllowRegister: policy.AllowRegister,
|
||||||
AllowExternalIDP: policy.AllowExternalIDPs,
|
AllowExternalIDP: policy.AllowExternalIDPs,
|
||||||
ForceMFA: policy.ForceMFA,
|
ForceMFA: policy.ForceMFA,
|
||||||
SecondFactors: policy.SecondFactors,
|
SecondFactors: policy.SecondFactors,
|
||||||
MultiFactors: policy.MultiFactors,
|
MultiFactors: policy.MultiFactors,
|
||||||
PasswordlessType: policy.PasswordlessType,
|
PasswordlessType: policy.PasswordlessType,
|
||||||
HidePasswordReset: policy.HidePasswordReset,
|
HidePasswordReset: policy.HidePasswordReset,
|
||||||
|
PasswordCheckLifetime: policy.PasswordCheckLifetime,
|
||||||
|
ExternalLoginCheckLifetime: policy.ExternalLoginCheckLifetime,
|
||||||
|
MFAInitSkipLifetime: policy.MFAInitSkipLifetime,
|
||||||
|
SecondFactorCheckLifetime: policy.SecondFactorCheckLifetime,
|
||||||
|
MultiFactorCheckLifetime: policy.MultiFactorCheckLifetime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,19 +33,14 @@ type EsRepository struct {
|
|||||||
eventstore.OrgRepository
|
eventstore.OrgRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, keyConfig *crypto.KeyConfig, assetsPrefix string, userCrypto *crypto.AESCrypto) (*EsRepository, error) {
|
func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, oidcEncryption crypto.EncryptionAlgorithm, userEncryption crypto.EncryptionAlgorithm) (*EsRepository, error) {
|
||||||
es, err := v1.Start(dbClient)
|
es, err := v1.Start(dbClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keyAlgorithm, err := crypto.NewAESCrypto(keyConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
idGenerator := id.SonyFlakeGenerator
|
idGenerator := id.SonyFlakeGenerator
|
||||||
|
|
||||||
view, err := auth_view.StartView(dbClient, keyAlgorithm, queries, idGenerator, assetsPrefix)
|
view, err := auth_view.StartView(dbClient, oidcEncryption, queries, idGenerator, assetsPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -80,7 +75,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Comma
|
|||||||
AuthRequests: authReq,
|
AuthRequests: authReq,
|
||||||
View: view,
|
View: view,
|
||||||
Eventstore: es,
|
Eventstore: es,
|
||||||
UserCodeAlg: userCrypto,
|
UserCodeAlg: userEncryption,
|
||||||
UserSessionViewProvider: view,
|
UserSessionViewProvider: view,
|
||||||
UserViewProvider: view,
|
UserViewProvider: view,
|
||||||
UserCommandProvider: command,
|
UserCommandProvider: command,
|
||||||
@ -101,7 +96,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Comma
|
|||||||
View: view,
|
View: view,
|
||||||
Eventstore: es,
|
Eventstore: es,
|
||||||
SearchLimit: conf.SearchLimit,
|
SearchLimit: conf.SearchLimit,
|
||||||
KeyAlgorithm: keyAlgorithm,
|
KeyAlgorithm: oidcEncryption,
|
||||||
},
|
},
|
||||||
eventstore.UserSessionRepo{
|
eventstore.UserSessionRepo{
|
||||||
View: view,
|
View: view,
|
||||||
|
@ -14,6 +14,6 @@ type Config struct {
|
|||||||
Repository eventsourcing.Config
|
Repository eventsourcing.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(config Config, systemDefaults sd.SystemDefaults, queries *query.Queries, dbClient *sql.DB, keyConfig *crypto.KeyConfig) (repository.Repository, error) {
|
func Start(config Config, systemDefaults sd.SystemDefaults, queries *query.Queries, dbClient *sql.DB, keyEncryptionAlgorithm crypto.EncryptionAlgorithm) (repository.Repository, error) {
|
||||||
return eventsourcing.Start(config.Repository, systemDefaults, queries, dbClient, keyConfig)
|
return eventsourcing.Start(config.Repository, systemDefaults, queries, dbClient, keyEncryptionAlgorithm)
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ type EsRepository struct {
|
|||||||
eventstore.TokenVerifierRepo
|
eventstore.TokenVerifierRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(conf Config, systemDefaults sd.SystemDefaults, queries *query.Queries, dbClient *sql.DB, keyConfig *crypto.KeyConfig) (repository.Repository, error) {
|
func Start(conf Config, systemDefaults sd.SystemDefaults, queries *query.Queries, dbClient *sql.DB, keyEncryptionAlgorithm crypto.EncryptionAlgorithm) (repository.Repository, error) {
|
||||||
es, err := v1.Start(dbClient)
|
es, err := v1.Start(dbClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -40,18 +40,13 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, queries *query.Queries
|
|||||||
|
|
||||||
spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, systemDefaults)
|
spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, systemDefaults)
|
||||||
|
|
||||||
keyAlgorithm, err := crypto.NewAESCrypto(keyConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &EsRepository{
|
return &EsRepository{
|
||||||
spool,
|
spool,
|
||||||
eventstore.UserMembershipRepo{
|
eventstore.UserMembershipRepo{
|
||||||
View: view,
|
View: view,
|
||||||
},
|
},
|
||||||
eventstore.TokenVerifierRepo{
|
eventstore.TokenVerifierRepo{
|
||||||
TokenVerificationKey: keyAlgorithm,
|
TokenVerificationKey: keyEncryptionAlgorithm,
|
||||||
Eventstore: es,
|
Eventstore: es,
|
||||||
View: view,
|
View: view,
|
||||||
Query: queries,
|
Query: queries,
|
||||||
|
@ -54,28 +54,33 @@ type orgFeatureChecker interface {
|
|||||||
CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error
|
CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartCommands(
|
func StartCommands(es *eventstore.Eventstore,
|
||||||
es *eventstore.Eventstore,
|
|
||||||
defaults sd.SystemDefaults,
|
defaults sd.SystemDefaults,
|
||||||
authZConfig authz.Config,
|
authZConfig authz.Config,
|
||||||
staticStore static.Storage,
|
staticStore static.Storage,
|
||||||
authZRepo authz_repo.Repository,
|
authZRepo authz_repo.Repository,
|
||||||
keyConfig *crypto.KeyConfig,
|
|
||||||
webAuthN webauthn_helper.Config,
|
webAuthN webauthn_helper.Config,
|
||||||
smtpPasswordEncAlg crypto.EncryptionAlgorithm,
|
idpConfigEncryption,
|
||||||
smsHashAlg crypto.EncryptionAlgorithm,
|
otpEncryption,
|
||||||
|
smtpEncryption,
|
||||||
|
smsEncryption,
|
||||||
|
domainVerificationEncryption,
|
||||||
|
oidcEncryption crypto.EncryptionAlgorithm,
|
||||||
) (repo *Commands, err error) {
|
) (repo *Commands, err error) {
|
||||||
repo = &Commands{
|
repo = &Commands{
|
||||||
eventstore: es,
|
eventstore: es,
|
||||||
static: staticStore,
|
static: staticStore,
|
||||||
idGenerator: id.SonyFlakeGenerator,
|
idGenerator: id.SonyFlakeGenerator,
|
||||||
iamDomain: defaults.Domain,
|
iamDomain: defaults.Domain,
|
||||||
zitadelRoles: authZConfig.RolePermissionMappings,
|
zitadelRoles: authZConfig.RolePermissionMappings,
|
||||||
keySize: defaults.KeyConfig.Size,
|
keySize: defaults.KeyConfig.Size,
|
||||||
privateKeyLifetime: defaults.KeyConfig.PrivateKeyLifetime,
|
privateKeyLifetime: defaults.KeyConfig.PrivateKeyLifetime,
|
||||||
publicKeyLifetime: defaults.KeyConfig.PublicKeyLifetime,
|
publicKeyLifetime: defaults.KeyConfig.PublicKeyLifetime,
|
||||||
smtpPasswordCrypto: smtpPasswordEncAlg,
|
idpConfigSecretCrypto: idpConfigEncryption,
|
||||||
smsCrypto: smsHashAlg,
|
smtpPasswordCrypto: smtpEncryption,
|
||||||
|
smsCrypto: smsEncryption,
|
||||||
|
domainVerificationAlg: domainVerificationEncryption,
|
||||||
|
keyAlgorithm: oidcEncryption,
|
||||||
}
|
}
|
||||||
iam_repo.RegisterEventMappers(repo.eventstore)
|
iam_repo.RegisterEventMappers(repo.eventstore)
|
||||||
org.RegisterEventMappers(repo.eventstore)
|
org.RegisterEventMappers(repo.eventstore)
|
||||||
@ -85,30 +90,17 @@ func StartCommands(
|
|||||||
keypair.RegisterEventMappers(repo.eventstore)
|
keypair.RegisterEventMappers(repo.eventstore)
|
||||||
action.RegisterEventMappers(repo.eventstore)
|
action.RegisterEventMappers(repo.eventstore)
|
||||||
|
|
||||||
repo.idpConfigSecretCrypto, err = crypto.NewAESCrypto(defaults.IDPConfigVerificationKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
repo.userPasswordAlg = crypto.NewBCrypt(defaults.SecretGenerators.PasswordSaltCost)
|
repo.userPasswordAlg = crypto.NewBCrypt(defaults.SecretGenerators.PasswordSaltCost)
|
||||||
repo.machineKeySize = int(defaults.SecretGenerators.MachineKeySize)
|
repo.machineKeySize = int(defaults.SecretGenerators.MachineKeySize)
|
||||||
repo.applicationKeySize = int(defaults.SecretGenerators.ApplicationKeySize)
|
repo.applicationKeySize = int(defaults.SecretGenerators.ApplicationKeySize)
|
||||||
|
|
||||||
aesOTPCrypto, err := crypto.NewAESCrypto(defaults.Multifactors.OTP.VerificationKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
repo.multifactors = domain.MultifactorConfigs{
|
repo.multifactors = domain.MultifactorConfigs{
|
||||||
OTP: domain.OTPConfig{
|
OTP: domain.OTPConfig{
|
||||||
CryptoMFA: aesOTPCrypto,
|
CryptoMFA: otpEncryption,
|
||||||
Issuer: defaults.Multifactors.OTP.Issuer,
|
Issuer: defaults.Multifactors.OTP.Issuer,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.domainVerificationAlg, err = crypto.NewAESCrypto(defaults.DomainVerification.VerificationKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
repo.domainVerificationGenerator = crypto.NewEncryptionGenerator(defaults.DomainVerification.VerificationGenerator, repo.domainVerificationAlg)
|
repo.domainVerificationGenerator = crypto.NewEncryptionGenerator(defaults.DomainVerification.VerificationGenerator, repo.domainVerificationAlg)
|
||||||
repo.domainVerificationValidator = http.ValidateDomain
|
repo.domainVerificationValidator = http.ValidateDomain
|
||||||
web, err := webauthn_helper.StartServer(webAuthN)
|
web, err := webauthn_helper.StartServer(webAuthN)
|
||||||
@ -117,12 +109,6 @@ func StartCommands(
|
|||||||
}
|
}
|
||||||
repo.webauthn = web
|
repo.webauthn = web
|
||||||
|
|
||||||
keyAlgorithm, err := crypto.NewAESCrypto(keyConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
repo.keyAlgorithm = keyAlgorithm
|
|
||||||
|
|
||||||
repo.tokenVerifier = authZRepo
|
repo.tokenVerifier = authZRepo
|
||||||
return repo, nil
|
return repo, nil
|
||||||
}
|
}
|
||||||
|
@ -6,25 +6,17 @@ import (
|
|||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
"github.com/caos/zitadel/internal/notification/channels/fs"
|
|
||||||
"github.com/caos/zitadel/internal/notification/channels/log"
|
|
||||||
"github.com/caos/zitadel/internal/notification/channels/twilio"
|
|
||||||
"github.com/caos/zitadel/internal/notification/templates"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SystemDefaults struct {
|
type SystemDefaults struct {
|
||||||
DefaultLanguage language.Tag
|
DefaultLanguage language.Tag
|
||||||
Domain string
|
Domain string
|
||||||
ZitadelDocs ZitadelDocs
|
ZitadelDocs ZitadelDocs
|
||||||
SecretGenerators SecretGenerators
|
SecretGenerators SecretGenerators
|
||||||
UserVerificationKey *crypto.KeyConfig
|
Multifactors MultifactorConfig
|
||||||
IDPConfigVerificationKey *crypto.KeyConfig
|
DomainVerification DomainVerification
|
||||||
SMTPPasswordVerificationKey *crypto.KeyConfig
|
Notifications Notifications
|
||||||
SMSVerificationKey *crypto.KeyConfig
|
KeyConfig KeyConfig
|
||||||
Multifactors MultifactorConfig
|
|
||||||
DomainVerification DomainVerification
|
|
||||||
Notifications Notifications
|
|
||||||
KeyConfig KeyConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ZitadelDocs struct {
|
type ZitadelDocs struct {
|
||||||
@ -43,20 +35,16 @@ type MultifactorConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OTPConfig struct {
|
type OTPConfig struct {
|
||||||
Issuer string
|
Issuer string
|
||||||
VerificationKey *crypto.KeyConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DomainVerification struct {
|
type DomainVerification struct {
|
||||||
VerificationKey *crypto.KeyConfig
|
|
||||||
VerificationGenerator crypto.GeneratorConfig
|
VerificationGenerator crypto.GeneratorConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type Notifications struct {
|
type Notifications struct {
|
||||||
DebugMode bool
|
|
||||||
Endpoints Endpoints
|
Endpoints Endpoints
|
||||||
FileSystemPath string
|
FileSystemPath string
|
||||||
//Providers Channels
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Endpoints struct {
|
type Endpoints struct {
|
||||||
@ -67,20 +55,6 @@ type Endpoints struct {
|
|||||||
PasswordlessRegistration string
|
PasswordlessRegistration string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Channels struct {
|
|
||||||
Twilio twilio.TwilioConfig
|
|
||||||
FileSystem fs.FSConfig
|
|
||||||
Log log.LogConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
type TemplateData struct {
|
|
||||||
InitCode templates.TemplateData
|
|
||||||
PasswordReset templates.TemplateData
|
|
||||||
VerifyEmail templates.TemplateData
|
|
||||||
VerifyPhone templates.TemplateData
|
|
||||||
DomainClaimed templates.TemplateData
|
|
||||||
}
|
|
||||||
|
|
||||||
type KeyConfig struct {
|
type KeyConfig struct {
|
||||||
Size int
|
Size int
|
||||||
PrivateKeyLifetime time.Duration
|
PrivateKeyLifetime time.Duration
|
||||||
|
@ -18,8 +18,8 @@ type AESCrypto struct {
|
|||||||
keyIDs []string
|
keyIDs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAESCrypto(config *KeyConfig) (*AESCrypto, error) {
|
func NewAESCrypto(config *KeyConfig, keyStorage KeyStorage) (*AESCrypto, error) {
|
||||||
keys, ids, err := LoadKeys(config)
|
keys, ids, err := LoadKeys(config, keyStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
133
internal/crypto/database/database.go
Normal file
133
internal/crypto/database/database.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type database struct {
|
||||||
|
client *sql.DB
|
||||||
|
masterKey string
|
||||||
|
encrypt func(key, masterKey string) (encryptedKey string, err error)
|
||||||
|
decrypt func(encryptedKey, masterKey string) (key string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
EncryptionKeysTable = "system.encryption_keys"
|
||||||
|
encryptionKeysIDCol = "id"
|
||||||
|
encryptionKeysKeyCol = "key"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewKeyStorage(client *sql.DB, masterKey string) (*database, error) {
|
||||||
|
if err := checkMasterKeyLength(masterKey); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &database{
|
||||||
|
client: client,
|
||||||
|
masterKey: masterKey,
|
||||||
|
encrypt: crypto.EncryptAESString,
|
||||||
|
decrypt: crypto.DecryptAESString,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *database) ReadKeys() (crypto.Keys, error) {
|
||||||
|
keys := make(map[string]string)
|
||||||
|
stmt, args, err := sq.Select(encryptionKeysIDCol, encryptionKeysKeyCol).
|
||||||
|
From(EncryptionKeysTable).
|
||||||
|
ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to read keys")
|
||||||
|
}
|
||||||
|
rows, err := d.client.Query(stmt, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to read keys")
|
||||||
|
}
|
||||||
|
for rows.Next() {
|
||||||
|
var id, encryptionKey string
|
||||||
|
err = rows.Scan(&id, &encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to read keys")
|
||||||
|
}
|
||||||
|
key, err := d.decrypt(encryptionKey, d.masterKey)
|
||||||
|
if err != nil {
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to close rows")
|
||||||
|
}
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to decrypt key")
|
||||||
|
}
|
||||||
|
keys[id] = key
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to close rows")
|
||||||
|
}
|
||||||
|
return keys, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *database) ReadKey(id string) (*crypto.Key, error) {
|
||||||
|
stmt, args, err := sq.Select(encryptionKeysKeyCol).
|
||||||
|
From(EncryptionKeysTable).
|
||||||
|
Where(sq.Eq{encryptionKeysIDCol: id}).
|
||||||
|
PlaceholderFormat(sq.Dollar).
|
||||||
|
ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to read key")
|
||||||
|
}
|
||||||
|
row := d.client.QueryRow(stmt, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to read key")
|
||||||
|
}
|
||||||
|
var encryptionKey string
|
||||||
|
err = row.Scan(&encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to read key")
|
||||||
|
}
|
||||||
|
key, err := d.decrypt(encryptionKey, d.masterKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, caos_errs.ThrowInternal(err, "", "unable to decrypt key")
|
||||||
|
}
|
||||||
|
return &crypto.Key{
|
||||||
|
ID: id,
|
||||||
|
Value: key,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *database) CreateKeys(keys ...*crypto.Key) error {
|
||||||
|
insert := sq.Insert(EncryptionKeysTable).
|
||||||
|
Columns(encryptionKeysIDCol, encryptionKeysKeyCol).PlaceholderFormat(sq.Dollar)
|
||||||
|
for _, key := range keys {
|
||||||
|
encryptionKey, err := d.encrypt(key.Value, d.masterKey)
|
||||||
|
if err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "", "unable to encrypt key")
|
||||||
|
}
|
||||||
|
insert = insert.Values(key.ID, encryptionKey)
|
||||||
|
}
|
||||||
|
stmt, args, err := insert.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "", "unable to insert new keys")
|
||||||
|
}
|
||||||
|
tx, err := d.client.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "", "unable to insert new keys")
|
||||||
|
}
|
||||||
|
_, err = tx.Exec(stmt, args...)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return caos_errs.ThrowInternal(err, "", "unable to insert new keys")
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "", "unable to insert new keys")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkMasterKeyLength(masterKey string) error {
|
||||||
|
if length := len([]byte(masterKey)); length != 32 {
|
||||||
|
return caos_errs.ThrowInternalf(nil, "", "masterkey must be 32 bytes, but is %d", length)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
524
internal/crypto/database/database_test.go
Normal file
524
internal/crypto/database/database_test.go
Normal file
@ -0,0 +1,524 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_database_ReadKeys(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
client db
|
||||||
|
masterKey string
|
||||||
|
decrypt func(encryptedKey, masterKey string) (key string, err error)
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
keys crypto.Keys
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"query fails, error",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t, expectQueryErr("SELECT id, key FROM system.encryption_keys", sql.ErrConnDone)),
|
||||||
|
masterKey: "",
|
||||||
|
decrypt: nil,
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: func(err error) bool {
|
||||||
|
return errors.Is(err, sql.ErrConnDone)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"decryption error",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t, expectQuery(
|
||||||
|
"SELECT id, key FROM system.encryption_keys",
|
||||||
|
[]string{"id", "key"},
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"id1",
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
masterKey: "wrong key",
|
||||||
|
decrypt: func(encryptedKey, masterKey string) (key string, err error) {
|
||||||
|
return "", fmt.Errorf("wrong masterkey")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: caos_errs.IsInternal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"single key ok",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t, expectQuery(
|
||||||
|
"SELECT id, key FROM system.encryption_keys",
|
||||||
|
[]string{"id", "key"},
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"id1",
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
masterKey: "masterKey",
|
||||||
|
decrypt: func(encryptedKey, masterKey string) (key string, err error) {
|
||||||
|
return encryptedKey, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
keys: crypto.Keys(map[string]string{"id1": "key1"}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multiple keys ok",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t, expectQuery(
|
||||||
|
"SELECT id, key FROM system.encryption_keys",
|
||||||
|
[]string{"id", "key"},
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"id1",
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id2",
|
||||||
|
"key2",
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
masterKey: "masterKey",
|
||||||
|
decrypt: func(encryptedKey, masterKey string) (key string, err error) {
|
||||||
|
return encryptedKey, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
keys: crypto.Keys(map[string]string{"id1": "key1", "id2": "key2"}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
d := &database{
|
||||||
|
client: tt.fields.client.db,
|
||||||
|
masterKey: tt.fields.masterKey,
|
||||||
|
decrypt: tt.fields.decrypt,
|
||||||
|
}
|
||||||
|
got, err := d.ReadKeys()
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
} else if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.keys, got)
|
||||||
|
}
|
||||||
|
if err := tt.fields.client.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_database_ReadKey(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
client db
|
||||||
|
masterKey string
|
||||||
|
decrypt func(encryptedKey, masterKey string) (key string, err error)
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
key *crypto.Key
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"query fails, error",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t, expectQueryErr("SELECT key FROM system.encryption_keys WHERE id = $1", sql.ErrConnDone)),
|
||||||
|
masterKey: "",
|
||||||
|
decrypt: nil,
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
id: "id1",
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: func(err error) bool {
|
||||||
|
return errors.Is(err, sql.ErrConnDone)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key not found err",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t, expectQuery(
|
||||||
|
"SELECT key FROM system.encryption_keys WHERE id = $1",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"id1")),
|
||||||
|
masterKey: "masterKey",
|
||||||
|
decrypt: func(encryptedKey, masterKey string) (key string, err error) {
|
||||||
|
return encryptedKey, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
id: "id1",
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: caos_errs.IsInternal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"decryption error",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t, expectQuery(
|
||||||
|
"SELECT key FROM system.encryption_keys WHERE id = $1",
|
||||||
|
[]string{"key"},
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"id1",
|
||||||
|
)),
|
||||||
|
masterKey: "wrong key",
|
||||||
|
decrypt: func(encryptedKey, masterKey string) (key string, err error) {
|
||||||
|
return "", fmt.Errorf("wrong masterkey")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
id: "id1",
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: caos_errs.IsInternal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key ok",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t, expectQuery(
|
||||||
|
"SELECT key FROM system.encryption_keys WHERE id = $1",
|
||||||
|
[]string{"key"},
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"id1",
|
||||||
|
)),
|
||||||
|
masterKey: "masterKey",
|
||||||
|
decrypt: func(encryptedKey, masterKey string) (key string, err error) {
|
||||||
|
return encryptedKey, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
id: "id1",
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
key: &crypto.Key{
|
||||||
|
ID: "id1",
|
||||||
|
Value: "key1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
d := &database{
|
||||||
|
client: tt.fields.client.db,
|
||||||
|
masterKey: tt.fields.masterKey,
|
||||||
|
decrypt: tt.fields.decrypt,
|
||||||
|
}
|
||||||
|
got, err := d.ReadKey(tt.args.id)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
} else if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v", err)
|
||||||
|
}
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.Equal(t, tt.res.key, got)
|
||||||
|
}
|
||||||
|
if err := tt.fields.client.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_database_CreateKeys(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
client db
|
||||||
|
masterKey string
|
||||||
|
encrypt func(key, masterKey string) (encryptedKey string, err error)
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
keys []*crypto.Key
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
err func(error) bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"encryption fails, error",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t),
|
||||||
|
masterKey: "",
|
||||||
|
encrypt: func(key, masterKey string) (encryptedKey string, err error) {
|
||||||
|
return "", fmt.Errorf("encryption failed")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
keys: []*crypto.Key{
|
||||||
|
{
|
||||||
|
"id1",
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: caos_errs.IsInternal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"insert fails, error",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t,
|
||||||
|
expectBegin(nil),
|
||||||
|
expectExec("INSERT INTO system.encryption_keys (id,key) VALUES ($1,$2)", sql.ErrTxDone),
|
||||||
|
expectRollback(nil),
|
||||||
|
),
|
||||||
|
masterKey: "masterkey",
|
||||||
|
encrypt: func(key, masterKey string) (encryptedKey string, err error) {
|
||||||
|
return key, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
keys: []*crypto.Key{
|
||||||
|
{
|
||||||
|
"id1",
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: func(err error) bool {
|
||||||
|
return errors.Is(err, sql.ErrTxDone)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"single insert ok",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t,
|
||||||
|
expectBegin(nil),
|
||||||
|
expectExec("INSERT INTO system.encryption_keys (id,key) VALUES ($1,$2)", nil, "id1", "key1"),
|
||||||
|
expectCommit(nil),
|
||||||
|
),
|
||||||
|
masterKey: "masterkey",
|
||||||
|
encrypt: func(key, masterKey string) (encryptedKey string, err error) {
|
||||||
|
return key, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
keys: []*crypto.Key{
|
||||||
|
{
|
||||||
|
"id1",
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multiple insert ok",
|
||||||
|
fields{
|
||||||
|
client: dbMock(t,
|
||||||
|
expectBegin(nil),
|
||||||
|
expectExec("INSERT INTO system.encryption_keys (id,key) VALUES ($1,$2)", nil, "id1", "key1", "id2", "key2"),
|
||||||
|
expectCommit(nil),
|
||||||
|
),
|
||||||
|
masterKey: "masterkey",
|
||||||
|
encrypt: func(key, masterKey string) (encryptedKey string, err error) {
|
||||||
|
return key, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args{
|
||||||
|
keys: []*crypto.Key{
|
||||||
|
{
|
||||||
|
"id1",
|
||||||
|
"key1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id2",
|
||||||
|
"key2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res{
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
d := &database{
|
||||||
|
client: tt.fields.client.db,
|
||||||
|
masterKey: tt.fields.masterKey,
|
||||||
|
encrypt: tt.fields.encrypt,
|
||||||
|
}
|
||||||
|
err := d.CreateKeys(tt.args.keys...)
|
||||||
|
if tt.res.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
} else if tt.res.err != nil && !tt.res.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v", err)
|
||||||
|
}
|
||||||
|
if err := tt.fields.client.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_checkMasterKeyLength(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
masterKey string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
err func(error) bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"invalid length",
|
||||||
|
args{
|
||||||
|
masterKey: "",
|
||||||
|
},
|
||||||
|
caos_errs.IsInternal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"valid length",
|
||||||
|
args{
|
||||||
|
masterKey: "!themasterkeywhichis32byteslong!",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := checkMasterKeyLength(tt.args.masterKey)
|
||||||
|
if tt.err == nil {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
} else if tt.err != nil && !tt.err(err) {
|
||||||
|
t.Errorf("got wrong err: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type db struct {
|
||||||
|
mock sqlmock.Sqlmock
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbMock(t *testing.T, expectations ...func(m sqlmock.Sqlmock)) db {
|
||||||
|
t.Helper()
|
||||||
|
client, mock, err := sqlmock.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create sql mock: %v", err)
|
||||||
|
}
|
||||||
|
for _, expectation := range expectations {
|
||||||
|
expectation(mock)
|
||||||
|
}
|
||||||
|
return db{
|
||||||
|
mock: mock,
|
||||||
|
db: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectQueryErr(query string, err error, args ...driver.Value) func(m sqlmock.Sqlmock) {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
m.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(args...).WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectQuery(stmt string, cols []string, rows [][]driver.Value, args ...driver.Value) func(m sqlmock.Sqlmock) {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
q := m.ExpectQuery(regexp.QuoteMeta(stmt)).WithArgs(args...)
|
||||||
|
result := sqlmock.NewRows(cols)
|
||||||
|
count := uint64(len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
if cols[len(cols)-1] == "count" {
|
||||||
|
row = append(row, count)
|
||||||
|
}
|
||||||
|
result.AddRow(row...)
|
||||||
|
}
|
||||||
|
q.WillReturnRows(result)
|
||||||
|
q.RowsWillBeClosed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectExec(stmt string, err error, args ...driver.Value) func(m sqlmock.Sqlmock) {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
query := m.ExpectExec(regexp.QuoteMeta(stmt)).WithArgs(args...)
|
||||||
|
if err != nil {
|
||||||
|
query.WillReturnError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
query.WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectBegin(err error) func(m sqlmock.Sqlmock) {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
query := m.ExpectBegin()
|
||||||
|
if err != nil {
|
||||||
|
query.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectCommit(err error) func(m sqlmock.Sqlmock) {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
query := m.ExpectCommit()
|
||||||
|
if err != nil {
|
||||||
|
query.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectRollback(err error) func(m sqlmock.Sqlmock) {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
query := m.ExpectRollback()
|
||||||
|
if err != nil {
|
||||||
|
query.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
internal/crypto/file/file.go
Normal file
44
internal/crypto/file/file.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/config"
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ZitadelKeyPath = "ZITADEL_KEY_PATH"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Storage struct{}
|
||||||
|
|
||||||
|
func (d *Storage) ReadKeys() (crypto.Keys, error) {
|
||||||
|
path := os.Getenv(ZitadelKeyPath)
|
||||||
|
if path == "" {
|
||||||
|
return nil, fmt.Errorf("no path set, %s is empty", ZitadelKeyPath)
|
||||||
|
}
|
||||||
|
keys := new(crypto.Keys)
|
||||||
|
err := config.Read(keys, path)
|
||||||
|
return *keys, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Storage) ReadKey(id string) (*crypto.Key, error) {
|
||||||
|
keys, err := d.ReadKeys()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key, ok := keys[id]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("key no found")
|
||||||
|
}
|
||||||
|
return &crypto.Key{
|
||||||
|
ID: id,
|
||||||
|
Value: key,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Storage) CreateKeys(keys ...*crypto.Key) error {
|
||||||
|
return fmt.Errorf("this provider is not able to store new keys")
|
||||||
|
}
|
@ -1,51 +1,49 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"crypto/rand"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/config"
|
|
||||||
"github.com/caos/zitadel/internal/errors"
|
"github.com/caos/zitadel/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
ZitadelKeyPath = "ZITADEL_KEY_PATH"
|
|
||||||
)
|
|
||||||
|
|
||||||
type KeyConfig struct {
|
type KeyConfig struct {
|
||||||
EncryptionKeyID string
|
EncryptionKeyID string
|
||||||
DecryptionKeyIDs []string
|
DecryptionKeyIDs []string
|
||||||
Path string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Keys map[string]string
|
type Keys map[string]string
|
||||||
|
|
||||||
func ReadKeys(path string) (Keys, error) {
|
type Key struct {
|
||||||
if path == "" {
|
ID string
|
||||||
path = os.Getenv(ZitadelKeyPath)
|
Value string
|
||||||
if path == "" {
|
|
||||||
return nil, errors.ThrowInvalidArgument(nil, "CRYPT-56lka", "no path set")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keys := new(Keys)
|
|
||||||
err := config.Read(keys, path)
|
|
||||||
return *keys, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadKey(config *KeyConfig, id string) (string, error) {
|
func NewKey(id string) (*Key, error) {
|
||||||
keys, _, err := LoadKeys(config)
|
randBytes := make([]byte, 32)
|
||||||
|
if _, err := rand.Read(randBytes); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Key{
|
||||||
|
ID: id,
|
||||||
|
Value: string(randBytes),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadKey(id string, keyStorage KeyStorage) (string, error) {
|
||||||
|
key, err := keyStorage.ReadKey(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return keys[id], nil
|
return key.Value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadKeys(config *KeyConfig) (map[string]string, []string, error) {
|
func LoadKeys(config *KeyConfig, keyStorage KeyStorage) (map[string]string, []string, error) {
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return nil, nil, errors.ThrowInvalidArgument(nil, "CRYPT-dJK8s", "config must not be nil")
|
return nil, nil, errors.ThrowInvalidArgument(nil, "CRYPT-dJK8s", "config must not be nil")
|
||||||
}
|
}
|
||||||
readKeys, err := ReadKeys(config.Path)
|
readKeys, err := keyStorage.ReadKeys()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -54,7 +52,7 @@ func LoadKeys(config *KeyConfig) (map[string]string, []string, error) {
|
|||||||
if config.EncryptionKeyID != "" {
|
if config.EncryptionKeyID != "" {
|
||||||
key, ok := readKeys[config.EncryptionKeyID]
|
key, ok := readKeys[config.EncryptionKeyID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, errors.ThrowInternalf(nil, "CRYPT-v2Kas", "encryption key not found")
|
return nil, nil, errors.ThrowInternalf(nil, "CRYPT-v2Kas", "encryption key %s not found", config.EncryptionKeyID)
|
||||||
}
|
}
|
||||||
keys[config.EncryptionKeyID] = key
|
keys[config.EncryptionKeyID] = key
|
||||||
ids = append(ids, config.EncryptionKeyID)
|
ids = append(ids, config.EncryptionKeyID)
|
||||||
@ -62,7 +60,7 @@ func LoadKeys(config *KeyConfig) (map[string]string, []string, error) {
|
|||||||
for _, id := range config.DecryptionKeyIDs {
|
for _, id := range config.DecryptionKeyIDs {
|
||||||
key, ok := readKeys[id]
|
key, ok := readKeys[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
logging.Log("CRYPT-s23rf").Warnf("description key %s not found", id)
|
logging.Errorf("description key %s not found", id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
keys[id] = key
|
keys[id] = key
|
||||||
|
7
internal/crypto/key_storage.go
Normal file
7
internal/crypto/key_storage.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
type KeyStorage interface {
|
||||||
|
ReadKeys() (Keys, error)
|
||||||
|
ReadKey(id string) (*Key, error)
|
||||||
|
CreateKeys(...*Key) error
|
||||||
|
}
|
@ -4,9 +4,10 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
|
||||||
"github.com/rakyll/statik/fs"
|
"github.com/rakyll/statik/fs"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/command"
|
"github.com/caos/zitadel/internal/command"
|
||||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/caos/zitadel/internal/notification/repository/eventsourcing"
|
"github.com/caos/zitadel/internal/notification/repository/eventsourcing"
|
||||||
@ -18,10 +19,19 @@ type Config struct {
|
|||||||
Repository eventsourcing.Config
|
Repository eventsourcing.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(config Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) {
|
func Start(config Config,
|
||||||
|
systemDefaults sd.SystemDefaults,
|
||||||
|
command *command.Commands,
|
||||||
|
queries *query.Queries,
|
||||||
|
dbClient *sql.DB,
|
||||||
|
assetsPrefix string,
|
||||||
|
userEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smtpEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smsEncryption crypto.EncryptionAlgorithm,
|
||||||
|
) {
|
||||||
statikFS, err := fs.NewWithNamespace("notification")
|
statikFS, err := fs.NewWithNamespace("notification")
|
||||||
logging.OnError(err).Panic("unable to start listener")
|
logging.OnError(err).Panic("unable to start listener")
|
||||||
|
|
||||||
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command, queries, dbClient, assetsPrefix, smtpPasswordEncAlg, smsCrypto)
|
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command, queries, dbClient, assetsPrefix, userEncryption, smtpEncryption, smsEncryption)
|
||||||
logging.OnError(err).Panic("unable to start app")
|
logging.OnError(err).Panic("unable to start app")
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/command"
|
"github.com/caos/zitadel/internal/command"
|
||||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
@ -34,10 +32,20 @@ func (h *handler) Eventstore() v1.Eventstore {
|
|||||||
return h.es
|
return h.es
|
||||||
}
|
}
|
||||||
|
|
||||||
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) []queryv1.Handler {
|
func Register(configs Configs,
|
||||||
aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey)
|
bulkLimit,
|
||||||
logging.OnError(err).Fatal("error create new aes crypto")
|
errorCount uint64,
|
||||||
|
view *view.View,
|
||||||
|
es v1.Eventstore,
|
||||||
|
command *command.Commands,
|
||||||
|
queries *query.Queries,
|
||||||
|
systemDefaults sd.SystemDefaults,
|
||||||
|
dir http.FileSystem,
|
||||||
|
assetsPrefix string,
|
||||||
|
userEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smtpEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smsEncryption crypto.EncryptionAlgorithm,
|
||||||
|
) []queryv1.Handler {
|
||||||
return []queryv1.Handler{
|
return []queryv1.Handler{
|
||||||
newNotifyUser(
|
newNotifyUser(
|
||||||
handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es},
|
handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es},
|
||||||
@ -48,11 +56,11 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
|
|||||||
command,
|
command,
|
||||||
queries,
|
queries,
|
||||||
systemDefaults,
|
systemDefaults,
|
||||||
aesCrypto,
|
|
||||||
dir,
|
dir,
|
||||||
assetsPrefix,
|
assetsPrefix,
|
||||||
smtpPasswordEncAlg,
|
userEncryption,
|
||||||
smsCrypto,
|
smtpEncryption,
|
||||||
|
smsEncryption,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/notification/channels/fs"
|
"github.com/caos/zitadel/internal/notification/channels/fs"
|
||||||
"github.com/caos/zitadel/internal/notification/channels/log"
|
"github.com/caos/zitadel/internal/notification/channels/log"
|
||||||
"github.com/caos/zitadel/internal/notification/channels/twilio"
|
"github.com/caos/zitadel/internal/notification/channels/twilio"
|
||||||
@ -40,26 +41,36 @@ type Notification struct {
|
|||||||
handler
|
handler
|
||||||
command *command.Commands
|
command *command.Commands
|
||||||
systemDefaults sd.SystemDefaults
|
systemDefaults sd.SystemDefaults
|
||||||
AesCrypto crypto.EncryptionAlgorithm
|
|
||||||
statikDir http.FileSystem
|
statikDir http.FileSystem
|
||||||
subscription *v1.Subscription
|
subscription *v1.Subscription
|
||||||
assetsPrefix string
|
assetsPrefix string
|
||||||
queries *query.Queries
|
queries *query.Queries
|
||||||
|
userDataCrypto crypto.EncryptionAlgorithm
|
||||||
smtpPasswordCrypto crypto.EncryptionAlgorithm
|
smtpPasswordCrypto crypto.EncryptionAlgorithm
|
||||||
smsTokenCrypto crypto.EncryptionAlgorithm
|
smsTokenCrypto crypto.EncryptionAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNotification(handler handler, command *command.Commands, query *query.Queries, defaults sd.SystemDefaults, aesCrypto crypto.EncryptionAlgorithm, statikDir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) *Notification {
|
func newNotification(
|
||||||
|
handler handler,
|
||||||
|
command *command.Commands,
|
||||||
|
query *query.Queries,
|
||||||
|
defaults sd.SystemDefaults,
|
||||||
|
statikDir http.FileSystem,
|
||||||
|
assetsPrefix string,
|
||||||
|
userEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smtpEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smsEncryption crypto.EncryptionAlgorithm,
|
||||||
|
) *Notification {
|
||||||
h := &Notification{
|
h := &Notification{
|
||||||
handler: handler,
|
handler: handler,
|
||||||
command: command,
|
command: command,
|
||||||
systemDefaults: defaults,
|
systemDefaults: defaults,
|
||||||
statikDir: statikDir,
|
statikDir: statikDir,
|
||||||
AesCrypto: aesCrypto,
|
|
||||||
assetsPrefix: assetsPrefix,
|
assetsPrefix: assetsPrefix,
|
||||||
queries: query,
|
queries: query,
|
||||||
smtpPasswordCrypto: smtpPasswordEncAlg,
|
userDataCrypto: userEncryption,
|
||||||
smsTokenCrypto: smsCrypto,
|
smtpPasswordCrypto: smtpEncryption,
|
||||||
|
smsTokenCrypto: smsEncryption,
|
||||||
}
|
}
|
||||||
|
|
||||||
h.subscribe()
|
h.subscribe()
|
||||||
@ -161,7 +172,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = types.SendUserInitCode(ctx, string(template.Template), translator, user, initCode, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto, colors, n.assetsPrefix)
|
err = types.SendUserInitCode(ctx, string(template.Template), translator, user, initCode, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -199,7 +210,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = types.SendPasswordCode(ctx, string(template.Template), translator, user, pwCode, n.systemDefaults, n.getSMTPConfig, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto, colors, n.assetsPrefix)
|
err = types.SendPasswordCode(ctx, string(template.Template), translator, user, pwCode, n.systemDefaults, n.getSMTPConfig, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -238,7 +249,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = types.SendEmailVerificationCode(ctx, string(template.Template), translator, user, emailCode, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto, colors, n.assetsPrefix)
|
err = types.SendEmailVerificationCode(ctx, string(template.Template), translator, user, emailCode, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -264,7 +275,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = types.SendPhoneVerificationCode(context.Background(), translator, user, phoneCode, n.systemDefaults, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto)
|
err = types.SendPhoneVerificationCode(context.Background(), translator, user, phoneCode, n.systemDefaults, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -351,7 +362,7 @@ func (n *Notification) handlePasswordlessRegistrationLink(event *models.Event) (
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = types.SendPasswordlessRegistrationLink(ctx, string(template.Template), translator, user, addedEvent, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto, colors, n.assetsPrefix)
|
err = types.SendPasswordlessRegistrationLink(ctx, string(template.Template), translator, user, addedEvent, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.userDataCrypto, colors, n.assetsPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,17 @@ type EsRepository struct {
|
|||||||
spooler *es_spol.Spooler
|
spooler *es_spol.Spooler
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) (*EsRepository, error) {
|
func Start(conf Config,
|
||||||
|
dir http.FileSystem,
|
||||||
|
systemDefaults sd.SystemDefaults,
|
||||||
|
command *command.Commands,
|
||||||
|
queries *query.Queries,
|
||||||
|
dbClient *sql.DB,
|
||||||
|
assetsPrefix string,
|
||||||
|
userEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smtpEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smsEncryption crypto.EncryptionAlgorithm,
|
||||||
|
) (*EsRepository, error) {
|
||||||
es, err := v1.Start(dbClient)
|
es, err := v1.Start(dbClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -33,7 +43,7 @@ func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, c
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, command, queries, systemDefaults, dir, assetsPrefix, smtpPasswordEncAlg, smsCrypto)
|
spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, command, queries, systemDefaults, dir, assetsPrefix, userEncryption, smtpEncryption, smsEncryption)
|
||||||
|
|
||||||
return &EsRepository{
|
return &EsRepository{
|
||||||
spool,
|
spool,
|
||||||
|
@ -21,12 +21,24 @@ type SpoolerConfig struct {
|
|||||||
Handlers handler.Configs
|
Handlers handler.Configs
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) *spooler.Spooler {
|
func StartSpooler(c SpoolerConfig,
|
||||||
|
es v1.Eventstore,
|
||||||
|
view *view.View,
|
||||||
|
sql *sql.DB,
|
||||||
|
command *command.Commands,
|
||||||
|
queries *query.Queries,
|
||||||
|
systemDefaults sd.SystemDefaults,
|
||||||
|
dir http.FileSystem,
|
||||||
|
assetsPrefix string,
|
||||||
|
userEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smtpEncryption crypto.EncryptionAlgorithm,
|
||||||
|
smsEncryption crypto.EncryptionAlgorithm,
|
||||||
|
) *spooler.Spooler {
|
||||||
spoolerConfig := spooler.Config{
|
spoolerConfig := spooler.Config{
|
||||||
Eventstore: es,
|
Eventstore: es,
|
||||||
Locker: &locker{dbClient: sql},
|
Locker: &locker{dbClient: sql},
|
||||||
ConcurrentWorkers: c.ConcurrentWorkers,
|
ConcurrentWorkers: c.ConcurrentWorkers,
|
||||||
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, queries, systemDefaults, dir, assetsPrefix, smtpPasswordEncAlg, smsCrypto),
|
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, queries, systemDefaults, dir, assetsPrefix, userEncryption, smtpEncryption, smsEncryption),
|
||||||
}
|
}
|
||||||
spool := spoolerConfig.New()
|
spool := spoolerConfig.New()
|
||||||
spool.Start()
|
spool.Start()
|
||||||
|
@ -26,17 +26,15 @@ const (
|
|||||||
KeyPublicTable = KeyProjectionTable + "_" + publicKeyTableSuffix
|
KeyPublicTable = KeyProjectionTable + "_" + publicKeyTableSuffix
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewKeyProjection(ctx context.Context, config crdb.StatementHandlerConfig, keyConfig *crypto.KeyConfig, keyChan chan<- interface{}) (_ *KeyProjection, err error) {
|
func NewKeyProjection(ctx context.Context, config crdb.StatementHandlerConfig, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, keyChan chan<- interface{}) *KeyProjection {
|
||||||
p := new(KeyProjection)
|
p := new(KeyProjection)
|
||||||
config.ProjectionName = KeyProjectionTable
|
config.ProjectionName = KeyProjectionTable
|
||||||
config.Reducers = p.reducers()
|
config.Reducers = p.reducers()
|
||||||
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
|
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
|
||||||
p.keyChan = keyChan
|
p.keyChan = keyChan
|
||||||
p.encryptionAlgorithm, err = crypto.NewAESCrypto(keyConfig)
|
p.encryptionAlgorithm = keyEncryptionAlgorithm
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return p
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *KeyProjection) reducers() []handler.AggregateReducer {
|
func (p *KeyProjection) reducers() []handler.AggregateReducer {
|
||||||
|
@ -17,7 +17,7 @@ const (
|
|||||||
FailedEventsTable = "projections.failed_events"
|
FailedEventsTable = "projections.failed_events"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, config Config, keyConfig *crypto.KeyConfig, keyChan chan<- interface{}) error {
|
func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, config Config, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, keyChan chan<- interface{}) error {
|
||||||
projectionConfig := crdb.StatementHandlerConfig{
|
projectionConfig := crdb.StatementHandlerConfig{
|
||||||
ProjectionHandlerConfig: handler.ProjectionHandlerConfig{
|
ProjectionHandlerConfig: handler.ProjectionHandlerConfig{
|
||||||
HandlerConfig: handler.HandlerConfig{
|
HandlerConfig: handler.HandlerConfig{
|
||||||
@ -73,10 +73,9 @@ func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, co
|
|||||||
NewSMTPConfigProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["smtp_configs"]))
|
NewSMTPConfigProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["smtp_configs"]))
|
||||||
NewSMSConfigProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["sms_config"]))
|
NewSMSConfigProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["sms_config"]))
|
||||||
NewOIDCSettingsProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["oidc_settings"]))
|
NewOIDCSettingsProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["oidc_settings"]))
|
||||||
_, err := NewKeyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["keys"]), keyConfig, keyChan)
|
|
||||||
NewDebugNotificationProviderProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["debug_notification_provider"]))
|
NewDebugNotificationProviderProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["debug_notification_provider"]))
|
||||||
|
NewKeyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["keys"]), keyEncryptionAlgorithm, keyChan)
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyCustomConfig(config crdb.StatementHandlerConfig, customConfig CustomConfig) crdb.StatementHandlerConfig {
|
func applyCustomConfig(config crdb.StatementHandlerConfig, customConfig CustomConfig) crdb.StatementHandlerConfig {
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
|
||||||
"github.com/caos/zitadel/internal/crypto"
|
"github.com/caos/zitadel/internal/crypto"
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
"github.com/caos/zitadel/internal/query/projection"
|
"github.com/caos/zitadel/internal/query/projection"
|
||||||
@ -38,7 +37,7 @@ type Queries struct {
|
|||||||
zitadelRoles []authz.RoleMapping
|
zitadelRoles []authz.RoleMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartQueries(ctx context.Context, es *eventstore.Eventstore, sqlClient *sql.DB, projections projection.Config, defaults sd.SystemDefaults, keyConfig *crypto.KeyConfig, keyChan chan<- interface{}, zitadelRoles []authz.RoleMapping) (repo *Queries, err error) {
|
func StartQueries(ctx context.Context, es *eventstore.Eventstore, sqlClient *sql.DB, projections projection.Config, keyEncryptionAlgorithm crypto.EncryptionAlgorithm, keyChan chan<- interface{}, zitadelRoles []authz.RoleMapping) (repo *Queries, err error) {
|
||||||
statikLoginFS, err := fs.NewWithNamespace("login")
|
statikLoginFS, err := fs.NewWithNamespace("login")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to start login statik dir")
|
return nil, fmt.Errorf("unable to start login statik dir")
|
||||||
@ -67,7 +66,7 @@ func StartQueries(ctx context.Context, es *eventstore.Eventstore, sqlClient *sql
|
|||||||
keypair.RegisterEventMappers(repo.eventstore)
|
keypair.RegisterEventMappers(repo.eventstore)
|
||||||
usergrant.RegisterEventMappers(repo.eventstore)
|
usergrant.RegisterEventMappers(repo.eventstore)
|
||||||
|
|
||||||
err = projection.Start(ctx, sqlClient, es, projections, keyConfig, keyChan)
|
err = projection.Start(ctx, sqlClient, es, projections, keyEncryptionAlgorithm, keyChan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -55,3 +55,10 @@ CREATE TABLE zitadel.projections.sms_configs_twilio (
|
|||||||
|
|
||||||
, PRIMARY KEY (sms_id)
|
, PRIMARY KEY (sms_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ALTER TABLE zitadel.projections.login_policies
|
||||||
|
ADD COLUMN password_check_lifetime BIGINT NOT NULL DEFAULT 0
|
||||||
|
, ADD COLUMN external_login_check_lifetime BIGINT NOT NULL DEFAULT 0
|
||||||
|
, ADD COLUMN mfa_init_skip_lifetime BIGINT NOT NULL DEFAULT 0
|
||||||
|
, ADD COLUMN second_factor_check_lifetime BIGINT NOT NULL DEFAULT 0
|
||||||
|
, ADD COLUMN multi_factor_check_lifetime BIGINT NOT NULL DEFAULT 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user