From 3bc3ef1f2c9cfcfe8bed3f5f5a1cab88dc05919e Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Fri, 12 Feb 2021 16:51:12 +0100 Subject: [PATCH] fix: add register org and key pairs (#1275) --- cmd/zitadel/startup.yaml | 7 -- cmd/zitadel/system-defaults.yaml | 9 +- internal/api/oidc/auth_request.go | 2 +- .../eventsourcing/eventstore/key.go | 19 ----- .../eventsourcing/eventstore/org.go | 48 ----------- .../repository/eventsourcing/repository.go | 11 +-- internal/auth/repository/key.go | 1 - internal/auth/repository/org.go | 2 - .../repository/eventsourcing/repository.go | 2 - .../config/systemdefaults/system_defaults.go | 9 ++ .../repository/eventsourcing/eventstore.go | 85 ------------------- internal/key/repository/eventsourcing/key.go | 21 ----- .../ui/login/handler/register_org_handler.go | 40 ++++----- internal/v2/command/command.go | 23 ++++- internal/v2/command/key_pair.go | 45 ++++++++++ internal/v2/command/key_pair_model.go | 61 +++++++++++++ internal/v2/domain/key_pair.go | 45 ++++++++++ internal/v2/repository/keypair/aggregate.go | 14 +++ internal/v2/repository/keypair/eventstore.go | 9 ++ internal/v2/repository/keypair/key_pair.go | 78 +++++++++++++++++ 20 files changed, 307 insertions(+), 224 deletions(-) delete mode 100644 internal/key/repository/eventsourcing/eventstore.go create mode 100644 internal/v2/command/key_pair.go create mode 100644 internal/v2/command/key_pair_model.go create mode 100644 internal/v2/domain/key_pair.go create mode 100644 internal/v2/repository/keypair/aggregate.go create mode 100644 internal/v2/repository/keypair/eventstore.go create mode 100644 internal/v2/repository/keypair/key_pair.go diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 8e92800397..4a62c52bc3 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -100,13 +100,6 @@ Auth: ConcurrentWorkers: 1 BulkLimit: 10000 FailureCountUntilSkip: 5 - KeyConfig: - Size: 2048 - PrivateKeyLifetime: 6h - PublicKeyLifetime: 30h - EncryptionConfig: - EncryptionKeyID: $ZITADEL_OIDC_KEYS_ID - SigningKeyRotation: 10s Admin: SearchLimit: 1000 diff --git a/cmd/zitadel/system-defaults.yaml b/cmd/zitadel/system-defaults.yaml index cd84668dce..b2ecfc023c 100644 --- a/cmd/zitadel/system-defaults.yaml +++ b/cmd/zitadel/system-defaults.yaml @@ -129,4 +129,11 @@ SystemDefaults: ID: $ZITADEL_DEFAULT_DOMAIN OriginLogin: $ZITADEL_ACCOUNTS OriginConsole: $ZITADEL_CONSOLE - DisplayName: ZITADEL \ No newline at end of file + DisplayName: ZITADEL + KeyConfig: + Size: 2048 + PrivateKeyLifetime: 6h + PublicKeyLifetime: 30h + EncryptionConfig: + EncryptionKeyID: $ZITADEL_OIDC_KEYS_ID + SigningKeyRotation: 10s \ No newline at end of file diff --git a/internal/api/oidc/auth_request.go b/internal/api/oidc/auth_request.go index 923bdbc640..69dd528654 100644 --- a/internal/api/oidc/auth_request.go +++ b/internal/api/oidc/auth_request.go @@ -131,7 +131,7 @@ func (o *OPStorage) GetKeySet(ctx context.Context) (_ *jose.JSONWebKeySet, err e } func (o *OPStorage) SaveNewKeyPair(ctx context.Context) error { - return o.repo.GenerateSigningKeyPair(ctx, o.signingKeyAlgorithm) + return o.command.GenerateSigningKeyPair(ctx, o.signingKeyAlgorithm) } func (o *OPStorage) assertProjectRoleScopes(app *proj_model.ApplicationView, scopes []string) ([]string, error) { diff --git a/internal/auth/repository/eventsourcing/eventstore/key.go b/internal/auth/repository/eventsourcing/eventstore/key.go index 7ab529bdf5..fea6f596af 100644 --- a/internal/auth/repository/eventsourcing/eventstore/key.go +++ b/internal/auth/repository/eventsourcing/eventstore/key.go @@ -6,29 +6,14 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" - "github.com/caos/zitadel/internal/key/model" - key_event "github.com/caos/zitadel/internal/key/repository/eventsourcing" -) - -const ( - oidcUser = "OIDC" - iamOrg = "IAM" ) type KeyRepository struct { - KeyEvents *key_event.KeyEventstore View *view.View SigningKeyRotation time.Duration } -func (k *KeyRepository) GenerateSigningKeyPair(ctx context.Context, algorithm string) error { - ctx = setOIDCCtx(ctx) - _, err := k.KeyEvents.GenerateKeyPair(ctx, model.KeyUsageSigning, algorithm) - return err -} - func (k *KeyRepository) GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKey, errCh chan<- error, renewTimer <-chan time.Time) { go func() { for { @@ -69,7 +54,3 @@ func (k *KeyRepository) refreshSigningKey(keyCh chan<- jose.SigningKey, errCh ch }, } } - -func setOIDCCtx(ctx context.Context) context.Context { - return authz.SetCtxData(ctx, authz.CtxData{UserID: oidcUser, OrgID: iamOrg}) -} diff --git a/internal/auth/repository/eventsourcing/eventstore/org.go b/internal/auth/repository/eventsourcing/eventstore/org.go index c5f3ecced0..062410e585 100644 --- a/internal/auth/repository/eventsourcing/eventstore/org.go +++ b/internal/auth/repository/eventsourcing/eventstore/org.go @@ -11,10 +11,7 @@ import ( iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" "github.com/caos/zitadel/internal/telemetry/tracing" - auth_model "github.com/caos/zitadel/internal/auth/model" auth_view "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" - es_models "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/eventstore/sdk" org_model "github.com/caos/zitadel/internal/org/model" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" "github.com/caos/zitadel/internal/org/repository/view/model" @@ -55,51 +52,6 @@ func (repo *OrgRepository) SearchOrgs(ctx context.Context, request *org_model.Or return result, nil } -func (repo *OrgRepository) RegisterOrg(ctx context.Context, register *auth_model.RegisterOrg) (*auth_model.RegisterOrg, error) { - pwPolicy, err := repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID) - if err != nil { - return nil, err - } - pwPolicyView := iam_view_model.PasswordComplexityViewToModel(pwPolicy) - orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID) - if err != nil { - return nil, err - } - orgPolicyView := iam_view_model.OrgIAMViewToModel(orgPolicy) - users := func(ctx context.Context, domain string) ([]*es_models.Aggregate, error) { - userIDs, err := repo.View.UserIDsByDomain(domain) - if err != nil { - return nil, err - } - return repo.UserEventstore.PrepareDomainClaimed(ctx, userIDs) - } - org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, register.Org, users) - if err != nil { - return nil, err - } - user, userAggregates, err := repo.UserEventstore.PrepareRegisterUser(ctx, register.User, nil, pwPolicyView, orgPolicyView, org.AggregateID) - if err != nil { - return nil, err - } - - aggregates = append(aggregates, userAggregates...) - registerModel := &Register{Org: org, User: user} - - member := org_model.NewOrgMemberWithRoles(org.AggregateID, user.AggregateID, orgOwnerRole) - _, memberAggregate, err := repo.OrgEventstore.PrepareAddOrgMember(ctx, member, org.AggregateID) - if err != nil { - return nil, err - } - aggregates = append(aggregates, memberAggregate) - - err = sdk.PushAggregates(ctx, repo.OrgEventstore.PushAggregates, registerModel.AppendEvents, aggregates...) - if err != nil { - return nil, err - } - - return RegisterToModel(registerModel), nil -} - func (repo *OrgRepository) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) { orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID) if err != nil { diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go index e554e06602..c8622e24cb 100644 --- a/internal/auth/repository/eventsourcing/repository.go +++ b/internal/auth/repository/eventsourcing/repository.go @@ -16,7 +16,6 @@ import ( es_spol "github.com/caos/zitadel/internal/eventstore/spooler" es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/zitadel/internal/id" - es_key "github.com/caos/zitadel/internal/key/repository/eventsourcing" es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing" es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing" es_user "github.com/caos/zitadel/internal/user/repository/eventsourcing" @@ -31,7 +30,6 @@ type Config struct { AuthRequest cache.Config View types.SQL Spooler spooler.SpoolerConfig - KeyConfig es_key.KeyConfig } type EsRepository struct { @@ -59,7 +57,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co return nil, err } - keyAlgorithm, err := crypto.NewAESCrypto(conf.KeyConfig.EncryptionConfig) + keyAlgorithm, err := crypto.NewAESCrypto(systemDefaults.KeyConfig.EncryptionConfig) if err != nil { return nil, err } @@ -85,10 +83,6 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co return nil, err } - key, err := es_key.StartKey(es, conf.KeyConfig, keyAlgorithm, idGenerator) - if err != nil { - return nil, err - } iam, err := es_iam.StartIAM( es_iam.IAMConfig{ Eventstore: es, @@ -158,9 +152,8 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co View: view, }, eventstore.KeyRepository{ - KeyEvents: key, View: view, - SigningKeyRotation: conf.KeyConfig.SigningKeyRotation.Duration, + SigningKeyRotation: systemDefaults.KeyConfig.SigningKeyRotation.Duration, }, eventstore.ApplicationRepo{ View: view, diff --git a/internal/auth/repository/key.go b/internal/auth/repository/key.go index 17623be806..1598041e53 100644 --- a/internal/auth/repository/key.go +++ b/internal/auth/repository/key.go @@ -8,7 +8,6 @@ import ( ) type KeyRepository interface { - GenerateSigningKeyPair(ctx context.Context, algorithm string) error GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKey, errCh chan<- error, timer <-chan time.Time) GetKeySet(ctx context.Context) (*jose.JSONWebKeySet, error) } diff --git a/internal/auth/repository/org.go b/internal/auth/repository/org.go index 2139987a18..cb38bcf872 100644 --- a/internal/auth/repository/org.go +++ b/internal/auth/repository/org.go @@ -2,12 +2,10 @@ package repository import ( "context" - auth_model "github.com/caos/zitadel/internal/auth/model" iam_model "github.com/caos/zitadel/internal/iam/model" ) type OrgRepository interface { - RegisterOrg(context.Context, *auth_model.RegisterOrg) (*auth_model.RegisterOrg, error) GetOrgIAMPolicy(ctx context.Context, orgID string) (*iam_model.OrgIAMPolicyView, error) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) GetIDPConfigByID(ctx context.Context, idpConfigID string) (*iam_model.IDPConfigView, error) diff --git a/internal/authz/repository/eventsourcing/repository.go b/internal/authz/repository/eventsourcing/repository.go index b6ad0d7680..3a107c8348 100644 --- a/internal/authz/repository/eventsourcing/repository.go +++ b/internal/authz/repository/eventsourcing/repository.go @@ -18,7 +18,6 @@ import ( es_spol "github.com/caos/zitadel/internal/eventstore/spooler" es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/zitadel/internal/id" - es_key "github.com/caos/zitadel/internal/key/repository/eventsourcing" es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing" ) @@ -27,7 +26,6 @@ type Config struct { AuthRequest cache.Config View types.SQL Spooler spooler.SpoolerConfig - KeyConfig es_key.KeyConfig } type EsRepository struct { diff --git a/internal/config/systemdefaults/system_defaults.go b/internal/config/systemdefaults/system_defaults.go index 1e6bab7a22..8f47681ff2 100644 --- a/internal/config/systemdefaults/system_defaults.go +++ b/internal/config/systemdefaults/system_defaults.go @@ -24,6 +24,7 @@ type SystemDefaults struct { IamID string Notifications Notifications WebAuthN WebAuthN + KeyConfig KeyConfig } type ZitadelDocs struct { @@ -97,3 +98,11 @@ type WebAuthN struct { OriginConsole string DisplayName string } + +type KeyConfig struct { + Size int + PrivateKeyLifetime types.Duration + PublicKeyLifetime types.Duration + EncryptionConfig *crypto.KeyConfig + SigningKeyRotation types.Duration +} diff --git a/internal/key/repository/eventsourcing/eventstore.go b/internal/key/repository/eventsourcing/eventstore.go deleted file mode 100644 index 1d0e2d8618..0000000000 --- a/internal/key/repository/eventsourcing/eventstore.go +++ /dev/null @@ -1,85 +0,0 @@ -package eventsourcing - -import ( - "context" - "time" - - "github.com/caos/zitadel/internal/config/types" - "github.com/caos/zitadel/internal/crypto" - caos_errs "github.com/caos/zitadel/internal/errors" - es_int "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/eventstore/models" - es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" - "github.com/caos/zitadel/internal/id" - key_model "github.com/caos/zitadel/internal/key/model" - "github.com/caos/zitadel/internal/key/repository/eventsourcing/model" -) - -type KeyEventstore struct { - es_int.Eventstore - keySize int - keyAlgorithm crypto.EncryptionAlgorithm - privateKeyLifetime time.Duration - publicKeyLifetime time.Duration - idGenerator id.Generator -} - -type KeyConfig struct { - Size int - PrivateKeyLifetime types.Duration - PublicKeyLifetime types.Duration - EncryptionConfig *crypto.KeyConfig - SigningKeyRotation types.Duration -} - -func StartKey(eventstore es_int.Eventstore, config KeyConfig, keyAlgorithm crypto.EncryptionAlgorithm, generator id.Generator) (*KeyEventstore, error) { - return &KeyEventstore{ - Eventstore: eventstore, - keySize: config.Size, - keyAlgorithm: keyAlgorithm, - privateKeyLifetime: config.PrivateKeyLifetime.Duration, - publicKeyLifetime: config.PublicKeyLifetime.Duration, - idGenerator: generator, - }, nil -} - -func (es *KeyEventstore) GenerateKeyPair(ctx context.Context, usage key_model.KeyUsage, algorithm string) (*key_model.KeyPair, error) { - privateKey, publicKey, err := crypto.GenerateEncryptedKeyPair(es.keySize, es.keyAlgorithm) - if err != nil { - return nil, err - } - privateKeyExp := time.Now().UTC().Add(es.privateKeyLifetime) - publicKeyExp := time.Now().UTC().Add(es.publicKeyLifetime) - return es.CreateKeyPair(ctx, &key_model.KeyPair{ - ObjectRoot: models.ObjectRoot{}, - Usage: usage, - Algorithm: algorithm, - PrivateKey: &key_model.Key{ - Key: privateKey, - Expiry: privateKeyExp, - }, - PublicKey: &key_model.Key{ - Key: publicKey, - Expiry: publicKeyExp, - }, - }) -} - -func (es *KeyEventstore) CreateKeyPair(ctx context.Context, pair *key_model.KeyPair) (*key_model.KeyPair, error) { - if !pair.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-G34ga", "Name is required") - } - id, err := es.idGenerator.Next() - if err != nil { - return nil, err - } - pair.AggregateID = id - repoKey := model.KeyPairFromModel(pair) - - createAggregate := KeyPairCreateAggregate(es.AggregateCreator(), repoKey) - err = es_sdk.Push(ctx, es.PushAggregates, repoKey.AppendEvents, createAggregate) - if err != nil { - return nil, err - } - return model.KeyPairToModel(repoKey), nil -} diff --git a/internal/key/repository/eventsourcing/key.go b/internal/key/repository/eventsourcing/key.go index b23d38bfb4..02a5ffac51 100644 --- a/internal/key/repository/eventsourcing/key.go +++ b/internal/key/repository/eventsourcing/key.go @@ -1,9 +1,6 @@ package eventsourcing import ( - "context" - - "github.com/caos/zitadel/internal/errors" es_models "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/key/repository/eventsourcing/model" ) @@ -13,21 +10,3 @@ func KeyPairQuery(latestSequence uint64) *es_models.SearchQuery { AggregateTypeFilter(model.KeyPairAggregate). LatestSequenceFilter(latestSequence) } - -func KeyPairAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, pair *model.KeyPair) (*es_models.Aggregate, error) { - if pair == nil { - return nil, errors.ThrowPreconditionFailed(nil, "EVENT-d5HNJA", "existing key pair must not be nil") - } - return aggCreator.NewAggregate(ctx, pair.AggregateID, model.KeyPairAggregate, model.KeyPairVersion, pair.Sequence) -} - -func KeyPairCreateAggregate(aggCreator *es_models.AggregateCreator, pair *model.KeyPair) func(ctx context.Context) (*es_models.Aggregate, error) { - return func(ctx context.Context) (*es_models.Aggregate, error) { - agg, err := KeyPairAggregate(ctx, aggCreator, pair) - if err != nil { - return nil, err - } - - return agg.AppendEvent(model.KeyPairAdded, pair) - } -} diff --git a/internal/ui/login/handler/register_org_handler.go b/internal/ui/login/handler/register_org_handler.go index aebdaba3bb..b61e75d9f7 100644 --- a/internal/ui/login/handler/register_org_handler.go +++ b/internal/ui/login/handler/register_org_handler.go @@ -4,10 +4,7 @@ import ( "github.com/caos/zitadel/internal/v2/domain" "net/http" - auth_model "github.com/caos/zitadel/internal/auth/model" caos_errs "github.com/caos/zitadel/internal/errors" - org_model "github.com/caos/zitadel/internal/org/model" - usr_model "github.com/caos/zitadel/internal/user/model" ) const ( @@ -61,11 +58,7 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) { return } - registerOrg := &auth_model.RegisterOrg{ - User: data.toUserModel(), - Org: data.toOrgModel(), - } - user, err := l.authRepo.RegisterOrg(setContext(r.Context(), ""), registerOrg) + err = l.command.SetUpOrg(setContext(r.Context(), ""), data.toOrgDomain(), data.toUserDomain()) if err != nil { l.renderRegisterOrg(w, r, authRequest, data, err) return @@ -74,7 +67,6 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, l.zitadelURL, http.StatusFound) return } - authRequest.LoginName = user.PreferredLoginName l.renderNextStep(w, r, authRequest) } @@ -117,29 +109,27 @@ func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRe l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplRegisterOrg], data, nil) } -func (d registerOrgFormData) toUserModel() *usr_model.User { +func (d registerOrgFormData) toUserDomain() *domain.Human { if d.Username == "" { d.Username = d.Email } - return &usr_model.User{ - UserName: d.Username, - Human: &usr_model.Human{ - Profile: &usr_model.Profile{ - FirstName: d.Firstname, - LastName: d.Lastname, - }, - Password: &usr_model.Password{ - SecretString: d.Password, - }, - Email: &usr_model.Email{ - EmailAddress: d.Email, - }, + return &domain.Human{ + Username: d.Username, + Profile: &domain.Profile{ + FirstName: d.Firstname, + LastName: d.Lastname, + }, + Password: &domain.Password{ + SecretString: d.Password, + }, + Email: &domain.Email{ + EmailAddress: d.Email, }, } } -func (d registerOrgFormData) toOrgModel() *org_model.Org { - return &org_model.Org{ +func (d registerOrgFormData) toOrgDomain() *domain.Org { + return &domain.Org{ Name: d.RegisterOrgName, } } diff --git a/internal/v2/command/command.go b/internal/v2/command/command.go index 160a19875c..e896c5e6d4 100644 --- a/internal/v2/command/command.go +++ b/internal/v2/command/command.go @@ -2,6 +2,7 @@ package command import ( "context" + "time" "github.com/caos/zitadel/internal/api/http" sd "github.com/caos/zitadel/internal/config/systemdefaults" @@ -11,6 +12,7 @@ import ( global_model "github.com/caos/zitadel/internal/model" "github.com/caos/zitadel/internal/telemetry/tracing" iam_repo "github.com/caos/zitadel/internal/v2/repository/iam" + keypair "github.com/caos/zitadel/internal/v2/repository/keypair" "github.com/caos/zitadel/internal/v2/repository/org" proj_repo "github.com/caos/zitadel/internal/v2/repository/project" usr_repo "github.com/caos/zitadel/internal/v2/repository/user" @@ -39,6 +41,11 @@ type CommandSide struct { //TODO: remove global model, or move to domain multifactors global_model.Multifactors webauthn *webauthn_helper.WebAuthN + + keySize int + keyAlgorithm crypto.EncryptionAlgorithm + privateKeyLifetime time.Duration + publicKeyLifetime time.Duration } type Config struct { @@ -48,15 +55,19 @@ type Config struct { func StartCommandSide(config *Config) (repo *CommandSide, err error) { repo = &CommandSide{ - eventstore: config.Eventstore, - idGenerator: id.SonyFlakeGenerator, - iamDomain: config.SystemDefaults.Domain, + eventstore: config.Eventstore, + idGenerator: id.SonyFlakeGenerator, + iamDomain: config.SystemDefaults.Domain, + keySize: config.SystemDefaults.KeyConfig.Size, + privateKeyLifetime: config.SystemDefaults.KeyConfig.PrivateKeyLifetime.Duration, + publicKeyLifetime: config.SystemDefaults.KeyConfig.PublicKeyLifetime.Duration, } iam_repo.RegisterEventMappers(repo.eventstore) org.RegisterEventMappers(repo.eventstore) usr_repo.RegisterEventMappers(repo.eventstore) usr_grant_repo.RegisterEventMappers(repo.eventstore) proj_repo.RegisterEventMappers(repo.eventstore) + keypair.RegisterEventMappers(repo.eventstore) //TODO: simplify!!!! repo.idpConfigSecretCrypto, err = crypto.NewAESCrypto(config.SystemDefaults.IDPConfigVerificationKey) @@ -99,6 +110,12 @@ func StartCommandSide(config *Config) (repo *CommandSide, err error) { return nil, err } repo.webauthn = web + + keyAlgorithm, err := crypto.NewAESCrypto(config.SystemDefaults.KeyConfig.EncryptionConfig) + if err != nil { + return nil, err + } + repo.keyAlgorithm = keyAlgorithm return repo, nil } diff --git a/internal/v2/command/key_pair.go b/internal/v2/command/key_pair.go new file mode 100644 index 0000000000..8cbd49b249 --- /dev/null +++ b/internal/v2/command/key_pair.go @@ -0,0 +1,45 @@ +package command + +import ( + "context" + "github.com/caos/zitadel/internal/api/authz" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/v2/domain" + keypair "github.com/caos/zitadel/internal/v2/repository/keypair" + "time" +) + +const ( + oidcUser = "OIDC" +) + +func (r *CommandSide) GenerateSigningKeyPair(ctx context.Context, algorithm string) error { + ctx = setOIDCCtx(ctx) + privateCrypto, publicCrypto, err := crypto.GenerateEncryptedKeyPair(r.keySize, r.keyAlgorithm) + if err != nil { + return err + } + keyID, err := r.idGenerator.Next() + if err != nil { + return err + } + + privateKeyExp := time.Now().UTC().Add(r.privateKeyLifetime) + publicKeyExp := time.Now().UTC().Add(r.publicKeyLifetime) + + keyPairWriteModel := NewKeyPairWriteModel(keyID, domain.IAMID) + keyAgg := KeyPairAggregateFromWriteModel(&keyPairWriteModel.WriteModel) + keyAgg.PushEvents( + keypair.NewAddedEvent( + ctx, + domain.KeyUsageSigning, + algorithm, + privateCrypto, publicCrypto, + privateKeyExp, publicKeyExp), + ) + return r.eventstore.PushAggregate(ctx, keyPairWriteModel, keyAgg) +} + +func setOIDCCtx(ctx context.Context) context.Context { + return authz.SetCtxData(ctx, authz.CtxData{UserID: oidcUser, OrgID: domain.IAMID}) +} diff --git a/internal/v2/command/key_pair_model.go b/internal/v2/command/key_pair_model.go new file mode 100644 index 0000000000..3fe72786b5 --- /dev/null +++ b/internal/v2/command/key_pair_model.go @@ -0,0 +1,61 @@ +package command + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/v2/domain" + keypair "github.com/caos/zitadel/internal/v2/repository/keypair" + "github.com/caos/zitadel/internal/v2/repository/project" +) + +type KeyPairWriteModel struct { + eventstore.WriteModel + + Usage domain.KeyUsage + Algorithm string + PrivateKey *domain.Key + PublicKey *domain.Key +} + +func NewKeyPairWriteModel(aggregateID, resourceOwner string) *KeyPairWriteModel { + return &KeyPairWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: aggregateID, + ResourceOwner: resourceOwner, + }, + } +} + +func (wm *KeyPairWriteModel) AppendEvents(events ...eventstore.EventReader) { + wm.WriteModel.AppendEvents(events...) +} + +func (wm *KeyPairWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *keypair.AddedEvent: + wm.Usage = e.Usage + wm.Algorithm = e.Algorithm + wm.PrivateKey = &domain.Key{ + Key: e.PrivateKey.Key, + Expiry: e.PrivateKey.Expiry, + } + wm.PublicKey = &domain.Key{ + Key: e.PublicKey.Key, + Expiry: e.PublicKey.Expiry, + } + } + } + return wm.WriteModel.Reduce() +} + +func (wm *KeyPairWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, project.AggregateType). + AggregateIDs(wm.AggregateID). + ResourceOwner(wm.ResourceOwner) +} + +func KeyPairAggregateFromWriteModel(wm *eventstore.WriteModel) *keypair.Aggregate { + return &keypair.Aggregate{ + Aggregate: *eventstore.AggregateFromWriteModel(wm, keypair.AggregateType, keypair.AggregateVersion), + } +} diff --git a/internal/v2/domain/key_pair.go b/internal/v2/domain/key_pair.go new file mode 100644 index 0000000000..8215734a1a --- /dev/null +++ b/internal/v2/domain/key_pair.go @@ -0,0 +1,45 @@ +package domain + +import ( + "github.com/caos/zitadel/internal/crypto" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "time" +) + +type KeyPair struct { + es_models.ObjectRoot + + Usage KeyUsage + Algorithm string + PrivateKey *Key + PublicKey *Key +} + +type KeyUsage int32 + +const ( + KeyUsageSigning KeyUsage = iota +) + +func (u KeyUsage) String() string { + switch u { + case KeyUsageSigning: + return "sig" + } + return "" +} + +type Key struct { + Key *crypto.CryptoValue + Expiry time.Time +} + +func (k *KeyPair) IsValid() bool { + return k.Algorithm != "" && + k.PrivateKey != nil && k.PrivateKey.IsValid() && + k.PublicKey != nil && k.PublicKey.IsValid() +} + +func (k *Key) IsValid() bool { + return k.Key != nil +} diff --git a/internal/v2/repository/keypair/aggregate.go b/internal/v2/repository/keypair/aggregate.go new file mode 100644 index 0000000000..44cdd8f152 --- /dev/null +++ b/internal/v2/repository/keypair/aggregate.go @@ -0,0 +1,14 @@ +package usergrant + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" +) + +const ( + AggregateType = "key_pair" + AggregateVersion = "v1" +) + +type Aggregate struct { + eventstore.Aggregate +} diff --git a/internal/v2/repository/keypair/eventstore.go b/internal/v2/repository/keypair/eventstore.go new file mode 100644 index 0000000000..49d9674bda --- /dev/null +++ b/internal/v2/repository/keypair/eventstore.go @@ -0,0 +1,9 @@ +package usergrant + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" +) + +func RegisterEventMappers(es *eventstore.Eventstore) { + es.RegisterFilterEventMapper(AddedEventType, AddedEventMapper) +} diff --git a/internal/v2/repository/keypair/key_pair.go b/internal/v2/repository/keypair/key_pair.go new file mode 100644 index 0000000000..5d81a7aaad --- /dev/null +++ b/internal/v2/repository/keypair/key_pair.go @@ -0,0 +1,78 @@ +package usergrant + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "github.com/caos/zitadel/internal/v2/domain" + "time" +) + +const ( + eventTypePrefix = eventstore.EventType("key_pair.") + AddedEventType = eventTypePrefix + "added" +) + +type AddedEvent struct { + eventstore.BaseEvent `json:"-"` + + Usage domain.KeyUsage `json:"usage"` + Algorithm string `json:"algorithm"` + PrivateKey *Key `json:"privateKey"` + PublicKey *Key `json:"publicKey"` +} + +type Key struct { + Key *crypto.CryptoValue `json:"key"` + Expiry time.Time `json:"expiry"` +} + +func (e *AddedEvent) Data() interface{} { + return e +} + +func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewAddedEvent( + ctx context.Context, + usage domain.KeyUsage, + algorithm string, + privateCrypto, + publicCrypto *crypto.CryptoValue, + privateKeyExpiration, + publicKeyExpiration time.Time) *AddedEvent { + return &AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + AddedEventType, + ), + Usage: usage, + Algorithm: algorithm, + PrivateKey: &Key{ + Key: privateCrypto, + Expiry: privateKeyExpiration, + }, + PublicKey: &Key{ + Key: publicCrypto, + Expiry: publicKeyExpiration, + }, + } +} + +func AddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &AddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "KEY-4n8vs", "unable to unmarshal key pair added") + } + + return e, nil +}