package projection import ( "context" "time" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" "github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/keypair" ) const ( KeyProjectionTable = "projections.keys4" KeyPrivateTable = KeyProjectionTable + "_" + privateKeyTableSuffix KeyPublicTable = KeyProjectionTable + "_" + publicKeyTableSuffix CertificateTable = KeyProjectionTable + "_" + certificateTableSuffix KeyColumnID = "id" KeyColumnCreationDate = "creation_date" KeyColumnChangeDate = "change_date" KeyColumnResourceOwner = "resource_owner" KeyColumnInstanceID = "instance_id" KeyColumnSequence = "sequence" KeyColumnAlgorithm = "algorithm" KeyColumnUse = "use" privateKeyTableSuffix = "private" KeyPrivateColumnID = "id" KeyPrivateColumnInstanceID = "instance_id" KeyPrivateColumnExpiry = "expiry" KeyPrivateColumnKey = "key" publicKeyTableSuffix = "public" KeyPublicColumnID = "id" KeyPublicColumnInstanceID = "instance_id" KeyPublicColumnExpiry = "expiry" KeyPublicColumnKey = "key" certificateTableSuffix = "certificate" CertificateColumnID = "id" CertificateColumnInstanceID = "instance_id" CertificateColumnExpiry = "expiry" CertificateColumnCertificate = "certificate" ) type keyProjection struct { encryptionAlgorithm crypto.EncryptionAlgorithm certEncryptionAlgorithm crypto.EncryptionAlgorithm } func newKeyProjection(ctx context.Context, config handler.Config, keyEncryptionAlgorithm, certEncryptionAlgorithm crypto.EncryptionAlgorithm) *handler.Handler { p := &keyProjection{ encryptionAlgorithm: keyEncryptionAlgorithm, certEncryptionAlgorithm: certEncryptionAlgorithm, } return handler.NewHandler(ctx, &config, p) } func (*keyProjection) Name() string { return KeyProjectionTable } func (*keyProjection) Init() *old_handler.Check { return handler.NewMultiTableCheck( handler.NewTable([]*handler.InitColumn{ handler.NewColumn(KeyColumnID, handler.ColumnTypeText), handler.NewColumn(KeyColumnCreationDate, handler.ColumnTypeTimestamp), handler.NewColumn(KeyColumnChangeDate, handler.ColumnTypeTimestamp), handler.NewColumn(KeyColumnResourceOwner, handler.ColumnTypeText), handler.NewColumn(KeyColumnInstanceID, handler.ColumnTypeText), handler.NewColumn(KeyColumnSequence, handler.ColumnTypeInt64), handler.NewColumn(KeyColumnAlgorithm, handler.ColumnTypeText, handler.Default("")), handler.NewColumn(KeyColumnUse, handler.ColumnTypeEnum, handler.Default(0)), }, handler.NewPrimaryKey(KeyColumnInstanceID, KeyColumnID), ), handler.NewSuffixedTable([]*handler.InitColumn{ handler.NewColumn(KeyPrivateColumnID, handler.ColumnTypeText), handler.NewColumn(KeyPrivateColumnInstanceID, handler.ColumnTypeText), handler.NewColumn(KeyPrivateColumnExpiry, handler.ColumnTypeTimestamp), handler.NewColumn(KeyPrivateColumnKey, handler.ColumnTypeJSONB), }, handler.NewPrimaryKey(KeyPrivateColumnInstanceID, KeyPrivateColumnID), privateKeyTableSuffix, handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), handler.NewSuffixedTable([]*handler.InitColumn{ handler.NewColumn(KeyPublicColumnID, handler.ColumnTypeText), handler.NewColumn(KeyPublicColumnInstanceID, handler.ColumnTypeText), handler.NewColumn(KeyPublicColumnExpiry, handler.ColumnTypeTimestamp), handler.NewColumn(KeyPublicColumnKey, handler.ColumnTypeBytes), }, handler.NewPrimaryKey(KeyPublicColumnInstanceID, KeyPublicColumnID), publicKeyTableSuffix, handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), handler.NewSuffixedTable([]*handler.InitColumn{ handler.NewColumn(CertificateColumnID, handler.ColumnTypeText), handler.NewColumn(CertificateColumnInstanceID, handler.ColumnTypeText), handler.NewColumn(CertificateColumnExpiry, handler.ColumnTypeTimestamp), handler.NewColumn(CertificateColumnCertificate, handler.ColumnTypeBytes), }, handler.NewPrimaryKey(CertificateColumnInstanceID, CertificateColumnID), certificateTableSuffix, handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()), ), ) } func (p *keyProjection) Reducers() []handler.AggregateReducer { return []handler.AggregateReducer{ { Aggregate: keypair.AggregateType, EventReducers: []handler.EventReducer{ { Event: keypair.AddedEventType, Reduce: p.reduceKeyPairAdded, }, { Event: keypair.AddedCertificateEventType, Reduce: p.reduceCertificateAdded, }, }, }, { Aggregate: instance.AggregateType, EventReducers: []handler.EventReducer{ { Event: instance.InstanceRemovedEventType, Reduce: reduceInstanceRemovedHelper(KeyColumnInstanceID), }, }, }, } } func (p *keyProjection) reduceKeyPairAdded(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*keypair.AddedEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAbr2", "reduce.wrong.event.type %s", keypair.AddedEventType) } if e.PrivateKey.Expiry.Before(time.Now()) && e.PublicKey.Expiry.Before(time.Now()) { return handler.NewNoOpStatement(e), nil } creates := []func(eventstore.Event) handler.Exec{ handler.AddCreateStatement( []handler.Column{ handler.NewCol(KeyColumnID, e.Aggregate().ID), handler.NewCol(KeyColumnCreationDate, e.CreationDate()), handler.NewCol(KeyColumnChangeDate, e.CreationDate()), handler.NewCol(KeyColumnResourceOwner, e.Aggregate().ResourceOwner), handler.NewCol(KeyColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(KeyColumnSequence, e.Sequence()), handler.NewCol(KeyColumnAlgorithm, e.Algorithm), handler.NewCol(KeyColumnUse, e.Usage), }, ), } if e.PrivateKey.Expiry.After(time.Now()) { creates = append(creates, handler.AddCreateStatement( []handler.Column{ handler.NewCol(KeyPrivateColumnID, e.Aggregate().ID), handler.NewCol(KeyPrivateColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(KeyPrivateColumnExpiry, e.PrivateKey.Expiry), handler.NewCol(KeyPrivateColumnKey, e.PrivateKey.Key), }, handler.WithTableSuffix(privateKeyTableSuffix), )) } if e.PublicKey.Expiry.After(time.Now()) { publicKey, err := crypto.Decrypt(e.PublicKey.Key, p.encryptionAlgorithm) if err != nil { return nil, errors.ThrowInternal(err, "HANDL-DAg2f", "cannot decrypt public key") } creates = append(creates, handler.AddCreateStatement( []handler.Column{ handler.NewCol(KeyPublicColumnID, e.Aggregate().ID), handler.NewCol(KeyPublicColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(KeyPublicColumnExpiry, e.PublicKey.Expiry), handler.NewCol(KeyPublicColumnKey, publicKey), }, handler.WithTableSuffix(publicKeyTableSuffix), )) } return handler.NewMultiStatement(e, creates...), nil } func (p *keyProjection) reduceCertificateAdded(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*keypair.AddedCertificateEvent) if !ok { return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAbr09", "reduce.wrong.event.type %s", keypair.AddedCertificateEventType) } if e.Certificate.Expiry.Before(time.Now()) { return handler.NewNoOpStatement(e), nil } certificate, err := crypto.Decrypt(e.Certificate.Key, p.certEncryptionAlgorithm) if err != nil { return nil, errors.ThrowInternal(err, "HANDL-Dajwig2f", "cannot decrypt certificate") } creates := []func(eventstore.Event) handler.Exec{handler.AddCreateStatement( []handler.Column{ handler.NewCol(CertificateColumnID, e.Aggregate().ID), handler.NewCol(CertificateColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(CertificateColumnExpiry, e.Certificate.Expiry), handler.NewCol(CertificateColumnCertificate, certificate), }, handler.WithTableSuffix(certificateTableSuffix), )} return handler.NewMultiStatement(e, creates...), nil }