mirror of
https://github.com/zitadel/zitadel.git
synced 2025-10-09 11:12:01 +00:00
feat: implement user schema management (#7416)
This PR adds the functionality to manage user schemas through the new user schema service. It includes the possibility to create a basic JSON schema and also provides a way on defining permissions (read, write) for owner and self context with an annotation. Further annotations for OIDC claims and SAML attribute mappings will follow. A guide on how to create a schema and assign permissions has been started. It will be extended though out the process of implementing the schema and users based on those. Note: This feature is in an early stage and therefore not enabled by default. To test it out, please enable the UserSchema feature flag on your instance / system though the feature service.
This commit is contained in:
@@ -15,12 +15,14 @@ type InstanceFeatures struct {
|
||||
LoginDefaultOrg *bool
|
||||
TriggerIntrospectionProjections *bool
|
||||
LegacyIntrospection *bool
|
||||
UserSchema *bool
|
||||
}
|
||||
|
||||
func (m *InstanceFeatures) isEmpty() bool {
|
||||
return m.LoginDefaultOrg == nil &&
|
||||
m.TriggerIntrospectionProjections == nil &&
|
||||
m.LegacyIntrospection == nil
|
||||
m.LegacyIntrospection == nil &&
|
||||
m.UserSchema == nil
|
||||
}
|
||||
|
||||
func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures) (*domain.ObjectDetails, error) {
|
||||
|
@@ -54,6 +54,7 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.InstanceLoginDefaultOrgEventType,
|
||||
feature_v2.InstanceTriggerIntrospectionProjectionsEventType,
|
||||
feature_v2.InstanceLegacyIntrospectionEventType,
|
||||
feature_v2.InstanceUserSchemaEventType,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -62,6 +63,7 @@ func (m *InstanceFeaturesWriteModel) reduceReset() {
|
||||
m.LoginDefaultOrg = nil
|
||||
m.TriggerIntrospectionProjections = nil
|
||||
m.LegacyIntrospection = nil
|
||||
m.UserSchema = nil
|
||||
}
|
||||
|
||||
func (m *InstanceFeaturesWriteModel) reduceBoolFeature(event *feature_v2.SetEvent[bool]) error {
|
||||
@@ -78,6 +80,8 @@ func (m *InstanceFeaturesWriteModel) reduceBoolFeature(event *feature_v2.SetEven
|
||||
m.TriggerIntrospectionProjections = &event.Value
|
||||
case feature.KeyLegacyIntrospection:
|
||||
m.LegacyIntrospection = &event.Value
|
||||
case feature.KeyUserSchema:
|
||||
m.UserSchema = &event.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -88,5 +92,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LoginDefaultOrg, f.LoginDefaultOrg, feature_v2.InstanceLoginDefaultOrgEventType)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TriggerIntrospectionProjections, f.TriggerIntrospectionProjections, feature_v2.InstanceTriggerIntrospectionProjectionsEventType)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LegacyIntrospection, f.LegacyIntrospection, feature_v2.InstanceLegacyIntrospectionEventType)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.InstanceUserSchemaEventType)
|
||||
return cmds
|
||||
}
|
||||
|
@@ -131,6 +131,24 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
ResourceOwner: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "set UserSchema",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceUserSchemaEventType, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{ctx, &InstanceFeatures{
|
||||
UserSchema: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "push error",
|
||||
eventstore: expectEventstore(
|
||||
@@ -164,12 +182,17 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceLegacyIntrospectionEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
ctx, aggregate,
|
||||
feature_v2.InstanceUserSchemaEventType, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{ctx, &InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
TriggerIntrospectionProjections: gu.Ptr(false),
|
||||
LegacyIntrospection: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
|
@@ -12,12 +12,14 @@ type SystemFeatures struct {
|
||||
LoginDefaultOrg *bool
|
||||
TriggerIntrospectionProjections *bool
|
||||
LegacyIntrospection *bool
|
||||
UserSchema *bool
|
||||
}
|
||||
|
||||
func (m *SystemFeatures) isEmpty() bool {
|
||||
return m.LoginDefaultOrg == nil &&
|
||||
m.TriggerIntrospectionProjections == nil &&
|
||||
m.LegacyIntrospection == nil
|
||||
m.LegacyIntrospection == nil &&
|
||||
m.UserSchema == nil
|
||||
}
|
||||
|
||||
func (c *Commands) SetSystemFeatures(ctx context.Context, f *SystemFeatures) (*domain.ObjectDetails, error) {
|
||||
|
@@ -49,6 +49,7 @@ func (m *SystemFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
feature_v2.SystemLoginDefaultOrgEventType,
|
||||
feature_v2.SystemTriggerIntrospectionProjectionsEventType,
|
||||
feature_v2.SystemLegacyIntrospectionEventType,
|
||||
feature_v2.SystemUserSchemaEventType,
|
||||
).
|
||||
Builder().ResourceOwner(m.ResourceOwner)
|
||||
}
|
||||
@@ -57,6 +58,7 @@ func (m *SystemFeaturesWriteModel) reduceReset() {
|
||||
m.LoginDefaultOrg = nil
|
||||
m.TriggerIntrospectionProjections = nil
|
||||
m.LegacyIntrospection = nil
|
||||
m.UserSchema = nil
|
||||
}
|
||||
|
||||
func (m *SystemFeaturesWriteModel) reduceBoolFeature(event *feature_v2.SetEvent[bool]) error {
|
||||
@@ -73,6 +75,8 @@ func (m *SystemFeaturesWriteModel) reduceBoolFeature(event *feature_v2.SetEvent[
|
||||
m.TriggerIntrospectionProjections = &event.Value
|
||||
case feature.KeyLegacyIntrospection:
|
||||
m.LegacyIntrospection = &event.Value
|
||||
case feature.KeyUserSchema:
|
||||
m.UserSchema = &event.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -83,6 +87,7 @@ func (wm *SystemFeaturesWriteModel) setCommands(ctx context.Context, f *SystemFe
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LoginDefaultOrg, f.LoginDefaultOrg, feature_v2.SystemLoginDefaultOrgEventType)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TriggerIntrospectionProjections, f.TriggerIntrospectionProjections, feature_v2.SystemTriggerIntrospectionProjectionsEventType)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LegacyIntrospection, f.LegacyIntrospection, feature_v2.SystemLegacyIntrospectionEventType)
|
||||
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.SystemUserSchemaEventType)
|
||||
return cmds
|
||||
}
|
||||
|
||||
|
@@ -99,6 +99,24 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
ResourceOwner: "SYSTEM",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "set UserSchema",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
feature_v2.NewSetEvent[bool](
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemUserSchemaEventType, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{context.Background(), &SystemFeatures{
|
||||
UserSchema: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "push error",
|
||||
eventstore: expectEventstore(
|
||||
@@ -132,12 +150,17 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemLegacyIntrospectionEventType, true,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemUserSchemaEventType, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{context.Background(), &SystemFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
TriggerIntrospectionProjections: gu.Ptr(false),
|
||||
LegacyIntrospection: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
@@ -178,12 +201,17 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemTriggerIntrospectionProjectionsEventType, false,
|
||||
),
|
||||
feature_v2.NewSetEvent[bool](
|
||||
context.Background(), aggregate,
|
||||
feature_v2.SystemUserSchemaEventType, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
args: args{context.Background(), &SystemFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
TriggerIntrospectionProjections: gu.Ptr(false),
|
||||
LegacyIntrospection: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
|
180
internal/command/user_schema.go
Normal file
180
internal/command/user_schema.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
domain_schema "github.com/zitadel/zitadel/internal/domain/schema"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/schema"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type CreateUserSchema struct {
|
||||
ResourceOwner string
|
||||
Type string
|
||||
Schema json.RawMessage
|
||||
PossibleAuthenticators []domain.AuthenticatorType
|
||||
}
|
||||
|
||||
func (s *CreateUserSchema) Valid() error {
|
||||
if s.Type == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMA-DGFj3", "Errors.UserSchema.Type.Missing")
|
||||
}
|
||||
if err := validateUserSchema(s.Schema); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, authenticator := range s.PossibleAuthenticators {
|
||||
if authenticator == domain.AuthenticatorTypeUnspecified {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMA-Gh652", "Errors.UserSchema.Authenticator.Invalid")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateUserSchema struct {
|
||||
ID string
|
||||
ResourceOwner string
|
||||
Type *string
|
||||
Schema json.RawMessage
|
||||
PossibleAuthenticators []domain.AuthenticatorType
|
||||
}
|
||||
|
||||
func (s *UpdateUserSchema) Valid() error {
|
||||
if s.ID == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMA-H5421", "Errors.IDMissing")
|
||||
}
|
||||
if s.Type != nil && *s.Type == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMA-G43gn", "Errors.UserSchema.Type.Missing")
|
||||
}
|
||||
if err := validateUserSchema(s.Schema); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, authenticator := range s.PossibleAuthenticators {
|
||||
if authenticator == domain.AuthenticatorTypeUnspecified {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMA-WF4hg", "Errors.UserSchema.Authenticator.Invalid")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) CreateUserSchema(ctx context.Context, userSchema *CreateUserSchema) (string, *domain.ObjectDetails, error) {
|
||||
if err := userSchema.Valid(); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if userSchema.ResourceOwner == "" {
|
||||
return "", nil, zerrors.ThrowInvalidArgument(nil, "COMMA-J3hhj", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
id, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(id, userSchema.ResourceOwner)
|
||||
err = c.pushAppendAndReduce(ctx, writeModel,
|
||||
schema.NewCreatedEvent(ctx,
|
||||
UserSchemaAggregateFromWriteModel(&writeModel.WriteModel),
|
||||
userSchema.Type, userSchema.Schema, userSchema.PossibleAuthenticators,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return id, writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) UpdateUserSchema(ctx context.Context, userSchema *UpdateUserSchema) (*domain.ObjectDetails, error) {
|
||||
if err := userSchema.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(userSchema.ID, userSchema.ResourceOwner)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if writeModel.State != domain.UserSchemaStateActive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-HB3e1", "Errors.UserSchema.NotActive")
|
||||
}
|
||||
updatedEvent := writeModel.NewUpdatedEvent(
|
||||
ctx,
|
||||
UserSchemaAggregateFromWriteModel(&writeModel.WriteModel),
|
||||
userSchema.Type,
|
||||
userSchema.Schema,
|
||||
userSchema.PossibleAuthenticators,
|
||||
)
|
||||
if updatedEvent == nil {
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
}
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel, updatedEvent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) DeactivateUserSchema(ctx context.Context, id, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMA-Vvf3w", "Errors.IDMissing")
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(id, resourceOwner)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if writeModel.State != domain.UserSchemaStateActive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-E4t4z", "Errors.UserSchema.NotActive")
|
||||
}
|
||||
err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schema.NewDeactivatedEvent(ctx, UserSchemaAggregateFromWriteModel(&writeModel.WriteModel)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ReactivateUserSchema(ctx context.Context, id, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMA-wq3Gw", "Errors.IDMissing")
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(id, resourceOwner)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if writeModel.State != domain.UserSchemaStateInactive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-DGzh5", "Errors.UserSchema.NotInactive")
|
||||
}
|
||||
err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schema.NewReactivatedEvent(ctx, UserSchemaAggregateFromWriteModel(&writeModel.WriteModel)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) DeleteUserSchema(ctx context.Context, id, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMA-E22gg", "Errors.IDMissing")
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(id, resourceOwner)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !writeModel.Exists() {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-Grg41", "Errors.UserSchema.NotExists")
|
||||
}
|
||||
// TODO: check for users based on that schema; this is only possible with / after https://github.com/zitadel/zitadel/issues/7308
|
||||
err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schema.NewDeletedEvent(ctx, UserSchemaAggregateFromWriteModel(&writeModel.WriteModel), writeModel.SchemaType),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func validateUserSchema(userSchema json.RawMessage) error {
|
||||
_, err := domain_schema.NewSchema(0, bytes.NewReader(userSchema))
|
||||
if err != nil {
|
||||
return zerrors.ThrowInvalidArgument(err, "COMMA-W21tg", "Errors.UserSchema.Schema.Invalid")
|
||||
}
|
||||
return nil
|
||||
}
|
112
internal/command/user_schema_model.go
Normal file
112
internal/command/user_schema_model.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/schema"
|
||||
)
|
||||
|
||||
type UserSchemaWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
SchemaType string
|
||||
Schema json.RawMessage
|
||||
PossibleAuthenticators []domain.AuthenticatorType
|
||||
State domain.UserSchemaState
|
||||
}
|
||||
|
||||
func NewUserSchemaWriteModel(schemaID, resourceOwner string) *UserSchemaWriteModel {
|
||||
return &UserSchemaWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: schemaID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *UserSchemaWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *schema.CreatedEvent:
|
||||
wm.SchemaType = e.SchemaType
|
||||
wm.Schema = e.Schema
|
||||
wm.PossibleAuthenticators = e.PossibleAuthenticators
|
||||
wm.State = domain.UserSchemaStateActive
|
||||
case *schema.UpdatedEvent:
|
||||
if e.SchemaType != nil {
|
||||
wm.SchemaType = *e.SchemaType
|
||||
}
|
||||
if len(e.Schema) > 0 {
|
||||
wm.Schema = e.Schema
|
||||
}
|
||||
if len(e.PossibleAuthenticators) > 0 {
|
||||
wm.PossibleAuthenticators = e.PossibleAuthenticators
|
||||
}
|
||||
case *schema.DeactivatedEvent:
|
||||
wm.State = domain.UserSchemaStateInactive
|
||||
case *schema.ReactivatedEvent:
|
||||
wm.State = domain.UserSchemaStateActive
|
||||
case *schema.DeletedEvent:
|
||||
wm.State = domain.UserSchemaStateDeleted
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *UserSchemaWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(schema.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(
|
||||
schema.CreatedType,
|
||||
schema.UpdatedType,
|
||||
schema.DeactivatedType,
|
||||
schema.ReactivatedType,
|
||||
schema.DeletedType,
|
||||
).
|
||||
Builder()
|
||||
}
|
||||
func (wm *UserSchemaWriteModel) NewUpdatedEvent(
|
||||
ctx context.Context,
|
||||
agg *eventstore.Aggregate,
|
||||
schemaType *string,
|
||||
userSchema json.RawMessage,
|
||||
possibleAuthenticators []domain.AuthenticatorType,
|
||||
) *schema.UpdatedEvent {
|
||||
changes := make([]schema.Changes, 0)
|
||||
if schemaType != nil && wm.SchemaType != *schemaType {
|
||||
changes = append(changes, schema.ChangeSchemaType(wm.SchemaType, *schemaType))
|
||||
}
|
||||
if !bytes.Equal(wm.Schema, userSchema) {
|
||||
changes = append(changes, schema.ChangeSchema(userSchema))
|
||||
}
|
||||
if len(possibleAuthenticators) > 0 && slices.Compare(wm.PossibleAuthenticators, possibleAuthenticators) != 0 {
|
||||
changes = append(changes, schema.ChangePossibleAuthenticators(possibleAuthenticators))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil
|
||||
}
|
||||
return schema.NewUpdatedEvent(ctx, agg, changes)
|
||||
}
|
||||
|
||||
func UserSchemaAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
|
||||
return &eventstore.Aggregate{
|
||||
ID: wm.AggregateID,
|
||||
Type: schema.AggregateType,
|
||||
ResourceOwner: wm.ResourceOwner,
|
||||
InstanceID: wm.InstanceID,
|
||||
Version: schema.AggregateVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *UserSchemaWriteModel) Exists() bool {
|
||||
return wm.State != domain.UserSchemaStateUnspecified && wm.State != domain.UserSchemaStateDeleted
|
||||
}
|
912
internal/command/user_schema_test.go
Normal file
912
internal/command/user_schema_test.go
Normal file
@@ -0,0 +1,912 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/id/mock"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/schema"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
userSchema *CreateUserSchema
|
||||
}
|
||||
type res struct {
|
||||
id string
|
||||
details *domain.ObjectDetails
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"no type, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &CreateUserSchema{},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-DGFj3", "Errors.UserSchema.Type.Missing"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"no schema, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &CreateUserSchema{
|
||||
Type: "type",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-W21tg", "Errors.UserSchema.Schema.Invalid"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid authenticator, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &CreateUserSchema{
|
||||
Type: "type",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUnspecified,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-Gh652", "Errors.UserSchema.Authenticator.Invalid"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"no resourceOwner, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &CreateUserSchema{
|
||||
Type: "type",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-J3hhj", "Errors.ResourceOwnerMissing"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"empty user schema created",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectPush(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: mock.ExpectID(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &CreateUserSchema{
|
||||
ResourceOwner: "instanceID",
|
||||
Type: "type",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
id: "id1",
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"user schema created",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectPush(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: mock.ExpectID(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &CreateUserSchema{
|
||||
ResourceOwner: "instanceID",
|
||||
Type: "type",
|
||||
Schema: json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
id: "id1",
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"user schema with invalid permission, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &CreateUserSchema{
|
||||
ResourceOwner: "instanceID",
|
||||
Type: "type",
|
||||
Schema: json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": true
|
||||
}
|
||||
}
|
||||
}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-W21tg", "Errors.UserSchema.Schema.Invalid"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"user schema with permission created",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectPush(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": {
|
||||
"self": "rw"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: mock.ExpectID(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &CreateUserSchema{
|
||||
ResourceOwner: "instanceID",
|
||||
Type: "type",
|
||||
Schema: json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": {
|
||||
"self": "rw"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
id: "id1",
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
gotID, gotDetails, err := c.CreateUserSchema(tt.args.ctx, tt.args.userSchema)
|
||||
assert.Equal(t, tt.res.id, gotID)
|
||||
assert.Equal(t, tt.res.details, gotDetails)
|
||||
assert.ErrorIs(t, err, tt.res.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
userSchema *UpdateUserSchema
|
||||
}
|
||||
type res struct {
|
||||
details *domain.ObjectDetails
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"missing id, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-H5421", "Errors.IDMissing"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"empty type, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
Type: gu.Ptr(""),
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-G43gn", "Errors.UserSchema.Type.Missing"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"no schema, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-W21tg", "Errors.UserSchema.Schema.Invalid"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid schema, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
}
|
||||
}
|
||||
}`),
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-W21tg", "Errors.UserSchema.Schema.Invalid"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid authenticator, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUnspecified,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-WF4hg", "Errors.UserSchema.Authenticator.Invalid"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"not active / exists, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
Type: gu.Ptr("type"),
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowPreconditionFailed(nil, "COMMA-HB3e1", "Errors.UserSchema.NotActive"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"no changes",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
Type: gu.Ptr("type"),
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"update type",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schema.NewUpdatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
[]schema.Changes{schema.ChangeSchemaType("type", "newType")},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
Type: gu.Ptr("newType"),
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"update schema",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": {
|
||||
"self": "rw"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schema.NewUpdatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
[]schema.Changes{schema.ChangeSchema(json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": {
|
||||
"self": "rw"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": {
|
||||
"self": "rw"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`))},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": {
|
||||
"self": "rw"
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": {
|
||||
"self": "rw"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`),
|
||||
Type: nil,
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"update possible authenticators",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schema.NewUpdatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
[]schema.Changes{schema.ChangePossibleAuthenticators([]domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
domain.AuthenticatorTypePassword,
|
||||
})},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
domain.AuthenticatorTypeUsername,
|
||||
domain.AuthenticatorTypePassword,
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
got, err := c.UpdateUserSchema(tt.args.ctx, tt.args.userSchema)
|
||||
assert.ErrorIs(t, err, tt.res.err)
|
||||
assert.Equal(t, tt.res.details, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_DeactivateUserSchema(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
id string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
details *domain.ObjectDetails
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"missing id, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "",
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-Vvf3w", "Errors.IDMissing"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"not active / exists, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "id1",
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowPreconditionFailed(nil, "COMMA-E4t4z", "Errors.UserSchema.NotActive"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"deactivate ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schema.NewDeactivatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "id1",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
got, err := c.DeactivateUserSchema(tt.args.ctx, tt.args.id, tt.args.resourceOwner)
|
||||
assert.ErrorIs(t, err, tt.res.err)
|
||||
assert.Equal(t, tt.res.details, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_ReactivateUserSchema(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
id string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
details *domain.ObjectDetails
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"missing id, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "",
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-wq3Gw", "Errors.IDMissing"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"not deactivated / exists, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "id1",
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowPreconditionFailed(nil, "COMMA-DGzh5", "Errors.UserSchema.NotInactive"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"reactivate ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
schema.NewDeactivatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schema.NewReactivatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "id1",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
got, err := c.ReactivateUserSchema(tt.args.ctx, tt.args.id, tt.args.resourceOwner)
|
||||
assert.ErrorIs(t, err, tt.res.err)
|
||||
assert.Equal(t, tt.res.details, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_DeleteUserSchema(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
id string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
details *domain.ObjectDetails
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"missing id, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "",
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-E22gg", "Errors.IDMissing"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"not exists, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "id1",
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowPreconditionFailed(nil, "COMMA-Grg41", "Errors.UserSchema.NotExists"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"delete ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schema.NewDeletedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "id1",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
got, err := c.DeleteUserSchema(tt.args.ctx, tt.args.id, tt.args.resourceOwner)
|
||||
assert.ErrorIs(t, err, tt.res.err)
|
||||
assert.Equal(t, tt.res.details, got)
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user