feat(queries): use org projection (#2342)

* 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 <florian@caos.ch>

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>

* 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] <support@github.com>

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Silvan <silvan.reusser@gmail.com>
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] <support@github.com>

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Silvan <silvan.reusser@gmail.com>
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] <support@github.com>

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Silvan <silvan.reusser@gmail.com>
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] <support@github.com>

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Silvan <silvan.reusser@gmail.com>
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] <support@github.com>

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] <support@github.com>

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] <support@github.com>

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] <support@github.com>

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Silvan <silvan.reusser@gmail.com>
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 <max@caos.ch>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Silvan <silvan.reusser@gmail.com>

* docs: update run and start section texts (#1745)

* update run and start section texts

* adds showcase

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>

* 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 <livio.a@gmail.com>

* 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 <max@caos.ch>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
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 <livio.a@gmail.com>

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* 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 <livio.a@gmail.com>

* 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 <florian@caos.ch>

Co-authored-by: Florian Forster <florian@caos.ch>

* 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 <livio.a@gmail.com>

* 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 <livio.a@gmail.com>
Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
Co-authored-by: Florian Forster <florian@caos.ch>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: Christian Jakob <47860090+thesephirot@users.noreply.github.com>
Co-authored-by: Elio Bischof <eliobischof@gmail.com>

* 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] <support@github.com>

* 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] <support@github.com>

* 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 <stefan@caos.ch>

* 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

* 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

* global counter column

* fix is org unique

* fix: merge main and change actions / flow projections to new query side (#2434)

* feat: actions (#2377)

* feat(actions): begin api

* feat(actions): begin api

* api and projections

* fix: handle multiple statements for a single event in projections

* export func type

* fix test

* update to new reduce interface

* flows in login

* feat: jwt idp

* feat: command side

* feat: add tests

* actions and flows

* fill idp views with jwt idps and return apis

* add jwtEndpoint to jwt idp

* begin jwt request handling

* add feature

* merge

* merge

* handle jwt idp

* cleanup

* bug fixes

* autoregister

* get token from specific header name

* fix: proto

* fixes

* i18n

* begin tests

* fix and log http proxy

* remove docker cache

* fixes

* usergrants in actions api

* tests adn cleanup

* cleanup

* fix add user grant

* set login context

* i18n

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

* change actions / flow projections to new query side

* fixes

* enable org projection

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

* fixes

* cleanup

* add tests

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
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 <eliobischof@gmail.com>
Co-authored-by: Stefan Benz <stefan@caos.ch>
Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Silvan 2021-09-29 13:20:57 +02:00 committed by GitHub
parent 5e110f0a48
commit 39c35c9455
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 2960 additions and 1549 deletions

View File

@ -6,7 +6,7 @@ services:
restart: always restart: always
networks: networks:
- zitadel - zitadel
image: cockroachdb/cockroach:v21.1.0 image: cockroachdb/cockroach:v21.1.7
command: start-single-node --insecure --listen-addr=0.0.0.0 command: start-single-node --insecure --listen-addr=0.0.0.0
ports: ports:
- 8080:8080 - 8080:8080
@ -155,7 +155,7 @@ services:
ports: ports:
- '50000:8080' - '50000:8080'
environment: environment:
- BKD_HOST=backend-run - BKD_HOST=host.docker.internal
- BKD_PORT=50001 - BKD_PORT=50001
frontend-local-run: frontend-local-run:
@ -187,7 +187,7 @@ services:
volumes: volumes:
- ./environment.json:/environment.json - ./environment.json:/environment.json
environment: environment:
- HOST=backend-run - HOST=host.docker.internal
- PORT=50002 - PORT=50002
networks: networks:

View File

@ -179,7 +179,15 @@ func startZitadel(configPaths []string) {
logging.Log("MAIN-9oRw6").OnError(err).Fatal("error starting auth repo") logging.Log("MAIN-9oRw6").OnError(err).Fatal("error starting auth repo")
} }
verifier := internal_authz.Start(authZRepo) repo := struct {
authz_repo.EsRepository
query.Queries
}{
*authZRepo,
*queries,
}
verifier := internal_authz.Start(&repo)
startAPI(ctx, conf, verifier, authZRepo, authRepo, commands, queries, store) startAPI(ctx, conf, verifier, authZRepo, authRepo, commands, queries, store)
startUI(ctx, conf, authRepo, commands, queries, store) startUI(ctx, conf, authRepo, commands, queries, store)
@ -213,7 +221,7 @@ func startAPI(ctx context.Context, conf *Config, verifier *internal_authz.TokenV
repo, err := admin_es.Start(ctx, conf.Admin, conf.SystemDefaults, command, static, roles, *localDevMode) repo, err := admin_es.Start(ctx, conf.Admin, conf.SystemDefaults, command, static, roles, *localDevMode)
logging.Log("API-D42tq").OnError(err).Fatal("error starting auth repo") logging.Log("API-D42tq").OnError(err).Fatal("error starting auth repo")
apis := api.Create(conf.API, conf.InternalAuthZ, authZRepo, authRepo, repo, conf.SystemDefaults) apis := api.Create(conf.API, conf.InternalAuthZ, query, authZRepo, authRepo, repo, conf.SystemDefaults)
if *adminEnabled { if *adminEnabled {
apis.RegisterServer(ctx, admin.CreateServer(command, query, repo, conf.SystemDefaults.Domain)) apis.RegisterServer(ctx, admin.CreateServer(command, query, repo, conf.SystemDefaults.Domain))

View File

@ -71,6 +71,7 @@ Projections:
RetryFailedAfter: 1s RetryFailedAfter: 1s
MaxFailureCount: 5 MaxFailureCount: 5
BulkLimit: 200 BulkLimit: 200
MaxIterators: 1
CRDB: CRDB:
Host: $ZITADEL_EVENTSTORE_HOST Host: $ZITADEL_EVENTSTORE_HOST
Port: $ZITADEL_EVENTSTORE_PORT Port: $ZITADEL_EVENTSTORE_PORT

View File

@ -32,18 +32,6 @@ Returns the default languages
GET: /languages GET: /languages
### IsOrgUnique
> **rpc** IsOrgUnique([IsOrgUniqueRequest](#isorguniquerequest))
[IsOrgUniqueResponse](#isorguniqueresponse)
Checks whether an organisation exists by the given parameters
GET: /orgs/_is_unique
### GetOrgByID ### GetOrgByID
> **rpc** GetOrgByID([GetOrgByIDRequest](#getorgbyidrequest)) > **rpc** GetOrgByID([GetOrgByIDRequest](#getorgbyidrequest))
@ -56,6 +44,18 @@ Returns an organisation by id
GET: /orgs/{id} GET: /orgs/{id}
### IsOrgUnique
> **rpc** IsOrgUnique([IsOrgUniqueRequest](#isorguniquerequest))
[IsOrgUniqueResponse](#isorguniqueresponse)
Checks whether an organisation exists by the given parameters
GET: /orgs/_is_unique
### ListOrgs ### ListOrgs
> **rpc** ListOrgs([ListOrgsRequest](#listorgsrequest)) > **rpc** ListOrgs([ListOrgsRequest](#listorgsrequest))

View File

@ -3,98 +3,19 @@ package eventstore
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/errors"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/models"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
iam_model "github.com/caos/zitadel/internal/iam/model"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/logging"
admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/view/model"
) )
type OrgRepo struct { type OrgRepo struct {
Eventstore v1.Eventstore
View *admin_view.View View *admin_view.View
SearchLimit uint64
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
} }
func (repo *OrgRepo) OrgByID(ctx context.Context, id string) (*org_model.OrgView, error) {
org, viewErr := repo.View.OrgByID(id)
if viewErr != nil && !errors.IsNotFound(viewErr) {
return nil, viewErr
}
if errors.IsNotFound(viewErr) {
org = new(model.OrgView)
}
events, esErr := repo.getOrgEvents(ctx, id, org.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 {
return nil, errors.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.Org.NotFound")
}
if esErr != nil {
logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events")
return model.OrgToModel(org), nil
}
orgCopy := *org
for _, event := range events {
if err := orgCopy.AppendEvent(event); err != nil {
return model.OrgToModel(&orgCopy), nil
}
}
return model.OrgToModel(&orgCopy), nil
}
func (repo *OrgRepo) SearchOrgs(ctx context.Context, query *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) {
err := query.EnsureLimit(repo.SearchLimit)
if err != nil {
return nil, err
}
sequence, err := repo.View.GetLatestOrgSequence()
logging.Log("EVENT-LXo9w").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest iam sequence")
orgs, count, err := repo.View.SearchOrgs(query)
if err != nil {
return nil, err
}
result := &org_model.OrgSearchResult{
Offset: query.Offset,
Limit: query.Limit,
TotalResult: count,
Result: model.OrgsToModel(orgs),
}
if err == nil {
result.Sequence = sequence.CurrentSequence
result.Timestamp = sequence.LastSuccessfulSpoolerRun
}
return result, nil
}
func (repo *OrgRepo) IsOrgUnique(ctx context.Context, name, domain string) (isUnique bool, err error) {
var found bool
err = es_sdk.Filter(ctx, repo.Eventstore.FilterEvents, isUniqueValidation(&found), view.OrgNameUniqueQuery(name))
if (err != nil && !errors.IsNotFound(err)) || found {
return false, err
}
err = es_sdk.Filter(ctx, repo.Eventstore.FilterEvents, isUniqueValidation(&found), view.OrgDomainUniqueQuery(domain))
if err != nil && !errors.IsNotFound(err) {
return false, err
}
return !found, nil
}
func (repo *OrgRepo) GetOrgIAMPolicyByID(ctx context.Context, id string) (*iam_model.OrgIAMPolicyView, error) { func (repo *OrgRepo) GetOrgIAMPolicyByID(ctx context.Context, id string) (*iam_model.OrgIAMPolicyView, error) {
policy, err := repo.View.OrgIAMPolicyByAggregateID(id) policy, err := repo.View.OrgIAMPolicyByAggregateID(id)
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
@ -114,22 +35,3 @@ func (repo *OrgRepo) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.Org
policy.Default = true policy.Default = true
return iam_es_model.OrgIAMViewToModel(policy), err return iam_es_model.OrgIAMViewToModel(policy), err
} }
func (repo *OrgRepo) getOrgEvents(ctx context.Context, orgID string, sequence uint64) ([]*models.Event, error) {
query, err := view.OrgByIDQuery(orgID, sequence)
if err != nil {
return nil, err
}
return repo.Eventstore.FilterEvents(ctx, query)
}
func isUniqueValidation(unique *bool) func(events ...*models.Event) error {
return func(events ...*models.Event) error {
if len(events) == 0 {
return nil
}
*unique = *unique || events[0].Type == org_es_model.OrgDomainReserved || events[0].Type == org_es_model.OrgNameReserved
return nil
}
}

View File

@ -3,14 +3,13 @@ package handler
import ( import (
"time" "time"
"github.com/caos/zitadel/internal/command"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/static"
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/config/types"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/query" "github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/static"
) )
type Configs map[string]*Config type Configs map[string]*Config
@ -34,8 +33,6 @@ func (h *handler) Eventstore() v1.Eventstore {
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, defaults systemdefaults.SystemDefaults, command *command.Commands, static static.Storage, localDevMode bool) []query.Handler { func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, defaults systemdefaults.SystemDefaults, command *command.Commands, static static.Storage, localDevMode bool) []query.Handler {
handlers := []query.Handler{ handlers := []query.Handler{
newOrg(
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
newIAMMember( newIAMMember(
handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount, es}),
newIDPConfig( newIDPConfig(

View File

@ -1,103 +0,0 @@
package handler
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/org/repository/view"
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/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
)
const (
orgTable = "adminapi.orgs"
)
type Org struct {
handler
subscription *v1.Subscription
}
func newOrg(handler handler) *Org {
h := &Org{
handler: handler,
}
h.subscribe()
return h
}
func (o *Org) subscribe() {
o.subscription = o.es.Subscribe(o.AggregateTypes()...)
go func() {
for event := range o.subscription.Events {
query.ReduceEvent(o, event)
}
}()
}
func (o *Org) ViewModel() string {
return orgTable
}
func (o *Org) Subscription() *v1.Subscription {
return o.subscription
}
func (o *Org) AggregateTypes() []es_models.AggregateType {
return []es_models.AggregateType{model.OrgAggregate}
}
func (o *Org) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := o.view.GetLatestOrgSequence()
if err != nil {
return nil, err
}
return view.OrgQuery(sequence.CurrentSequence), nil
}
func (o *Org) CurrentSequence() (uint64, error) {
sequence, err := o.view.GetLatestOrgSequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (o *Org) Reduce(event *es_models.Event) error {
org := new(org_model.OrgView)
switch event.Type {
case model.OrgAdded:
err := org.AppendEvent(event)
if err != nil {
return err
}
case model.OrgChanged:
org, err := o.view.OrgByID(event.ResourceOwner)
if err != nil {
return err
}
err = org.AppendEvent(event)
if err != nil {
return err
}
default:
return o.view.ProcessedOrgSequence(event)
}
return o.view.PutOrg(org, event)
}
func (o *Org) OnError(event *es_models.Event, spoolerErr error) error {
logging.LogWithFields("SPOOL-ls9ew", "id", event.AggregateID).WithError(spoolerErr).Warn("something went wrong in project app handler")
return spooler.HandleError(event, spoolerErr, o.view.GetLatestOrgFailedEvent, o.view.ProcessedOrgFailedEvent, o.view.ProcessedOrgSequence, o.errorCountUntilSkip)
}
func (o *Org) OnSuccess() error {
return spooler.HandleSuccess(o.view.UpdateOrgSpoolerRunTimestamp)
}

View File

@ -12,7 +12,7 @@ import (
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults" sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/config/types"
"github.com/caos/zitadel/internal/eventstore/v1" v1 "github.com/caos/zitadel/internal/eventstore/v1"
es_spol "github.com/caos/zitadel/internal/eventstore/v1/spooler" es_spol "github.com/caos/zitadel/internal/eventstore/v1/spooler"
"github.com/caos/zitadel/internal/static" "github.com/caos/zitadel/internal/static"
) )
@ -61,9 +61,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, c
return &EsRepository{ return &EsRepository{
spooler: spool, spooler: spool,
OrgRepo: eventstore.OrgRepo{ OrgRepo: eventstore.OrgRepo{
Eventstore: es,
View: view, View: view,
SearchLimit: conf.SearchLimit,
SystemDefaults: systemDefaults, SystemDefaults: systemDefaults,
}, },
IAMRepository: eventstore.IAMRepository{ IAMRepository: eventstore.IAMRepository{

View File

@ -1,49 +0,0 @@
package view
import (
"github.com/caos/zitadel/internal/eventstore/v1/models"
org_model "github.com/caos/zitadel/internal/org/model"
org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
)
const (
orgTable = "adminapi.orgs"
)
func (v *View) OrgByID(orgID string) (*model.OrgView, error) {
return org_view.OrgByID(v.Db, orgTable, orgID)
}
func (v *View) SearchOrgs(query *org_model.OrgSearchRequest) ([]*model.OrgView, uint64, error) {
return org_view.SearchOrgs(v.Db, orgTable, query)
}
func (v *View) PutOrg(org *model.OrgView, event *models.Event) error {
err := org_view.PutOrg(v.Db, orgTable, org)
if err != nil {
return err
}
return v.ProcessedOrgSequence(event)
}
func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
return v.latestFailedEvent(orgTable, sequence)
}
func (v *View) ProcessedOrgFailedEvent(failedEvent *repository.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}
func (v *View) UpdateOrgSpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(orgTable)
}
func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) {
return v.latestSequence(orgTable)
}
func (v *View) ProcessedOrgSequence(event *models.Event) error {
return v.saveCurrentSequence(orgTable, event)
}

View File

@ -4,14 +4,8 @@ import (
"context" "context"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
org_model "github.com/caos/zitadel/internal/org/model"
) )
type OrgRepository interface { type OrgRepository interface {
IsOrgUnique(ctx context.Context, name, domain string) (bool, error)
OrgByID(ctx context.Context, id string) (*org_model.OrgView, error)
SearchOrgs(ctx context.Context, query *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error)
GetOrgIAMPolicyByID(ctx context.Context, id string) (*iam_model.OrgIAMPolicyView, error) GetOrgIAMPolicyByID(ctx context.Context, id string) (*iam_model.OrgIAMPolicyView, error)
} }

View File

@ -22,6 +22,7 @@ import (
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/telemetry/metrics" "github.com/caos/zitadel/internal/telemetry/metrics"
"github.com/caos/zitadel/internal/telemetry/metrics/otel" "github.com/caos/zitadel/internal/telemetry/metrics/otel"
"github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/telemetry/tracing"
@ -58,11 +59,20 @@ type admin interface {
GetSpoolerDiv(database, viewName string) int64 GetSpoolerDiv(database, viewName string) int64
} }
func Create(config Config, authZ authz.Config, authZRepo *authz_es.EsRepository, authRepo *auth_es.EsRepository, adminRepo *admin_es.EsRepository, sd systemdefaults.SystemDefaults) *API { func Create(config Config, authZ authz.Config, q *query.Queries, authZRepo *authz_es.EsRepository, authRepo *auth_es.EsRepository, adminRepo *admin_es.EsRepository, sd systemdefaults.SystemDefaults) *API {
api := &API{ api := &API{
serverPort: config.GRPC.ServerPort, serverPort: config.GRPC.ServerPort,
} }
api.verifier = authz.Start(authZRepo)
repo := struct {
authz_es.EsRepository
query.Queries
}{
*authZRepo,
*q,
}
api.verifier = authz.Start(&repo)
api.health = authZRepo api.health = authZRepo
api.auth = authRepo api.auth = authRepo
api.admin = adminRepo api.admin = adminRepo

View File

@ -5,20 +5,21 @@ import (
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object" "github.com/caos/zitadel/internal/api/grpc/object"
org_grpc "github.com/caos/zitadel/internal/api/grpc/org"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
org_grpc "github.com/caos/zitadel/internal/api/grpc/org"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin" admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
obj_pb "github.com/caos/zitadel/pkg/grpc/object"
"google.golang.org/protobuf/types/known/timestamppb"
) )
func (s *Server) IsOrgUnique(ctx context.Context, req *admin_pb.IsOrgUniqueRequest) (*admin_pb.IsOrgUniqueResponse, error) { func (s *Server) IsOrgUnique(ctx context.Context, req *admin_pb.IsOrgUniqueRequest) (*admin_pb.IsOrgUniqueResponse, error) {
isUnique, err := s.org.IsOrgUnique(ctx, req.Name, req.Domain) isUnique, err := s.query.IsOrgUnique(ctx, req.Name, req.Domain)
return &admin_pb.IsOrgUniqueResponse{IsUnique: isUnique}, err return &admin_pb.IsOrgUniqueResponse{IsUnique: isUnique}, err
} }
func (s *Server) GetOrgByID(ctx context.Context, req *admin_pb.GetOrgByIDRequest) (*admin_pb.GetOrgByIDResponse, error) { func (s *Server) GetOrgByID(ctx context.Context, req *admin_pb.GetOrgByIDRequest) (*admin_pb.GetOrgByIDResponse, error) {
org, err := s.org.OrgByID(ctx, req.Id) org, err := s.query.OrgByID(ctx, req.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -26,15 +27,22 @@ func (s *Server) GetOrgByID(ctx context.Context, req *admin_pb.GetOrgByIDRequest
} }
func (s *Server) ListOrgs(ctx context.Context, req *admin_pb.ListOrgsRequest) (*admin_pb.ListOrgsResponse, error) { func (s *Server) ListOrgs(ctx context.Context, req *admin_pb.ListOrgsRequest) (*admin_pb.ListOrgsResponse, error) {
query, err := listOrgRequestToModel(req) queries, err := listOrgRequestToModel(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
orgs, err := s.org.SearchOrgs(ctx, query) orgs, err := s.query.SearchOrgs(ctx, queries)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &admin_pb.ListOrgsResponse{Result: org_grpc.OrgViewsToPb(orgs.Result)}, nil return &admin_pb.ListOrgsResponse{
Result: org_grpc.OrgViewsToPb(orgs.Orgs),
Details: &obj_pb.ListDetails{
TotalResult: orgs.Count,
ProcessedSequence: orgs.Sequence,
ViewTimestamp: timestamppb.New(orgs.Timestamp),
},
}, nil
} }
func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (*admin_pb.SetUpOrgResponse, error) { func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (*admin_pb.SetUpOrgResponse, error) {

View File

@ -4,24 +4,37 @@ import (
"github.com/caos/zitadel/internal/api/grpc/object" "github.com/caos/zitadel/internal/api/grpc/object"
org_grpc "github.com/caos/zitadel/internal/api/grpc/org" org_grpc "github.com/caos/zitadel/internal/api/grpc/org"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/pkg/grpc/admin" "github.com/caos/zitadel/pkg/grpc/admin"
"github.com/caos/zitadel/pkg/grpc/org"
) )
func listOrgRequestToModel(req *admin.ListOrgsRequest) (*model.OrgSearchRequest, error) { func listOrgRequestToModel(req *admin.ListOrgsRequest) (*query.OrgSearchQueries, error) {
offset, limit, asc := object.ListQueryToModel(req.Query) offset, limit, asc := object.ListQueryToModel(req.Query)
queries, err := org_grpc.OrgQueriesToModel(req.Queries) queries, err := org_grpc.OrgQueriesToModel(req.Queries)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &model.OrgSearchRequest{ return &query.OrgSearchQueries{
Offset: offset, SearchRequest: query.SearchRequest{
Limit: limit, Offset: offset,
Asc: asc, Limit: limit,
SortingColumn: fieldNameToOrgColumn(req.SortingColumn),
Asc: asc,
},
Queries: queries, Queries: queries,
}, nil }, nil
} }
func fieldNameToOrgColumn(fieldName org.OrgFieldName) query.Column {
switch fieldName {
case org.OrgFieldName_ORG_FIELD_NAME_NAME:
return query.OrgColumnName
default:
return query.Column{}
}
}
func setUpOrgOrgToDomain(req *admin.SetUpOrgRequest_Org) *domain.Org { func setUpOrgOrgToDomain(req *admin.SetUpOrgRequest_Org) *domain.Org {
org := &domain.Org{ org := &domain.Org{
Name: req.Name, Name: req.Name,

View File

@ -10,18 +10,22 @@ import (
) )
func (s *Server) ListActions(ctx context.Context, req *mgmt_pb.ListActionsRequest) (*mgmt_pb.ListActionsResponse, error) { func (s *Server) ListActions(ctx context.Context, req *mgmt_pb.ListActionsRequest) (*mgmt_pb.ListActionsResponse, error) {
query, _ := listActionsToQuery(authz.GetCtxData(ctx).OrgID, req) query, err := listActionsToQuery(authz.GetCtxData(ctx).OrgID, req)
if err != nil {
return nil, err
}
actions, err := s.query.SearchActions(ctx, query) actions, err := s.query.SearchActions(ctx, query)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &mgmt_pb.ListActionsResponse{ return &mgmt_pb.ListActionsResponse{
Result: action_grpc.ActionsToPb(actions), Details: obj_grpc.ToListDetails(actions.Count, actions.Sequence, actions.Timestamp),
Result: action_grpc.ActionsToPb(actions.Actions),
}, nil }, nil
} }
func (s *Server) GetAction(ctx context.Context, req *mgmt_pb.GetActionRequest) (*mgmt_pb.GetActionResponse, error) { func (s *Server) GetAction(ctx context.Context, req *mgmt_pb.GetActionRequest) (*mgmt_pb.GetActionResponse, error) {
action, err := s.query.GetAction(ctx, req.Id, authz.GetCtxData(ctx).OrgID) action, err := s.query.GetActionByID(ctx, req.Id, authz.GetCtxData(ctx).OrgID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -30,10 +30,10 @@ func updateActionRequestToDomain(req *mgmt_pb.UpdateActionRequest) *domain.Actio
} }
} }
func listActionsToQuery(id string, req *mgmt_pb.ListActionsRequest) (_ *query.ActionSearchQueries, err error) { func listActionsToQuery(orgID string, req *mgmt_pb.ListActionsRequest) (_ *query.ActionSearchQueries, err error) {
offset, limit, asc := object.ListQueryToModel(req.Query) offset, limit, asc := object.ListQueryToModel(req.Query)
queries := make([]query.SearchQuery, len(req.Queries)+1) queries := make([]query.SearchQuery, len(req.Queries)+1)
queries[0], err = query.NewActionResourceOwnerQuery(id) queries[0], err = query.NewActionResourceOwnerQuery(orgID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -10,7 +10,7 @@ import (
) )
func (s *Server) GetFlow(ctx context.Context, req *mgmt_pb.GetFlowRequest) (*mgmt_pb.GetFlowResponse, error) { func (s *Server) GetFlow(ctx context.Context, req *mgmt_pb.GetFlowRequest) (*mgmt_pb.GetFlowResponse, error) {
flow, err := s.query.GetFlow(ctx, action_grpc.FlowTypeToDomain(req.Type)) flow, err := s.query.GetFlow(ctx, action_grpc.FlowTypeToDomain(req.Type), authz.GetCtxData(ctx).OrgID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,7 +17,7 @@ import (
) )
func (s *Server) GetMyOrg(ctx context.Context, req *mgmt_pb.GetMyOrgRequest) (*mgmt_pb.GetMyOrgResponse, error) { func (s *Server) GetMyOrg(ctx context.Context, req *mgmt_pb.GetMyOrgRequest) (*mgmt_pb.GetMyOrgResponse, error) {
org, err := s.org.OrgByID(ctx, authz.GetCtxData(ctx).OrgID) org, err := s.query.OrgByID(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -25,10 +25,11 @@ func (s *Server) GetMyOrg(ctx context.Context, req *mgmt_pb.GetMyOrgRequest) (*m
} }
func (s *Server) GetOrgByDomainGlobal(ctx context.Context, req *mgmt_pb.GetOrgByDomainGlobalRequest) (*mgmt_pb.GetOrgByDomainGlobalResponse, error) { func (s *Server) GetOrgByDomainGlobal(ctx context.Context, req *mgmt_pb.GetOrgByDomainGlobalRequest) (*mgmt_pb.GetOrgByDomainGlobalResponse, error) {
org, err := s.org.OrgByDomainGlobal(ctx, req.Domain) org, err := s.query.OrgByDomainGlobal(ctx, req.Domain)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &mgmt_pb.GetOrgByDomainGlobalResponse{Org: org_grpc.OrgViewToPb(org)}, nil return &mgmt_pb.GetOrgByDomainGlobalResponse{Org: org_grpc.OrgViewToPb(org)}, nil
} }

View File

@ -7,7 +7,6 @@ import (
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/pkg/grpc/object"
object_pb "github.com/caos/zitadel/pkg/grpc/object" object_pb "github.com/caos/zitadel/pkg/grpc/object"
) )
@ -69,7 +68,7 @@ func ToListDetails(
totalResult, totalResult,
processedSequence uint64, processedSequence uint64,
viewTimestamp time.Time, viewTimestamp time.Time,
) *object.ListDetails { ) *object_pb.ListDetails {
return &object_pb.ListDetails{ return &object_pb.ListDetails{
TotalResult: totalResult, TotalResult: totalResult,
ProcessedSequence: processedSequence, ProcessedSequence: processedSequence,
@ -79,53 +78,53 @@ func ToListDetails(
func TextMethodToModel(method object_pb.TextQueryMethod) domain.SearchMethod { func TextMethodToModel(method object_pb.TextQueryMethod) domain.SearchMethod {
switch method { switch method {
case object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS: case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS:
return domain.SearchMethodEquals return domain.SearchMethodEquals
case object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE: case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE:
return domain.SearchMethodEqualsIgnoreCase return domain.SearchMethodEqualsIgnoreCase
case object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH: case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH:
return domain.SearchMethodStartsWith return domain.SearchMethodStartsWith
case object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE: case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE:
return domain.SearchMethodStartsWithIgnoreCase return domain.SearchMethodStartsWithIgnoreCase
case object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS: case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS:
return domain.SearchMethodContains return domain.SearchMethodContains
case object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE: case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE:
return domain.SearchMethodContainsIgnoreCase return domain.SearchMethodContainsIgnoreCase
case object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH: case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH:
return domain.SearchMethodEndsWith return domain.SearchMethodEndsWith
case object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE: case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE:
return domain.SearchMethodEndsWithIgnoreCase return domain.SearchMethodEndsWithIgnoreCase
default: default:
return -1 return -1
} }
} }
func TextMethodToQuery(method object_pb.TextQueryMethod) query.TextComparison {
switch method {
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS:
return query.TextEquals
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE:
return query.TextEqualsIgnoreCase
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH:
return query.TextStartsWith
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE:
return query.TextStartsWithIgnoreCase
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS:
return query.TextContains
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE:
return query.TextContainsIgnoreCase
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH:
return query.TextEndsWith
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE:
return query.TextEndsWithIgnoreCase
default:
return -1
}
}
func ListQueryToModel(query *object_pb.ListQuery) (offset, limit uint64, asc bool) { func ListQueryToModel(query *object_pb.ListQuery) (offset, limit uint64, asc bool) {
if query == nil { if query == nil {
return return
} }
return query.Offset, uint64(query.Limit), query.Asc return query.Offset, uint64(query.Limit), query.Asc
} }
func TextMethodToQuery(method object_pb.TextQueryMethod) query.TextComparison {
switch method {
case object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS:
return query.TextEquals
case object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS_IGNORE_CASE:
return query.TextEqualsIgnore
case object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH:
return query.TextStartsWith
case object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH_IGNORE_CASE:
return query.TextStartsWithIgnore
case object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS:
return query.TextContains
case object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE:
return query.TextContainsIgnore
case object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH:
return query.TextEndsWith
case object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE:
return query.TextEndsWithIgnore
default:
return -1
}
}

View File

@ -5,12 +5,13 @@ import (
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/query"
grant_model "github.com/caos/zitadel/internal/usergrant/model" grant_model "github.com/caos/zitadel/internal/usergrant/model"
org_pb "github.com/caos/zitadel/pkg/grpc/org" org_pb "github.com/caos/zitadel/pkg/grpc/org"
) )
func OrgQueriesToModel(queries []*org_pb.OrgQuery) (_ []*org_model.OrgSearchQuery, err error) { func OrgQueriesToModel(queries []*org_pb.OrgQuery) (_ []query.SearchQuery, err error) {
q := make([]*org_model.OrgSearchQuery, len(queries)) q := make([]query.SearchQuery, len(queries))
for i, query := range queries { for i, query := range queries {
q[i], err = OrgQueryToModel(query) q[i], err = OrgQueryToModel(query)
if err != nil { if err != nil {
@ -20,22 +21,14 @@ func OrgQueriesToModel(queries []*org_pb.OrgQuery) (_ []*org_model.OrgSearchQuer
return q, nil return q, nil
} }
func OrgQueryToModel(query *org_pb.OrgQuery) (*org_model.OrgSearchQuery, error) { func OrgQueryToModel(apiQuery *org_pb.OrgQuery) (query.SearchQuery, error) {
switch q := query.Query.(type) { switch q := apiQuery.Query.(type) {
case *org_pb.OrgQuery_DomainQuery: case *org_pb.OrgQuery_DomainQuery:
return &org_model.OrgSearchQuery{ return query.NewOrgDomainSearchQuery(object.TextMethodToQuery(q.DomainQuery.Method), q.DomainQuery.Domain)
Key: org_model.OrgSearchKeyOrgDomain,
Method: object.TextMethodToModel(q.DomainQuery.Method),
Value: q.DomainQuery.Domain,
}, nil
case *org_pb.OrgQuery_NameQuery: case *org_pb.OrgQuery_NameQuery:
return &org_model.OrgSearchQuery{ return query.NewOrgNameSearchQuery(object.TextMethodToQuery(q.NameQuery.Method), q.NameQuery.Name)
Key: org_model.OrgSearchKeyOrgName,
Method: object.TextMethodToModel(q.NameQuery.Method),
Value: q.NameQuery.Name,
}, nil
default: default:
return nil, errors.ThrowInvalidArgument(nil, "ADMIN-vR9nC", "List.Query.Invalid") return nil, errors.ThrowInvalidArgument(nil, "ORG-vR9nC", "List.Query.Invalid")
} }
} }
@ -69,7 +62,7 @@ func OrgQueryToUserGrantQueryModel(query *org_pb.OrgQuery) (*grant_model.UserGra
} }
} }
func OrgViewsToPb(orgs []*org_model.OrgView) []*org_pb.Org { func OrgViewsToPb(orgs []*query.Org) []*org_pb.Org {
o := make([]*org_pb.Org, len(orgs)) o := make([]*org_pb.Org, len(orgs))
for i, org := range orgs { for i, org := range orgs {
o[i] = OrgViewToPb(org) o[i] = OrgViewToPb(org)
@ -77,7 +70,7 @@ func OrgViewsToPb(orgs []*org_model.OrgView) []*org_pb.Org {
return o return o
} }
func OrgViewToPb(org *org_model.OrgView) *org_pb.Org { func OrgViewToPb(org *query.Org) *org_pb.Org {
return &org_pb.Org{ return &org_pb.Org{
Id: org.ID, Id: org.ID,
State: OrgStateToPb(org.State), State: OrgStateToPb(org.State),
@ -113,11 +106,11 @@ func OrgToPb(org *grant_model.Org) *org_pb.Org {
} }
} }
func OrgStateToPb(state org_model.OrgState) org_pb.OrgState { func OrgStateToPb(state domain.OrgState) org_pb.OrgState {
switch state { switch state {
case org_model.OrgStateActive: case domain.OrgStateActive:
return org_pb.OrgState_ORG_STATE_ACTIVE return org_pb.OrgState_ORG_STATE_ACTIVE
case org_model.OrgStateInactive: case domain.OrgStateInactive:
return org_pb.OrgState_ORG_STATE_INACTIVE return org_pb.OrgState_ORG_STATE_INACTIVE
default: default:
return org_pb.OrgState_ORG_STATE_UNSPECIFIED return org_pb.OrgState_ORG_STATE_UNSPECIFIED

View File

@ -88,7 +88,7 @@ func (o *OPStorage) ValidateJWTProfileScopes(ctx context.Context, subject string
scope := scopes[i] scope := scopes[i]
if strings.HasPrefix(scope, authreq_model.OrgDomainPrimaryScope) { if strings.HasPrefix(scope, authreq_model.OrgDomainPrimaryScope) {
var orgID string var orgID string
org, err := o.repo.OrgByPrimaryDomain(strings.TrimPrefix(scope, authreq_model.OrgDomainPrimaryScope)) org, err := o.query.OrgByDomainGlobal(ctx, strings.TrimPrefix(scope, authreq_model.OrgDomainPrimaryScope))
if err == nil { if err == nil {
orgID = org.ID orgID = org.ID
} }
@ -310,14 +310,18 @@ func (o *OPStorage) assertUserMetaData(ctx context.Context, userID string) (map[
} }
func (o *OPStorage) assertUserResourceOwner(ctx context.Context, userID string) (map[string]string, error) { func (o *OPStorage) assertUserResourceOwner(ctx context.Context, userID string) (map[string]string, error) {
resourceOwner, err := o.repo.OrgByUserID(ctx, userID) user, err := o.repo.UserByID(ctx, userID)
if err != nil {
return nil, err
}
resourceOwner, err := o.query.OrgByID(ctx, user.ResourceOwner)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return map[string]string{ return map[string]string{
ClaimResourceOwner + "id": resourceOwner.AggregateID, ClaimResourceOwner + "id": resourceOwner.ID,
ClaimResourceOwner + "name": resourceOwner.Name, ClaimResourceOwner + "name": resourceOwner.Name,
ClaimResourceOwner + "primary_domain": resourceOwner.PrimaryDomain, ClaimResourceOwner + "primary_domain": resourceOwner.Domain,
}, nil }, nil
} }

View File

@ -17,9 +17,8 @@ import (
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
"github.com/caos/zitadel/internal/id" "github.com/caos/zitadel/internal/id"
org_model "github.com/caos/zitadel/internal/org/model"
org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
project_view_model "github.com/caos/zitadel/internal/project/repository/view/model" project_view_model "github.com/caos/zitadel/internal/project/repository/view/model"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/repository/iam" "github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/telemetry/tracing"
user_model "github.com/caos/zitadel/internal/user/model" user_model "github.com/caos/zitadel/internal/user/model"
@ -87,8 +86,8 @@ type userCommandProvider interface {
} }
type orgViewProvider interface { type orgViewProvider interface {
OrgByID(string) (*org_view_model.OrgView, error) OrgByID(context.Context, string) (*query.Org, error)
OrgByPrimaryDomain(string) (*org_view_model.OrgView, error) OrgByDomainGlobal(context.Context, string) (*query.Org, error)
} }
type userGrantProvider interface { type userGrantProvider interface {
@ -935,7 +934,7 @@ func setOrgID(orgViewProvider orgViewProvider, request *domain.AuthRequest) erro
return nil return nil
} }
org, err := orgViewProvider.OrgByPrimaryDomain(primaryDomain) org, err := orgViewProvider.OrgByDomainGlobal(context.TODO(), primaryDomain)
if err != nil { if err != nil {
return err return err
} }
@ -1032,7 +1031,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
return user_view_model.UserSessionToModel(&sessionCopy, provider.PrefixAvatarURL()), nil return user_view_model.UserSessionToModel(&sessionCopy, provider.PrefixAvatarURL()), nil
} }
func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, orgViewProvider orgViewProvider, lockoutPolicyProvider lockoutPolicyViewProvider, userID string) (*user_model.UserView, error) { func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, queries orgViewProvider, lockoutPolicyProvider lockoutPolicyViewProvider, userID string) (*user_model.UserView, error) {
// PLANNED: Check LockoutPolicy // PLANNED: Check LockoutPolicy
user, err := userByID(ctx, userViewProvider, userEventProvider, userID) user, err := userByID(ctx, userViewProvider, userEventProvider, userID)
if err != nil { if err != nil {
@ -1048,11 +1047,11 @@ func activeUserByID(ctx context.Context, userViewProvider userViewProvider, user
if !(user.State == user_model.UserStateActive || user.State == user_model.UserStateInitial) { if !(user.State == user_model.UserStateActive || user.State == user_model.UserStateInitial) {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-FJ262", "Errors.User.NotActive") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-FJ262", "Errors.User.NotActive")
} }
org, err := orgViewProvider.OrgByID(user.ResourceOwner) org, err := queries.OrgByID(context.TODO(), user.ResourceOwner)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if org.State != int32(org_model.OrgStateActive) { if org.State != domain.OrgStateActive {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-Zws3s", "Errors.User.NotActive") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-Zws3s", "Errors.User.NotActive")
} }
return user, nil return user, nil

View File

@ -8,19 +8,17 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/auth_request/repository/cache" "github.com/caos/zitadel/internal/auth_request/repository/cache"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models" es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
org_model "github.com/caos/zitadel/internal/org/model"
org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
proj_view_model "github.com/caos/zitadel/internal/project/repository/view/model" proj_view_model "github.com/caos/zitadel/internal/project/repository/view/model"
"github.com/caos/zitadel/internal/query"
user_model "github.com/caos/zitadel/internal/user/model" user_model "github.com/caos/zitadel/internal/user/model"
user_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" user_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
user_view_model "github.com/caos/zitadel/internal/user/repository/view/model" user_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@ -186,28 +184,28 @@ func (m *mockViewUser) PrefixAvatarURL() string {
} }
type mockViewOrg struct { type mockViewOrg struct {
State org_model.OrgState State domain.OrgState
} }
func (m *mockViewOrg) OrgByID(string) (*org_view_model.OrgView, error) { func (m *mockViewOrg) OrgByID(context.Context, string) (*query.Org, error) {
return &org_view_model.OrgView{ return &query.Org{
State: int32(m.State), State: m.State,
}, nil }, nil
} }
func (m *mockViewOrg) OrgByPrimaryDomain(string) (*org_view_model.OrgView, error) { func (m *mockViewOrg) OrgByDomainGlobal(context.Context, string) (*query.Org, error) {
return &org_view_model.OrgView{ return &query.Org{
State: int32(m.State), State: m.State,
}, nil }, nil
} }
type mockViewErrOrg struct{} type mockViewErrOrg struct{}
func (m *mockViewErrOrg) OrgByID(string) (*org_view_model.OrgView, error) { func (m *mockViewErrOrg) OrgByID(context.Context, string) (*query.Org, error) {
return nil, errors.ThrowInternal(nil, "id", "internal error") return nil, errors.ThrowInternal(nil, "id", "internal error")
} }
func (m *mockViewErrOrg) OrgByPrimaryDomain(string) (*org_view_model.OrgView, error) { func (m *mockViewErrOrg) OrgByDomainGlobal(context.Context, string) (*query.Org, error) {
return nil, errors.ThrowInternal(nil, "id", "internal error") return nil, errors.ThrowInternal(nil, "id", "internal error")
} }
@ -439,7 +437,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
Type: user_es_model.UserDeactivated, Type: user_es_model.UserDeactivated,
}, },
}, },
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -460,7 +458,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
Type: user_es_model.UserLocked, Type: user_es_model.UserLocked,
}, },
}, },
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -492,7 +490,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
fields{ fields{
userViewProvider: &mockViewUser{}, userViewProvider: &mockViewUser{},
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateInactive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateInactive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -511,7 +509,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordSet: true, PasswordSet: true,
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -528,7 +526,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userSessionViewProvider: &mockViewErrUserSession{}, userSessionViewProvider: &mockViewErrUserSession{},
userViewProvider: &mockViewUser{}, userViewProvider: &mockViewUser{},
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -548,7 +546,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordSet: true, PasswordSet: true,
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -569,7 +567,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordlessInitRequired: true, PasswordlessInitRequired: true,
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
MultiFactorCheckLifeTime: 10 * time.Hour, MultiFactorCheckLifeTime: 10 * time.Hour,
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
@ -589,7 +587,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}}, PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}},
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -610,7 +608,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}}, PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}},
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -642,7 +640,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
MultiFactorCheckLifeTime: 10 * time.Hour, MultiFactorCheckLifeTime: 10 * time.Hour,
}, },
args{&domain.AuthRequest{ args{&domain.AuthRequest{
@ -668,7 +666,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
}, },
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false}, args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
[]domain.NextStep{&domain.InitPasswordStep{}}, []domain.NextStep{&domain.InitPasswordStep{}},
@ -690,7 +688,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
ShowLockOutFailures: true, ShowLockOutFailures: true,
}, },
}, },
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{&domain.AuthRequest{UserID: "UserID", SelectedIDPConfigID: "IDPConfigID"}, false}, args{&domain.AuthRequest{UserID: "UserID", SelectedIDPConfigID: "IDPConfigID"}, false},
@ -709,7 +707,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
projectProvider: &mockProject{}, projectProvider: &mockProject{},
loginPolicyProvider: &mockLoginPolicy{ loginPolicyProvider: &mockLoginPolicy{
@ -742,7 +740,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
PasswordSet: true, PasswordSet: true,
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -767,7 +765,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
projectProvider: &mockProject{}, projectProvider: &mockProject{},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
@ -801,7 +799,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelMultiFactor), MFAMaxSetUp: int32(model.MFALevelMultiFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -834,7 +832,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -868,7 +866,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -905,7 +903,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -936,7 +934,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -967,7 +965,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,
@ -998,7 +996,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
projectProvider: &mockProject{}, projectProvider: &mockProject{},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
@ -1032,7 +1030,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
projectProvider: &mockProject{}, projectProvider: &mockProject{},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
@ -1067,7 +1065,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userGrantProvider: &mockUserGrants{ userGrantProvider: &mockUserGrants{
roleCheck: true, roleCheck: true,
userGrants: 0, userGrants: 0,
@ -1105,7 +1103,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userGrantProvider: &mockUserGrants{ userGrantProvider: &mockUserGrants{
roleCheck: true, roleCheck: true,
userGrants: 2, userGrants: 2,
@ -1143,7 +1141,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
projectProvider: &mockProject{ projectProvider: &mockProject{
projectCheck: true, projectCheck: true,
@ -1181,7 +1179,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
userGrantProvider: &mockUserGrants{}, userGrantProvider: &mockUserGrants{},
projectProvider: &mockProject{ projectProvider: &mockProject{
projectCheck: true, projectCheck: true,
@ -1223,7 +1221,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
}, },
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
SecondFactorCheckLifeTime: 18 * time.Hour, SecondFactorCheckLifeTime: 18 * time.Hour,
}, },
args{ args{
@ -1249,7 +1247,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
MFAMaxSetUp: int32(model.MFALevelSecondFactor), MFAMaxSetUp: int32(model.MFALevelSecondFactor),
}, },
userEventProvider: &mockEventUser{}, userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive}, orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
lockoutPolicyProvider: &mockLockoutPolicy{ lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{ policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true, ShowLockOutFailures: true,

View File

@ -14,10 +14,7 @@ import (
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/repository/iam" "github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/telemetry/tracing"
) )
const ( const (
@ -32,38 +29,6 @@ type OrgRepository struct {
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
} }
func (repo *OrgRepository) SearchOrgs(ctx context.Context, request *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) {
err := request.EnsureLimit(repo.SearchLimit)
if err != nil {
return nil, err
}
sequence, err := repo.View.GetLatestOrgSequence()
logging.Log("EVENT-7Udhz").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("could not read latest org sequence")
members, count, err := repo.View.SearchOrgs(request)
if err != nil {
return nil, err
}
result := &org_model.OrgSearchResult{
Offset: request.Offset,
Limit: request.Limit,
TotalResult: count,
Result: model.OrgsToModel(members),
}
if err == nil {
result.Sequence = sequence.CurrentSequence
result.Timestamp = sequence.LastSuccessfulSpoolerRun
}
return result, nil
}
func (repo *OrgRepository) OrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) {
org, err := repo.View.OrgByPrimaryDomain(primaryDomain)
if err != nil {
return nil, err
}
return model.OrgToModel(org), nil
}
func (repo *OrgRepository) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) { func (repo *OrgRepository) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) {
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID) orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
if err != nil { if err != nil {

View File

@ -12,12 +12,11 @@ import (
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "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/models"
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model"
key_model "github.com/caos/zitadel/internal/key/model" key_model "github.com/caos/zitadel/internal/key/model"
key_view_model "github.com/caos/zitadel/internal/key/repository/view/model" key_view_model "github.com/caos/zitadel/internal/key/repository/view/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/model"
usr_view "github.com/caos/zitadel/internal/user/repository/view" usr_view "github.com/caos/zitadel/internal/user/repository/view"
@ -308,18 +307,6 @@ func (repo *UserRepo) GetMyMetadataByKey(ctx context.Context, key string) (*doma
return iam_model.MetadataViewToDomain(data), nil return iam_model.MetadataViewToDomain(data), nil
} }
func (repo *UserRepo) OrgByUserID(ctx context.Context, userID string) (*domain.Org, error) {
user, err := repo.View.UserByID(userID)
if err != nil {
return nil, err
}
org, err := repo.View.OrgByID(user.ResourceOwner)
if err != nil {
return nil, err
}
return org_model.OrgToDomain(org), nil
}
func (repo *UserRepo) SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error) { func (repo *UserRepo) SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error) {
req := new(domain.MetadataSearchRequest) req := new(domain.MetadataSearchRequest)
return repo.searchUserMetadata(userID, "", req) return repo.searchUserMetadata(userID, "", req)

View File

@ -6,13 +6,12 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
authz_repo "github.com/caos/zitadel/internal/authz/repository/eventsourcing" authz_repo "github.com/caos/zitadel/internal/authz/repository/eventsourcing"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
org_model "github.com/caos/zitadel/internal/org/model"
org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/telemetry/tracing"
user_model "github.com/caos/zitadel/internal/user/model" user_model "github.com/caos/zitadel/internal/user/model"
user_view_model "github.com/caos/zitadel/internal/user/repository/view/model" user_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@ -27,6 +26,8 @@ type UserGrantRepo struct {
Auth authz.Config Auth authz.Config
AuthZRepo *authz_repo.EsRepository AuthZRepo *authz_repo.EsRepository
PrefixAvatarURL string PrefixAvatarURL string
Query *query.Queries
} }
func (repo *UserGrantRepo) SearchMyUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) { func (repo *UserGrantRepo) SearchMyUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) {
@ -198,22 +199,28 @@ func (repo *UserGrantRepo) SearchMyProjectPermissions(ctx context.Context) ([]st
} }
func (repo *UserGrantRepo) SearchAdminOrgs(request *grant_model.UserGrantSearchRequest) (*grant_model.ProjectOrgSearchResponse, error) { func (repo *UserGrantRepo) SearchAdminOrgs(request *grant_model.UserGrantSearchRequest) (*grant_model.ProjectOrgSearchResponse, error) {
searchRequest := &org_model.OrgSearchRequest{ searchRequest := query.OrgSearchQueries{
SortingColumn: org_model.OrgSearchKeyOrgNameIgnoreCase, SearchRequest: query.SearchRequest{
Asc: true, SortingColumn: query.OrgColumnName,
Asc: true,
},
} }
if len(request.Queries) > 0 { if len(request.Queries) > 0 {
for _, q := range request.Queries { for _, q := range request.Queries {
if q.Key == grant_model.UserGrantSearchKeyOrgName { if q.Key == grant_model.UserGrantSearchKeyOrgName {
searchRequest.Queries = append(searchRequest.Queries, &org_model.OrgSearchQuery{Key: org_model.OrgSearchKeyOrgName, Method: q.Method, Value: q.Value}) nameQuery, err := query.NewOrgNameSearchQuery(query.TextComparisonFromMethod(q.Method), q.Value.(string))
if err != nil {
return nil, err
}
searchRequest.Queries = append(searchRequest.Queries, nameQuery)
} }
} }
} }
orgs, count, err := repo.View.SearchOrgs(searchRequest) orgs, err := repo.Query.SearchOrgs(context.TODO(), &searchRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return orgRespToOrgResp(orgs, count), nil return orgRespToOrgResp(orgs), nil
} }
func (repo *UserGrantRepo) IsIamAdmin(ctx context.Context) (bool, error) { func (repo *UserGrantRepo) IsIamAdmin(ctx context.Context) (bool, error) {
@ -244,7 +251,7 @@ func (repo *UserGrantRepo) userOrg(ctxData authz.CtxData) (*grant_model.ProjectO
if err != nil { if err != nil {
return nil, err return nil, err
} }
org, err := repo.View.OrgByID(user.ResourceOwner) org, err := repo.Query.OrgByID(context.TODO(), user.ResourceOwner)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -300,12 +307,12 @@ func grantRespToOrgResp(grants *grant_model.UserGrantSearchResponse) *grant_mode
return resp return resp
} }
func orgRespToOrgResp(orgs []*org_view_model.OrgView, count uint64) *grant_model.ProjectOrgSearchResponse { func orgRespToOrgResp(orgs *query.Orgs) *grant_model.ProjectOrgSearchResponse {
resp := &grant_model.ProjectOrgSearchResponse{ resp := &grant_model.ProjectOrgSearchResponse{
TotalResult: count, TotalResult: orgs.Count,
} }
resp.Result = make([]*grant_model.Org, len(orgs)) resp.Result = make([]*grant_model.Org, len(orgs.Orgs))
for i, o := range orgs { for i, o := range orgs.Orgs {
resp.Result[i] = &grant_model.Org{OrgID: o.ID, OrgName: o.Name} resp.Result[i] = &grant_model.Org{OrgID: o.ID, OrgName: o.Name}
} }
return resp return resp

View File

@ -45,8 +45,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount, es}, handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount, es},
keyChan), keyChan),
newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}), newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}),
newOrg(
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
newUserGrant( newUserGrant(
handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es},
systemDefaults.IamID), systemDefaults.IamID),

View File

@ -1,111 +0,0 @@
package handler
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/org/repository/view"
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/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
)
const (
orgTable = "auth.orgs"
)
type Org struct {
handler
subscription *v1.Subscription
}
func newOrg(handler handler) *Org {
h := &Org{
handler: handler,
}
h.subscribe()
return h
}
func (o *Org) subscribe() {
o.subscription = o.es.Subscribe(o.AggregateTypes()...)
go func() {
for event := range o.subscription.Events {
query.ReduceEvent(o, event)
}
}()
}
func (o *Org) ViewModel() string {
return orgTable
}
func (o *Org) Subscription() *v1.Subscription {
return o.subscription
}
func (_ *Org) AggregateTypes() []es_models.AggregateType {
return []es_models.AggregateType{model.OrgAggregate}
}
func (o *Org) CurrentSequence() (uint64, error) {
sequence, err := o.view.GetLatestOrgSequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (o *Org) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := o.view.GetLatestOrgSequence()
if err != nil {
return nil, err
}
return view.OrgQuery(sequence.CurrentSequence), nil
}
func (o *Org) Reduce(event *es_models.Event) (err error) {
org := new(org_model.OrgView)
switch event.Type {
case model.OrgAdded:
err = org.AppendEvent(event)
case model.OrgChanged:
org, err = o.view.OrgByID(event.ResourceOwner)
if err != nil {
return err
}
err = org.AppendEvent(event)
case model.OrgDomainPrimarySet:
domain := new(org_model.OrgDomainView)
err = domain.SetData(event)
if err != nil {
return err
}
org, err = o.view.OrgByID(event.AggregateID)
if err != nil {
return err
}
org.Domain = domain.Domain
default:
return o.view.ProcessedOrgSequence(event)
}
if err != nil {
return err
}
return o.view.PutOrg(org, event)
}
func (o *Org) OnError(event *es_models.Event, spoolerErr error) error {
logging.LogWithFields("SPOOL-8siWS", "id", event.AggregateID).WithError(spoolerErr).Warn("something went wrong in org handler")
return spooler.HandleError(event, spoolerErr, o.view.GetLatestOrgFailedEvent, o.view.ProcessedOrgFailedEvent, o.view.ProcessedOrgSequence, o.errorCountUntilSkip)
}
func (o *Org) OnSuccess() error {
return spooler.HandleSuccess(o.view.UpdateOrgSpoolerRunTimestamp)
}

View File

@ -99,6 +99,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
userRepo, userRepo,
eventstore.AuthRequestRepo{ eventstore.AuthRequestRepo{
Command: command, Command: command,
OrgViewProvider: queries,
AuthRequests: authReq, AuthRequests: authReq,
View: view, View: view,
Eventstore: es, Eventstore: es,
@ -106,7 +107,6 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
UserViewProvider: view, UserViewProvider: view,
UserCommandProvider: command, UserCommandProvider: command,
UserEventProvider: &userRepo, UserEventProvider: &userRepo,
OrgViewProvider: view,
IDPProviderViewProvider: view, IDPProviderViewProvider: view,
LoginPolicyViewProvider: view, LoginPolicyViewProvider: view,
LockoutPolicyViewProvider: view, LockoutPolicyViewProvider: view,
@ -154,6 +154,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
IamID: systemDefaults.IamID, IamID: systemDefaults.IamID,
Auth: authZ, Auth: authZ,
AuthZRepo: authZRepo, AuthZRepo: authZRepo,
Query: queries,
}, },
eventstore.OrgRepository{ eventstore.OrgRepository{
SearchLimit: conf.SearchLimit, SearchLimit: conf.SearchLimit,

View File

@ -1,53 +0,0 @@
package view
import (
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/org/model"
org_view "github.com/caos/zitadel/internal/org/repository/view"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
)
const (
orgTable = "auth.orgs"
)
func (v *View) OrgByID(orgID string) (*org_model.OrgView, error) {
return org_view.OrgByID(v.Db, orgTable, orgID)
}
func (v *View) OrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error) {
return org_view.OrgByPrimaryDomain(v.Db, orgTable, primaryDomain)
}
func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, uint64, error) {
return org_view.SearchOrgs(v.Db, orgTable, req)
}
func (v *View) PutOrg(org *org_model.OrgView, event *models.Event) error {
err := org_view.PutOrg(v.Db, orgTable, org)
if err != nil {
return err
}
return v.ProcessedOrgSequence(event)
}
func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
return v.latestFailedEvent(orgTable, sequence)
}
func (v *View) ProcessedOrgFailedEvent(failedEvent *repository.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}
func (v *View) UpdateOrgSpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(orgTable)
}
func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) {
return v.latestSequence(orgTable)
}
func (v *View) ProcessedOrgSequence(event *models.Event) error {
return v.saveCurrentSequence(orgTable, event)
}

View File

@ -2,13 +2,12 @@ package repository
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
org_model "github.com/caos/zitadel/internal/org/model"
) )
type OrgRepository interface { type OrgRepository interface {
OrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error)
GetOrgIAMPolicy(ctx context.Context, orgID string) (*iam_model.OrgIAMPolicyView, error) GetOrgIAMPolicy(ctx context.Context, orgID string) (*iam_model.OrgIAMPolicyView, error)
GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error)
GetIDPConfigByID(ctx context.Context, idpConfigID string) (*iam_model.IDPConfigView, error) GetIDPConfigByID(ctx context.Context, idpConfigID string) (*iam_model.IDPConfigView, error)

View File

@ -23,7 +23,6 @@ type UserRepository interface {
SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error) SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error)
SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error) SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error)
OrgByUserID(ctx context.Context, userID string) (*domain.Org, error)
} }
type myUserRepo interface { type myUserRepo interface {

View File

@ -109,11 +109,6 @@ func (repo *TokenVerifierRepo) ProjectIDAndOriginsByClientID(ctx context.Context
return app.ProjectID, app.OriginAllowList, nil return app.ProjectID, app.OriginAllowList, nil
} }
func (repo *TokenVerifierRepo) ExistsOrg(ctx context.Context, orgID string) error {
_, err := repo.View.OrgByID(orgID)
return err
}
func (repo *TokenVerifierRepo) CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error { func (repo *TokenVerifierRepo) CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error {
features, err := repo.View.FeaturesByAggregateID(orgID) features, err := repo.View.FeaturesByAggregateID(orgID)
if caos_errs.IsNotFound(err) { if caos_errs.IsNotFound(err) {

View File

@ -38,8 +38,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
handler{view, bulkLimit, configs.cycleDuration("UserMemberships"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("UserMemberships"), errorCount, es}),
newApplication( newApplication(
handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}),
newOrg(
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
newFeatures( newFeatures(
handler{view, bulkLimit, configs.cycleDuration("Features"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("Features"), errorCount, es}),
} }

View File

@ -1,103 +0,0 @@
package handler
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/org/repository/view"
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/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
)
const (
orgTable = "authz.orgs"
)
type Org struct {
handler
subscription *v1.Subscription
}
func newOrg(handler handler) *Org {
h := &Org{
handler: handler,
}
h.subscribe()
return h
}
func (k *Org) subscribe() {
k.subscription = k.es.Subscribe(k.AggregateTypes()...)
go func() {
for event := range k.subscription.Events {
query.ReduceEvent(k, event)
}
}()
}
func (o *Org) ViewModel() string {
return orgTable
}
func (o *Org) Subscription() *v1.Subscription {
return o.subscription
}
func (_ *Org) AggregateTypes() []es_models.AggregateType {
return []es_models.AggregateType{model.OrgAggregate}
}
func (o *Org) CurrentSequence() (uint64, error) {
sequence, err := o.view.GetLatestOrgSequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (o *Org) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := o.view.GetLatestOrgSequence()
if err != nil {
return nil, err
}
return view.OrgQuery(sequence.CurrentSequence), nil
}
func (o *Org) Reduce(event *es_models.Event) error {
org := new(org_model.OrgView)
switch event.Type {
case model.OrgAdded:
err := org.AppendEvent(event)
if err != nil {
return err
}
case model.OrgChanged:
org, err := o.view.OrgByID(event.ResourceOwner)
if err != nil {
return err
}
err = org.AppendEvent(event)
if err != nil {
return err
}
default:
return o.view.ProcessedOrgSequence(event)
}
return o.view.PutOrg(org, event)
}
func (o *Org) OnError(event *es_models.Event, spoolerErr error) error {
logging.LogWithFields("SPOOL-8siWS", "id", event.AggregateID).WithError(spoolerErr).Warn("something went wrong in org handler")
return spooler.HandleError(event, spoolerErr, o.view.GetLatestOrgFailedEvent, o.view.ProcessedOrgFailedEvent, o.view.ProcessedOrgSequence, o.errorCountUntilSkip)
}
func (o *Org) OnSuccess() error {
return spooler.HandleSuccess(o.view.UpdateOrgSpoolerRunTimestamp)
}

View File

@ -1,49 +0,0 @@
package view
import (
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/org/model"
org_view "github.com/caos/zitadel/internal/org/repository/view"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
)
const (
orgTable = "authz.orgs"
)
func (v *View) OrgByID(orgID string) (*org_model.OrgView, error) {
return org_view.OrgByID(v.Db, orgTable, orgID)
}
func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, uint64, error) {
return org_view.SearchOrgs(v.Db, orgTable, req)
}
func (v *View) PutOrg(org *org_model.OrgView, event *models.Event) error {
err := org_view.PutOrg(v.Db, orgTable, org)
if err != nil {
return err
}
return v.ProcessedOrgSequence(event)
}
func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
return v.latestFailedEvent(orgTable, sequence)
}
func (v *View) ProcessedOrgFailedEvent(failedEvent *repository.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}
func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) {
return v.latestSequence(orgTable)
}
func (v *View) ProcessedOrgSequence(event *models.Event) error {
return v.saveCurrentSequence(orgTable, event)
}
func (v *View) UpdateOrgSpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(orgTable)
}

View File

@ -55,16 +55,26 @@ type Commands struct {
keyAlgorithm crypto.EncryptionAlgorithm keyAlgorithm crypto.EncryptionAlgorithm
privateKeyLifetime time.Duration privateKeyLifetime time.Duration
publicKeyLifetime time.Duration publicKeyLifetime time.Duration
tokenVerifier *authz.TokenVerifier tokenVerifier orgFeatureChecker
}
type orgFeatureChecker interface {
CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error
} }
type Config struct { type Config struct {
Eventstore types.SQLUser Eventstore types.SQLUser
} }
func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults, authZConfig authz.Config, staticStore static.Storage, authZRepo *authz_repo.EsRepository) (repo *Commands, err error) { func StartCommands(
es *eventstore.Eventstore,
defaults sd.SystemDefaults,
authZConfig authz.Config,
staticStore static.Storage,
authZRepo *authz_repo.EsRepository,
) (repo *Commands, err error) {
repo = &Commands{ repo = &Commands{
eventstore: eventstore, eventstore: es,
static: staticStore, static: staticStore,
idGenerator: id.SonyFlakeGenerator, idGenerator: id.SonyFlakeGenerator,
iamDomain: defaults.Domain, iamDomain: defaults.Domain,
@ -130,7 +140,7 @@ func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults
} }
repo.keyAlgorithm = keyAlgorithm repo.keyAlgorithm = keyAlgorithm
repo.tokenVerifier = authz.Start(authZRepo) repo.tokenVerifier = authZRepo
return repo, nil return repo, nil
} }

View File

@ -182,10 +182,10 @@ func GetMockSecretGenerator(t *testing.T) crypto.Generator {
return generator return generator
} }
func GetMockVerifier(t *testing.T, features ...string) *authz.TokenVerifier { func GetMockVerifier(t *testing.T, features ...string) *testVerifier {
return authz.Start(&testVerifier{ return &testVerifier{
features: features, features: features,
}) }
} }
type testVerifier struct { type testVerifier struct {

View File

@ -3,7 +3,6 @@ package command
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/repository/org" "github.com/caos/zitadel/internal/repository/org"
@ -124,7 +123,7 @@ func (c *Commands) checkLabelPolicyAllowed(ctx context.Context, resourceOwner st
if defaultPolicy.DisableWatermark != policy.DisableWatermark { if defaultPolicy.DisableWatermark != policy.DisableWatermark {
requiredFeatures = append(requiredFeatures, domain.FeatureLabelPolicyWatermark) requiredFeatures = append(requiredFeatures, domain.FeatureLabelPolicyWatermark)
} }
return authz.CheckOrgFeatures(ctx, c.tokenVerifier, resourceOwner, requiredFeatures...) return c.tokenVerifier.CheckOrgFeatures(ctx, resourceOwner, requiredFeatures...)
} }
func (c *Commands) ActivateLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) { func (c *Commands) ActivateLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {

View File

@ -7,7 +7,6 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -23,7 +22,7 @@ import (
func TestCommandSide_AddLabelPolicy(t *testing.T) { func TestCommandSide_AddLabelPolicy(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
tokenVerifier *authz.TokenVerifier tokenVerifier orgFeatureChecker
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
@ -260,7 +259,7 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
func TestCommandSide_ChangeLabelPolicy(t *testing.T) { func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
tokenVerifier *authz.TokenVerifier tokenVerifier orgFeatureChecker
} }
type args struct { type args struct {
ctx context.Context ctx context.Context

View File

@ -6,7 +6,6 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -131,7 +130,7 @@ func (c *Commands) checkLoginPolicyAllowed(ctx context.Context, resourceOwner st
if defaultPolicy.HidePasswordReset != policy.HidePasswordReset { if defaultPolicy.HidePasswordReset != policy.HidePasswordReset {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyPasswordReset) requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyPasswordReset)
} }
return authz.CheckOrgFeatures(ctx, c.tokenVerifier, resourceOwner, requiredFeatures...) return c.tokenVerifier.CheckOrgFeatures(ctx, resourceOwner, requiredFeatures...)
} }
func (c *Commands) RemoveLoginPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) { func (c *Commands) RemoveLoginPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {

View File

@ -6,7 +6,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -21,7 +20,7 @@ import (
func TestCommandSide_AddLoginPolicy(t *testing.T) { func TestCommandSide_AddLoginPolicy(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
tokenVerifier *authz.TokenVerifier tokenVerifier orgFeatureChecker
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
@ -217,7 +216,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
func TestCommandSide_ChangeLoginPolicy(t *testing.T) { func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
tokenVerifier *authz.TokenVerifier tokenVerifier orgFeatureChecker
} }
type args struct { type args struct {
ctx context.Context ctx context.Context

View File

@ -181,6 +181,26 @@ func AddDeleteStatement(conditions []handler.Condition, opts ...execOption) func
} }
} }
func NewArrayAppendCol(column string, value interface{}) handler.Column {
return handler.Column{
Name: column,
Value: value,
ParameterOpt: func(placeholder string) string {
return "array_append(" + column + ", " + placeholder + ")"
},
}
}
func NewArrayRemoveCol(column string, value interface{}) handler.Column {
return handler.Column{
Name: column,
Value: value,
ParameterOpt: func(placeholder string) string {
return "array_remove(" + column + ", " + placeholder + ")"
},
}
}
func columnsToQuery(cols []handler.Column) (names []string, parameters []string, values []interface{}) { func columnsToQuery(cols []handler.Column) (names []string, parameters []string, values []interface{}) {
names = make([]string, len(cols)) names = make([]string, len(cols))
values = make([]interface{}, len(cols)) values = make([]interface{}, len(cols))
@ -189,7 +209,9 @@ func columnsToQuery(cols []handler.Column) (names []string, parameters []string,
names[i] = col.Name names[i] = col.Name
values[i] = col.Value values[i] = col.Value
parameters[i] = "$" + strconv.Itoa(i+1) parameters[i] = "$" + strconv.Itoa(i+1)
if col.ParameterOpt != nil {
parameters[i] = col.ParameterOpt(parameters[i])
}
} }
return names, parameters, values return names, parameters, values
} }

View File

@ -1015,3 +1015,46 @@ func Test_columnsToWhere(t *testing.T) {
}) })
} }
} }
func TestParameterOpts(t *testing.T) {
type args struct {
column string
value interface{}
placeholder string
}
tests := []struct {
name string
args args
constructor func(column string, value interface{}) handler.Column
want string
}{
{
name: "NewArrayAppendCol",
args: args{
column: "testCol",
value: "val",
placeholder: "$1",
},
constructor: NewArrayAppendCol,
want: "array_append(testCol, $1)",
},
{
name: "NewArrayRemoveCol",
args: args{
column: "testCol",
value: "val",
placeholder: "$1",
},
constructor: NewArrayRemoveCol,
want: "array_remove(testCol, $1)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
col := tt.constructor(tt.args.column, tt.args.value)
if param := col.ParameterOpt(tt.args.placeholder); param != tt.want {
t.Errorf("constructor() = %v, want %v", param, tt.want)
}
})
}
}

View File

@ -2,8 +2,11 @@ package handler
import ( import (
"database/sql" "database/sql"
"encoding/json"
"errors" "errors"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
) )
@ -14,6 +17,12 @@ var (
ErrSomeStmtsFailed = errors.New("some statements failed") ErrSomeStmtsFailed = errors.New("some statements failed")
) )
type Statements []Statement
func (stmts Statements) Len() int { return len(stmts) }
func (stmts Statements) Swap(i, j int) { stmts[i], stmts[j] = stmts[j], stmts[i] }
func (stmts Statements) Less(i, j int) bool { return stmts[i].Sequence < stmts[j].Sequence }
type Statement struct { type Statement struct {
AggregateType eventstore.AggregateType AggregateType eventstore.AggregateType
Sequence uint64 Sequence uint64
@ -31,8 +40,9 @@ type Executer interface {
} }
type Column struct { type Column struct {
Name string Name string
Value interface{} Value interface{}
ParameterOpt func(string) string
} }
func NewCol(name string, value interface{}) Column { func NewCol(name string, value interface{}) Column {
@ -42,6 +52,15 @@ func NewCol(name string, value interface{}) Column {
} }
} }
func NewJSONCol(name string, value interface{}) Column {
marshalled, err := json.Marshal(value)
if err != nil {
logging.LogWithFields("HANDL-oFvsl", "column", name).WithError(err).Panic("unable to marshal column")
}
return NewCol(name, marshalled)
}
type Condition Column type Condition Column
func NewCond(name string, value interface{}) Condition { func NewCond(name string, value interface{}) Condition {

View File

@ -25,7 +25,6 @@ import (
"github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/i18n"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
iam_view "github.com/caos/zitadel/internal/iam/repository/view" iam_view "github.com/caos/zitadel/internal/iam/repository/view"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model" iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
@ -65,22 +64,6 @@ func (repo *OrgRepository) Languages(ctx context.Context) ([]language.Tag, error
return repo.supportedLangs, nil return repo.supportedLangs, nil
} }
func (repo *OrgRepository) OrgByID(ctx context.Context, id string) (*org_model.OrgView, error) {
org, err := repo.View.OrgByID(id)
if err != nil {
return nil, err
}
return model.OrgToModel(org), nil
}
func (repo *OrgRepository) OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.OrgView, error) {
verifiedDomain, err := repo.View.VerifiedOrgDomain(domain)
if err != nil {
return nil, err
}
return repo.OrgByID(ctx, verifiedDomain.OrgID)
}
func (repo *OrgRepository) GetMyOrgIamPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) { func (repo *OrgRepository) GetMyOrgIamPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error) {
policy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID) policy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
@ -93,7 +76,7 @@ func (repo *OrgRepository) GetMyOrgIamPolicy(ctx context.Context) (*iam_model.Or
if err != nil { if err != nil {
return nil, err return nil, err
} }
return iam_es_model.OrgIAMViewToModel(policy), err return iam_view_model.OrgIAMViewToModel(policy), err
} }
func (repo *OrgRepository) SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error) { func (repo *OrgRepository) SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error) {
@ -233,7 +216,7 @@ func (repo *OrgRepository) GetLabelPolicy(ctx context.Context) (*iam_model.Label
if err != nil { if err != nil {
return nil, err return nil, err
} }
return iam_es_model.LabelPolicyViewToModel(policy), err return iam_view_model.LabelPolicyViewToModel(policy), err
} }
func (repo *OrgRepository) GetPreviewLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { func (repo *OrgRepository) GetPreviewLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) {
@ -248,7 +231,7 @@ func (repo *OrgRepository) GetPreviewLabelPolicy(ctx context.Context) (*iam_mode
if err != nil { if err != nil {
return nil, err return nil, err
} }
return iam_es_model.LabelPolicyViewToModel(policy), err return iam_view_model.LabelPolicyViewToModel(policy), err
} }
func (repo *OrgRepository) GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) { func (repo *OrgRepository) GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) {
@ -265,7 +248,7 @@ func (repo *OrgRepository) getDefaultLabelPolicy(ctx context.Context, state doma
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.LabelPolicyView) policy = new(iam_view_model.LabelPolicyView)
} }
events, esErr := repo.getIAMEvents(ctx, policy.Sequence) events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -273,16 +256,16 @@ func (repo *OrgRepository) getDefaultLabelPolicy(ctx context.Context, state doma
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-28uLp").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-28uLp").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.LabelPolicyViewToModel(policy), nil return iam_view_model.LabelPolicyViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.LabelPolicyViewToModel(policy), nil return iam_view_model.LabelPolicyViewToModel(policy), nil
} }
} }
policy.Default = true policy.Default = true
return iam_es_model.LabelPolicyViewToModel(policy), nil return iam_view_model.LabelPolicyViewToModel(policy), nil
} }
func (repo *OrgRepository) GetLoginPolicy(ctx context.Context) (*iam_model.LoginPolicyView, error) { func (repo *OrgRepository) GetLoginPolicy(ctx context.Context) (*iam_model.LoginPolicyView, error) {
@ -291,7 +274,7 @@ func (repo *OrgRepository) GetLoginPolicy(ctx context.Context) (*iam_model.Login
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.LoginPolicyView) policy = new(iam_view_model.LoginPolicyView)
} }
events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence) events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -299,15 +282,15 @@ func (repo *OrgRepository) GetLoginPolicy(ctx context.Context) (*iam_model.Login
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-38iTr").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-38iTr").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.LoginPolicyViewToModel(policy), nil return iam_view_model.LoginPolicyViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.LoginPolicyViewToModel(policy), nil return iam_view_model.LoginPolicyViewToModel(policy), nil
} }
} }
return iam_es_model.LoginPolicyViewToModel(policy), nil return iam_view_model.LoginPolicyViewToModel(policy), nil
} }
func (repo *OrgRepository) GetIDPProvidersByIDPConfigID(ctx context.Context, aggregateID, idpConfigID string) ([]*iam_model.IDPProviderView, error) { func (repo *OrgRepository) GetIDPProvidersByIDPConfigID(ctx context.Context, aggregateID, idpConfigID string) ([]*iam_model.IDPProviderView, error) {
@ -324,7 +307,7 @@ func (repo *OrgRepository) GetDefaultLoginPolicy(ctx context.Context) (*iam_mode
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.LoginPolicyView) policy = new(iam_view_model.LoginPolicyView)
} }
events, esErr := repo.getIAMEvents(ctx, policy.Sequence) events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -332,16 +315,16 @@ func (repo *OrgRepository) GetDefaultLoginPolicy(ctx context.Context) (*iam_mode
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-28uLp").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-28uLp").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.LoginPolicyViewToModel(policy), nil return iam_view_model.LoginPolicyViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.LoginPolicyViewToModel(policy), nil return iam_view_model.LoginPolicyViewToModel(policy), nil
} }
} }
policy.Default = true policy.Default = true
return iam_es_model.LoginPolicyViewToModel(policy), nil return iam_view_model.LoginPolicyViewToModel(policy), nil
} }
func (repo *OrgRepository) SearchIDPProviders(ctx context.Context, request *iam_model.IDPProviderSearchRequest) (*iam_model.IDPProviderSearchResponse, error) { func (repo *OrgRepository) SearchIDPProviders(ctx context.Context, request *iam_model.IDPProviderSearchRequest) (*iam_model.IDPProviderSearchResponse, error) {
@ -368,7 +351,7 @@ func (repo *OrgRepository) SearchIDPProviders(ctx context.Context, request *iam_
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: iam_es_model.IDPProviderViewsToModel(providers), Result: iam_view_model.IDPProviderViewsToModel(providers),
} }
if sequenceErr == nil { if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence
@ -405,7 +388,7 @@ func (repo *OrgRepository) GetPasswordComplexityPolicy(ctx context.Context) (*ia
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.PasswordComplexityPolicyView) policy = new(iam_view_model.PasswordComplexityPolicyView)
} }
events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence) events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -413,15 +396,15 @@ func (repo *OrgRepository) GetPasswordComplexityPolicy(ctx context.Context) (*ia
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-1Bx8s").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-1Bx8s").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.PasswordComplexityViewToModel(policy), nil return iam_view_model.PasswordComplexityViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.PasswordComplexityViewToModel(policy), nil return iam_view_model.PasswordComplexityViewToModel(policy), nil
} }
} }
return iam_es_model.PasswordComplexityViewToModel(policy), nil return iam_view_model.PasswordComplexityViewToModel(policy), nil
} }
func (repo *OrgRepository) GetDefaultPasswordComplexityPolicy(ctx context.Context) (*iam_model.PasswordComplexityPolicyView, error) { func (repo *OrgRepository) GetDefaultPasswordComplexityPolicy(ctx context.Context) (*iam_model.PasswordComplexityPolicyView, error) {
@ -430,7 +413,7 @@ func (repo *OrgRepository) GetDefaultPasswordComplexityPolicy(ctx context.Contex
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.PasswordComplexityPolicyView) policy = new(iam_view_model.PasswordComplexityPolicyView)
} }
events, esErr := repo.getIAMEvents(ctx, policy.Sequence) events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -438,16 +421,16 @@ func (repo *OrgRepository) GetDefaultPasswordComplexityPolicy(ctx context.Contex
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-pL9sw").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-pL9sw").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.PasswordComplexityViewToModel(policy), nil return iam_view_model.PasswordComplexityViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.PasswordComplexityViewToModel(policy), nil return iam_view_model.PasswordComplexityViewToModel(policy), nil
} }
} }
policy.Default = true policy.Default = true
return iam_es_model.PasswordComplexityViewToModel(policy), nil return iam_view_model.PasswordComplexityViewToModel(policy), nil
} }
func (repo *OrgRepository) GetPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error) { func (repo *OrgRepository) GetPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error) {
@ -456,7 +439,7 @@ func (repo *OrgRepository) GetPasswordAgePolicy(ctx context.Context) (*iam_model
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.PasswordAgePolicyView) policy = new(iam_view_model.PasswordAgePolicyView)
} }
events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence) events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -464,15 +447,15 @@ func (repo *OrgRepository) GetPasswordAgePolicy(ctx context.Context) (*iam_model
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-5Mx7s").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-5Mx7s").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.PasswordAgeViewToModel(policy), nil return iam_view_model.PasswordAgeViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.PasswordAgeViewToModel(policy), nil return iam_view_model.PasswordAgeViewToModel(policy), nil
} }
} }
return iam_es_model.PasswordAgeViewToModel(policy), nil return iam_view_model.PasswordAgeViewToModel(policy), nil
} }
func (repo *OrgRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error) { func (repo *OrgRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*iam_model.PasswordAgePolicyView, error) {
@ -481,7 +464,7 @@ func (repo *OrgRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*ia
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.PasswordAgePolicyView) policy = new(iam_view_model.PasswordAgePolicyView)
} }
events, esErr := repo.getIAMEvents(ctx, policy.Sequence) events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -489,16 +472,16 @@ func (repo *OrgRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*ia
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-3I90s").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-3I90s").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.PasswordAgeViewToModel(policy), nil return iam_view_model.PasswordAgeViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.PasswordAgeViewToModel(policy), nil return iam_view_model.PasswordAgeViewToModel(policy), nil
} }
} }
policy.Default = true policy.Default = true
return iam_es_model.PasswordAgeViewToModel(policy), nil return iam_view_model.PasswordAgeViewToModel(policy), nil
} }
func (repo *OrgRepository) GetLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) { func (repo *OrgRepository) GetLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) {
@ -507,7 +490,7 @@ func (repo *OrgRepository) GetLockoutPolicy(ctx context.Context) (*iam_model.Loc
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.LockoutPolicyView) policy = new(iam_view_model.LockoutPolicyView)
} }
events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence) events, esErr := repo.getOrgEvents(ctx, repo.SystemDefaults.IamID, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -515,15 +498,15 @@ func (repo *OrgRepository) GetLockoutPolicy(ctx context.Context) (*iam_model.Loc
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-mS9od").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-mS9od").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.LockoutViewToModel(policy), nil return iam_view_model.LockoutViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.LockoutViewToModel(policy), nil return iam_view_model.LockoutViewToModel(policy), nil
} }
} }
return iam_es_model.LockoutViewToModel(policy), nil return iam_view_model.LockoutViewToModel(policy), nil
} }
func (repo *OrgRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) { func (repo *OrgRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_model.LockoutPolicyView, error) {
@ -532,7 +515,7 @@ func (repo *OrgRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_mo
return nil, viewErr return nil, viewErr
} }
if errors.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
policy = new(iam_es_model.LockoutPolicyView) policy = new(iam_view_model.LockoutPolicyView)
} }
events, esErr := repo.getIAMEvents(ctx, policy.Sequence) events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
if errors.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
@ -540,16 +523,16 @@ func (repo *OrgRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_mo
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-2Ms9f").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-2Ms9f").WithError(esErr).Debug("error retrieving new events")
return iam_es_model.LockoutViewToModel(policy), nil return iam_view_model.LockoutViewToModel(policy), nil
} }
policyCopy := *policy policyCopy := *policy
for _, event := range events { for _, event := range events {
if err := policyCopy.AppendEvent(event); err != nil { if err := policyCopy.AppendEvent(event); err != nil {
return iam_es_model.LockoutViewToModel(policy), nil return iam_view_model.LockoutViewToModel(policy), nil
} }
} }
policy.Default = true policy.Default = true
return iam_es_model.LockoutViewToModel(policy), nil return iam_view_model.LockoutViewToModel(policy), nil
} }
func (repo *OrgRepository) GetPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) { func (repo *OrgRepository) GetPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) {
@ -557,7 +540,7 @@ func (repo *OrgRepository) GetPrivacyPolicy(ctx context.Context) (*iam_model.Pri
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
return repo.GetDefaultPrivacyPolicy(ctx) return repo.GetDefaultPrivacyPolicy(ctx)
} }
return iam_es_model.PrivacyViewToModel(policy), nil return iam_view_model.PrivacyViewToModel(policy), nil
} }
func (repo *OrgRepository) GetDefaultPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) { func (repo *OrgRepository) GetDefaultPrivacyPolicy(ctx context.Context) (*iam_model.PrivacyPolicyView, error) {
@ -566,7 +549,7 @@ func (repo *OrgRepository) GetDefaultPrivacyPolicy(ctx context.Context) (*iam_mo
return nil, err return nil, err
} }
policy.Default = true policy.Default = true
return iam_es_model.PrivacyViewToModel(policy), nil return iam_view_model.PrivacyViewToModel(policy), nil
} }
func (repo *OrgRepository) GetDefaultMailTemplate(ctx context.Context) (*iam_model.MailTemplateView, error) { func (repo *OrgRepository) GetDefaultMailTemplate(ctx context.Context) (*iam_model.MailTemplateView, error) {
@ -575,7 +558,7 @@ func (repo *OrgRepository) GetDefaultMailTemplate(ctx context.Context) (*iam_mod
return nil, err return nil, err
} }
template.Default = true template.Default = true
return iam_es_model.MailTemplateViewToModel(template), err return iam_view_model.MailTemplateViewToModel(template), err
} }
func (repo *OrgRepository) GetMailTemplate(ctx context.Context) (*iam_model.MailTemplateView, error) { func (repo *OrgRepository) GetMailTemplate(ctx context.Context) (*iam_model.MailTemplateView, error) {
@ -590,7 +573,7 @@ func (repo *OrgRepository) GetMailTemplate(ctx context.Context) (*iam_model.Mail
if err != nil { if err != nil {
return nil, err return nil, err
} }
return iam_es_model.MailTemplateViewToModel(template), err return iam_view_model.MailTemplateViewToModel(template), err
} }
func (repo *OrgRepository) GetDefaultMessageText(ctx context.Context, textType, lang string) (*domain.CustomMessageText, error) { func (repo *OrgRepository) GetDefaultMessageText(ctx context.Context, textType, lang string) (*domain.CustomMessageText, error) {
@ -644,7 +627,7 @@ func (repo *OrgRepository) GetMessageText(ctx context.Context, orgID, textType,
if len(texts) == 0 { if len(texts) == 0 {
return repo.GetDefaultMessageText(ctx, textType, lang) return repo.GetDefaultMessageText(ctx, textType, lang)
} }
return iam_es_model.CustomTextViewsToMessageDomain(repo.SystemDefaults.IamID, lang, texts), err return iam_view_model.CustomTextViewsToMessageDomain(repo.SystemDefaults.IamID, lang, texts), err
} }
func (repo *OrgRepository) GetDefaultLoginTexts(ctx context.Context, lang string) (*domain.CustomLoginText, error) { func (repo *OrgRepository) GetDefaultLoginTexts(ctx context.Context, lang string) (*domain.CustomLoginText, error) {
@ -694,7 +677,7 @@ func (repo *OrgRepository) GetLoginTexts(ctx context.Context, orgID, lang string
if err != nil { if err != nil {
return nil, err return nil, err
} }
return iam_es_model.CustomTextViewsToLoginDomain(repo.SystemDefaults.IamID, lang, texts), err return iam_view_model.CustomTextViewsToLoginDomain(repo.SystemDefaults.IamID, lang, texts), err
} }
func (repo *OrgRepository) getOrgChanges(ctx context.Context, orgID string, lastSequence uint64, limit uint64, sortAscending bool, auditLogRetention time.Duration) (*org_model.OrgChanges, error) { func (repo *OrgRepository) getOrgChanges(ctx context.Context, orgID string, lastSequence uint64, limit uint64, sortAscending bool, auditLogRetention time.Duration) (*org_model.OrgChanges, error) {

View File

@ -4,24 +4,21 @@ import (
"context" "context"
"time" "time"
"github.com/golang/protobuf/ptypes"
"github.com/caos/zitadel/internal/domain"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model"
usr_view "github.com/caos/zitadel/internal/user/repository/view"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/golang/protobuf/ptypes"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/config/systemdefaults" "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
caos_errs "github.com/caos/zitadel/internal/errors" v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model"
key_model "github.com/caos/zitadel/internal/key/model" key_model "github.com/caos/zitadel/internal/key/model"
key_view_model "github.com/caos/zitadel/internal/key/repository/view/model" key_view_model "github.com/caos/zitadel/internal/key/repository/view/model"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
usr_view "github.com/caos/zitadel/internal/user/repository/view"
"github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/user/repository/view/model"
"github.com/caos/zitadel/internal/view/repository" "github.com/caos/zitadel/internal/view/repository"
) )
@ -36,16 +33,16 @@ type UserRepo struct {
func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserView, error) { func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserView, error) {
user, viewErr := repo.View.UserByID(id) user, viewErr := repo.View.UserByID(id)
if viewErr != nil && !caos_errs.IsNotFound(viewErr) { if viewErr != nil && !errors.IsNotFound(viewErr) {
return nil, viewErr return nil, viewErr
} }
if caos_errs.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
user = new(model.UserView) user = new(model.UserView)
} }
events, esErr := repo.getUserEvents(ctx, id, user.Sequence) events, esErr := repo.getUserEvents(ctx, id, user.Sequence)
if caos_errs.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.User.NotFound")
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events")
@ -58,23 +55,23 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserV
} }
} }
if userCopy.State == int32(usr_model.UserStateDeleted) { if userCopy.State == int32(usr_model.UserStateDeleted) {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-4Fm9s", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-4Fm9s", "Errors.User.NotFound")
} }
return model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil return model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil
} }
func (repo *UserRepo) UserByIDAndResourceOwner(ctx context.Context, id, resourceOwner string) (*usr_model.UserView, error) { func (repo *UserRepo) UserByIDAndResourceOwner(ctx context.Context, id, resourceOwner string) (*usr_model.UserView, error) {
user, viewErr := repo.View.UserByIDAndResourceOwner(id, resourceOwner) user, viewErr := repo.View.UserByIDAndResourceOwner(id, resourceOwner)
if viewErr != nil && !caos_errs.IsNotFound(viewErr) { if viewErr != nil && !errors.IsNotFound(viewErr) {
return nil, viewErr return nil, viewErr
} }
if caos_errs.IsNotFound(viewErr) { if errors.IsNotFound(viewErr) {
user = new(model.UserView) user = new(model.UserView)
} }
events, esErr := repo.getUserEvents(ctx, id, user.Sequence) events, esErr := repo.getUserEvents(ctx, id, user.Sequence)
if caos_errs.IsNotFound(viewErr) && len(events) == 0 { if errors.IsNotFound(viewErr) && len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.User.NotFound")
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events")
@ -87,7 +84,7 @@ func (repo *UserRepo) UserByIDAndResourceOwner(ctx context.Context, id, resource
} }
} }
if userCopy.State == int32(usr_model.UserStateDeleted) || userCopy.ResourceOwner != resourceOwner { if userCopy.State == int32(usr_model.UserStateDeleted) || userCopy.ResourceOwner != resourceOwner {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-4Fm9s", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-4Fm9s", "Errors.User.NotFound")
} }
return model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil return model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil
} }

View File

@ -44,8 +44,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
newUser(handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, newUser(handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es},
defaults.IamID), defaults.IamID),
newUserGrant(handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}), newUserGrant(handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}),
newOrg(
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
newOrgMember( newOrgMember(
handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount, es}),
newOrgDomain( newOrgDomain(

View File

@ -1,99 +0,0 @@
package handler
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/org/repository/view"
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/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
)
const (
orgTable = "management.orgs"
)
type Org struct {
handler
subscription *v1.Subscription
}
func newOrg(handler handler) *Org {
h := &Org{
handler: handler,
}
h.subscribe()
return h
}
func (m *Org) subscribe() {
m.subscription = m.es.Subscribe(m.AggregateTypes()...)
go func() {
for event := range m.subscription.Events {
query.ReduceEvent(m, event)
}
}()
}
func (o *Org) ViewModel() string {
return orgTable
}
func (o *Org) Subscription() *v1.Subscription {
return o.subscription
}
func (_ *Org) AggregateTypes() []es_models.AggregateType {
return []es_models.AggregateType{model.OrgAggregate}
}
func (o *Org) CurrentSequence() (uint64, error) {
sequence, err := o.view.GetLatestOrgSequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (o *Org) EventQuery() (*es_models.SearchQuery, error) {
sequence, err := o.view.GetLatestOrgSequence()
if err != nil {
return nil, err
}
return view.OrgQuery(sequence.CurrentSequence), nil
}
func (o *Org) Reduce(event *es_models.Event) (err error) {
org := new(org_model.OrgView)
switch event.Type {
case model.OrgAdded:
err = org.AppendEvent(event)
case model.OrgChanged:
org, err = o.view.OrgByID(event.ResourceOwner)
if err != nil {
return err
}
err = org.AppendEvent(event)
default:
return o.view.ProcessedOrgSequence(event)
}
if err != nil {
return err
}
return o.view.PutOrg(org, event)
}
func (o *Org) OnError(event *es_models.Event, spoolerErr error) error {
logging.LogWithFields("SPOOL-ls9ew", "id", event.AggregateID).WithError(spoolerErr).Warn("something went wrong in project app handler")
return spooler.HandleError(event, spoolerErr, o.view.GetLatestOrgFailedEvent, o.view.ProcessedOrgFailedEvent, o.view.ProcessedOrgSequence, o.errorCountUntilSkip)
}
func (o *Org) OnSuccess() error {
return spooler.HandleSuccess(o.view.UpdateOrgSpoolerRunTimestamp)
}

View File

@ -1,44 +0,0 @@
package view
import (
"github.com/caos/zitadel/internal/eventstore/v1/models"
org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
)
const (
orgTable = "management.orgs"
)
func (v *View) OrgByID(orgID string) (*model.OrgView, error) {
return org_view.OrgByID(v.Db, orgTable, orgID)
}
func (v *View) PutOrg(org *model.OrgView, event *models.Event) error {
err := org_view.PutOrg(v.Db, orgTable, org)
if err != nil {
return err
}
return v.ProcessedOrgSequence(event)
}
func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
return v.latestFailedEvent(orgTable, sequence)
}
func (v *View) ProcessedOrgFailedEvent(failedEvent *repository.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}
func (v *View) UpdateOrgSpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(orgTable)
}
func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) {
return v.latestSequence(orgTable)
}
func (v *View) ProcessedOrgSequence(event *models.Event) error {
return v.saveCurrentSequence(orgTable, event)
}

View File

@ -14,8 +14,6 @@ import (
type OrgRepository interface { type OrgRepository interface {
Languages(ctx context.Context) ([]language.Tag, error) Languages(ctx context.Context) ([]language.Tag, error)
OrgByID(ctx context.Context, id string) (*org_model.OrgView, error)
OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.OrgView, error)
OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool, auditLogRetention time.Duration) (*org_model.OrgChanges, error) OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool, auditLogRetention time.Duration) (*org_model.OrgChanges, error)
SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error) SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error)

View File

@ -1,50 +0,0 @@
package view
import (
"github.com/jinzhu/gorm"
caos_errs "github.com/caos/zitadel/internal/errors"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
)
func OrgByID(db *gorm.DB, table, orgID string) (*model.OrgView, error) {
org := new(model.OrgView)
query := repository.PrepareGetByKey(table, model.OrgSearchKey(org_model.OrgSearchKeyOrgID), orgID)
err := query(db, org)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-GEwea", "Errors.Org.NotFound")
}
return org, err
}
func OrgByPrimaryDomain(db *gorm.DB, table, primaryDomain string) (*model.OrgView, error) {
org := new(model.OrgView)
query := repository.PrepareGetByKey(table, model.OrgSearchKey(org_model.OrgSearchKeyOrgDomain), primaryDomain)
err := query(db, org)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-GEwea", "Errors.Org.NotFound")
}
return org, err
}
func SearchOrgs(db *gorm.DB, table string, req *org_model.OrgSearchRequest) ([]*model.OrgView, uint64, error) {
orgs := make([]*model.OrgView, 0)
query := repository.PrepareSearchQuery(table, model.OrgSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries, SortingColumn: req.SortingColumn, Asc: req.Asc})
count, err := query(db, &orgs)
if err != nil {
return nil, 0, err
}
return orgs, count, nil
}
func PutOrg(db *gorm.DB, table string, org *model.OrgView) error {
save := repository.PrepareSave(table)
return save(db, org)
}
func DeleteOrg(db *gorm.DB, table, orgID string) error {
delete := repository.PrepareDeleteByKey(table, model.OrgSearchKey(org_model.OrgSearchKeyOrgID), orgID)
return delete(db)
}

View File

@ -2,76 +2,80 @@ package query
import ( import (
"context" "context"
"database/sql"
errs "errors"
"time" "time"
"github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/query/projection"
) )
var actionsQuery = squirrel.StatementBuilder.Select("creation_date", "change_date", "resource_owner", "sequence", "id", "action_state", "name", "script", "timeout", "allowed_to_fail"). var (
From("zitadel.projections.actions").PlaceholderFormat(squirrel.Dollar) actionTable = table{
name: projection.ActionTable,
func (q *Queries) GetAction(ctx context.Context, id string, orgID string) (*Action, error) {
idQuery, _ := newActionIDSearchQuery(id)
actions, err := q.SearchActions(ctx, &ActionSearchQueries{Queries: []SearchQuery{idQuery}})
if err != nil {
return nil, err
} }
if len(actions) != 1 { ActionColumnID = Column{
return nil, errors.ThrowNotFound(nil, "QUERY-dft2g", "Errors.Action.NotFound") name: projection.ActionIDCol,
table: actionTable,
} }
return actions[0], err ActionColumnCreationDate = Column{
} name: projection.ActionCreationDateCol,
table: actionTable,
func (q *Queries) SearchActions(ctx context.Context, query *ActionSearchQueries) ([]*Action, error) {
stmt, args, err := query.ToQuery(actionsQuery).ToSql()
if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-wQ3by", "Errors.orgs.invalid.request")
} }
ActionColumnChangeDate = Column{
rows, err := q.client.QueryContext(ctx, stmt, args...) name: projection.ActionChangeDateCol,
if err != nil { table: actionTable,
return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.orgs.internal")
} }
ActionColumnResourceOwner = Column{
actions := []*Action{} name: projection.ActionResourceOwnerCol,
for rows.Next() { table: actionTable,
org := new(Action)
rows.Scan(
&org.CreationDate,
&org.ChangeDate,
&org.ResourceOwner,
&org.Sequence,
&org.ID,
&org.State,
&org.Name,
&org.Script,
&org.Timeout,
&org.AllowedToFail,
)
actions = append(actions, org)
} }
ActionColumnSequence = Column{
if err := rows.Err(); err != nil { name: projection.ActionSequenceCol,
return nil, errors.ThrowInternal(err, "QUERY-pA0Wj", "Errors.actions.internal") table: actionTable,
} }
ActionColumnState = Column{
name: projection.ActionStateCol,
table: actionTable,
}
ActionColumnName = Column{
name: projection.ActionNameCol,
table: actionTable,
}
ActionColumnScript = Column{
name: projection.ActionScriptCol,
table: actionTable,
}
ActionColumnTimeout = Column{
name: projection.ActionTimeoutCol,
table: actionTable,
}
ActionColumnAllowedToFail = Column{
name: projection.ActionAllowedToFailCol,
table: actionTable,
}
)
return actions, nil type Actions struct {
SearchResponse
Actions []*Action
} }
type Action struct { type Action struct {
ID string `col:"id"` ID string
CreationDate time.Time `col:"creation_date"` CreationDate time.Time
ChangeDate time.Time `col:"change_date"` ChangeDate time.Time
ResourceOwner string `col:"resource_owner"` ResourceOwner string
State domain.ActionState `col:"action_state"` State domain.ActionState
Sequence uint64 `col:"sequence"` Sequence uint64
Name string `col:"name"` Name string
Script string `col:"script"` Script string
Timeout time.Duration `col:"-"` Timeout time.Duration
AllowedToFail bool `col:"-"` AllowedToFail bool
} }
type ActionSearchQueries struct { type ActionSearchQueries struct {
@ -79,26 +83,146 @@ type ActionSearchQueries struct {
Queries []SearchQuery Queries []SearchQuery
} }
func (q *ActionSearchQueries) ToQuery(query squirrel.SelectBuilder) squirrel.SelectBuilder { func (q *ActionSearchQueries) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
query = q.SearchRequest.ToQuery(query) query = q.SearchRequest.toQuery(query)
for _, q := range q.Queries { for _, q := range q.Queries {
query = q.ToQuery(query) query = q.ToQuery(query)
} }
return query return query
} }
func (q *Queries) SearchActions(ctx context.Context, queries *ActionSearchQueries) (actions *Actions, err error) {
query, scan := prepareActionsQuery()
stmt, args, err := queries.toQuery(query).ToSql()
if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-SDgwg", "Errors.Query.InvalidRequest")
}
rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-SDfr52", "Errors.Internal")
}
actions, err = scan(rows)
if err != nil {
return nil, err
}
actions.LatestSequence, err = q.latestSequence(ctx, actionTable)
return actions, err
}
func (q *Queries) GetActionByID(ctx context.Context, id string, orgID string) (*Action, error) {
stmt, scan := prepareActionQuery()
query, args, err := stmt.Where(
sq.Eq{
ActionColumnID.identifier(): id,
},
sq.Eq{
ActionColumnResourceOwner.identifier(): orgID,
}).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-Dgff3", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func NewActionResourceOwnerQuery(id string) (SearchQuery, error) { func NewActionResourceOwnerQuery(id string) (SearchQuery, error) {
return NewTextQuery("resource_owner", id, TextEquals) return NewTextQuery(ActionColumnResourceOwner, id, TextEquals)
} }
func NewActionNameSearchQuery(method TextComparison, value string) (SearchQuery, error) { func NewActionNameSearchQuery(method TextComparison, value string) (SearchQuery, error) {
return NewTextQuery("name", value, method) return NewTextQuery(ActionColumnName, value, method)
} }
func NewActionStateSearchQuery(value domain.ActionState) (SearchQuery, error) { func NewActionStateSearchQuery(value domain.ActionState) (SearchQuery, error) {
return NewIntQuery("state", int(value), IntEquals) return NewNumberQuery(ActionColumnState, int(value), NumberEquals)
} }
func newActionIDSearchQuery(id string) (SearchQuery, error) { func prepareActionsQuery() (sq.SelectBuilder, func(rows *sql.Rows) (*Actions, error)) {
return NewTextQuery("id", id, TextEquals) return sq.Select(
ActionColumnID.identifier(),
ActionColumnCreationDate.identifier(),
ActionColumnChangeDate.identifier(),
ActionColumnResourceOwner.identifier(),
ActionColumnSequence.identifier(),
ActionColumnState.identifier(),
ActionColumnName.identifier(),
ActionColumnScript.identifier(),
ActionColumnTimeout.identifier(),
ActionColumnAllowedToFail.identifier(),
countColumn.identifier(),
).From(actionTable.identifier()).PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*Actions, error) {
actions := make([]*Action, 0)
var count uint64
for rows.Next() {
action := new(Action)
err := rows.Scan(
&action.ID,
&action.CreationDate,
&action.ChangeDate,
&action.ResourceOwner,
&action.Sequence,
&action.State,
&action.Name,
&action.Script,
&action.Timeout,
&action.AllowedToFail,
&count,
)
if err != nil {
return nil, err
}
actions = append(actions, action)
}
if err := rows.Close(); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-EGdff", "Errors.Query.CloseRows")
}
return &Actions{
Actions: actions,
SearchResponse: SearchResponse{
Count: count,
},
}, nil
}
}
func prepareActionQuery() (sq.SelectBuilder, func(row *sql.Row) (*Action, error)) {
return sq.Select(
ActionColumnID.identifier(),
ActionColumnCreationDate.identifier(),
ActionColumnChangeDate.identifier(),
ActionColumnResourceOwner.identifier(),
ActionColumnSequence.identifier(),
ActionColumnState.identifier(),
ActionColumnName.identifier(),
ActionColumnScript.identifier(),
ActionColumnTimeout.identifier(),
ActionColumnAllowedToFail.identifier(),
).From(actionTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*Action, error) {
action := new(Action)
err := row.Scan(
&action.ID,
&action.CreationDate,
&action.ChangeDate,
&action.ResourceOwner,
&action.Sequence,
&action.State,
&action.Name,
&action.Script,
&action.Timeout,
&action.AllowedToFail,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-GEfnb", "Errors.Action.NotFound")
}
return nil, errors.ThrowInternal(err, "QUERY-Dbnt4", "Errors.Internal")
}
return action, nil
}
} }

View File

@ -2,172 +2,227 @@ package query
import ( import (
"context" "context"
"database/sql"
"time" "time"
"github.com/Masterminds/squirrel" "github.com/Masterminds/squirrel"
sq "github.com/Masterminds/squirrel"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/query/projection"
) )
func (q *Queries) GetActionsByFlowAndTriggerType(ctx context.Context, flowType domain.FlowType, triggerType domain.TriggerType) ([]*Action, error) { var (
flowTypeQuery, _ := NewTriggerActionFlowTypeSearchQuery(flowType) flowsTriggersTable = table{
triggerTypeQuery, _ := NewTriggerActionTriggerTypeSearchQuery(triggerType) name: projection.FlowTriggerTable,
return q.SearchActionsFromFlow(ctx, &TriggerActionSearchQueries{Queries: []SearchQuery{flowTypeQuery, triggerTypeQuery}}) }
FlowsTriggersColumnFlowType = Column{
name: projection.FlowTypeCol,
table: flowsTriggersTable,
}
FlowsTriggersColumnTriggerType = Column{
name: projection.FlowTriggerTypeCol,
table: flowsTriggersTable,
}
FlowsTriggersColumnResourceOwner = Column{
name: projection.FlowResourceOwnerCol,
table: flowsTriggersTable,
}
FlowsTriggersColumnTriggerSequence = Column{
name: projection.FlowActionTriggerSequenceCol,
table: flowsTriggersTable,
}
FlowsTriggersColumnActionID = Column{
name: projection.FlowActionIDCol,
table: flowsTriggersTable,
}
)
type Flow struct {
ID string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
Type domain.FlowType
TriggerActions map[domain.TriggerType][]*Action
} }
var triggerActionsQuery = squirrel.StatementBuilder.Select("creation_date", "change_date", "resource_owner", "sequence", "action_id", "name", "script", "trigger_type", "trigger_sequence"). func (q *Queries) GetFlow(ctx context.Context, flowType domain.FlowType, orgID string) (*Flow, error) {
From("zitadel.projections.flows_actions_triggers").PlaceholderFormat(squirrel.Dollar) query, scan := q.prepareFlowQuery()
stmt, args, err := query.Where(
func (q *Queries) SearchActionsFromFlow(ctx context.Context, query *TriggerActionSearchQueries) ([]*Action, error) { sq.Eq{
stmt, args, err := query.ToQuery(triggerActionsQuery).OrderBy("flow_type", "trigger_type", "trigger_sequence").ToSql() FlowsTriggersColumnFlowType.identifier(): flowType,
},
sq.Eq{
FlowsTriggersColumnResourceOwner.identifier(): orgID,
}).ToSql()
if err != nil { if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-wQ3by", "Errors.orgs.invalid.request") return nil, errors.ThrowInvalidArgument(err, "QUERY-HBRh3", "Errors.Query.InvalidRequest")
} }
rows, err := q.client.QueryContext(ctx, stmt, args...) rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil { if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.orgs.internal") return nil, errors.ThrowInternal(err, "QUERY-Gg42f", "Errors.Internal")
} }
return scan(rows)
actions := []*Action{}
for rows.Next() {
action := new(Action)
var triggerType domain.TriggerType
var triggerSequence int
rows.Scan(
&action.CreationDate,
&action.ChangeDate,
&action.ResourceOwner,
&action.Sequence,
//&action.State, //TODO: state in next release
&action.ID,
&action.Name,
&action.Script,
&triggerType,
&triggerSequence,
)
actions = append(actions, action)
}
if err := rows.Err(); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-pA0Wj", "Errors.actions.internal")
}
return actions, nil
} }
func (q *Queries) GetFlow(ctx context.Context, flowType domain.FlowType) (*Flow, error) { func (q *Queries) GetActionsByFlowAndTriggerType(ctx context.Context, flowType domain.FlowType, triggerType domain.TriggerType, orgID string) ([]*Action, error) {
flowTypeQuery, _ := NewTriggerActionFlowTypeSearchQuery(flowType) stmt, scan := q.prepareTriggerActionsQuery()
return q.SearchFlow(ctx, &TriggerActionSearchQueries{Queries: []SearchQuery{flowTypeQuery}}) query, args, err := stmt.Where(
} sq.Eq{
FlowsTriggersColumnFlowType.identifier(): flowType,
func (q *Queries) SearchFlow(ctx context.Context, query *TriggerActionSearchQueries) (*Flow, error) { },
stmt, args, err := query.ToQuery(triggerActionsQuery.OrderBy("flow_type", "trigger_type", "trigger_sequence")).ToSql() sq.Eq{
FlowsTriggersColumnTriggerType.identifier(): triggerType,
},
sq.Eq{
FlowsTriggersColumnResourceOwner.identifier(): orgID,
}).ToSql()
if err != nil { if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-wQ3by", "Errors.orgs.invalid.request") return nil, errors.ThrowInternal(err, "QUERY-Dgff3", "Errors.Query.SQLStatement")
} }
rows, err := q.client.QueryContext(ctx, stmt, args...) rows, err := q.client.QueryContext(ctx, query, args...)
if err != nil { if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.orgs.internal") return nil, errors.ThrowInternal(err, "QUERY-SDf52", "Errors.Internal")
} }
return scan(rows)
flow := &Flow{
TriggerActions: make(map[domain.TriggerType][]*Action),
}
for rows.Next() {
action := new(Action)
var triggerType domain.TriggerType
var triggerSequence int
rows.Scan(
&action.CreationDate,
&action.ChangeDate,
&action.ResourceOwner,
&action.Sequence,
//&action.State, //TODO: state in next release
&action.ID,
&action.Name,
&action.Script,
&triggerType,
&triggerSequence,
)
flow.TriggerActions[triggerType] = append(flow.TriggerActions[triggerType], action)
}
if err := rows.Err(); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-pA0Wj", "Errors.actions.internal")
}
return flow, nil
} }
func (q *Queries) GetFlowTypesOfActionID(ctx context.Context, actionID string) ([]domain.FlowType, error) { func (q *Queries) GetFlowTypesOfActionID(ctx context.Context, actionID string) ([]domain.FlowType, error) {
actionIDQuery, _ := NewTriggerActionActionIDSearchQuery(actionID) stmt, args, err := squirrel.StatementBuilder.
query := &TriggerActionSearchQueries{Queries: []SearchQuery{actionIDQuery}} Select(FlowsTriggersColumnFlowType.identifier()).
stmt, args, err := query.ToQuery( From(flowsTriggersTable.identifier()).
squirrel.StatementBuilder. Where(sq.Eq{
Select("flow_type"). FlowsTriggersColumnActionID.identifier(): actionID,
From("zitadel.projections.flows_actions_triggers"). }).
PlaceholderFormat(squirrel.Dollar)).ToSql() PlaceholderFormat(squirrel.Dollar).
ToSql()
if err != nil { if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-wQ3by", "Errors.orgs.invalid.request") return nil, errors.ThrowInvalidArgument(err, "QUERY-Dh311", "Errors.Query.InvalidRequest")
} }
rows, err := q.client.QueryContext(ctx, stmt, args...) rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil { if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.orgs.internal") return nil, errors.ThrowInternal(err, "QUERY-Bhj4w", "Errors.Internal")
} }
flowTypes := make([]domain.FlowType, 0) flowTypes := make([]domain.FlowType, 0)
for rows.Next() { for rows.Next() {
var flow_type domain.FlowType var flowType domain.FlowType
rows.Scan( err := rows.Scan(
&flow_type, &flowType,
) )
if err != nil {
flowTypes = append(flowTypes, flow_type) return nil, err
}
flowTypes = append(flowTypes, flowType)
} }
if err := rows.Err(); err != nil { if err := rows.Close(); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-pA0Wj", "Errors.actions.internal") return nil, errors.ThrowInternal(err, "QUERY-Fbgnh", "Errors.Query.CloseRows")
} }
return flowTypes, nil return flowTypes, nil
} }
type Flow struct { func (q *Queries) prepareTriggerActionsQuery() (sq.SelectBuilder, func(*sql.Rows) ([]*Action, error)) {
ID string `col:"id"` return sq.Select(
CreationDate time.Time `col:"creation_date"` ActionColumnID.identifier(),
ChangeDate time.Time `col:"change_date"` ActionColumnCreationDate.identifier(),
ResourceOwner string `col:"resource_owner"` ActionColumnChangeDate.identifier(),
Sequence uint64 `col:"sequence"` ActionColumnResourceOwner.identifier(),
Type domain.FlowType `col:"flow_type"` //ActionColumnState.identifier(),
ActionColumnSequence.identifier(),
ActionColumnName.identifier(),
ActionColumnScript.identifier(),
FlowsTriggersColumnTriggerType.identifier(),
FlowsTriggersColumnTriggerSequence.identifier(),
).
From(flowsTriggersTable.name).
LeftJoin(join(ActionColumnID, FlowsTriggersColumnActionID)).
PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) ([]*Action, error) {
actions := make([]*Action, 0)
for rows.Next() {
action := new(Action)
var triggerType domain.TriggerType
var triggerSequence int
err := rows.Scan(
&action.ID,
&action.CreationDate,
&action.ChangeDate,
&action.ResourceOwner,
//&action.State, //TODO: state in next release
&action.Sequence,
&action.Name,
&action.Script,
&triggerType,
&triggerSequence,
)
if err != nil {
return nil, err
}
actions = append(actions, action)
}
TriggerActions map[domain.TriggerType][]*Action if err := rows.Close(); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-Df42d", "Errors.Query.CloseRows")
}
return actions, nil
}
} }
type TriggerActionSearchQueries struct { func (q *Queries) prepareFlowQuery() (sq.SelectBuilder, func(*sql.Rows) (*Flow, error)) {
SearchRequest return sq.Select(
Queries []SearchQuery ActionColumnID.identifier(),
} ActionColumnCreationDate.identifier(),
ActionColumnChangeDate.identifier(),
ActionColumnResourceOwner.identifier(),
//ActionColumnState.identifier(),
ActionColumnSequence.identifier(),
ActionColumnName.identifier(),
ActionColumnScript.identifier(),
FlowsTriggersColumnTriggerType.identifier(),
FlowsTriggersColumnTriggerSequence.identifier(),
).
From(flowsTriggersTable.name).
LeftJoin(join(ActionColumnID, FlowsTriggersColumnActionID)).
PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*Flow, error) {
flow := &Flow{
TriggerActions: make(map[domain.TriggerType][]*Action),
}
for rows.Next() {
action := new(Action)
var triggerType domain.TriggerType
var triggerSequence int
err := rows.Scan(
&action.ID,
&action.CreationDate,
&action.ChangeDate,
&action.ResourceOwner,
//&action.State, //TODO: state in next release
&action.Sequence,
&action.Name,
&action.Script,
&triggerType,
&triggerSequence,
)
if err != nil {
return nil, err
}
flow.TriggerActions[triggerType] = append(flow.TriggerActions[triggerType], action)
}
func (q *TriggerActionSearchQueries) ToQuery(query squirrel.SelectBuilder) squirrel.SelectBuilder { if err := rows.Close(); err != nil {
query = q.SearchRequest.ToQuery(query) return nil, errors.ThrowInternal(err, "QUERY-Dfbe2", "Errors.Query.CloseRows")
for _, q := range q.Queries { }
query = q.ToQuery(query)
}
return query
}
func NewTriggerActionTriggerTypeSearchQuery(value domain.TriggerType) (SearchQuery, error) { return flow, nil
return NewIntQuery("trigger_type", int(value), IntEquals) }
}
func NewTriggerActionFlowTypeSearchQuery(value domain.FlowType) (SearchQuery, error) {
return NewIntQuery("flow_type", int(value), IntEquals)
}
func NewTriggerActionActionIDSearchQuery(actionID string) (SearchQuery, error) {
return NewTextQuery("action_id", actionID, TextEquals)
} }

View File

@ -0,0 +1,75 @@
package query
import (
"context"
"database/sql"
errs "errors"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/caos/zitadel/internal/query/projection"
"github.com/caos/zitadel/internal/errors"
)
type LatestSequence struct {
Sequence uint64
Timestamp time.Time
}
func prepareLatestSequence() (sq.SelectBuilder, func(*sql.Row) (*LatestSequence, error)) {
return sq.Select(
CurrentSequenceColCurrentSequence.identifier(),
CurrentSequenceColTimestamp.identifier()).
From(currentSequencesTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*LatestSequence, error) {
seq := new(LatestSequence)
err := row.Scan(
&seq.Sequence,
&seq.Timestamp,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-gmd9o", "Errors.CurrentSequence.NotFound")
}
return nil, errors.ThrowInternal(err, "QUERY-aAZ1D", "Errors.Internal")
}
return seq, nil
}
}
func (q *Queries) latestSequence(ctx context.Context, projection table) (*LatestSequence, error) {
query, scan := prepareLatestSequence()
stmt, args, err := query.Where(sq.Eq{
CurrentSequenceColProjectionName.identifier(): projection.name,
}).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-5CfX9", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
}
var (
currentSequencesTable = table{
name: projection.CurrentSeqTable,
}
CurrentSequenceColAggregateType = Column{
name: "aggregate_type",
table: currentSequencesTable,
}
CurrentSequenceColCurrentSequence = Column{
name: "current_sequence",
table: currentSequencesTable,
}
CurrentSequenceColTimestamp = Column{
name: "timestamp",
table: currentSequencesTable,
}
CurrentSequenceColProjectionName = Column{
name: "projection_name",
table: currentSequencesTable,
}
)

252
internal/query/org.go Normal file
View File

@ -0,0 +1,252 @@
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"
)
var (
orgsTable = table{
name: projection.OrgProjectionTable,
}
OrgColumnID = Column{
name: projection.OrgColumnID,
table: orgsTable,
}
OrgColumnCreationDate = Column{
name: projection.OrgColumnCreationDate,
table: orgsTable,
}
OrgColumnChangeDate = Column{
name: projection.OrgColumnChangeDate,
table: orgsTable,
}
OrgColumnResourceOwner = Column{
name: projection.OrgColumnResourceOwner,
table: orgsTable,
}
OrgColumnState = Column{
name: projection.OrgColumnState,
table: orgsTable,
}
OrgColumnSequence = Column{
name: projection.OrgColumnSequence,
table: orgsTable,
}
OrgColumnName = Column{
name: projection.OrgColumnName,
table: orgsTable,
}
OrgColumnDomain = Column{
name: projection.OrgColumnDomain,
table: orgsTable,
}
)
type Orgs struct {
SearchResponse
Orgs []*Org
}
type Org struct {
ID string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
State domain.OrgState
Sequence uint64
Name string
Domain string
}
type OrgSearchQueries struct {
SearchRequest
Queries []SearchQuery
}
func (q *OrgSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
query = q.SearchRequest.toQuery(query)
for _, q := range q.Queries {
query = q.ToQuery(query)
}
return query
}
func (q *Queries) OrgByID(ctx context.Context, id string) (*Org, error) {
stmt, scan := prepareOrgQuery()
query, args, err := stmt.Where(sq.Eq{
OrgColumnID.identifier(): id,
}).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-AWx52", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func (q *Queries) OrgByDomainGlobal(ctx context.Context, domain string) (*Org, error) {
stmt, scan := prepareOrgQuery()
query, args, err := stmt.Where(sq.Eq{
OrgColumnDomain.identifier(): domain,
}).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-TYUCE", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func (q *Queries) IsOrgUnique(ctx context.Context, name, domain string) (isUnique bool, err error) {
query, scan := prepareOrgUniqueQuery()
stmt, args, err := query.Where(
sq.Or{
sq.Eq{
OrgColumnDomain.identifier(): domain,
},
sq.Eq{
OrgColumnName.identifier(): name,
},
}).ToSql()
if err != nil {
return false, errors.ThrowInternal(err, "QUERY-Dgbe2", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
}
func (q *Queries) ExistsOrg(ctx context.Context, id string) (err error) {
_, err = q.OrgByID(ctx, id)
return err
}
func (q *Queries) SearchOrgs(ctx context.Context, queries *OrgSearchQueries) (orgs *Orgs, err error) {
query, scan := prepareOrgsQuery()
stmt, args, err := queries.toQuery(query).ToSql()
if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-wQ3by", "Errors.Query.InvalidRequest")
}
rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.Internal")
}
orgs, err = scan(rows)
if err != nil {
return nil, err
}
orgs.LatestSequence, err = q.latestSequence(ctx, orgsTable)
return orgs, err
}
func NewOrgDomainSearchQuery(method TextComparison, value string) (SearchQuery, error) {
return NewTextQuery(OrgColumnDomain, value, method)
}
func NewOrgNameSearchQuery(method TextComparison, value string) (SearchQuery, error) {
return NewTextQuery(OrgColumnName, value, method)
}
func prepareOrgsQuery() (sq.SelectBuilder, func(*sql.Rows) (*Orgs, error)) {
return sq.Select(
OrgColumnID.identifier(),
OrgColumnCreationDate.identifier(),
OrgColumnChangeDate.identifier(),
OrgColumnResourceOwner.identifier(),
OrgColumnState.identifier(),
OrgColumnSequence.identifier(),
OrgColumnName.identifier(),
OrgColumnDomain.identifier(),
countColumn.identifier()).
From(orgsTable.identifier()).PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*Orgs, error) {
orgs := make([]*Org, 0)
var count uint64
for rows.Next() {
org := new(Org)
err := rows.Scan(
&org.ID,
&org.CreationDate,
&org.ChangeDate,
&org.ResourceOwner,
&org.State,
&org.Sequence,
&org.Name,
&org.Domain,
&count,
)
if err != nil {
return nil, err
}
orgs = append(orgs, org)
}
if err := rows.Close(); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-QMXJv", "Errors.Query.CloseRows")
}
return &Orgs{
Orgs: orgs,
SearchResponse: SearchResponse{
Count: count,
},
}, nil
}
}
func prepareOrgQuery() (sq.SelectBuilder, func(*sql.Row) (*Org, error)) {
return sq.Select(
OrgColumnID.identifier(),
OrgColumnCreationDate.identifier(),
OrgColumnChangeDate.identifier(),
OrgColumnResourceOwner.identifier(),
OrgColumnState.identifier(),
OrgColumnSequence.identifier(),
OrgColumnName.identifier(),
OrgColumnDomain.identifier(),
).
From(orgsTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*Org, error) {
o := new(Org)
err := row.Scan(
&o.ID,
&o.CreationDate,
&o.ChangeDate,
&o.ResourceOwner,
&o.State,
&o.Sequence,
&o.Name,
&o.Domain,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-iTTGJ", "Errors.Org.NotFound")
}
return nil, errors.ThrowInternal(err, "QUERY-pWS5H", "Errors.Internal")
}
return o, nil
}
}
func prepareOrgUniqueQuery() (sq.SelectBuilder, func(*sql.Row) (bool, error)) {
return sq.Select(uniqueColumn.identifier()).
From(orgsTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (isUnique bool, err error) {
err = row.Scan(&isUnique)
if err != nil {
return false, errors.ThrowInternal(err, "QUERY-e6EiG", "Errors.Internal")
}
return isUnique, err
}
}

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -12,13 +13,27 @@ import (
"github.com/caos/zitadel/internal/repository/action" "github.com/caos/zitadel/internal/repository/action"
) )
const (
ActionTable = "zitadel.projections.actions"
ActionIDCol = "id"
ActionCreationDateCol = "creation_date"
ActionChangeDateCol = "change_date"
ActionResourceOwnerCol = "resource_owner"
ActionStateCol = "action_state"
ActionSequenceCol = "sequence"
ActionNameCol = "name"
ActionScriptCol = "script"
ActionTimeoutCol = "timeout"
ActionAllowedToFailCol = "allowed_to_fail"
)
type ActionProjection struct { type ActionProjection struct {
crdb.StatementHandler crdb.StatementHandler
} }
func NewActionProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ActionProjection { func NewActionProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ActionProjection {
p := &ActionProjection{} p := &ActionProjection{}
config.ProjectionName = "projections.actions" config.ProjectionName = ActionTable
config.Reducers = p.reducers() config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config) p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p return p
@ -54,38 +69,25 @@ func (p *ActionProjection) reducers() []handler.AggregateReducer {
} }
} }
const (
actionIDCol = "id"
actionCreationDateCol = "creation_date"
actionChangeDateCol = "change_date"
actionResourceOwnerCol = "resource_owner"
actionStateCol = "action_state"
actionSequenceCol = "sequence"
actionNameCol = "name"
actionScriptCol = "script"
actionTimeoutCol = "timeout"
actionAllowedToFailCol = "allowed_to_fail"
)
func (p *ActionProjection) reduceActionAdded(event eventstore.EventReader) (*handler.Statement, error) { func (p *ActionProjection) reduceActionAdded(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*action.AddedEvent) e, ok := event.(*action.AddedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-zWCk3", "seq", event.Sequence, "expectedType", action.AddedEventType).Error("was not an event") logging.LogWithFields("HANDL-Sgg31", "seq", event.Sequence, "expectedType", action.AddedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-uYq4r", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-Dff21", "reduce.wrong.event.type")
} }
return crdb.NewCreateStatement( return crdb.NewCreateStatement(
e, e,
[]handler.Column{ []handler.Column{
handler.NewCol(actionIDCol, e.Aggregate().ID), handler.NewCol(ActionIDCol, e.Aggregate().ID),
handler.NewCol(actionCreationDateCol, e.CreationDate()), handler.NewCol(ActionCreationDateCol, e.CreationDate()),
handler.NewCol(actionChangeDateCol, e.CreationDate()), handler.NewCol(ActionChangeDateCol, e.CreationDate()),
handler.NewCol(actionResourceOwnerCol, e.Aggregate().ResourceOwner), handler.NewCol(ActionResourceOwnerCol, e.Aggregate().ResourceOwner),
handler.NewCol(actionSequenceCol, e.Sequence()), handler.NewCol(ActionSequenceCol, e.Sequence()),
handler.NewCol(actionNameCol, e.Name), handler.NewCol(ActionNameCol, e.Name),
handler.NewCol(actionScriptCol, e.Script), handler.NewCol(ActionScriptCol, e.Script),
handler.NewCol(actionTimeoutCol, e.Timeout), handler.NewCol(ActionTimeoutCol, e.Timeout),
handler.NewCol(actionAllowedToFailCol, e.AllowedToFail), handler.NewCol(ActionAllowedToFailCol, e.AllowedToFail),
handler.NewCol(actionStateCol, domain.ActionStateActive), handler.NewCol(ActionStateCol, domain.ActionStateActive),
}, },
), nil ), nil
} }
@ -93,30 +95,30 @@ func (p *ActionProjection) reduceActionAdded(event eventstore.EventReader) (*han
func (p *ActionProjection) reduceActionChanged(event eventstore.EventReader) (*handler.Statement, error) { func (p *ActionProjection) reduceActionChanged(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*action.ChangedEvent) e, ok := event.(*action.ChangedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-q4oq8", "seq", event.Sequence, "expected", action.ChangedEventType).Error("wrong event type") logging.LogWithFields("HANDL-Dg2th", "seq", event.Sequence, "expected", action.ChangedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-Bg8oM", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-Gg43d", "reduce.wrong.event.type")
} }
values := []handler.Column{ values := []handler.Column{
handler.NewCol(actionChangeDateCol, e.CreationDate()), handler.NewCol(ActionChangeDateCol, e.CreationDate()),
handler.NewCol(actionSequenceCol, e.Sequence()), handler.NewCol(ActionSequenceCol, e.Sequence()),
} }
if e.Name != nil { if e.Name != nil {
values = append(values, handler.NewCol(actionNameCol, *e.Name)) values = append(values, handler.NewCol(ActionNameCol, *e.Name))
} }
if e.Script != nil { if e.Script != nil {
values = append(values, handler.NewCol(actionScriptCol, *e.Script)) values = append(values, handler.NewCol(ActionScriptCol, *e.Script))
} }
if e.Timeout != nil { if e.Timeout != nil {
values = append(values, handler.NewCol(actionTimeoutCol, *e.Timeout)) values = append(values, handler.NewCol(ActionTimeoutCol, *e.Timeout))
} }
if e.AllowedToFail != nil { if e.AllowedToFail != nil {
values = append(values, handler.NewCol(actionAllowedToFailCol, *e.AllowedToFail)) values = append(values, handler.NewCol(ActionAllowedToFailCol, *e.AllowedToFail))
} }
return crdb.NewUpdateStatement( return crdb.NewUpdateStatement(
e, e,
values, values,
[]handler.Condition{ []handler.Condition{
handler.NewCond(actionIDCol, e.Aggregate().ID), handler.NewCond(ActionIDCol, e.Aggregate().ID),
}, },
), nil ), nil
} }
@ -124,18 +126,18 @@ func (p *ActionProjection) reduceActionChanged(event eventstore.EventReader) (*h
func (p *ActionProjection) reduceActionDeactivated(event eventstore.EventReader) (*handler.Statement, error) { func (p *ActionProjection) reduceActionDeactivated(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*action.DeactivatedEvent) e, ok := event.(*action.DeactivatedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-1gwdc", "seq", event.Sequence, "expectedType", action.DeactivatedEventType).Error("wrong event type") logging.LogWithFields("HANDL-Fhhjd", "seq", event.Sequence, "expectedType", action.DeactivatedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-BApK4", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-Fgh32", "reduce.wrong.event.type")
} }
return crdb.NewUpdateStatement( return crdb.NewUpdateStatement(
e, e,
[]handler.Column{ []handler.Column{
handler.NewCol(actionChangeDateCol, e.CreationDate()), handler.NewCol(ActionChangeDateCol, e.CreationDate()),
handler.NewCol(actionSequenceCol, e.Sequence()), handler.NewCol(ActionSequenceCol, e.Sequence()),
handler.NewCol(actionStateCol, domain.ActionStateInactive), handler.NewCol(ActionStateCol, domain.ActionStateInactive),
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(actionIDCol, e.Aggregate().ID), handler.NewCond(ActionIDCol, e.Aggregate().ID),
}, },
), nil ), nil
} }
@ -143,18 +145,18 @@ func (p *ActionProjection) reduceActionDeactivated(event eventstore.EventReader)
func (p *ActionProjection) reduceActionReactivated(event eventstore.EventReader) (*handler.Statement, error) { func (p *ActionProjection) reduceActionReactivated(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*action.ReactivatedEvent) e, ok := event.(*action.ReactivatedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-Vjwiy", "seq", event.Sequence, "expectedType", action.ReactivatedEventType).Error("wrong event type") logging.LogWithFields("HANDL-Fg4r3", "seq", event.Sequence, "expectedType", action.ReactivatedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-o37De", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-hwdqa", "reduce.wrong.event.type")
} }
return crdb.NewUpdateStatement( return crdb.NewUpdateStatement(
e, e,
[]handler.Column{ []handler.Column{
handler.NewCol(actionChangeDateCol, e.CreationDate()), handler.NewCol(ActionChangeDateCol, e.CreationDate()),
handler.NewCol(actionSequenceCol, e.Sequence()), handler.NewCol(ActionSequenceCol, e.Sequence()),
handler.NewCol(actionStateCol, domain.ActionStateActive), handler.NewCol(ActionStateCol, domain.ActionStateActive),
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(actionIDCol, e.Aggregate().ID), handler.NewCond(ActionIDCol, e.Aggregate().ID),
}, },
), nil ), nil
} }
@ -162,13 +164,13 @@ func (p *ActionProjection) reduceActionReactivated(event eventstore.EventReader)
func (p *ActionProjection) reduceActionRemoved(event eventstore.EventReader) (*handler.Statement, error) { func (p *ActionProjection) reduceActionRemoved(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*action.RemovedEvent) e, ok := event.(*action.RemovedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-79OhB", "seq", event.Sequence, "expectedType", action.RemovedEventType).Error("wrong event type") logging.LogWithFields("HANDL-Dgwh2", "seq", event.Sequence, "expectedType", action.RemovedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-4TbKT", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-Dgh2d", "reduce.wrong.event.type")
} }
return crdb.NewDeleteStatement( return crdb.NewDeleteStatement(
e, e,
[]handler.Condition{ []handler.Condition{
handler.NewCond(actionIDCol, e.Aggregate().ID), handler.NewCond(ActionIDCol, e.Aggregate().ID),
}, },
), nil ), nil
} }

View File

@ -0,0 +1,178 @@
package projection
import (
"testing"
"time"
"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/action"
)
func TestActionProjection_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: "reduceActionAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(action.AddedEventType),
action.AggregateType,
[]byte(`{"name": "name", "script":"name(){}","timeout": 3000000000, "allowedToFail": true}`),
), action.AddedEventMapper),
},
reduce: (&ActionProjection{}).reduceActionAdded,
want: wantReduce{
projection: ActionTable,
aggregateType: eventstore.AggregateType("action"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "INSERT INTO zitadel.projections.actions (id, creation_date, change_date, resource_owner, sequence, name, script, timeout, allowed_to_fail, action_state) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
expectedArgs: []interface{}{
"agg-id",
anyArg{},
anyArg{},
"ro-id",
uint64(15),
"name",
"name(){}",
3 * time.Second,
true,
domain.ActionStateActive,
},
},
},
},
{
name: "reduceActionChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(action.ChangedEventType),
action.AggregateType,
[]byte(`{"name": "name2", "script":"name2(){}"}`),
), action.ChangedEventMapper),
},
reduce: (&ActionProjection{}).reduceActionChanged,
want: wantReduce{
projection: ActionTable,
aggregateType: eventstore.AggregateType("action"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "UPDATE zitadel.projections.actions SET (change_date, sequence, name, script) = ($1, $2, $3, $4) WHERE (id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"name2",
"name2(){}",
"agg-id",
},
},
},
},
{
name: "reduceActionDeactivated",
args: args{
event: getEvent(testEvent(
repository.EventType(action.ChangedEventType),
action.AggregateType,
[]byte(`{}`),
), action.DeactivatedEventMapper),
},
reduce: (&ActionProjection{}).reduceActionDeactivated,
want: wantReduce{
projection: ActionTable,
aggregateType: eventstore.AggregateType("action"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "UPDATE zitadel.projections.actions SET (change_date, sequence, action_state) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
domain.ActionStateInactive,
"agg-id",
},
},
},
},
{
name: "reduceActionReactivated",
args: args{
event: getEvent(testEvent(
repository.EventType(action.ChangedEventType),
action.AggregateType,
[]byte(`{}`),
), action.ReactivatedEventMapper),
},
reduce: (&ActionProjection{}).reduceActionReactivated,
want: wantReduce{
projection: ActionTable,
aggregateType: eventstore.AggregateType("action"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "UPDATE zitadel.projections.actions SET (change_date, sequence, action_state) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
domain.ActionStateActive,
"agg-id",
},
},
},
},
{
name: "reduceActionRemoved",
args: args{
event: getEvent(testEvent(
repository.EventType(action.ChangedEventType),
action.AggregateType,
[]byte(`{}`),
), action.RemovedEventMapper),
},
reduce: (&ActionProjection{}).reduceActionRemoved,
want: wantReduce{
projection: ActionTable,
aggregateType: eventstore.AggregateType("action"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "DELETE FROM zitadel.projections.actions WHERE (id = $1)",
expectedArgs: []interface{}{
"agg-id",
},
},
},
},
}
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)
})
}
}

View File

@ -9,6 +9,7 @@ type Config struct {
BulkLimit uint64 BulkLimit uint64
CRDB types.SQL CRDB types.SQL
Customizations map[string]CustomConfig Customizations map[string]CustomConfig
MaxIterators int
} }
type CustomConfig struct { type CustomConfig struct {

View File

@ -0,0 +1,93 @@
package projection
import (
"context"
"github.com/caos/logging"
"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/org"
)
const (
FlowTriggerTable = "zitadel.projections.flows_triggers"
FlowTypeCol = "flow_type"
FlowTriggerTypeCol = "trigger_type"
FlowResourceOwnerCol = "resource_owner"
FlowActionTriggerSequenceCol = "trigger_sequence"
FlowActionIDCol = "action_id"
)
type FlowProjection struct {
crdb.StatementHandler
}
func NewFlowProjection(ctx context.Context, config crdb.StatementHandlerConfig) *FlowProjection {
p := &FlowProjection{}
config.ProjectionName = FlowTriggerTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p
}
func (p *FlowProjection) reducers() []handler.AggregateReducer {
return []handler.AggregateReducer{
{
Aggregate: org.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: org.TriggerActionsSetEventType,
Reduce: p.reduceTriggerActionsSetEventType,
},
{
Event: org.FlowClearedEventType,
Reduce: p.reduceFlowClearedEventType,
},
},
},
}
}
func (p *FlowProjection) reduceTriggerActionsSetEventType(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*org.TriggerActionsSetEvent)
if !ok {
logging.LogWithFields("HANDL-zWCk3", "seq", event.Sequence, "expectedType", org.TriggerActionsSetEventType).Error("was not an trigger actions set event")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-uYq4r", "reduce.wrong.event.type")
}
stmts := make([]func(reader eventstore.EventReader) crdb.Exec, len(e.ActionIDs)+1)
stmts[0] = crdb.AddDeleteStatement(
[]handler.Condition{
handler.NewCond(FlowTypeCol, e.FlowType),
handler.NewCond(FlowTriggerTypeCol, e.TriggerType),
},
)
for i, id := range e.ActionIDs {
stmts[i+1] = crdb.AddCreateStatement(
[]handler.Column{
handler.NewCol(FlowResourceOwnerCol, e.Aggregate().ResourceOwner),
handler.NewCol(FlowTypeCol, e.FlowType),
handler.NewCol(FlowTriggerTypeCol, e.TriggerType),
handler.NewCol(FlowActionIDCol, id),
handler.NewCol(FlowActionTriggerSequenceCol, i),
},
)
}
return crdb.NewMultiStatement(e, stmts...), nil
}
func (p *FlowProjection) reduceFlowClearedEventType(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*org.FlowClearedEvent)
if !ok {
logging.LogWithFields("HANDL-zWCk3", "seq", event.Sequence, "expectedType", org.FlowClearedEventType).Error("was not a flow cleared event")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-uYq4r", "reduce.wrong.event.type")
}
return crdb.NewDeleteStatement(
e,
[]handler.Condition{
handler.NewCond(FlowTypeCol, e.FlowType),
},
), nil
}

View File

@ -0,0 +1,93 @@
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/org"
)
func TestFlowProjection_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
}{
//TODO: multi stmt tests
//{
// name: "reduceTriggerActionsSetEventType",
// args: args{
// event: getEvent(testEvent(
// repository.EventType(org.TriggerActionsSetEventType),
// org.AggregateType,
// []byte(`{"flowType": 1, "triggerType": 1, "actionIDs": ["id1", "id2"]}`),
// ), org.TriggerActionsSetEventMapper),
// },
// reduce: (&FlowProjection{}).reduceTriggerActionsSetEventType,
// want: wantReduce{
// projection: FlowTriggerTable,
// aggregateType: eventstore.AggregateType("org"),
// sequence: 15,
// previousSequence: 10,
// executer: &testExecuter{
// shouldExec: true,
// expectedStmt: "DELETE FROM zitadel.projections.actions WHERE (flow_type, trigger_type) = ($1, $2); INSERT INTO zitadel.projections.actions (resource_owner, flow_type, trigger_type, action_id, trigger_sequence) = ($3, $1, $2, $4, $5); INSERT INTO zitadel.projections.actions (resource_owner, flow_type, trigger_type, action_id, trigger_sequence) = ($3, $1, $2, $6, $7)",
// expectedArgs: []interface{}{
// domain.FlowTypeExternalAuthentication,
// domain.TriggerTypePostAuthentication,
// "ro-id",
// "id1",
// 0,
// "id2",
// 1,
// },
// },
// },
//},
{
name: "reduceFlowClearedEventType",
args: args{
event: getEvent(testEvent(
repository.EventType(org.FlowClearedEventType),
org.AggregateType,
[]byte(`{"flowType": 1}`),
), org.FlowClearedEventMapper),
},
reduce: (&FlowProjection{}).reduceFlowClearedEventType,
want: wantReduce{
projection: FlowTriggerTable,
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "DELETE FROM zitadel.projections.flows_triggers WHERE (flow_type = $1)",
expectedArgs: []interface{}{
domain.FlowTypeExternalAuthentication,
},
},
},
},
}
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)
})
}
}

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
@ -16,9 +17,13 @@ type OrgProjection struct {
crdb.StatementHandler crdb.StatementHandler
} }
const (
OrgProjectionTable = "zitadel.projections.orgs"
)
func NewOrgProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgProjection { func NewOrgProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgProjection {
p := &OrgProjection{} p := &OrgProjection{}
config.ProjectionName = "projections.orgs" config.ProjectionName = OrgProjectionTable
config.Reducers = p.reducers() config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config) p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p return p
@ -54,33 +59,35 @@ func (p *OrgProjection) reducers() []handler.AggregateReducer {
} }
} }
type OrgColumn string
const ( const (
orgIDCol = "id" OrgColumnID = "id"
orgCreationDateCol = "creation_date" OrgColumnCreationDate = "creation_date"
orgChangeDateCol = "change_date" OrgColumnChangeDate = "change_date"
orgResourceOwnerCol = "resource_owner" OrgColumnResourceOwner = "resource_owner"
orgStateCol = "org_state" OrgColumnState = "org_state"
orgSequenceCol = "sequence" OrgColumnSequence = "sequence"
orgDomainCol = "domain" OrgColumnName = "name"
orgNameCol = "name" OrgColumnDomain = "primary_domain"
) )
func (p *OrgProjection) reduceOrgAdded(event eventstore.EventReader) (*handler.Statement, error) { func (p *OrgProjection) reduceOrgAdded(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*org.OrgAddedEvent) e, ok := event.(*org.OrgAddedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-zWCk3", "seq", event.Sequence, "expectedType", org.OrgAddedEventType).Error("was not an event") logging.LogWithFields("HANDL-zWCk3", "seq", event.Sequence(), "expectedType", org.OrgAddedEventType).Error("was not an event")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-uYq4r", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-uYq4r", "reduce.wrong.event.type")
} }
return crdb.NewCreateStatement( return crdb.NewCreateStatement(
e, e,
[]handler.Column{ []handler.Column{
handler.NewCol(orgIDCol, e.Aggregate().ID), handler.NewCol(OrgColumnID, e.Aggregate().ID),
handler.NewCol(orgCreationDateCol, e.CreationDate()), handler.NewCol(OrgColumnCreationDate, e.CreationDate()),
handler.NewCol(orgChangeDateCol, e.CreationDate()), handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
handler.NewCol(orgResourceOwnerCol, e.Aggregate().ResourceOwner), handler.NewCol(OrgColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCol(orgSequenceCol, e.Sequence()), handler.NewCol(OrgColumnSequence, e.Sequence()),
handler.NewCol(orgNameCol, e.Name), handler.NewCol(OrgColumnName, e.Name),
handler.NewCol(orgStateCol, domain.OrgStateActive), handler.NewCol(OrgColumnState, domain.OrgStateActive),
}, },
), nil ), nil
} }
@ -88,21 +95,21 @@ func (p *OrgProjection) reduceOrgAdded(event eventstore.EventReader) (*handler.S
func (p *OrgProjection) reduceOrgChanged(event eventstore.EventReader) (*handler.Statement, error) { func (p *OrgProjection) reduceOrgChanged(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*org.OrgChangedEvent) e, ok := event.(*org.OrgChangedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-q4oq8", "seq", event.Sequence, "expected", org.OrgChangedEventType).Error("wrong event type") logging.LogWithFields("HANDL-q4oq8", "seq", event.Sequence(), "expected", org.OrgChangedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-Bg8oM", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-Bg8oM", "reduce.wrong.event.type")
} }
values := []handler.Column{ if e.Name == "" {
handler.NewCol(orgChangeDateCol, e.CreationDate()), return crdb.NewNoOpStatement(e), nil
handler.NewCol(orgSequenceCol, e.Sequence()),
}
if e.Name != "" {
values = append(values, handler.NewCol(orgNameCol, e.Name))
} }
return crdb.NewUpdateStatement( return crdb.NewUpdateStatement(
e, e,
values, []handler.Column{
handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
handler.NewCol(OrgColumnSequence, e.Sequence()),
handler.NewCol(OrgColumnName, e.Name),
},
[]handler.Condition{ []handler.Condition{
handler.NewCond(orgIDCol, e.Aggregate().ID), handler.NewCond(OrgColumnID, e.Aggregate().ID),
}, },
), nil ), nil
} }
@ -110,18 +117,18 @@ func (p *OrgProjection) reduceOrgChanged(event eventstore.EventReader) (*handler
func (p *OrgProjection) reduceOrgDeactivated(event eventstore.EventReader) (*handler.Statement, error) { func (p *OrgProjection) reduceOrgDeactivated(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*org.OrgDeactivatedEvent) e, ok := event.(*org.OrgDeactivatedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-1gwdc", "seq", event.Sequence, "expectedType", org.OrgDeactivatedEventType).Error("wrong event type") logging.LogWithFields("HANDL-1gwdc", "seq", event.Sequence(), "expectedType", org.OrgDeactivatedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-BApK4", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-BApK4", "reduce.wrong.event.type")
} }
return crdb.NewUpdateStatement( return crdb.NewUpdateStatement(
e, e,
[]handler.Column{ []handler.Column{
handler.NewCol(orgChangeDateCol, e.CreationDate()), handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
handler.NewCol(orgSequenceCol, e.Sequence()), handler.NewCol(OrgColumnSequence, e.Sequence()),
handler.NewCol(orgStateCol, domain.OrgStateInactive), handler.NewCol(OrgColumnState, domain.OrgStateInactive),
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(orgIDCol, e.Aggregate().ID), handler.NewCond(OrgColumnID, e.Aggregate().ID),
}, },
), nil ), nil
} }
@ -129,18 +136,18 @@ func (p *OrgProjection) reduceOrgDeactivated(event eventstore.EventReader) (*han
func (p *OrgProjection) reduceOrgReactivated(event eventstore.EventReader) (*handler.Statement, error) { func (p *OrgProjection) reduceOrgReactivated(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*org.OrgReactivatedEvent) e, ok := event.(*org.OrgReactivatedEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-Vjwiy", "seq", event.Sequence, "expectedType", org.OrgReactivatedEventType).Error("wrong event type") logging.LogWithFields("HANDL-Vjwiy", "seq", event.Sequence(), "expectedType", org.OrgReactivatedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-o37De", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-o37De", "reduce.wrong.event.type")
} }
return crdb.NewUpdateStatement( return crdb.NewUpdateStatement(
e, e,
[]handler.Column{ []handler.Column{
handler.NewCol(orgChangeDateCol, e.CreationDate()), handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
handler.NewCol(orgSequenceCol, e.Sequence()), handler.NewCol(OrgColumnSequence, e.Sequence()),
handler.NewCol(orgStateCol, domain.OrgStateActive), handler.NewCol(OrgColumnState, domain.OrgStateActive),
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(orgIDCol, e.Aggregate().ID), handler.NewCond(OrgColumnID, e.Aggregate().ID),
}, },
), nil ), nil
} }
@ -148,18 +155,18 @@ func (p *OrgProjection) reduceOrgReactivated(event eventstore.EventReader) (*han
func (p *OrgProjection) reducePrimaryDomainSet(event eventstore.EventReader) (*handler.Statement, error) { func (p *OrgProjection) reducePrimaryDomainSet(event eventstore.EventReader) (*handler.Statement, error) {
e, ok := event.(*org.DomainPrimarySetEvent) e, ok := event.(*org.DomainPrimarySetEvent)
if !ok { if !ok {
logging.LogWithFields("HANDL-79OhB", "seq", event.Sequence, "expectedType", org.OrgDomainPrimarySetEventType).Error("wrong event type") logging.LogWithFields("HANDL-79OhB", "seq", event.Sequence(), "expectedType", org.OrgDomainPrimarySetEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-4TbKT", "reduce.wrong.event.type") return nil, errors.ThrowInvalidArgument(nil, "HANDL-4TbKT", "reduce.wrong.event.type")
} }
return crdb.NewUpdateStatement( return crdb.NewUpdateStatement(
e, e,
[]handler.Column{ []handler.Column{
handler.NewCol(orgChangeDateCol, e.CreationDate()), handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
handler.NewCol(orgSequenceCol, e.Sequence()), handler.NewCol(OrgColumnSequence, e.Sequence()),
handler.NewCol(orgDomainCol, e.Domain), handler.NewCol(OrgColumnDomain, e.Domain),
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(orgIDCol, e.Aggregate().ID), handler.NewCond(OrgColumnID, e.Aggregate().ID),
}, },
), nil ), nil
} }

View File

@ -16,15 +16,15 @@ import (
) )
type OrgOwner struct { type OrgOwner struct {
OrgID string `col:"org_id"` OrgID string
OrgName string `col:"org_name"` OrgName string
OrgCreationDate time.Time `col:"org_creation_date"` OrgCreationDate time.Time
OwnerID string `col:"owner_id"` OwnerID string
OwnerLanguage *language.Tag `col:"owner_language"` OwnerLanguage *language.Tag
OwnerEmailAddress string `col:"owner_email"` OwnerEmailAddress string
OwnerFirstName string `col:"owner_first_name"` OwnerFirstName string
OwnerLastName string `col:"owner_last_name"` OwnerLastName string
OwnerGender domain.Gender `col:"owner_gender"` OwnerGender domain.Gender
} }
type OrgOwnerProjection struct { type OrgOwnerProjection struct {
@ -49,7 +49,7 @@ const (
func NewOrgOwnerProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgOwnerProjection { func NewOrgOwnerProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgOwnerProjection {
p := &OrgOwnerProjection{} p := &OrgOwnerProjection{}
config.ProjectionName = "projections.org_owners" config.ProjectionName = "zitadel.projections.org_owners"
config.Reducers = p.reducers() config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config) p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p return p

View File

@ -0,0 +1,196 @@
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/org"
)
func TestOrgProjection_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: "reducePrimaryDomainSet",
args: args{
event: getEvent(testEvent(
repository.EventType(org.OrgDomainPrimarySetEventType),
org.AggregateType,
[]byte(`{"domain": "domain.new"}`),
), org.DomainPrimarySetEventMapper),
},
reduce: (&OrgProjection{}).reducePrimaryDomainSet,
want: wantReduce{
projection: OrgProjectionTable,
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "UPDATE zitadel.projections.orgs SET (change_date, sequence, primary_domain) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"domain.new",
"agg-id",
},
},
},
},
{
name: "reduceOrgReactivated",
args: args{
event: getEvent(testEvent(
repository.EventType(org.OrgReactivatedEventType),
org.AggregateType,
nil,
), org.OrgReactivatedEventMapper),
},
reduce: (&OrgProjection{}).reduceOrgReactivated,
want: wantReduce{
projection: OrgProjectionTable,
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "UPDATE zitadel.projections.orgs SET (change_date, sequence, org_state) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
domain.OrgStateActive,
"agg-id",
},
},
},
},
{
name: "reduceOrgDeactivated",
args: args{
event: getEvent(testEvent(
repository.EventType(org.OrgDeactivatedEventType),
org.AggregateType,
nil,
), org.OrgDeactivatedEventMapper),
},
reduce: (&OrgProjection{}).reduceOrgDeactivated,
want: wantReduce{
projection: OrgProjectionTable,
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "UPDATE zitadel.projections.orgs SET (change_date, sequence, org_state) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
domain.OrgStateInactive,
"agg-id",
},
},
},
},
{
name: "reduceOrgChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(org.OrgChangedEventType),
org.AggregateType,
[]byte(`{"name": "new name"}`),
), org.OrgChangedEventMapper),
},
reduce: (&OrgProjection{}).reduceOrgChanged,
want: wantReduce{
projection: OrgProjectionTable,
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "UPDATE zitadel.projections.orgs SET (change_date, sequence, name) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"new name",
"agg-id",
},
},
},
},
{
name: "reduceOrgChanged no changes",
args: args{
event: getEvent(testEvent(
repository.EventType(org.OrgChangedEventType),
org.AggregateType,
[]byte(`{}`),
), org.OrgChangedEventMapper),
},
reduce: (&OrgProjection{}).reduceOrgChanged,
want: wantReduce{
projection: OrgProjectionTable,
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: false,
},
},
},
{
name: "reduceOrgAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(org.OrgAddedEventType),
org.AggregateType,
[]byte(`{"name": "name"}`),
), org.OrgAddedEventMapper),
},
reduce: (&OrgProjection{}).reduceOrgAdded,
want: wantReduce{
projection: OrgProjectionTable,
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
shouldExec: true,
expectedStmt: "INSERT INTO zitadel.projections.orgs (id, creation_date, change_date, resource_owner, sequence, name, org_state) VALUES ($1, $2, $3, $4, $5, $6, $7)",
expectedArgs: []interface{}{
"agg-id",
anyArg{},
anyArg{},
"ro-id",
uint64(15),
"name",
domain.OrgStateActive,
},
},
},
},
}
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)
})
}
}

View File

@ -15,7 +15,7 @@ type ProjectProjection struct {
func NewProjectProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ProjectProjection { func NewProjectProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ProjectProjection {
p := &ProjectProjection{} p := &ProjectProjection{}
config.ProjectionName = "projections.projects" config.ProjectionName = "zitadel.projections.projects"
config.Reducers = p.reducers() config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config) p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p return p

View File

@ -2,25 +2,21 @@ package projection
import ( import (
"context" "context"
"database/sql"
"time"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/handler" "github.com/caos/zitadel/internal/eventstore/handler"
"github.com/caos/zitadel/internal/eventstore/handler/crdb" "github.com/caos/zitadel/internal/eventstore/handler/crdb"
"github.com/caos/zitadel/internal/query/projection/flow"
) )
const ( const (
currentSeqTable = "projections.current_sequences" CurrentSeqTable = "projections.current_sequences"
locksTable = "projections.locks" locksTable = "projections.locks"
failedEventsTable = "projections.failed_events" failedEventsTable = "projections.failed_events"
) )
func Start(ctx context.Context, es *eventstore.Eventstore, config Config) error { func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, config Config) error {
sqlClient, err := config.CRDB.Start()
if err != nil {
return err
}
projectionConfig := crdb.StatementHandlerConfig{ projectionConfig := crdb.StatementHandlerConfig{
ProjectionHandlerConfig: handler.ProjectionHandlerConfig{ ProjectionHandlerConfig: handler.ProjectionHandlerConfig{
HandlerConfig: handler.HandlerConfig{ HandlerConfig: handler.HandlerConfig{
@ -30,19 +26,18 @@ func Start(ctx context.Context, es *eventstore.Eventstore, config Config) error
RetryFailedAfter: config.RetryFailedAfter.Duration, RetryFailedAfter: config.RetryFailedAfter.Duration,
}, },
Client: sqlClient, Client: sqlClient,
SequenceTable: currentSeqTable, SequenceTable: CurrentSeqTable,
LockTable: locksTable, LockTable: locksTable,
FailedEventsTable: failedEventsTable, FailedEventsTable: failedEventsTable,
MaxFailureCount: config.MaxFailureCount, MaxFailureCount: config.MaxFailureCount,
BulkLimit: config.BulkLimit, BulkLimit: config.BulkLimit,
} }
// turned off for this release NewOrgProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["orgs"]))
//NewOrgProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["orgs"]))
//NewProjectProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["projects"])) //NewProjectProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["projects"]))
//owner.NewOrgOwnerProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["org_owners"])) //owner.NewOrgOwnerProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["org_owners"]))
NewActionProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["actions"])) NewActionProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["actions"]))
flow.NewFlowProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["flows"])) NewFlowProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["flows"]))
return nil return nil
} }
@ -62,3 +57,20 @@ func applyCustomConfig(config crdb.StatementHandlerConfig, customConfig CustomCo
return config return config
} }
func iteratorPool(workerCount int) chan func() {
if workerCount <= 0 {
return nil
}
queue := make(chan func())
for i := 0; i < workerCount; i++ {
go func() {
for iteration := range queue {
iteration()
time.Sleep(2 * time.Second)
}
}()
}
return queue
}

View File

@ -0,0 +1,87 @@
package projection
import (
"testing"
"time"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/handler"
"github.com/caos/zitadel/internal/eventstore/repository"
)
func testEvent(
eventType repository.EventType,
aggregateType repository.AggregateType,
data []byte,
) *repository.Event {
return &repository.Event{
Sequence: 15,
PreviousAggregateSequence: 10,
PreviousAggregateTypeSequence: 10,
CreationDate: time.Now(),
Type: eventType,
AggregateType: aggregateType,
Data: data,
Version: "v1",
AggregateID: "agg-id",
ResourceOwner: "ro-id",
ID: "event-id",
EditorService: "editor-svc",
EditorUser: "editor-user",
}
}
func baseEvent(*testing.T) eventstore.EventReader {
return &eventstore.BaseEvent{}
}
func getEvent(event *repository.Event, mapper func(*repository.Event) (eventstore.EventReader, error)) func(t *testing.T) eventstore.EventReader {
return func(t *testing.T) eventstore.EventReader {
e, err := mapper(event)
if err != nil {
t.Fatalf("mapper failed: %v", err)
}
return e
}
}
type wantReduce struct {
projection string
aggregateType eventstore.AggregateType
sequence uint64
previousSequence uint64
executer *testExecuter
err func(error) bool
}
func assertReduce(t *testing.T, stmt *handler.Statement, err error, want wantReduce) {
t.Helper()
if want.err == nil && err != nil {
t.Errorf("unexpected error of type %T: %v", err, err)
return
}
if want.err != nil && want.err(err) {
return
}
if stmt.AggregateType != want.aggregateType {
t.Errorf("wront aggregate type: want: %q got: %q", want.aggregateType, stmt.AggregateType)
}
if stmt.PreviousSequence != want.previousSequence {
t.Errorf("wront previous sequence: want: %d got: %d", want.previousSequence, stmt.PreviousSequence)
}
if stmt.Sequence != want.sequence {
t.Errorf("wront sequence: want: %d got: %d", want.sequence, stmt.Sequence)
}
if stmt.Execute == nil {
want.executer.Validate(t)
return
}
err = stmt.Execute(want.executer, want.projection)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
want.executer.Validate(t)
}

View File

@ -0,0 +1,48 @@
package projection
import (
"database/sql"
"testing"
)
type testExecuter struct {
expectedStmt string
gottenStmt string
shouldExec bool
expectedArgs []interface{}
gottenArgs []interface{}
gotExecuted bool
}
type anyArg struct{}
func (e *testExecuter) Exec(stmt string, args ...interface{}) (sql.Result, error) {
e.gottenStmt = stmt
e.gottenArgs = args
e.gotExecuted = true
return nil, nil
}
func (e *testExecuter) Validate(t *testing.T) {
t.Helper()
if e.shouldExec != e.gotExecuted {
t.Error("expected to be executed")
return
}
if len(e.gottenArgs) != len(e.expectedArgs) {
t.Errorf("wrong arg len expected: %d got: %d", len(e.expectedArgs), len(e.gottenArgs))
} else {
for i := 0; i < len(e.expectedArgs); i++ {
if _, ok := e.expectedArgs[i].(anyArg); ok {
continue
}
if e.expectedArgs[i] != e.gottenArgs[i] {
t.Errorf("wrong argument at index %d: got: %v want: %v", i, e.gottenArgs[i], e.expectedArgs[i])
}
}
}
if e.gottenStmt != e.expectedStmt {
t.Errorf("wrong stmt want:\n%s\ngot:\n%s", e.expectedStmt, e.gottenStmt)
}
}

View File

@ -6,10 +6,8 @@ import (
sd "github.com/caos/zitadel/internal/config/systemdefaults" sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/config/types"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/id"
"github.com/caos/zitadel/internal/query/projection" "github.com/caos/zitadel/internal/query/projection"
"github.com/caos/zitadel/internal/repository/action" "github.com/caos/zitadel/internal/repository/action"
iam_repo "github.com/caos/zitadel/internal/repository/iam" iam_repo "github.com/caos/zitadel/internal/repository/iam"
@ -20,12 +18,9 @@ import (
) )
type Queries struct { type Queries struct {
iamID string iamID string
eventstore *eventstore.Eventstore eventstore *eventstore.Eventstore
idGenerator id.Generator client *sql.DB
secretCrypto crypto.Crypto
client *sql.DB
} }
type Config struct { type Config struct {
@ -39,10 +34,9 @@ func StartQueries(ctx context.Context, es *eventstore.Eventstore, projections pr
} }
repo = &Queries{ repo = &Queries{
iamID: defaults.IamID, iamID: defaults.IamID,
eventstore: es, eventstore: es,
idGenerator: id.SonyFlakeGenerator, client: sqlClient,
client: sqlClient,
} }
iam_repo.RegisterEventMappers(repo.eventstore) iam_repo.RegisterEventMappers(repo.eventstore)
usr_repo.RegisterEventMappers(repo.eventstore) usr_repo.RegisterEventMappers(repo.eventstore)
@ -50,12 +44,7 @@ func StartQueries(ctx context.Context, es *eventstore.Eventstore, projections pr
project.RegisterEventMappers(repo.eventstore) project.RegisterEventMappers(repo.eventstore)
action.RegisterEventMappers(repo.eventstore) action.RegisterEventMappers(repo.eventstore)
repo.secretCrypto, err = crypto.NewAESCrypto(defaults.IDPConfigVerificationKey) err = projection.Start(ctx, sqlClient, es, projections)
if err != nil {
return nil, err
}
err = projection.Start(ctx, es, projections)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,19 +2,27 @@ package query
import ( import (
"errors" "errors"
"strings" "reflect"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
"github.com/lib/pq"
"github.com/caos/zitadel/internal/domain"
) )
type SearchResponse struct {
Count uint64
*LatestSequence
}
type SearchRequest struct { type SearchRequest struct {
Offset uint64 Offset uint64
Limit uint64 Limit uint64
SortingColumn string SortingColumn Column
Asc bool Asc bool
} }
func (req *SearchRequest) ToQuery(query sq.SelectBuilder) sq.SelectBuilder { func (req *SearchRequest) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
if req.Offset > 0 { if req.Offset > 0 {
query = query.Offset(req.Offset) query = query.Offset(req.Offset)
} }
@ -22,12 +30,12 @@ func (req *SearchRequest) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
query = query.Limit(req.Limit) query = query.Limit(req.Limit)
} }
if req.SortingColumn != "" { if !req.SortingColumn.isZero() {
clause := "LOWER(?)" clause := "LOWER(" + sqlPlaceholder + ")"
if !req.Asc { if !req.Asc {
clause += " DESC" clause += " DESC"
} }
query.OrderByClause(clause, req.SortingColumn) query = query.OrderByClause(clause, req.SortingColumn.identifier())
} }
return query return query
@ -40,110 +48,232 @@ type SearchQuery interface {
} }
type TextQuery struct { type TextQuery struct {
Column string Column Column
Text string Text string
Compare TextComparison Compare TextComparison
} }
func NewTextQuery(column, value string, compare TextComparison) (*TextQuery, error) { var (
if compare < 0 || compare >= textMax { ErrInvalidCompare = errors.New("invalid compare")
return nil, errors.New("invalid compare") ErrMissingColumn = errors.New("missing column")
ErrInvalidNumber = errors.New("value is no number")
)
func NewTextQuery(col Column, value string, compare TextComparison) (*TextQuery, error) {
if compare < 0 || compare >= textCompareMax {
return nil, ErrInvalidCompare
} }
if column == "" { if col.isZero() {
return nil, errors.New("missing column") return nil, ErrMissingColumn
} }
return &TextQuery{ return &TextQuery{
Column: column, Column: col,
Text: value, Text: value,
Compare: compare, Compare: compare,
}, nil }, nil
} }
func (q *TextQuery) ToQuery(query sq.SelectBuilder) sq.SelectBuilder { func (q *TextQuery) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
query = query.Where(q.comp()) where, args := q.comp()
return query return query.Where(where, args...)
} }
func (s *TextQuery) comp() map[string]interface{} { func (s *TextQuery) comp() (comparison interface{}, args []interface{}) {
switch s.Compare { switch s.Compare {
case TextEquals: case TextEquals:
return sq.Eq{s.Column: s.Text} return sq.Eq{s.Column.identifier(): s.Text}, nil
case TextEqualsIgnore: case TextEqualsIgnoreCase:
return sq.Eq{"LOWER(" + s.Column + ")": strings.ToLower(s.Text)} return sq.ILike{s.Column.identifier(): s.Text}, nil
case TextStartsWith: case TextStartsWith:
return sq.Like{s.Column: s.Text + sqlPlaceholder} return sq.Like{s.Column.identifier(): s.Text + "%"}, nil
case TextStartsWithIgnore: case TextStartsWithIgnoreCase:
return sq.Like{"LOWER(" + s.Column + ")": strings.ToLower(s.Text) + sqlPlaceholder} return sq.ILike{s.Column.identifier(): s.Text + "%"}, nil
case TextEndsWith: case TextEndsWith:
return sq.Like{s.Column: sqlPlaceholder + s.Text} return sq.Like{s.Column.identifier(): "%" + s.Text}, nil
case TextEndsWithIgnore: case TextEndsWithIgnoreCase:
return sq.Like{"LOWER(" + s.Column + ")": sqlPlaceholder + strings.ToLower(s.Text)} return sq.ILike{s.Column.identifier(): "%" + s.Text}, nil
case TextContains: case TextContains:
return sq.Like{s.Column: sqlPlaceholder + s.Text + sqlPlaceholder} return sq.Like{s.Column.identifier(): "%" + s.Text + "%"}, nil
case TextContainsIgnore: case TextContainsIgnoreCase:
return sq.Like{"LOWER(" + s.Column + ")": sqlPlaceholder + strings.ToLower(s.Text) + sqlPlaceholder} return sq.ILike{s.Column.identifier(): "%" + s.Text + "%"}, nil
case TextListContains:
return s.Column.identifier() + " @> ? ", []interface{}{pq.StringArray{s.Text}}
} }
return nil return nil, nil
} }
type TextComparison int type TextComparison int
const ( const (
TextEquals TextComparison = iota TextEquals TextComparison = iota
TextEqualsIgnore TextEqualsIgnoreCase
TextStartsWith TextStartsWith
TextStartsWithIgnore TextStartsWithIgnoreCase
TextEndsWith TextEndsWith
TextEndsWithIgnore TextEndsWithIgnoreCase
TextContains TextContains
TextContainsIgnore TextContainsIgnoreCase
TextListContains
textMax textCompareMax
) )
type IntQuery struct { //Deprecated: Use TextComparison, will be removed as soon as all calls are changed to query
Column string func TextComparisonFromMethod(m domain.SearchMethod) TextComparison {
Int int switch m {
Compare IntComparison case domain.SearchMethodEquals:
return TextEquals
case domain.SearchMethodEqualsIgnoreCase:
return TextEqualsIgnoreCase
case domain.SearchMethodStartsWith:
return TextStartsWith
case domain.SearchMethodStartsWithIgnoreCase:
return TextStartsWithIgnoreCase
case domain.SearchMethodContains:
return TextContains
case domain.SearchMethodContainsIgnoreCase:
return TextContainsIgnoreCase
case domain.SearchMethodEndsWith:
return TextEndsWith
case domain.SearchMethodEndsWithIgnoreCase:
return TextEndsWithIgnoreCase
case domain.SearchMethodListContains:
return TextListContains
default:
return textCompareMax
}
} }
func NewIntQuery(column string, value int, compare IntComparison) (*IntQuery, error) { type NumberQuery struct {
if compare < 0 || compare >= intMax { Column Column
return nil, errors.New("invalid compare") Number interface{}
Compare NumberComparison
}
func NewNumberQuery(c Column, value interface{}, compare NumberComparison) (*NumberQuery, error) {
if compare < 0 || compare >= numberCompareMax {
return nil, ErrInvalidCompare
} }
if column == "" { if c.isZero() {
return nil, errors.New("missing column") return nil, ErrMissingColumn
} }
return &IntQuery{ switch reflect.TypeOf(value).Kind() {
Column: column, case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
Int: value, //everything fine
default:
return nil, ErrInvalidNumber
}
return &NumberQuery{
Column: c,
Number: value,
Compare: compare, Compare: compare,
}, nil }, nil
} }
func (q *IntQuery) ToQuery(query sq.SelectBuilder) sq.SelectBuilder { func (q *NumberQuery) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
query = query.Where(q.comp()) where, args := q.comp()
return query return query.Where(where, args...)
} }
func (s *IntQuery) comp() sq.Sqlizer { func (s *NumberQuery) comp() (comparison interface{}, args []interface{}) {
switch s.Compare { switch s.Compare {
case IntEquals: case NumberEquals:
return sq.Eq{s.Column: s.Int} return sq.Eq{s.Column.identifier(): s.Number}, nil
case IntGreater: case NumberNotEquals:
return sq.Gt{s.Column: s.Int} return sq.NotEq{s.Column.identifier(): s.Number}, nil
case IntLess: case NumberLess:
return sq.Lt{s.Column: s.Int} return sq.Lt{s.Column.identifier(): s.Number}, nil
case NumberGreater:
return sq.Gt{s.Column.identifier(): s.Number}, nil
case NumberListContains:
return s.Column.identifier() + " @> ? ", []interface{}{pq.Array(s.Number)}
} }
return nil return nil, nil
} }
type IntComparison int type NumberComparison int
const ( const (
IntEquals IntComparison = iota NumberEquals NumberComparison = iota
IntGreater NumberNotEquals
IntLess NumberLess
NumberGreater
NumberListContains
intMax numberCompareMax
) )
//Deprecated: Use NumberComparison, will be removed as soon as all calls are changed to query
func NumberComparisonFromMethod(m domain.SearchMethod) NumberComparison {
switch m {
case domain.SearchMethodEquals:
return NumberEquals
case domain.SearchMethodNotEquals:
return NumberNotEquals
case domain.SearchMethodGreaterThan:
return NumberGreater
case domain.SearchMethodLessThan:
return NumberLess
case domain.SearchMethodListContains:
return NumberListContains
default:
return numberCompareMax
}
}
var (
//countColumn represents the default counter for search responses
countColumn = Column{
name: "COUNT(*) OVER ()",
}
//uniqueColumn shows if there are any results
uniqueColumn = Column{
name: "COUNT(*) = 0",
}
)
type table struct {
name string
alias string
}
func (t table) setAlias(a string) table {
t.alias = a
return t
}
func (t table) identifier() string {
if t.alias == "" {
return t.name
}
return t.name + " as " + t.alias
}
func (t table) isZero() bool {
return t.name == ""
}
type Column struct {
name string
table table
}
func (c Column) identifier() string {
if c.table.alias == "" {
return c.table.name + "." + c.name
}
return c.table.alias + "." + c.name
}
func (c Column) setTable(t table) Column {
c.table = t
return c
}
func (c Column) isZero() bool {
return c.table.isZero() || c.name == ""
}
func join(join, from Column) string {
return join.table.identifier() + " ON " + from.identifier() + " = " + join.identifier()
}

View File

@ -0,0 +1,767 @@
package query
import (
"errors"
"reflect"
"testing"
sq "github.com/Masterminds/squirrel"
"github.com/caos/zitadel/internal/domain"
"github.com/lib/pq"
)
var (
testTable = table{
name: "test_table",
alias: "test_table",
}
testCol = Column{
name: "test_col",
table: testTable,
}
testNoCol = Column{
name: "",
table: testTable,
}
)
func TestSearchRequest_ToQuery(t *testing.T) {
type fields struct {
Offset uint64
Limit uint64
SortingColumn Column
Asc bool
}
type want struct {
stmtAddition string
args []interface{}
}
tests := []struct {
name string
fields fields
want want
}{
{
name: "no queries",
fields: fields{},
want: want{
stmtAddition: "",
args: nil,
},
},
{
name: "offset",
fields: fields{
Offset: 5,
},
want: want{
stmtAddition: "OFFSET 5",
args: nil,
},
},
{
name: "limit",
fields: fields{
Limit: 5,
},
want: want{
stmtAddition: "LIMIT 5",
args: nil,
},
},
{
name: "sort asc",
fields: fields{
SortingColumn: testCol,
Asc: true,
},
want: want{
stmtAddition: "ORDER BY LOWER(?)",
args: []interface{}{"test_table.test_col"},
},
},
{
name: "sort desc",
fields: fields{
SortingColumn: testCol,
},
want: want{
stmtAddition: "ORDER BY LOWER(?) DESC",
args: []interface{}{"test_table.test_col"},
},
},
{
name: "all",
fields: fields{
Offset: 5,
Limit: 10,
SortingColumn: testCol,
Asc: true,
},
want: want{
stmtAddition: "ORDER BY LOWER(?) LIMIT 10 OFFSET 5",
args: []interface{}{"test_table.test_col"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := &SearchRequest{
Offset: tt.fields.Offset,
Limit: tt.fields.Limit,
SortingColumn: tt.fields.SortingColumn,
Asc: tt.fields.Asc,
}
query := sq.Select((testCol).identifier()).From(testTable.identifier())
expectedQuery, _, _ := query.ToSql()
stmt, args, err := req.toQuery(query).ToSql()
if len(tt.want.stmtAddition) > 0 {
expectedQuery += " " + tt.want.stmtAddition
}
if expectedQuery != stmt {
t.Errorf("stmt = %q, want %q", stmt, expectedQuery)
}
if !reflect.DeepEqual(args, tt.want.args) {
t.Errorf("args = %v, want %v", args, tt.want.stmtAddition)
}
if err != nil {
t.Errorf("no error expected but got %v", err)
}
})
}
}
func TestNewTextQuery(t *testing.T) {
type args struct {
column Column
value string
compare TextComparison
}
tests := []struct {
name string
args args
want *TextQuery
wantErr func(error) bool
}{
{
name: "too low compare",
args: args{
column: testCol,
value: "hurst",
compare: -1,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrInvalidCompare)
},
},
{
name: "too high compare",
args: args{
column: testCol,
value: "hurst",
compare: textCompareMax,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrInvalidCompare)
},
},
{
name: "no column",
args: args{
column: Column{},
value: "hurst",
compare: TextEquals,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrMissingColumn)
},
},
{
name: "no column name",
args: args{
column: testNoCol,
value: "hurst",
compare: TextEquals,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrMissingColumn)
},
},
{
name: "correct",
args: args{
column: testCol,
value: "hurst",
compare: TextEquals,
},
want: &TextQuery{
Column: testCol,
Text: "hurst",
Compare: TextEquals,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewTextQuery(tt.args.column, tt.args.value, tt.args.compare)
if err != nil && tt.wantErr == nil {
t.Errorf("NewTextQuery() no error expected got %v", err)
return
} else if tt.wantErr != nil && !tt.wantErr(err) {
t.Errorf("NewTextQuery() unexpeted error = %v", err)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewTextQuery() = %v, want %v", got, tt.want)
}
})
}
}
func TestTextQuery_comp(t *testing.T) {
type fields struct {
Column Column
Text string
Compare TextComparison
}
type want struct {
query interface{}
args []interface{}
isNil bool
}
tests := []struct {
name string
fields fields
want want
}{
{
name: "equals",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextEquals,
},
want: want{
query: sq.Eq{"test_table.test_col": "Hurst"},
args: nil,
},
},
{
name: "equals ignore case",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextEqualsIgnoreCase,
},
want: want{
query: sq.ILike{"test_table.test_col": "Hurst"},
args: nil,
},
},
{
name: "starts with",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextStartsWith,
},
want: want{
query: sq.Like{"test_table.test_col": "Hurst%"},
args: nil,
},
},
{
name: "starts with ignore case",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextStartsWithIgnoreCase,
},
want: want{
query: sq.ILike{"test_table.test_col": "Hurst%"},
args: nil,
},
},
{
name: "ends with",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextEndsWith,
},
want: want{
query: sq.Like{"test_table.test_col": "%Hurst"},
args: nil,
},
},
{
name: "ends with ignore case",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextEndsWithIgnoreCase,
},
want: want{
query: sq.ILike{"test_table.test_col": "%Hurst"},
args: nil,
},
},
{
name: "contains",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextContains,
},
want: want{
query: sq.Like{"test_table.test_col": "%Hurst%"},
args: nil,
},
},
{
name: "containts ignore case",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextContainsIgnoreCase,
},
want: want{
query: sq.ILike{"test_table.test_col": "%Hurst%"},
args: nil,
},
},
{
name: "list containts",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: TextListContains,
},
want: want{
query: "test_table.test_col @> ? ",
args: []interface{}{pq.StringArray{"Hurst"}},
},
},
{
name: "too high comparison",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: textCompareMax,
},
want: want{
isNil: true,
},
},
{
name: "too low comparison",
fields: fields{
Column: testCol,
Text: "Hurst",
Compare: -1,
},
want: want{
isNil: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &TextQuery{
Column: tt.fields.Column,
Text: tt.fields.Text,
Compare: tt.fields.Compare,
}
query, args := s.comp()
if query == nil && tt.want.isNil {
return
} else if tt.want.isNil && query != nil {
t.Error("query should not be nil")
}
if !reflect.DeepEqual(query, tt.want.query) {
t.Errorf("wrong query: want: %v, (%T), got: %v, (%T)", tt.want.query, tt.want.query, query, query)
}
if !reflect.DeepEqual(args, tt.want.args) {
t.Errorf("wrong args: want: %v, (%T), got: %v (%T)", tt.want.args, tt.want.args, args, args)
}
})
}
}
func TestTextComparisonFromMethod(t *testing.T) {
type args struct {
m domain.SearchMethod
}
tests := []struct {
name string
args args
want TextComparison
}{
{
name: "equals",
args: args{
m: domain.SearchMethodEquals,
},
want: TextEquals,
},
{
name: "equals ignore case",
args: args{
m: domain.SearchMethodEqualsIgnoreCase,
},
want: TextEqualsIgnoreCase,
},
{
name: "starts with",
args: args{
m: domain.SearchMethodStartsWith,
},
want: TextStartsWith,
},
{
name: "starts with ignore case",
args: args{
m: domain.SearchMethodStartsWithIgnoreCase,
},
want: TextStartsWithIgnoreCase,
},
{
name: "ends with",
args: args{
m: domain.SearchMethodEndsWith,
},
want: TextEndsWith,
},
{
name: "ends with ignore case",
args: args{
m: domain.SearchMethodEndsWithIgnoreCase,
},
want: TextEndsWithIgnoreCase,
},
{
name: "contains",
args: args{
m: domain.SearchMethodContains,
},
want: TextContains,
},
{
name: "list contains",
args: args{
m: domain.SearchMethodListContains,
},
want: TextListContains,
},
{
name: "containts ignore case",
args: args{
m: domain.SearchMethodContainsIgnoreCase,
},
want: TextContainsIgnoreCase,
},
{
name: "invalid search method",
args: args{
m: -1,
},
want: textCompareMax,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := TextComparisonFromMethod(tt.args.m); got != tt.want {
t.Errorf("TextCompareFromMethod() = %v, want %v", got, tt.want)
}
})
}
}
func TestNewNumberQuery(t *testing.T) {
type args struct {
column Column
value interface{}
compare NumberComparison
}
tests := []struct {
name string
args args
want *NumberQuery
wantErr func(error) bool
}{
{
name: "too low compare",
args: args{
column: testCol,
value: "hurst",
compare: -1,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrInvalidCompare)
},
},
{
name: "too high compare",
args: args{
column: testCol,
value: "hurst",
compare: numberCompareMax,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrInvalidCompare)
},
},
{
name: "no column",
args: args{
column: Column{},
value: "hurst",
compare: NumberEquals,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrMissingColumn)
},
},
{
name: "no column name",
args: args{
column: testNoCol,
value: "hurst",
compare: NumberEquals,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrMissingColumn)
},
},
{
name: "no number",
args: args{
column: testCol,
value: "hurst",
compare: NumberEquals,
},
wantErr: func(err error) bool {
return errors.Is(err, ErrInvalidNumber)
},
},
{
name: "correct",
args: args{
column: testCol,
value: 5,
compare: NumberEquals,
},
want: &NumberQuery{
Column: testCol,
Number: 5,
Compare: NumberEquals,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewNumberQuery(tt.args.column, tt.args.value, tt.args.compare)
if err != nil && tt.wantErr == nil {
t.Errorf("NewNumberQuery() no error expected got %v", err)
return
} else if tt.wantErr != nil && !tt.wantErr(err) {
t.Errorf("NewNumberQuery() unexpeted error = %v", err)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewNumberQuery() = %v, want %v", got, tt.want)
}
})
}
}
func TestNumberQuery_comp(t *testing.T) {
type fields struct {
Column Column
Number interface{}
Compare NumberComparison
}
type want struct {
query interface{}
args []interface{}
isNil bool
}
tests := []struct {
name string
fields fields
want want
}{
{
name: "equals",
fields: fields{
Column: testCol,
Number: 42,
Compare: NumberEquals,
},
want: want{
query: sq.Eq{"test_table.test_col": 42},
args: nil,
},
},
{
name: "not equals",
fields: fields{
Column: testCol,
Number: 42,
Compare: NumberNotEquals,
},
want: want{
query: sq.NotEq{"test_table.test_col": 42},
args: nil,
},
},
{
name: "less",
fields: fields{
Column: testCol,
Number: 42,
Compare: NumberLess,
},
want: want{
query: sq.Lt{"test_table.test_col": 42},
args: nil,
},
},
{
name: "greater",
fields: fields{
Column: testCol,
Number: 42,
Compare: NumberGreater,
},
want: want{
query: sq.Gt{"test_table.test_col": 42},
args: nil,
},
},
{
name: "list containts",
fields: fields{
Column: testCol,
Number: 42,
Compare: NumberListContains,
},
want: want{
query: "test_table.test_col @> ? ",
args: []interface{}{pq.Array(42)},
},
},
{
name: "too high comparison",
fields: fields{
Column: testCol,
Number: 42,
Compare: numberCompareMax,
},
want: want{
isNil: true,
},
},
{
name: "too low comparison",
fields: fields{
Column: testCol,
Number: 42,
Compare: -1,
},
want: want{
isNil: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &NumberQuery{
Column: tt.fields.Column,
Number: tt.fields.Number,
Compare: tt.fields.Compare,
}
query, args := s.comp()
if query == nil && tt.want.isNil {
return
} else if tt.want.isNil && query != nil {
t.Error("query should not be nil")
}
if !reflect.DeepEqual(query, tt.want.query) {
t.Errorf("wrong query: want: %v, (%T), got: %v, (%T)", tt.want.query, tt.want.query, query, query)
}
if !reflect.DeepEqual(args, tt.want.args) {
t.Errorf("wrong args: want: %v, (%T), got: %v (%T)", tt.want.args, tt.want.args, args, args)
}
})
}
}
func TestNumberComparisonFromMethod(t *testing.T) {
type args struct {
m domain.SearchMethod
}
tests := []struct {
name string
args args
want NumberComparison
}{
{
name: "equals",
args: args{
m: domain.SearchMethodEquals,
},
want: NumberEquals,
},
{
name: "not equals",
args: args{
m: domain.SearchMethodNotEquals,
},
want: NumberNotEquals,
},
{
name: "less than",
args: args{
m: domain.SearchMethodLessThan,
},
want: NumberLess,
},
{
name: "greater than",
args: args{
m: domain.SearchMethodGreaterThan,
},
want: NumberGreater,
},
{
name: "list contains",
args: args{
m: domain.SearchMethodListContains,
},
want: NumberListContains,
},
{
name: "invalid search method",
args: args{
m: -1,
},
want: numberCompareMax,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NumberComparisonFromMethod(tt.args.m); got != tt.want {
t.Errorf("TextCompareFromMethod() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -20,9 +20,9 @@ const (
type TriggerActionsSetEvent struct { type TriggerActionsSetEvent struct {
eventstore.BaseEvent eventstore.BaseEvent
FlowType domain.FlowType FlowType domain.FlowType `json:"flowType"`
TriggerType domain.TriggerType TriggerType domain.TriggerType `json:"triggerType"`
ActionIDs []string ActionIDs []string `json:"actionIDs"`
} }
func (e *TriggerActionsSetEvent) Data() interface{} { func (e *TriggerActionsSetEvent) Data() interface{} {
@ -63,9 +63,9 @@ func TriggerActionsSetEventMapper(event *repository.Event) (eventstore.EventRead
type TriggerActionsCascadeRemovedEvent struct { type TriggerActionsCascadeRemovedEvent struct {
eventstore.BaseEvent eventstore.BaseEvent
FlowType domain.FlowType FlowType domain.FlowType `json:"flowType"`
TriggerType domain.TriggerType TriggerType domain.TriggerType `json:"triggerType"`
ActionID string ActionID string `json:"actionID"`
} }
func (e *TriggerActionsCascadeRemovedEvent) Data() interface{} { func (e *TriggerActionsCascadeRemovedEvent) Data() interface{} {
@ -104,7 +104,7 @@ func TriggerActionsCascadeRemovedEventMapper(event *repository.Event) (eventstor
type FlowClearedEvent struct { type FlowClearedEvent struct {
eventstore.BaseEvent eventstore.BaseEvent
FlowType domain.FlowType FlowType domain.FlowType `json:"flowType"`
} }
func (e *FlowClearedEvent) Data() interface{} { func (e *FlowClearedEvent) Data() interface{} {

View File

@ -133,15 +133,9 @@ func NewOrgDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate
} }
func OrgDeactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func OrgDeactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
orgChanged := &OrgDeactivatedEvent{ return &OrgDeactivatedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event), BaseEvent: *eventstore.BaseEventFromRepo(event),
} }, nil
err := json.Unmarshal(event.Data, orgChanged)
if err != nil {
return nil, errors.ThrowInternal(err, "ORG-DAfbs", "unable to unmarshal org deactivated")
}
return orgChanged, nil
} }
type OrgReactivatedEvent struct { type OrgReactivatedEvent struct {
@ -167,15 +161,9 @@ func NewOrgReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate
} }
func OrgReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { func OrgReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
orgChanged := &OrgReactivatedEvent{ return &OrgReactivatedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event), BaseEvent: *eventstore.BaseEventFromRepo(event),
} }, nil
err := json.Unmarshal(event.Data, orgChanged)
if err != nil {
return nil, errors.ThrowInternal(err, "ORG-DAfbs", "unable to unmarshal org deactivated")
}
return orgChanged, nil
} }
type OrgRemovedEvent struct { type OrgRemovedEvent struct {

View File

@ -376,6 +376,10 @@ Errors:
WrongTriggerType: TriggerType ist ungültig WrongTriggerType: TriggerType ist ungültig
NoChanges: Keine Änderungen NoChanges: Keine Änderungen
ActionIDsNotExist: ActionIDs existieren nicht ActionIDsNotExist: ActionIDs existieren nicht
Query:
CloseRows: SQL Statement konnte nicht abgeschlossen werden
SQLStatement: SQL Statement konnte nicht erstellt werden
InvalidRequest: Anfage ist ungültig
EventTypes: EventTypes:
user: user:
added: Benutzer hinzugefügt added: Benutzer hinzugefügt

View File

@ -376,6 +376,10 @@ Errors:
WrongTriggerType: TriggerType is invalid WrongTriggerType: TriggerType is invalid
NoChanges: No Changes NoChanges: No Changes
ActionIDsNotExist: ActionIDs do not exist ActionIDsNotExist: ActionIDs do not exist
Query:
CloseRows: SQL Statement could not be finished
SQLStatement: SQL Statement coud not be created
InvalidRequest: Request is invalid
EventTypes: EventTypes:
user: user:
added: User added added: User added

View File

@ -4,20 +4,32 @@ import (
"context" "context"
"github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/oidc"
"github.com/caos/zitadel/internal/actions" "github.com/caos/zitadel/internal/actions"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
) )
func (l *Login) customExternalUserMapping(user *domain.ExternalUser, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView) (*domain.ExternalUser, error) { func (l *Login) customExternalUserMapping(ctx context.Context, user *domain.ExternalUser, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView) (*domain.ExternalUser, error) {
triggerActions, err := l.query.GetActionsByFlowAndTriggerType(context.TODO(), domain.FlowTypeExternalAuthentication, domain.TriggerTypePostAuthentication) resourceOwner := req.RequestedOrgID
if resourceOwner == "" {
resourceOwner = config.AggregateID
}
if resourceOwner == domain.IAMID {
iam, err := l.authRepo.GetIAM(ctx)
if err != nil {
return nil, err
}
resourceOwner = iam.GlobalOrgID
}
triggerActions, err := l.query.GetActionsByFlowAndTriggerType(ctx, domain.FlowTypeExternalAuthentication, domain.TriggerTypePostAuthentication, resourceOwner)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx := (&actions.Context{}).SetToken(tokens) actionCtx := (&actions.Context{}).SetToken(tokens)
api := (&actions.API{}).SetExternalUser(user).SetMetadata(&user.Metadatas) api := (&actions.API{}).SetExternalUser(user).SetMetadata(&user.Metadatas)
for _, a := range triggerActions { for _, a := range triggerActions {
err = actions.Run(ctx, api, a.Script, a.Name, a.Timeout, a.AllowedToFail) err = actions.Run(actionCtx, api, a.Script, a.Name, a.Timeout, a.AllowedToFail)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -25,15 +37,15 @@ func (l *Login) customExternalUserMapping(user *domain.ExternalUser, tokens *oid
return user, err return user, err
} }
func (l *Login) customExternalUserToLoginUserMapping(user *domain.Human, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView, metadata []*domain.Metadata) (*domain.Human, []*domain.Metadata, error) { func (l *Login) customExternalUserToLoginUserMapping(user *domain.Human, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView, metadata []*domain.Metadata, resourceOwner string) (*domain.Human, []*domain.Metadata, error) {
triggerActions, err := l.query.GetActionsByFlowAndTriggerType(context.TODO(), domain.FlowTypeExternalAuthentication, domain.TriggerTypePreCreation) triggerActions, err := l.query.GetActionsByFlowAndTriggerType(context.TODO(), domain.FlowTypeExternalAuthentication, domain.TriggerTypePreCreation, resourceOwner)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
ctx := (&actions.Context{}).SetToken(tokens) actionCtx := (&actions.Context{}).SetToken(tokens)
api := (&actions.API{}).SetHuman(user).SetMetadata(&metadata) api := (&actions.API{}).SetHuman(user).SetMetadata(&metadata)
for _, a := range triggerActions { for _, a := range triggerActions {
err = actions.Run(ctx, api, a.Script, a.Name, a.Timeout, a.AllowedToFail) err = actions.Run(actionCtx, api, a.Script, a.Name, a.Timeout, a.AllowedToFail)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -41,16 +53,16 @@ func (l *Login) customExternalUserToLoginUserMapping(user *domain.Human, tokens
return user, metadata, err return user, metadata, err
} }
func (l *Login) customGrants(userID string, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView) ([]*domain.UserGrant, error) { func (l *Login) customGrants(userID string, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView, resourceOwner string) ([]*domain.UserGrant, error) {
triggerActions, err := l.query.GetActionsByFlowAndTriggerType(context.TODO(), domain.FlowTypeExternalAuthentication, domain.TriggerTypePostCreation) triggerActions, err := l.query.GetActionsByFlowAndTriggerType(context.TODO(), domain.FlowTypeExternalAuthentication, domain.TriggerTypePostCreation, resourceOwner)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx := (&actions.Context{}).SetToken(tokens) actionCtx := (&actions.Context{}).SetToken(tokens)
actionUserGrants := make([]actions.UserGrant, 0) actionUserGrants := make([]actions.UserGrant, 0)
api := (&actions.API{}).SetUserGrants(&actionUserGrants) api := (&actions.API{}).SetUserGrants(&actionUserGrants)
for _, a := range triggerActions { for _, a := range triggerActions {
err = actions.Run(ctx, api, a.Script, a.Name, a.Timeout, a.AllowedToFail) err = actions.Run(actionCtx, api, a.Script, a.Name, a.Timeout, a.AllowedToFail)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -184,7 +184,7 @@ func (l *Login) getRPConfig(w http.ResponseWriter, r *http.Request, authReq *dom
func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) { func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) {
externalUser := l.mapTokenToLoginUser(tokens, idpConfig) externalUser := l.mapTokenToLoginUser(tokens, idpConfig)
externalUser, err := l.customExternalUserMapping(externalUser, tokens, authReq, idpConfig) externalUser, err := l.customExternalUserMapping(r.Context(), externalUser, tokens, authReq, idpConfig)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
@ -284,7 +284,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
linkingUser := authReq.LinkingUsers[len(authReq.LinkingUsers)-1] linkingUser := authReq.LinkingUsers[len(authReq.LinkingUsers)-1]
user, externalIDP, metadata := l.mapExternalUserToLoginUser(orgIamPolicy, linkingUser, idpConfig) user, externalIDP, metadata := l.mapExternalUserToLoginUser(orgIamPolicy, linkingUser, idpConfig)
user, metadata, err = l.customExternalUserToLoginUserMapping(user, nil, authReq, idpConfig, metadata) user, metadata, err = l.customExternalUserToLoginUserMapping(user, nil, authReq, idpConfig, metadata, resourceOwner)
err = l.authRepo.AutoRegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, memberRoles, authReq.ID, userAgentID, resourceOwner, metadata, domain.BrowserInfoFromRequest(r)) err = l.authRepo.AutoRegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, memberRoles, authReq.ID, userAgentID, resourceOwner, metadata, domain.BrowserInfoFromRequest(r))
if err != nil { if err != nil {
l.renderExternalNotFoundOption(w, r, authReq, err) l.renderExternalNotFoundOption(w, r, authReq, err)
@ -295,7 +295,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
userGrants, err := l.customGrants(authReq.UserID, nil, authReq, idpConfig) userGrants, err := l.customGrants(authReq.UserID, nil, authReq, idpConfig, resourceOwner)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return

View File

@ -11,6 +11,7 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/oidc/pkg/client/rp" "github.com/caos/oidc/pkg/client/rp"
"github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/oidc"
http_util "github.com/caos/zitadel/internal/api/http" http_util "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
@ -75,7 +76,7 @@ func (l *Login) handleJWTExtraction(w http.ResponseWriter, r *http.Request, auth
} }
tokens := &oidc.Tokens{IDToken: token, IDTokenClaims: tokenClaims} tokens := &oidc.Tokens{IDToken: token, IDTokenClaims: tokenClaims}
externalUser := l.mapTokenToLoginUser(tokens, idpConfig) externalUser := l.mapTokenToLoginUser(tokens, idpConfig)
externalUser, err = l.customExternalUserMapping(externalUser, tokens, authReq, idpConfig) externalUser, err = l.customExternalUserMapping(r.Context(), externalUser, tokens, authReq, idpConfig)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
@ -126,7 +127,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
return return
} }
user, externalIDP, metadata := l.mapExternalUserToLoginUser(orgIamPolicy, authReq.LinkingUsers[len(authReq.LinkingUsers)-1], idpConfig) user, externalIDP, metadata := l.mapExternalUserToLoginUser(orgIamPolicy, authReq.LinkingUsers[len(authReq.LinkingUsers)-1], idpConfig)
user, metadata, err = l.customExternalUserToLoginUserMapping(user, tokens, authReq, idpConfig, metadata) user, metadata, err = l.customExternalUserToLoginUserMapping(user, tokens, authReq, idpConfig, metadata, resourceOwner)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
@ -141,7 +142,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
userGrants, err := l.customGrants(authReq.UserID, tokens, authReq, idpConfig) userGrants, err := l.customGrants(authReq.UserID, tokens, authReq, idpConfig, resourceOwner)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return

View File

@ -6,6 +6,10 @@ import (
"net/http" "net/http"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/gorilla/csrf"
"github.com/rakyll/statik/fs"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
http_utils "github.com/caos/zitadel/internal/api/http" http_utils "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/api/http/middleware" "github.com/caos/zitadel/internal/api/http/middleware"
@ -23,9 +27,6 @@ import (
"github.com/caos/zitadel/internal/static" "github.com/caos/zitadel/internal/static"
_ "github.com/caos/zitadel/internal/ui/login/statik" _ "github.com/caos/zitadel/internal/ui/login/statik"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/gorilla/csrf"
"github.com/rakyll/statik/fs"
"golang.org/x/text/language"
) )
type Login struct { type Login struct {

View File

@ -0,0 +1,9 @@
ALTER TABLE zitadel.projections.orgs RENAME COLUMN domain TO primary_domain;
DROP VIEW zitadel.projections.flows_actions_triggers;
ALTER TABLE zitadel.projections.flows_triggers DROP CONSTRAINT fk_action;
DROP TABLE zitadel.projections.flows_actions;
DELETE FROM zitadel.projections.current_sequences where projection_name in (
'zitadel.projections.actions',
'zitadel.projections.flows_actions',
'zitadel.projections.flows_triggers');

View File

@ -153,6 +153,32 @@ service AdminService {
}; };
} }
// Returns an organisation by id
rpc GetOrgByID(GetOrgByIDRequest) returns (GetOrgByIDResponse) {
option (google.api.http) = {
get: "/orgs/{id}";
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "orgs";
tags: "global";
external_docs: {
url: "https://docs.zitadel.ch/administrate#Organizations";
description: "detailed information about organizations";
};
responses: {
key: "200";
value: {
description: "requested org found";
};
};
};
}
//Checks whether an organisation exists by the given parameters //Checks whether an organisation exists by the given parameters
rpc IsOrgUnique(IsOrgUniqueRequest) returns (IsOrgUniqueResponse) { rpc IsOrgUnique(IsOrgUniqueRequest) returns (IsOrgUniqueResponse) {
option (google.api.http) = { option (google.api.http) = {
@ -185,32 +211,6 @@ service AdminService {
}; };
} }
// Returns an organisation by id
rpc GetOrgByID(GetOrgByIDRequest) returns (GetOrgByIDResponse) {
option (google.api.http) = {
get: "/orgs/{id}";
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "orgs";
tags: "global";
external_docs: {
url: "https://docs.zitadel.ch/administrate#Organizations";
description: "detailed information about organizations";
};
responses: {
key: "200";
value: {
description: "requested org found";
};
};
};
}
//Returns all organisations matching the request //Returns all organisations matching the request
// all queries need to match (AND) // all queries need to match (AND)
rpc ListOrgs(ListOrgsRequest) returns (ListOrgsResponse) { rpc ListOrgs(ListOrgsRequest) returns (ListOrgsResponse) {