fix(queries): idp projection (#2411)

* 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

* move org domain to query

* cleanup code

* add org id as condition to projection

* begin projection

* add custom column constructors

* start query

* import

* initial implementation of login policy

* remove unused field

* tests

* factors

* simplify reducers

* idp projection

* fix: org projection table const

* start query

* apis changed

* 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

* column

* column

* column for current sequences

* latest sequence

* global counter column

* fix is org unique

* count

* remove col

* fix naming

* correct errors

* error messages

* deprecate duplicated is_default in api's,
error messages,
migrations

* migration version

* add bool query

* Update internal/query/policy_login.go

Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>

* Update login_policy.go

* fix tests

* add creation-, change-date and sequence to idp,
update meta data of idp on config changes,
fix wrong mapping

* tests

* remove unused file

* add resource owner

* move vars to top

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
Co-authored-by: Christian Jakob <47860090+thesephirot@users.noreply.github.com>
Co-authored-by: Elio Bischof <eliobischof@gmail.com>
Co-authored-by: Stefan Benz <stefan@caos.ch>
Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Silvan 2021-10-20 13:20:07 +02:00 committed by GitHub
parent 48135daa84
commit 1aa26e727e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2036 additions and 100 deletions

View File

@ -3,13 +3,14 @@ package admin
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
idp_grpc "github.com/caos/zitadel/internal/api/grpc/idp"
object_pb "github.com/caos/zitadel/internal/api/grpc/object"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
)
func (s *Server) GetIDPByID(ctx context.Context, req *admin_pb.GetIDPByIDRequest) (*admin_pb.GetIDPByIDResponse, error) {
idp, err := s.query.DefaultIDPConfigByID(ctx, req.Id)
idp, err := s.query.IDPByIDAndResourceOwner(ctx, req.Id, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
@ -17,13 +18,17 @@ func (s *Server) GetIDPByID(ctx context.Context, req *admin_pb.GetIDPByIDRequest
}
func (s *Server) ListIDPs(ctx context.Context, req *admin_pb.ListIDPsRequest) (*admin_pb.ListIDPsResponse, error) {
resp, err := s.iam.SearchIDPConfigs(ctx, listIDPsToModel(req))
queries, err := listIDPsToModel(req)
if err != nil {
return nil, err
}
resp, err := s.query.SearchIDPs(ctx, authz.GetCtxData(ctx).OrgID, queries)
if err != nil {
return nil, err
}
return &admin_pb.ListIDPsResponse{
Result: idp_grpc.IDPViewsToPb(resp.Result),
Details: object_pb.ToListDetails(resp.TotalResult, resp.Sequence, resp.Timestamp),
Result: idp_grpc.IDPViewsToPb(resp.IDPs),
Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.Timestamp),
}, nil
}

View File

@ -4,8 +4,10 @@ import (
idp_grpc "github.com/caos/zitadel/internal/api/grpc/idp"
"github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/query"
user_model "github.com/caos/zitadel/internal/user/model"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
)
@ -81,34 +83,43 @@ func updateJWTConfigToDomain(req *admin_pb.UpdateIDPJWTConfigRequest) *domain.JW
}
}
func listIDPsToModel(req *admin_pb.ListIDPsRequest) *iam_model.IDPConfigSearchRequest {
func listIDPsToModel(req *admin_pb.ListIDPsRequest) (*query.IDPSearchQueries, error) {
offset, limit, asc := object.ListQueryToModel(req.Query)
return &iam_model.IDPConfigSearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: idp_grpc.FieldNameToModel(req.SortingColumn),
Queries: idpQueriesToModel(req.Queries),
queries, err := idpQueriesToModel(req.Queries)
if err != nil {
return nil, err
}
return &query.IDPSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: idp_grpc.FieldNameToModel(req.SortingColumn),
},
Queries: queries,
}, nil
}
func idpQueriesToModel(queries []*admin_pb.IDPQuery) []*iam_model.IDPConfigSearchQuery {
q := make([]*iam_model.IDPConfigSearchQuery, len(queries))
func idpQueriesToModel(queries []*admin_pb.IDPQuery) (q []query.SearchQuery, err error) {
q = make([]query.SearchQuery, len(queries))
for i, query := range queries {
q[i] = idpQueryToModel(query)
q[i], err = idpQueryToModel(query)
if err != nil {
return nil, err
}
}
return q
return q, nil
}
func idpQueryToModel(query *admin_pb.IDPQuery) *iam_model.IDPConfigSearchQuery {
switch q := query.Query.(type) {
func idpQueryToModel(idpQuery *admin_pb.IDPQuery) (query.SearchQuery, error) {
switch q := idpQuery.Query.(type) {
case *admin_pb.IDPQuery_IdpNameQuery:
return idp_grpc.IDPNameQueryToModel(q.IdpNameQuery)
return query.NewIDPNameSearchQuery(object.TextMethodToQuery(q.IdpNameQuery.Method), q.IdpNameQuery.Name)
case *admin_pb.IDPQuery_IdpIdQuery:
return idp_grpc.IDPIDQueryToModel(q.IdpIdQuery)
return query.NewIDPIDSearchQuery(q.IdpIdQuery.Id)
default:
return nil
return nil, errors.ThrowInvalidArgument(nil, "ADMIN-VmqQu", "List.Query.Invalid")
}
}

View File

@ -4,11 +4,12 @@ import (
obj_grpc "github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/domain"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/query"
user_model "github.com/caos/zitadel/internal/user/model"
idp_pb "github.com/caos/zitadel/pkg/grpc/idp"
)
func IDPViewsToPb(idps []*iam_model.IDPConfigView) []*idp_pb.IDP {
func IDPViewsToPb(idps []*query.IDP) []*idp_pb.IDP {
resp := make([]*idp_pb.IDP, len(idps))
for i, idp := range idps {
resp[i] = ModelIDPViewToPb(idp)
@ -16,33 +17,34 @@ func IDPViewsToPb(idps []*iam_model.IDPConfigView) []*idp_pb.IDP {
return resp
}
func ModelIDPViewToPb(idp *iam_model.IDPConfigView) *idp_pb.IDP {
func ModelIDPViewToPb(idp *query.IDP) *idp_pb.IDP {
return &idp_pb.IDP{
Id: idp.IDPConfigID,
Id: idp.ID,
State: ModelIDPStateToPb(idp.State),
Name: idp.Name,
StylingType: ModelIDPStylingTypeToPb(idp.StylingType),
AutoRegister: idp.AutoRegister,
Owner: ModelIDPProviderTypeToPb(idp.IDPProviderType),
Owner: ModelIDPProviderTypeToPb(idp.OwnerType),
Config: ModelIDPViewToConfigPb(idp),
Details: obj_grpc.ToViewDetailsPb(
idp.Sequence,
idp.CreationDate,
idp.ChangeDate,
idp.AggregateID,
idp.ID,
),
}
}
func IDPViewToPb(idp *domain.IDPConfigView) *idp_pb.IDP {
func IDPViewToPb(idp *query.IDP) *idp_pb.IDP {
mapped := &idp_pb.IDP{
Id: idp.IDPConfigID,
Owner: ownerTypeToPB(idp.OwnerType),
Id: idp.ID,
State: IDPStateToPb(idp.State),
Name: idp.Name,
StylingType: IDPStylingTypeToPb(idp.StylingType),
AutoRegister: idp.AutoRegister,
Config: IDPViewToConfigPb(idp),
Details: obj_grpc.ToViewDetailsPb(idp.Sequence, idp.CreationDate, idp.ChangeDate, idp.AggregateID),
Details: obj_grpc.ToViewDetailsPb(idp.Sequence, idp.CreationDate, idp.ChangeDate, idp.ID),
}
return mapped
}
@ -107,11 +109,11 @@ func IDPStateToPb(state domain.IDPConfigState) idp_pb.IDPState {
}
}
func ModelIDPStateToPb(state iam_model.IDPConfigState) idp_pb.IDPState {
func ModelIDPStateToPb(state domain.IDPConfigState) idp_pb.IDPState {
switch state {
case iam_model.IDPConfigStateActive:
case domain.IDPConfigStateActive:
return idp_pb.IDPState_IDP_STATE_ACTIVE
case iam_model.IDPConfigStateInactive:
case domain.IDPConfigStateInactive:
return idp_pb.IDPState_IDP_STATE_INACTIVE
default:
return idp_pb.IDPState_IDP_STATE_UNSPECIFIED
@ -127,9 +129,9 @@ func IDPStylingTypeToDomain(stylingType idp_pb.IDPStylingType) domain.IDPConfigS
}
}
func ModelIDPStylingTypeToPb(stylingType iam_model.IDPStylingType) idp_pb.IDPStylingType {
func ModelIDPStylingTypeToPb(stylingType domain.IDPConfigStylingType) idp_pb.IDPStylingType {
switch stylingType {
case iam_model.IDPStylingTypeGoogle:
case domain.IDPConfigStylingTypeGoogle:
return idp_pb.IDPStylingType_STYLING_TYPE_GOOGLE
default:
return idp_pb.IDPStylingType_STYLING_TYPE_UNSPECIFIED
@ -145,65 +147,65 @@ func IDPStylingTypeToPb(stylingType domain.IDPConfigStylingType) idp_pb.IDPStyli
}
}
func ModelIDPViewToConfigPb(config *iam_model.IDPConfigView) idp_pb.IDPConfig {
if config.IsOIDC {
func ModelIDPViewToConfigPb(config *query.IDP) idp_pb.IDPConfig {
if config.OIDCIDP != nil {
return &idp_pb.IDP_OidcConfig{
OidcConfig: &idp_pb.OIDCConfig{
ClientId: config.OIDCClientID,
Issuer: config.OIDCIssuer,
Scopes: config.OIDCScopes,
DisplayNameMapping: ModelMappingFieldToPb(config.OIDCIDPDisplayNameMapping),
UsernameMapping: ModelMappingFieldToPb(config.OIDCUsernameMapping),
ClientId: config.ClientID,
Issuer: config.OIDCIDP.Issuer,
Scopes: config.Scopes,
DisplayNameMapping: ModelMappingFieldToPb(config.DisplayNameMapping),
UsernameMapping: ModelMappingFieldToPb(config.UsernameMapping),
},
}
}
return &idp_pb.IDP_JwtConfig{
JwtConfig: &idp_pb.JWTConfig{
JwtEndpoint: config.JWTEndpoint,
Issuer: config.JWTIssuer,
KeysEndpoint: config.JWTKeysEndpoint,
HeaderName: config.JWTHeaderName,
JwtEndpoint: config.Endpoint,
Issuer: config.JWTIDP.Issuer,
KeysEndpoint: config.KeysEndpoint,
HeaderName: config.HeaderName,
},
}
}
func IDPViewToConfigPb(config *domain.IDPConfigView) idp_pb.IDPConfig {
if config.IsOIDC {
func IDPViewToConfigPb(config *query.IDP) idp_pb.IDPConfig {
if config.OIDCIDP != nil {
return &idp_pb.IDP_OidcConfig{
OidcConfig: &idp_pb.OIDCConfig{
ClientId: config.OIDCClientID,
Issuer: config.OIDCIssuer,
Scopes: config.OIDCScopes,
DisplayNameMapping: MappingFieldToPb(config.OIDCIDPDisplayNameMapping),
UsernameMapping: MappingFieldToPb(config.OIDCUsernameMapping),
ClientId: config.ClientID,
Issuer: config.OIDCIDP.Issuer,
Scopes: config.Scopes,
DisplayNameMapping: MappingFieldToPb(config.DisplayNameMapping),
UsernameMapping: MappingFieldToPb(config.UsernameMapping),
},
}
}
return &idp_pb.IDP_JwtConfig{
JwtConfig: &idp_pb.JWTConfig{
JwtEndpoint: config.JWTEndpoint,
Issuer: config.JWTIssuer,
KeysEndpoint: config.JWTKeysEndpoint,
JwtEndpoint: config.JWTIDP.Endpoint,
Issuer: config.JWTIDP.Issuer,
KeysEndpoint: config.JWTIDP.KeysEndpoint,
},
}
}
func FieldNameToModel(fieldName idp_pb.IDPFieldName) iam_model.IDPConfigSearchKey {
func FieldNameToModel(fieldName idp_pb.IDPFieldName) query.Column {
switch fieldName {
// case admin.IdpSearchKey_IDPSEARCHKEY_IDP_CONFIG_ID: //TODO: not implemented in proto
// return iam_model.IDPConfigSearchKeyIdpConfigID
case idp_pb.IDPFieldName_IDP_FIELD_NAME_NAME:
return iam_model.IDPConfigSearchKeyName
return query.IDPNameCol
default:
return iam_model.IDPConfigSearchKeyUnspecified
return query.Column{}
}
}
func ModelMappingFieldToPb(mappingField iam_model.OIDCMappingField) idp_pb.OIDCMappingField {
func ModelMappingFieldToPb(mappingField domain.OIDCMappingField) idp_pb.OIDCMappingField {
switch mappingField {
case iam_model.OIDCMappingFieldEmail:
case domain.OIDCMappingFieldEmail:
return idp_pb.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL
case iam_model.OIDCMappingFieldPreferredLoginName:
case domain.OIDCMappingFieldPreferredLoginName:
return idp_pb.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME
default:
return idp_pb.OIDCMappingField_OIDC_MAPPING_FIELD_UNSPECIFIED
@ -232,11 +234,11 @@ func MappingFieldToDomain(mappingField idp_pb.OIDCMappingField) domain.OIDCMappi
}
}
func ModelIDPProviderTypeToPb(typ iam_model.IDPProviderType) idp_pb.IDPOwnerType {
func ModelIDPProviderTypeToPb(typ domain.IdentityProviderType) idp_pb.IDPOwnerType {
switch typ {
case iam_model.IDPProviderTypeOrg:
case domain.IdentityProviderTypeOrg:
return idp_pb.IDPOwnerType_IDP_OWNER_TYPE_ORG
case iam_model.IDPProviderTypeSystem:
case domain.IdentityProviderTypeSystem:
return idp_pb.IDPOwnerType_IDP_OWNER_TYPE_SYSTEM
default:
return idp_pb.IDPOwnerType_IDP_OWNER_TYPE_UNSPECIFIED
@ -288,3 +290,13 @@ func IDPOwnerTypeQueryToModel(query *idp_pb.IDPOwnerTypeQuery) *iam_model.IDPCon
Value: IDPProviderTypeModelFromPb(query.OwnerType),
}
}
func ownerTypeToPB(typ domain.IdentityProviderType) idp_pb.IDPOwnerType {
switch typ {
case domain.IdentityProviderTypeOrg:
return idp_pb.IDPOwnerType_IDP_OWNER_TYPE_ORG
case domain.IdentityProviderTypeSystem:
return idp_pb.IDPOwnerType_IDP_OWNER_TYPE_SYSTEM
default:
return idp_pb.IDPOwnerType_IDP_OWNER_TYPE_UNSPECIFIED
}
}

View File

@ -10,20 +10,25 @@ import (
)
func (s *Server) GetOrgIDPByID(ctx context.Context, req *mgmt_pb.GetOrgIDPByIDRequest) (*mgmt_pb.GetOrgIDPByIDResponse, error) {
idp, err := s.org.IDPConfigByID(ctx, req.Id)
idp, err := s.query.IDPByIDAndResourceOwner(ctx, req.Id, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return &mgmt_pb.GetOrgIDPByIDResponse{Idp: idp_grpc.ModelIDPViewToPb(idp)}, nil
}
func (s *Server) ListOrgIDPs(ctx context.Context, req *mgmt_pb.ListOrgIDPsRequest) (*mgmt_pb.ListOrgIDPsResponse, error) {
resp, err := s.org.SearchIDPConfigs(ctx, listIDPsToModel(req))
queries, err := listIDPsToModel(req)
if err != nil {
return nil, err
}
resp, err := s.query.SearchIDPs(ctx, authz.GetCtxData(ctx).OrgID, queries)
if err != nil {
return nil, err
}
return &mgmt_pb.ListOrgIDPsResponse{
Result: idp_grpc.IDPViewsToPb(resp.Result),
Details: object_pb.ToListDetails(resp.TotalResult, resp.Sequence, resp.Timestamp),
Result: idp_grpc.IDPViewsToPb(resp.IDPs),
Details: object_pb.ToListDetails(resp.Count, resp.Sequence, resp.Timestamp),
}, nil
}
func (s *Server) AddOrgOIDCIDP(ctx context.Context, req *mgmt_pb.AddOrgOIDCIDPRequest) (*mgmt_pb.AddOrgOIDCIDPResponse, error) {

View File

@ -4,8 +4,10 @@ import (
idp_grpc "github.com/caos/zitadel/internal/api/grpc/idp"
"github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/query"
user_model "github.com/caos/zitadel/internal/user/model"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
)
@ -81,36 +83,45 @@ func updateJWTConfigToDomain(req *mgmt_pb.UpdateOrgIDPJWTConfigRequest) *domain.
}
}
func listIDPsToModel(req *mgmt_pb.ListOrgIDPsRequest) *iam_model.IDPConfigSearchRequest {
func listIDPsToModel(req *mgmt_pb.ListOrgIDPsRequest) (queries *query.IDPSearchQueries, err error) {
offset, limit, asc := object.ListQueryToModel(req.Query)
return &iam_model.IDPConfigSearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: idp_grpc.FieldNameToModel(req.SortingColumn),
Queries: idpQueriesToModel(req.Queries),
q, err := idpQueriesToModel(req.Queries)
if err != nil {
return nil, err
}
return &query.IDPSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: idp_grpc.FieldNameToModel(req.SortingColumn),
},
Queries: q,
}, nil
}
func idpQueriesToModel(queries []*mgmt_pb.IDPQuery) []*iam_model.IDPConfigSearchQuery {
q := make([]*iam_model.IDPConfigSearchQuery, len(queries))
func idpQueriesToModel(queries []*mgmt_pb.IDPQuery) (q []query.SearchQuery, err error) {
q = make([]query.SearchQuery, len(queries))
for i, query := range queries {
q[i] = idpQueryToModel(query)
q[i], err = idpQueryToModel(query)
if err != nil {
return nil, err
}
}
return q
return q, nil
}
func idpQueryToModel(query *mgmt_pb.IDPQuery) *iam_model.IDPConfigSearchQuery {
switch q := query.Query.(type) {
func idpQueryToModel(idpQuery *mgmt_pb.IDPQuery) (query.SearchQuery, error) {
switch q := idpQuery.Query.(type) {
case *mgmt_pb.IDPQuery_IdpNameQuery:
return idp_grpc.IDPNameQueryToModel(q.IdpNameQuery)
return query.NewIDPNameSearchQuery(object.TextMethodToQuery(q.IdpNameQuery.Method), q.IdpNameQuery.Name)
case *mgmt_pb.IDPQuery_IdpIdQuery:
return idp_grpc.IDPIDQueryToModel(q.IdpIdQuery)
return query.NewIDPIDSearchQuery(q.IdpIdQuery.Id)
case *mgmt_pb.IDPQuery_OwnerTypeQuery:
return idp_grpc.IDPOwnerTypeQueryToModel(q.OwnerTypeQuery)
return query.NewIDPOwnerTypeSearchQuery(idp_grpc.IDPProviderTypeFromPb(q.OwnerTypeQuery.OwnerType))
default:
return nil
return nil, errors.ThrowInvalidArgument(nil, "MANAG-WtLPV", "List.Query.Invalid")
}
}

View File

@ -1,16 +0,0 @@
package query
import (
"context"
"github.com/caos/zitadel/internal/domain"
)
func (r *Queries) DefaultIDPConfigByID(ctx context.Context, idpConfigID string) (*domain.IDPConfigView, error) {
idpConfig := NewIAMIDPConfigReadModel(r.iamID, idpConfigID)
err := r.eventstore.FilterToQueryReducer(ctx, idpConfig)
if err != nil {
return nil, err
}
return readModelToIDPConfigView(idpConfig), nil
}

489
internal/query/idp.go Normal file
View File

@ -0,0 +1,489 @@
package query
import (
"context"
"database/sql"
errs "errors"
"log"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/query/projection"
"github.com/lib/pq"
)
type IDP struct {
CreationDate time.Time
ChangeDate time.Time
Sequence uint64
ResourceOwner string
ID string
State domain.IDPConfigState
Name string
StylingType domain.IDPConfigStylingType
OwnerType domain.IdentityProviderType
AutoRegister bool
*OIDCIDP
*JWTIDP
}
type IDPs struct {
SearchResponse
IDPs []*IDP
}
type OIDCIDP struct {
IDPID string
ClientID string
ClientSecret *crypto.CryptoValue
Issuer string
Scopes []string
DisplayNameMapping domain.OIDCMappingField
UsernameMapping domain.OIDCMappingField
AuthorizationEndpoint string
TokenEndpoint string
}
type JWTIDP struct {
IDPID string
Issuer string
KeysEndpoint string
HeaderName string
Endpoint string
}
var (
idpTable = table{
name: projection.IDPTable,
}
IDPIDCol = Column{
name: projection.IDPIDCol,
table: idpTable,
}
IDPCreationDateCol = Column{
name: projection.IDPCreationDateCol,
table: idpTable,
}
IDPChangeDateCol = Column{
name: projection.IDPChangeDateCol,
table: idpTable,
}
IDPSequenceCol = Column{
name: projection.IDPSequenceCol,
table: idpTable,
}
IDPResourceOwnerCol = Column{
name: projection.IDPResourceOwnerCol,
table: idpTable,
}
IDPStateCol = Column{
name: projection.IDPStateCol,
table: idpTable,
}
IDPNameCol = Column{
name: projection.IDPNameCol,
table: idpTable,
}
IDPStylingTypeCol = Column{
name: projection.IDPStylingTypeCol,
table: idpTable,
}
IDPOwnerCol = Column{
name: projection.IDPOwnerTypeCol,
table: idpTable,
}
IDPAutoRegisterCol = Column{
name: projection.IDPAutoRegisterCol,
table: idpTable,
}
)
var (
oidcIDPTable = table{
name: projection.IDPOIDCTable,
}
OIDCIDPColIDPID = Column{
name: projection.OIDCConfigIDPIDCol,
table: oidcIDPTable,
}
OIDCIDPColClientID = Column{
name: projection.OIDCConfigClientIDCol,
table: oidcIDPTable,
}
OIDCIDPColClientSecret = Column{
name: projection.OIDCConfigClientSecretCol,
table: oidcIDPTable,
}
OIDCIDPColIssuer = Column{
name: projection.OIDCConfigIssuerCol,
table: oidcIDPTable,
}
OIDCIDPColScopes = Column{
name: projection.OIDCConfigScopesCol,
table: oidcIDPTable,
}
OIDCIDPColDisplayNameMapping = Column{
name: projection.OIDCConfigDisplayNameMappingCol,
table: oidcIDPTable,
}
OIDCIDPColUsernameMapping = Column{
name: projection.OIDCConfigUsernameMappingCol,
table: oidcIDPTable,
}
OIDCIDPColAuthorizationEndpoint = Column{
name: projection.OIDCConfigAuthorizationEndpointCol,
table: oidcIDPTable,
}
OIDCIDPColTokenEndpoint = Column{
name: projection.OIDCConfigTokenEndpointCol,
table: oidcIDPTable,
}
)
var (
jwtIDPTable = table{
name: projection.IDPJWTTable,
}
JWTIDPColIDPID = Column{
name: projection.JWTConfigIDPIDCol,
table: jwtIDPTable,
}
JWTIDPColIssuer = Column{
name: projection.JWTConfigIssuerCol,
table: jwtIDPTable,
}
JWTIDPColKeysEndpoint = Column{
name: projection.JWTConfigKeysEndpointCol,
table: jwtIDPTable,
}
JWTIDPColHeaderName = Column{
name: projection.JWTConfigHeaderNameCol,
table: jwtIDPTable,
}
JWTIDPColEndpoint = Column{
name: projection.JWTConfigEndpointCol,
table: jwtIDPTable,
}
)
//IDPByIDAndResourceOwner searches for the requested id in the context of the resource owner and IAM
func (q *Queries) IDPByIDAndResourceOwner(ctx context.Context, id, resourceOwner string) (*IDP, error) {
stmt, scan := prepareIDPByIDQuery()
query, args, err := stmt.Where(
sq.And{
sq.Eq{
IDPIDCol.identifier(): id,
},
sq.Or{
sq.Eq{
IDPResourceOwnerCol.identifier(): resourceOwner,
},
sq.Eq{
IDPResourceOwnerCol.identifier(): q.iamID,
},
},
},
).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-0gocI", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
//SearchIDPs searches executes the query in the context of the resource owner and IAM
func (q *Queries) SearchIDPs(ctx context.Context, resourceOwner string, queries *IDPSearchQueries) (idps *IDPs, err error) {
query, scan := prepareIDPsQuery()
query = queries.toQuery(query)
query = query.Where(
sq.Or{
sq.Eq{
IDPResourceOwnerCol.identifier(): resourceOwner,
IDPResourceOwnerCol.identifier(): q.iamID,
},
},
)
stmt, args, err := queries.toQuery(query).ToSql()
if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-zC6gk", "Errors.Query.InvalidRequest")
}
rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil {
log.Println(err)
return nil, errors.ThrowInternal(err, "QUERY-YTug9", "Errors.Internal")
}
idps, err = scan(rows)
if err != nil {
return nil, err
}
idps.LatestSequence, err = q.latestSequence(ctx, idpTable)
return idps, err
}
type IDPSearchQueries struct {
SearchRequest
Queries []SearchQuery
}
func NewIDPIDSearchQuery(id string) (SearchQuery, error) {
return NewTextQuery(IDPIDCol, id, TextEquals)
}
func NewIDPOwnerTypeSearchQuery(ownerType domain.IdentityProviderType) (SearchQuery, error) {
switch ownerType {
case domain.IdentityProviderTypeOrg:
return NewBoolQuery(LoginPolicyColumnIsDefault, false)
case domain.IdentityProviderTypeSystem:
return NewBoolQuery(LoginPolicyColumnIsDefault, true)
default:
return nil, errors.ThrowUnimplemented(nil, "QUERY-8yZAI", "Errors.Query.InvalidRequest")
}
}
func NewIDPNameSearchQuery(method TextComparison, value string) (SearchQuery, error) {
return NewTextQuery(IDPNameCol, value, method)
}
func (q *IDPSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
query = q.SearchRequest.toQuery(query)
for _, q := range q.Queries {
query = q.ToQuery(query)
}
return query
}
func prepareIDPByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDP, error)) {
return sq.Select(
IDPIDCol.identifier(),
IDPResourceOwnerCol.identifier(),
IDPCreationDateCol.identifier(),
IDPChangeDateCol.identifier(),
IDPSequenceCol.identifier(),
IDPStateCol.identifier(),
IDPNameCol.identifier(),
IDPStylingTypeCol.identifier(),
IDPOwnerCol.identifier(),
IDPAutoRegisterCol.identifier(),
OIDCIDPColIDPID.identifier(),
OIDCIDPColClientID.identifier(),
OIDCIDPColClientSecret.identifier(),
OIDCIDPColIssuer.identifier(),
OIDCIDPColScopes.identifier(),
OIDCIDPColDisplayNameMapping.identifier(),
OIDCIDPColUsernameMapping.identifier(),
OIDCIDPColAuthorizationEndpoint.identifier(),
OIDCIDPColTokenEndpoint.identifier(),
JWTIDPColIDPID.identifier(),
JWTIDPColIssuer.identifier(),
JWTIDPColKeysEndpoint.identifier(),
JWTIDPColHeaderName.identifier(),
JWTIDPColEndpoint.identifier(),
).From(idpTable.identifier()).
LeftJoin(join(OIDCIDPColIDPID, IDPIDCol)).
LeftJoin(join(JWTIDPColIDPID, IDPIDCol)).
PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*IDP, error) {
idp := new(IDP)
oidcIDPID := sql.NullString{}
oidcClientID := sql.NullString{}
oidcClientSecret := new(crypto.CryptoValue)
oidcIssuer := sql.NullString{}
oidcScopes := pq.StringArray{}
oidcDisplayNameMapping := sql.NullInt32{}
oidcUsernameMapping := sql.NullInt32{}
oidcAuthorizationEndpoint := sql.NullString{}
oidcTokenEndpoint := sql.NullString{}
jwtIDPID := sql.NullString{}
jwtIssuer := sql.NullString{}
jwtKeysEndpoint := sql.NullString{}
jwtHeaderName := sql.NullString{}
jwtEndpoint := sql.NullString{}
err := row.Scan(
&idp.ID,
&idp.ResourceOwner,
&idp.CreationDate,
&idp.ChangeDate,
&idp.Sequence,
&idp.State,
&idp.Name,
&idp.StylingType,
&idp.OwnerType,
&idp.AutoRegister,
&oidcIDPID,
&oidcClientID,
oidcClientSecret,
&oidcIssuer,
&oidcScopes,
&oidcDisplayNameMapping,
&oidcUsernameMapping,
&oidcAuthorizationEndpoint,
&oidcTokenEndpoint,
&jwtIDPID,
&jwtIssuer,
&jwtKeysEndpoint,
&jwtHeaderName,
&jwtEndpoint,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-rhR2o", "Errors.IDPConfig.NotExisting")
}
return nil, errors.ThrowInternal(err, "QUERY-zE3Ro", "Errors.Internal")
}
if oidcIDPID.Valid {
idp.OIDCIDP = &OIDCIDP{
IDPID: oidcIDPID.String,
ClientID: oidcClientID.String,
ClientSecret: oidcClientSecret,
Issuer: oidcIssuer.String,
Scopes: oidcScopes,
DisplayNameMapping: domain.OIDCMappingField(oidcDisplayNameMapping.Int32),
UsernameMapping: domain.OIDCMappingField(oidcUsernameMapping.Int32),
AuthorizationEndpoint: oidcAuthorizationEndpoint.String,
TokenEndpoint: oidcTokenEndpoint.String,
}
} else if jwtIDPID.Valid {
idp.JWTIDP = &JWTIDP{
IDPID: jwtIDPID.String,
Issuer: jwtIssuer.String,
KeysEndpoint: jwtKeysEndpoint.String,
HeaderName: jwtHeaderName.String,
Endpoint: jwtEndpoint.String,
}
}
return idp, nil
}
}
func prepareIDPsQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPs, error)) {
return sq.Select(
IDPIDCol.identifier(),
IDPResourceOwnerCol.identifier(),
IDPCreationDateCol.identifier(),
IDPChangeDateCol.identifier(),
IDPSequenceCol.identifier(),
IDPStateCol.identifier(),
IDPNameCol.identifier(),
IDPStylingTypeCol.identifier(),
IDPOwnerCol.identifier(),
IDPAutoRegisterCol.identifier(),
OIDCIDPColIDPID.identifier(),
OIDCIDPColClientID.identifier(),
OIDCIDPColClientSecret.identifier(),
OIDCIDPColIssuer.identifier(),
OIDCIDPColScopes.identifier(),
OIDCIDPColDisplayNameMapping.identifier(),
OIDCIDPColUsernameMapping.identifier(),
OIDCIDPColAuthorizationEndpoint.identifier(),
OIDCIDPColTokenEndpoint.identifier(),
JWTIDPColIDPID.identifier(),
JWTIDPColIssuer.identifier(),
JWTIDPColKeysEndpoint.identifier(),
JWTIDPColHeaderName.identifier(),
JWTIDPColEndpoint.identifier(),
countColumn.identifier(),
).From(idpTable.identifier()).
LeftJoin(join(OIDCIDPColIDPID, IDPIDCol)).
LeftJoin(join(JWTIDPColIDPID, IDPIDCol)).
PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*IDPs, error) {
idps := make([]*IDP, 0)
var count uint64
for rows.Next() {
idp := new(IDP)
oidcIDPID := sql.NullString{}
oidcClientID := sql.NullString{}
oidcClientSecret := new(crypto.CryptoValue)
oidcIssuer := sql.NullString{}
oidcScopes := pq.StringArray{}
oidcDisplayNameMapping := sql.NullInt32{}
oidcUsernameMapping := sql.NullInt32{}
oidcAuthorizationEndpoint := sql.NullString{}
oidcTokenEndpoint := sql.NullString{}
jwtIDPID := sql.NullString{}
jwtIssuer := sql.NullString{}
jwtKeysEndpoint := sql.NullString{}
jwtHeaderName := sql.NullString{}
jwtEndpoint := sql.NullString{}
err := rows.Scan(
&idp.ID,
&idp.ResourceOwner,
&idp.CreationDate,
&idp.ChangeDate,
&idp.Sequence,
&idp.State,
&idp.Name,
&idp.StylingType,
&idp.OwnerType,
&idp.AutoRegister,
&oidcIDPID,
&oidcClientID,
oidcClientSecret,
&oidcIssuer,
&oidcScopes,
&oidcDisplayNameMapping,
&oidcUsernameMapping,
&oidcAuthorizationEndpoint,
&oidcTokenEndpoint,
&jwtIDPID,
&jwtIssuer,
&jwtKeysEndpoint,
&jwtHeaderName,
&jwtEndpoint,
&count,
)
if err != nil {
return nil, err
}
if oidcIDPID.Valid {
idp.OIDCIDP = &OIDCIDP{
IDPID: oidcIDPID.String,
ClientID: oidcClientID.String,
ClientSecret: oidcClientSecret,
Issuer: oidcIssuer.String,
Scopes: oidcScopes,
DisplayNameMapping: domain.OIDCMappingField(oidcDisplayNameMapping.Int32),
UsernameMapping: domain.OIDCMappingField(oidcUsernameMapping.Int32),
AuthorizationEndpoint: oidcAuthorizationEndpoint.String,
TokenEndpoint: oidcTokenEndpoint.String,
}
} else if jwtIDPID.Valid {
idp.JWTIDP = &JWTIDP{
IDPID: jwtIDPID.String,
Issuer: jwtIssuer.String,
KeysEndpoint: jwtKeysEndpoint.String,
HeaderName: jwtHeaderName.String,
Endpoint: jwtEndpoint.String,
}
}
idps = append(idps, idp)
}
if err := rows.Close(); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-iiBgK", "Errors.Query.CloseRows")
}
return &IDPs{
IDPs: idps,
SearchResponse: SearchResponse{
Count: count,
},
}, nil
}
}

View File

@ -0,0 +1,484 @@
package projection
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/handler"
"github.com/caos/zitadel/internal/eventstore/handler/crdb"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/idpconfig"
"github.com/caos/zitadel/internal/repository/org"
"github.com/lib/pq"
)
type IDPProjection struct {
crdb.StatementHandler
}
const (
IDPTable = "zitadel.projections.idps"
IDPOIDCTable = IDPTable + "_" + IDPOIDCSuffix
IDPJWTTable = IDPTable + "_" + IDPJWTSuffix
)
func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *IDPProjection {
p := &IDPProjection{}
config.ProjectionName = IDPTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p
}
func (p *IDPProjection) reducers() []handler.AggregateReducer {
return []handler.AggregateReducer{
{
Aggregate: iam.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: iam.IDPConfigAddedEventType,
Reduce: p.reduceIDPAdded,
},
{
Event: iam.IDPConfigChangedEventType,
Reduce: p.reduceIDPChanged,
},
{
Event: iam.IDPConfigDeactivatedEventType,
Reduce: p.reduceIDPDeactivated,
},
{
Event: iam.IDPConfigReactivatedEventType,
Reduce: p.reduceIDPReactivated,
},
{
Event: iam.IDPConfigRemovedEventType,
Reduce: p.reduceIDPRemoved,
},
{
Event: iam.IDPOIDCConfigAddedEventType,
Reduce: p.reduceOIDCConfigAdded,
},
{
Event: iam.IDPOIDCConfigChangedEventType,
Reduce: p.reduceOIDCConfigChanged,
},
{
Event: iam.IDPJWTConfigAddedEventType,
Reduce: p.reduceJWTConfigAdded,
},
{
Event: iam.IDPJWTConfigChangedEventType,
Reduce: p.reduceJWTConfigChanged,
},
},
},
{
Aggregate: org.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: org.IDPConfigAddedEventType,
Reduce: p.reduceIDPAdded,
},
{
Event: org.IDPConfigChangedEventType,
Reduce: p.reduceIDPChanged,
},
{
Event: org.IDPConfigDeactivatedEventType,
Reduce: p.reduceIDPDeactivated,
},
{
Event: org.IDPConfigReactivatedEventType,
Reduce: p.reduceIDPReactivated,
},
{
Event: org.IDPConfigRemovedEventType,
Reduce: p.reduceIDPRemoved,
},
{
Event: org.IDPOIDCConfigAddedEventType,
Reduce: p.reduceOIDCConfigAdded,
},
{
Event: org.IDPOIDCConfigChangedEventType,
Reduce: p.reduceOIDCConfigChanged,
},
{
Event: org.IDPJWTConfigAddedEventType,
Reduce: p.reduceJWTConfigAdded,
},
{
Event: org.IDPJWTConfigChangedEventType,
Reduce: p.reduceJWTConfigChanged,
},
},
},
}
}
const (
IDPOIDCSuffix = "oidc_config"
IDPJWTSuffix = "jwt_config"
IDPIDCol = "id"
IDPCreationDateCol = "creation_date"
IDPChangeDateCol = "change_date"
IDPSequenceCol = "sequence"
IDPResourceOwnerCol = "resource_owner"
IDPStateCol = "state"
IDPNameCol = "name"
IDPStylingTypeCol = "styling_type"
IDPOwnerTypeCol = "owner_type"
IDPAutoRegisterCol = "auto_register"
OIDCConfigIDPIDCol = "idp_id"
OIDCConfigClientIDCol = "client_id"
OIDCConfigClientSecretCol = "client_secret"
OIDCConfigIssuerCol = "issuer"
OIDCConfigScopesCol = "scopes"
OIDCConfigDisplayNameMappingCol = "display_name_mapping"
OIDCConfigUsernameMappingCol = "username_mapping"
OIDCConfigAuthorizationEndpointCol = "authorization_endpoint"
OIDCConfigTokenEndpointCol = "token_endpoint"
JWTConfigIDPIDCol = "idp_id"
JWTConfigIssuerCol = "issuer"
JWTConfigKeysEndpointCol = "keys_endpoint"
JWTConfigHeaderNameCol = "header_name"
JWTConfigEndpointCol = "endpoint"
)
func (p *IDPProjection) reduceIDPAdded(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.IDPConfigAddedEvent
var idpOwnerType domain.IdentityProviderType
switch e := event.(type) {
case *org.IDPConfigAddedEvent:
idpEvent = e.IDPConfigAddedEvent
idpOwnerType = domain.IdentityProviderTypeOrg
case *iam.IDPConfigAddedEvent:
idpEvent = e.IDPConfigAddedEvent
idpOwnerType = domain.IdentityProviderTypeSystem
default:
logging.LogWithFields("HANDL-hBriG", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPConfigAddedEventType, iam.IDPConfigAddedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-fcUdQ", "reduce.wrong.event.type")
}
return crdb.NewCreateStatement(
&idpEvent,
[]handler.Column{
handler.NewCol(IDPIDCol, idpEvent.ConfigID),
handler.NewCol(IDPCreationDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPSequenceCol, idpEvent.Sequence()),
handler.NewCol(IDPResourceOwnerCol, idpEvent.Aggregate().ResourceOwner),
handler.NewCol(IDPStateCol, domain.IDPConfigStateActive),
handler.NewCol(IDPNameCol, idpEvent.Name),
handler.NewCol(IDPStylingTypeCol, idpEvent.StylingType),
handler.NewCol(IDPAutoRegisterCol, idpEvent.AutoRegister),
handler.NewCol(IDPOwnerTypeCol, idpOwnerType),
},
), nil
}
func (p *IDPProjection) reduceIDPChanged(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.IDPConfigChangedEvent
switch e := event.(type) {
case *org.IDPConfigChangedEvent:
idpEvent = e.IDPConfigChangedEvent
case *iam.IDPConfigChangedEvent:
idpEvent = e.IDPConfigChangedEvent
default:
logging.LogWithFields("HANDL-FFrph", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPConfigChangedEventType, iam.IDPConfigChangedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-NVvJD", "reduce.wrong.event.type")
}
cols := make([]handler.Column, 0, 5)
if idpEvent.Name != nil {
cols = append(cols, handler.NewCol(IDPNameCol, *idpEvent.Name))
}
if idpEvent.StylingType != nil {
cols = append(cols, handler.NewCol(IDPStylingTypeCol, *idpEvent.StylingType))
}
if idpEvent.AutoRegister != nil {
cols = append(cols, handler.NewCol(IDPAutoRegisterCol, *idpEvent.AutoRegister))
}
if len(cols) == 0 {
return crdb.NewNoOpStatement(&idpEvent), nil
}
cols = append(cols,
handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPSequenceCol, idpEvent.Sequence()),
)
return crdb.NewUpdateStatement(
&idpEvent,
cols,
[]handler.Condition{
handler.NewCond(IDPIDCol, idpEvent.ConfigID),
},
), nil
}
func (p *IDPProjection) reduceIDPDeactivated(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.IDPConfigDeactivatedEvent
switch e := event.(type) {
case *org.IDPConfigDeactivatedEvent:
idpEvent = e.IDPConfigDeactivatedEvent
case *iam.IDPConfigDeactivatedEvent:
idpEvent = e.IDPConfigDeactivatedEvent
default:
logging.LogWithFields("HANDL-1s33a", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPConfigDeactivatedEventType, iam.IDPConfigDeactivatedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-94O5l", "reduce.wrong.event.type")
}
return crdb.NewUpdateStatement(
&idpEvent,
[]handler.Column{
handler.NewCol(IDPStateCol, domain.IDPConfigStateInactive),
handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPSequenceCol, idpEvent.Sequence()),
},
[]handler.Condition{
handler.NewCond(IDPIDCol, idpEvent.ConfigID),
},
), nil
}
func (p *IDPProjection) reduceIDPReactivated(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.IDPConfigReactivatedEvent
switch e := event.(type) {
case *org.IDPConfigReactivatedEvent:
idpEvent = e.IDPConfigReactivatedEvent
case *iam.IDPConfigReactivatedEvent:
idpEvent = e.IDPConfigReactivatedEvent
default:
logging.LogWithFields("HANDL-Zgzpt", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPConfigReactivatedEventType, iam.IDPConfigReactivatedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-I8QyS", "reduce.wrong.event.type")
}
return crdb.NewUpdateStatement(
&idpEvent,
[]handler.Column{
handler.NewCol(IDPStateCol, domain.IDPConfigStateActive),
handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPSequenceCol, idpEvent.Sequence()),
},
[]handler.Condition{
handler.NewCond(IDPIDCol, idpEvent.ConfigID),
},
), nil
}
func (p *IDPProjection) reduceIDPRemoved(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.IDPConfigRemovedEvent
switch e := event.(type) {
case *org.IDPConfigRemovedEvent:
idpEvent = e.IDPConfigRemovedEvent
case *iam.IDPConfigRemovedEvent:
idpEvent = e.IDPConfigRemovedEvent
default:
logging.LogWithFields("HANDL-JJasT", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPConfigRemovedEventType, iam.IDPConfigRemovedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-B4zy8", "reduce.wrong.event.type")
}
return crdb.NewDeleteStatement(
&idpEvent,
[]handler.Condition{
handler.NewCond(IDPIDCol, idpEvent.ConfigID),
},
), nil
}
func (p *IDPProjection) reduceOIDCConfigAdded(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.OIDCConfigAddedEvent
switch e := event.(type) {
case *org.IDPOIDCConfigAddedEvent:
idpEvent = e.OIDCConfigAddedEvent
case *iam.IDPOIDCConfigAddedEvent:
idpEvent = e.OIDCConfigAddedEvent
default:
logging.LogWithFields("HANDL-DCmeB", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPOIDCConfigAddedEventType, iam.IDPOIDCConfigAddedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-2FuAA", "reduce.wrong.event.type")
}
return crdb.NewMultiStatement(&idpEvent,
crdb.AddUpdateStatement(
[]handler.Column{
handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPSequenceCol, idpEvent.Sequence()),
},
[]handler.Condition{
handler.NewCond(IDPIDCol, idpEvent.IDPConfigID),
},
),
crdb.AddCreateStatement(
[]handler.Column{
handler.NewCol(OIDCConfigIDPIDCol, idpEvent.IDPConfigID),
handler.NewCol(OIDCConfigClientIDCol, idpEvent.ClientID),
handler.NewCol(OIDCConfigClientSecretCol, idpEvent.ClientSecret),
handler.NewCol(OIDCConfigIssuerCol, idpEvent.Issuer),
handler.NewCol(OIDCConfigScopesCol, pq.StringArray(idpEvent.Scopes)),
handler.NewCol(OIDCConfigDisplayNameMappingCol, idpEvent.IDPDisplayNameMapping),
handler.NewCol(OIDCConfigUsernameMappingCol, idpEvent.UserNameMapping),
handler.NewCol(OIDCConfigAuthorizationEndpointCol, idpEvent.AuthorizationEndpoint),
handler.NewCol(OIDCConfigTokenEndpointCol, idpEvent.TokenEndpoint),
},
crdb.WithTableSuffix(IDPOIDCSuffix),
),
), nil
}
func (p *IDPProjection) reduceOIDCConfigChanged(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.OIDCConfigChangedEvent
switch e := event.(type) {
case *org.IDPOIDCConfigChangedEvent:
idpEvent = e.OIDCConfigChangedEvent
case *iam.IDPOIDCConfigChangedEvent:
idpEvent = e.OIDCConfigChangedEvent
default:
logging.LogWithFields("HANDL-VyBm2", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPOIDCConfigChangedEventType, iam.IDPOIDCConfigChangedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-x2IVI", "reduce.wrong.event.type")
}
cols := make([]handler.Column, 0, 8)
if idpEvent.ClientID != nil {
cols = append(cols, handler.NewCol(OIDCConfigClientIDCol, *idpEvent.ClientID))
}
if idpEvent.ClientSecret != nil {
cols = append(cols, handler.NewCol(OIDCConfigClientSecretCol, *idpEvent.ClientSecret))
}
if idpEvent.Issuer != nil {
cols = append(cols, handler.NewCol(OIDCConfigIssuerCol, *idpEvent.Issuer))
}
if idpEvent.AuthorizationEndpoint != nil {
cols = append(cols, handler.NewCol(OIDCConfigAuthorizationEndpointCol, *idpEvent.AuthorizationEndpoint))
}
if idpEvent.TokenEndpoint != nil {
cols = append(cols, handler.NewCol(OIDCConfigTokenEndpointCol, *idpEvent.TokenEndpoint))
}
if idpEvent.Scopes != nil {
cols = append(cols, handler.NewCol(OIDCConfigScopesCol, pq.StringArray(idpEvent.Scopes)))
}
if idpEvent.IDPDisplayNameMapping != nil {
cols = append(cols, handler.NewCol(OIDCConfigDisplayNameMappingCol, *idpEvent.IDPDisplayNameMapping))
}
if idpEvent.UserNameMapping != nil {
cols = append(cols, handler.NewCol(OIDCConfigUsernameMappingCol, *idpEvent.UserNameMapping))
}
if len(cols) == 0 {
return crdb.NewNoOpStatement(&idpEvent), nil
}
return crdb.NewMultiStatement(&idpEvent,
crdb.AddUpdateStatement(
[]handler.Column{
handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPSequenceCol, idpEvent.Sequence()),
},
[]handler.Condition{
handler.NewCond(IDPIDCol, idpEvent.IDPConfigID),
},
),
crdb.AddUpdateStatement(
cols,
[]handler.Condition{
handler.NewCond(OIDCConfigIDPIDCol, idpEvent.IDPConfigID),
},
crdb.WithTableSuffix(IDPOIDCSuffix),
),
), nil
}
func (p *IDPProjection) reduceJWTConfigAdded(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.JWTConfigAddedEvent
switch e := event.(type) {
case *org.IDPJWTConfigAddedEvent:
idpEvent = e.JWTConfigAddedEvent
case *iam.IDPJWTConfigAddedEvent:
idpEvent = e.JWTConfigAddedEvent
default:
logging.LogWithFields("HANDL-228q7", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPJWTConfigAddedEventType, iam.IDPJWTConfigAddedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-qvPdb", "reduce.wrong.event.type")
}
return crdb.NewMultiStatement(&idpEvent,
crdb.AddUpdateStatement(
[]handler.Column{
handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPSequenceCol, idpEvent.Sequence()),
},
[]handler.Condition{
handler.NewCond(IDPIDCol, idpEvent.IDPConfigID),
},
),
crdb.AddCreateStatement(
[]handler.Column{
handler.NewCol(OIDCConfigIDPIDCol, idpEvent.IDPConfigID),
handler.NewCol(JWTConfigEndpointCol, idpEvent.JWTEndpoint),
handler.NewCol(JWTConfigIssuerCol, idpEvent.Issuer),
handler.NewCol(JWTConfigKeysEndpointCol, idpEvent.KeysEndpoint),
handler.NewCol(JWTConfigHeaderNameCol, idpEvent.HeaderName),
},
crdb.WithTableSuffix(IDPJWTSuffix),
),
), nil
}
func (p *IDPProjection) reduceJWTConfigChanged(event eventstore.EventReader) (*handler.Statement, error) {
var idpEvent idpconfig.JWTConfigChangedEvent
switch e := event.(type) {
case *org.IDPJWTConfigChangedEvent:
idpEvent = e.JWTConfigChangedEvent
case *iam.IDPJWTConfigChangedEvent:
idpEvent = e.JWTConfigChangedEvent
default:
logging.LogWithFields("HANDL-VyBm2", "seq", e.Sequence(), "expectedTypes", []eventstore.EventType{org.IDPJWTConfigChangedEventType, iam.IDPJWTConfigChangedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-x2IVI", "reduce.wrong.event.type")
}
cols := make([]handler.Column, 0, 4)
if idpEvent.JWTEndpoint != nil {
cols = append(cols, handler.NewCol(JWTConfigEndpointCol, *idpEvent.JWTEndpoint))
}
if idpEvent.Issuer != nil {
cols = append(cols, handler.NewCol(JWTConfigIssuerCol, *idpEvent.Issuer))
}
if idpEvent.KeysEndpoint != nil {
cols = append(cols, handler.NewCol(JWTConfigKeysEndpointCol, *idpEvent.KeysEndpoint))
}
if idpEvent.HeaderName != nil {
cols = append(cols, handler.NewCol(JWTConfigHeaderNameCol, *idpEvent.HeaderName))
}
if len(cols) == 0 {
return crdb.NewNoOpStatement(&idpEvent), nil
}
return crdb.NewMultiStatement(&idpEvent,
crdb.AddUpdateStatement(
[]handler.Column{
handler.NewCol(IDPChangeDateCol, idpEvent.CreationDate()),
handler.NewCol(IDPSequenceCol, idpEvent.Sequence()),
},
[]handler.Condition{
handler.NewCond(IDPIDCol, idpEvent.IDPConfigID),
},
),
crdb.AddUpdateStatement(
cols,
[]handler.Condition{
handler.NewCond(OIDCConfigIDPIDCol, idpEvent.IDPConfigID),
},
crdb.WithTableSuffix(IDPJWTSuffix),
),
), nil
}

View File

@ -0,0 +1,872 @@
package projection
import (
"testing"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/handler"
"github.com/caos/zitadel/internal/eventstore/repository"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/org"
"github.com/lib/pq"
)
func TestIDPProjection_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: "iam.reduceIDPAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPConfigAddedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"name": "custom-zitadel-instance",
"idpType": 0,
"stylingType": 0,
"autoRegister": true
}`),
), iam.IDPConfigAddedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO zitadel.projections.idps (id, creation_date, change_date, sequence, resource_owner, state, name, styling_type, auto_register, owner_type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
expectedArgs: []interface{}{
"idp-config-id",
anyArg{},
anyArg{},
uint64(15),
"ro-id",
domain.IDPConfigStateActive,
"custom-zitadel-instance",
domain.IDPConfigStylingTypeUnspecified,
true,
domain.IdentityProviderTypeSystem,
},
},
},
},
},
},
{
name: "iam.reduceIDPChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPConfigChangedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"name": "custom-zitadel-instance",
"stylingType": 1,
"autoRegister": true
}`),
), iam.IDPConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (name, styling_type, auto_register, change_date, sequence) = ($1, $2, $3, $4, $5) WHERE (id = $6)",
expectedArgs: []interface{}{
"custom-zitadel-instance",
domain.IDPConfigStylingTypeGoogle,
true,
anyArg{},
uint64(15),
"idp-config-id",
},
},
},
},
},
},
{
name: "iam.reduceIDPDeactivated",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPConfigDeactivatedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id"
}`),
), iam.IDPConfigDeactivatedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPDeactivated,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
domain.IDPConfigStateInactive,
anyArg{},
uint64(15),
"idp-config-id",
},
},
},
},
},
},
{
name: "iam.reduceIDPReactivated",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPConfigReactivatedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id"
}`),
), iam.IDPConfigReactivatedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPReactivated,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
domain.IDPConfigStateActive,
anyArg{},
uint64(15),
"idp-config-id",
},
},
},
},
},
},
{
name: "iam.reduceIDPRemoved",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPConfigRemovedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id"
}`),
), iam.IDPConfigRemovedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPRemoved,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM zitadel.projections.idps WHERE (id = $1)",
expectedArgs: []interface{}{
"idp-config-id",
},
},
},
},
},
},
{
name: "iam.reduceOIDCConfigAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPOIDCConfigAddedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"clientId": "client-id",
"clientSecret": {
"cryptoType": 0,
"algorithm": "RSA-265",
"keyId": "key-id"
},
"issuer": "issuer",
"authorizationEndpoint": "https://api.zitadel.ch/authorize",
"tokenEndpoint": "https://api.zitadel.ch/token",
"scopes": ["profile"],
"idpDisplayNameMapping": 0,
"usernameMapping": 1
}`),
), iam.IDPOIDCConfigAddedEventMapper),
},
reduce: (&IDPProjection{}).reduceOIDCConfigAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"idp-config-id",
},
},
{
expectedStmt: "INSERT INTO zitadel.projections.idps_oidc_config (idp_id, client_id, client_secret, issuer, scopes, display_name_mapping, username_mapping, authorization_endpoint, token_endpoint) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
"idp-config-id",
"client-id",
anyArg{},
"issuer",
pq.StringArray{"profile"},
domain.OIDCMappingFieldUnspecified,
domain.OIDCMappingFieldPreferredLoginName,
"https://api.zitadel.ch/authorize",
"https://api.zitadel.ch/token",
},
},
},
},
},
},
{
name: "iam.reduceOIDCConfigChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPOIDCConfigChangedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"clientId": "client-id",
"clientSecret": {
"cryptoType": 0,
"algorithm": "RSA-265",
"keyId": "key-id"
},
"issuer": "issuer",
"authorizationEndpoint": "https://api.zitadel.ch/authorize",
"tokenEndpoint": "https://api.zitadel.ch/token",
"scopes": ["profile"],
"idpDisplayNameMapping": 0,
"usernameMapping": 1
}`),
), iam.IDPOIDCConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceOIDCConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"idp-config-id",
},
},
{
expectedStmt: "UPDATE zitadel.projections.idps_oidc_config SET (client_id, client_secret, issuer, authorization_endpoint, token_endpoint, scopes, display_name_mapping, username_mapping) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (idp_id = $9)",
expectedArgs: []interface{}{
"client-id",
anyArg{},
"issuer",
"https://api.zitadel.ch/authorize",
"https://api.zitadel.ch/token",
pq.StringArray{"profile"},
domain.OIDCMappingFieldUnspecified,
domain.OIDCMappingFieldPreferredLoginName,
"idp-config-id",
},
},
},
},
},
},
{
name: "iam.reduceOIDCConfigChanged: no op",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPOIDCConfigChangedEventType),
iam.AggregateType,
[]byte("{}"),
), iam.IDPOIDCConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceOIDCConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{},
},
},
},
{
name: "iam.reduceJWTConfigAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPJWTConfigAddedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"jwtEndpoint": "https://api.zitadel.ch/jwt",
"issuer": "issuer",
"keysEndpoint": "https://api.zitadel.ch/keys",
"headerName": "hodor"
}`),
), iam.IDPJWTConfigAddedEventMapper),
},
reduce: (&IDPProjection{}).reduceJWTConfigAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"idp-config-id",
},
},
{
expectedStmt: "INSERT INTO zitadel.projections.idps_jwt_config (idp_id, endpoint, issuer, keys_endpoint, header_name) VALUES ($1, $2, $3, $4, $5)",
expectedArgs: []interface{}{
"idp-config-id",
"https://api.zitadel.ch/jwt",
"issuer",
"https://api.zitadel.ch/keys",
"hodor",
},
},
},
},
},
},
{
name: "iam.reduceJWTConfigChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPJWTConfigChangedEventType),
iam.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"jwtEndpoint": "https://api.zitadel.ch/jwt",
"issuer": "issuer",
"keysEndpoint": "https://api.zitadel.ch/keys",
"headerName": "hodor"
}`),
), iam.IDPJWTConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceJWTConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"idp-config-id",
},
},
{
expectedStmt: "UPDATE zitadel.projections.idps_jwt_config SET (endpoint, issuer, keys_endpoint, header_name) = ($1, $2, $3, $4) WHERE (idp_id = $5)",
expectedArgs: []interface{}{
"https://api.zitadel.ch/jwt",
"issuer",
"https://api.zitadel.ch/keys",
"hodor",
"idp-config-id",
},
},
},
},
},
},
{
name: "iam.reduceJWTConfigChanged: no op",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.IDPJWTConfigChangedEventType),
iam.AggregateType,
[]byte(`{}`),
), iam.IDPJWTConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceJWTConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{},
},
},
},
{
name: "org.reduceIDPAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPConfigAddedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"name": "custom-zitadel-instance",
"idpType": 0,
"stylingType": 0,
"autoRegister": true
}`),
), org.IDPConfigAddedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO zitadel.projections.idps (id, creation_date, change_date, sequence, resource_owner, state, name, styling_type, auto_register, owner_type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
expectedArgs: []interface{}{
"idp-config-id",
anyArg{},
anyArg{},
uint64(15),
"ro-id",
domain.IDPConfigStateActive,
"custom-zitadel-instance",
domain.IDPConfigStylingTypeUnspecified,
true,
domain.IdentityProviderTypeOrg,
},
},
},
},
},
},
{
name: "org.reduceIDPChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPConfigChangedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"name": "custom-zitadel-instance",
"stylingType": 1,
"autoRegister": true
}`),
), org.IDPConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (name, styling_type, auto_register, change_date, sequence) = ($1, $2, $3, $4, $5) WHERE (id = $6)",
expectedArgs: []interface{}{
"custom-zitadel-instance",
domain.IDPConfigStylingTypeGoogle,
true,
anyArg{},
uint64(15),
"idp-config-id",
},
},
},
},
},
},
{
name: "org.reduceIDPDeactivated",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPConfigDeactivatedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id"
}`),
), org.IDPConfigDeactivatedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPDeactivated,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
domain.IDPConfigStateInactive,
anyArg{},
uint64(15),
"idp-config-id",
},
},
},
},
},
},
{
name: "org.reduceIDPReactivated",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPConfigReactivatedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id"
}`),
), org.IDPConfigReactivatedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPReactivated,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)",
expectedArgs: []interface{}{
domain.IDPConfigStateActive,
anyArg{},
uint64(15),
"idp-config-id",
},
},
},
},
},
},
{
name: "org.reduceIDPRemoved",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPConfigRemovedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id"
}`),
), org.IDPConfigRemovedEventMapper),
},
reduce: (&IDPProjection{}).reduceIDPRemoved,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM zitadel.projections.idps WHERE (id = $1)",
expectedArgs: []interface{}{
"idp-config-id",
},
},
},
},
},
},
{
name: "org.reduceOIDCConfigAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPOIDCConfigAddedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"clientId": "client-id",
"clientSecret": {
"cryptoType": 0,
"algorithm": "RSA-265",
"keyId": "key-id"
},
"issuer": "issuer",
"authorizationEndpoint": "https://api.zitadel.ch/authorize",
"tokenEndpoint": "https://api.zitadel.ch/token",
"scopes": ["profile"],
"idpDisplayNameMapping": 0,
"usernameMapping": 1
}`),
), org.IDPOIDCConfigAddedEventMapper),
},
reduce: (&IDPProjection{}).reduceOIDCConfigAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"idp-config-id",
},
},
{
expectedStmt: "INSERT INTO zitadel.projections.idps_oidc_config (idp_id, client_id, client_secret, issuer, scopes, display_name_mapping, username_mapping, authorization_endpoint, token_endpoint) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
"idp-config-id",
"client-id",
anyArg{},
"issuer",
pq.StringArray{"profile"},
domain.OIDCMappingFieldUnspecified,
domain.OIDCMappingFieldPreferredLoginName,
"https://api.zitadel.ch/authorize",
"https://api.zitadel.ch/token",
},
},
},
},
},
},
{
name: "org.reduceOIDCConfigChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPOIDCConfigChangedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"clientId": "client-id",
"clientSecret": {
"cryptoType": 0,
"algorithm": "RSA-265",
"keyId": "key-id"
},
"issuer": "issuer",
"authorizationEndpoint": "https://api.zitadel.ch/authorize",
"tokenEndpoint": "https://api.zitadel.ch/token",
"scopes": ["profile"],
"idpDisplayNameMapping": 0,
"usernameMapping": 1
}`),
), org.IDPOIDCConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceOIDCConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"idp-config-id",
},
},
{
expectedStmt: "UPDATE zitadel.projections.idps_oidc_config SET (client_id, client_secret, issuer, authorization_endpoint, token_endpoint, scopes, display_name_mapping, username_mapping) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (idp_id = $9)",
expectedArgs: []interface{}{
"client-id",
anyArg{},
"issuer",
"https://api.zitadel.ch/authorize",
"https://api.zitadel.ch/token",
pq.StringArray{"profile"},
domain.OIDCMappingFieldUnspecified,
domain.OIDCMappingFieldPreferredLoginName,
"idp-config-id",
},
},
},
},
},
},
{
name: "org.reduceOIDCConfigChanged: no op",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPOIDCConfigChangedEventType),
org.AggregateType,
[]byte("{}"),
), org.IDPOIDCConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceOIDCConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{},
},
},
},
{
name: "org.reduceJWTConfigAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPJWTConfigAddedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"jwtEndpoint": "https://api.zitadel.ch/jwt",
"issuer": "issuer",
"keysEndpoint": "https://api.zitadel.ch/keys",
"headerName": "hodor"
}`),
), org.IDPJWTConfigAddedEventMapper),
},
reduce: (&IDPProjection{}).reduceJWTConfigAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"idp-config-id",
},
},
{
expectedStmt: "INSERT INTO zitadel.projections.idps_jwt_config (idp_id, endpoint, issuer, keys_endpoint, header_name) VALUES ($1, $2, $3, $4, $5)",
expectedArgs: []interface{}{
"idp-config-id",
"https://api.zitadel.ch/jwt",
"issuer",
"https://api.zitadel.ch/keys",
"hodor",
},
},
},
},
},
},
{
name: "org.reduceJWTConfigChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPJWTConfigChangedEventType),
org.AggregateType,
[]byte(`{
"idpConfigId": "idp-config-id",
"jwtEndpoint": "https://api.zitadel.ch/jwt",
"issuer": "issuer",
"keysEndpoint": "https://api.zitadel.ch/keys",
"headerName": "hodor"
}`),
), org.IDPJWTConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceJWTConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"idp-config-id",
},
},
{
expectedStmt: "UPDATE zitadel.projections.idps_jwt_config SET (endpoint, issuer, keys_endpoint, header_name) = ($1, $2, $3, $4) WHERE (idp_id = $5)",
expectedArgs: []interface{}{
"https://api.zitadel.ch/jwt",
"issuer",
"https://api.zitadel.ch/keys",
"hodor",
"idp-config-id",
},
},
},
},
},
},
{
name: "org.reduceJWTConfigChanged: no op",
args: args{
event: getEvent(testEvent(
repository.EventType(org.IDPJWTConfigChangedEventType),
org.AggregateType,
[]byte(`{}`),
), org.IDPJWTConfigChangedEventMapper),
},
reduce: (&IDPProjection{}).reduceJWTConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: IDPTable,
executer: &testExecuter{
executions: []execution{},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := baseEvent(t)
got, err := tt.reduce(event)
if _, ok := err.(errors.InvalidArgument); !ok {
t.Errorf("no wrong event mapping: %v, got: %v", err, got)
}
event = tt.args.event(t)
got, err = tt.reduce(event)
assertReduce(t, got, err, tt.want)
})
}
}

View File

@ -42,6 +42,7 @@ func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, co
// owner.NewOrgOwnerProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["org_owners"]))
NewOrgDomainProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["org_domains"]))
NewLoginPolicyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["login_policies"]))
NewIDPProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["idps"]))
return nil
}

View File

@ -270,6 +270,27 @@ func ListComparisonFromMethod(m domain.SearchMethod) ListComparison {
}
}
type BoolQuery struct {
Column Column
Value bool
}
func NewBoolQuery(c Column, value bool) (*BoolQuery, error) {
return &BoolQuery{
Column: c,
Value: value,
}, nil
}
func (q *BoolQuery) ToQuery(query sq.SelectBuilder) sq.SelectBuilder {
where, args := q.comp()
return query.Where(where, args...)
}
func (s *BoolQuery) comp() (comparison interface{}, args []interface{}) {
return sq.Eq{s.Column.identifier(): s.Value}, nil
}
var (
//countColumn represents the default counter for search responses
countColumn = Column{

View File

@ -0,0 +1,41 @@
CREATE TABLE zitadel.projections.idps (
id TEXT,
creation_date TIMESTAMPTZ,
change_date TIMESTAMPTZ,
sequence BIGINT,
resource_owner TEXT,
state SMALLINT,
name TEXT,
styling_type SMALLINT,
owner_type SMALLINT,
auto_register BOOLEAN,
PRIMARY KEY (id)
);
CREATE TABLE zitadel.projections.idps_oidc_config (
idp_id TEXT REFERENCES zitadel.projections.idps (id) ON DELETE CASCADE,
client_id TEXT,
client_secret JSONB,
issuer TEXT,
scopes STRING[],
display_name_mapping SMALLINT,
username_mapping SMALLINT,
authorization_endpoint TEXT,
token_endpoint TEXT,
PRIMARY KEY (idp_id)
);
CREATE TABLE zitadel.projections.idps_jwt_config (
idp_id TEXT REFERENCES zitadel.projections.idps (id) ON DELETE CASCADE,
issuer TEXT,
keys_endpoint TEXT,
header_name TEXT,
endpoint TEXT,
PRIMARY KEY (idp_id)
);