mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 18:07:31 +00:00
chore!: Introduce ZITADEL v3 (#9645)
This PR summarizes multiple changes specifically only available with ZITADEL v3: - feat: Web Keys management (https://github.com/zitadel/zitadel/pull/9526) - fix(cmd): ensure proper working of mirror (https://github.com/zitadel/zitadel/pull/9509) - feat(Authz): system user support for permission check v2 (https://github.com/zitadel/zitadel/pull/9640) - chore(license): change from Apache to AGPL (https://github.com/zitadel/zitadel/pull/9597) - feat(console): list v2 sessions (https://github.com/zitadel/zitadel/pull/9539) - fix(console): add loginV2 feature flag (https://github.com/zitadel/zitadel/pull/9682) - fix(feature flags): allow reading "own" flags (https://github.com/zitadel/zitadel/pull/9649) - feat(console): add Actions V2 UI (https://github.com/zitadel/zitadel/pull/9591) BREAKING CHANGE - feat(webkey): migrate to v2beta API (https://github.com/zitadel/zitadel/pull/9445) - chore!: remove CockroachDB Support (https://github.com/zitadel/zitadel/pull/9444) - feat(actions): migrate to v2beta API (https://github.com/zitadel/zitadel/pull/9489) --------- Co-authored-by: Livio Spring <livio.a@gmail.com> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com> Co-authored-by: Ramon <mail@conblem.me> Co-authored-by: Elio Bischof <elio@zitadel.com> Co-authored-by: Kenta Yamaguchi <56732734+KEY60228@users.noreply.github.com> Co-authored-by: Harsha Reddy <harsha.reddy@klaviyo.com> Co-authored-by: Livio Spring <livio@zitadel.com> Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Iraq <66622793+kkrime@users.noreply.github.com> Co-authored-by: Florian Forster <florian@zitadel.com> Co-authored-by: Tim Möhlmann <tim+github@zitadel.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Max Peintner <peintnerm@gmail.com>
This commit is contained in:
@@ -20,6 +20,7 @@ func existsMock(exists bool) func(method string) bool {
|
||||
return exists
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_SetExecutionRequest(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
|
@@ -40,31 +40,31 @@ func (a *AddTarget) IsValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddTarget(ctx context.Context, add *AddTarget, resourceOwner string) (_ *domain.ObjectDetails, err error) {
|
||||
func (c *Commands) AddTarget(ctx context.Context, add *AddTarget, resourceOwner string) (_ time.Time, err error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-brml926e2d", "Errors.IDMissing")
|
||||
return time.Time{}, zerrors.ThrowInvalidArgument(nil, "COMMAND-brml926e2d", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
if err := add.IsValid(); err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
if add.AggregateID == "" {
|
||||
add.AggregateID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
}
|
||||
wm, err := c.getTargetWriteModelByID(ctx, add.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
if wm.State.Exists() {
|
||||
return nil, zerrors.ThrowAlreadyExists(nil, "INSTANCE-9axkz0jvzm", "Errors.Target.AlreadyExists")
|
||||
return time.Time{}, zerrors.ThrowAlreadyExists(nil, "INSTANCE-9axkz0jvzm", "Errors.Target.AlreadyExists")
|
||||
}
|
||||
code, err := c.newSigningKey(ctx, c.eventstore.Filter, c.targetEncryption) //nolint
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
add.SigningKey = code.PlainCode()
|
||||
pushedEvents, err := c.eventstore.Push(ctx, target.NewAddedEvent(
|
||||
@@ -78,12 +78,12 @@ func (c *Commands) AddTarget(ctx context.Context, add *AddTarget, resourceOwner
|
||||
code.Crypted,
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
if err := AppendAndReduce(wm, pushedEvents...); err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
return writeModelToObjectDetails(&wm.WriteModel), nil
|
||||
return wm.ChangeDate, nil
|
||||
}
|
||||
|
||||
type ChangeTarget struct {
|
||||
@@ -118,26 +118,26 @@ func (a *ChangeTarget) IsValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeTarget(ctx context.Context, change *ChangeTarget, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) ChangeTarget(ctx context.Context, change *ChangeTarget, resourceOwner string) (time.Time, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-zqibgg0wwh", "Errors.IDMissing")
|
||||
return time.Time{}, zerrors.ThrowInvalidArgument(nil, "COMMAND-zqibgg0wwh", "Errors.IDMissing")
|
||||
}
|
||||
if err := change.IsValid(); err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
existing, err := c.getTargetWriteModelByID(ctx, change.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
if !existing.State.Exists() {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMAND-xj14f2cccn", "Errors.Target.NotFound")
|
||||
return time.Time{}, zerrors.ThrowNotFound(nil, "COMMAND-xj14f2cccn", "Errors.Target.NotFound")
|
||||
}
|
||||
|
||||
var changedSigningKey *crypto.CryptoValue
|
||||
if change.ExpirationSigningKey {
|
||||
code, err := c.newSigningKey(ctx, c.eventstore.Filter, c.targetEncryption) //nolint
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
changedSigningKey = code.Crypted
|
||||
change.SigningKey = &code.Plain
|
||||
@@ -154,30 +154,30 @@ func (c *Commands) ChangeTarget(ctx context.Context, change *ChangeTarget, resou
|
||||
changedSigningKey,
|
||||
)
|
||||
if changedEvent == nil {
|
||||
return writeModelToObjectDetails(&existing.WriteModel), nil
|
||||
return existing.WriteModel.ChangeDate, nil
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, changedEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
err = AppendAndReduce(existing, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existing.WriteModel), nil
|
||||
return existing.WriteModel.ChangeDate, nil
|
||||
}
|
||||
|
||||
func (c *Commands) DeleteTarget(ctx context.Context, id, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) DeleteTarget(ctx context.Context, id, resourceOwner string) (time.Time, error) {
|
||||
if id == "" || resourceOwner == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-obqos2l3no", "Errors.IDMissing")
|
||||
return time.Time{}, zerrors.ThrowInvalidArgument(nil, "COMMAND-obqos2l3no", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existing, err := c.getTargetWriteModelByID(ctx, id, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
if !existing.State.Exists() {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMAND-k4s7ucu0ax", "Errors.Target.NotFound")
|
||||
return existing.WriteModel.ChangeDate, nil
|
||||
}
|
||||
|
||||
if err := c.pushAppendAndReduce(ctx,
|
||||
@@ -187,9 +187,9 @@ func (c *Commands) DeleteTarget(ctx context.Context, id, resourceOwner string) (
|
||||
existing.Name,
|
||||
),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existing.WriteModel), nil
|
||||
return existing.WriteModel.ChangeDate, nil
|
||||
}
|
||||
|
||||
func (c *Commands) existsTargetsByIDs(ctx context.Context, ids []string, resourceOwner string) bool {
|
||||
|
@@ -31,9 +31,8 @@ func TestCommands_AddTarget(t *testing.T) {
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
id string
|
||||
details *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
id string
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -213,10 +212,6 @@ func TestCommands_AddTarget(t *testing.T) {
|
||||
},
|
||||
res{
|
||||
id: "id1",
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance",
|
||||
ID: "id1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -249,10 +244,6 @@ func TestCommands_AddTarget(t *testing.T) {
|
||||
},
|
||||
res{
|
||||
id: "id1",
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance",
|
||||
ID: "id1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -264,7 +255,7 @@ func TestCommands_AddTarget(t *testing.T) {
|
||||
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
|
||||
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
|
||||
}
|
||||
details, err := c.AddTarget(tt.args.ctx, tt.args.add, tt.args.resourceOwner)
|
||||
_, err := c.AddTarget(tt.args.ctx, tt.args.add, tt.args.resourceOwner)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -273,7 +264,6 @@ func TestCommands_AddTarget(t *testing.T) {
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.id, tt.args.add.AggregateID)
|
||||
assertObjectDetails(t, tt.res.details, details)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -291,8 +281,7 @@ func TestCommands_ChangeTarget(t *testing.T) {
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
details *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -434,12 +423,7 @@ func TestCommands_ChangeTarget(t *testing.T) {
|
||||
},
|
||||
resourceOwner: "instance",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance",
|
||||
ID: "id1",
|
||||
},
|
||||
},
|
||||
res{},
|
||||
},
|
||||
{
|
||||
"unique constraint failed, error",
|
||||
@@ -504,12 +488,7 @@ func TestCommands_ChangeTarget(t *testing.T) {
|
||||
},
|
||||
resourceOwner: "instance",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance",
|
||||
ID: "id1",
|
||||
},
|
||||
},
|
||||
res{},
|
||||
},
|
||||
{
|
||||
"push full ok",
|
||||
@@ -557,12 +536,7 @@ func TestCommands_ChangeTarget(t *testing.T) {
|
||||
},
|
||||
resourceOwner: "instance",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance",
|
||||
ID: "id1",
|
||||
},
|
||||
},
|
||||
res{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -572,16 +546,13 @@ func TestCommands_ChangeTarget(t *testing.T) {
|
||||
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
|
||||
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
|
||||
}
|
||||
details, err := c.ChangeTarget(tt.args.ctx, tt.args.change, tt.args.resourceOwner)
|
||||
_, err := c.ChangeTarget(tt.args.ctx, tt.args.change, tt.args.resourceOwner)
|
||||
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 tt.res.err == nil {
|
||||
assertObjectDetails(t, tt.res.details, details)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -596,8 +567,7 @@ func TestCommands_DeleteTarget(t *testing.T) {
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
details *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -631,9 +601,7 @@ func TestCommands_DeleteTarget(t *testing.T) {
|
||||
id: "id1",
|
||||
resourceOwner: "instance",
|
||||
},
|
||||
res{
|
||||
err: zerrors.IsNotFound,
|
||||
},
|
||||
res{},
|
||||
},
|
||||
{
|
||||
"remove ok",
|
||||
@@ -657,12 +625,31 @@ func TestCommands_DeleteTarget(t *testing.T) {
|
||||
id: "id1",
|
||||
resourceOwner: "instance",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance",
|
||||
ID: "id1",
|
||||
},
|
||||
res{},
|
||||
},
|
||||
{
|
||||
"already removed",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
targetAddEvent("id1", "instance"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
target.NewRemovedEvent(context.Background(),
|
||||
target.NewAggregate("id1", "instance"),
|
||||
"name",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
id: "id1",
|
||||
resourceOwner: "instance",
|
||||
},
|
||||
res{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -670,16 +657,13 @@ func TestCommands_DeleteTarget(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
details, err := c.DeleteTarget(tt.args.ctx, tt.args.id, tt.args.resourceOwner)
|
||||
_, err := c.DeleteTarget(tt.args.ctx, tt.args.id, tt.args.resourceOwner)
|
||||
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 tt.res.err == nil {
|
||||
assertObjectDetails(t, tt.res.details, details)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,9 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -177,10 +179,15 @@ func StartCommands(
|
||||
defaultSecretGenerators: defaultSecretGenerators,
|
||||
samlCertificateAndKeyGenerator: samlCertificateAndKeyGenerator(defaults.KeyConfig.CertificateSize, defaults.KeyConfig.CertificateLifetime),
|
||||
webKeyGenerator: crypto.GenerateEncryptedWebKey,
|
||||
// always true for now until we can check with an eventlist
|
||||
EventExisting: func(event string) bool { return true },
|
||||
// always true for now until we can check with an eventlist
|
||||
EventGroupExisting: func(group string) bool { return true },
|
||||
EventExisting: func(value string) bool {
|
||||
return slices.Contains(es.EventTypes(), value)
|
||||
},
|
||||
EventGroupExisting: func(group string) bool {
|
||||
return slices.ContainsFunc(es.EventTypes(), func(value string) bool {
|
||||
return strings.HasPrefix(value, group)
|
||||
},
|
||||
)
|
||||
},
|
||||
GrpcServiceExisting: func(service string) bool { return false },
|
||||
GrpcMethodExisting: func(method string) bool { return false },
|
||||
ActionFunctionExisting: domain.ActionFunctionExists(),
|
||||
@@ -218,33 +225,6 @@ func (c *Commands) pushAppendAndReduce(ctx context.Context, object AppendReducer
|
||||
return AppendAndReduce(object, events...)
|
||||
}
|
||||
|
||||
// pushChunked pushes the commands in chunks of size to the eventstore.
|
||||
// This can be used to reduce the amount of events in a single transaction.
|
||||
// When an error occurs, the events that have been pushed so far will be returned.
|
||||
//
|
||||
// Warning: chunks are pushed in separate transactions.
|
||||
// Successful pushes will not be rolled back if a later chunk fails.
|
||||
// Only use this function when the caller is able to handle partial success
|
||||
// and is able to consolidate the state on errors.
|
||||
func (c *Commands) pushChunked(ctx context.Context, size uint16, cmds ...eventstore.Command) (_ []eventstore.Event, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
events := make([]eventstore.Event, 0, len(cmds))
|
||||
for i := 0; i < len(cmds); i += int(size) {
|
||||
end := i + int(size)
|
||||
if end > len(cmds) {
|
||||
end = len(cmds)
|
||||
}
|
||||
chunk, err := c.eventstore.Push(ctx, cmds[i:end]...)
|
||||
if err != nil {
|
||||
return events, err
|
||||
}
|
||||
events = append(events, chunk...)
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
type AppendReducerDetails interface {
|
||||
AppendEvents(...eventstore.Event)
|
||||
// TODO: Why is it allowed to return an error here?
|
||||
|
@@ -2,7 +2,6 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -14,7 +13,6 @@ import (
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/i18n"
|
||||
"github.com/zitadel/zitadel/internal/repository/permission"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
@@ -31,93 +29,6 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestCommands_pushChunked(t *testing.T) {
|
||||
aggregate := permission.NewAggregate("instanceID")
|
||||
cmds := make([]eventstore.Command, 100)
|
||||
for i := 0; i < 100; i++ {
|
||||
cmds[i] = permission.NewAddedEvent(context.Background(), aggregate, "role", fmt.Sprintf("permission%d", i))
|
||||
}
|
||||
type args struct {
|
||||
size uint16
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
wantEvents int
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "push error",
|
||||
args: args{
|
||||
size: 100,
|
||||
},
|
||||
eventstore: expectEventstore(
|
||||
expectPushFailed(io.ErrClosedPipe, cmds...),
|
||||
),
|
||||
wantEvents: 0,
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
{
|
||||
name: "single chunk",
|
||||
args: args{
|
||||
size: 100,
|
||||
},
|
||||
eventstore: expectEventstore(
|
||||
expectPush(cmds...),
|
||||
),
|
||||
wantEvents: len(cmds),
|
||||
},
|
||||
{
|
||||
name: "aligned chunks",
|
||||
args: args{
|
||||
size: 50,
|
||||
},
|
||||
eventstore: expectEventstore(
|
||||
expectPush(cmds[0:50]...),
|
||||
expectPush(cmds[50:100]...),
|
||||
),
|
||||
wantEvents: len(cmds),
|
||||
},
|
||||
{
|
||||
name: "odd chunks",
|
||||
args: args{
|
||||
size: 30,
|
||||
},
|
||||
eventstore: expectEventstore(
|
||||
expectPush(cmds[0:30]...),
|
||||
expectPush(cmds[30:60]...),
|
||||
expectPush(cmds[60:90]...),
|
||||
expectPush(cmds[90:100]...),
|
||||
),
|
||||
wantEvents: len(cmds),
|
||||
},
|
||||
{
|
||||
name: "partial error",
|
||||
args: args{
|
||||
size: 30,
|
||||
},
|
||||
eventstore: expectEventstore(
|
||||
expectPush(cmds[0:30]...),
|
||||
expectPush(cmds[30:60]...),
|
||||
expectPushFailed(io.ErrClosedPipe, cmds[60:90]...),
|
||||
),
|
||||
wantEvents: len(cmds[0:60]),
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.eventstore(t),
|
||||
}
|
||||
gotEvents, err := c.pushChunked(context.Background(), tt.args.size, cmds...)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.Len(t, gotEvents, tt.wantEvents)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_asyncPush(t *testing.T) {
|
||||
// make sure the test terminates on deadlock
|
||||
background := context.Background()
|
||||
|
@@ -17,15 +17,9 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
CockroachRollPermissionChunkSize uint16 = 50
|
||||
)
|
||||
|
||||
// SynchronizeRolePermission checks the current state of role permissions in the eventstore for the aggregate.
|
||||
// It pushes the commands required to reach the desired state passed in target.
|
||||
// For system level permissions aggregateID must be set to `SYSTEM`, else it is the instance ID.
|
||||
//
|
||||
// In case cockroachDB is used, the commands are pushed in chunks of CockroachRollPermissionChunkSize.
|
||||
func (c *Commands) SynchronizeRolePermission(ctx context.Context, aggregateID string, target []authz.RoleMapping) (_ *domain.ObjectDetails, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
@@ -36,13 +30,9 @@ func (c *Commands) SynchronizeRolePermission(ctx context.Context, aggregateID st
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "COMMA-Iej2r", "Errors.Internal")
|
||||
}
|
||||
var events []eventstore.Event
|
||||
if c.eventstore.Client().Database.Type() == "cockroach" {
|
||||
events, err = c.pushChunked(ctx, CockroachRollPermissionChunkSize, cmds...)
|
||||
} else {
|
||||
events, err = c.eventstore.Push(ctx, cmds...)
|
||||
}
|
||||
events, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
logging.WithError(err).Error("failed to push role permission commands")
|
||||
return nil, zerrors.ThrowInternal(err, "COMMA-AiV3u", "Errors.Internal")
|
||||
}
|
||||
return pushedEventsToObjectDetails(events), nil
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
@@ -156,27 +157,28 @@ func (c *Commands) getAllWebKeys(ctx context.Context) (_ map[string]*WebKeyWrite
|
||||
return models.keys, models.activeID, nil
|
||||
}
|
||||
|
||||
func (c *Commands) DeleteWebKey(ctx context.Context, keyID string) (_ *domain.ObjectDetails, err error) {
|
||||
func (c *Commands) DeleteWebKey(ctx context.Context, keyID string) (_ time.Time, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
model := NewWebKeyWriteModel(keyID, authz.GetInstance(ctx).InstanceID())
|
||||
if err = c.eventstore.FilterToQueryReducer(ctx, model); err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
if model.State == domain.WebKeyStateUnspecified {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMAND-ooCa7", "Errors.WebKey.NotFound")
|
||||
if model.State == domain.WebKeyStateUnspecified ||
|
||||
model.State == domain.WebKeyStateRemoved {
|
||||
return model.WriteModel.ChangeDate, nil
|
||||
}
|
||||
if model.State == domain.WebKeyStateActive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Chai1", "Errors.WebKey.ActiveDelete")
|
||||
return time.Time{}, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Chai1", "Errors.WebKey.ActiveDelete")
|
||||
}
|
||||
err = c.pushAppendAndReduce(ctx, model, webkey.NewRemovedEvent(ctx,
|
||||
webkey.AggregateFromWriteModel(ctx, &model.WriteModel),
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
return writeModelToObjectDetails(&model.WriteModel), nil
|
||||
return model.WriteModel.ChangeDate, nil
|
||||
}
|
||||
|
||||
func (c *Commands) prepareGenerateInitialWebKeys(instanceID string, conf crypto.WebKeyConfig) preparation.Validation {
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -610,7 +611,7 @@ func TestCommands_DeleteWebKey(t *testing.T) {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *domain.ObjectDetails
|
||||
want time.Time
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
@@ -624,14 +625,73 @@ func TestCommands_DeleteWebKey(t *testing.T) {
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
name: "not found",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{"key1"},
|
||||
wantErr: zerrors.ThrowNotFound(nil, "COMMAND-ooCa7", "Errors.WebKey.NotFound"),
|
||||
args: args{"key1"},
|
||||
want: time.Time{},
|
||||
},
|
||||
{
|
||||
name: "previously deleted",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(mustNewWebkeyAddedEvent(ctx,
|
||||
webkey.NewAggregate("key1", "instance1"),
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "encKey",
|
||||
Crypted: []byte("crypted"),
|
||||
},
|
||||
&jose.JSONWebKey{
|
||||
Key: &key.PublicKey,
|
||||
KeyID: "key1",
|
||||
Algorithm: string(jose.ES384),
|
||||
Use: crypto.KeyUsageSigning.String(),
|
||||
},
|
||||
&crypto.WebKeyECDSAConfig{
|
||||
Curve: crypto.EllipticCurveP384,
|
||||
},
|
||||
)),
|
||||
eventFromEventPusher(webkey.NewActivatedEvent(ctx,
|
||||
webkey.NewAggregate("key1", "instance1"),
|
||||
)),
|
||||
eventFromEventPusher(mustNewWebkeyAddedEvent(ctx,
|
||||
webkey.NewAggregate("key2", "instance1"),
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "encKey",
|
||||
Crypted: []byte("crypted"),
|
||||
},
|
||||
&jose.JSONWebKey{
|
||||
Key: &key.PublicKey,
|
||||
KeyID: "key2",
|
||||
Algorithm: string(jose.ES384),
|
||||
Use: crypto.KeyUsageSigning.String(),
|
||||
},
|
||||
&crypto.WebKeyECDSAConfig{
|
||||
Curve: crypto.EllipticCurveP384,
|
||||
},
|
||||
)),
|
||||
eventFromEventPusher(webkey.NewActivatedEvent(ctx,
|
||||
webkey.NewAggregate("key2", "instance1"),
|
||||
)),
|
||||
eventFromEventPusher(webkey.NewDeactivatedEvent(ctx,
|
||||
webkey.NewAggregate("key1", "instance1"),
|
||||
)),
|
||||
eventFromEventPusher(webkey.NewRemovedEvent(ctx,
|
||||
webkey.NewAggregate("key1", "instance1"),
|
||||
)),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{"key1"},
|
||||
want: time.Time{},
|
||||
},
|
||||
{
|
||||
name: "key active error",
|
||||
@@ -722,10 +782,7 @@ func TestCommands_DeleteWebKey(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{"key1"},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "key1",
|
||||
},
|
||||
want: time.Time{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
Reference in New Issue
Block a user