feat: Instance create (#4502)

* feat(instance): implement create instance with direct machine user and credentials

* fix: deprecated add endpoint and variable declaration

* fix(instance): update logic for pats and machinekeys

* fix(instance): unit test corrections and additional unit test for pats and machinekeys

* fix(instance-create): include review changes

* fix(instance-create): linter fixes

* move iframe usage to solution scenarios configurations

* Revert "move iframe usage to solution scenarios configurations"

This reverts commit 9db31f3808.

* fix merge

* fix: add review suggestions

Co-authored-by: Livio Spring <livio.a@gmail.com>

* fix: add review changes

* fix: add review changes for default definitions

* fix: add review changes for machinekey details

* fix: add machinekey output when setup with machineuser

* fix: add changes from review

* fix instance converter for machine and allow overwriting of further machine fields

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Stefan Benz
2022-12-09 13:04:33 +00:00
committed by GitHub
parent c5ebeea590
commit 47ffa52f0f
27 changed files with 1403 additions and 354 deletions

View File

@@ -2,108 +2,204 @@ package command
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/repository/user"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (c *Commands) AddUserMachineKeyWithID(ctx context.Context, machineKey *domain.MachineKey, resourceOwner string) (*domain.MachineKey, error) {
writeModel, err := c.machineKeyWriteModelByID(ctx, machineKey.AggregateID, machineKey.KeyID, resourceOwner)
if err != nil {
return nil, err
}
if writeModel.State != domain.MachineKeyStateUnspecified {
return nil, errors.ThrowNotFound(nil, "COMMAND-p22101", "Errors.User.Machine.Key.AlreadyExisting")
}
return c.addUserMachineKey(ctx, machineKey, resourceOwner)
type AddMachineKey struct {
Type domain.AuthNKeyType
ExpirationDate time.Time
}
func (c *Commands) AddUserMachineKey(ctx context.Context, machineKey *domain.MachineKey, resourceOwner string) (*domain.MachineKey, error) {
keyID, err := c.idGenerator.Next()
if err != nil {
return nil, err
}
machineKey.KeyID = keyID
return c.addUserMachineKey(ctx, machineKey, resourceOwner)
type MachineKey struct {
models.ObjectRoot
KeyID string
Type domain.AuthNKeyType
ExpirationDate time.Time
PrivateKey []byte
PublicKey []byte
}
func (c *Commands) addUserMachineKey(ctx context.Context, machineKey *domain.MachineKey, resourceOwner string) (*domain.MachineKey, error) {
err := c.checkUserExists(ctx, machineKey.AggregateID, resourceOwner)
if err != nil {
return nil, err
}
keyWriteModel := NewMachineKeyWriteModel(machineKey.AggregateID, machineKey.KeyID, resourceOwner)
if err := c.eventstore.FilterToQueryReducer(ctx, keyWriteModel); err != nil {
return nil, err
func NewMachineKey(resourceOwner string, userID string, expirationDate time.Time, keyType domain.AuthNKeyType) *MachineKey {
return &MachineKey{
ObjectRoot: models.ObjectRoot{
AggregateID: userID,
ResourceOwner: resourceOwner,
},
ExpirationDate: expirationDate,
Type: keyType,
}
}
if err := domain.EnsureValidExpirationDate(machineKey); err != nil {
return nil, err
}
func (key *MachineKey) SetPublicKey(publicKey []byte) {
key.PublicKey = publicKey
}
func (key *MachineKey) SetPrivateKey(privateKey []byte) {
key.PrivateKey = privateKey
}
func (key *MachineKey) GetExpirationDate() time.Time {
return key.ExpirationDate
}
func (key *MachineKey) SetExpirationDate(t time.Time) {
key.ExpirationDate = t
}
if len(machineKey.PublicKey) == 0 {
if err := domain.SetNewAuthNKeyPair(machineKey, c.machineKeySize); err != nil {
func (key *MachineKey) Detail() ([]byte, error) {
if len(key.PrivateKey) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "KEY-sp2l2m", "Errors.Internal")
}
if key.Type == domain.AuthNKeyTypeJSON {
return domain.MachineKeyMarshalJSON(key.KeyID, key.PrivateKey, key.AggregateID)
}
return nil, errors.ThrowPreconditionFailed(nil, "KEY-dsg52", "Errors.Internal")
}
func (key *MachineKey) content() error {
if key.ResourceOwner == "" {
return errors.ThrowInvalidArgument(nil, "COMMAND-kqpoix", "Errors.ResourceOwnerMissing")
}
if key.AggregateID == "" {
return errors.ThrowInvalidArgument(nil, "COMMAND-xuiwk2", "Errors.User.UserIDMissing")
}
if key.KeyID == "" {
return errors.ThrowInvalidArgument(nil, "COMMAND-0p2m1h", "Errors.IDMissing")
}
return nil
}
func (key *MachineKey) valid() (err error) {
if err := key.content(); err != nil {
return err
}
key.ExpirationDate, err = domain.ValidateExpirationDate(key.ExpirationDate)
return err
}
func (key *MachineKey) checkAggregate(ctx context.Context, filter preparation.FilterToQueryReducer) error {
if exists, err := ExistsUser(ctx, filter, key.AggregateID, key.ResourceOwner); err != nil || !exists {
return errors.ThrowPreconditionFailed(err, "COMMAND-bnipwm1", "Errors.User.NotFound")
}
return nil
}
func (c *Commands) AddUserMachineKey(ctx context.Context, machineKey *MachineKey) (*domain.ObjectDetails, error) {
if machineKey.KeyID == "" {
keyID, err := c.idGenerator.Next()
if err != nil {
return nil, err
}
machineKey.KeyID = keyID
}
events, err := c.eventstore.Push(ctx,
user.NewMachineKeyAddedEvent(
ctx,
UserAggregateFromWriteModel(&keyWriteModel.WriteModel),
machineKey.KeyID,
machineKey.Type,
machineKey.ExpirationDate,
machineKey.PublicKey))
validation := prepareAddUserMachineKey(machineKey, c.machineKeySize)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
if err != nil {
return nil, err
}
err = AppendAndReduce(keyWriteModel, events...)
events, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}
key := keyWriteModelToMachineKey(keyWriteModel)
if len(machineKey.PrivateKey) > 0 {
key.PrivateKey = machineKey.PrivateKey
}
return key, nil
return &domain.ObjectDetails{
Sequence: events[len(events)-1].Sequence(),
EventDate: events[len(events)-1].CreationDate(),
ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner,
}, nil
}
func (c *Commands) RemoveUserMachineKey(ctx context.Context, userID, keyID, resourceOwner string) (*domain.ObjectDetails, error) {
keyWriteModel, err := c.machineKeyWriteModelByID(ctx, userID, keyID, resourceOwner)
if err != nil {
return nil, err
func prepareAddUserMachineKey(machineKey *MachineKey, keySize int) preparation.Validation {
return func() (_ preparation.CreateCommands, err error) {
if err := machineKey.valid(); err != nil {
return nil, err
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
if err := machineKey.checkAggregate(ctx, filter); err != nil {
return nil, err
}
if len(machineKey.PublicKey) == 0 {
if err = domain.SetNewAuthNKeyPair(machineKey, keySize); err != nil {
return nil, err
}
}
writeModel, err := getMachineKeyWriteModelByID(ctx, filter, machineKey.AggregateID, machineKey.KeyID, machineKey.ResourceOwner)
if err != nil {
return nil, err
}
if writeModel.Exists() {
return nil, errors.ThrowAlreadyExists(nil, "COMMAND-091mops", "Errors.User.Machine.Key.AlreadyExists")
}
return []eventstore.Command{
user.NewMachineKeyAddedEvent(
ctx,
UserAggregateFromWriteModel(&writeModel.WriteModel),
machineKey.KeyID,
machineKey.Type,
machineKey.ExpirationDate,
machineKey.PublicKey,
),
}, nil
}, nil
}
if !keyWriteModel.Exists() {
return nil, errors.ThrowNotFound(nil, "COMMAND-4m77G", "Errors.User.Machine.Key.NotFound")
}
pushedEvents, err := c.eventstore.Push(ctx,
user.NewMachineKeyRemovedEvent(ctx, UserAggregateFromWriteModel(&keyWriteModel.WriteModel), keyID))
if err != nil {
return nil, err
}
err = AppendAndReduce(keyWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&keyWriteModel.WriteModel), nil
}
func (c *Commands) machineKeyWriteModelByID(ctx context.Context, userID, keyID, resourceOwner string) (writeModel *MachineKeyWriteModel, err error) {
if userID == "" {
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-4n8vs", "Errors.User.UserIDMissing")
}
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewMachineKeyWriteModel(userID, keyID, resourceOwner)
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
func (c *Commands) RemoveUserMachineKey(ctx context.Context, machineKey *MachineKey) (*domain.ObjectDetails, error) {
validation := prepareRemoveUserMachineKey(machineKey)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
if err != nil {
return nil, err
}
return writeModel, nil
events, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}
return &domain.ObjectDetails{
Sequence: events[len(events)-1].Sequence(),
EventDate: events[len(events)-1].CreationDate(),
ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner,
}, nil
}
func prepareRemoveUserMachineKey(machineKey *MachineKey) preparation.Validation {
return func() (_ preparation.CreateCommands, err error) {
if err := machineKey.content(); err != nil {
return nil, err
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
writeModel, err := getMachineKeyWriteModelByID(ctx, filter, machineKey.AggregateID, machineKey.KeyID, machineKey.ResourceOwner)
if err != nil {
return nil, err
}
if !writeModel.Exists() {
return nil, errors.ThrowNotFound(nil, "COMMAND-4m77G", "Errors.User.Machine.Key.NotFound")
}
return []eventstore.Command{
user.NewMachineKeyRemovedEvent(
ctx,
UserAggregateFromWriteModel(&writeModel.WriteModel),
machineKey.KeyID,
),
}, nil
}, nil
}
}
func getMachineKeyWriteModelByID(ctx context.Context, filter preparation.FilterToQueryReducer, userID, keyID, resourceOwner string) (_ *MachineKeyWriteModel, err error) {
writeModel := NewMachineKeyWriteModel(userID, keyID, resourceOwner)
events, err := filter(ctx, writeModel.Query())
if err != nil {
return nil, err
}
if len(events) == 0 {
return writeModel, nil
}
writeModel.AppendEvents(events...)
err = writeModel.Reduce()
return writeModel, err
}