mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 19:17:24 +00:00
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:
parent
5e110f0a48
commit
39c35c9455
@ -6,7 +6,7 @@ services:
|
||||
restart: always
|
||||
networks:
|
||||
- zitadel
|
||||
image: cockroachdb/cockroach:v21.1.0
|
||||
image: cockroachdb/cockroach:v21.1.7
|
||||
command: start-single-node --insecure --listen-addr=0.0.0.0
|
||||
ports:
|
||||
- 8080:8080
|
||||
@ -155,7 +155,7 @@ services:
|
||||
ports:
|
||||
- '50000:8080'
|
||||
environment:
|
||||
- BKD_HOST=backend-run
|
||||
- BKD_HOST=host.docker.internal
|
||||
- BKD_PORT=50001
|
||||
|
||||
frontend-local-run:
|
||||
@ -187,7 +187,7 @@ services:
|
||||
volumes:
|
||||
- ./environment.json:/environment.json
|
||||
environment:
|
||||
- HOST=backend-run
|
||||
- HOST=host.docker.internal
|
||||
- PORT=50002
|
||||
|
||||
networks:
|
||||
|
@ -179,7 +179,15 @@ func startZitadel(configPaths []string) {
|
||||
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)
|
||||
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)
|
||||
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 {
|
||||
apis.RegisterServer(ctx, admin.CreateServer(command, query, repo, conf.SystemDefaults.Domain))
|
||||
|
@ -71,6 +71,7 @@ Projections:
|
||||
RetryFailedAfter: 1s
|
||||
MaxFailureCount: 5
|
||||
BulkLimit: 200
|
||||
MaxIterators: 1
|
||||
CRDB:
|
||||
Host: $ZITADEL_EVENTSTORE_HOST
|
||||
Port: $ZITADEL_EVENTSTORE_PORT
|
||||
|
@ -32,18 +32,6 @@ Returns the default languages
|
||||
GET: /languages
|
||||
|
||||
|
||||
### IsOrgUnique
|
||||
|
||||
> **rpc** IsOrgUnique([IsOrgUniqueRequest](#isorguniquerequest))
|
||||
[IsOrgUniqueResponse](#isorguniqueresponse)
|
||||
|
||||
Checks whether an organisation exists by the given parameters
|
||||
|
||||
|
||||
|
||||
GET: /orgs/_is_unique
|
||||
|
||||
|
||||
### GetOrgByID
|
||||
|
||||
> **rpc** GetOrgByID([GetOrgByIDRequest](#getorgbyidrequest))
|
||||
@ -56,6 +44,18 @@ Returns an organisation by 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
|
||||
|
||||
> **rpc** ListOrgs([ListOrgsRequest](#listorgsrequest))
|
||||
|
@ -3,98 +3,19 @@ package eventstore
|
||||
import (
|
||||
"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"
|
||||
"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"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
"github.com/caos/zitadel/internal/org/repository/view/model"
|
||||
)
|
||||
|
||||
type OrgRepo struct {
|
||||
Eventstore v1.Eventstore
|
||||
|
||||
View *admin_view.View
|
||||
|
||||
SearchLimit uint64
|
||||
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) {
|
||||
policy, err := repo.View.OrgIAMPolicyByAggregateID(id)
|
||||
if errors.IsNotFound(err) {
|
||||
@ -114,22 +35,3 @@ func (repo *OrgRepo) GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.Org
|
||||
policy.Default = true
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,13 @@ package handler
|
||||
import (
|
||||
"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/command"
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"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/static"
|
||||
)
|
||||
|
||||
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 {
|
||||
handlers := []query.Handler{
|
||||
newOrg(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
|
||||
newIAMMember(
|
||||
handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount, es}),
|
||||
newIDPConfig(
|
||||
|
@ -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)
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"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"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
@ -61,9 +61,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, c
|
||||
return &EsRepository{
|
||||
spooler: spool,
|
||||
OrgRepo: eventstore.OrgRepo{
|
||||
Eventstore: es,
|
||||
View: view,
|
||||
SearchLimit: conf.SearchLimit,
|
||||
SystemDefaults: systemDefaults,
|
||||
},
|
||||
IAMRepository: eventstore.IAMRepository{
|
||||
|
@ -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)
|
||||
}
|
@ -4,14 +4,8 @@ import (
|
||||
"context"
|
||||
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
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/otel"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
@ -58,11 +59,20 @@ type admin interface {
|
||||
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{
|
||||
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.auth = authRepo
|
||||
api.admin = adminRepo
|
||||
|
@ -5,20 +5,21 @@ import (
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
org_grpc "github.com/caos/zitadel/internal/api/grpc/org"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
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"
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
query, err := listOrgRequestToModel(req)
|
||||
queries, err := listOrgRequestToModel(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orgs, err := s.org.SearchOrgs(ctx, query)
|
||||
orgs, err := s.query.SearchOrgs(ctx, queries)
|
||||
if err != nil {
|
||||
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) {
|
||||
|
@ -4,24 +4,37 @@ import (
|
||||
"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/org/model"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"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)
|
||||
queries, err := org_grpc.OrgQueriesToModel(req.Queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.OrgSearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
return &query.OrgSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
SortingColumn: fieldNameToOrgColumn(req.SortingColumn),
|
||||
Asc: asc,
|
||||
},
|
||||
Queries: queries,
|
||||
}, 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 {
|
||||
org := &domain.Org{
|
||||
Name: req.Name,
|
||||
|
@ -10,18 +10,22 @@ import (
|
||||
)
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -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)
|
||||
queries := make([]query.SearchQuery, len(req.Queries)+1)
|
||||
queries[0], err = query.NewActionResourceOwnerQuery(id)
|
||||
queries[0], err = query.NewActionResourceOwnerQuery(orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
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 {
|
||||
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) {
|
||||
org, err := s.org.OrgByDomainGlobal(ctx, req.Domain)
|
||||
org, err := s.query.OrgByDomainGlobal(ctx, req.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mgmt_pb.GetOrgByDomainGlobalResponse{Org: org_grpc.OrgViewToPb(org)}, nil
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/pkg/grpc/object"
|
||||
object_pb "github.com/caos/zitadel/pkg/grpc/object"
|
||||
)
|
||||
|
||||
@ -69,7 +68,7 @@ func ToListDetails(
|
||||
totalResult,
|
||||
processedSequence uint64,
|
||||
viewTimestamp time.Time,
|
||||
) *object.ListDetails {
|
||||
) *object_pb.ListDetails {
|
||||
return &object_pb.ListDetails{
|
||||
TotalResult: totalResult,
|
||||
ProcessedSequence: processedSequence,
|
||||
@ -79,53 +78,53 @@ func ToListDetails(
|
||||
|
||||
func TextMethodToModel(method object_pb.TextQueryMethod) domain.SearchMethod {
|
||||
switch method {
|
||||
case object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS:
|
||||
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS:
|
||||
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
|
||||
case object.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH:
|
||||
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_STARTS_WITH:
|
||||
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
|
||||
case object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS:
|
||||
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS:
|
||||
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
|
||||
case object.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH:
|
||||
case object_pb.TextQueryMethod_TEXT_QUERY_METHOD_ENDS_WITH:
|
||||
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
|
||||
default:
|
||||
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) {
|
||||
if query == nil {
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
org_pb "github.com/caos/zitadel/pkg/grpc/org"
|
||||
)
|
||||
|
||||
func OrgQueriesToModel(queries []*org_pb.OrgQuery) (_ []*org_model.OrgSearchQuery, err error) {
|
||||
q := make([]*org_model.OrgSearchQuery, len(queries))
|
||||
func OrgQueriesToModel(queries []*org_pb.OrgQuery) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i], err = OrgQueryToModel(query)
|
||||
if err != nil {
|
||||
@ -20,22 +21,14 @@ func OrgQueriesToModel(queries []*org_pb.OrgQuery) (_ []*org_model.OrgSearchQuer
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func OrgQueryToModel(query *org_pb.OrgQuery) (*org_model.OrgSearchQuery, error) {
|
||||
switch q := query.Query.(type) {
|
||||
func OrgQueryToModel(apiQuery *org_pb.OrgQuery) (query.SearchQuery, error) {
|
||||
switch q := apiQuery.Query.(type) {
|
||||
case *org_pb.OrgQuery_DomainQuery:
|
||||
return &org_model.OrgSearchQuery{
|
||||
Key: org_model.OrgSearchKeyOrgDomain,
|
||||
Method: object.TextMethodToModel(q.DomainQuery.Method),
|
||||
Value: q.DomainQuery.Domain,
|
||||
}, nil
|
||||
return query.NewOrgDomainSearchQuery(object.TextMethodToQuery(q.DomainQuery.Method), q.DomainQuery.Domain)
|
||||
case *org_pb.OrgQuery_NameQuery:
|
||||
return &org_model.OrgSearchQuery{
|
||||
Key: org_model.OrgSearchKeyOrgName,
|
||||
Method: object.TextMethodToModel(q.NameQuery.Method),
|
||||
Value: q.NameQuery.Name,
|
||||
}, nil
|
||||
return query.NewOrgNameSearchQuery(object.TextMethodToQuery(q.NameQuery.Method), q.NameQuery.Name)
|
||||
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))
|
||||
for i, org := range orgs {
|
||||
o[i] = OrgViewToPb(org)
|
||||
@ -77,7 +70,7 @@ func OrgViewsToPb(orgs []*org_model.OrgView) []*org_pb.Org {
|
||||
return o
|
||||
}
|
||||
|
||||
func OrgViewToPb(org *org_model.OrgView) *org_pb.Org {
|
||||
func OrgViewToPb(org *query.Org) *org_pb.Org {
|
||||
return &org_pb.Org{
|
||||
Id: org.ID,
|
||||
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 {
|
||||
case org_model.OrgStateActive:
|
||||
case domain.OrgStateActive:
|
||||
return org_pb.OrgState_ORG_STATE_ACTIVE
|
||||
case org_model.OrgStateInactive:
|
||||
case domain.OrgStateInactive:
|
||||
return org_pb.OrgState_ORG_STATE_INACTIVE
|
||||
default:
|
||||
return org_pb.OrgState_ORG_STATE_UNSPECIFIED
|
||||
|
@ -88,7 +88,7 @@ func (o *OPStorage) ValidateJWTProfileScopes(ctx context.Context, subject string
|
||||
scope := scopes[i]
|
||||
if strings.HasPrefix(scope, authreq_model.OrgDomainPrimaryScope) {
|
||||
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 {
|
||||
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) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]string{
|
||||
ClaimResourceOwner + "id": resourceOwner.AggregateID,
|
||||
ClaimResourceOwner + "id": resourceOwner.ID,
|
||||
ClaimResourceOwner + "name": resourceOwner.Name,
|
||||
ClaimResourceOwner + "primary_domain": resourceOwner.PrimaryDomain,
|
||||
ClaimResourceOwner + "primary_domain": resourceOwner.Domain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,8 @@ import (
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
"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"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
@ -87,8 +86,8 @@ type userCommandProvider interface {
|
||||
}
|
||||
|
||||
type orgViewProvider interface {
|
||||
OrgByID(string) (*org_view_model.OrgView, error)
|
||||
OrgByPrimaryDomain(string) (*org_view_model.OrgView, error)
|
||||
OrgByID(context.Context, string) (*query.Org, error)
|
||||
OrgByDomainGlobal(context.Context, string) (*query.Org, error)
|
||||
}
|
||||
|
||||
type userGrantProvider interface {
|
||||
@ -935,7 +934,7 @@ func setOrgID(orgViewProvider orgViewProvider, request *domain.AuthRequest) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
org, err := orgViewProvider.OrgByPrimaryDomain(primaryDomain)
|
||||
org, err := orgViewProvider.OrgByDomainGlobal(context.TODO(), primaryDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1032,7 +1031,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
|
||||
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
|
||||
user, err := userByID(ctx, userViewProvider, userEventProvider, userID)
|
||||
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) {
|
||||
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 {
|
||||
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 user, nil
|
||||
|
@ -8,19 +8,17 @@ import (
|
||||
|
||||
"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_request/model"
|
||||
"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/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/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"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
user_model "github.com/caos/zitadel/internal/user/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"
|
||||
@ -186,28 +184,28 @@ func (m *mockViewUser) PrefixAvatarURL() string {
|
||||
}
|
||||
|
||||
type mockViewOrg struct {
|
||||
State org_model.OrgState
|
||||
State domain.OrgState
|
||||
}
|
||||
|
||||
func (m *mockViewOrg) OrgByID(string) (*org_view_model.OrgView, error) {
|
||||
return &org_view_model.OrgView{
|
||||
State: int32(m.State),
|
||||
func (m *mockViewOrg) OrgByID(context.Context, string) (*query.Org, error) {
|
||||
return &query.Org{
|
||||
State: m.State,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockViewOrg) OrgByPrimaryDomain(string) (*org_view_model.OrgView, error) {
|
||||
return &org_view_model.OrgView{
|
||||
State: int32(m.State),
|
||||
func (m *mockViewOrg) OrgByDomainGlobal(context.Context, string) (*query.Org, error) {
|
||||
return &query.Org{
|
||||
State: m.State,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
@ -439,7 +437,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
Type: user_es_model.UserDeactivated,
|
||||
},
|
||||
},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -460,7 +458,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
Type: user_es_model.UserLocked,
|
||||
},
|
||||
},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -492,7 +490,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
fields{
|
||||
userViewProvider: &mockViewUser{},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateInactive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateInactive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -511,7 +509,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
PasswordSet: true,
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -528,7 +526,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
userSessionViewProvider: &mockViewErrUserSession{},
|
||||
userViewProvider: &mockViewUser{},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -548,7 +546,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
PasswordSet: true,
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -569,7 +567,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
PasswordlessInitRequired: true,
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
MultiFactorCheckLifeTime: 10 * time.Hour,
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
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)}},
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
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)}},
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -642,7 +640,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
MultiFactorCheckLifeTime: 10 * time.Hour,
|
||||
},
|
||||
args{&domain.AuthRequest{
|
||||
@ -668,7 +666,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
},
|
||||
args{&domain.AuthRequest{UserID: "UserID", LoginPolicy: &domain.LoginPolicy{}}, false},
|
||||
[]domain.NextStep{&domain.InitPasswordStep{}},
|
||||
@ -690,7 +688,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
SecondFactorCheckLifeTime: 18 * time.Hour,
|
||||
},
|
||||
args{&domain.AuthRequest{UserID: "UserID", SelectedIDPConfigID: "IDPConfigID"}, false},
|
||||
@ -709,7 +707,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
projectProvider: &mockProject{},
|
||||
loginPolicyProvider: &mockLoginPolicy{
|
||||
@ -742,7 +740,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
PasswordSet: true,
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -767,7 +765,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
projectProvider: &mockProject{},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
@ -801,7 +799,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelMultiFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -834,7 +832,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -868,7 +866,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -905,7 +903,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -936,7 +934,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -967,7 +965,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
@ -998,7 +996,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
projectProvider: &mockProject{},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
@ -1032,7 +1030,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
projectProvider: &mockProject{},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
@ -1067,7 +1065,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{
|
||||
roleCheck: true,
|
||||
userGrants: 0,
|
||||
@ -1105,7 +1103,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{
|
||||
roleCheck: true,
|
||||
userGrants: 2,
|
||||
@ -1143,7 +1141,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
projectProvider: &mockProject{
|
||||
projectCheck: true,
|
||||
@ -1181,7 +1179,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
projectProvider: &mockProject{
|
||||
projectCheck: true,
|
||||
@ -1223,7 +1221,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
},
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
SecondFactorCheckLifeTime: 18 * time.Hour,
|
||||
},
|
||||
args{
|
||||
@ -1249,7 +1247,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
MFAMaxSetUp: int32(model.MFALevelSecondFactor),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
orgViewProvider: &mockViewOrg{State: domain.OrgStateActive},
|
||||
lockoutPolicyProvider: &mockLockoutPolicy{
|
||||
policy: &iam_view_model.LockoutPolicyView{
|
||||
ShowLockOutFailures: true,
|
||||
|
@ -14,10 +14,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/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/telemetry/tracing"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -32,38 +29,6 @@ type OrgRepository struct {
|
||||
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) {
|
||||
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
if err != nil {
|
||||
|
@ -12,12 +12,11 @@ import (
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1"
|
||||
v1 "github.com/caos/zitadel/internal/eventstore/v1"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
key_model "github.com/caos/zitadel/internal/key/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/user/model"
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
req := new(domain.MetadataSearchRequest)
|
||||
return repo.searchUserMetadata(userID, "", req)
|
||||
|
@ -6,13 +6,12 @@ import (
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||
authz_repo "github.com/caos/zitadel/internal/authz/repository/eventsourcing"
|
||||
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"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
user_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||
@ -27,6 +26,8 @@ type UserGrantRepo struct {
|
||||
Auth authz.Config
|
||||
AuthZRepo *authz_repo.EsRepository
|
||||
PrefixAvatarURL string
|
||||
|
||||
Query *query.Queries
|
||||
}
|
||||
|
||||
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) {
|
||||
searchRequest := &org_model.OrgSearchRequest{
|
||||
SortingColumn: org_model.OrgSearchKeyOrgNameIgnoreCase,
|
||||
Asc: true,
|
||||
searchRequest := query.OrgSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
SortingColumn: query.OrgColumnName,
|
||||
Asc: true,
|
||||
},
|
||||
}
|
||||
if len(request.Queries) > 0 {
|
||||
for _, q := range request.Queries {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
return orgRespToOrgResp(orgs, count), nil
|
||||
return orgRespToOrgResp(orgs), nil
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
org, err := repo.View.OrgByID(user.ResourceOwner)
|
||||
org, err := repo.Query.OrgByID(context.TODO(), user.ResourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -300,12 +307,12 @@ func grantRespToOrgResp(grants *grant_model.UserGrantSearchResponse) *grant_mode
|
||||
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{
|
||||
TotalResult: count,
|
||||
TotalResult: orgs.Count,
|
||||
}
|
||||
resp.Result = make([]*grant_model.Org, len(orgs))
|
||||
for i, o := range orgs {
|
||||
resp.Result = make([]*grant_model.Org, len(orgs.Orgs))
|
||||
for i, o := range orgs.Orgs {
|
||||
resp.Result[i] = &grant_model.Org{OrgID: o.ID, OrgName: o.Name}
|
||||
}
|
||||
return resp
|
||||
|
@ -45,8 +45,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
|
||||
handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount, es},
|
||||
keyChan),
|
||||
newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}),
|
||||
newOrg(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
|
||||
newUserGrant(
|
||||
handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es},
|
||||
systemDefaults.IamID),
|
||||
|
@ -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)
|
||||
}
|
@ -99,6 +99,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
|
||||
userRepo,
|
||||
eventstore.AuthRequestRepo{
|
||||
Command: command,
|
||||
OrgViewProvider: queries,
|
||||
AuthRequests: authReq,
|
||||
View: view,
|
||||
Eventstore: es,
|
||||
@ -106,7 +107,6 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
|
||||
UserViewProvider: view,
|
||||
UserCommandProvider: command,
|
||||
UserEventProvider: &userRepo,
|
||||
OrgViewProvider: view,
|
||||
IDPProviderViewProvider: view,
|
||||
LoginPolicyViewProvider: view,
|
||||
LockoutPolicyViewProvider: view,
|
||||
@ -154,6 +154,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
|
||||
IamID: systemDefaults.IamID,
|
||||
Auth: authZ,
|
||||
AuthZRepo: authZRepo,
|
||||
Query: queries,
|
||||
},
|
||||
eventstore.OrgRepository{
|
||||
SearchLimit: conf.SearchLimit,
|
||||
|
@ -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)
|
||||
}
|
@ -2,13 +2,12 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
)
|
||||
|
||||
type OrgRepository interface {
|
||||
OrgByPrimaryDomain(primaryDomain string) (*org_model.OrgView, error)
|
||||
GetOrgIAMPolicy(ctx context.Context, orgID string) (*iam_model.OrgIAMPolicyView, error)
|
||||
GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error)
|
||||
GetIDPConfigByID(ctx context.Context, idpConfigID string) (*iam_model.IDPConfigView, error)
|
||||
|
@ -23,7 +23,6 @@ type UserRepository interface {
|
||||
SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error)
|
||||
|
||||
SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error)
|
||||
OrgByUserID(ctx context.Context, userID string) (*domain.Org, error)
|
||||
}
|
||||
|
||||
type myUserRepo interface {
|
||||
|
@ -109,11 +109,6 @@ func (repo *TokenVerifierRepo) ProjectIDAndOriginsByClientID(ctx context.Context
|
||||
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 {
|
||||
features, err := repo.View.FeaturesByAggregateID(orgID)
|
||||
if caos_errs.IsNotFound(err) {
|
||||
|
@ -38,8 +38,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
|
||||
handler{view, bulkLimit, configs.cycleDuration("UserMemberships"), errorCount, es}),
|
||||
newApplication(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}),
|
||||
newOrg(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
|
||||
newFeatures(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Features"), errorCount, es}),
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -55,16 +55,26 @@ type Commands struct {
|
||||
keyAlgorithm crypto.EncryptionAlgorithm
|
||||
privateKeyLifetime 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 {
|
||||
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{
|
||||
eventstore: eventstore,
|
||||
eventstore: es,
|
||||
static: staticStore,
|
||||
idGenerator: id.SonyFlakeGenerator,
|
||||
iamDomain: defaults.Domain,
|
||||
@ -130,7 +140,7 @@ func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults
|
||||
}
|
||||
repo.keyAlgorithm = keyAlgorithm
|
||||
|
||||
repo.tokenVerifier = authz.Start(authZRepo)
|
||||
repo.tokenVerifier = authZRepo
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
|
@ -182,10 +182,10 @@ func GetMockSecretGenerator(t *testing.T) crypto.Generator {
|
||||
return generator
|
||||
}
|
||||
|
||||
func GetMockVerifier(t *testing.T, features ...string) *authz.TokenVerifier {
|
||||
return authz.Start(&testVerifier{
|
||||
func GetMockVerifier(t *testing.T, features ...string) *testVerifier {
|
||||
return &testVerifier{
|
||||
features: features,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testVerifier struct {
|
||||
|
@ -3,7 +3,6 @@ package command
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"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 {
|
||||
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) {
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
@ -23,7 +22,7 @@ import (
|
||||
func TestCommandSide_AddLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
tokenVerifier *authz.TokenVerifier
|
||||
tokenVerifier orgFeatureChecker
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -260,7 +259,7 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
|
||||
func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
tokenVerifier *authz.TokenVerifier
|
||||
tokenVerifier orgFeatureChecker
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
@ -131,7 +130,7 @@ func (c *Commands) checkLoginPolicyAllowed(ctx context.Context, resourceOwner st
|
||||
if defaultPolicy.HidePasswordReset != policy.HidePasswordReset {
|
||||
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) {
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
@ -21,7 +20,7 @@ import (
|
||||
func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
tokenVerifier *authz.TokenVerifier
|
||||
tokenVerifier orgFeatureChecker
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -217,7 +216,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
tokenVerifier *authz.TokenVerifier
|
||||
tokenVerifier orgFeatureChecker
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
|
@ -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{}) {
|
||||
names = make([]string, len(cols))
|
||||
values = make([]interface{}, len(cols))
|
||||
@ -189,7 +209,9 @@ func columnsToQuery(cols []handler.Column) (names []string, parameters []string,
|
||||
names[i] = col.Name
|
||||
values[i] = col.Value
|
||||
parameters[i] = "$" + strconv.Itoa(i+1)
|
||||
|
||||
if col.ParameterOpt != nil {
|
||||
parameters[i] = col.ParameterOpt(parameters[i])
|
||||
}
|
||||
}
|
||||
return names, parameters, values
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@ package handler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
@ -14,6 +17,12 @@ var (
|
||||
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 {
|
||||
AggregateType eventstore.AggregateType
|
||||
Sequence uint64
|
||||
@ -31,8 +40,9 @@ type Executer interface {
|
||||
}
|
||||
|
||||
type Column struct {
|
||||
Name string
|
||||
Value interface{}
|
||||
Name string
|
||||
Value interface{}
|
||||
ParameterOpt func(string) string
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
func NewCond(name string, value interface{}) Condition {
|
||||
|
@ -25,7 +25,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/i18n"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
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"
|
||||
mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
policy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
||||
if errors.IsNotFound(err) {
|
||||
@ -93,7 +76,7 @@ func (repo *OrgRepository) GetMyOrgIamPolicy(ctx context.Context) (*iam_model.Or
|
||||
if err != nil {
|
||||
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) {
|
||||
@ -233,7 +216,7 @@ func (repo *OrgRepository) GetLabelPolicy(ctx context.Context) (*iam_model.Label
|
||||
if err != nil {
|
||||
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) {
|
||||
@ -248,7 +231,7 @@ func (repo *OrgRepository) GetPreviewLabelPolicy(ctx context.Context) (*iam_mode
|
||||
if err != nil {
|
||||
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) {
|
||||
@ -265,7 +248,7 @@ func (repo *OrgRepository) getDefaultLabelPolicy(ctx context.Context, state doma
|
||||
return nil, viewErr
|
||||
}
|
||||
if errors.IsNotFound(viewErr) {
|
||||
policy = new(iam_es_model.LabelPolicyView)
|
||||
policy = new(iam_view_model.LabelPolicyView)
|
||||
}
|
||||
events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -273,16 +256,16 @@ func (repo *OrgRepository) getDefaultLabelPolicy(ctx context.Context, state doma
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
if err := policyCopy.AppendEvent(event); err != nil {
|
||||
return iam_es_model.LabelPolicyViewToModel(policy), nil
|
||||
return iam_view_model.LabelPolicyViewToModel(policy), nil
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -291,7 +274,7 @@ func (repo *OrgRepository) GetLoginPolicy(ctx context.Context) (*iam_model.Login
|
||||
return nil, 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)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -299,15 +282,15 @@ func (repo *OrgRepository) GetLoginPolicy(ctx context.Context) (*iam_model.Login
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
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) {
|
||||
@ -324,7 +307,7 @@ func (repo *OrgRepository) GetDefaultLoginPolicy(ctx context.Context) (*iam_mode
|
||||
return nil, viewErr
|
||||
}
|
||||
if errors.IsNotFound(viewErr) {
|
||||
policy = new(iam_es_model.LoginPolicyView)
|
||||
policy = new(iam_view_model.LoginPolicyView)
|
||||
}
|
||||
events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -332,16 +315,16 @@ func (repo *OrgRepository) GetDefaultLoginPolicy(ctx context.Context) (*iam_mode
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
if err := policyCopy.AppendEvent(event); err != nil {
|
||||
return iam_es_model.LoginPolicyViewToModel(policy), nil
|
||||
return iam_view_model.LoginPolicyViewToModel(policy), nil
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -368,7 +351,7 @@ func (repo *OrgRepository) SearchIDPProviders(ctx context.Context, request *iam_
|
||||
Offset: request.Offset,
|
||||
Limit: request.Limit,
|
||||
TotalResult: count,
|
||||
Result: iam_es_model.IDPProviderViewsToModel(providers),
|
||||
Result: iam_view_model.IDPProviderViewsToModel(providers),
|
||||
}
|
||||
if sequenceErr == nil {
|
||||
result.Sequence = sequence.CurrentSequence
|
||||
@ -405,7 +388,7 @@ func (repo *OrgRepository) GetPasswordComplexityPolicy(ctx context.Context) (*ia
|
||||
return nil, 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)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -413,15 +396,15 @@ func (repo *OrgRepository) GetPasswordComplexityPolicy(ctx context.Context) (*ia
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
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) {
|
||||
@ -430,7 +413,7 @@ func (repo *OrgRepository) GetDefaultPasswordComplexityPolicy(ctx context.Contex
|
||||
return nil, viewErr
|
||||
}
|
||||
if errors.IsNotFound(viewErr) {
|
||||
policy = new(iam_es_model.PasswordComplexityPolicyView)
|
||||
policy = new(iam_view_model.PasswordComplexityPolicyView)
|
||||
}
|
||||
events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -438,16 +421,16 @@ func (repo *OrgRepository) GetDefaultPasswordComplexityPolicy(ctx context.Contex
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
if err := policyCopy.AppendEvent(event); err != nil {
|
||||
return iam_es_model.PasswordComplexityViewToModel(policy), nil
|
||||
return iam_view_model.PasswordComplexityViewToModel(policy), nil
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -456,7 +439,7 @@ func (repo *OrgRepository) GetPasswordAgePolicy(ctx context.Context) (*iam_model
|
||||
return nil, 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)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -464,15 +447,15 @@ func (repo *OrgRepository) GetPasswordAgePolicy(ctx context.Context) (*iam_model
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
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) {
|
||||
@ -481,7 +464,7 @@ func (repo *OrgRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*ia
|
||||
return nil, viewErr
|
||||
}
|
||||
if errors.IsNotFound(viewErr) {
|
||||
policy = new(iam_es_model.PasswordAgePolicyView)
|
||||
policy = new(iam_view_model.PasswordAgePolicyView)
|
||||
}
|
||||
events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -489,16 +472,16 @@ func (repo *OrgRepository) GetDefaultPasswordAgePolicy(ctx context.Context) (*ia
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
if err := policyCopy.AppendEvent(event); err != nil {
|
||||
return iam_es_model.PasswordAgeViewToModel(policy), nil
|
||||
return iam_view_model.PasswordAgeViewToModel(policy), nil
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -507,7 +490,7 @@ func (repo *OrgRepository) GetLockoutPolicy(ctx context.Context) (*iam_model.Loc
|
||||
return nil, 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)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -515,15 +498,15 @@ func (repo *OrgRepository) GetLockoutPolicy(ctx context.Context) (*iam_model.Loc
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
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) {
|
||||
@ -532,7 +515,7 @@ func (repo *OrgRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_mo
|
||||
return nil, viewErr
|
||||
}
|
||||
if errors.IsNotFound(viewErr) {
|
||||
policy = new(iam_es_model.LockoutPolicyView)
|
||||
policy = new(iam_view_model.LockoutPolicyView)
|
||||
}
|
||||
events, esErr := repo.getIAMEvents(ctx, policy.Sequence)
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
@ -540,16 +523,16 @@ func (repo *OrgRepository) GetDefaultLockoutPolicy(ctx context.Context) (*iam_mo
|
||||
}
|
||||
if esErr != nil {
|
||||
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
|
||||
for _, event := range events {
|
||||
if err := policyCopy.AppendEvent(event); err != nil {
|
||||
return iam_es_model.LockoutViewToModel(policy), nil
|
||||
return iam_view_model.LockoutViewToModel(policy), nil
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -557,7 +540,7 @@ func (repo *OrgRepository) GetPrivacyPolicy(ctx context.Context) (*iam_model.Pri
|
||||
if errors.IsNotFound(err) {
|
||||
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) {
|
||||
@ -566,7 +549,7 @@ func (repo *OrgRepository) GetDefaultPrivacyPolicy(ctx context.Context) (*iam_mo
|
||||
return nil, err
|
||||
}
|
||||
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) {
|
||||
@ -575,7 +558,7 @@ func (repo *OrgRepository) GetDefaultMailTemplate(ctx context.Context) (*iam_mod
|
||||
return nil, err
|
||||
}
|
||||
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) {
|
||||
@ -590,7 +573,7 @@ func (repo *OrgRepository) GetMailTemplate(ctx context.Context) (*iam_model.Mail
|
||||
if err != nil {
|
||||
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) {
|
||||
@ -644,7 +627,7 @@ func (repo *OrgRepository) GetMessageText(ctx context.Context, orgID, textType,
|
||||
if len(texts) == 0 {
|
||||
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) {
|
||||
@ -694,7 +677,7 @@ func (repo *OrgRepository) GetLoginTexts(ctx context.Context, orgID, lang string
|
||||
if err != nil {
|
||||
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) {
|
||||
|
@ -4,24 +4,21 @@ import (
|
||||
"context"
|
||||
"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/golang/protobuf/ptypes"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"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_view_model "github.com/caos/zitadel/internal/key/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
||||
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/view/repository"
|
||||
)
|
||||
@ -36,16 +33,16 @@ type UserRepo struct {
|
||||
|
||||
func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserView, error) {
|
||||
user, viewErr := repo.View.UserByID(id)
|
||||
if viewErr != nil && !caos_errs.IsNotFound(viewErr) {
|
||||
if viewErr != nil && !errors.IsNotFound(viewErr) {
|
||||
return nil, viewErr
|
||||
}
|
||||
if caos_errs.IsNotFound(viewErr) {
|
||||
if errors.IsNotFound(viewErr) {
|
||||
user = new(model.UserView)
|
||||
}
|
||||
|
||||
events, esErr := repo.getUserEvents(ctx, id, user.Sequence)
|
||||
if caos_errs.IsNotFound(viewErr) && len(events) == 0 {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.User.NotFound")
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
return nil, errors.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.User.NotFound")
|
||||
}
|
||||
if esErr != nil {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
func (repo *UserRepo) UserByIDAndResourceOwner(ctx context.Context, id, resourceOwner string) (*usr_model.UserView, error) {
|
||||
user, viewErr := repo.View.UserByIDAndResourceOwner(id, resourceOwner)
|
||||
if viewErr != nil && !caos_errs.IsNotFound(viewErr) {
|
||||
if viewErr != nil && !errors.IsNotFound(viewErr) {
|
||||
return nil, viewErr
|
||||
}
|
||||
if caos_errs.IsNotFound(viewErr) {
|
||||
if errors.IsNotFound(viewErr) {
|
||||
user = new(model.UserView)
|
||||
}
|
||||
|
||||
events, esErr := repo.getUserEvents(ctx, id, user.Sequence)
|
||||
if caos_errs.IsNotFound(viewErr) && len(events) == 0 {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.User.NotFound")
|
||||
if errors.IsNotFound(viewErr) && len(events) == 0 {
|
||||
return nil, errors.ThrowNotFound(nil, "EVENT-Lsoj7", "Errors.User.NotFound")
|
||||
}
|
||||
if esErr != nil {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
@ -44,8 +44,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
|
||||
newUser(handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es},
|
||||
defaults.IamID),
|
||||
newUserGrant(handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}),
|
||||
newOrg(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
|
||||
newOrgMember(
|
||||
handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount, es}),
|
||||
newOrgDomain(
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -14,8 +14,6 @@ import (
|
||||
|
||||
type OrgRepository interface {
|
||||
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)
|
||||
|
||||
SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error)
|
||||
|
@ -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)
|
||||
}
|
@ -2,76 +2,80 @@ package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
errs "errors"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
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 actionsQuery = squirrel.StatementBuilder.Select("creation_date", "change_date", "resource_owner", "sequence", "id", "action_state", "name", "script", "timeout", "allowed_to_fail").
|
||||
From("zitadel.projections.actions").PlaceholderFormat(squirrel.Dollar)
|
||||
|
||||
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
|
||||
var (
|
||||
actionTable = table{
|
||||
name: projection.ActionTable,
|
||||
}
|
||||
if len(actions) != 1 {
|
||||
return nil, errors.ThrowNotFound(nil, "QUERY-dft2g", "Errors.Action.NotFound")
|
||||
ActionColumnID = Column{
|
||||
name: projection.ActionIDCol,
|
||||
table: actionTable,
|
||||
}
|
||||
return actions[0], err
|
||||
}
|
||||
|
||||
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")
|
||||
ActionColumnCreationDate = Column{
|
||||
name: projection.ActionCreationDateCol,
|
||||
table: actionTable,
|
||||
}
|
||||
|
||||
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.orgs.internal")
|
||||
ActionColumnChangeDate = Column{
|
||||
name: projection.ActionChangeDateCol,
|
||||
table: actionTable,
|
||||
}
|
||||
|
||||
actions := []*Action{}
|
||||
for rows.Next() {
|
||||
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)
|
||||
ActionColumnResourceOwner = Column{
|
||||
name: projection.ActionResourceOwnerCol,
|
||||
table: actionTable,
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-pA0Wj", "Errors.actions.internal")
|
||||
ActionColumnSequence = Column{
|
||||
name: projection.ActionSequenceCol,
|
||||
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 {
|
||||
ID string `col:"id"`
|
||||
CreationDate time.Time `col:"creation_date"`
|
||||
ChangeDate time.Time `col:"change_date"`
|
||||
ResourceOwner string `col:"resource_owner"`
|
||||
State domain.ActionState `col:"action_state"`
|
||||
Sequence uint64 `col:"sequence"`
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
State domain.ActionState
|
||||
Sequence uint64
|
||||
|
||||
Name string `col:"name"`
|
||||
Script string `col:"script"`
|
||||
Timeout time.Duration `col:"-"`
|
||||
AllowedToFail bool `col:"-"`
|
||||
Name string
|
||||
Script string
|
||||
Timeout time.Duration
|
||||
AllowedToFail bool
|
||||
}
|
||||
|
||||
type ActionSearchQueries struct {
|
||||
@ -79,26 +83,146 @@ type ActionSearchQueries struct {
|
||||
Queries []SearchQuery
|
||||
}
|
||||
|
||||
func (q *ActionSearchQueries) ToQuery(query squirrel.SelectBuilder) squirrel.SelectBuilder {
|
||||
query = q.SearchRequest.ToQuery(query)
|
||||
func (q *ActionSearchQueries) 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) 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) {
|
||||
return NewTextQuery("resource_owner", id, TextEquals)
|
||||
return NewTextQuery(ActionColumnResourceOwner, id, TextEquals)
|
||||
}
|
||||
|
||||
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) {
|
||||
return NewIntQuery("state", int(value), IntEquals)
|
||||
return NewNumberQuery(ActionColumnState, int(value), NumberEquals)
|
||||
}
|
||||
|
||||
func newActionIDSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery("id", id, TextEquals)
|
||||
func prepareActionsQuery() (sq.SelectBuilder, func(rows *sql.Rows) (*Actions, error)) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -2,172 +2,227 @@ package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"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) {
|
||||
flowTypeQuery, _ := NewTriggerActionFlowTypeSearchQuery(flowType)
|
||||
triggerTypeQuery, _ := NewTriggerActionTriggerTypeSearchQuery(triggerType)
|
||||
return q.SearchActionsFromFlow(ctx, &TriggerActionSearchQueries{Queries: []SearchQuery{flowTypeQuery, triggerTypeQuery}})
|
||||
var (
|
||||
flowsTriggersTable = table{
|
||||
name: projection.FlowTriggerTable,
|
||||
}
|
||||
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").
|
||||
From("zitadel.projections.flows_actions_triggers").PlaceholderFormat(squirrel.Dollar)
|
||||
|
||||
func (q *Queries) SearchActionsFromFlow(ctx context.Context, query *TriggerActionSearchQueries) ([]*Action, error) {
|
||||
stmt, args, err := query.ToQuery(triggerActionsQuery).OrderBy("flow_type", "trigger_type", "trigger_sequence").ToSql()
|
||||
func (q *Queries) GetFlow(ctx context.Context, flowType domain.FlowType, orgID string) (*Flow, error) {
|
||||
query, scan := q.prepareFlowQuery()
|
||||
stmt, args, err := query.Where(
|
||||
sq.Eq{
|
||||
FlowsTriggersColumnFlowType.identifier(): flowType,
|
||||
},
|
||||
sq.Eq{
|
||||
FlowsTriggersColumnResourceOwner.identifier(): orgID,
|
||||
}).ToSql()
|
||||
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...)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.orgs.internal")
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Gg42f", "Errors.Internal")
|
||||
}
|
||||
|
||||
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
|
||||
return scan(rows)
|
||||
}
|
||||
|
||||
func (q *Queries) GetFlow(ctx context.Context, flowType domain.FlowType) (*Flow, error) {
|
||||
flowTypeQuery, _ := NewTriggerActionFlowTypeSearchQuery(flowType)
|
||||
return q.SearchFlow(ctx, &TriggerActionSearchQueries{Queries: []SearchQuery{flowTypeQuery}})
|
||||
}
|
||||
|
||||
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()
|
||||
func (q *Queries) GetActionsByFlowAndTriggerType(ctx context.Context, flowType domain.FlowType, triggerType domain.TriggerType, orgID string) ([]*Action, error) {
|
||||
stmt, scan := q.prepareTriggerActionsQuery()
|
||||
query, args, err := stmt.Where(
|
||||
sq.Eq{
|
||||
FlowsTriggersColumnFlowType.identifier(): flowType,
|
||||
},
|
||||
sq.Eq{
|
||||
FlowsTriggersColumnTriggerType.identifier(): triggerType,
|
||||
},
|
||||
sq.Eq{
|
||||
FlowsTriggersColumnResourceOwner.identifier(): orgID,
|
||||
}).ToSql()
|
||||
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 {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-M6mYN", "Errors.orgs.internal")
|
||||
return nil, errors.ThrowInternal(err, "QUERY-SDf52", "Errors.Internal")
|
||||
}
|
||||
|
||||
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
|
||||
return scan(rows)
|
||||
}
|
||||
|
||||
func (q *Queries) GetFlowTypesOfActionID(ctx context.Context, actionID string) ([]domain.FlowType, error) {
|
||||
actionIDQuery, _ := NewTriggerActionActionIDSearchQuery(actionID)
|
||||
query := &TriggerActionSearchQueries{Queries: []SearchQuery{actionIDQuery}}
|
||||
stmt, args, err := query.ToQuery(
|
||||
squirrel.StatementBuilder.
|
||||
Select("flow_type").
|
||||
From("zitadel.projections.flows_actions_triggers").
|
||||
PlaceholderFormat(squirrel.Dollar)).ToSql()
|
||||
stmt, args, err := squirrel.StatementBuilder.
|
||||
Select(FlowsTriggersColumnFlowType.identifier()).
|
||||
From(flowsTriggersTable.identifier()).
|
||||
Where(sq.Eq{
|
||||
FlowsTriggersColumnActionID.identifier(): actionID,
|
||||
}).
|
||||
PlaceholderFormat(squirrel.Dollar).
|
||||
ToSql()
|
||||
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...)
|
||||
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)
|
||||
for rows.Next() {
|
||||
var flow_type domain.FlowType
|
||||
rows.Scan(
|
||||
&flow_type,
|
||||
var flowType domain.FlowType
|
||||
err := rows.Scan(
|
||||
&flowType,
|
||||
)
|
||||
|
||||
flowTypes = append(flowTypes, flow_type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flowTypes = append(flowTypes, flowType)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-pA0Wj", "Errors.actions.internal")
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Fbgnh", "Errors.Query.CloseRows")
|
||||
}
|
||||
|
||||
return flowTypes, nil
|
||||
}
|
||||
|
||||
type Flow struct {
|
||||
ID string `col:"id"`
|
||||
CreationDate time.Time `col:"creation_date"`
|
||||
ChangeDate time.Time `col:"change_date"`
|
||||
ResourceOwner string `col:"resource_owner"`
|
||||
Sequence uint64 `col:"sequence"`
|
||||
Type domain.FlowType `col:"flow_type"`
|
||||
func (q *Queries) prepareTriggerActionsQuery() (sq.SelectBuilder, func(*sql.Rows) ([]*Action, error)) {
|
||||
return sq.Select(
|
||||
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) ([]*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 {
|
||||
SearchRequest
|
||||
Queries []SearchQuery
|
||||
}
|
||||
func (q *Queries) prepareFlowQuery() (sq.SelectBuilder, func(*sql.Rows) (*Flow, error)) {
|
||||
return sq.Select(
|
||||
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 {
|
||||
query = q.SearchRequest.ToQuery(query)
|
||||
for _, q := range q.Queries {
|
||||
query = q.ToQuery(query)
|
||||
}
|
||||
return query
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dfbe2", "Errors.Query.CloseRows")
|
||||
}
|
||||
|
||||
func NewTriggerActionTriggerTypeSearchQuery(value domain.TriggerType) (SearchQuery, error) {
|
||||
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)
|
||||
return flow, nil
|
||||
}
|
||||
}
|
||||
|
75
internal/query/current_sequence.go
Normal file
75
internal/query/current_sequence.go
Normal 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
252
internal/query/org.go
Normal 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
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
@ -12,13 +13,27 @@ import (
|
||||
"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 {
|
||||
crdb.StatementHandler
|
||||
}
|
||||
|
||||
func NewActionProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ActionProjection {
|
||||
p := &ActionProjection{}
|
||||
config.ProjectionName = "projections.actions"
|
||||
config.ProjectionName = ActionTable
|
||||
config.Reducers = p.reducers()
|
||||
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
|
||||
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) {
|
||||
e, ok := event.(*action.AddedEvent)
|
||||
if !ok {
|
||||
logging.LogWithFields("HANDL-zWCk3", "seq", event.Sequence, "expectedType", action.AddedEventType).Error("was not an event")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-uYq4r", "reduce.wrong.event.type")
|
||||
logging.LogWithFields("HANDL-Sgg31", "seq", event.Sequence, "expectedType", action.AddedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-Dff21", "reduce.wrong.event.type")
|
||||
}
|
||||
return crdb.NewCreateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(actionIDCol, e.Aggregate().ID),
|
||||
handler.NewCol(actionCreationDateCol, e.CreationDate()),
|
||||
handler.NewCol(actionChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(actionResourceOwnerCol, e.Aggregate().ResourceOwner),
|
||||
handler.NewCol(actionSequenceCol, e.Sequence()),
|
||||
handler.NewCol(actionNameCol, e.Name),
|
||||
handler.NewCol(actionScriptCol, e.Script),
|
||||
handler.NewCol(actionTimeoutCol, e.Timeout),
|
||||
handler.NewCol(actionAllowedToFailCol, e.AllowedToFail),
|
||||
handler.NewCol(actionStateCol, domain.ActionStateActive),
|
||||
handler.NewCol(ActionIDCol, e.Aggregate().ID),
|
||||
handler.NewCol(ActionCreationDateCol, e.CreationDate()),
|
||||
handler.NewCol(ActionChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(ActionResourceOwnerCol, e.Aggregate().ResourceOwner),
|
||||
handler.NewCol(ActionSequenceCol, e.Sequence()),
|
||||
handler.NewCol(ActionNameCol, e.Name),
|
||||
handler.NewCol(ActionScriptCol, e.Script),
|
||||
handler.NewCol(ActionTimeoutCol, e.Timeout),
|
||||
handler.NewCol(ActionAllowedToFailCol, e.AllowedToFail),
|
||||
handler.NewCol(ActionStateCol, domain.ActionStateActive),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@ -93,30 +95,30 @@ func (p *ActionProjection) reduceActionAdded(event eventstore.EventReader) (*han
|
||||
func (p *ActionProjection) reduceActionChanged(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*action.ChangedEvent)
|
||||
if !ok {
|
||||
logging.LogWithFields("HANDL-q4oq8", "seq", event.Sequence, "expected", action.ChangedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-Bg8oM", "reduce.wrong.event.type")
|
||||
logging.LogWithFields("HANDL-Dg2th", "seq", event.Sequence, "expected", action.ChangedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-Gg43d", "reduce.wrong.event.type")
|
||||
}
|
||||
values := []handler.Column{
|
||||
handler.NewCol(actionChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(actionSequenceCol, e.Sequence()),
|
||||
handler.NewCol(ActionChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(ActionSequenceCol, e.Sequence()),
|
||||
}
|
||||
if e.Name != nil {
|
||||
values = append(values, handler.NewCol(actionNameCol, *e.Name))
|
||||
values = append(values, handler.NewCol(ActionNameCol, *e.Name))
|
||||
}
|
||||
if e.Script != nil {
|
||||
values = append(values, handler.NewCol(actionScriptCol, *e.Script))
|
||||
values = append(values, handler.NewCol(ActionScriptCol, *e.Script))
|
||||
}
|
||||
if e.Timeout != nil {
|
||||
values = append(values, handler.NewCol(actionTimeoutCol, *e.Timeout))
|
||||
values = append(values, handler.NewCol(ActionTimeoutCol, *e.Timeout))
|
||||
}
|
||||
if e.AllowedToFail != nil {
|
||||
values = append(values, handler.NewCol(actionAllowedToFailCol, *e.AllowedToFail))
|
||||
values = append(values, handler.NewCol(ActionAllowedToFailCol, *e.AllowedToFail))
|
||||
}
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
values,
|
||||
[]handler.Condition{
|
||||
handler.NewCond(actionIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(ActionIDCol, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@ -124,18 +126,18 @@ func (p *ActionProjection) reduceActionChanged(event eventstore.EventReader) (*h
|
||||
func (p *ActionProjection) reduceActionDeactivated(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*action.DeactivatedEvent)
|
||||
if !ok {
|
||||
logging.LogWithFields("HANDL-1gwdc", "seq", event.Sequence, "expectedType", action.DeactivatedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-BApK4", "reduce.wrong.event.type")
|
||||
logging.LogWithFields("HANDL-Fhhjd", "seq", event.Sequence, "expectedType", action.DeactivatedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-Fgh32", "reduce.wrong.event.type")
|
||||
}
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(actionChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(actionSequenceCol, e.Sequence()),
|
||||
handler.NewCol(actionStateCol, domain.ActionStateInactive),
|
||||
handler.NewCol(ActionChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(ActionSequenceCol, e.Sequence()),
|
||||
handler.NewCol(ActionStateCol, domain.ActionStateInactive),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(actionIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(ActionIDCol, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@ -143,18 +145,18 @@ func (p *ActionProjection) reduceActionDeactivated(event eventstore.EventReader)
|
||||
func (p *ActionProjection) reduceActionReactivated(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*action.ReactivatedEvent)
|
||||
if !ok {
|
||||
logging.LogWithFields("HANDL-Vjwiy", "seq", event.Sequence, "expectedType", action.ReactivatedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-o37De", "reduce.wrong.event.type")
|
||||
logging.LogWithFields("HANDL-Fg4r3", "seq", event.Sequence, "expectedType", action.ReactivatedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-hwdqa", "reduce.wrong.event.type")
|
||||
}
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(actionChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(actionSequenceCol, e.Sequence()),
|
||||
handler.NewCol(actionStateCol, domain.ActionStateActive),
|
||||
handler.NewCol(ActionChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(ActionSequenceCol, e.Sequence()),
|
||||
handler.NewCol(ActionStateCol, domain.ActionStateActive),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(actionIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(ActionIDCol, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@ -162,13 +164,13 @@ func (p *ActionProjection) reduceActionReactivated(event eventstore.EventReader)
|
||||
func (p *ActionProjection) reduceActionRemoved(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*action.RemovedEvent)
|
||||
if !ok {
|
||||
logging.LogWithFields("HANDL-79OhB", "seq", event.Sequence, "expectedType", action.RemovedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-4TbKT", "reduce.wrong.event.type")
|
||||
logging.LogWithFields("HANDL-Dgwh2", "seq", event.Sequence, "expectedType", action.RemovedEventType).Error("wrong event type")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "HANDL-Dgh2d", "reduce.wrong.event.type")
|
||||
}
|
||||
return crdb.NewDeleteStatement(
|
||||
e,
|
||||
[]handler.Condition{
|
||||
handler.NewCond(actionIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(ActionIDCol, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
178
internal/query/projection/action_test.go
Normal file
178
internal/query/projection/action_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ type Config struct {
|
||||
BulkLimit uint64
|
||||
CRDB types.SQL
|
||||
Customizations map[string]CustomConfig
|
||||
MaxIterators int
|
||||
}
|
||||
|
||||
type CustomConfig struct {
|
||||
|
93
internal/query/projection/flow.go
Normal file
93
internal/query/projection/flow.go
Normal 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
|
||||
}
|
93
internal/query/projection/flow_test.go
Normal file
93
internal/query/projection/flow_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
@ -16,9 +17,13 @@ type OrgProjection struct {
|
||||
crdb.StatementHandler
|
||||
}
|
||||
|
||||
const (
|
||||
OrgProjectionTable = "zitadel.projections.orgs"
|
||||
)
|
||||
|
||||
func NewOrgProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgProjection {
|
||||
p := &OrgProjection{}
|
||||
config.ProjectionName = "projections.orgs"
|
||||
config.ProjectionName = OrgProjectionTable
|
||||
config.Reducers = p.reducers()
|
||||
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
|
||||
return p
|
||||
@ -54,33 +59,35 @@ func (p *OrgProjection) reducers() []handler.AggregateReducer {
|
||||
}
|
||||
}
|
||||
|
||||
type OrgColumn string
|
||||
|
||||
const (
|
||||
orgIDCol = "id"
|
||||
orgCreationDateCol = "creation_date"
|
||||
orgChangeDateCol = "change_date"
|
||||
orgResourceOwnerCol = "resource_owner"
|
||||
orgStateCol = "org_state"
|
||||
orgSequenceCol = "sequence"
|
||||
orgDomainCol = "domain"
|
||||
orgNameCol = "name"
|
||||
OrgColumnID = "id"
|
||||
OrgColumnCreationDate = "creation_date"
|
||||
OrgColumnChangeDate = "change_date"
|
||||
OrgColumnResourceOwner = "resource_owner"
|
||||
OrgColumnState = "org_state"
|
||||
OrgColumnSequence = "sequence"
|
||||
OrgColumnName = "name"
|
||||
OrgColumnDomain = "primary_domain"
|
||||
)
|
||||
|
||||
func (p *OrgProjection) reduceOrgAdded(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*org.OrgAddedEvent)
|
||||
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 crdb.NewCreateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(orgIDCol, e.Aggregate().ID),
|
||||
handler.NewCol(orgCreationDateCol, e.CreationDate()),
|
||||
handler.NewCol(orgChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(orgResourceOwnerCol, e.Aggregate().ResourceOwner),
|
||||
handler.NewCol(orgSequenceCol, e.Sequence()),
|
||||
handler.NewCol(orgNameCol, e.Name),
|
||||
handler.NewCol(orgStateCol, domain.OrgStateActive),
|
||||
handler.NewCol(OrgColumnID, e.Aggregate().ID),
|
||||
handler.NewCol(OrgColumnCreationDate, e.CreationDate()),
|
||||
handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(OrgColumnResourceOwner, e.Aggregate().ResourceOwner),
|
||||
handler.NewCol(OrgColumnSequence, e.Sequence()),
|
||||
handler.NewCol(OrgColumnName, e.Name),
|
||||
handler.NewCol(OrgColumnState, domain.OrgStateActive),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@ -88,21 +95,21 @@ func (p *OrgProjection) reduceOrgAdded(event eventstore.EventReader) (*handler.S
|
||||
func (p *OrgProjection) reduceOrgChanged(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*org.OrgChangedEvent)
|
||||
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")
|
||||
}
|
||||
values := []handler.Column{
|
||||
handler.NewCol(orgChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(orgSequenceCol, e.Sequence()),
|
||||
}
|
||||
if e.Name != "" {
|
||||
values = append(values, handler.NewCol(orgNameCol, e.Name))
|
||||
if e.Name == "" {
|
||||
return crdb.NewNoOpStatement(e), nil
|
||||
}
|
||||
return crdb.NewUpdateStatement(
|
||||
e,
|
||||
values,
|
||||
[]handler.Column{
|
||||
handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(OrgColumnSequence, e.Sequence()),
|
||||
handler.NewCol(OrgColumnName, e.Name),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(orgIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(OrgColumnID, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@ -110,18 +117,18 @@ func (p *OrgProjection) reduceOrgChanged(event eventstore.EventReader) (*handler
|
||||
func (p *OrgProjection) reduceOrgDeactivated(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*org.OrgDeactivatedEvent)
|
||||
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 crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(orgChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(orgSequenceCol, e.Sequence()),
|
||||
handler.NewCol(orgStateCol, domain.OrgStateInactive),
|
||||
handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(OrgColumnSequence, e.Sequence()),
|
||||
handler.NewCol(OrgColumnState, domain.OrgStateInactive),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(orgIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(OrgColumnID, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@ -129,18 +136,18 @@ func (p *OrgProjection) reduceOrgDeactivated(event eventstore.EventReader) (*han
|
||||
func (p *OrgProjection) reduceOrgReactivated(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*org.OrgReactivatedEvent)
|
||||
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 crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(orgChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(orgSequenceCol, e.Sequence()),
|
||||
handler.NewCol(orgStateCol, domain.OrgStateActive),
|
||||
handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(OrgColumnSequence, e.Sequence()),
|
||||
handler.NewCol(OrgColumnState, domain.OrgStateActive),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(orgIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(OrgColumnID, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@ -148,18 +155,18 @@ func (p *OrgProjection) reduceOrgReactivated(event eventstore.EventReader) (*han
|
||||
func (p *OrgProjection) reducePrimaryDomainSet(event eventstore.EventReader) (*handler.Statement, error) {
|
||||
e, ok := event.(*org.DomainPrimarySetEvent)
|
||||
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 crdb.NewUpdateStatement(
|
||||
e,
|
||||
[]handler.Column{
|
||||
handler.NewCol(orgChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(orgSequenceCol, e.Sequence()),
|
||||
handler.NewCol(orgDomainCol, e.Domain),
|
||||
handler.NewCol(OrgColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(OrgColumnSequence, e.Sequence()),
|
||||
handler.NewCol(OrgColumnDomain, e.Domain),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(orgIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(OrgColumnID, e.Aggregate().ID),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
@ -16,15 +16,15 @@ import (
|
||||
)
|
||||
|
||||
type OrgOwner struct {
|
||||
OrgID string `col:"org_id"`
|
||||
OrgName string `col:"org_name"`
|
||||
OrgCreationDate time.Time `col:"org_creation_date"`
|
||||
OwnerID string `col:"owner_id"`
|
||||
OwnerLanguage *language.Tag `col:"owner_language"`
|
||||
OwnerEmailAddress string `col:"owner_email"`
|
||||
OwnerFirstName string `col:"owner_first_name"`
|
||||
OwnerLastName string `col:"owner_last_name"`
|
||||
OwnerGender domain.Gender `col:"owner_gender"`
|
||||
OrgID string
|
||||
OrgName string
|
||||
OrgCreationDate time.Time
|
||||
OwnerID string
|
||||
OwnerLanguage *language.Tag
|
||||
OwnerEmailAddress string
|
||||
OwnerFirstName string
|
||||
OwnerLastName string
|
||||
OwnerGender domain.Gender
|
||||
}
|
||||
|
||||
type OrgOwnerProjection struct {
|
||||
@ -49,7 +49,7 @@ const (
|
||||
|
||||
func NewOrgOwnerProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgOwnerProjection {
|
||||
p := &OrgOwnerProjection{}
|
||||
config.ProjectionName = "projections.org_owners"
|
||||
config.ProjectionName = "zitadel.projections.org_owners"
|
||||
config.Reducers = p.reducers()
|
||||
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
|
||||
return p
|
||||
|
196
internal/query/projection/org_test.go
Normal file
196
internal/query/projection/org_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ type ProjectProjection struct {
|
||||
|
||||
func NewProjectProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ProjectProjection {
|
||||
p := &ProjectProjection{}
|
||||
config.ProjectionName = "projections.projects"
|
||||
config.ProjectionName = "zitadel.projections.projects"
|
||||
config.Reducers = p.reducers()
|
||||
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
|
||||
return p
|
||||
|
@ -2,25 +2,21 @@ package projection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"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/query/projection/flow"
|
||||
)
|
||||
|
||||
const (
|
||||
currentSeqTable = "projections.current_sequences"
|
||||
CurrentSeqTable = "projections.current_sequences"
|
||||
locksTable = "projections.locks"
|
||||
failedEventsTable = "projections.failed_events"
|
||||
)
|
||||
|
||||
func Start(ctx context.Context, es *eventstore.Eventstore, config Config) error {
|
||||
sqlClient, err := config.CRDB.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, config Config) error {
|
||||
projectionConfig := crdb.StatementHandlerConfig{
|
||||
ProjectionHandlerConfig: handler.ProjectionHandlerConfig{
|
||||
HandlerConfig: handler.HandlerConfig{
|
||||
@ -30,19 +26,18 @@ func Start(ctx context.Context, es *eventstore.Eventstore, config Config) error
|
||||
RetryFailedAfter: config.RetryFailedAfter.Duration,
|
||||
},
|
||||
Client: sqlClient,
|
||||
SequenceTable: currentSeqTable,
|
||||
SequenceTable: CurrentSeqTable,
|
||||
LockTable: locksTable,
|
||||
FailedEventsTable: failedEventsTable,
|
||||
MaxFailureCount: config.MaxFailureCount,
|
||||
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"]))
|
||||
//owner.NewOrgOwnerProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["org_owners"]))
|
||||
NewActionProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["actions"]))
|
||||
flow.NewFlowProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["flows"]))
|
||||
NewFlowProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["flows"]))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -62,3 +57,20 @@ func applyCustomConfig(config crdb.StatementHandlerConfig, customConfig CustomCo
|
||||
|
||||
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
|
||||
}
|
||||
|
87
internal/query/projection/test_event.go
Normal file
87
internal/query/projection/test_event.go
Normal 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)
|
||||
}
|
48
internal/query/projection/test_executer.go
Normal file
48
internal/query/projection/test_executer.go
Normal 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)
|
||||
}
|
||||
}
|
@ -6,10 +6,8 @@ import (
|
||||
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
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/repository/action"
|
||||
iam_repo "github.com/caos/zitadel/internal/repository/iam"
|
||||
@ -20,12 +18,9 @@ import (
|
||||
)
|
||||
|
||||
type Queries struct {
|
||||
iamID string
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
secretCrypto crypto.Crypto
|
||||
|
||||
client *sql.DB
|
||||
iamID string
|
||||
eventstore *eventstore.Eventstore
|
||||
client *sql.DB
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@ -39,10 +34,9 @@ func StartQueries(ctx context.Context, es *eventstore.Eventstore, projections pr
|
||||
}
|
||||
|
||||
repo = &Queries{
|
||||
iamID: defaults.IamID,
|
||||
eventstore: es,
|
||||
idGenerator: id.SonyFlakeGenerator,
|
||||
client: sqlClient,
|
||||
iamID: defaults.IamID,
|
||||
eventstore: es,
|
||||
client: sqlClient,
|
||||
}
|
||||
iam_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)
|
||||
action.RegisterEventMappers(repo.eventstore)
|
||||
|
||||
repo.secretCrypto, err = crypto.NewAESCrypto(defaults.IDPConfigVerificationKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = projection.Start(ctx, es, projections)
|
||||
err = projection.Start(ctx, sqlClient, es, projections)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2,19 +2,27 @@ package query
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"reflect"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type SearchResponse struct {
|
||||
Count uint64
|
||||
*LatestSequence
|
||||
}
|
||||
|
||||
type SearchRequest struct {
|
||||
Offset uint64
|
||||
Limit uint64
|
||||
SortingColumn string
|
||||
SortingColumn Column
|
||||
Asc bool
|
||||
}
|
||||
|
||||
func (req *SearchRequest) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
func (req *SearchRequest) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
if req.Offset > 0 {
|
||||
query = query.Offset(req.Offset)
|
||||
}
|
||||
@ -22,12 +30,12 @@ func (req *SearchRequest) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
query = query.Limit(req.Limit)
|
||||
}
|
||||
|
||||
if req.SortingColumn != "" {
|
||||
clause := "LOWER(?)"
|
||||
if !req.SortingColumn.isZero() {
|
||||
clause := "LOWER(" + sqlPlaceholder + ")"
|
||||
if !req.Asc {
|
||||
clause += " DESC"
|
||||
}
|
||||
query.OrderByClause(clause, req.SortingColumn)
|
||||
query = query.OrderByClause(clause, req.SortingColumn.identifier())
|
||||
}
|
||||
|
||||
return query
|
||||
@ -40,110 +48,232 @@ type SearchQuery interface {
|
||||
}
|
||||
|
||||
type TextQuery struct {
|
||||
Column string
|
||||
Column Column
|
||||
Text string
|
||||
Compare TextComparison
|
||||
}
|
||||
|
||||
func NewTextQuery(column, value string, compare TextComparison) (*TextQuery, error) {
|
||||
if compare < 0 || compare >= textMax {
|
||||
return nil, errors.New("invalid compare")
|
||||
var (
|
||||
ErrInvalidCompare = 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 == "" {
|
||||
return nil, errors.New("missing column")
|
||||
if col.isZero() {
|
||||
return nil, ErrMissingColumn
|
||||
}
|
||||
return &TextQuery{
|
||||
Column: column,
|
||||
Column: col,
|
||||
Text: value,
|
||||
Compare: compare,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (q *TextQuery) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
query = query.Where(q.comp())
|
||||
return query
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
}
|
||||
|
||||
func (s *TextQuery) comp() map[string]interface{} {
|
||||
func (s *TextQuery) comp() (comparison interface{}, args []interface{}) {
|
||||
switch s.Compare {
|
||||
case TextEquals:
|
||||
return sq.Eq{s.Column: s.Text}
|
||||
case TextEqualsIgnore:
|
||||
return sq.Eq{"LOWER(" + s.Column + ")": strings.ToLower(s.Text)}
|
||||
return sq.Eq{s.Column.identifier(): s.Text}, nil
|
||||
case TextEqualsIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): s.Text}, nil
|
||||
case TextStartsWith:
|
||||
return sq.Like{s.Column: s.Text + sqlPlaceholder}
|
||||
case TextStartsWithIgnore:
|
||||
return sq.Like{"LOWER(" + s.Column + ")": strings.ToLower(s.Text) + sqlPlaceholder}
|
||||
return sq.Like{s.Column.identifier(): s.Text + "%"}, nil
|
||||
case TextStartsWithIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): s.Text + "%"}, nil
|
||||
case TextEndsWith:
|
||||
return sq.Like{s.Column: sqlPlaceholder + s.Text}
|
||||
case TextEndsWithIgnore:
|
||||
return sq.Like{"LOWER(" + s.Column + ")": sqlPlaceholder + strings.ToLower(s.Text)}
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text}, nil
|
||||
case TextEndsWithIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text}, nil
|
||||
case TextContains:
|
||||
return sq.Like{s.Column: sqlPlaceholder + s.Text + sqlPlaceholder}
|
||||
case TextContainsIgnore:
|
||||
return sq.Like{"LOWER(" + s.Column + ")": sqlPlaceholder + strings.ToLower(s.Text) + sqlPlaceholder}
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text + "%"}, nil
|
||||
case TextContainsIgnoreCase:
|
||||
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
|
||||
|
||||
const (
|
||||
TextEquals TextComparison = iota
|
||||
TextEqualsIgnore
|
||||
TextEqualsIgnoreCase
|
||||
TextStartsWith
|
||||
TextStartsWithIgnore
|
||||
TextStartsWithIgnoreCase
|
||||
TextEndsWith
|
||||
TextEndsWithIgnore
|
||||
TextEndsWithIgnoreCase
|
||||
TextContains
|
||||
TextContainsIgnore
|
||||
TextContainsIgnoreCase
|
||||
TextListContains
|
||||
|
||||
textMax
|
||||
textCompareMax
|
||||
)
|
||||
|
||||
type IntQuery struct {
|
||||
Column string
|
||||
Int int
|
||||
Compare IntComparison
|
||||
//Deprecated: Use TextComparison, will be removed as soon as all calls are changed to query
|
||||
func TextComparisonFromMethod(m domain.SearchMethod) TextComparison {
|
||||
switch m {
|
||||
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) {
|
||||
if compare < 0 || compare >= intMax {
|
||||
return nil, errors.New("invalid compare")
|
||||
type NumberQuery struct {
|
||||
Column Column
|
||||
Number interface{}
|
||||
Compare NumberComparison
|
||||
}
|
||||
|
||||
func NewNumberQuery(c Column, value interface{}, compare NumberComparison) (*NumberQuery, error) {
|
||||
if compare < 0 || compare >= numberCompareMax {
|
||||
return nil, ErrInvalidCompare
|
||||
}
|
||||
if column == "" {
|
||||
return nil, errors.New("missing column")
|
||||
if c.isZero() {
|
||||
return nil, ErrMissingColumn
|
||||
}
|
||||
return &IntQuery{
|
||||
Column: column,
|
||||
Int: value,
|
||||
switch reflect.TypeOf(value).Kind() {
|
||||
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:
|
||||
//everything fine
|
||||
default:
|
||||
return nil, ErrInvalidNumber
|
||||
}
|
||||
return &NumberQuery{
|
||||
Column: c,
|
||||
Number: value,
|
||||
Compare: compare,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (q *IntQuery) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
query = query.Where(q.comp())
|
||||
return query
|
||||
func (q *NumberQuery) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
}
|
||||
|
||||
func (s *IntQuery) comp() sq.Sqlizer {
|
||||
func (s *NumberQuery) comp() (comparison interface{}, args []interface{}) {
|
||||
switch s.Compare {
|
||||
case IntEquals:
|
||||
return sq.Eq{s.Column: s.Int}
|
||||
case IntGreater:
|
||||
return sq.Gt{s.Column: s.Int}
|
||||
case IntLess:
|
||||
return sq.Lt{s.Column: s.Int}
|
||||
case NumberEquals:
|
||||
return sq.Eq{s.Column.identifier(): s.Number}, nil
|
||||
case NumberNotEquals:
|
||||
return sq.NotEq{s.Column.identifier(): s.Number}, nil
|
||||
case NumberLess:
|
||||
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 (
|
||||
IntEquals IntComparison = iota
|
||||
IntGreater
|
||||
IntLess
|
||||
NumberEquals NumberComparison = iota
|
||||
NumberNotEquals
|
||||
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()
|
||||
}
|
||||
|
767
internal/query/search_query_test.go
Normal file
767
internal/query/search_query_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -20,9 +20,9 @@ const (
|
||||
type TriggerActionsSetEvent struct {
|
||||
eventstore.BaseEvent
|
||||
|
||||
FlowType domain.FlowType
|
||||
TriggerType domain.TriggerType
|
||||
ActionIDs []string
|
||||
FlowType domain.FlowType `json:"flowType"`
|
||||
TriggerType domain.TriggerType `json:"triggerType"`
|
||||
ActionIDs []string `json:"actionIDs"`
|
||||
}
|
||||
|
||||
func (e *TriggerActionsSetEvent) Data() interface{} {
|
||||
@ -63,9 +63,9 @@ func TriggerActionsSetEventMapper(event *repository.Event) (eventstore.EventRead
|
||||
type TriggerActionsCascadeRemovedEvent struct {
|
||||
eventstore.BaseEvent
|
||||
|
||||
FlowType domain.FlowType
|
||||
TriggerType domain.TriggerType
|
||||
ActionID string
|
||||
FlowType domain.FlowType `json:"flowType"`
|
||||
TriggerType domain.TriggerType `json:"triggerType"`
|
||||
ActionID string `json:"actionID"`
|
||||
}
|
||||
|
||||
func (e *TriggerActionsCascadeRemovedEvent) Data() interface{} {
|
||||
@ -104,7 +104,7 @@ func TriggerActionsCascadeRemovedEventMapper(event *repository.Event) (eventstor
|
||||
type FlowClearedEvent struct {
|
||||
eventstore.BaseEvent
|
||||
|
||||
FlowType domain.FlowType
|
||||
FlowType domain.FlowType `json:"flowType"`
|
||||
}
|
||||
|
||||
func (e *FlowClearedEvent) Data() interface{} {
|
||||
|
@ -133,15 +133,9 @@ func NewOrgDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate
|
||||
}
|
||||
|
||||
func OrgDeactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
||||
orgChanged := &OrgDeactivatedEvent{
|
||||
return &OrgDeactivatedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
err := json.Unmarshal(event.Data, orgChanged)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "ORG-DAfbs", "unable to unmarshal org deactivated")
|
||||
}
|
||||
|
||||
return orgChanged, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
type OrgReactivatedEvent struct {
|
||||
@ -167,15 +161,9 @@ func NewOrgReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate
|
||||
}
|
||||
|
||||
func OrgReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
||||
orgChanged := &OrgReactivatedEvent{
|
||||
return &OrgReactivatedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
err := json.Unmarshal(event.Data, orgChanged)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "ORG-DAfbs", "unable to unmarshal org deactivated")
|
||||
}
|
||||
|
||||
return orgChanged, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
type OrgRemovedEvent struct {
|
||||
|
@ -376,6 +376,10 @@ Errors:
|
||||
WrongTriggerType: TriggerType ist ungültig
|
||||
NoChanges: Keine Änderungen
|
||||
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:
|
||||
user:
|
||||
added: Benutzer hinzugefügt
|
||||
|
@ -376,6 +376,10 @@ Errors:
|
||||
WrongTriggerType: TriggerType is invalid
|
||||
NoChanges: No Changes
|
||||
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:
|
||||
user:
|
||||
added: User added
|
||||
|
@ -4,20 +4,32 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
|
||||
"github.com/caos/zitadel/internal/actions"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
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) {
|
||||
triggerActions, err := l.query.GetActionsByFlowAndTriggerType(context.TODO(), domain.FlowTypeExternalAuthentication, domain.TriggerTypePostAuthentication)
|
||||
func (l *Login) customExternalUserMapping(ctx context.Context, user *domain.ExternalUser, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView) (*domain.ExternalUser, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
ctx := (&actions.Context{}).SetToken(tokens)
|
||||
actionCtx := (&actions.Context{}).SetToken(tokens)
|
||||
api := (&actions.API{}).SetExternalUser(user).SetMetadata(&user.Metadatas)
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -25,15 +37,15 @@ func (l *Login) customExternalUserMapping(user *domain.ExternalUser, tokens *oid
|
||||
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) {
|
||||
triggerActions, err := l.query.GetActionsByFlowAndTriggerType(context.TODO(), domain.FlowTypeExternalAuthentication, domain.TriggerTypePreCreation)
|
||||
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, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ctx := (&actions.Context{}).SetToken(tokens)
|
||||
actionCtx := (&actions.Context{}).SetToken(tokens)
|
||||
api := (&actions.API{}).SetHuman(user).SetMetadata(&metadata)
|
||||
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 {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -41,16 +53,16 @@ func (l *Login) customExternalUserToLoginUserMapping(user *domain.Human, tokens
|
||||
return user, metadata, err
|
||||
}
|
||||
|
||||
func (l *Login) customGrants(userID string, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView) ([]*domain.UserGrant, error) {
|
||||
triggerActions, err := l.query.GetActionsByFlowAndTriggerType(context.TODO(), domain.FlowTypeExternalAuthentication, domain.TriggerTypePostCreation)
|
||||
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, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx := (&actions.Context{}).SetToken(tokens)
|
||||
actionCtx := (&actions.Context{}).SetToken(tokens)
|
||||
actionUserGrants := make([]actions.UserGrant, 0)
|
||||
api := (&actions.API{}).SetUserGrants(&actionUserGrants)
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -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) {
|
||||
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 {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
@ -284,7 +284,7 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
|
||||
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
|
||||
linkingUser := authReq.LinkingUsers[len(authReq.LinkingUsers)-1]
|
||||
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))
|
||||
if err != nil {
|
||||
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)
|
||||
return
|
||||
}
|
||||
userGrants, err := l.customGrants(authReq.UserID, nil, authReq, idpConfig)
|
||||
userGrants, err := l.customGrants(authReq.UserID, nil, authReq, idpConfig, resourceOwner)
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/oidc/pkg/client/rp"
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
|
||||
http_util "github.com/caos/zitadel/internal/api/http"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"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}
|
||||
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 {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
@ -126,7 +127,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
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 {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
@ -141,7 +142,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
}
|
||||
userGrants, err := l.customGrants(authReq.UserID, tokens, authReq, idpConfig)
|
||||
userGrants, err := l.customGrants(authReq.UserID, tokens, authReq, idpConfig, resourceOwner)
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
|
@ -6,6 +6,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
http_utils "github.com/caos/zitadel/internal/api/http"
|
||||
"github.com/caos/zitadel/internal/api/http/middleware"
|
||||
@ -23,9 +27,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
_ "github.com/caos/zitadel/internal/ui/login/statik"
|
||||
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 {
|
||||
|
9
migrations/cockroach/V1.73__fix_projections.sql
Normal file
9
migrations/cockroach/V1.73__fix_projections.sql
Normal 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');
|
@ -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
|
||||
rpc IsOrgUnique(IsOrgUniqueRequest) returns (IsOrgUniqueResponse) {
|
||||
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
|
||||
// all queries need to match (AND)
|
||||
rpc ListOrgs(ListOrgsRequest) returns (ListOrgsResponse) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user