feat: label policy (#1708)

* feat: label policy proto extension

* feat: label policy and activate event

* feat: label policy asset events

* feat: label policy asset commands

* feat: add storage key

* feat: storage key validation

* feat: label policy asset tests

* feat: label policy query side

* feat: avatar

* feat: avatar event

* feat: human avatar

* feat: avatar read side

* feat: font on iam label policy

* feat: label policy font

* feat: possiblity to create bucket on put file

* uplaoder

* login policy logo

* set bucket prefix

* feat: avatar upload

* feat: avatar upload

* feat: use assets on command side

* feat: fix human avatar removed event

* feat: remove human avatar

* feat: mock asset storage

* feat: remove human avatar

* fix(operator): add configuration of asset storage to zitadel operator

* feat(console): private labeling policy (#1697)

* private labeling component, routing, preview

* font, colors, upload, i18n

* show logo

* fix: uniqueness (#1710)

* fix: uniqueconstraint to lower

* feat: change org

* feat: org change test

* feat: change org

* fix: tests

* fix: handle domain claims correctly

* feat: update org

Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>

* fix: handle domain claimed event correctly for service users (#1711)

* fix: handle domain claimed event correctly on user view

* fix: ignore domain claimed events for email notifications

* fix: change org

* handle org changed in read models correctly

* fix: change org in user grant handler

Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>

* fix: correct value (#1695)

* docs(api): correct link (#1712)

* upload service

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>

* feat: fix tests,

* feat: remove assets from label policy

* fix npm, set environment

* lint ts

* remove stylelinting

* fix(operator): add mapping for console with changed unit tests

* fix(operator): add secrets as env variables to pod

* feat: remove human avatar

* fix(operator): add secrets as env variables to pod

* feat: map label policy

* feat: labelpolicy, admin, mgmt, adv settings (#1715)

* fetch label policy, mgmt, admin service

* feat: advanced beh, links, add, update

* lint ts

* feat: watermark

* feat: remove human avatar

* feat: remove human avatar

* feat: remove human avatar

* feat: remove human avatar

* feat: remove human avatar

* feat: remove human avatar

* feat: remove human avatar

* feat: custom css

* css

* css

* css

* css

* css

* getobject

* feat: dynamic handler

* feat: varibale css

* content info

* css overwrite

* feat: variablen css

* feat: generate css file

* feat: dark mode

* feat: dark mode

* fix logo css

* feat: upload logos

* dark mode with cookie

* feat: handle images in login

* avatar css and begin font

* feat: avatar

* feat: user avatar

* caching of static assets in login

* add avatar.js to main.html

* feat: header dont show logo if no url

* feat: label policy colors

* feat: mock asset storage

* feat: mock asset storage

* feat: fix tests

* feat: user avatar

* feat: header logo

* avatar

* avatar

* make it compatible with go 1.15

* feat: remove unused logos

* fix handler

* fix: styling error handling

* fonts

* fix: download func

* switch to mux

* fix: change upload api to assets

* fix build

* fix: download avatar

* fix: download logos

* fix: my avatar

* font

* fix: remove error msg popup possibility

* fix: docs

* fix: svalidate colors

* rem msg popup from frontend

* fix: email with private labeling

* fix: tests

* fix: email templates

* fix: change migration version

* fix: fix duplicate imports

* fix(console): assets, service url, upload, policy current and preview  (#1781)

* upload endpoint, layout

* fetch current, preview, fix upload

* cleanup private labeling

* fix linting

* begin generated asset handler

* generate asset api in dockerfile

* features for label policy

* features for label policy

* features

* flag for asset generator

* change asset generator flag

* fix label policy view in grpc

* fix: layout, activate policy (#1786)

* theme switcher up on top

* change layout

* activate policy

* feat(console): label policy back color, layout (#1788)

* theme switcher up on top

* change layout

* activate policy

* fix overwrite value fc

* reset policy, reset service

* autosave policy, preview desc, layout impv

* layout, i18n

* background colors, inject material styles

* load images

* clean, lint

* fix layout

* set custom hex

* fix content size conversion

* remove font format in generated css

* fix features for assets

* fix(console): label policy colors, image downloads, preview (#1804)

* load images

* colors, images binding

* lint

* refresh emitter

* lint

* propagate font colors

* upload error handling

* label policy feature check

* add blob in csp for console

* log

* fix: feature edits for label policy, refresh state on upload (#1807)

* show error on load image, stop spinner

* fix merge

* fix migration versions

* fix assets

* fix csp

* fix background color

* scss

* fix build

* lint scss

* fix statik for console

* fix features check for label policy

* cleanup

* lint

* public links

* fix notifications

* public links

* feat: merge main

* feat: fix translation files

* fix migration

* set api domain

* fix logo in email

* font face in email

* font face in email

* validate assets on upload

* cleanup

* add missing translations

* add missing translations

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Stefan Benz <stefan@caos.ch>
Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Florian Forster <florian@caos.ch>
This commit is contained in:
Fabi
2021-06-04 14:53:51 +02:00
committed by GitHub
parent c0d9d86b09
commit 73d37459bb
257 changed files with 18248 additions and 7178 deletions

View File

@@ -20,12 +20,14 @@ import (
proj_repo "github.com/caos/zitadel/internal/repository/project"
usr_repo "github.com/caos/zitadel/internal/repository/user"
usr_grant_repo "github.com/caos/zitadel/internal/repository/usergrant"
"github.com/caos/zitadel/internal/static"
"github.com/caos/zitadel/internal/telemetry/tracing"
webauthn_helper "github.com/caos/zitadel/internal/webauthn"
)
type Commands struct {
eventstore *eventstore.Eventstore
static static.Storage
idGenerator id.Generator
iamDomain string
zitadelRoles []authz.RoleMapping
@@ -58,9 +60,10 @@ type Config struct {
Eventstore types.SQLUser
}
func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults, authZConfig authz.Config, authZRepo *authz_repo.EsRepository) (repo *Commands, err error) {
func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults, authZConfig authz.Config, staticStore static.Storage, authZRepo *authz_repo.EsRepository) (repo *Commands, err error) {
repo = &Commands{
eventstore: eventstore,
static: staticStore,
idGenerator: id.SonyFlakeGenerator,
iamDomain: defaults.Domain,
zitadelRoles: authZConfig.RolePermissionMappings,

View File

@@ -0,0 +1,54 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/org"
)
type ExistingLabelPoliciesReadModel struct {
eventstore.WriteModel
aggregateIDs []string
}
func NewExistingLabelPoliciesReadModel(ctx context.Context) *ExistingLabelPoliciesReadModel {
return &ExistingLabelPoliciesReadModel{}
}
func (rm *ExistingLabelPoliciesReadModel) AppendEvents(events ...eventstore.EventReader) {
rm.WriteModel.AppendEvents(events...)
}
func (rm *ExistingLabelPoliciesReadModel) Reduce() error {
for _, event := range rm.Events {
switch e := event.(type) {
case *iam.LabelPolicyAddedEvent,
*org.LabelPolicyAddedEvent:
rm.aggregateIDs = append(rm.aggregateIDs, e.Aggregate().ID)
case *org.LabelPolicyRemovedEvent:
for i := len(rm.aggregateIDs) - 1; i >= 0; i-- {
if rm.aggregateIDs[i] == e.Aggregate().ID {
copy(rm.aggregateIDs[i:], rm.aggregateIDs[i+1:])
rm.aggregateIDs[len(rm.aggregateIDs)-1] = ""
rm.aggregateIDs = rm.aggregateIDs[:len(rm.aggregateIDs)-1]
}
}
}
}
return nil
}
func (rm *ExistingLabelPoliciesReadModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(
eventstore.ColumnsEvent,
iam.AggregateType,
org.AggregateType).
EventTypes(
iam.LabelPolicyAddedEventType,
org.LabelPolicyAddedEventType,
org.LabelPolicyRemovedEventType,
)
}

View File

@@ -23,7 +23,8 @@ type FeaturesWriteModel struct {
LoginPolicyUsernameLogin bool
LoginPolicyPasswordReset bool
PasswordComplexityPolicy bool
LabelPolicy bool
LabelPolicyPrivateLabel bool
LabelPolicyWatermark bool
CustomDomain bool
}
@@ -69,7 +70,13 @@ func (wm *FeaturesWriteModel) Reduce() error {
wm.PasswordComplexityPolicy = *e.PasswordComplexityPolicy
}
if e.LabelPolicy != nil {
wm.LabelPolicy = *e.LabelPolicy
wm.LabelPolicyPrivateLabel = *e.LabelPolicy
}
if e.LabelPolicyPrivateLabel != nil {
wm.LabelPolicyPrivateLabel = *e.LabelPolicyPrivateLabel
}
if e.LabelPolicyWatermark != nil {
wm.LabelPolicyWatermark = *e.LabelPolicyWatermark
}
if e.CustomDomain != nil {
wm.CustomDomain = *e.CustomDomain

View File

@@ -49,8 +49,16 @@ func writeModelToLabelPolicy(wm *LabelPolicyWriteModel) *domain.LabelPolicy {
return &domain.LabelPolicy{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
PrimaryColor: wm.PrimaryColor,
SecondaryColor: wm.SecondaryColor,
BackgroundColor: wm.BackgroundColor,
WarnColor: wm.WarnColor,
FontColor: wm.FontColor,
PrimaryColorDark: wm.PrimaryColorDark,
BackgroundColorDark: wm.BackgroundColorDark,
WarnColorDark: wm.WarnColorDark,
FontColorDark: wm.FontColorDark,
HideLoginNameSuffix: wm.HideLoginNameSuffix,
ErrorMsgPopup: wm.ErrorMsgPopup,
DisableWatermark: wm.DisableWatermark,
}
}
@@ -176,6 +184,8 @@ func writeModelToFeatures(wm *FeaturesWriteModel) *domain.Features {
LoginPolicyRegistration: wm.LoginPolicyRegistration,
LoginPolicyUsernameLogin: wm.LoginPolicyUsernameLogin,
PasswordComplexityPolicy: wm.PasswordComplexityPolicy,
LabelPolicy: wm.LabelPolicy,
LabelPolicyPrivateLabel: wm.LabelPolicyPrivateLabel,
LabelPolicyWatermark: wm.LabelPolicyWatermark,
CustomDomain: wm.CustomDomain,
}
}

View File

@@ -46,7 +46,8 @@ func (c *Commands) setDefaultFeatures(ctx context.Context, existingFeatures *IAM
features.LoginPolicyRegistration,
features.LoginPolicyUsernameLogin,
features.PasswordComplexityPolicy,
features.LabelPolicy,
features.LabelPolicyPrivateLabel,
features.LabelPolicyWatermark,
features.CustomDomain,
)
if !hasChanged {
@@ -61,5 +62,7 @@ func (c *Commands) getDefaultFeatures(ctx context.Context) (*domain.Features, er
if err != nil {
return nil, err
}
return writeModelToFeatures(&existingFeatures.FeaturesWriteModel), nil
features := writeModelToFeatures(&existingFeatures.FeaturesWriteModel)
features.IsDefault = true
return features, nil
}

View File

@@ -62,7 +62,8 @@ func (wm *IAMFeaturesWriteModel) NewSetEvent(
loginPolicyRegistration,
loginPolicyUsernameLogin,
passwordComplexityPolicy,
labelPolicy,
labelPolicyPrivateLabel,
labelPolicyWatermark,
customDomain bool,
) (*iam.FeaturesSetEvent, bool) {
@@ -101,8 +102,11 @@ func (wm *IAMFeaturesWriteModel) NewSetEvent(
if wm.PasswordComplexityPolicy != passwordComplexityPolicy {
changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy))
}
if wm.LabelPolicy != labelPolicy {
changes = append(changes, features.ChangeLabelPolicy(labelPolicy))
if wm.LabelPolicyPrivateLabel != labelPolicyPrivateLabel {
changes = append(changes, features.ChangeLabelPolicyPrivateLabel(labelPolicyPrivateLabel))
}
if wm.LabelPolicyWatermark != labelPolicyWatermark {
changes = append(changes, features.ChangeLabelPolicyWatermark(labelPolicyWatermark))
}
if wm.CustomDomain != customDomain {
changes = append(changes, features.ChangeCustomDomain(customDomain))

View File

@@ -29,8 +29,8 @@ func (c *Commands) AddDefaultLabelPolicy(ctx context.Context, policy *domain.Lab
}
func (c *Commands) addDefaultLabelPolicy(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMLabelPolicyWriteModel, policy *domain.LabelPolicy) (eventstore.EventPusher, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3m9fo", "Errors.IAM.LabelPolicy.Invalid")
if err := policy.IsValid(); err != nil {
return nil, err
}
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
@@ -40,13 +40,26 @@ func (c *Commands) addDefaultLabelPolicy(ctx context.Context, iamAgg *eventstore
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LabelPolicy.AlreadyExists")
}
return iam_repo.NewLabelPolicyAddedEvent(ctx, iamAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix), nil
return iam_repo.NewLabelPolicyAddedEvent(
ctx,
iamAgg,
policy.PrimaryColor,
policy.BackgroundColor,
policy.WarnColor,
policy.FontColor,
policy.PrimaryColorDark,
policy.BackgroundColorDark,
policy.WarnColorDark,
policy.FontColorDark,
policy.HideLoginNameSuffix,
policy.ErrorMsgPopup,
policy.DisableWatermark), nil
}
func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-33m8f", "Errors.IAM.LabelPolicy.Invalid")
if err := policy.IsValid(); err != nil {
return nil, err
}
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
@@ -57,7 +70,20 @@ func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.
return nil, caos_errs.ThrowNotFound(nil, "IAM-0K9dq", "Errors.IAM.LabelPolicy.NotFound")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(
ctx,
iamAgg,
policy.PrimaryColor,
policy.BackgroundColor,
policy.WarnColor,
policy.FontColor,
policy.PrimaryColorDark,
policy.BackgroundColorDark,
policy.WarnColorDark,
policy.FontColorDark,
policy.HideLoginNameSuffix,
policy.ErrorMsgPopup,
policy.DisableWatermark)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
@@ -73,6 +99,275 @@ func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.
return writeModelToLabelPolicy(&existingPolicy.LabelPolicyWriteModel), nil
}
func (c *Commands) ActivateDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-6M23e", "Errors.IAM.LabelPolicy.NotFound")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyActivatedEvent(ctx, iamAgg))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddLogoDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3m20c", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-Qw0pd", "Errors.IAM.LabelPolicy.NotFound")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyLogoAddedEvent(ctx, iamAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveLogoDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-Xc8Kf", "Errors.IAM.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.LogoKey)
if err != nil {
return nil, err
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyLogoRemovedEvent(ctx, iamAgg, existingPolicy.LogoKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddIconDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-yxE4f", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-1yMx0", "Errors.IAM.LabelPolicy.NotFound")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyIconAddedEvent(ctx, iamAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveIconDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M0qw", "Errors.IAM.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.IconKey)
if err != nil {
return nil, err
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyIconRemovedEvent(ctx, iamAgg, existingPolicy.IconKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddLogoDarkDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4fMs9", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-ZR9fs", "Errors.IAM.LabelPolicy.NotFound")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyLogoDarkAddedEvent(ctx, iamAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveLogoDarkDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-3FGds", "Errors.IAM.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.LogoDarkKey)
if err != nil {
return nil, err
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyLogoDarkRemovedEvent(ctx, iamAgg, existingPolicy.LogoDarkKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddIconDarkDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-1cxM3", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-vMsf9", "Errors.IAM.LabelPolicy.NotFound")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyIconDarkAddedEvent(ctx, iamAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveIconDarkDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-2nc7F", "Errors.IAM.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.IconDarkKey)
if err != nil {
return nil, err
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyIconDarkRemovedEvent(ctx, iamAgg, existingPolicy.IconDarkKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddFontDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-1N8fs", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-1N8fE", "Errors.IAM.LabelPolicy.NotFound")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyFontAddedEvent(ctx, iamAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveFontDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-Tk0gw", "Errors.IAM.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.FontKey)
if err != nil {
return nil, err
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyFontRemovedEvent(ctx, iamAgg, existingPolicy.FontKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) defaultLabelPolicyWriteModelByID(ctx context.Context) (policy *IAMLabelPolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
@@ -84,3 +379,13 @@ func (c *Commands) defaultLabelPolicyWriteModelByID(ctx context.Context) (policy
}
return writeModel, nil
}
func (c *Commands) getDefaultLabelPolicy(ctx context.Context) (*domain.LabelPolicy, error) {
policyWriteModel, err := c.defaultLabelPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
}
policy := writeModelToLabelPolicy(&policyWriteModel.LabelPolicyWriteModel)
policy.Default = true
return policy, nil
}

View File

@@ -2,9 +2,9 @@ package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/policy"
)
@@ -31,6 +31,28 @@ func (wm *IAMLabelPolicyWriteModel) AppendEvents(events ...eventstore.EventReade
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyAddedEvent)
case *iam.LabelPolicyChangedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyChangedEvent)
case *iam.LabelPolicyActivatedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyActivatedEvent)
case *iam.LabelPolicyLogoAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyLogoAddedEvent)
case *iam.LabelPolicyLogoRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyLogoRemovedEvent)
case *iam.LabelPolicyLogoDarkAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyLogoDarkAddedEvent)
case *iam.LabelPolicyLogoDarkRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyLogoDarkRemovedEvent)
case *iam.LabelPolicyIconAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyIconAddedEvent)
case *iam.LabelPolicyIconRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyIconRemovedEvent)
case *iam.LabelPolicyIconDarkAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyIconDarkAddedEvent)
case *iam.LabelPolicyIconDarkRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyIconDarkRemovedEvent)
case *iam.LabelPolicyFontAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyFontAddedEvent)
case *iam.LabelPolicyFontRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyFontRemovedEvent)
}
}
}
@@ -45,26 +67,69 @@ func (wm *IAMLabelPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
ResourceOwner(wm.ResourceOwner).
EventTypes(
iam.LabelPolicyAddedEventType,
iam.LabelPolicyChangedEventType)
iam.LabelPolicyChangedEventType,
iam.LabelPolicyLogoAddedEventType,
iam.LabelPolicyLogoRemovedEventType,
iam.LabelPolicyIconAddedEventType,
iam.LabelPolicyIconRemovedEventType,
iam.LabelPolicyLogoDarkAddedEventType,
iam.LabelPolicyLogoDarkRemovedEventType,
iam.LabelPolicyIconDarkAddedEventType,
iam.LabelPolicyIconDarkRemovedEventType,
iam.LabelPolicyFontAddedEventType,
iam.LabelPolicyFontRemovedEventType,
)
}
func (wm *IAMLabelPolicyWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
primaryColor,
secondaryColor string,
hideLoginNameSuffix bool,
backgroundColor,
warnColor,
fontColor,
primaryColorDark,
backgroundColorDark,
warnColorDark,
fontColorDark string,
hideLoginNameSuffix,
errorMsgPopup,
disableWatermark bool,
) (*iam.LabelPolicyChangedEvent, bool) {
changes := make([]policy.LabelPolicyChanges, 0)
if wm.PrimaryColor != primaryColor {
changes = append(changes, policy.ChangePrimaryColor(primaryColor))
}
if wm.SecondaryColor != secondaryColor {
changes = append(changes, policy.ChangeSecondaryColor(secondaryColor))
if wm.BackgroundColor != backgroundColor {
changes = append(changes, policy.ChangeBackgroundColor(backgroundColor))
}
if wm.WarnColor != warnColor {
changes = append(changes, policy.ChangeWarnColor(warnColor))
}
if wm.FontColor != fontColor {
changes = append(changes, policy.ChangeFontColor(fontColor))
}
if wm.PrimaryColorDark != primaryColorDark {
changes = append(changes, policy.ChangePrimaryColorDark(primaryColorDark))
}
if wm.BackgroundColorDark != backgroundColorDark {
changes = append(changes, policy.ChangeBackgroundColorDark(backgroundColorDark))
}
if wm.WarnColorDark != warnColorDark {
changes = append(changes, policy.ChangeWarnColorDark(warnColorDark))
}
if wm.FontColorDark != fontColorDark {
changes = append(changes, policy.ChangeFontColorDark(fontColorDark))
}
if wm.HideLoginNameSuffix != hideLoginNameSuffix {
changes = append(changes, policy.ChangeHideLoginNameSuffix(hideLoginNameSuffix))
}
if wm.ErrorMsgPopup != errorMsgPopup {
changes = append(changes, policy.ChangeErrorMsgPopup(errorMsgPopup))
}
if wm.DisableWatermark != disableWatermark {
changes = append(changes, policy.ChangeDisableWatermark(disableWatermark))
}
if len(changes) == 0 {
return nil, false
}

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,8 @@ func (c *Commands) SetOrgFeatures(ctx context.Context, resourceOwner string, fea
features.LoginPolicyUsernameLogin,
features.LoginPolicyPasswordReset,
features.PasswordComplexityPolicy,
features.LabelPolicy,
features.LabelPolicyPrivateLabel,
features.LabelPolicyWatermark,
features.CustomDomain,
)
if !hasChanged {
@@ -106,15 +107,12 @@ func (c *Commands) ensureOrgSettingsToFeatures(ctx context.Context, orgID string
events = append(events, removePasswordComplexityEvent)
}
}
if !features.LabelPolicy {
removeLabelPolicyEvent, err := c.removeLabelPolicyIfExists(ctx, orgID)
if err != nil {
return nil, err
}
if removeLabelPolicyEvent != nil {
events = append(events, removeLabelPolicyEvent)
}
labelPolicyEvents, err := c.setAllowedLabelPolicy(ctx, orgID, features)
if err != nil {
return nil, err
}
events = append(events, labelPolicyEvents...)
if !features.CustomDomain {
removeCustomDomainsEvents, err := c.removeCustomDomains(ctx, orgID)
if err != nil {
@@ -233,3 +231,56 @@ func (c *Commands) setDefaultAuthFactorsInCustomLoginPolicy(ctx context.Context,
}
return events, nil
}
func (c *Commands) setAllowedLabelPolicy(ctx context.Context, orgID string, features *domain.Features) ([]eventstore.EventPusher, error) {
events := make([]eventstore.EventPusher, 0)
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, nil
}
if !features.LabelPolicyPrivateLabel && !features.LabelPolicyWatermark {
removeEvent, err := c.removeLabelPolicy(ctx, existingPolicy)
if err != nil {
return nil, err
}
return append(events, removeEvent), nil
}
defaultPolicy, err := c.getDefaultLabelPolicy(ctx)
if err != nil {
return nil, err
}
policy := *existingPolicy
if !features.LabelPolicyWatermark && defaultPolicy.DisableWatermark != existingPolicy.DisableWatermark {
policy.DisableWatermark = defaultPolicy.DisableWatermark
}
if !features.LabelPolicyPrivateLabel {
if defaultPolicy.HideLoginNameSuffix != existingPolicy.HideLoginNameSuffix {
policy.HideLoginNameSuffix = defaultPolicy.HideLoginNameSuffix
}
policy.PrimaryColor = ""
policy.BackgroundColor = ""
policy.WarnColor = ""
policy.FontColor = ""
policy.PrimaryColorDark = ""
policy.BackgroundColorDark = ""
policy.WarnColorDark = ""
policy.FontColorDark = ""
assetsEvent, err := c.removeLabelPolicyAssets(ctx, existingPolicy)
if err != nil {
return nil, err
}
events = append(events, assetsEvent)
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel),
policy.PrimaryColor, policy.BackgroundColor, policy.WarnColor, policy.FontColor,
policy.PrimaryColorDark, policy.BackgroundColorDark, policy.WarnColorDark, policy.FontColorDark,
policy.HideLoginNameSuffix, policy.ErrorMsgPopup, policy.HideLoginNameSuffix)
if hasChanged {
events = append(events, changedEvent)
}
return events, nil
}

View File

@@ -69,7 +69,8 @@ func (wm *OrgFeaturesWriteModel) NewSetEvent(
loginPolicyUsernameLogin,
loginPolicyPasswordReset,
passwordComplexityPolicy,
labelPolicy,
labelPolicyPrivateLabel,
labelPolicyWatermark,
customDomain bool,
) (*org.FeaturesSetEvent, bool) {
@@ -111,8 +112,11 @@ func (wm *OrgFeaturesWriteModel) NewSetEvent(
if wm.PasswordComplexityPolicy != passwordComplexityPolicy {
changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy))
}
if wm.LabelPolicy != labelPolicy {
changes = append(changes, features.ChangeLabelPolicy(labelPolicy))
if wm.LabelPolicyPrivateLabel != labelPolicyPrivateLabel {
changes = append(changes, features.ChangeLabelPolicyPrivateLabel(labelPolicyPrivateLabel))
}
if wm.LabelPolicyWatermark != labelPolicyWatermark {
changes = append(changes, features.ChangeLabelPolicyWatermark(labelPolicyWatermark))
}
if wm.CustomDomain != customDomain {
changes = append(changes, features.ChangeCustomDomain(customDomain))

View File

@@ -5,6 +5,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/domain"
@@ -14,12 +15,15 @@ import (
"github.com/caos/zitadel/internal/repository/features"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/org"
"github.com/caos/zitadel/internal/static"
"github.com/caos/zitadel/internal/static/mock"
)
func TestCommandSide_SetOrgFeatures(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
iamDomain string
static static.Storage
}
type args struct {
ctx context.Context
@@ -56,7 +60,8 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false,
LabelPolicy: false,
LabelPolicyPrivateLabel: false,
LabelPolicyWatermark: false,
CustomDomain: false,
},
},
@@ -90,7 +95,8 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false,
LabelPolicy: false,
LabelPolicyPrivateLabel: false,
LabelPolicyWatermark: false,
CustomDomain: false,
},
},
@@ -138,6 +144,14 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"primary",
"secondary",
"warn",
"font",
"primary-dark",
"secondary-dark",
"warn-dark",
"font-dark",
false,
false,
false,
),
),
@@ -196,7 +210,8 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false,
LabelPolicy: false,
LabelPolicyPrivateLabel: false,
LabelPolicyWatermark: false,
CustomDomain: false,
},
},
@@ -246,6 +261,14 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"primary",
"secondary",
"warn",
"font",
"primary-dark",
"secondary-dark",
"warn-dark",
"font-dark",
false,
false,
false,
),
),
@@ -332,7 +355,8 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false,
LabelPolicy: false,
LabelPolicyPrivateLabel: false,
LabelPolicyWatermark: false,
CustomDomain: false,
},
},
@@ -382,6 +406,14 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"primary",
"secondary",
"warn",
"font",
"primary-dark",
"secondary-dark",
"warn-dark",
"font-dark",
false,
false,
false,
),
),
@@ -478,7 +510,8 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false,
LabelPolicy: false,
LabelPolicyPrivateLabel: false,
LabelPolicyWatermark: false,
CustomDomain: false,
},
},
@@ -528,6 +561,14 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"primary",
"secondary",
"warn",
"font",
"primary-dark",
"secondary-dark",
"warn-dark",
"font-dark",
false,
false,
false,
),
),
@@ -634,7 +675,8 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyUsernameLogin: false,
LoginPolicyPasswordReset: false,
PasswordComplexityPolicy: false,
LabelPolicy: false,
LabelPolicyPrivateLabel: false,
LabelPolicyWatermark: false,
CustomDomain: false,
},
},
@@ -740,6 +782,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
),
),
),
//begin setDefaultAuthFactorsInCustomLoginPolicy
//orgLabelPolicyWriteModelByID
expectFilter(
eventFromEventPusher(
@@ -748,6 +791,14 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"primary",
"secondary",
"warn",
"font",
"primary-dark",
"secondary-dark",
"warn-dark",
"font-dark",
false,
false,
false,
),
),
@@ -757,10 +808,22 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"custom",
"secondary",
"warn",
"font",
"primary-dark",
"secondary-dark",
"warn-dark",
"font-dark",
false,
false,
false,
),
),
),
//removeLabelPolicy
expectFilter(),
//end setDefaultAuthFactorsInCustomLoginPolicy
//removeCustomDomains
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(
@@ -818,6 +881,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
),
),
iamDomain: "iam-domain",
static: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectNoError(),
},
args: args{
ctx: context.Background(),
@@ -832,7 +896,9 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
LoginPolicyRegistration: false,
LoginPolicyUsernameLogin: false,
PasswordComplexityPolicy: false,
LabelPolicy: false,
LabelPolicyPrivateLabel: false,
LabelPolicyWatermark: false,
CustomDomain: false,
},
},
res: res{
@@ -847,6 +913,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
iamDomain: tt.fields.iamDomain,
static: tt.fields.static,
}
got, err := r.SetOrgFeatures(tt.args.ctx, tt.args.resourceOwner, tt.args.features)
if tt.res.err == nil {
@@ -958,6 +1025,14 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"primary",
"secondary",
"warn",
"font",
"primary-dark",
"secondary-dark",
"warn-dark",
"font-dark",
false,
false,
false,
),
),

View File

@@ -12,8 +12,8 @@ func (c *Commands) AddLabelPolicy(ctx context.Context, resourceOwner string, pol
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Fn8ds", "Errors.ResourceOwnerMissing")
}
if !policy.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Md9sf", "Errors.Org.LabelPolicy.Invalid")
if err := policy.IsValid(); err != nil {
return nil, err
}
addedPolicy := NewOrgLabelPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
@@ -25,7 +25,20 @@ func (c *Commands) AddLabelPolicy(ctx context.Context, resourceOwner string, pol
}
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyAddedEvent(ctx, orgAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix))
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyAddedEvent(
ctx,
orgAgg,
policy.PrimaryColor,
policy.BackgroundColor,
policy.WarnColor,
policy.FontColor,
policy.PrimaryColorDark,
policy.BackgroundColorDark,
policy.WarnColorDark,
policy.FontColorDark,
policy.HideLoginNameSuffix,
policy.ErrorMsgPopup,
policy.DisableWatermark))
if err != nil {
return nil, err
}
@@ -40,8 +53,8 @@ func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string,
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3N9fs", "Errors.ResourceOwnerMissing")
}
if !policy.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-dM9fs", "Errors.Org.LabelPolicy.Invalid")
if err := policy.IsValid(); err != nil {
return nil, err
}
existingPolicy := NewOrgLabelPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
@@ -53,7 +66,20 @@ func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string,
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(
ctx,
orgAgg,
policy.PrimaryColor,
policy.BackgroundColor,
policy.WarnColor,
policy.FontColor,
policy.PrimaryColorDark,
policy.BackgroundColorDark,
policy.WarnColorDark,
policy.FontColorDark,
policy.HideLoginNameSuffix,
policy.ErrorMsgPopup,
policy.DisableWatermark)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4M9vs", "Errors.Org.LabelPolicy.NotChanged")
}
@@ -69,6 +95,306 @@ func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string,
return writeModelToLabelPolicy(&existingPolicy.LabelPolicyWriteModel), nil
}
func (c *Commands) ActivateLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-KKd4X", "Errors.ResourceOwnerMissing")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-34mSE", "Errors.Org.LabelPolicy.NotFound")
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyActivatedEvent(ctx, orgAgg))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddLogoLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-KKd4X", "Errors.ResourceOwnerMissing")
}
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4N3nf", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-23BMs", "Errors.Org.LabelPolicy.NotFound")
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyLogoAddedEvent(ctx, orgAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveLogoLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-2FN8s", "Errors.ResourceOwnerMissing")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-4MVsf", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, orgID, existingPolicy.LogoKey)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyLogoRemovedEvent(ctx, orgAgg, existingPolicy.LogoKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddIconLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-hMDs3", "Errors.ResourceOwnerMissing")
}
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4BS7f", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-4nq2f", "Errors.Org.LabelPolicy.NotFound")
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyIconAddedEvent(ctx, orgAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveIconLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-1nd0d", "Errors.ResourceOwnerMissing")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-1nd9f", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, orgID, existingPolicy.IconKey)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyIconRemovedEvent(ctx, orgAgg, existingPolicy.IconKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddLogoDarkLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-67Ms2", "Errors.ResourceOwnerMissing")
}
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3S7fN", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-QSqcd", "Errors.Org.LabelPolicy.NotFound")
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyLogoDarkAddedEvent(ctx, orgAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveLogoDarkLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-4NF0d", "Errors.ResourceOwnerMissing")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-0peQw", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, orgID, existingPolicy.LogoDarkKey)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyLogoDarkRemovedEvent(ctx, orgAgg, existingPolicy.LogoDarkKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddIconDarkLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-tzBfs", "Errors.ResourceOwnerMissing")
}
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4B7cs", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-4Nf8s", "Errors.Org.LabelPolicy.NotFound")
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyIconDarkAddedEvent(ctx, orgAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveIconDarkLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Mv9ds", "Errors.ResourceOwnerMissing")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-3NFos", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, orgID, existingPolicy.IconDarkKey)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyIconDarkRemovedEvent(ctx, orgAgg, existingPolicy.IconDarkKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) AddFontLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-1Nf9s", "Errors.ResourceOwnerMissing")
}
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-2f9fw", "Errors.Assets.EmptyKey")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-2M9fs", "Errors.Org.LabelPolicy.NotFound")
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyFontAddedEvent(ctx, orgAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveFontLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-2n0fW", "Errors.ResourceOwnerMissing")
}
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-4n9SD", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, orgID, existingPolicy.FontKey)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyFontRemovedEvent(ctx, orgAgg, existingPolicy.FontKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
}
func (c *Commands) RemoveLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Mf9sf", "Errors.ResourceOwnerMissing")
@@ -97,6 +423,10 @@ func (c *Commands) removeLabelPolicy(ctx context.Context, existingPolicy *OrgLab
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "Org-3M9df", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, existingPolicy.AggregateID, domain.LabelPolicyPrefix)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel)
return org.NewLabelPolicyRemovedEvent(ctx, orgAgg), nil
}
@@ -109,10 +439,23 @@ func (c *Commands) removeLabelPolicyIfExists(ctx context.Context, orgID string)
if existingPolicy.State != domain.PolicyStateActive {
return nil, nil
}
err = c.RemoveAsset(ctx, orgID, domain.LabelPolicyPrefix)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel)
return org.NewLabelPolicyRemovedEvent(ctx, orgAgg), nil
}
func (c *Commands) removeLabelPolicyAssets(ctx context.Context, existingPolicy *OrgLabelPolicyWriteModel) (*org.LabelPolicyAssetsRemovedEvent, error) {
err := c.RemoveAsset(ctx, existingPolicy.AggregateID, domain.LabelPolicyPrefix)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel)
return org.NewLabelPolicyAssetsRemovedEvent(ctx, orgAgg), nil
}
func (c *Commands) orgLabelPolicyWriteModelByID(ctx context.Context, orgID string) (*OrgLabelPolicyWriteModel, error) {
policy := NewOrgLabelPolicyWriteModel(orgID)
err := c.eventstore.FilterToQueryReducer(ctx, policy)

View File

@@ -30,6 +30,26 @@ func (wm *OrgLabelPolicyWriteModel) AppendEvents(events ...eventstore.EventReade
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyAddedEvent)
case *org.LabelPolicyChangedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyChangedEvent)
case *org.LabelPolicyLogoAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyLogoAddedEvent)
case *org.LabelPolicyLogoRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyLogoRemovedEvent)
case *org.LabelPolicyLogoDarkAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyLogoDarkAddedEvent)
case *org.LabelPolicyLogoDarkRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyLogoDarkRemovedEvent)
case *org.LabelPolicyIconAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyIconAddedEvent)
case *org.LabelPolicyIconRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyIconRemovedEvent)
case *org.LabelPolicyIconDarkAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyIconDarkAddedEvent)
case *org.LabelPolicyIconDarkRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyIconDarkRemovedEvent)
case *org.LabelPolicyFontAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyFontAddedEvent)
case *org.LabelPolicyFontRemovedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyFontRemovedEvent)
}
}
}
@@ -44,26 +64,69 @@ func (wm *OrgLabelPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
ResourceOwner(wm.ResourceOwner).
EventTypes(
org.LabelPolicyAddedEventType,
org.LabelPolicyChangedEventType)
org.LabelPolicyChangedEventType,
org.LabelPolicyLogoAddedEventType,
org.LabelPolicyLogoRemovedEventType,
org.LabelPolicyIconAddedEventType,
org.LabelPolicyIconRemovedEventType,
org.LabelPolicyLogoDarkAddedEventType,
org.LabelPolicyLogoDarkRemovedEventType,
org.LabelPolicyIconDarkAddedEventType,
org.LabelPolicyIconDarkRemovedEventType,
org.LabelPolicyFontAddedEventType,
org.LabelPolicyFontRemovedEventType,
)
}
func (wm *OrgLabelPolicyWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
primaryColor,
secondaryColor string,
hideLoginNameSuffix bool,
backgroundColor,
warnColor,
fontColor,
primaryColorDark,
backgroundColorDark,
warnColorDark,
fontColorDark string,
hideLoginNameSuffix,
errorMsgPopup,
disableWatermark bool,
) (*org.LabelPolicyChangedEvent, bool) {
changes := make([]policy.LabelPolicyChanges, 0)
if wm.PrimaryColor != primaryColor {
changes = append(changes, policy.ChangePrimaryColor(primaryColor))
}
if wm.SecondaryColor != secondaryColor {
changes = append(changes, policy.ChangeSecondaryColor(secondaryColor))
if wm.BackgroundColor != backgroundColor {
changes = append(changes, policy.ChangeBackgroundColor(backgroundColor))
}
if wm.WarnColor != warnColor {
changes = append(changes, policy.ChangeWarnColor(warnColor))
}
if wm.FontColor != fontColor {
changes = append(changes, policy.ChangeFontColor(fontColor))
}
if wm.PrimaryColorDark != primaryColorDark {
changes = append(changes, policy.ChangePrimaryColorDark(primaryColorDark))
}
if wm.BackgroundColorDark != backgroundColorDark {
changes = append(changes, policy.ChangeBackgroundColorDark(backgroundColorDark))
}
if wm.WarnColorDark != warnColorDark {
changes = append(changes, policy.ChangeWarnColorDark(warnColorDark))
}
if wm.FontColorDark != fontColorDark {
changes = append(changes, policy.ChangeFontColorDark(fontColorDark))
}
if wm.HideLoginNameSuffix != hideLoginNameSuffix {
changes = append(changes, policy.ChangeHideLoginNameSuffix(hideLoginNameSuffix))
}
if wm.ErrorMsgPopup != errorMsgPopup {
changes = append(changes, policy.ChangeErrorMsgPopup(errorMsgPopup))
}
if wm.DisableWatermark != disableWatermark {
changes = append(changes, policy.ChangeDisableWatermark(disableWatermark))
}
if len(changes) == 0 {
return nil, false
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,9 +9,25 @@ import (
type LabelPolicyWriteModel struct {
eventstore.WriteModel
PrimaryColor string
SecondaryColor string
PrimaryColor string
BackgroundColor string
WarnColor string
FontColor string
LogoKey string
IconKey string
PrimaryColorDark string
BackgroundColorDark string
WarnColorDark string
FontColorDark string
LogoDarkKey string
IconDarkKey string
FontKey string
HideLoginNameSuffix bool
ErrorMsgPopup bool
DisableWatermark bool
State domain.PolicyState
}
@@ -21,19 +37,71 @@ func (wm *LabelPolicyWriteModel) Reduce() error {
switch e := event.(type) {
case *policy.LabelPolicyAddedEvent:
wm.PrimaryColor = e.PrimaryColor
wm.SecondaryColor = e.SecondaryColor
wm.BackgroundColor = e.BackgroundColor
wm.WarnColor = e.WarnColor
wm.FontColor = e.FontColor
wm.PrimaryColorDark = e.PrimaryColorDark
wm.BackgroundColorDark = e.BackgroundColorDark
wm.WarnColorDark = e.WarnColorDark
wm.FontColorDark = e.FontColorDark
wm.HideLoginNameSuffix = e.HideLoginNameSuffix
wm.ErrorMsgPopup = e.ErrorMsgPopup
wm.DisableWatermark = e.DisableWatermark
wm.State = domain.PolicyStateActive
case *policy.LabelPolicyChangedEvent:
if e.PrimaryColor != nil {
wm.PrimaryColor = *e.PrimaryColor
}
if e.SecondaryColor != nil {
wm.SecondaryColor = *e.SecondaryColor
if e.BackgroundColor != nil {
wm.BackgroundColor = *e.BackgroundColor
}
if e.WarnColor != nil {
wm.WarnColor = *e.WarnColor
}
if e.FontColor != nil {
wm.FontColor = *e.FontColor
}
if e.PrimaryColorDark != nil {
wm.PrimaryColorDark = *e.PrimaryColorDark
}
if e.BackgroundColorDark != nil {
wm.BackgroundColorDark = *e.BackgroundColorDark
}
if e.WarnColorDark != nil {
wm.WarnColorDark = *e.WarnColorDark
}
if e.FontColorDark != nil {
wm.FontColorDark = *e.FontColorDark
}
if e.HideLoginNameSuffix != nil {
wm.HideLoginNameSuffix = *e.HideLoginNameSuffix
}
if e.ErrorMsgPopup != nil {
wm.ErrorMsgPopup = *e.ErrorMsgPopup
}
if e.DisableWatermark != nil {
wm.DisableWatermark = *e.DisableWatermark
}
case *policy.LabelPolicyLogoAddedEvent:
wm.LogoKey = e.StoreKey
case *policy.LabelPolicyLogoRemovedEvent:
wm.LogoKey = ""
case *policy.LabelPolicyLogoDarkAddedEvent:
wm.LogoDarkKey = e.StoreKey
case *policy.LabelPolicyLogoDarkRemovedEvent:
wm.LogoDarkKey = ""
case *policy.LabelPolicyIconAddedEvent:
wm.IconKey = e.StoreKey
case *policy.LabelPolicyIconRemovedEvent:
wm.IconKey = ""
case *policy.LabelPolicyIconDarkAddedEvent:
wm.IconDarkKey = e.StoreKey
case *policy.LabelPolicyIconDarkRemovedEvent:
wm.IconDarkKey = ""
case *policy.LabelPolicyFontAddedEvent:
wm.FontKey = e.StoreKey
case *policy.LabelPolicyFontRemovedEvent:
wm.FontKey = ""
case *policy.LabelPolicyRemovedEvent:
wm.State = domain.PolicyStateRemoved
}

View File

@@ -44,7 +44,7 @@ func (c *Commands) SetupStep12(ctx context.Context, step *Step12) error {
LoginPolicyRegistration: step.LoginPolicyRegistration,
LoginPolicyUsernameLogin: step.LoginPolicyUsernameLogin,
PasswordComplexityPolicy: step.PasswordComplexityPolicy,
LabelPolicy: step.LabelPolicy,
LabelPolicyPrivateLabel: step.LabelPolicy,
CustomDomain: step.CustomDomain,
})
if err != nil {

View File

@@ -0,0 +1,48 @@
package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
iam_repo "github.com/caos/zitadel/internal/repository/iam"
org_repo "github.com/caos/zitadel/internal/repository/org"
)
type Step14 struct {
ActivateExistingLabelPolicies bool
}
func (s *Step14) Step() domain.Step {
return domain.Step14
}
func (s *Step14) execute(ctx context.Context, commandSide *Commands) error {
return commandSide.SetupStep14(ctx, s)
}
func (c *Commands) SetupStep14(ctx context.Context, step *Step14) error {
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel)
var events []eventstore.EventPusher
if step.ActivateExistingLabelPolicies {
existingPolicies := NewExistingLabelPoliciesReadModel(ctx)
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicies)
if err != nil {
return nil, err
}
for _, aggID := range existingPolicies.aggregateIDs {
if iamAgg.ID == aggID {
events = append(events, iam_repo.NewLabelPolicyActivatedEvent(ctx, iamAgg))
continue
}
events = append(events, org_repo.NewLabelPolicyActivatedEvent(ctx, &org_repo.NewAggregate(aggID, aggID).Aggregate))
}
}
logging.Log("SETUP-M9fsd").Info("activate login policies")
return events, nil
}
return c.setup(ctx, step, fn)
}

View File

@@ -0,0 +1,32 @@
package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
)
type Step15 struct {
DefaultMailTemplate domain.MailTemplate
}
func (s *Step15) Step() domain.Step {
return domain.Step15
}
func (s *Step15) execute(ctx context.Context, commandSide *Commands) error {
return commandSide.SetupStep15(ctx, s)
}
func (c *Commands) SetupStep15(ctx context.Context, step *Step15) error {
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
_, mailTemplateEvent, err := c.changeDefaultMailTemplate(ctx, &step.DefaultMailTemplate)
if err != nil {
return nil, err
}
logging.Log("SETUP-2nfsd").Info("default mail template/text set up")
return []eventstore.EventPusher{mailTemplateEvent}, nil
}
return c.setup(ctx, step, fn)
}

View File

@@ -2,15 +2,16 @@ package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type Step2 struct {
DefaultPasswordComplexityPolicy iam_model.PasswordComplexityPolicy
DefaultPasswordComplexityPolicy domain.PasswordComplexityPolicy
}
func (s *Step2) Step() domain.Step {

View File

@@ -2,15 +2,16 @@ package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type Step3 struct {
DefaultPasswordAgePolicy iam_model.PasswordAgePolicy
DefaultPasswordAgePolicy domain.PasswordAgePolicy
}
func (s *Step3) Step() domain.Step {

View File

@@ -2,15 +2,16 @@ package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type Step4 struct {
DefaultPasswordLockoutPolicy iam_model.PasswordLockoutPolicy
DefaultPasswordLockoutPolicy domain.PasswordLockoutPolicy
}
func (s *Step4) Step() domain.Step {

View File

@@ -2,15 +2,16 @@ package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type Step5 struct {
DefaultOrgIAMPolicy iam_model.OrgIAMPolicy
DefaultOrgIAMPolicy domain.OrgIAMPolicy
}
func (s *Step5) Step() domain.Step {

View File

@@ -2,15 +2,16 @@ package command
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type Step6 struct {
DefaultLabelPolicy iam_model.LabelPolicy
DefaultLabelPolicy domain.LabelPolicy
}
func (s *Step6) Step() domain.Step {
@@ -24,10 +25,7 @@ func (s *Step6) execute(ctx context.Context, commandSide *Commands) error {
func (c *Commands) SetupStep6(ctx context.Context, step *Step6) error {
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel)
event, err := c.addDefaultLabelPolicy(ctx, iamAgg, NewIAMLabelPolicyWriteModel(), &domain.LabelPolicy{
PrimaryColor: step.DefaultLabelPolicy.PrimaryColor,
SecondaryColor: step.DefaultLabelPolicy.SecondaryColor,
})
event, err := c.addDefaultLabelPolicy(ctx, iamAgg, NewIAMLabelPolicyWriteModel(), &step.DefaultLabelPolicy)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,27 @@
package command
import (
"context"
"io"
"github.com/caos/zitadel/internal/domain"
caos_errors "github.com/caos/zitadel/internal/errors"
)
func (c *Commands) UploadAsset(ctx context.Context, bucketName, objectName, contentType string, file io.Reader, size int64) (*domain.AssetInfo, error) {
if c.static == nil {
return nil, caos_errors.ThrowPreconditionFailed(nil, "STATIC-Fm92f", "Errors.Assets.Store.NotConfigured")
}
return c.static.PutObject(ctx,
bucketName,
objectName,
contentType,
file,
size,
true,
)
}
func (c *Commands) RemoveAsset(ctx context.Context, bucketName, storeKey string) error {
return c.static.RemoveObject(ctx, bucketName, storeKey)
}

View File

@@ -23,7 +23,7 @@ func (c *Commands) getHuman(ctx context.Context, userID, resourceowner string) (
func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Human) (*domain.Human, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M90d", "Errors.ResourceOwnerMissing")
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-XYFk9", "Errors.ResourceOwnerMissing")
}
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
if err != nil {
@@ -81,7 +81,7 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) ([]eventstore.EventPusher, *HumanWriteModel, error) {
if orgID == "" || !human.IsValid() {
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M90d", "Errors.User.Invalid")
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-67Ms8", "Errors.User.Invalid")
}
if human.Password != nil && human.SecretString != "" {
human.ChangeRequired = true
@@ -91,7 +91,7 @@ func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Hum
func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) ([]eventstore.EventPusher, *HumanWriteModel, error) {
if orgID == "" || !human.IsValid() {
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M90d", "Errors.User.Invalid")
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.User.Invalid")
}
return c.createHuman(ctx, orgID, human, nil, false, orgIAMPolicy, pwPolicy)
}

View File

@@ -0,0 +1,63 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/repository/user"
)
func (c *Commands) AddHumanAvatar(ctx context.Context, orgID, userID, storageKey string) (*domain.ObjectDetails, error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "USER-Ba5Ds", "Errors.IDMissing")
}
if storageKey == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "USER-1Xyud", "Errors.Assets.EmptyKey")
}
existingUser, err := c.userWriteModelByID(ctx, userID, orgID)
if err != nil {
return nil, err
}
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "USER-vJ3fS", "Errors.Users.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, user.NewHumanAvatarAddedEvent(ctx, userAgg, storageKey))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingUser, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingUser.WriteModel), nil
}
func (c *Commands) RemoveHumanAvatar(ctx context.Context, orgID, userID string) (*domain.ObjectDetails, error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "USER-1B8sd", "Errors.IDMissing")
}
existingUser, err := c.getHumanWriteModelByID(ctx, userID, orgID)
if err != nil {
return nil, err
}
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "USER-35N8f", "Errors.Users.NotFound")
}
err = c.RemoveAsset(ctx, orgID, existingUser.Avatar)
if err != nil {
return nil, err
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, user.NewHumanAvatarRemovedEvent(ctx, userAgg, existingUser.Avatar))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingUser, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingUser.WriteModel), nil
}

View File

@@ -0,0 +1,319 @@
package command
import (
"context"
"testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
"github.com/caos/zitadel/internal/repository/user"
"github.com/caos/zitadel/internal/static"
"github.com/caos/zitadel/internal/static/mock"
)
func TestCommandSide_AddHumanAvatar(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
orgID string
userID string
storageKey string
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userID empty, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
storageKey: "key",
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "storage key empty, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "user not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
storageKey: "key",
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "logo added, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.Und,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
user.NewHumanAvatarAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"key",
),
),
},
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
storageKey: "key",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.AddHumanAvatar(tt.args.ctx, tt.args.orgID, tt.args.userID, tt.args.storageKey)
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 {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_RemoveHumanAvatar(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
storage static.Storage
}
type args struct {
ctx context.Context
orgID string
userID string
storageKey string
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userID empty, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
storageKey: "key",
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "user not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
storageKey: "key",
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "file remove error, not found error",
fields: fields{
storage: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectError(),
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.Und,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
eventFromEventPusher(
user.NewHumanAvatarAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"key",
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
storageKey: "key",
},
res: res{
err: caos_errs.IsInternal,
},
},
{
name: "logo removed, ok",
fields: fields{
storage: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectNoError(),
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.Und,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
eventFromEventPusher(
user.NewHumanAvatarAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"key",
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
user.NewHumanAvatarRemovedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"key",
),
),
},
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
storageKey: "key",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
static: tt.fields.storage,
}
got, err := r.RemoveHumanAvatar(tt.args.ctx, tt.args.orgID, tt.args.userID)
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 {
assert.Equal(t, tt.res.want, got)
}
})
}
}

View File

@@ -1,11 +1,12 @@
package command
import (
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/user"
"golang.org/x/text/language"
)
type HumanWriteModel struct {
@@ -19,6 +20,7 @@ type HumanWriteModel struct {
DisplayName string
PreferredLanguage language.Tag
Gender domain.Gender
Avatar string
Email string
IsEmailVerified bool
@@ -74,6 +76,10 @@ func (wm *HumanWriteModel) Reduce() error {
wm.reduceHumanPhoneRemovedEvent()
case *user.HumanPasswordChangedEvent:
wm.reduceHumanPasswordChangedEvent(e)
case *user.HumanAvatarAddedEvent:
wm.Avatar = e.StoreKey
case *user.HumanAvatarRemovedEvent:
wm.Avatar = ""
case *user.UserLockedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateLocked
@@ -112,6 +118,8 @@ func (wm *HumanWriteModel) Query() *eventstore.SearchQueryBuilder {
user.HumanPhoneChangedType,
user.HumanPhoneVerifiedType,
user.HumanPhoneRemovedType,
user.HumanAvatarAddedType,
user.HumanAvatarRemovedType,
user.HumanPasswordChangedType,
user.UserLockedType,
user.UserUnlockedType,

View File

@@ -828,70 +828,70 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
args args
res res
}{
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
resourceOwner: "org1",
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "user not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
userID: "user1",
resourceOwner: "org1",
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "phone not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
userID: "user1",
resourceOwner: "org1",
},
res: res{
err: caos_errs.IsNotFound,
},
},
//{
// name: "userid missing, invalid argument error",
// fields: fields{
// eventstore: eventstoreExpect(
// t,
// ),
// },
// args: args{
// ctx: context.Background(),
// resourceOwner: "org1",
// },
// res: res{
// err: caos_errs.IsErrorInvalidArgument,
// },
//},
//{
// name: "user not existing, precondition error",
// fields: fields{
// eventstore: eventstoreExpect(
// t,
// expectFilter(),
// ),
// },
// args: args{
// ctx: context.Background(),
// userID: "user1",
// resourceOwner: "org1",
// },
// res: res{
// err: caos_errs.IsPreconditionFailed,
// },
//},
//{
// name: "phone not existing, precondition error",
// fields: fields{
// eventstore: eventstoreExpect(
// t,
// expectFilter(
// eventFromEventPusher(
// user.NewHumanAddedEvent(context.Background(),
// &user.NewAggregate("user1", "org1").Aggregate,
// "username",
// "firstname",
// "lastname",
// "nickname",
// "displayname",
// language.German,
// domain.GenderUnspecified,
// "email@test.ch",
// true,
// ),
// ),
// ),
// ),
// },
// args: args{
// ctx: context.Background(),
// userID: "user1",
// resourceOwner: "org1",
// },
// res: res{
// err: caos_errs.IsNotFound,
// },
//},
{
name: "remove phone, ok",
fields: fields{