mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 11:04:25 +00:00
0e181b218c
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.
181 lines
6.1 KiB
Go
181 lines
6.1 KiB
Go
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
|
|
}
|