From eac5045821ff7a02a5677f4a218e6735c64240dd Mon Sep 17 00:00:00 2001 From: Silvan Date: Wed, 24 Nov 2021 16:02:00 +0100 Subject: [PATCH] fix(queries): Label policy projection (#2479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * job queue * wg improvements * start handler * statement * statements * imporve handler * improve statement * statement in seperate file * move handlers * move query/old to query * handler * read models * bulk works * cleanup * contrib * rename readmodel to projection * rename read_models schema to projections * rename read_models schema to projections * search query as func, bulk iterates as long as new events * add event sequence less query * update checks for events between current sequence and sequence of first statement if it has previous sequence 0 * cleanup crdb projection * refactor projection handler * start with testing * tests for handler * remove todo * refactor statement: remove table name, add tests * improve projection handler shutdown, no savepoint if noop stmt, tests for stmt handler * tests * start failed events * seperate branch for contrib * move statement constructors to crdb pkg * correct import * Subscribe for eventtypes (#1800) * fix: is default (#1737) * fix: use email as username on global org (#1738) * fix: use email as username on global org * Update user_human.go * Update register_handler.go * chore(deps): update docusaurus (#1739) * chore: remove PAT and use GH Token (#1716) * chore: remove PAT and use GH Token * fix env * fix env * fix env * md lint * trigger ci * change user * fix GH bug * replace login part * chore: add GH Token to sem rel (#1746) * chore: add GH Token to sem rel * try branch * add GH Token * remove test branch again * docs: changes acme to acme-caos (#1744) * changes acme to acme-caos * Apply suggestions from code review Co-authored-by: Florian Forster Co-authored-by: Maximilian Panne Co-authored-by: Florian Forster * feat: add additional origins on applications (#1691) * feat: add additional origins on applications * app additional redirects * chore(deps-dev): bump @angular/cli from 11.2.8 to 11.2.11 in /console (#1706) * fix: show org with regex (#1688) * fix: flag mapping (#1699) * chore(deps-dev): bump @angular/cli from 11.2.8 to 11.2.11 in /console Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.2.8 to 11.2.11. - [Release notes](https://github.com/angular/angular-cli/releases) - [Commits](https://github.com/angular/angular-cli/compare/v11.2.8...v11.2.11) Signed-off-by: dependabot[bot] Co-authored-by: Max Peintner Co-authored-by: Silvan Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump stylelint from 13.10.0 to 13.13.1 in /console (#1703) * fix: show org with regex (#1688) * fix: flag mapping (#1699) * chore(deps-dev): bump stylelint from 13.10.0 to 13.13.1 in /console Bumps [stylelint](https://github.com/stylelint/stylelint) from 13.10.0 to 13.13.1. - [Release notes](https://github.com/stylelint/stylelint/releases) - [Changelog](https://github.com/stylelint/stylelint/blob/master/CHANGELOG.md) - [Commits](https://github.com/stylelint/stylelint/compare/13.10.0...13.13.1) Signed-off-by: dependabot[bot] Co-authored-by: Max Peintner Co-authored-by: Silvan Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/node from 14.14.37 to 15.0.1 in /console (#1702) * fix: show org with regex (#1688) * fix: flag mapping (#1699) * chore(deps-dev): bump @types/node from 14.14.37 to 15.0.1 in /console Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.37 to 15.0.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Signed-off-by: dependabot[bot] Co-authored-by: Max Peintner Co-authored-by: Silvan Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump ts-protoc-gen from 0.14.0 to 0.15.0 in /console (#1701) * fix: show org with regex (#1688) * fix: flag mapping (#1699) * chore(deps): bump ts-protoc-gen from 0.14.0 to 0.15.0 in /console Bumps [ts-protoc-gen](https://github.com/improbable-eng/ts-protoc-gen) from 0.14.0 to 0.15.0. - [Release notes](https://github.com/improbable-eng/ts-protoc-gen/releases) - [Changelog](https://github.com/improbable-eng/ts-protoc-gen/blob/master/CHANGELOG.md) - [Commits](https://github.com/improbable-eng/ts-protoc-gen/compare/0.14.0...0.15.0) Signed-off-by: dependabot[bot] Co-authored-by: Max Peintner Co-authored-by: Silvan Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @types/jasmine from 3.6.9 to 3.6.10 in /console (#1682) Bumps [@types/jasmine](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jasmine) from 3.6.9 to 3.6.10. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jasmine) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @types/google-protobuf in /console (#1681) Bumps [@types/google-protobuf](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/google-protobuf) from 3.7.4 to 3.15.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/google-protobuf) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump grpc from 1.24.5 to 1.24.7 in /console (#1666) Bumps [grpc](https://github.com/grpc/grpc-node) from 1.24.5 to 1.24.7. - [Release notes](https://github.com/grpc/grpc-node/releases) - [Commits](https://github.com/grpc/grpc-node/compare/grpc@1.24.5...grpc@1.24.7) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * lock * chore(deps-dev): bump @angular/language-service from 11.2.9 to 11.2.12 in /console (#1704) * fix: show org with regex (#1688) * fix: flag mapping (#1699) * chore(deps-dev): bump @angular/language-service in /console Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 11.2.9 to 11.2.12. - [Release notes](https://github.com/angular/angular/releases) - [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md) - [Commits](https://github.com/angular/angular/commits/11.2.12/packages/language-service) Signed-off-by: dependabot[bot] Co-authored-by: Max Peintner Co-authored-by: Silvan Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * package lock * downgrade grpc * downgrade protobuf types * revert npm packs 🥸 Co-authored-by: Max Peintner Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Silvan * docs: update run and start section texts (#1745) * update run and start section texts * adds showcase Co-authored-by: Maximilian Panne * fix: additional origin list (#1753) * fix: handle api configs in authz handler (#1755) * fix(console): add model for api keys, fix toast, binding (#1757) * fix: add model for api keys, fix toast, binding * show api clientid * fix: missing patchvalue (#1758) * feat: refresh token (#1728) * begin refresh tokens * refresh tokens * list and revoke refresh tokens * handle remove * tests for refresh tokens * uniqueness and default expiration * rename oidc token methods * cleanup * migration version * Update internal/static/i18n/en.yaml Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * fixes * feat: update oidc pkg for refresh tokens Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * fix: correct json name of clientId in key.json (#1760) * fix: migration version (#1767) * start subscription * eventtypes * fix(login): links (#1778) * fix(login): href for help * fix(login): correct link to tos * fix: access tokens for service users and refresh token infos (#1779) * fix: access token for service user * handle info from refresh request * uniqueness * postpone access token uniqueness change * chore(coc): recommend code of conduct (#1782) * subscribe for events * feat(console): refresh toggle out of granttype context (#1785) * refresh toggle * disable if not code flow, lint * lint * fix: change oidc config order * accept refresh option within flow Co-authored-by: Livio Amstutz * fix: refresh token activation (#1795) * fix: oidc grant type check * docs: add offline_access scope * docs: update refresh token status in supported grant types * fix: update oidc pkg * fix: check refresh token grant type (#1796) * configuration structs * org admins * failed events * fixes Co-authored-by: Max Peintner Co-authored-by: Livio Amstutz Co-authored-by: Florian Forster Co-authored-by: mffap Co-authored-by: Maximilian Panne Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * remove comment * aggregate reducer * remove eventtypes * add protoc-get-validate to mod * fix transaltion * upsert * add gender on org admins, allow to retry failed stmts after configurable time * remove if * sub queries * fix: tests * add builder to tests * new search query * rename searchquerybuilder to builder * remove comment from code * test with multiple queries * add filters test * current sequences * make org and org_admins work again * add aggregate type to current sequence * fix(contibute): listing * add validate module * fix: search queries * feat(eventstore): previous aggregate root sequence (#1810) * feat(eventstore): previous aggregate root sequence * fix tests * fix: eventstore v1 test * add col to all mocked rows * next try * fix mig * rename aggregate root to aggregate type * update comment Co-authored-by: Livio Amstutz Co-authored-by: Livio Amstutz * small refactorings * allow update multiple current sequences * unique log id * fix migrations * rename org admin to org owner * improve error handling and logging * fix(migration): optimize prev agg root seq * fix: projection handler test * fix: sub queries * small fixes * additional event types * correct org owner projection * fix primary key * feat(eventstore): jobs for projections (#2026) * fix: template names in login (#1974) * fix: template names in login * fix: error.html * fix: check for features on mgmt only (#1976) * fix: add sentry in ui, http and projection handlers (#1977) * fix: add sentry in ui, http and projection handlers * fix test * fix(eventstore): sub queries (#1805) * sub queries * fix: tests * add builder to tests * new search query * rename searchquerybuilder to builder * remove comment from code * test with multiple queries * add filters test * fix(contibute): listing * add validate module * fix: search queries * remove unused event type in query * ignore query if error in marshal * go mod tidy * update privacy policy query * update queries Co-authored-by: Livio Amstutz * feat: Extend oidc idp with oauth endpoints (#1980) * feat: add oauth attributes to oidc idp configuration * feat: return idpconfig id on create idp * feat: tests * feat: descriptions * feat: docs * feat: tests * docs: update to beta 3 (#1984) * fix: role assertion (#1986) * fix: enum to display access token role assertion * improve assertion descriptions * fix nil pointer * docs: eventstore (#1982) * docs: eventstore * Apply suggestions from code review Co-authored-by: Florian Forster Co-authored-by: Florian Forster * fix(sentry): trigger sentry release (#1989) * feat(send sentry release): send sentry release * fix(moved step and added releasetag): moved step and added releasetag * fix: set version for sentry release (#1990) * feat(send sentry release): send sentry release * fix(moved step and added releasetag): moved step and added releasetag * fix(corrected var name): corrected var name Co-authored-by: Livio Amstutz * fix: log error reason on terminate session (#1973) * fix: return default language file, if requested lang does not exist for default login texts (#1988) * fix: return default language file, if requested lang doesnt exists * feat: read default translation file * feat: docs * fix: race condition in auth request unmarshalling (#1993) * feat: handle ui_locales in login (#1994) * fix: handle ui_locales in login * move supportedlanguage func into i18n package * update oidc pkg * fix: handle closed channels on unsubscribe (#1995) * fix: give restore more time (#1997) * fix: translation file read (#2009) * feat: translation file read * feat: readme * fix: enable idp add button for iam users (#2010) * fix: filter event_data (#2011) * feat: Custom message files (#1992) * feat: add get custom message text to admin api * feat: read custom message texts from files * feat: get languages in apis * feat: get languages in apis * feat: get languages in apis * feat: pr feedback * feat: docs * feat: merge main * fix: sms notification (#2013) * fix: phone verifications * feat: fix password reset as sms * fix: phone verification * fix: grpc status in sentry and validation interceptors (#2012) * fix: remove oauth endpoints from oidc config proto (#2014) * try with view * fix(console): disable sw (#2021) * fix: disable sw * angular.json disable sw * project projections * fix typos * customize projections * customizable projections, add change date to projects Co-authored-by: Livio Amstutz Co-authored-by: Max Peintner Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Florian Forster Co-authored-by: mffap Co-authored-by: Christian Jakob <47860090+thesephirot@users.noreply.github.com> Co-authored-by: Elio Bischof * env file * typo * correct users * correct migration * fix: merge fail * fix test * fix(tests): unordered matcher * improve currentSequenceMatcher * correct certs * correct certs * add zitadel database on database list * refctor switch in match * enable all handlers * Delete io.env * cleanup * add handlers * rename view to projection * rename view to projection * fix type typo * remove unnecessary logs * refactor stmts * simplify interval calculation * fix tests * fix unlock test * fix migration * migs * fix(operator): update cockroach and flyway versions (#2138) * chore(deps): bump k8s.io/apiextensions-apiserver from 0.19.2 to 0.21.3 Bumps [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) from 0.19.2 to 0.21.3. - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.19.2...v0.21.3) --- updated-dependencies: - dependency-name: k8s.io/apiextensions-apiserver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump google.golang.org/api from 0.34.0 to 0.52.0 Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.34.0 to 0.52.0. - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/master/CHANGES.md) - [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.34.0...v0.52.0) --- updated-dependencies: - dependency-name: google.golang.org/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * start update dependencies * update mods and otlp * fix(build): update to go 1.16 * old version for k8s mods * update k8s versions * update orbos * fix(operator): update cockroach and flyway version * Update images.go Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Stefan Benz * fix import * fix typo * fix(migration): add org projection * fix(projection): correct table for org events in org owners * better insert stmt * fix typo * fix typo * set max connection lifetime * set max conns and conn lifetime in eventstore v1 * configure sql connection settings * add mig for agg type index * fix replace tab in yaml * handler interfaces * subscription * first try * handler * move sql client initialization * first part implemented * removed all occurencies of org by id and search orgs * fix merge issues * cleanup code * fix: queries implements orgviewprovider * cleanup * refactor text comparison * remove unused file * remove unused code * log * remove unused code * remove unused field * remove unused file * refactor * tests for search query * remove try * simplify state change mappers * projection tests * query functions * move reusable objects to separate files * rename domain column to primar_domain * fix tests * add current sequence * remove log prints * fix tests * fix: verifier * fix test * rename domain col migrations * simplify search response * add custom column constructors * fix: org projection table const * fix: full column name * feat: text query extension * fix: tests for query * number query * add deprection message * projection * correct migration * projection * projection * column in a single place (#2416) * column in a single place * use projection for columns * query column with aliases * rename methods * remove unused code * column for current sequences * correct file name * global counter column * fix is org unique * query * fix wrong code * remove unused code * query * remove unused code * remove unused code * query * api * remove unused cod * remove unused code * remove unused code * remove unused code * fix(queries): org iam policy * fix: init provider * tests * tests * tests * tests * tests * add copy stmt * label policy projection * tests * constant naming in test * query * fix nil pointer * fix test * refactor: remove useless file * fix(configs): add options to setup * fix: sql changes * tests * remove old comment * rename label policy to styling * fix where * remove unused logs * migration * correct primary key Co-authored-by: Livio Amstutz Co-authored-by: Max Peintner Co-authored-by: Livio Amstutz Co-authored-by: Florian Forster Co-authored-by: mffap Co-authored-by: Maximilian Panne Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Christian Jakob <47860090+thesephirot@users.noreply.github.com> Co-authored-by: Elio Bischof Co-authored-by: Stefan Benz Co-authored-by: fabi --- .gitignore | 4 + cmd/zitadel/main.go | 2 +- cmd/zitadel/setup.yaml | 1 + .../eventsourcing/eventstore/iam.go | 16 - .../eventsourcing/handler/handler.go | 2 - .../eventsourcing/handler/label_policy.go | 125 --- .../eventsourcing/handler/styling.go | 15 +- .../handler/user_external_idps.go | 3 +- .../eventsourcing/view/label_policies.go | 44 - .../repository/eventsourcing/view/styling.go | 4 +- internal/admin/repository/iam.go | 3 - internal/api/assets/asset.go | 4 + internal/api/assets/login_policy.go | 85 +- internal/api/grpc/admin/label_policy.go | 4 +- internal/api/grpc/management/policy_label.go | 10 +- internal/api/grpc/policy/label_policy.go | 34 +- .../eventsourcing/eventstore/auth_request.go | 46 +- .../eventsourcing/eventstore/org.go | 16 - .../eventsourcing/handler/handler.go | 1 - .../eventsourcing/handler/label_policy.go | 123 --- .../repository/eventsourcing/repository.go | 1 + .../eventsourcing/view/label_policies.go | 53 - internal/auth/repository/org.go | 1 - internal/eventstore/handler/crdb/statement.go | 60 ++ .../eventstore/handler/crdb/statement_test.go | 176 ++++ .../view/{label_policy_view.go => styling.go} | 6 +- .../eventsourcing/eventstore/org.go | 64 -- .../eventsourcing/handler/handler.go | 3 - .../eventsourcing/handler/label_policy.go | 160 --- .../eventsourcing/view/label_policies.go | 53 - internal/management/repository/org.go | 5 - .../eventsourcing/handler/notification.go | 6 +- .../eventsourcing/view/label_policies.go | 4 +- internal/query/idp.go | 2 - internal/query/label_policy.go | 300 ++++++ internal/query/projection/label_policy.go | 531 ++++++++++ .../query/projection/label_policy_test.go | 981 ++++++++++++++++++ internal/query/projection/projection.go | 1 + .../passwordless_registration_handler.go | 38 +- migrations/cockroach/V1.94__label_policy.sql | 28 + 40 files changed, 2245 insertions(+), 770 deletions(-) delete mode 100644 internal/admin/repository/eventsourcing/handler/label_policy.go delete mode 100644 internal/admin/repository/eventsourcing/view/label_policies.go delete mode 100644 internal/auth/repository/eventsourcing/handler/label_policy.go delete mode 100644 internal/auth/repository/eventsourcing/view/label_policies.go rename internal/iam/repository/view/{label_policy_view.go => styling.go} (79%) delete mode 100644 internal/management/repository/eventsourcing/handler/label_policy.go delete mode 100644 internal/management/repository/eventsourcing/view/label_policies.go create mode 100644 internal/query/label_policy.go create mode 100644 internal/query/projection/label_policy.go create mode 100644 internal/query/projection/label_policy_test.go create mode 100644 migrations/cockroach/V1.94__label_policy.sql diff --git a/.gitignore b/.gitignore index d3e9529d41..44d5c04628 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,7 @@ console/src/app/proto/generated/ openapi/**/*.json /internal/api/assets/authz.go /internal/api/assets/router.go + +# local +build/local/cloud.env +migrations/cockroach/migrate_cloud.go diff --git a/cmd/zitadel/main.go b/cmd/zitadel/main.go index cc40cbeb63..f10b85622c 100644 --- a/cmd/zitadel/main.go +++ b/cmd/zitadel/main.go @@ -240,7 +240,7 @@ func startAPI(ctx context.Context, conf *Config, verifier *internal_authz.TokenV apis.RegisterHandler("/oauth/v2", op.HttpHandler()) } if *assetsEnabled { - assetsHandler := assets.NewHandler(command, verifier, conf.InternalAuthZ, id.SonyFlakeGenerator, static, managementRepo) + assetsHandler := assets.NewHandler(command, verifier, conf.InternalAuthZ, id.SonyFlakeGenerator, static, managementRepo, query) apis.RegisterHandler("/assets/v1", assetsHandler) } diff --git a/cmd/zitadel/setup.yaml b/cmd/zitadel/setup.yaml index 0643eae9ee..dbdccc602f 100644 --- a/cmd/zitadel/setup.yaml +++ b/cmd/zitadel/setup.yaml @@ -9,6 +9,7 @@ Eventstore: User: 'eventstore' Database: 'eventstore' Password: $CR_EVENTSTORE_PASSWORD + Options: $CR_OPTIONS SSL: Mode: $CR_SSL_MODE RootCert: $CR_ROOT_CERT diff --git a/internal/admin/repository/eventsourcing/eventstore/iam.go b/internal/admin/repository/eventsourcing/eventstore/iam.go index 11a379a4d9..e4ad17539e 100644 --- a/internal/admin/repository/eventsourcing/eventstore/iam.go +++ b/internal/admin/repository/eventsourcing/eventstore/iam.go @@ -167,22 +167,6 @@ func (repo *IAMRepository) SearchDefaultIDPProviders(ctx context.Context, reques return result, nil } -func (repo *IAMRepository) GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { - policy, err := repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(domain.LabelPolicyStateActive)) - if err != nil { - return nil, err - } - return iam_es_model.LabelPolicyViewToModel(policy), err -} - -func (repo *IAMRepository) GetDefaultPreviewLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { - policy, err := repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(domain.LabelPolicyStatePreview)) - if err != nil { - return nil, err - } - return iam_es_model.LabelPolicyViewToModel(policy), err -} - func (repo *IAMRepository) GetDefaultMailTemplate(ctx context.Context) (*iam_model.MailTemplateView, error) { template, err := repo.View.MailTemplateByAggregateID(repo.SystemDefaults.IamID) if err != nil { diff --git a/internal/admin/repository/eventsourcing/handler/handler.go b/internal/admin/repository/eventsourcing/handler/handler.go index 6fdb301587..9aad062873 100644 --- a/internal/admin/repository/eventsourcing/handler/handler.go +++ b/internal/admin/repository/eventsourcing/handler/handler.go @@ -37,8 +37,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount, es}), newIDPConfig( handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), - newLabelPolicy( - handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount, es}), newIDPProvider( handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount, es}, defaults), diff --git a/internal/admin/repository/eventsourcing/handler/label_policy.go b/internal/admin/repository/eventsourcing/handler/label_policy.go deleted file mode 100644 index c54c39be98..0000000000 --- a/internal/admin/repository/eventsourcing/handler/label_policy.go +++ /dev/null @@ -1,125 +0,0 @@ -package handler - -import ( - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/domain" - "github.com/caos/zitadel/internal/eventstore/v1" - es_models "github.com/caos/zitadel/internal/eventstore/v1/models" - "github.com/caos/zitadel/internal/eventstore/v1/query" - "github.com/caos/zitadel/internal/eventstore/v1/spooler" - "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" -) - -const ( - labelPolicyTable = "adminapi.label_policies" -) - -type LabelPolicy struct { - handler - subscription *v1.Subscription -} - -func newLabelPolicy(handler handler) *LabelPolicy { - h := &LabelPolicy{ - handler: handler, - } - - h.subscribe() - - return h -} - -func (p *LabelPolicy) subscribe() { - p.subscription = p.es.Subscribe(p.AggregateTypes()...) - go func() { - for event := range p.subscription.Events { - query.ReduceEvent(p, event) - } - }() -} - -func (p *LabelPolicy) ViewModel() string { - return labelPolicyTable -} - -func (p *LabelPolicy) Subscription() *v1.Subscription { - return p.subscription -} - -func (p *LabelPolicy) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{model.IAMAggregate} -} - -func (p *LabelPolicy) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := p.view.GetLatestLabelPolicySequence() - if err != nil { - return nil, err - } - return es_models.NewSearchQuery(). - AggregateTypeFilter(p.AggregateTypes()...). - LatestSequenceFilter(sequence.CurrentSequence), nil -} - -func (p *LabelPolicy) CurrentSequence() (uint64, error) { - sequence, err := p.view.GetLatestLabelPolicySequence() - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (p *LabelPolicy) Reduce(event *es_models.Event) (err error) { - switch event.AggregateType { - case model.IAMAggregate: - err = p.processLabelPolicy(event) - } - return err -} - -func (p *LabelPolicy) processLabelPolicy(event *es_models.Event) (err error) { - policy := new(iam_model.LabelPolicyView) - switch event.Type { - case model.LabelPolicyAdded: - err = policy.AppendEvent(event) - case model.LabelPolicyChanged, - model.LabelPolicyLogoAdded, - model.LabelPolicyLogoRemoved, - model.LabelPolicyIconAdded, - model.LabelPolicyIconRemoved, - model.LabelPolicyLogoDarkAdded, - model.LabelPolicyLogoDarkRemoved, - model.LabelPolicyIconDarkAdded, - model.LabelPolicyIconDarkRemoved, - model.LabelPolicyFontAdded, - model.LabelPolicyFontRemoved, - model.LabelPolicyAssetsRemoved: - policy, err = p.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview)) - if err != nil { - return err - } - err = policy.AppendEvent(event) - case model.LabelPolicyActivated: - policy, err = p.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview)) - if err != nil { - return err - } - err = policy.AppendEvent(event) - default: - return p.view.ProcessedLabelPolicySequence(event) - } - if err != nil { - return err - } - return p.view.PutLabelPolicy(policy, event) -} - -func (p *LabelPolicy) OnError(event *es_models.Event, err error) error { - logging.LogWithFields("SPOOL-Wj8sf", "id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler") - return spooler.HandleError(event, err, p.view.GetLatestLabelPolicyFailedEvent, p.view.ProcessedLabelPolicyFailedEvent, p.view.ProcessedLabelPolicySequence, p.errorCountUntilSkip) -} - -func (p *LabelPolicy) OnSuccess() error { - return spooler.HandleSuccess(p.view.UpdateLabelPolicySpoolerRunTimestamp) -} diff --git a/internal/admin/repository/eventsourcing/handler/styling.go b/internal/admin/repository/eventsourcing/handler/styling.go index f449098d21..eae24492cb 100644 --- a/internal/admin/repository/eventsourcing/handler/styling.go +++ b/internal/admin/repository/eventsourcing/handler/styling.go @@ -12,7 +12,7 @@ import ( "github.com/muesli/gamut" "github.com/caos/zitadel/internal/domain" - "github.com/caos/zitadel/internal/eventstore/v1" + v1 "github.com/caos/zitadel/internal/eventstore/v1" es_models "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/query" "github.com/caos/zitadel/internal/eventstore/v1/spooler" @@ -30,7 +30,6 @@ type Styling struct { handler static static.Storage subscription *v1.Subscription - devMode bool resourceUrl string } @@ -141,11 +140,11 @@ func (m *Styling) processLabelPolicy(event *es_models.Event) (err error) { func (m *Styling) OnError(event *es_models.Event, err error) error { logging.LogWithFields("SPOOL-2m9fs", "id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler") - return spooler.HandleError(event, err, m.view.GetLatestLabelPolicyFailedEvent, m.view.ProcessedLabelPolicyFailedEvent, m.view.ProcessedLabelPolicySequence, m.errorCountUntilSkip) + return spooler.HandleError(event, err, m.view.GetLatestStylingFailedEvent, m.view.ProcessedStylingFailedEvent, m.view.ProcessedStylingSequence, m.errorCountUntilSkip) } func (m *Styling) OnSuccess() error { - return spooler.HandleSuccess(m.view.UpdateLabelPolicySpoolerRunTimestamp) + return spooler.HandleSuccess(m.view.UpdateStylingSpoolerRunTimestamp) } func (m *Styling) generateStylingFile(policy *iam_model.LabelPolicyView) error { @@ -158,7 +157,7 @@ func (m *Styling) generateStylingFile(policy *iam_model.LabelPolicyView) error { func (m *Styling) writeFile(policy *iam_model.LabelPolicyView) (io.Reader, int64, error) { cssContent := "" - cssContent += fmt.Sprint(":root {") + cssContent += ":root {" if policy.PrimaryColor != "" { palette := m.generateColorPaletteRGBA255(policy.PrimaryColor) for i, color := range palette { @@ -190,11 +189,11 @@ func (m *Styling) writeFile(policy *iam_model.LabelPolicyView) (io.Reader, int64 fontname = split[len(split)-1] cssContent += fmt.Sprintf("--zitadel-font-family: %s;", fontname) } - cssContent += fmt.Sprint("}") + cssContent += "}" if policy.FontURL != "" { cssContent += fmt.Sprintf(fontFaceTemplate, fontname, m.resourceUrl, policy.AggregateID, policy.FontURL) } - cssContent += fmt.Sprint(".lgn-dark-theme {") + cssContent += ".lgn-dark-theme {" if policy.PrimaryColorDark != "" { palette := m.generateColorPaletteRGBA255(policy.PrimaryColorDark) for i, color := range palette { @@ -219,7 +218,7 @@ func (m *Styling) writeFile(policy *iam_model.LabelPolicyView) (io.Reader, int64 cssContent += fmt.Sprintf("--zitadel-color-text-%v: %s;", i, color) } } - cssContent += fmt.Sprint("}") + cssContent += "}" data := []byte(cssContent) buffer := bytes.NewBuffer(data) diff --git a/internal/admin/repository/eventsourcing/handler/user_external_idps.go b/internal/admin/repository/eventsourcing/handler/user_external_idps.go index 0cb37fa07e..d4331a88ef 100644 --- a/internal/admin/repository/eventsourcing/handler/user_external_idps.go +++ b/internal/admin/repository/eventsourcing/handler/user_external_idps.go @@ -2,11 +2,12 @@ package handler import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/v1" + v1 "github.com/caos/zitadel/internal/eventstore/v1" es_models "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/query" es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk" diff --git a/internal/admin/repository/eventsourcing/view/label_policies.go b/internal/admin/repository/eventsourcing/view/label_policies.go deleted file mode 100644 index 6ad222159c..0000000000 --- a/internal/admin/repository/eventsourcing/view/label_policies.go +++ /dev/null @@ -1,44 +0,0 @@ -package view - -import ( - "github.com/caos/zitadel/internal/eventstore/v1/models" - "github.com/caos/zitadel/internal/iam/repository/view" - "github.com/caos/zitadel/internal/iam/repository/view/model" - global_view "github.com/caos/zitadel/internal/view/repository" -) - -const ( - labelPolicyTable = "adminapi.label_policies" -) - -func (v *View) LabelPolicyByAggregateIDAndState(aggregateID string, state int32) (*model.LabelPolicyView, error) { - return view.GetLabelPolicyByAggregateIDAndState(v.Db, labelPolicyTable, aggregateID, state) -} - -func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error { - err := view.PutLabelPolicy(v.Db, labelPolicyTable, policy) - if err != nil { - return err - } - return v.ProcessedLabelPolicySequence(event) -} - -func (v *View) GetLatestLabelPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(labelPolicyTable) -} - -func (v *View) ProcessedLabelPolicySequence(event *models.Event) error { - return v.saveCurrentSequence(labelPolicyTable, event) -} - -func (v *View) UpdateLabelPolicySpoolerRunTimestamp() error { - return v.updateSpoolerRunSequence(labelPolicyTable) -} - -func (v *View) GetLatestLabelPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) { - return v.latestFailedEvent(labelPolicyTable, sequence) -} - -func (v *View) ProcessedLabelPolicyFailedEvent(failedEvent *global_view.FailedEvent) error { - return v.saveFailedEvent(failedEvent) -} diff --git a/internal/admin/repository/eventsourcing/view/styling.go b/internal/admin/repository/eventsourcing/view/styling.go index 66e22f44a1..7d78172a25 100644 --- a/internal/admin/repository/eventsourcing/view/styling.go +++ b/internal/admin/repository/eventsourcing/view/styling.go @@ -12,11 +12,11 @@ const ( ) func (v *View) StylingByAggregateIDAndState(aggregateID string, state int32) (*model.LabelPolicyView, error) { - return view.GetLabelPolicyByAggregateIDAndState(v.Db, stylingTyble, aggregateID, state) + return view.GetStylingByAggregateIDAndState(v.Db, stylingTyble, aggregateID, state) } func (v *View) PutStyling(policy *model.LabelPolicyView, event *models.Event) error { - err := view.PutLabelPolicy(v.Db, stylingTyble, policy) + err := view.PutStyling(v.Db, stylingTyble, policy) if err != nil { return err } diff --git a/internal/admin/repository/iam.go b/internal/admin/repository/iam.go index 3937b49ba1..b2d978c2fd 100644 --- a/internal/admin/repository/iam.go +++ b/internal/admin/repository/iam.go @@ -25,9 +25,6 @@ type IAMRepository interface { IDPProvidersByIDPConfigID(ctx context.Context, idpConfigID string) ([]*iam_model.IDPProviderView, error) ExternalIDPsByIDPConfigID(ctx context.Context, idpConfigID string) ([]*usr_model.ExternalIDPView, error) - GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) - GetDefaultPreviewLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) - GetDefaultMailTemplate(ctx context.Context) (*iam_model.MailTemplateView, error) GetDefaultMessageText(ctx context.Context, textType, language string) (*domain.CustomMessageText, error) diff --git a/internal/api/assets/asset.go b/internal/api/assets/asset.go index ecef142696..442ddb0ad6 100644 --- a/internal/api/assets/asset.go +++ b/internal/api/assets/asset.go @@ -18,6 +18,7 @@ import ( "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/id" "github.com/caos/zitadel/internal/management/repository" + "github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/internal/static" ) @@ -28,6 +29,7 @@ type Handler struct { authInterceptor *http_mw.AuthInterceptor idGenerator id.Generator orgRepo repository.OrgRepository + query *query.Queries } func (h *Handler) AuthInterceptor() *http_mw.AuthInterceptor { @@ -73,6 +75,7 @@ func NewHandler( idGenerator id.Generator, storage static.Storage, orgRepo repository.OrgRepository, + queries *query.Queries, ) http.Handler { h := &Handler{ commands: commands, @@ -81,6 +84,7 @@ func NewHandler( idGenerator: idGenerator, storage: storage, orgRepo: orgRepo, + query: queries, } verifier.RegisterServer("Management-API", "assets", AssetsService_AuthMethods) //TODO: separate api? diff --git a/internal/api/assets/login_policy.go b/internal/api/assets/login_policy.go index 586ec43e96..e816469fce 100644 --- a/internal/api/assets/login_policy.go +++ b/internal/api/assets/login_policy.go @@ -7,9 +7,8 @@ import ( "github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/domain" - "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/id" - "github.com/caos/zitadel/internal/management/repository" + "github.com/caos/zitadel/internal/query" ) func (h *Handler) UploadDefaultLabelPolicyLogo() Uploader { @@ -86,57 +85,57 @@ func (l *labelPolicyLogoUploader) Callback(ctx context.Context, info *domain.Ass } func (h *Handler) GetDefaultLabelPolicyLogo() Downloader { - return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: true, preview: false} + return &labelPolicyLogoDownloader{query: h.query, darkMode: false, defaultPolicy: true, preview: false} } func (h *Handler) GetDefaultLabelPolicyLogoDark() Downloader { - return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: true, preview: false} + return &labelPolicyLogoDownloader{query: h.query, darkMode: true, defaultPolicy: true, preview: false} } func (h *Handler) GetPreviewDefaultLabelPolicyLogo() Downloader { - return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: true, preview: true} + return &labelPolicyLogoDownloader{query: h.query, darkMode: false, defaultPolicy: true, preview: true} } func (h *Handler) GetPreviewDefaultLabelPolicyLogoDark() Downloader { - return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: true, preview: true} + return &labelPolicyLogoDownloader{query: h.query, darkMode: true, defaultPolicy: true, preview: true} } func (h *Handler) GetOrgLabelPolicyLogo() Downloader { - return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: false, preview: false} + return &labelPolicyLogoDownloader{query: h.query, darkMode: false, defaultPolicy: false, preview: false} } func (h *Handler) GetOrgLabelPolicyLogoDark() Downloader { - return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: false, preview: false} + return &labelPolicyLogoDownloader{query: h.query, darkMode: true, defaultPolicy: false, preview: false} } func (h *Handler) GetPreviewOrgLabelPolicyLogo() Downloader { - return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: false, preview: true} + return &labelPolicyLogoDownloader{query: h.query, darkMode: false, defaultPolicy: false, preview: true} } func (h *Handler) GetPreviewOrgLabelPolicyLogoDark() Downloader { - return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: false, preview: true} + return &labelPolicyLogoDownloader{query: h.query, darkMode: true, defaultPolicy: false, preview: true} } type labelPolicyLogoDownloader struct { - org repository.OrgRepository + query *query.Queries darkMode bool defaultPolicy bool preview bool } func (l *labelPolicyLogoDownloader) ObjectName(ctx context.Context, path string) (string, error) { - policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.org) + policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.query) if err != nil { return "", nil } if l.darkMode { - return policy.LogoDarkURL, nil + return policy.Dark.LogoURL, nil } - return policy.LogoURL, nil + return policy.Light.LogoURL, nil } func (l *labelPolicyLogoDownloader) BucketName(ctx context.Context, id string) string { - return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.org) + return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.query) } func (h *Handler) UploadDefaultLabelPolicyIcon() Uploader { @@ -214,57 +213,57 @@ func (l *labelPolicyIconUploader) Callback(ctx context.Context, info *domain.Ass } func (h *Handler) GetDefaultLabelPolicyIcon() Downloader { - return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: true, preview: false} + return &labelPolicyIconDownloader{query: h.query, darkMode: false, defaultPolicy: true, preview: false} } func (h *Handler) GetDefaultLabelPolicyIconDark() Downloader { - return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: true, preview: false} + return &labelPolicyIconDownloader{query: h.query, darkMode: true, defaultPolicy: true, preview: false} } func (h *Handler) GetPreviewDefaultLabelPolicyIcon() Downloader { - return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: true, preview: true} + return &labelPolicyIconDownloader{query: h.query, darkMode: false, defaultPolicy: true, preview: true} } func (h *Handler) GetPreviewDefaultLabelPolicyIconDark() Downloader { - return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: true, preview: true} + return &labelPolicyIconDownloader{query: h.query, darkMode: true, defaultPolicy: true, preview: true} } func (h *Handler) GetOrgLabelPolicyIcon() Downloader { - return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: false, preview: false} + return &labelPolicyIconDownloader{query: h.query, darkMode: false, defaultPolicy: false, preview: false} } func (h *Handler) GetOrgLabelPolicyIconDark() Downloader { - return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: false, preview: false} + return &labelPolicyIconDownloader{query: h.query, darkMode: true, defaultPolicy: false, preview: false} } func (h *Handler) GetPreviewOrgLabelPolicyIcon() Downloader { - return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: false, preview: true} + return &labelPolicyIconDownloader{query: h.query, darkMode: false, defaultPolicy: false, preview: true} } func (h *Handler) GetPreviewOrgLabelPolicyIconDark() Downloader { - return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: false, preview: true} + return &labelPolicyIconDownloader{query: h.query, darkMode: true, defaultPolicy: false, preview: true} } type labelPolicyIconDownloader struct { - org repository.OrgRepository + query *query.Queries darkMode bool defaultPolicy bool preview bool } func (l *labelPolicyIconDownloader) ObjectName(ctx context.Context, path string) (string, error) { - policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.org) + policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.query) if err != nil { return "", nil } if l.darkMode { - return policy.IconDarkURL, nil + return policy.Dark.IconURL, nil } - return policy.IconURL, nil + return policy.Light.IconURL, nil } func (l *labelPolicyIconDownloader) BucketName(ctx context.Context, id string) string { - return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.org) + return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.query) } func (h *Handler) UploadDefaultLabelPolicyFont() Uploader { @@ -321,29 +320,29 @@ func (l *labelPolicyFontUploader) Callback(ctx context.Context, info *domain.Ass } func (h *Handler) GetDefaultLabelPolicyFont() Downloader { - return &labelPolicyFontDownloader{org: h.orgRepo, defaultPolicy: true, preview: false} + return &labelPolicyFontDownloader{query: h.query, defaultPolicy: true, preview: false} } func (h *Handler) GetPreviewDefaultLabelPolicyFont() Downloader { - return &labelPolicyFontDownloader{org: h.orgRepo, defaultPolicy: true, preview: true} + return &labelPolicyFontDownloader{query: h.query, defaultPolicy: true, preview: true} } func (h *Handler) GetOrgLabelPolicyFont() Downloader { - return &labelPolicyFontDownloader{org: h.orgRepo, defaultPolicy: false, preview: false} + return &labelPolicyFontDownloader{query: h.query, defaultPolicy: false, preview: false} } func (h *Handler) GetPreviewOrgLabelPolicyFont() Downloader { - return &labelPolicyFontDownloader{org: h.orgRepo, defaultPolicy: true, preview: true} + return &labelPolicyFontDownloader{query: h.query, defaultPolicy: true, preview: true} } type labelPolicyFontDownloader struct { - org repository.OrgRepository + query *query.Queries defaultPolicy bool preview bool } func (l *labelPolicyFontDownloader) ObjectName(ctx context.Context, path string) (string, error) { - policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.org) + policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.query) if err != nil { return "", nil } @@ -351,31 +350,31 @@ func (l *labelPolicyFontDownloader) ObjectName(ctx context.Context, path string) } func (l *labelPolicyFontDownloader) BucketName(ctx context.Context, id string) string { - return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.org) + return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.query) } -func getLabelPolicy(ctx context.Context, defaultPolicy, preview bool, orgRepo repository.OrgRepository) (*model.LabelPolicyView, error) { +func getLabelPolicy(ctx context.Context, defaultPolicy, preview bool, queries *query.Queries) (*query.LabelPolicy, error) { if defaultPolicy { if preview { - return orgRepo.GetPreviewDefaultLabelPolicy(ctx) + return queries.DefaultPreviewLabelPolicy(ctx) } - return orgRepo.GetDefaultLabelPolicy(ctx) + return queries.DefaultActiveLabelPolicy(ctx) } if preview { - return orgRepo.GetPreviewLabelPolicy(ctx) + return queries.PreviewLabelPolicyByOrg(ctx, authz.GetCtxData(ctx).OrgID) } - return orgRepo.GetLabelPolicy(ctx) + return queries.ActiveLabelPolicyByOrg(ctx, authz.GetCtxData(ctx).OrgID) } -func getLabelPolicyBucketName(ctx context.Context, defaultPolicy, preview bool, org repository.OrgRepository) string { +func getLabelPolicyBucketName(ctx context.Context, defaultPolicy, preview bool, queries *query.Queries) string { if defaultPolicy { return domain.IAMID } - policy, err := getLabelPolicy(ctx, defaultPolicy, preview, org) + policy, err := getLabelPolicy(ctx, defaultPolicy, preview, queries) if err != nil { return "" } - if policy.Default { + if policy.IsDefault { return domain.IAMID } return authz.GetCtxData(ctx).OrgID diff --git a/internal/api/grpc/admin/label_policy.go b/internal/api/grpc/admin/label_policy.go index ebd56bf269..54df2a414f 100644 --- a/internal/api/grpc/admin/label_policy.go +++ b/internal/api/grpc/admin/label_policy.go @@ -9,7 +9,7 @@ import ( ) func (s *Server) GetLabelPolicy(ctx context.Context, req *admin_pb.GetLabelPolicyRequest) (*admin_pb.GetLabelPolicyResponse, error) { - policy, err := s.iam.GetDefaultLabelPolicy(ctx) + policy, err := s.query.DefaultActiveLabelPolicy(ctx) if err != nil { return nil, err } @@ -17,7 +17,7 @@ func (s *Server) GetLabelPolicy(ctx context.Context, req *admin_pb.GetLabelPolic } func (s *Server) GetPreviewLabelPolicy(ctx context.Context, req *admin_pb.GetPreviewLabelPolicyRequest) (*admin_pb.GetPreviewLabelPolicyResponse, error) { - policy, err := s.iam.GetDefaultPreviewLabelPolicy(ctx) + policy, err := s.query.DefaultPreviewLabelPolicy(ctx) if err != nil { return nil, err } diff --git a/internal/api/grpc/management/policy_label.go b/internal/api/grpc/management/policy_label.go index 2c5b6c2fc7..a5abcd3146 100644 --- a/internal/api/grpc/management/policy_label.go +++ b/internal/api/grpc/management/policy_label.go @@ -10,23 +10,23 @@ import ( ) func (s *Server) GetLabelPolicy(ctx context.Context, req *mgmt_pb.GetLabelPolicyRequest) (*mgmt_pb.GetLabelPolicyResponse, error) { - policy, err := s.org.GetLabelPolicy(ctx) + policy, err := s.query.ActiveLabelPolicyByOrg(ctx, authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return &mgmt_pb.GetLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy), IsDefault: policy.Default}, nil + return &mgmt_pb.GetLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy), IsDefault: policy.IsDefault}, nil } func (s *Server) GetPreviewLabelPolicy(ctx context.Context, req *mgmt_pb.GetPreviewLabelPolicyRequest) (*mgmt_pb.GetPreviewLabelPolicyResponse, error) { - policy, err := s.org.GetPreviewLabelPolicy(ctx) + policy, err := s.query.PreviewLabelPolicyByOrg(ctx, authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return &mgmt_pb.GetPreviewLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy)}, nil + return &mgmt_pb.GetPreviewLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy), IsDefault: policy.IsDefault}, nil } func (s *Server) GetDefaultLabelPolicy(ctx context.Context, req *mgmt_pb.GetDefaultLabelPolicyRequest) (*mgmt_pb.GetDefaultLabelPolicyResponse, error) { - policy, err := s.org.GetDefaultLabelPolicy(ctx) + policy, err := s.query.DefaultActiveLabelPolicy(ctx) if err != nil { return nil, err } diff --git a/internal/api/grpc/policy/label_policy.go b/internal/api/grpc/policy/label_policy.go index 9f795717fc..2b8a0ba4fb 100644 --- a/internal/api/grpc/policy/label_policy.go +++ b/internal/api/grpc/policy/label_policy.go @@ -2,34 +2,34 @@ package policy import ( "github.com/caos/zitadel/internal/api/grpc/object" - "github.com/caos/zitadel/internal/iam/model" + "github.com/caos/zitadel/internal/query" policy_pb "github.com/caos/zitadel/pkg/grpc/policy" ) -func ModelLabelPolicyToPb(policy *model.LabelPolicyView) *policy_pb.LabelPolicy { +func ModelLabelPolicyToPb(policy *query.LabelPolicy) *policy_pb.LabelPolicy { return &policy_pb.LabelPolicy{ - IsDefault: policy.Default, - PrimaryColor: policy.PrimaryColor, - BackgroundColor: policy.BackgroundColor, - FontColor: policy.FontColor, - WarnColor: policy.WarnColor, - PrimaryColorDark: policy.PrimaryColorDark, - BackgroundColorDark: policy.BackgroundColorDark, - WarnColorDark: policy.WarnColorDark, - FontColorDark: policy.FontColorDark, + IsDefault: policy.IsDefault, + PrimaryColor: policy.Light.PrimaryColor, + BackgroundColor: policy.Light.BackgroundColor, + FontColor: policy.Light.FontColor, + WarnColor: policy.Light.WarnColor, + PrimaryColorDark: policy.Dark.PrimaryColor, + BackgroundColorDark: policy.Dark.BackgroundColor, + WarnColorDark: policy.Dark.WarnColor, + FontColorDark: policy.Dark.FontColor, FontUrl: policy.FontURL, - LogoUrl: policy.LogoURL, - LogoUrlDark: policy.LogoDarkURL, - IconUrl: policy.IconURL, - IconUrlDark: policy.IconDarkURL, + LogoUrl: policy.Light.LogoURL, + LogoUrlDark: policy.Dark.LogoURL, + IconUrl: policy.Light.IconURL, + IconUrlDark: policy.Dark.IconURL, - DisableWatermark: policy.DisableWatermark, + DisableWatermark: policy.WatermarkDisabled, HideLoginNameSuffix: policy.HideLoginNameSuffix, Details: object.ToViewDetailsPb( policy.Sequence, policy.CreationDate, policy.ChangeDate, - "", //TODO: resourceowner + policy.ResourceOwner, ), } } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 102b970bdc..6cdb9cfe15 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -33,6 +33,7 @@ type AuthRequestRepo struct { View *view.View Eventstore v1.Eventstore + LabelPolicyProvider labelPolicyProvider UserSessionViewProvider userSessionViewProvider UserViewProvider userViewProvider UserCommandProvider userCommandProvider @@ -56,6 +57,10 @@ type AuthRequestRepo struct { IAMID string } +type labelPolicyProvider interface { + ActiveLabelPolicyByOrg(context.Context, string) (*query.LabelPolicy, error) +} + type privacyPolicyProvider interface { PrivacyPolicyByOrg(context.Context, string) (*query.PrivacyPolicy, error) } @@ -931,18 +936,41 @@ func (repo *AuthRequestRepo) getLockoutPolicy(ctx context.Context, orgID string) } func (repo *AuthRequestRepo) getLabelPolicy(ctx context.Context, orgID string) (*domain.LabelPolicy, error) { - policy, err := repo.View.LabelPolicyByAggregateIDAndState(orgID, int32(domain.LabelPolicyStateActive)) - if errors.IsNotFound(err) { - policy, err = repo.View.LabelPolicyByAggregateIDAndState(repo.IAMID, int32(domain.LabelPolicyStateActive)) - if err != nil { - return nil, err - } - policy.Default = true - } + policy, err := repo.LabelPolicyProvider.ActiveLabelPolicyByOrg(ctx, orgID) if err != nil { return nil, err } - return policy.ToDomain(), err + return labelPolicyToDomain(policy), nil +} + +func labelPolicyToDomain(p *query.LabelPolicy) *domain.LabelPolicy { + return &domain.LabelPolicy{ + ObjectRoot: es_models.ObjectRoot{ + AggregateID: p.ID, + Sequence: p.Sequence, + ResourceOwner: p.ResourceOwner, + CreationDate: p.CreationDate, + ChangeDate: p.ChangeDate, + }, + State: p.State, + Default: p.IsDefault, + PrimaryColor: p.Light.PrimaryColor, + BackgroundColor: p.Light.BackgroundColor, + WarnColor: p.Light.WarnColor, + FontColor: p.Light.FontColor, + LogoURL: p.Light.LogoURL, + IconURL: p.Light.IconURL, + PrimaryColorDark: p.Dark.PrimaryColor, + BackgroundColorDark: p.Dark.BackgroundColor, + WarnColorDark: p.Dark.WarnColor, + FontColorDark: p.Dark.FontColor, + LogoDarkURL: p.Dark.LogoURL, + IconDarkURL: p.Dark.IconURL, + Font: p.FontURL, + HideLoginNameSuffix: p.HideLoginNameSuffix, + ErrorMsgPopup: p.ShouldErrorPopup, + DisableWatermark: p.WatermarkDisabled, + } } func (repo *AuthRequestRepo) getLoginTexts(ctx context.Context, aggregateID string) ([]*domain.CustomText, error) { diff --git a/internal/auth/repository/eventsourcing/eventstore/org.go b/internal/auth/repository/eventsourcing/eventstore/org.go index b516603c35..c0b620d9f3 100644 --- a/internal/auth/repository/eventsourcing/eventstore/org.go +++ b/internal/auth/repository/eventsourcing/eventstore/org.go @@ -7,7 +7,6 @@ import ( auth_view "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" "github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/domain" - "github.com/caos/zitadel/internal/errors" eventstore "github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1/models" iam_model "github.com/caos/zitadel/internal/iam/model" @@ -16,10 +15,6 @@ import ( "github.com/caos/zitadel/internal/repository/iam" ) -const ( - orgOwnerRole = "ORG_OWNER" -) - type OrgRepository struct { SearchLimit uint64 @@ -45,17 +40,6 @@ func (repo *OrgRepository) GetMyPasswordComplexityPolicy(ctx context.Context) (* return iam_view_model.PasswordComplexityViewToModel(policy), err } -func (repo *OrgRepository) GetLabelPolicy(ctx context.Context, orgID string) (*domain.LabelPolicy, error) { - orgPolicy, err := repo.View.LabelPolicyByAggregateIDAndState(orgID, int32(domain.LabelPolicyStateActive)) - if errors.IsNotFound(err) { - orgPolicy, err = repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(domain.LabelPolicyStateActive)) - } - if err != nil { - return nil, err - } - return orgPolicy.ToDomain(), nil -} - func (repo *OrgRepository) GetLoginText(ctx context.Context, orgID string) ([]*domain.CustomText, error) { loginTexts, err := repo.View.CustomTextsByAggregateIDAndTemplate(domain.IAMID, domain.LoginCustomText) if err != nil { diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go index ac3cf5a554..409910aaa3 100644 --- a/internal/auth/repository/eventsourcing/handler/handler.go +++ b/internal/auth/repository/eventsourcing/handler/handler.go @@ -58,7 +58,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es newExternalIDP( handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount, es}, systemDefaults), - newLabelPolicy(handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount, es}), newRefreshToken(handler{view, bulkLimit, configs.cycleDuration("RefreshToken"), errorCount, es}), newCustomText(handler{view, bulkLimit, configs.cycleDuration("CustomTexts"), errorCount, es}), newMetadata(handler{view, bulkLimit, configs.cycleDuration("Metadata"), errorCount, es}), diff --git a/internal/auth/repository/eventsourcing/handler/label_policy.go b/internal/auth/repository/eventsourcing/handler/label_policy.go deleted file mode 100644 index a00ac4a9e7..0000000000 --- a/internal/auth/repository/eventsourcing/handler/label_policy.go +++ /dev/null @@ -1,123 +0,0 @@ -package handler - -import ( - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/domain" - v1 "github.com/caos/zitadel/internal/eventstore/v1" - "github.com/caos/zitadel/internal/eventstore/v1/models" - es_models "github.com/caos/zitadel/internal/eventstore/v1/models" - "github.com/caos/zitadel/internal/eventstore/v1/query" - "github.com/caos/zitadel/internal/eventstore/v1/spooler" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" - "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" -) - -const ( - labelPolicyTable = "auth.label_policies" -) - -type LabelPolicy struct { - handler - subscription *v1.Subscription -} - -func newLabelPolicy(handler handler) *LabelPolicy { - h := &LabelPolicy{ - handler: handler, - } - - h.subscribe() - - return h -} - -func (m *LabelPolicy) subscribe() { - m.subscription = m.es.Subscribe(m.AggregateTypes()...) - go func() { - for event := range m.subscription.Events { - query.ReduceEvent(m, event) - } - }() -} - -func (m *LabelPolicy) ViewModel() string { - return labelPolicyTable -} - -func (p *LabelPolicy) Subscription() *v1.Subscription { - return p.subscription -} - -func (_ *LabelPolicy) AggregateTypes() []models.AggregateType { - return []models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} -} - -func (m *LabelPolicy) CurrentSequence() (uint64, error) { - sequence, err := m.view.GetLatestLabelPolicySequence() - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (m *LabelPolicy) EventQuery() (*models.SearchQuery, error) { - sequence, err := m.view.GetLatestLabelPolicySequence() - if err != nil { - return nil, err - } - return es_models.NewSearchQuery(). - AggregateTypeFilter(m.AggregateTypes()...). - LatestSequenceFilter(sequence.CurrentSequence), nil -} - -func (m *LabelPolicy) Reduce(event *models.Event) (err error) { - switch event.AggregateType { - case model.OrgAggregate, iam_es_model.IAMAggregate: - err = m.processLabelPolicy(event) - } - return err -} - -func (m *LabelPolicy) processLabelPolicy(event *models.Event) (err error) { - policy := new(iam_model.LabelPolicyView) - switch event.Type { - case iam_es_model.LabelPolicyAdded, model.LabelPolicyAdded: - err = policy.AppendEvent(event) - case iam_es_model.LabelPolicyChanged, model.LabelPolicyChanged, - iam_es_model.LabelPolicyActivated, model.LabelPolicyActivated, - iam_es_model.LabelPolicyLogoAdded, model.LabelPolicyLogoAdded, - iam_es_model.LabelPolicyLogoRemoved, model.LabelPolicyLogoRemoved, - iam_es_model.LabelPolicyIconAdded, model.LabelPolicyIconAdded, - iam_es_model.LabelPolicyIconRemoved, model.LabelPolicyIconRemoved, - iam_es_model.LabelPolicyLogoDarkAdded, model.LabelPolicyLogoDarkAdded, - iam_es_model.LabelPolicyLogoDarkRemoved, model.LabelPolicyLogoDarkRemoved, - iam_es_model.LabelPolicyIconDarkAdded, model.LabelPolicyIconDarkAdded, - iam_es_model.LabelPolicyIconDarkRemoved, model.LabelPolicyIconDarkRemoved, - iam_es_model.LabelPolicyFontAdded, model.LabelPolicyFontAdded, - iam_es_model.LabelPolicyFontRemoved, model.LabelPolicyFontRemoved: - policy, err = m.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview)) - if err != nil { - return err - } - err = policy.AppendEvent(event) - case model.LabelPolicyRemoved: - return m.view.DeleteLabelPolicy(event.AggregateID, event) - default: - return m.view.ProcessedLabelPolicySequence(event) - } - if err != nil { - return err - } - return m.view.PutLabelPolicy(policy, event) -} - -func (m *LabelPolicy) OnError(event *models.Event, err error) error { - logging.LogWithFields("SPOOL-2ff7s", "id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler") - return spooler.HandleError(event, err, m.view.GetLatestLabelPolicyFailedEvent, m.view.ProcessedLabelPolicyFailedEvent, m.view.ProcessedLabelPolicySequence, m.errorCountUntilSkip) -} - -func (m *LabelPolicy) OnSuccess() error { - return spooler.HandleSuccess(m.view.UpdateLabelPolicySpoolerRunTimestamp) -} diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go index c44fc8d276..acd368b308 100644 --- a/internal/auth/repository/eventsourcing/repository.go +++ b/internal/auth/repository/eventsourcing/repository.go @@ -98,6 +98,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co userRepo, eventstore.AuthRequestRepo{ PrivacyPolicyProvider: queries, + LabelPolicyProvider: queries, Command: command, OrgViewProvider: queries, AuthRequests: authReq, diff --git a/internal/auth/repository/eventsourcing/view/label_policies.go b/internal/auth/repository/eventsourcing/view/label_policies.go deleted file mode 100644 index 40839c303a..0000000000 --- a/internal/auth/repository/eventsourcing/view/label_policies.go +++ /dev/null @@ -1,53 +0,0 @@ -package view - -import ( - "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/v1/models" - "github.com/caos/zitadel/internal/iam/repository/view" - "github.com/caos/zitadel/internal/iam/repository/view/model" - global_view "github.com/caos/zitadel/internal/view/repository" -) - -const ( - labelPolicyTable = "auth.label_policies" -) - -func (v *View) LabelPolicyByAggregateIDAndState(aggregateID string, state int32) (*model.LabelPolicyView, error) { - return view.GetLabelPolicyByAggregateIDAndState(v.Db, labelPolicyTable, aggregateID, state) -} - -func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error { - err := view.PutLabelPolicy(v.Db, labelPolicyTable, policy) - if err != nil { - return err - } - return v.ProcessedLabelPolicySequence(event) -} - -func (v *View) DeleteLabelPolicy(aggregateID string, event *models.Event) error { - err := view.DeleteLabelPolicy(v.Db, labelPolicyTable, aggregateID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedLabelPolicySequence(event) -} - -func (v *View) GetLatestLabelPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(labelPolicyTable) -} - -func (v *View) ProcessedLabelPolicySequence(event *models.Event) error { - return v.saveCurrentSequence(labelPolicyTable, event) -} - -func (v *View) UpdateLabelPolicySpoolerRunTimestamp() error { - return v.updateSpoolerRunSequence(labelPolicyTable) -} - -func (v *View) GetLatestLabelPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) { - return v.latestFailedEvent(labelPolicyTable, sequence) -} - -func (v *View) ProcessedLabelPolicyFailedEvent(failedEvent *global_view.FailedEvent) error { - return v.saveFailedEvent(failedEvent) -} diff --git a/internal/auth/repository/org.go b/internal/auth/repository/org.go index 4d8ff74a21..f5daba2bfa 100644 --- a/internal/auth/repository/org.go +++ b/internal/auth/repository/org.go @@ -10,6 +10,5 @@ import ( type OrgRepository interface { GetIDPConfigByID(ctx context.Context, idpConfigID string) (*iam_model.IDPConfigView, error) GetMyPasswordComplexityPolicy(ctx context.Context) (*iam_model.PasswordComplexityPolicyView, error) - GetLabelPolicy(ctx context.Context, orgID string) (*domain.LabelPolicy, error) GetLoginText(ctx context.Context, orgID string) ([]*domain.CustomText, error) } diff --git a/internal/eventstore/handler/crdb/statement.go b/internal/eventstore/handler/crdb/statement.go index e0a06a776b..08dccd2d52 100644 --- a/internal/eventstore/handler/crdb/statement.go +++ b/internal/eventstore/handler/crdb/statement.go @@ -201,6 +201,66 @@ func NewArrayRemoveCol(column string, value interface{}) handler.Column { } } +//NewCopyStatement creates a new upsert statement which updates a column from an existing row +// cols represent the columns which are objective to change. +// if the value of a col is empty the data will be copied from the selected row +// if the value of a col is not empty the data will be set by the static value +// conds represent the conditions for the selection subquery +func NewCopyStatement(event eventstore.EventReader, cols []handler.Column, conds []handler.Condition, opts ...execOption) *handler.Statement { + columnNames := make([]string, len(cols)) + selectColumns := make([]string, len(cols)) + argCounter := 0 + args := []interface{}{} + + for i, col := range cols { + columnNames[i] = col.Name + selectColumns[i] = col.Name + if col.Value != nil { + argCounter++ + selectColumns[i] = "$" + strconv.Itoa(argCounter) + args = append(args, col.Value) + } + } + + wheres := make([]string, len(conds)) + for i, cond := range conds { + argCounter++ + wheres[i] = "copy_table." + cond.Name + " = $" + strconv.Itoa(argCounter) + args = append(args, cond.Value) + } + + config := execConfig{ + args: args, + } + + if len(cols) == 0 { + config.err = handler.ErrNoValues + } + + if len(conds) == 0 { + config.err = handler.ErrNoCondition + } + + q := func(config execConfig) string { + return "UPSERT INTO " + + config.tableName + + " (" + + strings.Join(columnNames, ", ") + + ") SELECT " + + strings.Join(selectColumns, ", ") + + " FROM " + + config.tableName + " AS copy_table WHERE " + + strings.Join(wheres, " AND ") + } + + return &handler.Statement{ + AggregateType: event.Aggregate().Type, + Sequence: event.Sequence(), + PreviousSequence: event.PreviousAggregateTypeSequence(), + Execute: exec(config, q, opts), + } +} + func columnsToQuery(cols []handler.Column) (names []string, parameters []string, values []interface{}) { names = make([]string, len(cols)) values = make([]interface{}, len(cols)) diff --git a/internal/eventstore/handler/crdb/statement_test.go b/internal/eventstore/handler/crdb/statement_test.go index 57b6e07db9..bac7ac951a 100644 --- a/internal/eventstore/handler/crdb/statement_test.go +++ b/internal/eventstore/handler/crdb/statement_test.go @@ -795,6 +795,182 @@ func TestNewMultiStatement(t *testing.T) { } } +func TestNewCopyStatement(t *testing.T) { + type args struct { + table string + event *testEvent + cols []handler.Column + conds []handler.Condition + } + type want struct { + aggregateType eventstore.AggregateType + sequence uint64 + previousSequence uint64 + table string + executer *wantExecuter + isErr func(error) bool + } + tests := []struct { + name string + args args + want want + }{ + { + name: "no table", + args: args{ + table: "", + event: &testEvent{ + aggregateType: "agg", + sequence: 1, + previousSequence: 0, + }, + conds: []handler.Condition{ + { + Name: "col2", + Value: 1, + }, + }, + }, + want: want{ + table: "", + aggregateType: "agg", + sequence: 1, + previousSequence: 0, + executer: &wantExecuter{ + shouldExecute: false, + }, + isErr: func(err error) bool { + return errors.Is(err, handler.ErrNoProjection) + }, + }, + }, + { + name: "no conditions", + args: args{ + table: "my_table", + event: &testEvent{ + aggregateType: "agg", + sequence: 1, + previousSequence: 0, + }, + conds: []handler.Condition{}, + cols: []handler.Column{ + { + Name: "col", + }, + }, + }, + want: want{ + table: "my_table", + aggregateType: "agg", + sequence: 1, + previousSequence: 1, + executer: &wantExecuter{ + shouldExecute: false, + }, + isErr: func(err error) bool { + return errors.Is(err, handler.ErrNoCondition) + }, + }, + }, + { + name: "no values", + args: args{ + table: "my_table", + event: &testEvent{ + aggregateType: "agg", + sequence: 1, + previousSequence: 0, + }, + conds: []handler.Condition{ + { + Name: "col", + }, + }, + cols: []handler.Column{}, + }, + want: want{ + table: "my_table", + aggregateType: "agg", + sequence: 1, + previousSequence: 1, + executer: &wantExecuter{ + shouldExecute: false, + }, + isErr: func(err error) bool { + return errors.Is(err, handler.ErrNoValues) + }, + }, + }, + { + name: "correct", + args: args{ + table: "my_table", + event: &testEvent{ + aggregateType: "agg", + sequence: 1, + previousSequence: 0, + }, + cols: []handler.Column{ + { + Name: "state", + Value: 1, + }, + { + Name: "id", + }, + { + Name: "col_a", + }, + { + Name: "col_b", + }, + }, + conds: []handler.Condition{ + { + Name: "id", + Value: 2, + }, + { + Name: "state", + Value: 3, + }, + }, + }, + want: want{ + table: "my_table", + aggregateType: "agg", + sequence: 1, + previousSequence: 1, + executer: &wantExecuter{ + params: []params{ + { + query: "UPSERT INTO my_table (state, id, col_a, col_b) SELECT $1, id, col_a, col_b FROM my_table AS copy_table WHERE copy_table.id = $2 AND copy_table.state = $3", + args: []interface{}{1, 2, 3}, + }, + }, + shouldExecute: true, + }, + isErr: func(err error) bool { + return err == nil + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.want.executer.t = t + stmt := NewCopyStatement(tt.args.event, tt.args.cols, tt.args.conds) + + err := stmt.Execute(tt.want.executer, tt.args.table) + if !tt.want.isErr(err) { + t.Errorf("unexpected error: %v", err) + } + tt.want.executer.check(t) + }) + } +} + func TestStatement_Execute(t *testing.T) { type fields struct { execute func(ex handler.Executer, projectionName string) error diff --git a/internal/iam/repository/view/label_policy_view.go b/internal/iam/repository/view/styling.go similarity index 79% rename from internal/iam/repository/view/label_policy_view.go rename to internal/iam/repository/view/styling.go index a4731d0511..b685733b3f 100644 --- a/internal/iam/repository/view/label_policy_view.go +++ b/internal/iam/repository/view/styling.go @@ -10,7 +10,7 @@ import ( "github.com/jinzhu/gorm" ) -func GetLabelPolicyByAggregateIDAndState(db *gorm.DB, table, aggregateID string, state int32) (*model.LabelPolicyView, error) { +func GetStylingByAggregateIDAndState(db *gorm.DB, table, aggregateID string, state int32) (*model.LabelPolicyView, error) { policy := new(model.LabelPolicyView) aggregateIDQuery := &model.LabelPolicySearchQuery{Key: iam_model.LabelPolicySearchKeyAggregateID, Value: aggregateID, Method: domain.SearchMethodEquals} stateQuery := &model.LabelPolicySearchQuery{Key: iam_model.LabelPolicySearchKeyState, Value: state, Method: domain.SearchMethodEquals} @@ -22,12 +22,12 @@ func GetLabelPolicyByAggregateIDAndState(db *gorm.DB, table, aggregateID string, return policy, err } -func PutLabelPolicy(db *gorm.DB, table string, policy *model.LabelPolicyView) error { +func PutStyling(db *gorm.DB, table string, policy *model.LabelPolicyView) error { save := repository.PrepareSave(table) return save(db, policy) } -func DeleteLabelPolicy(db *gorm.DB, table, aggregateID string) error { +func DeleteStyling(db *gorm.DB, table, aggregateID string) error { delete := repository.PrepareDeleteByKey(table, model.LabelPolicySearchKey(iam_model.LabelPolicySearchKeyAggregateID), aggregateID) return delete(db) diff --git a/internal/management/repository/eventsourcing/eventstore/org.go b/internal/management/repository/eventsourcing/eventstore/org.go index 4c1f063209..1f75f7e84f 100644 --- a/internal/management/repository/eventsourcing/eventstore/org.go +++ b/internal/management/repository/eventsourcing/eventstore/org.go @@ -165,70 +165,6 @@ func (repo *OrgRepository) SearchIDPConfigs(ctx context.Context, request *iam_mo return result, nil } -func (repo *OrgRepository) GetLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { - policy, err := repo.View.LabelPolicyByAggregateIDAndState(authz.GetCtxData(ctx).OrgID, int32(domain.LabelPolicyStateActive)) - if errors.IsNotFound(err) { - policy, err = repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(domain.LabelPolicyStateActive)) - if err != nil { - return nil, err - } - policy.Default = true - } - if err != nil { - return nil, err - } - return iam_view_model.LabelPolicyViewToModel(policy), err -} - -func (repo *OrgRepository) GetPreviewLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { - policy, err := repo.View.LabelPolicyByAggregateIDAndState(authz.GetCtxData(ctx).OrgID, int32(domain.LabelPolicyStatePreview)) - if errors.IsNotFound(err) { - policy, err = repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(domain.LabelPolicyStatePreview)) - if err != nil { - return nil, err - } - policy.Default = true - } - if err != nil { - return nil, err - } - return iam_view_model.LabelPolicyViewToModel(policy), err -} - -func (repo *OrgRepository) GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { - return repo.getDefaultLabelPolicy(ctx, domain.LabelPolicyStateActive) -} - -func (repo *OrgRepository) GetPreviewDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { - return repo.getDefaultLabelPolicy(ctx, domain.LabelPolicyStatePreview) -} - -func (repo *OrgRepository) getDefaultLabelPolicy(ctx context.Context, state domain.LabelPolicyState) (*iam_model.LabelPolicyView, error) { - policy, viewErr := repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(state)) - if viewErr != nil && !errors.IsNotFound(viewErr) { - return nil, viewErr - } - if errors.IsNotFound(viewErr) { - policy = new(iam_view_model.LabelPolicyView) - } - events, esErr := repo.getIAMEvents(ctx, policy.Sequence) - if errors.IsNotFound(viewErr) && len(events) == 0 { - return nil, errors.ThrowNotFound(nil, "EVENT-3Nf8sd", "Errors.IAM.LabelPolicy.NotFound") - } - if esErr != nil { - logging.Log("EVENT-28uLp").WithError(esErr).Debug("error retrieving new events") - return iam_view_model.LabelPolicyViewToModel(policy), nil - } - policyCopy := *policy - for _, event := range events { - if err := policyCopy.AppendEvent(event); err != nil { - return iam_view_model.LabelPolicyViewToModel(policy), nil - } - } - policy.Default = true - return iam_view_model.LabelPolicyViewToModel(policy), nil -} - func (repo *OrgRepository) GetIDPProvidersByIDPConfigID(ctx context.Context, aggregateID, idpConfigID string) ([]*iam_model.IDPProviderView, error) { idpProviders, err := repo.View.IDPProvidersByIdpConfigID(aggregateID, idpConfigID) if err != nil { diff --git a/internal/management/repository/eventsourcing/handler/handler.go b/internal/management/repository/eventsourcing/handler/handler.go index fa2dd1f7b5..22f103b70c 100644 --- a/internal/management/repository/eventsourcing/handler/handler.go +++ b/internal/management/repository/eventsourcing/handler/handler.go @@ -47,9 +47,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es handler{view, bulkLimit, configs.cycleDuration("MachineKeys"), errorCount, es}), newIDPConfig( handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), - newLabelPolicy( - handler{view, bulkLimit, configs.cycleDuration("LabelPolicy"), errorCount, es}, - staticStorage), newIDPProvider( handler{view, bulkLimit, configs.cycleDuration("IDPProvider"), errorCount, es}, defaults), diff --git a/internal/management/repository/eventsourcing/handler/label_policy.go b/internal/management/repository/eventsourcing/handler/label_policy.go deleted file mode 100644 index d7b0fb3da7..0000000000 --- a/internal/management/repository/eventsourcing/handler/label_policy.go +++ /dev/null @@ -1,160 +0,0 @@ -package handler - -import ( - "context" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/domain" - "github.com/caos/zitadel/internal/eventstore/v1" - es_models "github.com/caos/zitadel/internal/eventstore/v1/models" - "github.com/caos/zitadel/internal/eventstore/v1/query" - "github.com/caos/zitadel/internal/eventstore/v1/spooler" - iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" - "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" - "github.com/caos/zitadel/internal/static" -) - -const ( - labelPolicyTable = "management.label_policies" -) - -type LabelPolicy struct { - handler - subscription *v1.Subscription - static static.Storage -} - -func newLabelPolicy(handler handler, static static.Storage) *LabelPolicy { - h := &LabelPolicy{ - handler: handler, - static: static, - } - - h.subscribe() - - return h -} - -func (m *LabelPolicy) subscribe() { - m.subscription = m.es.Subscribe(m.AggregateTypes()...) - go func() { - for event := range m.subscription.Events { - query.ReduceEvent(m, event) - } - }() -} - -func (m *LabelPolicy) ViewModel() string { - return labelPolicyTable -} - -func (p *LabelPolicy) Subscription() *v1.Subscription { - return p.subscription -} - -func (_ *LabelPolicy) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate} -} - -func (m *LabelPolicy) CurrentSequence() (uint64, error) { - sequence, err := m.view.GetLatestLabelPolicySequence() - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (m *LabelPolicy) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := m.view.GetLatestLabelPolicySequence() - if err != nil { - return nil, err - } - return es_models.NewSearchQuery(). - AggregateTypeFilter(m.AggregateTypes()...). - LatestSequenceFilter(sequence.CurrentSequence), nil -} - -func (m *LabelPolicy) Reduce(event *es_models.Event) (err error) { - switch event.AggregateType { - case model.OrgAggregate, iam_es_model.IAMAggregate: - err = m.processLabelPolicy(event) - } - return err -} - -func (m *LabelPolicy) processLabelPolicy(event *es_models.Event) (err error) { - policy := new(iam_model.LabelPolicyView) - switch event.Type { - case iam_es_model.LabelPolicyAdded, model.LabelPolicyAdded: - err = policy.AppendEvent(event) - case iam_es_model.LabelPolicyChanged, model.LabelPolicyChanged, - iam_es_model.LabelPolicyLogoAdded, model.LabelPolicyLogoAdded, - iam_es_model.LabelPolicyLogoRemoved, model.LabelPolicyLogoRemoved, - iam_es_model.LabelPolicyIconAdded, model.LabelPolicyIconAdded, - iam_es_model.LabelPolicyIconRemoved, model.LabelPolicyIconRemoved, - iam_es_model.LabelPolicyLogoDarkAdded, model.LabelPolicyLogoDarkAdded, - iam_es_model.LabelPolicyLogoDarkRemoved, model.LabelPolicyLogoDarkRemoved, - iam_es_model.LabelPolicyIconDarkAdded, model.LabelPolicyIconDarkAdded, - iam_es_model.LabelPolicyIconDarkRemoved, model.LabelPolicyIconDarkRemoved, - iam_es_model.LabelPolicyFontAdded, model.LabelPolicyFontAdded, - iam_es_model.LabelPolicyFontRemoved, model.LabelPolicyFontRemoved: - policy, err = m.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview)) - if err != nil { - return err - } - err = policy.AppendEvent(event) - case model.LabelPolicyRemoved: - return m.view.DeleteLabelPolicy(event.AggregateID, event) - case iam_es_model.LabelPolicyActivated, model.LabelPolicyActivated: - policy, err = m.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview)) - if err != nil { - return err - } - go m.CleanUpBucket(policy) - err = policy.AppendEvent(event) - default: - return m.view.ProcessedLabelPolicySequence(event) - } - if err != nil { - return err - } - return m.view.PutLabelPolicy(policy, event) -} - -func (m *LabelPolicy) OnError(event *es_models.Event, err error) error { - logging.LogWithFields("SPOOL-66Cs8", "id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler") - return spooler.HandleError(event, err, m.view.GetLatestLabelPolicyFailedEvent, m.view.ProcessedLabelPolicyFailedEvent, m.view.ProcessedLabelPolicySequence, m.errorCountUntilSkip) -} - -func (m *LabelPolicy) OnSuccess() error { - return spooler.HandleSuccess(m.view.UpdateLabelPolicySpoolerRunTimestamp) -} - -func (p *LabelPolicy) CleanUpBucket(policy *iam_model.LabelPolicyView) { - ctx := context.Background() - objects, err := p.static.ListObjectInfos(ctx, policy.AggregateID, domain.LabelPolicyPrefix+"/", false) - if err != nil { - return - } - for _, object := range objects { - if !deletableObject(object, policy) { - continue - } - err = p.static.RemoveObject(ctx, policy.AggregateID, object.Key) - logging.LogWithFields("SPOOL-ASd3g", "aggregate", policy.AggregateID, "key", object.Key).OnError(err).Warn("could not delete asset") - } -} - -func deletableObject(object *domain.AssetInfo, policy *iam_model.LabelPolicyView) bool { - if object.Key == policy.LogoURL || - object.Key == policy.LogoDarkURL || - object.Key == policy.IconURL || - object.Key == policy.IconDarkURL || - object.Key == policy.FontURL || - object.Key == domain.LabelPolicyPrefix+"/css/" { - return false - } - return true -} diff --git a/internal/management/repository/eventsourcing/view/label_policies.go b/internal/management/repository/eventsourcing/view/label_policies.go deleted file mode 100644 index f85857cc8d..0000000000 --- a/internal/management/repository/eventsourcing/view/label_policies.go +++ /dev/null @@ -1,53 +0,0 @@ -package view - -import ( - "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/v1/models" - "github.com/caos/zitadel/internal/iam/repository/view" - "github.com/caos/zitadel/internal/iam/repository/view/model" - global_view "github.com/caos/zitadel/internal/view/repository" -) - -const ( - labelPolicyTable = "management.label_policies" -) - -func (v *View) LabelPolicyByAggregateIDAndState(aggregateID string, state int32) (*model.LabelPolicyView, error) { - return view.GetLabelPolicyByAggregateIDAndState(v.Db, labelPolicyTable, aggregateID, state) -} - -func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error { - err := view.PutLabelPolicy(v.Db, labelPolicyTable, policy) - if err != nil { - return err - } - return v.ProcessedLabelPolicySequence(event) -} - -func (v *View) DeleteLabelPolicy(aggregateID string, event *models.Event) error { - err := view.DeleteLabelPolicy(v.Db, labelPolicyTable, aggregateID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedLabelPolicySequence(event) -} - -func (v *View) GetLatestLabelPolicySequence() (*global_view.CurrentSequence, error) { - return v.latestSequence(labelPolicyTable) -} - -func (v *View) ProcessedLabelPolicySequence(event *models.Event) error { - return v.saveCurrentSequence(labelPolicyTable, event) -} - -func (v *View) UpdateLabelPolicySpoolerRunTimestamp() error { - return v.updateSpoolerRunSequence(labelPolicyTable) -} - -func (v *View) GetLatestLabelPolicyFailedEvent(sequence uint64) (*global_view.FailedEvent, error) { - return v.latestFailedEvent(labelPolicyTable, sequence) -} - -func (v *View) ProcessedLabelPolicyFailedEvent(failedEvent *global_view.FailedEvent) error { - return v.saveFailedEvent(failedEvent) -} diff --git a/internal/management/repository/org.go b/internal/management/repository/org.go index 75a35fddb8..ce564db525 100644 --- a/internal/management/repository/org.go +++ b/internal/management/repository/org.go @@ -34,9 +34,4 @@ type OrgRepository interface { GetDefaultLoginTexts(ctx context.Context, lang string) (*domain.CustomLoginText, error) GetLoginTexts(ctx context.Context, orgID, lang string) (*domain.CustomLoginText, error) - - GetLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) - GetPreviewLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) - GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) - GetPreviewDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) } diff --git a/internal/notification/repository/eventsourcing/handler/notification.go b/internal/notification/repository/eventsourcing/handler/notification.go index 130d21d596..35304d9013 100644 --- a/internal/notification/repository/eventsourcing/handler/notification.go +++ b/internal/notification/repository/eventsourcing/handler/notification.go @@ -15,7 +15,7 @@ import ( "github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/v1" + v1 "github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/query" "github.com/caos/zitadel/internal/eventstore/v1/spooler" @@ -408,10 +408,10 @@ func getSetNotifyContextData(orgID string) context.Context { // Read organization specific colors func (n *Notification) getLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { // read from Org - policy, err := n.view.LabelPolicyByAggregateIDAndState(authz.GetCtxData(ctx).OrgID, labelPolicyTableOrg, int32(domain.LabelPolicyStateActive)) + policy, err := n.view.StylingByAggregateIDAndState(authz.GetCtxData(ctx).OrgID, labelPolicyTableOrg, int32(domain.LabelPolicyStateActive)) if errors.IsNotFound(err) { // read from default - policy, err = n.view.LabelPolicyByAggregateIDAndState(n.systemDefaults.IamID, labelPolicyTableDef, int32(domain.LabelPolicyStateActive)) + policy, err = n.view.StylingByAggregateIDAndState(n.systemDefaults.IamID, labelPolicyTableDef, int32(domain.LabelPolicyStateActive)) if err != nil { return nil, err } diff --git a/internal/notification/repository/eventsourcing/view/label_policies.go b/internal/notification/repository/eventsourcing/view/label_policies.go index a43d0ecb78..96f631a985 100644 --- a/internal/notification/repository/eventsourcing/view/label_policies.go +++ b/internal/notification/repository/eventsourcing/view/label_policies.go @@ -5,6 +5,6 @@ import ( "github.com/caos/zitadel/internal/iam/repository/view/model" ) -func (v *View) LabelPolicyByAggregateIDAndState(aggregateID, labelPolicyTableVar string, state int32) (*model.LabelPolicyView, error) { - return view.GetLabelPolicyByAggregateIDAndState(v.Db, labelPolicyTableVar, aggregateID, state) +func (v *View) StylingByAggregateIDAndState(aggregateID, labelPolicyTableVar string, state int32) (*model.LabelPolicyView, error) { + return view.GetStylingByAggregateIDAndState(v.Db, labelPolicyTableVar, aggregateID, state) } diff --git a/internal/query/idp.go b/internal/query/idp.go index 83a16cb492..e7f99333bc 100644 --- a/internal/query/idp.go +++ b/internal/query/idp.go @@ -4,7 +4,6 @@ import ( "context" "database/sql" errs "errors" - "log" "time" sq "github.com/Masterminds/squirrel" @@ -216,7 +215,6 @@ func (q *Queries) SearchIDPs(ctx context.Context, resourceOwner string, queries rows, err := q.client.QueryContext(ctx, stmt, args...) if err != nil { - log.Println(err) return nil, errors.ThrowInternal(err, "QUERY-YTug9", "Errors.Internal") } idps, err = scan(rows) diff --git a/internal/query/label_policy.go b/internal/query/label_policy.go new file mode 100644 index 0000000000..9406e4d109 --- /dev/null +++ b/internal/query/label_policy.go @@ -0,0 +1,300 @@ +package query + +import ( + "context" + "database/sql" + errs "errors" + "time" + + sq "github.com/Masterminds/squirrel" + "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/query/projection" +) + +type LabelPolicy struct { + ID string + CreationDate time.Time + ChangeDate time.Time + Sequence uint64 + State domain.LabelPolicyState + IsDefault bool + ResourceOwner string + HideLoginNameSuffix bool + FontURL string + WatermarkDisabled bool + ShouldErrorPopup bool + + Dark Theme + Light Theme +} + +type Theme struct { + PrimaryColor string + WarnColor string + BackgroundColor string + FontColor string + LogoURL string + IconURL string +} + +func (q *Queries) ActiveLabelPolicyByOrg(ctx context.Context, orgID string) (*LabelPolicy, error) { + stmt, scan := prepareLabelPolicyQuery() + query, args, err := stmt.Where( + sq.And{ + sq.Or{ + sq.Eq{ + LabelPolicyColID.identifier(): orgID, + }, + sq.Eq{ + LabelPolicyColID.identifier(): q.iamID, + }, + }, + sq.Eq{ + LabelPolicyColState.identifier(): domain.LabelPolicyStateActive, + }, + }). + OrderBy(LabelPolicyColIsDefault.identifier()). + Limit(1).ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-V22un", "unable to create sql stmt") + } + + row := q.client.QueryRowContext(ctx, query, args...) + return scan(row) +} + +func (q *Queries) PreviewLabelPolicyByOrg(ctx context.Context, orgID string) (*LabelPolicy, error) { + stmt, scan := prepareLabelPolicyQuery() + query, args, err := stmt.Where( + sq.And{ + sq.Or{ + sq.Eq{ + LabelPolicyColID.identifier(): orgID, + }, + sq.Eq{ + LabelPolicyColID.identifier(): q.iamID, + }, + }, + sq.Eq{ + LabelPolicyColState.identifier(): domain.LabelPolicyStatePreview, + }, + }). + OrderBy(LabelPolicyColIsDefault.identifier()). + Limit(1).ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-AG5eq", "unable to create sql stmt") + } + + row := q.client.QueryRowContext(ctx, query, args...) + return scan(row) +} + +func (q *Queries) DefaultActiveLabelPolicy(ctx context.Context) (*LabelPolicy, error) { + stmt, scan := prepareLabelPolicyQuery() + query, args, err := stmt.Where(sq.Eq{ + LabelPolicyColID.identifier(): q.iamID, + LabelPolicyColState.identifier(): domain.LabelPolicyStateActive, + }). + OrderBy(LabelPolicyColIsDefault.identifier()). + Limit(1).ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-mN0Ci", "unable to create sql stmt") + } + + row := q.client.QueryRowContext(ctx, query, args...) + return scan(row) +} + +func (q *Queries) DefaultPreviewLabelPolicy(ctx context.Context) (*LabelPolicy, error) { + stmt, scan := prepareLabelPolicyQuery() + query, args, err := stmt.Where(sq.Eq{ + LabelPolicyColID.identifier(): q.iamID, + LabelPolicyColState.identifier(): domain.LabelPolicyStatePreview, + }). + OrderBy(LabelPolicyColIsDefault.identifier()). + Limit(1).ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-B3JQR", "unable to create sql stmt") + } + + row := q.client.QueryRowContext(ctx, query, args...) + return scan(row) +} + +var ( + labelPolicyTable = table{ + name: projection.LabelPolicyTable, + } + LabelPolicyColCreationDate = Column{ + name: projection.LabelPolicyCreationDateCol, + } + LabelPolicyColChangeDate = Column{ + name: projection.LabelPolicyChangeDateCol, + } + LabelPolicyColSequence = Column{ + name: projection.LabelPolicySequenceCol, + } + LabelPolicyColID = Column{ + name: projection.LabelPolicyIDCol, + } + LabelPolicyColState = Column{ + name: projection.LabelPolicyStateCol, + } + LabelPolicyColIsDefault = Column{ + name: projection.LabelPolicyIsDefaultCol, + } + LabelPolicyColResourceOwner = Column{ + name: projection.LabelPolicyResourceOwnerCol, + } + LabelPolicyColHideLoginNameSuffix = Column{ + name: projection.LabelPolicyHideLoginNameSuffixCol, + } + LabelPolicyColFontURL = Column{ + name: projection.LabelPolicyFontURLCol, + } + LabelPolicyColWatermarkDisabled = Column{ + name: projection.LabelPolicyWatermarkDisabledCol, + } + LabelPolicyColShouldErrorPopup = Column{ + name: projection.LabelPolicyShouldErrorPopupCol, + } + LabelPolicyColLightPrimaryColor = Column{ + name: projection.LabelPolicyLightPrimaryColorCol, + } + LabelPolicyColLightWarnColor = Column{ + name: projection.LabelPolicyLightWarnColorCol, + } + LabelPolicyColLightBackgroundColor = Column{ + name: projection.LabelPolicyLightBackgroundColorCol, + } + LabelPolicyColLightFontColor = Column{ + name: projection.LabelPolicyLightFontColorCol, + } + LabelPolicyColLightLogoURL = Column{ + name: projection.LabelPolicyLightLogoURLCol, + } + LabelPolicyColLightIconURL = Column{ + name: projection.LabelPolicyLightIconURLCol, + } + LabelPolicyColDarkPrimaryColor = Column{ + name: projection.LabelPolicyDarkPrimaryColorCol, + } + LabelPolicyColDarkWarnColor = Column{ + name: projection.LabelPolicyDarkWarnColorCol, + } + LabelPolicyColDarkBackgroundColor = Column{ + name: projection.LabelPolicyDarkBackgroundColorCol, + } + LabelPolicyColDarkFontColor = Column{ + name: projection.LabelPolicyDarkFontColorCol, + } + LabelPolicyColDarkLogoURL = Column{ + name: projection.LabelPolicyDarkLogoURLCol, + } + LabelPolicyColDarkIconURL = Column{ + name: projection.LabelPolicyDarkIconURLCol, + } +) + +func prepareLabelPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*LabelPolicy, error)) { + return sq.Select( + LabelPolicyColCreationDate.identifier(), + LabelPolicyColChangeDate.identifier(), + LabelPolicyColSequence.identifier(), + LabelPolicyColID.identifier(), + LabelPolicyColState.identifier(), + LabelPolicyColIsDefault.identifier(), + LabelPolicyColResourceOwner.identifier(), + + LabelPolicyColHideLoginNameSuffix.identifier(), + LabelPolicyColFontURL.identifier(), + LabelPolicyColWatermarkDisabled.identifier(), + LabelPolicyColShouldErrorPopup.identifier(), + + LabelPolicyColLightPrimaryColor.identifier(), + LabelPolicyColLightWarnColor.identifier(), + LabelPolicyColLightBackgroundColor.identifier(), + LabelPolicyColLightFontColor.identifier(), + LabelPolicyColLightLogoURL.identifier(), + LabelPolicyColLightIconURL.identifier(), + + LabelPolicyColDarkPrimaryColor.identifier(), + LabelPolicyColDarkWarnColor.identifier(), + LabelPolicyColDarkBackgroundColor.identifier(), + LabelPolicyColDarkFontColor.identifier(), + LabelPolicyColDarkLogoURL.identifier(), + LabelPolicyColDarkIconURL.identifier(), + ). + From(labelPolicyTable.identifier()).PlaceholderFormat(sq.Dollar), + func(row *sql.Row) (*LabelPolicy, error) { + policy := new(LabelPolicy) + + var ( + fontURL = sql.NullString{} + lightPrimaryColor = sql.NullString{} + lightWarnColor = sql.NullString{} + lightBackgroundColor = sql.NullString{} + lightFontColor = sql.NullString{} + lightLogoURL = sql.NullString{} + lightIconURL = sql.NullString{} + darkPrimaryColor = sql.NullString{} + darkWarnColor = sql.NullString{} + darkBackgroundColor = sql.NullString{} + darkFontColor = sql.NullString{} + darkLogoURL = sql.NullString{} + darkIconURL = sql.NullString{} + ) + + err := row.Scan( + &policy.CreationDate, + &policy.ChangeDate, + &policy.Sequence, + &policy.ID, + &policy.State, + &policy.IsDefault, + &policy.ResourceOwner, + + &policy.HideLoginNameSuffix, + &fontURL, + &policy.WatermarkDisabled, + &policy.ShouldErrorPopup, + + &lightPrimaryColor, + &lightWarnColor, + &lightBackgroundColor, + &lightFontColor, + &lightLogoURL, + &lightIconURL, + + &darkPrimaryColor, + &darkWarnColor, + &darkBackgroundColor, + &darkFontColor, + &darkLogoURL, + &darkIconURL, + ) + if err != nil { + if errs.Is(err, sql.ErrNoRows) { + return nil, errors.ThrowNotFound(err, "QUERY-bJEsm", "errors.policy.label.not_found") + } + return nil, errors.ThrowInternal(err, "QUERY-awLM6", "errors.internal") + } + + policy.FontURL = fontURL.String + policy.Light.PrimaryColor = lightPrimaryColor.String + policy.Light.WarnColor = lightWarnColor.String + policy.Light.BackgroundColor = lightBackgroundColor.String + policy.Light.FontColor = lightFontColor.String + policy.Light.LogoURL = lightLogoURL.String + policy.Light.IconURL = lightIconURL.String + policy.Dark.PrimaryColor = darkPrimaryColor.String + policy.Dark.WarnColor = darkWarnColor.String + policy.Dark.BackgroundColor = darkBackgroundColor.String + policy.Dark.FontColor = darkFontColor.String + policy.Dark.LogoURL = darkLogoURL.String + policy.Dark.IconURL = darkIconURL.String + + return policy, nil + } +} diff --git a/internal/query/projection/label_policy.go b/internal/query/projection/label_policy.go new file mode 100644 index 0000000000..5e2223b8db --- /dev/null +++ b/internal/query/projection/label_policy.go @@ -0,0 +1,531 @@ +package projection + +import ( + "context" + + "github.com/caos/logging" + "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/handler" + "github.com/caos/zitadel/internal/eventstore/handler/crdb" + "github.com/caos/zitadel/internal/repository/iam" + "github.com/caos/zitadel/internal/repository/org" + "github.com/caos/zitadel/internal/repository/policy" +) + +type LabelPolicyProjection struct { + crdb.StatementHandler +} + +const ( + LabelPolicyTable = "zitadel.projections.label_policies" +) + +func NewLabelPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *LabelPolicyProjection { + p := &LabelPolicyProjection{} + config.ProjectionName = LabelPolicyTable + config.Reducers = p.reducers() + p.StatementHandler = crdb.NewStatementHandler(ctx, config) + return p +} + +func (p *LabelPolicyProjection) reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: org.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: org.LabelPolicyAddedEventType, + Reduce: p.reduceAdded, + }, + { + Event: org.LabelPolicyChangedEventType, + Reduce: p.reduceChanged, + }, + { + Event: org.LabelPolicyRemovedEventType, + Reduce: p.reduceRemoved, + }, + { + Event: org.LabelPolicyActivatedEventType, + Reduce: p.reduceActivated, + }, + { + Event: org.LabelPolicyLogoAddedEventType, + Reduce: p.reduceLogoAdded, + }, + { + Event: org.LabelPolicyLogoRemovedEventType, + Reduce: p.reduceLogoRemoved, + }, + { + Event: org.LabelPolicyIconAddedEventType, + Reduce: p.reduceIconAdded, + }, + { + Event: org.LabelPolicyIconRemovedEventType, + Reduce: p.reduceIconRemoved, + }, + { + Event: org.LabelPolicyLogoDarkAddedEventType, + Reduce: p.reduceLogoAdded, + }, + { + Event: org.LabelPolicyLogoDarkRemovedEventType, + Reduce: p.reduceLogoRemoved, + }, + { + Event: org.LabelPolicyIconDarkAddedEventType, + Reduce: p.reduceIconAdded, + }, + { + Event: org.LabelPolicyIconDarkRemovedEventType, + Reduce: p.reduceIconRemoved, + }, + { + Event: org.LabelPolicyFontAddedEventType, + Reduce: p.reduceFontAdded, + }, + { + Event: org.LabelPolicyFontRemovedEventType, + Reduce: p.reduceFontRemoved, + }, + { + Event: org.LabelPolicyAssetsRemovedEventType, + Reduce: p.reduceAssetsRemoved, + }, + }, + }, + { + Aggregate: iam.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: iam.LabelPolicyAddedEventType, + Reduce: p.reduceAdded, + }, + { + Event: iam.LabelPolicyChangedEventType, + Reduce: p.reduceChanged, + }, + { + Event: iam.LabelPolicyActivatedEventType, + Reduce: p.reduceActivated, + }, + { + Event: iam.LabelPolicyLogoAddedEventType, + Reduce: p.reduceLogoAdded, + }, + { + Event: iam.LabelPolicyLogoRemovedEventType, + Reduce: p.reduceLogoRemoved, + }, + { + Event: iam.LabelPolicyIconAddedEventType, + Reduce: p.reduceIconAdded, + }, + { + Event: iam.LabelPolicyIconRemovedEventType, + Reduce: p.reduceIconRemoved, + }, + { + Event: iam.LabelPolicyLogoDarkAddedEventType, + Reduce: p.reduceLogoAdded, + }, + { + Event: iam.LabelPolicyLogoDarkRemovedEventType, + Reduce: p.reduceLogoRemoved, + }, + { + Event: iam.LabelPolicyIconDarkAddedEventType, + Reduce: p.reduceIconAdded, + }, + { + Event: iam.LabelPolicyIconDarkRemovedEventType, + Reduce: p.reduceIconRemoved, + }, + { + Event: iam.LabelPolicyFontAddedEventType, + Reduce: p.reduceFontAdded, + }, + { + Event: iam.LabelPolicyFontRemovedEventType, + Reduce: p.reduceFontRemoved, + }, + { + Event: iam.LabelPolicyAssetsRemovedEventType, + Reduce: p.reduceAssetsRemoved, + }, + }, + }, + } +} + +func (p *LabelPolicyProjection) reduceAdded(event eventstore.EventReader) (*handler.Statement, error) { + var policyEvent policy.LabelPolicyAddedEvent + var isDefault bool + switch e := event.(type) { + case *org.LabelPolicyAddedEvent: + policyEvent = e.LabelPolicyAddedEvent + isDefault = false + case *iam.LabelPolicyAddedEvent: + policyEvent = e.LabelPolicyAddedEvent + isDefault = true + default: + logging.LogWithFields("PROJE-zR6h0", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyAddedEventType, iam.LabelPolicyAddedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-CSE7A", "reduce.wrong.event.type") + } + return crdb.NewCreateStatement( + &policyEvent, + []handler.Column{ + handler.NewCol(LabelPolicyCreationDateCol, policyEvent.CreationDate()), + handler.NewCol(LabelPolicyChangeDateCol, policyEvent.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, policyEvent.Sequence()), + handler.NewCol(LabelPolicyIDCol, policyEvent.Aggregate().ID), + handler.NewCol(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + handler.NewCol(LabelPolicyIsDefaultCol, isDefault), + handler.NewCol(LabelPolicyResourceOwnerCol, policyEvent.Aggregate().ResourceOwner), + handler.NewCol(LabelPolicyLightPrimaryColorCol, policyEvent.PrimaryColor), + handler.NewCol(LabelPolicyLightBackgroundColorCol, policyEvent.BackgroundColor), + handler.NewCol(LabelPolicyLightWarnColorCol, policyEvent.WarnColor), + handler.NewCol(LabelPolicyLightFontColorCol, policyEvent.FontColor), + handler.NewCol(LabelPolicyDarkPrimaryColorCol, policyEvent.PrimaryColorDark), + handler.NewCol(LabelPolicyDarkBackgroundColorCol, policyEvent.BackgroundColorDark), + handler.NewCol(LabelPolicyDarkWarnColorCol, policyEvent.WarnColorDark), + handler.NewCol(LabelPolicyDarkFontColorCol, policyEvent.FontColorDark), + handler.NewCol(LabelPolicyHideLoginNameSuffixCol, policyEvent.HideLoginNameSuffix), + handler.NewCol(LabelPolicyShouldErrorPopupCol, policyEvent.ErrorMsgPopup), + handler.NewCol(LabelPolicyWatermarkDisabledCol, policyEvent.DisableWatermark), + }), nil +} + +func (p *LabelPolicyProjection) reduceChanged(event eventstore.EventReader) (*handler.Statement, error) { + var policyEvent policy.LabelPolicyChangedEvent + switch e := event.(type) { + case *org.LabelPolicyChangedEvent: + policyEvent = e.LabelPolicyChangedEvent + case *iam.LabelPolicyChangedEvent: + policyEvent = e.LabelPolicyChangedEvent + default: + logging.LogWithFields("PROJE-2VrlG", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyChangedEventType, iam.LabelPolicyChangedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-qgVug", "reduce.wrong.event.type") + } + cols := []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, policyEvent.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, policyEvent.Sequence()), + } + if policyEvent.PrimaryColor != nil { + cols = append(cols, handler.NewCol(LabelPolicyLightPrimaryColorCol, *policyEvent.PrimaryColor)) + } + if policyEvent.BackgroundColor != nil { + cols = append(cols, handler.NewCol(LabelPolicyLightBackgroundColorCol, *policyEvent.BackgroundColor)) + } + if policyEvent.WarnColor != nil { + cols = append(cols, handler.NewCol(LabelPolicyLightWarnColorCol, *policyEvent.WarnColor)) + } + if policyEvent.FontColor != nil { + cols = append(cols, handler.NewCol(LabelPolicyLightFontColorCol, *policyEvent.FontColor)) + } + if policyEvent.PrimaryColorDark != nil { + cols = append(cols, handler.NewCol(LabelPolicyDarkPrimaryColorCol, *policyEvent.PrimaryColorDark)) + } + if policyEvent.BackgroundColorDark != nil { + cols = append(cols, handler.NewCol(LabelPolicyDarkBackgroundColorCol, *policyEvent.BackgroundColorDark)) + } + if policyEvent.WarnColorDark != nil { + cols = append(cols, handler.NewCol(LabelPolicyDarkWarnColorCol, *policyEvent.WarnColorDark)) + } + if policyEvent.FontColorDark != nil { + cols = append(cols, handler.NewCol(LabelPolicyDarkFontColorCol, *policyEvent.FontColorDark)) + } + if policyEvent.HideLoginNameSuffix != nil { + cols = append(cols, handler.NewCol(LabelPolicyHideLoginNameSuffixCol, *policyEvent.HideLoginNameSuffix)) + } + if policyEvent.ErrorMsgPopup != nil { + cols = append(cols, handler.NewCol(LabelPolicyShouldErrorPopupCol, *policyEvent.ErrorMsgPopup)) + } + if policyEvent.DisableWatermark != nil { + cols = append(cols, handler.NewCol(LabelPolicyWatermarkDisabledCol, *policyEvent.DisableWatermark)) + } + return crdb.NewUpdateStatement( + &policyEvent, + cols, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, policyEvent.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +func (p *LabelPolicyProjection) reduceRemoved(event eventstore.EventReader) (*handler.Statement, error) { + policyEvent, ok := event.(*org.LabelPolicyRemovedEvent) + if !ok { + logging.LogWithFields("PROJE-izDbs", "seq", event.Sequence(), "expectedType", org.LabelPolicyRemovedEventType).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-ATMBz", "reduce.wrong.event.type") + } + return crdb.NewDeleteStatement( + policyEvent, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, policyEvent.Aggregate().ID), + }), nil +} + +func (p *LabelPolicyProjection) reduceActivated(event eventstore.EventReader) (*handler.Statement, error) { + switch event.(type) { + case *org.LabelPolicyActivatedEvent, *iam.LabelPolicyActivatedEvent: + // everything ok + default: + logging.LogWithFields("PROJE-ZQO7J", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyActivatedEventType, iam.LabelPolicyActivatedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-dldEU", "reduce.wrong.event.type") + } + return crdb.NewCopyStatement( + event, + []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, event.Sequence()), + handler.NewCol(LabelPolicyStateCol, domain.LabelPolicyStateActive), + handler.NewCol(LabelPolicyCreationDateCol, nil), + handler.NewCol(LabelPolicyResourceOwnerCol, nil), + handler.NewCol(LabelPolicyIDCol, nil), + handler.NewCol(LabelPolicyIsDefaultCol, nil), + handler.NewCol(LabelPolicyHideLoginNameSuffixCol, nil), + handler.NewCol(LabelPolicyFontURLCol, nil), + handler.NewCol(LabelPolicyWatermarkDisabledCol, nil), + handler.NewCol(LabelPolicyShouldErrorPopupCol, nil), + handler.NewCol(LabelPolicyLightPrimaryColorCol, nil), + handler.NewCol(LabelPolicyLightWarnColorCol, nil), + handler.NewCol(LabelPolicyLightBackgroundColorCol, nil), + handler.NewCol(LabelPolicyLightFontColorCol, nil), + handler.NewCol(LabelPolicyLightLogoURLCol, nil), + handler.NewCol(LabelPolicyLightIconURLCol, nil), + handler.NewCol(LabelPolicyDarkPrimaryColorCol, nil), + handler.NewCol(LabelPolicyDarkWarnColorCol, nil), + handler.NewCol(LabelPolicyDarkBackgroundColorCol, nil), + handler.NewCol(LabelPolicyDarkFontColorCol, nil), + handler.NewCol(LabelPolicyDarkLogoURLCol, nil), + handler.NewCol(LabelPolicyDarkIconURLCol, nil), + }, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, event.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +func (p *LabelPolicyProjection) reduceLogoAdded(event eventstore.EventReader) (*handler.Statement, error) { + var storeKey handler.Column + switch e := event.(type) { + case *org.LabelPolicyLogoAddedEvent: + storeKey = handler.NewCol(LabelPolicyLightLogoURLCol, e.StoreKey) + case *iam.LabelPolicyLogoAddedEvent: + storeKey = handler.NewCol(LabelPolicyLightLogoURLCol, e.StoreKey) + case *org.LabelPolicyLogoDarkAddedEvent: + storeKey = handler.NewCol(LabelPolicyDarkLogoURLCol, e.StoreKey) + case *iam.LabelPolicyLogoDarkAddedEvent: + storeKey = handler.NewCol(LabelPolicyDarkLogoURLCol, e.StoreKey) + default: + logging.LogWithFields("PROJE-NHrbi", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyLogoAddedEventType, iam.LabelPolicyLogoAddedEventType, org.LabelPolicyLogoDarkAddedEventType, iam.LabelPolicyLogoDarkAddedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-4wbOI", "reduce.wrong.event.type") + } + + return crdb.NewUpdateStatement( + event, + []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, event.Sequence()), + storeKey, + }, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, event.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +func (p *LabelPolicyProjection) reduceLogoRemoved(event eventstore.EventReader) (*handler.Statement, error) { + var col string + switch event.(type) { + case *org.LabelPolicyLogoRemovedEvent: + col = LabelPolicyLightLogoURLCol + case *iam.LabelPolicyLogoRemovedEvent: + col = LabelPolicyLightLogoURLCol + case *org.LabelPolicyLogoDarkRemovedEvent: + col = LabelPolicyDarkLogoURLCol + case *iam.LabelPolicyLogoDarkRemovedEvent: + col = LabelPolicyDarkLogoURLCol + default: + logging.LogWithFields("PROJE-oUmnS", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyLogoRemovedEventType, iam.LabelPolicyLogoRemovedEventType, org.LabelPolicyLogoDarkRemovedEventType, iam.LabelPolicyLogoDarkRemovedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-kg8H4", "reduce.wrong.event.type") + } + + return crdb.NewUpdateStatement( + event, + []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, event.Sequence()), + handler.NewCol(col, nil), + }, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, event.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +func (p *LabelPolicyProjection) reduceIconAdded(event eventstore.EventReader) (*handler.Statement, error) { + var storeKey handler.Column + switch e := event.(type) { + case *org.LabelPolicyIconAddedEvent: + storeKey = handler.NewCol(LabelPolicyLightIconURLCol, e.StoreKey) + case *iam.LabelPolicyIconAddedEvent: + storeKey = handler.NewCol(LabelPolicyLightIconURLCol, e.StoreKey) + case *org.LabelPolicyIconDarkAddedEvent: + storeKey = handler.NewCol(LabelPolicyDarkIconURLCol, e.StoreKey) + case *iam.LabelPolicyIconDarkAddedEvent: + storeKey = handler.NewCol(LabelPolicyDarkIconURLCol, e.StoreKey) + default: + logging.LogWithFields("PROJE-6efFw", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyIconAddedEventType, iam.LabelPolicyIconAddedEventType, org.LabelPolicyIconDarkAddedEventType, iam.LabelPolicyIconDarkAddedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-e2JFz", "reduce.wrong.event.type") + } + + return crdb.NewUpdateStatement( + event, + []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, event.Sequence()), + storeKey, + }, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, event.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +func (p *LabelPolicyProjection) reduceIconRemoved(event eventstore.EventReader) (*handler.Statement, error) { + var col string + switch event.(type) { + case *org.LabelPolicyIconRemovedEvent: + col = LabelPolicyLightIconURLCol + case *iam.LabelPolicyIconRemovedEvent: + col = LabelPolicyLightIconURLCol + case *org.LabelPolicyIconDarkRemovedEvent: + col = LabelPolicyDarkIconURLCol + case *iam.LabelPolicyIconDarkRemovedEvent: + col = LabelPolicyDarkIconURLCol + default: + logging.LogWithFields("PROJE-0BiAZ", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyIconRemovedEventType, iam.LabelPolicyIconRemovedEventType, org.LabelPolicyIconDarkRemovedEventType, iam.LabelPolicyIconDarkRemovedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-gfgbY", "reduce.wrong.event.type") + } + + return crdb.NewUpdateStatement( + event, + []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, event.Sequence()), + handler.NewCol(col, nil), + }, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, event.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +func (p *LabelPolicyProjection) reduceFontAdded(event eventstore.EventReader) (*handler.Statement, error) { + var storeKey handler.Column + switch e := event.(type) { + case *org.LabelPolicyFontAddedEvent: + storeKey = handler.NewCol(LabelPolicyFontURLCol, e.StoreKey) + case *iam.LabelPolicyFontAddedEvent: + storeKey = handler.NewCol(LabelPolicyFontURLCol, e.StoreKey) + default: + logging.LogWithFields("PROJE-DCzfX", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyFontAddedEventType, iam.LabelPolicyFontAddedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-65i9W", "reduce.wrong.event.type") + } + + return crdb.NewUpdateStatement( + event, + []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, event.Sequence()), + storeKey, + }, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, event.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +func (p *LabelPolicyProjection) reduceFontRemoved(event eventstore.EventReader) (*handler.Statement, error) { + var col string + switch event.(type) { + case *org.LabelPolicyFontRemovedEvent: + col = LabelPolicyFontURLCol + case *iam.LabelPolicyFontRemovedEvent: + col = LabelPolicyFontURLCol + default: + logging.LogWithFields("PROJE-YKwG4", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyFontRemovedEventType, iam.LabelPolicyFontRemovedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-xf32J", "reduce.wrong.event.type") + } + + return crdb.NewUpdateStatement( + event, + []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, event.Sequence()), + handler.NewCol(col, nil), + }, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, event.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +func (p *LabelPolicyProjection) reduceAssetsRemoved(event eventstore.EventReader) (*handler.Statement, error) { + switch event.(type) { + case *org.LabelPolicyAssetsRemovedEvent, *iam.LabelPolicyAssetsRemovedEvent: + //ok + default: + logging.LogWithFields("PROJE-YKwG4", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LabelPolicyAssetsRemovedEventType, iam.LabelPolicyAssetsRemovedEventType}).Error("was not an event") + return nil, errors.ThrowInvalidArgument(nil, "PROJE-qi39A", "reduce.wrong.event.type") + } + + return crdb.NewUpdateStatement( + event, + []handler.Column{ + handler.NewCol(LabelPolicyChangeDateCol, event.CreationDate()), + handler.NewCol(LabelPolicySequenceCol, event.Sequence()), + handler.NewCol(LabelPolicyLightLogoURLCol, nil), + handler.NewCol(LabelPolicyLightIconURLCol, nil), + handler.NewCol(LabelPolicyDarkLogoURLCol, nil), + handler.NewCol(LabelPolicyDarkIconURLCol, nil), + handler.NewCol(LabelPolicyFontURLCol, nil), + }, + []handler.Condition{ + handler.NewCond(LabelPolicyIDCol, event.Aggregate().ID), + handler.NewCond(LabelPolicyStateCol, domain.LabelPolicyStatePreview), + }), nil +} + +const ( + LabelPolicyCreationDateCol = "creation_date" + LabelPolicyChangeDateCol = "change_date" + LabelPolicySequenceCol = "sequence" + LabelPolicyIDCol = "id" + LabelPolicyStateCol = "state" + LabelPolicyIsDefaultCol = "is_default" + LabelPolicyResourceOwnerCol = "resource_owner" + LabelPolicyHideLoginNameSuffixCol = "hide_login_name_suffix" + LabelPolicyFontURLCol = "font_url" + LabelPolicyWatermarkDisabledCol = "watermark_disabled" + LabelPolicyShouldErrorPopupCol = "should_error_popup" + + LabelPolicyLightPrimaryColorCol = "light_primary_color" + LabelPolicyLightWarnColorCol = "light_warn_color" + LabelPolicyLightBackgroundColorCol = "light_background_color" + LabelPolicyLightFontColorCol = "light_font_color" + LabelPolicyLightLogoURLCol = "light_logo_url" + LabelPolicyLightIconURLCol = "light_icon_url" + + LabelPolicyDarkPrimaryColorCol = "dark_primary_color" + LabelPolicyDarkWarnColorCol = "dark_warn_color" + LabelPolicyDarkBackgroundColorCol = "dark_background_color" + LabelPolicyDarkFontColorCol = "dark_font_color" + LabelPolicyDarkLogoURLCol = "dark_logo_url" + LabelPolicyDarkIconURLCol = "dark_icon_url" +) diff --git a/internal/query/projection/label_policy_test.go b/internal/query/projection/label_policy_test.go new file mode 100644 index 0000000000..5721bb3bc1 --- /dev/null +++ b/internal/query/projection/label_policy_test.go @@ -0,0 +1,981 @@ +package projection + +import ( + "testing" + + "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/handler" + "github.com/caos/zitadel/internal/eventstore/repository" + "github.com/caos/zitadel/internal/repository/iam" + "github.com/caos/zitadel/internal/repository/org" +) + +func TestLabelPolicyProjection_reduces(t *testing.T) { + type args struct { + event func(t *testing.T) eventstore.EventReader + } + tests := []struct { + name string + args args + reduce func(event eventstore.EventReader) (*handler.Statement, error) + want wantReduce + }{ + { + name: "org.reduceAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyAddedEventType), + org.AggregateType, + []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), + ), org.LabelPolicyAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO zitadel.projections.label_policies (creation_date, change_date, sequence, id, state, is_default, resource_owner, light_primary_color, light_background_color, light_warn_color, light_font_color, dark_primary_color, dark_background_color, dark_warn_color, dark_font_color, hide_login_name_suffix, should_error_popup, watermark_disabled) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)", + expectedArgs: []interface{}{ + anyArg{}, + anyArg{}, + uint64(15), + "agg-id", + domain.LabelPolicyStatePreview, + false, + "ro-id", + "#5282c1", + "#141735", + "#ff3b5b", + "#ffffff", + "", + "", + "", + "", + false, + false, + false, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceChanged", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyChangedEventType), + org.AggregateType, + []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), + ), org.LabelPolicyChangedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_primary_color, light_background_color, light_warn_color, light_font_color) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (state = $8)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "#5282c1", + "#141735", + "#ff3b5b", + "#ffffff", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyRemovedEventType), + org.AggregateType, + nil, + ), org.LabelPolicyRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "DELETE FROM zitadel.projections.label_policies WHERE (id = $1)", + expectedArgs: []interface{}{ + "agg-id", + }, + }, + }, + }, + }, + }, + { + name: "org.reduceActivated", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyActivatedEventType), + org.AggregateType, + nil, + ), org.LabelPolicyActivatedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceActivated, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPSERT INTO zitadel.projections.label_policies (change_date, sequence, state, creation_date, resource_owner, id, is_default, hide_login_name_suffix, font_url, watermark_disabled, should_error_popup, light_primary_color, light_warn_color, light_background_color, light_font_color, light_logo_url, light_icon_url, dark_primary_color, dark_warn_color, dark_background_color, dark_font_color, dark_logo_url, dark_icon_url) SELECT $1, $2, $3, creation_date, resource_owner, id, is_default, hide_login_name_suffix, font_url, watermark_disabled, should_error_popup, light_primary_color, light_warn_color, light_background_color, light_font_color, light_logo_url, light_icon_url, dark_primary_color, dark_warn_color, dark_background_color, dark_font_color, dark_logo_url, dark_icon_url FROM zitadel.projections.label_policies AS copy_table WHERE copy_table.id = $4 AND copy_table.state = $5", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + domain.LabelPolicyStateActive, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceLogoAdded light", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyLogoAddedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), org.LabelPolicyLogoAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceLogoAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_logo_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/logo.png", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceLogoAdded dark", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyLogoDarkAddedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), org.LabelPolicyLogoDarkAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceLogoAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, dark_logo_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/logo.png", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceIconAdded light", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyIconAddedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), org.LabelPolicyIconAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceIconAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_icon_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/icon.png", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceIconAdded dark", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyIconDarkAddedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), org.LabelPolicyIconDarkAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceIconAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, dark_icon_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/icon.png", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceLogoRemoved light", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyLogoRemovedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), org.LabelPolicyLogoRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceLogoRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_logo_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceLogoRemoved dark", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyLogoDarkRemovedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), org.LabelPolicyLogoDarkRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceLogoRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, dark_logo_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceIconRemoved light", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyIconRemovedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), org.LabelPolicyIconRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceIconRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_icon_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceIconRemoved dark", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyIconDarkRemovedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), org.LabelPolicyIconDarkRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceIconRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, dark_icon_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceFontAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyFontAddedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/font.ttf"}`), + ), org.LabelPolicyFontAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceFontAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, font_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/font.ttf", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceFontRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyFontRemovedEventType), + org.AggregateType, + []byte(`{"storeKey": "/path/to/font.ttf"}`), + ), org.LabelPolicyFontRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceFontRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, font_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceAssetsRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LabelPolicyAssetsRemovedEventType), + org.AggregateType, + nil, + ), org.LabelPolicyAssetsRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceAssetsRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_logo_url, light_icon_url, dark_logo_url, dark_icon_url, font_url) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (state = $9)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + nil, + nil, + nil, + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyAddedEventType), + iam.AggregateType, + []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b"}`), + ), iam.LabelPolicyAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO zitadel.projections.label_policies (creation_date, change_date, sequence, id, state, is_default, resource_owner, light_primary_color, light_background_color, light_warn_color, light_font_color, dark_primary_color, dark_background_color, dark_warn_color, dark_font_color, hide_login_name_suffix, should_error_popup, watermark_disabled) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)", + expectedArgs: []interface{}{ + anyArg{}, + anyArg{}, + uint64(15), + "agg-id", + domain.LabelPolicyStatePreview, + true, + "ro-id", + "#5282c1", + "#141735", + "#ff3b5b", + "#ffffff", + "", + "", + "", + "", + false, + false, + false, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceChanged", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyChangedEventType), + iam.AggregateType, + []byte(`{"backgroundColor": "#141735", "fontColor": "#ffffff", "primaryColor": "#5282c1", "warnColor": "#ff3b5b", "primaryColorDark": "#ffffff","backgroundColorDark": "#ffffff", "warnColorDark": "#ffffff", "fontColorDark": "#ffffff", "hideLoginNameSuffix": true, "errorMsgPopup": true, "disableWatermark": true}`), + ), iam.LabelPolicyChangedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_primary_color, light_background_color, light_warn_color, light_font_color, dark_primary_color, dark_background_color, dark_warn_color, dark_font_color, hide_login_name_suffix, should_error_popup, watermark_disabled) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) WHERE (id = $14) AND (state = $15)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "#5282c1", + "#141735", + "#ff3b5b", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + "#ffffff", + true, + true, + true, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceActivated", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyActivatedEventType), + iam.AggregateType, + nil, + ), iam.LabelPolicyActivatedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceActivated, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPSERT INTO zitadel.projections.label_policies (change_date, sequence, state, creation_date, resource_owner, id, is_default, hide_login_name_suffix, font_url, watermark_disabled, should_error_popup, light_primary_color, light_warn_color, light_background_color, light_font_color, light_logo_url, light_icon_url, dark_primary_color, dark_warn_color, dark_background_color, dark_font_color, dark_logo_url, dark_icon_url) SELECT $1, $2, $3, creation_date, resource_owner, id, is_default, hide_login_name_suffix, font_url, watermark_disabled, should_error_popup, light_primary_color, light_warn_color, light_background_color, light_font_color, light_logo_url, light_icon_url, dark_primary_color, dark_warn_color, dark_background_color, dark_font_color, dark_logo_url, dark_icon_url FROM zitadel.projections.label_policies AS copy_table WHERE copy_table.id = $4 AND copy_table.state = $5", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + domain.LabelPolicyStateActive, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceLogoAdded light", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyLogoAddedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), iam.LabelPolicyLogoAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceLogoAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_logo_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/logo.png", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceLogoAdded dark", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyLogoDarkAddedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), iam.LabelPolicyLogoDarkAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceLogoAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, dark_logo_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/logo.png", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceIconAdded light", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyIconAddedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), iam.LabelPolicyIconAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceIconAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_icon_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/icon.png", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceIconAdded dark", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyIconDarkAddedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), iam.LabelPolicyIconDarkAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceIconAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, dark_icon_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/icon.png", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceLogoRemoved light", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyLogoRemovedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), iam.LabelPolicyLogoRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceLogoRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_logo_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceLogoRemoved dark", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyLogoDarkRemovedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/logo.png"}`), + ), iam.LabelPolicyLogoDarkRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceLogoRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, dark_logo_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceIconRemoved light", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyIconRemovedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), iam.LabelPolicyIconRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceIconRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_icon_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceIconRemoved dark", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyIconDarkRemovedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/icon.png"}`), + ), iam.LabelPolicyIconDarkRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceIconRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, dark_icon_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceFontAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyFontAddedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/font.ttf"}`), + ), iam.LabelPolicyFontAddedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceFontAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, font_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + "/path/to/font.ttf", + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceFontRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyFontRemovedEventType), + iam.AggregateType, + []byte(`{"storeKey": "/path/to/font.ttf"}`), + ), iam.LabelPolicyFontRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceFontRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, font_url) = ($1, $2, $3) WHERE (id = $4) AND (state = $5)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceAssetsRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LabelPolicyAssetsRemovedEventType), + iam.AggregateType, + nil, + ), iam.LabelPolicyAssetsRemovedEventMapper), + }, + reduce: (&LabelPolicyProjection{}).reduceAssetsRemoved, + want: wantReduce{ + aggregateType: eventstore.AggregateType("iam"), + sequence: 15, + previousSequence: 10, + projection: LabelPolicyTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE zitadel.projections.label_policies SET (change_date, sequence, light_logo_url, light_icon_url, dark_logo_url, dark_icon_url, font_url) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (state = $9)", + expectedArgs: []interface{}{ + anyArg{}, + uint64(15), + nil, + nil, + nil, + nil, + nil, + "agg-id", + domain.LabelPolicyStatePreview, + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + event := baseEvent(t) + got, err := tt.reduce(event) + if _, ok := err.(errors.InvalidArgument); !ok { + t.Errorf("no wrong event mapping: %v, got: %v", err, got) + } + + event = tt.args.event(t) + got, err = tt.reduce(event) + assertReduce(t, got, err, tt.want) + }) + } +} diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go index 588c1b26ca..6601480c16 100644 --- a/internal/query/projection/projection.go +++ b/internal/query/projection/projection.go @@ -42,6 +42,7 @@ func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, co NewLockoutPolicyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["lockout_policy"])) NewPrivacyPolicyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["privacy_policy"])) NewOrgIAMPolicyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["org_iam_policy"])) + NewLabelPolicyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["label_policy"])) NewProjectGrantProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["project_grants"])) NewProjectRoleProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["project_roles"])) // owner.NewOrgOwnerProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["org_owners"])) diff --git a/internal/ui/login/handler/passwordless_registration_handler.go b/internal/ui/login/handler/passwordless_registration_handler.go index eb969bf191..2d66604109 100644 --- a/internal/ui/login/handler/passwordless_registration_handler.go +++ b/internal/ui/login/handler/passwordless_registration_handler.go @@ -8,6 +8,8 @@ import ( http_mw "github.com/caos/zitadel/internal/api/http/middleware" "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/eventstore/v1/models" + "github.com/caos/zitadel/internal/query" ) const ( @@ -111,9 +113,9 @@ func (l *Login) renderPasswordlessRegistration(w http.ResponseWriter, r *http.Re } translator := l.getTranslator(authReq) if authReq == nil { - policy, err := l.authRepo.GetLabelPolicy(r.Context(), orgID) - logging.Log("LOGIN-afgr2").OnError(err).Warn("could not get label policy") - data.LabelPolicy = policy + policy, err := l.query.ActiveLabelPolicyByOrg(r.Context(), orgID) + logging.Log("HANDL-XjWKE").OnError(err).Error("unable to get active label policy") + data.LabelPolicy = labelPolicyToDomain(policy) translator, err = l.renderer.NewTranslator() if err == nil { @@ -125,6 +127,36 @@ func (l *Login) renderPasswordlessRegistration(w http.ResponseWriter, r *http.Re l.renderer.RenderTemplate(w, r, translator, l.renderer.Templates[tmplPasswordlessRegistration], data, nil) } +func labelPolicyToDomain(p *query.LabelPolicy) *domain.LabelPolicy { + return &domain.LabelPolicy{ + ObjectRoot: models.ObjectRoot{ + AggregateID: p.ID, + Sequence: p.Sequence, + ResourceOwner: p.ResourceOwner, + CreationDate: p.CreationDate, + ChangeDate: p.ChangeDate, + }, + State: p.State, + Default: p.IsDefault, + PrimaryColor: p.Light.PrimaryColor, + BackgroundColor: p.Light.BackgroundColor, + WarnColor: p.Light.WarnColor, + FontColor: p.Light.FontColor, + LogoURL: p.Light.LogoURL, + IconURL: p.Light.IconURL, + PrimaryColorDark: p.Dark.PrimaryColor, + BackgroundColorDark: p.Dark.BackgroundColor, + WarnColorDark: p.Dark.WarnColor, + FontColorDark: p.Dark.FontColor, + LogoDarkURL: p.Dark.LogoURL, + IconDarkURL: p.Dark.IconURL, + Font: p.FontURL, + HideLoginNameSuffix: p.HideLoginNameSuffix, + ErrorMsgPopup: p.ShouldErrorPopup, + DisableWatermark: p.WatermarkDisabled, + } +} + func (l *Login) handlePasswordlessRegistrationCheck(w http.ResponseWriter, r *http.Request) { formData := new(passwordlessRegistrationFormData) authReq, err := l.getAuthRequestAndParseData(r, formData) diff --git a/migrations/cockroach/V1.94__label_policy.sql b/migrations/cockroach/V1.94__label_policy.sql new file mode 100644 index 0000000000..0cc728b54a --- /dev/null +++ b/migrations/cockroach/V1.94__label_policy.sql @@ -0,0 +1,28 @@ +CREATE TABLE zitadel.projections.label_policies ( + id STRING NOT NULL, + creation_date TIMESTAMPTZ NOT NULL, + change_date TIMESTAMPTZ NOT NULL, + sequence INT8 NOT NULL, + state INT2 NOT NULL, + resource_owner TEXT NOT NULL, + + is_default BOOLEAN NOT NULL DEFAULT false, + hide_login_name_suffix BOOLEAN NOT NULL DEFAULT false, + font_url STRING, + watermark_disabled BOOLEAN NOT NULL DEFAULT false, + should_error_popup BOOLEAN NOT NULL DEFAULT false, + light_primary_color STRING, + light_warn_color STRING, + light_background_color STRING, + light_font_color STRING, + light_logo_url STRING, + light_icon_url STRING, + dark_primary_color STRING, + dark_warn_color STRING, + dark_background_color STRING, + dark_font_color STRING, + dark_logo_url STRING, + dark_icon_url STRING, + + PRIMARY KEY (id, state) +); \ No newline at end of file