From 80003939ad9208e7ed2624d3df77a67317144d73 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 27 Feb 2023 16:32:18 +0100 Subject: [PATCH] feat(api): add oidc and jwt provider template (#5290) Adds possibility to manage OIDC and JWT template based providers --- go.mod | 29 +- go.sum | 56 +- internal/api/grpc/admin/idp.go | 42 + internal/api/grpc/admin/idp_converter.go | 44 ++ internal/api/grpc/idp/converter.go | 47 +- internal/api/grpc/management/idp.go | 42 + internal/api/grpc/management/idp_converter.go | 44 ++ internal/command/idp.go | 18 + internal/command/idp_model.go | 310 +++++++- internal/command/instance_idp.go | 281 ++++++- internal/command/instance_idp_model.go | 242 +++++- internal/command/org_idp.go | 277 ++++++- internal/command/org_idp_model.go | 242 +++++- internal/query/idp_template.go | 192 +++++ internal/query/idp_template_test.go | 722 +++++++++++++++--- internal/query/projection/idp_template.go | 592 +++++++++++++- .../query/projection/idp_template_test.go | 574 +++++++++++++- internal/repository/idp/google.go | 4 +- internal/repository/idp/idp.go | 10 +- internal/repository/idp/jwt.go | 153 ++++ internal/repository/idp/oauth.go | 8 +- internal/repository/idp/oidc.go | 154 ++++ internal/repository/instance/eventstore.go | 4 + internal/repository/instance/idp.go | 162 +++- internal/repository/org/eventstore.go | 4 + internal/repository/org/idp.go | 162 +++- proto/zitadel/admin.proto | 105 +++ proto/zitadel/idp.proto | 8 + proto/zitadel/management.proto | 105 +++ 29 files changed, 4338 insertions(+), 295 deletions(-) create mode 100644 internal/repository/idp/jwt.go create mode 100644 internal/repository/idp/oidc.go diff --git a/go.mod b/go.mod index 6081f5942..2dc5f6cee 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/dop251/goja_nodejs v0.0.0-20220905124449-678b33ca5009 github.com/drone/envsubst v1.0.3 github.com/duo-labs/webauthn v0.0.0-20211216225436-9a12cd078b8a - github.com/envoyproxy/protoc-gen-validate v0.6.7 + github.com/envoyproxy/protoc-gen-validate v0.9.1 github.com/go-ldap/ldap/v3 v3.4.4 github.com/golang/glog v1.0.0 github.com/golang/mock v1.6.0 @@ -30,7 +30,7 @@ require ( github.com/gorilla/securecookie v1.1.1 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.1 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.1 github.com/h2non/gock v1.2.0 github.com/improbable-eng/grpc-web v0.15.0 github.com/jackc/pgconn v1.12.1 @@ -71,14 +71,14 @@ require ( go.opentelemetry.io/otel/sdk/metric v0.25.0 go.opentelemetry.io/otel/trace v1.2.0 golang.org/x/crypto v0.6.0 - golang.org/x/net v0.6.0 - golang.org/x/oauth2 v0.4.0 + golang.org/x/net v0.7.0 + golang.org/x/oauth2 v0.5.0 golang.org/x/sync v0.1.0 golang.org/x/text v0.7.0 - golang.org/x/tools v0.1.13-0.20220928184430-f80e98464e27 - google.golang.org/api v0.106.0 - google.golang.org/genproto v0.0.0-20230106154932-a12b697841d9 - google.golang.org/grpc v1.51.0 + golang.org/x/tools v0.3.0 + google.golang.org/api v0.110.0 + google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 + google.golang.org/grpc v1.53.0 google.golang.org/protobuf v1.28.1 gopkg.in/square/go-jose.v2 v2.6.0 sigs.k8s.io/yaml v1.3.0 @@ -86,9 +86,9 @@ require ( require ( cloud.google.com/go v0.108.0 // indirect - cloud.google.com/go/compute v1.15.0 // indirect + cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.10.0 // indirect + cloud.google.com/go/iam v0.11.0 // indirect cloud.google.com/go/trace v1.4.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -97,7 +97,7 @@ require ( github.com/beevik/etree v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.1.2 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect @@ -129,7 +129,7 @@ require ( github.com/google/certificate-transparency-go v1.0.21 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect @@ -175,7 +175,7 @@ require ( github.com/russellhaering/goxmldsig v1.2.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/sirupsen/logrus v1.9.0 - github.com/spf13/afero v1.8.1 // indirect + github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -189,7 +189,7 @@ require ( go.opentelemetry.io/otel/internal/metric v0.25.0 // indirect go.opentelemetry.io/proto/otlp v0.10.0 // indirect golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/mod v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -200,3 +200,4 @@ require ( ) replace github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.4 + diff --git a/go.sum b/go.sum index 86213ecc9..d7639d425 100644 --- a/go.sum +++ b/go.sum @@ -38,16 +38,16 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.15.0 h1:PiKE4V948A1BRvhuwA2hOxL8imyvwuRgrOiytC+NlXo= -cloud.google.com/go/compute v1.15.0/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= -cloud.google.com/go/iam v0.10.0 h1:fpP/gByFs6US1ma53v7VxhvbJpO2Aapng6wabJ99MuI= -cloud.google.com/go/iam v0.10.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM= +cloud.google.com/go/iam v0.11.0 h1:kwCWfKwB6ePZoZnGLwrd3B6Ru/agoHANTUBWpVNIdnM= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -145,8 +145,9 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -244,8 +245,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/envoyproxy/protoc-gen-validate v0.6.7 h1:qcZcULcd/abmQg6dwigimCNEyi4gg31M/xaciQlDml8= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -415,8 +416,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -453,8 +454,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.1 h1:Y7pyy1viWfoKMUVxmjfI5X6fVLlen75kdYjeIwl9CKc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.1/go.mod h1:chrfS3YoLAlKTRE5cFWvCbt8uGAjshktT4PveTUpsFQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.1 h1:I6ITHEanAwjB0FvaxmGm8pKqmCLR7QIe05ZmO4QAXMw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.1/go.mod h1:gYC+WX4YJFarA2ie73G2epzt7TBWpo9pzcBnK1g0MSw= github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4= github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= @@ -646,7 +647,6 @@ github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -848,8 +848,8 @@ github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5Pse github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.1 h1:izYHOT71f9iZ7iq37Uqjael60/vYC6vMtzedudZ0zEk= -github.com/spf13/afero v1.8.1/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -1050,8 +1050,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1109,8 +1110,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1129,8 +1130,8 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1320,8 +1321,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.1.13-0.20220928184430-f80e98464e27 h1:mOqz7ZhDqMSA3LafrO1Q+1yLQ/KCnCy2/5xiFQVkCWQ= -golang.org/x/tools v0.1.13-0.20220928184430-f80e98464e27/go.mod h1:VsjNM1dMo+Ofkp5d7y7fOdQZD8MTXSQ4w3EPk65AvKU= +golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1363,8 +1364,8 @@ google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdr google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= -google.golang.org/api v0.106.0 h1:ffmW0faWCwKkpbbtvlY/K/8fUl+JKvNS5CVzRoyfCv8= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1445,8 +1446,8 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230106154932-a12b697841d9 h1:3wPBShTLWQnEkZ9VW/HZZ8zT/9LLtleBtq7l8SKtJIA= -google.golang.org/genproto v0.0.0-20230106154932-a12b697841d9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148 h1:muK+gVBJBfFb4SejshDBlN2/UgxCCOKH9Y34ljqEGOc= +google.golang.org/genproto v0.0.0-20230221151758-ace64dc21148/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1481,9 +1482,8 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/internal/api/grpc/admin/idp.go b/internal/api/grpc/admin/idp.go index 921378e05..24c3c0e00 100644 --- a/internal/api/grpc/admin/idp.go +++ b/internal/api/grpc/admin/idp.go @@ -195,6 +195,48 @@ func (s *Server) UpdateGenericOAuthProvider(ctx context.Context, req *admin_pb.U }, nil } +func (s *Server) AddGenericOIDCProvider(ctx context.Context, req *admin_pb.AddGenericOIDCProviderRequest) (*admin_pb.AddGenericOIDCProviderResponse, error) { + id, details, err := s.command.AddInstanceGenericOIDCProvider(ctx, addGenericOIDCProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.AddGenericOIDCProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGenericOIDCProvider(ctx context.Context, req *admin_pb.UpdateGenericOIDCProviderRequest) (*admin_pb.UpdateGenericOIDCProviderResponse, error) { + details, err := s.command.UpdateInstanceGenericOIDCProvider(ctx, req.Id, updateGenericOIDCProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.UpdateGenericOIDCProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + +func (s *Server) AddJWTProvider(ctx context.Context, req *admin_pb.AddJWTProviderRequest) (*admin_pb.AddJWTProviderResponse, error) { + id, details, err := s.command.AddInstanceJWTProvider(ctx, addJWTProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.AddJWTProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateJWTProvider(ctx context.Context, req *admin_pb.UpdateJWTProviderRequest) (*admin_pb.UpdateJWTProviderResponse, error) { + details, err := s.command.UpdateInstanceJWTProvider(ctx, req.Id, updateJWTProviderToCommand(req)) + if err != nil { + return nil, err + } + return &admin_pb.UpdateJWTProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + func (s *Server) AddGoogleProvider(ctx context.Context, req *admin_pb.AddGoogleProviderRequest) (*admin_pb.AddGoogleProviderResponse, error) { id, details, err := s.command.AddInstanceGoogleProvider(ctx, addGoogleProviderToCommand(req)) if err != nil { diff --git a/internal/api/grpc/admin/idp_converter.go b/internal/api/grpc/admin/idp_converter.go index e08065e19..c8f2f78e9 100644 --- a/internal/api/grpc/admin/idp_converter.go +++ b/internal/api/grpc/admin/idp_converter.go @@ -227,6 +227,50 @@ func updateGenericOAuthProviderToCommand(req *admin_pb.UpdateGenericOAuthProvide } } +func addGenericOIDCProviderToCommand(req *admin_pb.AddGenericOIDCProviderRequest) command.GenericOIDCProvider { + return command.GenericOIDCProvider{ + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGenericOIDCProviderToCommand(req *admin_pb.UpdateGenericOIDCProviderRequest) command.GenericOIDCProvider { + return command.GenericOIDCProvider{ + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func addJWTProviderToCommand(req *admin_pb.AddJWTProviderRequest) command.JWTProvider { + return command.JWTProvider{ + Name: req.Name, + Issuer: req.Issuer, + JWTEndpoint: req.JwtEndpoint, + KeyEndpoint: req.KeysEndpoint, + HeaderName: req.HeaderName, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateJWTProviderToCommand(req *admin_pb.UpdateJWTProviderRequest) command.JWTProvider { + return command.JWTProvider{ + Name: req.Name, + Issuer: req.Issuer, + JWTEndpoint: req.JwtEndpoint, + KeyEndpoint: req.KeysEndpoint, + HeaderName: req.HeaderName, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + func addGoogleProviderToCommand(req *admin_pb.AddGoogleProviderRequest) command.GoogleProvider { return command.GoogleProvider{ Name: req.Name, diff --git a/internal/api/grpc/idp/converter.go b/internal/api/grpc/idp/converter.go index 0526b50f7..b478f1e5d 100644 --- a/internal/api/grpc/idp/converter.go +++ b/internal/api/grpc/idp/converter.go @@ -406,6 +406,14 @@ func configToPb(config *query.IDPTemplate) *idp_pb.ProviderConfig { oauthConfigToPb(providerConfig, config.OAuthIDPTemplate) return providerConfig } + if config.OIDCIDPTemplate != nil { + oidcConfigToPb(providerConfig, config.OIDCIDPTemplate) + return providerConfig + } + if config.JWTIDPTemplate != nil { + jwtConfigToPb(providerConfig, config.JWTIDPTemplate) + return providerConfig + } if config.GoogleIDPTemplate != nil { googleConfigToPb(providerConfig, config.GoogleIDPTemplate) return providerConfig @@ -417,15 +425,6 @@ func configToPb(config *query.IDPTemplate) *idp_pb.ProviderConfig { return providerConfig } -func googleConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.GoogleIDPTemplate) { - providerConfig.Config = &idp_pb.ProviderConfig_Google{ - Google: &idp_pb.GoogleConfig{ - ClientId: template.ClientID, - Scopes: template.Scopes, - }, - } -} - func oauthConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.OAuthIDPTemplate) { providerConfig.Config = &idp_pb.ProviderConfig_Oauth{ Oauth: &idp_pb.OAuthConfig{ @@ -438,6 +437,36 @@ func oauthConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.OAut } } +func oidcConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.OIDCIDPTemplate) { + providerConfig.Config = &idp_pb.ProviderConfig_Oidc{ + Oidc: &idp_pb.GenericOIDCConfig{ + ClientId: template.ClientID, + Issuer: template.Issuer, + Scopes: template.Scopes, + }, + } +} + +func jwtConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.JWTIDPTemplate) { + providerConfig.Config = &idp_pb.ProviderConfig_Jwt{ + Jwt: &idp_pb.JWTConfig{ + JwtEndpoint: template.Endpoint, + Issuer: template.Issuer, + KeysEndpoint: template.KeysEndpoint, + HeaderName: template.HeaderName, + }, + } +} + +func googleConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.GoogleIDPTemplate) { + providerConfig.Config = &idp_pb.ProviderConfig_Google{ + Google: &idp_pb.GoogleConfig{ + ClientId: template.ClientID, + Scopes: template.Scopes, + }, + } +} + func ldapConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.LDAPIDPTemplate) { providerConfig.Config = &idp_pb.ProviderConfig_Ldap{ Ldap: &idp_pb.LDAPConfig{ diff --git a/internal/api/grpc/management/idp.go b/internal/api/grpc/management/idp.go index 5dc88be53..df76c0309 100644 --- a/internal/api/grpc/management/idp.go +++ b/internal/api/grpc/management/idp.go @@ -187,6 +187,48 @@ func (s *Server) UpdateGenericOAuthProvider(ctx context.Context, req *mgmt_pb.Up }, nil } +func (s *Server) AddGenericOIDCProvider(ctx context.Context, req *mgmt_pb.AddGenericOIDCProviderRequest) (*mgmt_pb.AddGenericOIDCProviderResponse, error) { + id, details, err := s.command.AddOrgGenericOIDCProvider(ctx, authz.GetCtxData(ctx).OrgID, addGenericOIDCProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.AddGenericOIDCProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateGenericOIDCProvider(ctx context.Context, req *mgmt_pb.UpdateGenericOIDCProviderRequest) (*mgmt_pb.UpdateGenericOIDCProviderResponse, error) { + details, err := s.command.UpdateOrgGenericOIDCProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id, updateGenericOIDCProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.UpdateGenericOIDCProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + +func (s *Server) AddJWTProvider(ctx context.Context, req *mgmt_pb.AddJWTProviderRequest) (*mgmt_pb.AddJWTProviderResponse, error) { + id, details, err := s.command.AddOrgJWTProvider(ctx, authz.GetCtxData(ctx).OrgID, addJWTProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.AddJWTProviderResponse{ + Id: id, + Details: object_pb.DomainToAddDetailsPb(details), + }, nil +} + +func (s *Server) UpdateJWTProvider(ctx context.Context, req *mgmt_pb.UpdateJWTProviderRequest) (*mgmt_pb.UpdateJWTProviderResponse, error) { + details, err := s.command.UpdateOrgJWTProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id, updateJWTProviderToCommand(req)) + if err != nil { + return nil, err + } + return &mgmt_pb.UpdateJWTProviderResponse{ + Details: object_pb.DomainToChangeDetailsPb(details), + }, nil +} + func (s *Server) AddGoogleProvider(ctx context.Context, req *mgmt_pb.AddGoogleProviderRequest) (*mgmt_pb.AddGoogleProviderResponse, error) { id, details, err := s.command.AddOrgGoogleProvider(ctx, authz.GetCtxData(ctx).OrgID, addGoogleProviderToCommand(req)) if err != nil { diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go index ed863591a..0138392ca 100644 --- a/internal/api/grpc/management/idp_converter.go +++ b/internal/api/grpc/management/idp_converter.go @@ -244,6 +244,50 @@ func updateGenericOAuthProviderToCommand(req *mgmt_pb.UpdateGenericOAuthProvider } } +func addGenericOIDCProviderToCommand(req *mgmt_pb.AddGenericOIDCProviderRequest) command.GenericOIDCProvider { + return command.GenericOIDCProvider{ + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateGenericOIDCProviderToCommand(req *mgmt_pb.UpdateGenericOIDCProviderRequest) command.GenericOIDCProvider { + return command.GenericOIDCProvider{ + Name: req.Name, + Issuer: req.Issuer, + ClientID: req.ClientId, + ClientSecret: req.ClientSecret, + Scopes: req.Scopes, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func addJWTProviderToCommand(req *mgmt_pb.AddJWTProviderRequest) command.JWTProvider { + return command.JWTProvider{ + Name: req.Name, + Issuer: req.Issuer, + JWTEndpoint: req.JwtEndpoint, + KeyEndpoint: req.KeysEndpoint, + HeaderName: req.HeaderName, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + +func updateJWTProviderToCommand(req *mgmt_pb.UpdateJWTProviderRequest) command.JWTProvider { + return command.JWTProvider{ + Name: req.Name, + Issuer: req.Issuer, + JWTEndpoint: req.JwtEndpoint, + KeyEndpoint: req.KeysEndpoint, + HeaderName: req.HeaderName, + IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions), + } +} + func addGoogleProviderToCommand(req *mgmt_pb.AddGoogleProviderRequest) command.GoogleProvider { return command.GoogleProvider{ Name: req.Name, diff --git a/internal/command/idp.go b/internal/command/idp.go index 2d2295c29..f6de9cdf8 100644 --- a/internal/command/idp.go +++ b/internal/command/idp.go @@ -13,6 +13,24 @@ type GenericOAuthProvider struct { IDPOptions idp.Options } +type GenericOIDCProvider struct { + Name string + Issuer string + ClientID string + ClientSecret string + Scopes []string + IDPOptions idp.Options +} + +type JWTProvider struct { + Name string + Issuer string + JWTEndpoint string + KeyEndpoint string + HeaderName string + IDPOptions idp.Options +} + type GoogleProvider struct { Name string ClientID string diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index 424e8a671..eb2944b61 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -122,6 +122,287 @@ func (wm *OAuthIDPWriteModel) NewChanges( return changes, nil } +type OIDCIDPWriteModel struct { + eventstore.WriteModel + + Name string + ID string + Issuer string + ClientID string + ClientSecret *crypto.CryptoValue + Scopes []string + idp.Options + + State domain.IDPState +} + +func (wm *OIDCIDPWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *idp.OIDCIDPAddedEvent: + wm.reduceAddedEvent(e) + case *idp.OIDCIDPChangedEvent: + wm.reduceChangedEvent(e) + case *idpconfig.IDPConfigAddedEvent: + wm.reduceIDPConfigAddedEvent(e) + case *idpconfig.IDPConfigChangedEvent: + wm.reduceIDPConfigChangedEvent(e) + case *idpconfig.OIDCConfigAddedEvent: + wm.reduceOIDCConfigAddedEvent(e) + case *idpconfig.OIDCConfigChangedEvent: + wm.reduceOIDCConfigChangedEvent(e) + case *idpconfig.IDPConfigRemovedEvent: + wm.State = domain.IDPStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *OIDCIDPWriteModel) reduceAddedEvent(e *idp.OIDCIDPAddedEvent) { + wm.Name = e.Name + wm.Issuer = e.Issuer + wm.ClientID = e.ClientID + wm.ClientSecret = e.ClientSecret + wm.Scopes = e.Scopes + wm.Options = e.Options + wm.State = domain.IDPStateActive +} + +func (wm *OIDCIDPWriteModel) reduceChangedEvent(e *idp.OIDCIDPChangedEvent) { + if e.ClientID != nil { + wm.ClientID = *e.ClientID + } + if e.ClientSecret != nil { + wm.ClientSecret = e.ClientSecret + } + if e.Name != nil { + wm.Name = *e.Name + } + if e.Issuer != nil { + wm.Issuer = *e.Issuer + } + if e.Scopes != nil { + wm.Scopes = e.Scopes + } + wm.Options.ReduceChanges(e.OptionChanges) +} + +func (wm *OIDCIDPWriteModel) NewChanges( + name, + issuer, + clientID, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) ([]idp.OIDCIDPChanges, error) { + changes := make([]idp.OIDCIDPChanges, 0) + var clientSecret *crypto.CryptoValue + var err error + if clientSecretString != "" { + clientSecret, err = crypto.Crypt([]byte(clientSecretString), secretCrypto) + if err != nil { + return nil, err + } + changes = append(changes, idp.ChangeOIDCClientSecret(clientSecret)) + } + if wm.ClientID != clientID { + changes = append(changes, idp.ChangeOIDCClientID(clientID)) + } + if wm.Name != name { + changes = append(changes, idp.ChangeOIDCName(name)) + } + if wm.Issuer != issuer { + changes = append(changes, idp.ChangeOIDCIssuer(issuer)) + } + if !reflect.DeepEqual(wm.Scopes, scopes) { + changes = append(changes, idp.ChangeOIDCScopes(scopes)) + } + opts := wm.Options.Changes(options) + if !opts.IsZero() { + changes = append(changes, idp.ChangeOIDCOptions(opts)) + } + return changes, nil +} + +// reduceIDPConfigAddedEvent handles old idpConfig events +func (wm *OIDCIDPWriteModel) reduceIDPConfigAddedEvent(e *idpconfig.IDPConfigAddedEvent) { + wm.Name = e.Name + wm.Options.IsAutoCreation = e.AutoRegister + wm.State = domain.IDPStateActive +} + +// reduceIDPConfigChangedEvent handles old idpConfig changes +func (wm *OIDCIDPWriteModel) reduceIDPConfigChangedEvent(e *idpconfig.IDPConfigChangedEvent) { + if e.Name != nil { + wm.Name = *e.Name + } + if e.AutoRegister != nil { + wm.Options.IsAutoCreation = *e.AutoRegister + } +} + +// reduceOIDCConfigAddedEvent handles old OIDC idpConfig events +func (wm *OIDCIDPWriteModel) reduceOIDCConfigAddedEvent(e *idpconfig.OIDCConfigAddedEvent) { + wm.Issuer = e.Issuer + wm.ClientID = e.ClientID + wm.ClientSecret = e.ClientSecret + wm.Scopes = e.Scopes +} + +// reduceOIDCConfigChangedEvent handles old OIDC idpConfig changes +func (wm *OIDCIDPWriteModel) reduceOIDCConfigChangedEvent(e *idpconfig.OIDCConfigChangedEvent) { + if e.Issuer != nil { + wm.Issuer = *e.Issuer + } + if e.ClientID != nil { + wm.ClientID = *e.ClientID + } + if e.ClientSecret != nil { + wm.ClientSecret = e.ClientSecret + } + if e.Scopes != nil { + wm.Scopes = e.Scopes + } +} + +type JWTIDPWriteModel struct { + eventstore.WriteModel + + ID string + Name string + Issuer string + JWTEndpoint string + KeysEndpoint string + HeaderName string + idp.Options + + State domain.IDPState +} + +func (wm *JWTIDPWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *idp.JWTIDPAddedEvent: + wm.reduceAddedEvent(e) + case *idp.JWTIDPChangedEvent: + wm.reduceChangedEvent(e) + case *idpconfig.IDPConfigAddedEvent: + wm.reduceIDPConfigAddedEvent(e) + case *idpconfig.IDPConfigChangedEvent: + wm.reduceIDPConfigChangedEvent(e) + case *idpconfig.JWTConfigAddedEvent: + wm.reduceJWTConfigAddedEvent(e) + case *idpconfig.JWTConfigChangedEvent: + wm.reduceJWTConfigChangedEvent(e) + case *idpconfig.IDPConfigRemovedEvent: + wm.State = domain.IDPStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *JWTIDPWriteModel) reduceAddedEvent(e *idp.JWTIDPAddedEvent) { + wm.Name = e.Name + wm.Issuer = e.Issuer + wm.JWTEndpoint = e.JWTEndpoint + wm.KeysEndpoint = e.KeysEndpoint + wm.HeaderName = e.HeaderName + wm.Options = e.Options + wm.State = domain.IDPStateActive +} + +func (wm *JWTIDPWriteModel) reduceChangedEvent(e *idp.JWTIDPChangedEvent) { + if e.Name != nil { + wm.Name = *e.Name + } + if e.Issuer != nil { + wm.Issuer = *e.Issuer + } + if e.JWTEndpoint != nil { + wm.JWTEndpoint = *e.JWTEndpoint + } + if e.KeysEndpoint != nil { + wm.KeysEndpoint = *e.KeysEndpoint + } + if e.HeaderName != nil { + wm.HeaderName = *e.HeaderName + } + wm.Options.ReduceChanges(e.OptionChanges) +} + +func (wm *JWTIDPWriteModel) NewChanges( + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName string, + options idp.Options, +) ([]idp.JWTIDPChanges, error) { + changes := make([]idp.JWTIDPChanges, 0) + if wm.Name != name { + changes = append(changes, idp.ChangeJWTName(name)) + } + if wm.Issuer != issuer { + changes = append(changes, idp.ChangeJWTIssuer(issuer)) + } + if wm.JWTEndpoint != jwtEndpoint { + changes = append(changes, idp.ChangeJWTEndpoint(jwtEndpoint)) + } + if wm.KeysEndpoint != keysEndpoint { + changes = append(changes, idp.ChangeJWTKeysEndpoint(keysEndpoint)) + } + if wm.HeaderName != headerName { + changes = append(changes, idp.ChangeJWTHeaderName(headerName)) + } + opts := wm.Options.Changes(options) + if !opts.IsZero() { + changes = append(changes, idp.ChangeJWTOptions(opts)) + } + return changes, nil +} + +// reduceIDPConfigAddedEvent handles old idpConfig events +func (wm *JWTIDPWriteModel) reduceIDPConfigAddedEvent(e *idpconfig.IDPConfigAddedEvent) { + wm.Name = e.Name + wm.Options.IsAutoCreation = e.AutoRegister + wm.State = domain.IDPStateActive +} + +// reduceIDPConfigChangedEvent handles old idpConfig changes +func (wm *JWTIDPWriteModel) reduceIDPConfigChangedEvent(e *idpconfig.IDPConfigChangedEvent) { + if e.Name != nil { + wm.Name = *e.Name + } + if e.AutoRegister != nil { + wm.Options.IsAutoCreation = *e.AutoRegister + } +} + +// reduceJWTConfigAddedEvent handles old JWT idpConfig events +func (wm *JWTIDPWriteModel) reduceJWTConfigAddedEvent(e *idpconfig.JWTConfigAddedEvent) { + wm.Issuer = e.Issuer + wm.JWTEndpoint = e.JWTEndpoint + wm.KeysEndpoint = e.KeysEndpoint + wm.HeaderName = e.HeaderName +} + +// reduceJWTConfigChangedEvent handles old JWT idpConfig changes +func (wm *JWTIDPWriteModel) reduceJWTConfigChangedEvent(e *idpconfig.JWTConfigChangedEvent) { + if e.Issuer != nil { + wm.Issuer = *e.Issuer + } + if e.JWTEndpoint != nil { + wm.JWTEndpoint = *e.JWTEndpoint + } + if e.KeysEndpoint != nil { + wm.KeysEndpoint = *e.KeysEndpoint + } + if e.HeaderName != nil { + wm.HeaderName = *e.HeaderName + } +} + type GoogleIDPWriteModel struct { eventstore.WriteModel @@ -365,28 +646,25 @@ type IDPRemoveWriteModel struct { ID string State domain.IDPState - name string } func (wm *IDPRemoveWriteModel) Reduce() error { for _, event := range wm.Events { switch e := event.(type) { case *idp.OAuthIDPAddedEvent: - wm.reduceAdded(e.ID, e.Name) - case *idp.OAuthIDPChangedEvent: - wm.reduceChanged(e.ID, e.Name) + wm.reduceAdded(e.ID) + case *idp.OIDCIDPAddedEvent: + wm.reduceAdded(e.ID) + case *idp.JWTIDPAddedEvent: + wm.reduceAdded(e.ID) case *idp.GoogleIDPAddedEvent: - wm.reduceAdded(e.ID, e.Name) - case *idp.GoogleIDPChangedEvent: - wm.reduceChanged(e.ID, e.Name) + wm.reduceAdded(e.ID) case *idp.LDAPIDPAddedEvent: - wm.reduceAdded(e.ID, e.Name) - case *idp.LDAPIDPChangedEvent: - wm.reduceChanged(e.ID, e.Name) + wm.reduceAdded(e.ID) case *idp.RemovedEvent: wm.reduceRemoved(e.ID) case *idpconfig.IDPConfigAddedEvent: - wm.reduceAdded(e.ConfigID, "") + wm.reduceAdded(e.ConfigID) case *idpconfig.IDPConfigRemovedEvent: wm.reduceRemoved(e.ConfigID) } @@ -394,19 +672,11 @@ func (wm *IDPRemoveWriteModel) Reduce() error { return wm.WriteModel.Reduce() } -func (wm *IDPRemoveWriteModel) reduceAdded(id string, name string) { +func (wm *IDPRemoveWriteModel) reduceAdded(id string) { if wm.ID != id { return } wm.State = domain.IDPStateActive - wm.name = name -} - -func (wm *IDPRemoveWriteModel) reduceChanged(id string, name *string) { - if wm.ID != id || name == nil { - return - } - wm.name = *name } func (wm *IDPRemoveWriteModel) reduceRemoved(id string) { diff --git a/internal/command/instance_idp.go b/internal/command/instance_idp.go index 0f37218cf..37b581f55 100644 --- a/internal/command/instance_idp.go +++ b/internal/command/instance_idp.go @@ -55,6 +55,90 @@ func (c *Commands) UpdateInstanceGenericOAuthProvider(ctx context.Context, id st return pushedEventsToObjectDetails(pushedEvents), nil } +func (c *Commands) AddInstanceGenericOIDCProvider(ctx context.Context, provider GenericOIDCProvider) (string, *domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewOIDCInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddInstanceOIDCProvider(instanceAgg, writeModel, provider)) + if err != nil { + return "", nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return "", nil, err + } + return id, pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) UpdateInstanceGenericOIDCProvider(ctx context.Context, id string, provider GenericOIDCProvider) (*domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + writeModel := NewOIDCInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateInstanceOIDCProvider(instanceAgg, writeModel, provider)) + if err != nil { + return nil, err + } + if len(cmds) == 0 { + // no change, so return directly + return &domain.ObjectDetails{ + Sequence: writeModel.ProcessedSequence, + EventDate: writeModel.ChangeDate, + ResourceOwner: writeModel.ResourceOwner, + }, nil + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) AddInstanceJWTProvider(ctx context.Context, provider JWTProvider) (string, *domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewJWTInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddInstanceJWTProvider(instanceAgg, writeModel, provider)) + if err != nil { + return "", nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return "", nil, err + } + return id, pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) UpdateInstanceJWTProvider(ctx context.Context, id string, provider JWTProvider) (*domain.ObjectDetails, error) { + instanceID := authz.GetInstance(ctx).InstanceID() + instanceAgg := instance.NewAggregate(instanceID) + writeModel := NewJWTInstanceIDPWriteModel(instanceID, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateInstanceJWTProvider(instanceAgg, writeModel, provider)) + if err != nil { + return nil, err + } + if len(cmds) == 0 { + // no change, so return directly + return &domain.ObjectDetails{ + Sequence: writeModel.ProcessedSequence, + EventDate: writeModel.ChangeDate, + ResourceOwner: writeModel.ResourceOwner, + }, nil + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + func (c *Commands) AddInstanceGoogleProvider(ctx context.Context, provider GoogleProvider) (string, *domain.ObjectDetails, error) { instanceID := authz.GetInstance(ctx).InstanceID() instanceAgg := instance.NewAggregate(instanceID) @@ -247,11 +331,192 @@ func (c *Commands) prepareUpdateInstanceOAuthProvider(a *instance.Aggregate, wri provider.Scopes, provider.IDPOptions, ) + if err != nil || event == nil { + return nil, err + } + return []eventstore.Command{event}, nil + }, nil + } +} + +func (c *Commands) prepareAddInstanceOIDCProvider(a *instance.Aggregate, writeModel *InstanceOIDCIDPWriteModel, provider GenericOIDCProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Sgtj5", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Hz6zj", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-fb5jm", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Sfdf4", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) if err != nil { return nil, err } - if event == nil { - return nil, nil + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + instance.NewOIDCIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateInstanceOIDCProvider(a *instance.Aggregate, writeModel *InstanceOIDCIDPWriteModel, provider GenericOIDCProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SAfd3", "Errors.Invalid.Argument") + } + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Dvf4f", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-BDfr3", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Db3bs", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "INST-Dg331", "Errors.Instance.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.ClientID, + provider.ClientSecret, + c.idpConfigEncryption, + provider.Scopes, + provider.IDPOptions, + ) + if err != nil || event == nil { + return nil, err + } + return []eventstore.Command{event}, nil + }, nil + } +} + +func (c *Commands) prepareAddInstanceJWTProvider(a *instance.Aggregate, writeModel *InstanceJWTIDPWriteModel, provider JWTProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-JLKef", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-WNJK3", "Errors.Invalid.Argument") + } + if provider.JWTEndpoint = strings.TrimSpace(provider.JWTEndpoint); provider.JWTEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-NJKSD", "Errors.Invalid.Argument") + } + if provider.KeyEndpoint = strings.TrimSpace(provider.KeyEndpoint); provider.KeyEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-NJKE3", "Errors.Invalid.Argument") + } + if provider.HeaderName = strings.TrimSpace(provider.HeaderName); provider.HeaderName == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-2rlks", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + return []eventstore.Command{ + instance.NewJWTIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.JWTEndpoint, + provider.KeyEndpoint, + provider.HeaderName, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateInstanceJWTProvider(a *instance.Aggregate, writeModel *InstanceJWTIDPWriteModel, provider JWTProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-HUe3q", "Errors.Invalid.Argument") + } + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-JKLS2", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-JKs3f", "Errors.Invalid.Argument") + } + if provider.JWTEndpoint = strings.TrimSpace(provider.JWTEndpoint); provider.JWTEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-NJKS2", "Errors.Invalid.Argument") + } + if provider.KeyEndpoint = strings.TrimSpace(provider.KeyEndpoint); provider.KeyEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SJk2d", "Errors.Invalid.Argument") + } + if provider.HeaderName = strings.TrimSpace(provider.HeaderName); provider.HeaderName == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SJK2f", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "INST-Bhju5", "Errors.Instance.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.JWTEndpoint, + provider.KeyEndpoint, + provider.HeaderName, + provider.IDPOptions, + ) + if err != nil || event == nil { + return nil, err } return []eventstore.Command{event}, nil }, nil @@ -326,12 +591,9 @@ func (c *Commands) prepareUpdateInstanceGoogleProvider(a *instance.Aggregate, wr provider.Scopes, provider.IDPOptions, ) - if err != nil { + if err != nil || event == nil { return nil, err } - if event == nil { - return nil, nil - } return []eventstore.Command{event}, nil }, nil } @@ -448,12 +710,9 @@ func (c *Commands) prepareUpdateInstanceLDAPProvider(a *instance.Aggregate, writ provider.LDAPAttributes, provider.IDPOptions, ) - if err != nil { + if err != nil || event == nil { return nil, err } - if event == nil { - return nil, nil - } return []eventstore.Command{event}, nil }, nil } @@ -474,7 +733,7 @@ func (c *Commands) prepareDeleteInstanceProvider(a *instance.Aggregate, id strin if !writeModel.State.Exists() { return nil, caos_errs.ThrowNotFound(nil, "INST-Se3tg", "Errors.Instance.IDPConfig.NotExisting") } - return []eventstore.Command{instance.NewIDPRemovedEvent(ctx, &a.Aggregate, id, writeModel.name)}, nil + return []eventstore.Command{instance.NewIDPRemovedEvent(ctx, &a.Aggregate, id)}, nil }, nil } } diff --git a/internal/command/instance_idp_model.go b/internal/command/instance_idp_model.go index 42fa70ef4..4184892c5 100644 --- a/internal/command/instance_idp_model.go +++ b/internal/command/instance_idp_model.go @@ -83,15 +83,212 @@ func (wm *InstanceOAuthIDPWriteModel) NewChangedEvent( scopes, options, ) - if err != nil { + if err != nil || len(changes) == 0 { return nil, err } - if len(changes) == 0 { - return nil, nil - } return instance.NewOAuthIDPChangedEvent(ctx, aggregate, id, changes) } +type InstanceOIDCIDPWriteModel struct { + OIDCIDPWriteModel +} + +func NewOIDCInstanceIDPWriteModel(instanceID, id string) *InstanceOIDCIDPWriteModel { + return &InstanceOIDCIDPWriteModel{ + OIDCIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: instanceID, + ResourceOwner: instanceID, + }, + ID: id, + }, + } +} + +func (wm *InstanceOIDCIDPWriteModel) Reduce() error { + return wm.OIDCIDPWriteModel.Reduce() +} + +func (wm *InstanceOIDCIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *instance.OIDCIDPAddedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPAddedEvent) + case *instance.OIDCIDPChangedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPChangedEvent) + case *instance.IDPRemovedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.RemovedEvent) + + // old events + case *instance.IDPConfigAddedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.IDPConfigAddedEvent) + case *instance.IDPConfigChangedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.IDPConfigChangedEvent) + case *instance.IDPOIDCConfigAddedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCConfigAddedEvent) + case *instance.IDPOIDCConfigChangedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCConfigChangedEvent) + case *instance.IDPConfigRemovedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.IDPConfigRemovedEvent) + default: + wm.OIDCIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *InstanceOIDCIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.OIDCIDPAddedEventType, + instance.OIDCIDPChangedEventType, + instance.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Or(). // old events + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.IDPConfigAddedEventType, + instance.IDPConfigChangedEventType, + instance.IDPOIDCConfigAddedEventType, + instance.IDPOIDCConfigChangedEventType, + instance.IDPConfigRemovedEventType, + ). + EventData(map[string]interface{}{"idpConfigId": wm.ID}). + Builder() +} + +func (wm *InstanceOIDCIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + clientID, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) (*instance.OIDCIDPChangedEvent, error) { + + changes, err := wm.OIDCIDPWriteModel.NewChanges( + name, + issuer, + clientID, + clientSecretString, + secretCrypto, + scopes, + options, + ) + if err != nil || len(changes) == 0 { + return nil, err + } + return instance.NewOIDCIDPChangedEvent(ctx, aggregate, id, changes) +} + +type InstanceJWTIDPWriteModel struct { + JWTIDPWriteModel +} + +func NewJWTInstanceIDPWriteModel(instanceID, id string) *InstanceJWTIDPWriteModel { + return &InstanceJWTIDPWriteModel{ + JWTIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: instanceID, + ResourceOwner: instanceID, + }, + ID: id, + }, + } +} + +func (wm *InstanceJWTIDPWriteModel) Reduce() error { + return wm.JWTIDPWriteModel.Reduce() +} + +func (wm *InstanceJWTIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *instance.JWTIDPAddedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.JWTIDPAddedEvent) + case *instance.JWTIDPChangedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.JWTIDPChangedEvent) + case *instance.IDPRemovedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.RemovedEvent) + + // old events + case *instance.IDPConfigAddedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.IDPConfigAddedEvent) + case *instance.IDPConfigChangedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.IDPConfigChangedEvent) + case *instance.IDPJWTConfigAddedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.JWTConfigAddedEvent) + case *instance.IDPJWTConfigChangedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.JWTConfigChangedEvent) + case *instance.IDPConfigRemovedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.IDPConfigRemovedEvent) + default: + wm.JWTIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *InstanceJWTIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.JWTIDPAddedEventType, + instance.JWTIDPChangedEventType, + instance.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Or(). // old events + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.IDPConfigAddedEventType, + instance.IDPConfigChangedEventType, + instance.IDPJWTConfigAddedEventType, + instance.IDPJWTConfigChangedEventType, + instance.IDPConfigRemovedEventType, + ). + EventData(map[string]interface{}{"idpConfigId": wm.ID}). + Builder() +} + +func (wm *InstanceJWTIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName string, + options idp.Options, +) (*instance.JWTIDPChangedEvent, error) { + + changes, err := wm.JWTIDPWriteModel.NewChanges( + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName, + options, + ) + if err != nil || len(changes) == 0 { + return nil, err + } + return instance.NewJWTIDPChangedEvent(ctx, aggregate, id, changes) +} + type InstanceGoogleIDPWriteModel struct { GoogleIDPWriteModel } @@ -153,12 +350,9 @@ func (wm *InstanceGoogleIDPWriteModel) NewChangedEvent( ) (*instance.GoogleIDPChangedEvent, error) { changes, err := wm.GoogleIDPWriteModel.NewChanges(name, clientID, clientSecretString, secretCrypto, scopes, options) - if err != nil { + if err != nil || len(changes) == 0 { return nil, err } - if len(changes) == 0 { - return nil, nil - } return instance.NewGoogleIDPChangedEvent(ctx, aggregate, id, changes) } @@ -245,12 +439,9 @@ func (wm *InstanceLDAPIDPWriteModel) NewChangedEvent( attributes, options, ) - if err != nil { + if err != nil || len(changes) == 0 { return nil, err } - if len(changes) == 0 { - return nil, nil - } return instance.NewLDAPIDPChangedEvent(ctx, aggregate, id, oldName, changes) } @@ -279,18 +470,20 @@ func (wm *InstanceIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) switch e := event.(type) { case *instance.OAuthIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.OAuthIDPAddedEvent) - case *instance.OAuthIDPChangedEvent: - wm.IDPRemoveWriteModel.AppendEvents(&e.OAuthIDPChangedEvent) + case *instance.OIDCIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.OIDCIDPAddedEvent) + case *instance.JWTIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.JWTIDPAddedEvent) case *instance.GoogleIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.GoogleIDPAddedEvent) - case *instance.GoogleIDPChangedEvent: - wm.IDPRemoveWriteModel.AppendEvents(&e.GoogleIDPChangedEvent) case *instance.LDAPIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.LDAPIDPAddedEvent) - case *instance.LDAPIDPChangedEvent: - wm.IDPRemoveWriteModel.AppendEvents(&e.LDAPIDPChangedEvent) case *instance.IDPRemovedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.RemovedEvent) + case *instance.IDPConfigAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.IDPConfigAddedEvent) + case *instance.IDPConfigRemovedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.IDPConfigRemovedEvent) default: wm.IDPRemoveWriteModel.AppendEvents(e) } @@ -305,13 +498,20 @@ func (wm *InstanceIDPRemoveWriteModel) Query() *eventstore.SearchQueryBuilder { AggregateIDs(wm.AggregateID). EventTypes( instance.OAuthIDPAddedEventType, - instance.OAuthIDPChangedEventType, + instance.OIDCIDPAddedEventType, + instance.JWTIDPAddedEventType, instance.GoogleIDPAddedEventType, - instance.GoogleIDPChangedEventType, instance.LDAPIDPAddedEventType, - instance.LDAPIDPChangedEventType, instance.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). + Or(). // old events + AggregateTypes(instance.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + instance.IDPConfigAddedEventType, + instance.IDPConfigRemovedEventType, + ). + EventData(map[string]interface{}{"idpConfigId": wm.ID}). Builder() } diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go index 6cae2f480..fc559fdd9 100644 --- a/internal/command/org_idp.go +++ b/internal/command/org_idp.go @@ -52,6 +52,86 @@ func (c *Commands) UpdateOrgGenericOAuthProvider(ctx context.Context, resourceOw return pushedEventsToObjectDetails(pushedEvents), nil } +func (c *Commands) AddOrgGenericOIDCProvider(ctx context.Context, resourceOwner string, provider GenericOIDCProvider) (string, *domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewOIDCOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgOIDCProvider(orgAgg, writeModel, provider)) + if err != nil { + return "", nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return "", nil, err + } + return id, pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) UpdateOrgGenericOIDCProvider(ctx context.Context, resourceOwner, id string, provider GenericOIDCProvider) (*domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + writeModel := NewOIDCOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgOIDCProvider(orgAgg, writeModel, provider)) + if err != nil { + return nil, err + } + if len(cmds) == 0 { + // no change, so return directly + return &domain.ObjectDetails{ + Sequence: writeModel.ProcessedSequence, + EventDate: writeModel.ChangeDate, + ResourceOwner: writeModel.ResourceOwner, + }, nil + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) AddOrgJWTProvider(ctx context.Context, resourceOwner string, provider JWTProvider) (string, *domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + id, err := c.idGenerator.Next() + if err != nil { + return "", nil, err + } + writeModel := NewJWTOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgJWTProvider(orgAgg, writeModel, provider)) + if err != nil { + return "", nil, err + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return "", nil, err + } + return id, pushedEventsToObjectDetails(pushedEvents), nil +} + +func (c *Commands) UpdateOrgJWTProvider(ctx context.Context, resourceOwner, id string, provider JWTProvider) (*domain.ObjectDetails, error) { + orgAgg := org.NewAggregate(resourceOwner) + writeModel := NewJWTOrgIDPWriteModel(resourceOwner, id) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgJWTProvider(orgAgg, writeModel, provider)) + if err != nil { + return nil, err + } + if len(cmds) == 0 { + // no change, so return directly + return &domain.ObjectDetails{ + Sequence: writeModel.ProcessedSequence, + EventDate: writeModel.ChangeDate, + ResourceOwner: writeModel.ResourceOwner, + }, nil + } + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return pushedEventsToObjectDetails(pushedEvents), nil +} + func (c *Commands) AddOrgGoogleProvider(ctx context.Context, resourceOwner string, provider GoogleProvider) (string, *domain.ObjectDetails, error) { orgAgg := org.NewAggregate(resourceOwner) id, err := c.idGenerator.Next() @@ -243,11 +323,192 @@ func (c *Commands) prepareUpdateOrgOAuthProvider(a *org.Aggregate, writeModel *O provider.Scopes, provider.IDPOptions, ) + if err != nil || event == nil { + return nil, err + } + return []eventstore.Command{event}, nil + }, nil + } +} + +func (c *Commands) prepareAddOrgOIDCProvider(a *org.Aggregate, writeModel *OrgOIDCIDPWriteModel, provider GenericOIDCProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Sgtj5", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Hz6zj", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-fb5jm", "Errors.Invalid.Argument") + } + if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Sfdf4", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) if err != nil { return nil, err } - if event == nil { - return nil, nil + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption) + if err != nil { + return nil, err + } + return []eventstore.Command{ + org.NewOIDCIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.ClientID, + secret, + provider.Scopes, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateOrgOIDCProvider(a *org.Aggregate, writeModel *OrgOIDCIDPWriteModel, provider GenericOIDCProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SAfd3", "Errors.Invalid.Argument") + } + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Dvf4f", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-BDfr3", "Errors.Invalid.Argument") + } + if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Db3bs", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "ORG-Dg331", "Errors.Org.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.ClientID, + provider.ClientSecret, + c.idpConfigEncryption, + provider.Scopes, + provider.IDPOptions, + ) + if err != nil || event == nil { + return nil, err + } + return []eventstore.Command{event}, nil + }, nil + } +} + +func (c *Commands) prepareAddOrgJWTProvider(a *org.Aggregate, writeModel *OrgJWTIDPWriteModel, provider JWTProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-JLKef", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-WNJK3", "Errors.Invalid.Argument") + } + if provider.JWTEndpoint = strings.TrimSpace(provider.JWTEndpoint); provider.JWTEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-NJKSD", "Errors.Invalid.Argument") + } + if provider.KeyEndpoint = strings.TrimSpace(provider.KeyEndpoint); provider.KeyEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-NJKE3", "Errors.Invalid.Argument") + } + if provider.HeaderName = strings.TrimSpace(provider.HeaderName); provider.HeaderName == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-2rlks", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + return []eventstore.Command{ + org.NewJWTIDPAddedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.JWTEndpoint, + provider.KeyEndpoint, + provider.HeaderName, + provider.IDPOptions, + ), + }, nil + }, nil + } +} + +func (c *Commands) prepareUpdateOrgJWTProvider(a *org.Aggregate, writeModel *OrgJWTIDPWriteModel, provider JWTProvider) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-HUe3q", "Errors.Invalid.Argument") + } + if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-JKLS2", "Errors.Invalid.Argument") + } + if provider.Issuer = strings.TrimSpace(provider.Issuer); provider.Issuer == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-JKs3f", "Errors.Invalid.Argument") + } + if provider.JWTEndpoint = strings.TrimSpace(provider.JWTEndpoint); provider.JWTEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-NJKS2", "Errors.Invalid.Argument") + } + if provider.KeyEndpoint = strings.TrimSpace(provider.KeyEndpoint); provider.KeyEndpoint == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SJk2d", "Errors.Invalid.Argument") + } + if provider.HeaderName = strings.TrimSpace(provider.HeaderName); provider.HeaderName == "" { + return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SJK2f", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "ORG-Bhju5", "Errors.Org.IDPConfig.NotExisting") + } + event, err := writeModel.NewChangedEvent( + ctx, + &a.Aggregate, + writeModel.ID, + provider.Name, + provider.Issuer, + provider.JWTEndpoint, + provider.KeyEndpoint, + provider.HeaderName, + provider.IDPOptions, + ) + if err != nil || event == nil { + return nil, err } return []eventstore.Command{event}, nil }, nil @@ -322,12 +583,9 @@ func (c *Commands) prepareUpdateOrgGoogleProvider(a *org.Aggregate, writeModel * provider.Scopes, provider.IDPOptions, ) - if err != nil { + if err != nil || event == nil { return nil, err } - if event == nil { - return nil, nil - } return []eventstore.Command{event}, nil }, nil } @@ -444,12 +702,9 @@ func (c *Commands) prepareUpdateOrgLDAPProvider(a *org.Aggregate, writeModel *Or provider.LDAPAttributes, provider.IDPOptions, ) - if err != nil { + if err != nil || event == nil { return nil, err } - if event == nil { - return nil, nil - } return []eventstore.Command{event}, nil }, nil } @@ -470,7 +725,7 @@ func (c *Commands) prepareDeleteOrgProvider(a *org.Aggregate, resourceOwner, id if !writeModel.State.Exists() { return nil, caos_errs.ThrowNotFound(nil, "ORG-Se3tg", "Errors.Org.IDPConfig.NotExisting") } - return []eventstore.Command{org.NewIDPRemovedEvent(ctx, &a.Aggregate, id, writeModel.name)}, nil + return []eventstore.Command{org.NewIDPRemovedEvent(ctx, &a.Aggregate, id)}, nil }, nil } } diff --git a/internal/command/org_idp_model.go b/internal/command/org_idp_model.go index 8192f18fd..f6b3c6ec4 100644 --- a/internal/command/org_idp_model.go +++ b/internal/command/org_idp_model.go @@ -85,15 +85,212 @@ func (wm *OrgOAuthIDPWriteModel) NewChangedEvent( scopes, options, ) - if err != nil { + if err != nil || len(changes) == 0 { return nil, err } - if len(changes) == 0 { - return nil, nil - } return org.NewOAuthIDPChangedEvent(ctx, aggregate, id, changes) } +type OrgOIDCIDPWriteModel struct { + OIDCIDPWriteModel +} + +func NewOIDCOrgIDPWriteModel(orgID, id string) *OrgOIDCIDPWriteModel { + return &OrgOIDCIDPWriteModel{ + OIDCIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: orgID, + ResourceOwner: orgID, + }, + ID: id, + }, + } +} + +func (wm *OrgOIDCIDPWriteModel) Reduce() error { + return wm.OIDCIDPWriteModel.Reduce() +} + +func (wm *OrgOIDCIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *org.OIDCIDPAddedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPAddedEvent) + case *org.OIDCIDPChangedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCIDPChangedEvent) + case *org.IDPRemovedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.RemovedEvent) + + // old events + case *org.IDPConfigAddedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.IDPConfigAddedEvent) + case *org.IDPConfigChangedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.IDPConfigChangedEvent) + case *org.IDPOIDCConfigAddedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCConfigAddedEvent) + case *org.IDPOIDCConfigChangedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.OIDCConfigChangedEvent) + case *org.IDPConfigRemovedEvent: + wm.OIDCIDPWriteModel.AppendEvents(&e.IDPConfigRemovedEvent) + default: + wm.OIDCIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *OrgOIDCIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.OIDCIDPAddedEventType, + org.OIDCIDPChangedEventType, + org.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Or(). // old events + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.IDPConfigAddedEventType, + org.IDPConfigChangedEventType, + org.IDPOIDCConfigAddedEventType, + org.IDPOIDCConfigChangedEventType, + org.IDPConfigRemovedEventType, + ). + EventData(map[string]interface{}{"idpConfigId": wm.ID}). + Builder() +} + +func (wm *OrgOIDCIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + clientID, + clientSecretString string, + secretCrypto crypto.Crypto, + scopes []string, + options idp.Options, +) (*org.OIDCIDPChangedEvent, error) { + + changes, err := wm.OIDCIDPWriteModel.NewChanges( + name, + issuer, + clientID, + clientSecretString, + secretCrypto, + scopes, + options, + ) + if err != nil || len(changes) == 0 { + return nil, err + } + return org.NewOIDCIDPChangedEvent(ctx, aggregate, id, changes) +} + +type OrgJWTIDPWriteModel struct { + JWTIDPWriteModel +} + +func NewJWTOrgIDPWriteModel(orgID, id string) *OrgJWTIDPWriteModel { + return &OrgJWTIDPWriteModel{ + JWTIDPWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: orgID, + ResourceOwner: orgID, + }, + ID: id, + }, + } +} + +func (wm *OrgJWTIDPWriteModel) Reduce() error { + return wm.JWTIDPWriteModel.Reduce() +} + +func (wm *OrgJWTIDPWriteModel) AppendEvents(events ...eventstore.Event) { + for _, event := range events { + switch e := event.(type) { + case *org.JWTIDPAddedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.JWTIDPAddedEvent) + case *org.JWTIDPChangedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.JWTIDPChangedEvent) + case *org.IDPRemovedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.RemovedEvent) + + // old events + case *org.IDPConfigAddedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.IDPConfigAddedEvent) + case *org.IDPConfigChangedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.IDPConfigChangedEvent) + case *org.IDPJWTConfigAddedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.JWTConfigAddedEvent) + case *org.IDPJWTConfigChangedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.JWTConfigChangedEvent) + case *org.IDPConfigRemovedEvent: + wm.JWTIDPWriteModel.AppendEvents(&e.IDPConfigRemovedEvent) + default: + wm.JWTIDPWriteModel.AppendEvents(e) + } + } +} + +func (wm *OrgJWTIDPWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.JWTIDPAddedEventType, + org.JWTIDPChangedEventType, + org.IDPRemovedEventType, + ). + EventData(map[string]interface{}{"id": wm.ID}). + Or(). // old events + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.IDPConfigAddedEventType, + org.IDPConfigChangedEventType, + org.IDPJWTConfigAddedEventType, + org.IDPJWTConfigChangedEventType, + org.IDPConfigRemovedEventType, + ). + EventData(map[string]interface{}{"idpConfigId": wm.ID}). + Builder() +} + +func (wm *OrgJWTIDPWriteModel) NewChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName string, + options idp.Options, +) (*org.JWTIDPChangedEvent, error) { + + changes, err := wm.JWTIDPWriteModel.NewChanges( + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName, + options, + ) + if err != nil || len(changes) == 0 { + return nil, err + } + return org.NewJWTIDPChangedEvent(ctx, aggregate, id, changes) +} + type OrgGoogleIDPWriteModel struct { GoogleIDPWriteModel } @@ -157,12 +354,9 @@ func (wm *OrgGoogleIDPWriteModel) NewChangedEvent( ) (*org.GoogleIDPChangedEvent, error) { changes, err := wm.GoogleIDPWriteModel.NewChanges(name, clientID, clientSecretString, secretCrypto, scopes, options) - if err != nil { + if err != nil || len(changes) == 0 { return nil, err } - if len(changes) == 0 { - return nil, nil - } return org.NewGoogleIDPChangedEvent(ctx, aggregate, id, changes) } @@ -249,12 +443,9 @@ func (wm *OrgLDAPIDPWriteModel) NewChangedEvent( attributes, options, ) - if err != nil { + if err != nil || len(changes) == 0 { return nil, err } - if len(changes) == 0 { - return nil, nil - } return org.NewLDAPIDPChangedEvent(ctx, aggregate, id, oldName, changes) } @@ -283,18 +474,20 @@ func (wm *OrgIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) { switch e := event.(type) { case *org.OAuthIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.OAuthIDPAddedEvent) - case *org.OAuthIDPChangedEvent: - wm.IDPRemoveWriteModel.AppendEvents(&e.OAuthIDPChangedEvent) + case *org.OIDCIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.OIDCIDPAddedEvent) + case *org.JWTIDPAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.JWTIDPAddedEvent) case *org.GoogleIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.GoogleIDPAddedEvent) - case *org.GoogleIDPChangedEvent: - wm.IDPRemoveWriteModel.AppendEvents(&e.GoogleIDPChangedEvent) case *org.LDAPIDPAddedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.LDAPIDPAddedEvent) - case *org.LDAPIDPChangedEvent: - wm.IDPRemoveWriteModel.AppendEvents(&e.LDAPIDPChangedEvent) case *org.IDPRemovedEvent: wm.IDPRemoveWriteModel.AppendEvents(&e.RemovedEvent) + case *org.IDPConfigAddedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.IDPConfigAddedEvent) + case *org.IDPConfigRemovedEvent: + wm.IDPRemoveWriteModel.AppendEvents(&e.IDPConfigRemovedEvent) default: wm.IDPRemoveWriteModel.AppendEvents(e) } @@ -309,13 +502,20 @@ func (wm *OrgIDPRemoveWriteModel) Query() *eventstore.SearchQueryBuilder { AggregateIDs(wm.AggregateID). EventTypes( org.OAuthIDPAddedEventType, - org.OAuthIDPChangedEventType, + org.OIDCIDPAddedEventType, + org.JWTIDPAddedEventType, org.GoogleIDPAddedEventType, - org.GoogleIDPChangedEventType, org.LDAPIDPAddedEventType, - org.LDAPIDPChangedEventType, org.IDPRemovedEventType, ). EventData(map[string]interface{}{"id": wm.ID}). + Or(). // old events + AggregateTypes(org.AggregateType). + AggregateIDs(wm.AggregateID). + EventTypes( + org.IDPConfigAddedEventType, + org.IDPConfigRemovedEventType, + ). + EventData(map[string]interface{}{"idpConfigId": wm.ID}). Builder() } diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go index 6f992fef8..2ca0ffe39 100644 --- a/internal/query/idp_template.go +++ b/internal/query/idp_template.go @@ -34,6 +34,8 @@ type IDPTemplate struct { IsAutoCreation bool IsAutoUpdate bool *OAuthIDPTemplate + *OIDCIDPTemplate + *JWTIDPTemplate *GoogleIDPTemplate *LDAPIDPTemplate } @@ -53,6 +55,22 @@ type OAuthIDPTemplate struct { Scopes database.StringArray } +type OIDCIDPTemplate struct { + IDPID string + ClientID string + ClientSecret *crypto.CryptoValue + Issuer string + Scopes database.StringArray +} + +type JWTIDPTemplate struct { + IDPID string + Issuer string + KeysEndpoint string + HeaderName string + Endpoint string +} + type GoogleIDPTemplate struct { IDPID string ClientID string @@ -179,6 +197,68 @@ var ( } ) +var ( + oidcIdpTemplateTable = table{ + name: projection.IDPTemplateOIDCTable, + instanceIDCol: projection.OIDCInstanceIDCol, + } + OIDCIDCol = Column{ + name: projection.OIDCIDCol, + table: oidcIdpTemplateTable, + } + OIDCInstanceIDCol = Column{ + name: projection.OIDCInstanceIDCol, + table: oidcIdpTemplateTable, + } + OIDCIssuerCol = Column{ + name: projection.OIDCIssuerCol, + table: oidcIdpTemplateTable, + } + OIDCClientIDCol = Column{ + name: projection.OIDCClientIDCol, + table: oidcIdpTemplateTable, + } + OIDCClientSecretCol = Column{ + name: projection.OIDCClientSecretCol, + table: oidcIdpTemplateTable, + } + OIDCScopesCol = Column{ + name: projection.OIDCScopesCol, + table: oidcIdpTemplateTable, + } +) + +var ( + jwtIdpTemplateTable = table{ + name: projection.IDPTemplateJWTTable, + instanceIDCol: projection.JWTInstanceIDCol, + } + JWTIDCol = Column{ + name: projection.JWTIDCol, + table: jwtIdpTemplateTable, + } + JWTInstanceIDCol = Column{ + name: projection.JWTInstanceIDCol, + table: jwtIdpTemplateTable, + } + JWTIssuerCol = Column{ + name: projection.JWTIssuerCol, + table: jwtIdpTemplateTable, + } + JWTEndpointCol = Column{ + name: projection.JWTEndpointCol, + table: jwtIdpTemplateTable, + } + JWTKeysEndpointCol = Column{ + name: projection.JWTKeysEndpointCol, + table: jwtIdpTemplateTable, + } + JWTHeaderNameCol = Column{ + name: projection.JWTHeaderNameCol, + table: jwtIdpTemplateTable, + } +) + var ( googleIdpTemplateTable = table{ name: projection.IDPTemplateGoogleTable, @@ -428,6 +508,18 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla OAuthTokenEndpointCol.identifier(), OAuthUserEndpointCol.identifier(), OAuthScopesCol.identifier(), + // oidc + OIDCIDCol.identifier(), + OIDCIssuerCol.identifier(), + OIDCClientIDCol.identifier(), + OIDCClientSecretCol.identifier(), + OIDCScopesCol.identifier(), + // jwt + JWTIDCol.identifier(), + JWTIssuerCol.identifier(), + JWTEndpointCol.identifier(), + JWTKeysEndpointCol.identifier(), + JWTHeaderNameCol.identifier(), // google GoogleIDCol.identifier(), GoogleClientIDCol.identifier(), @@ -458,6 +550,8 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla LDAPProfileAttributeCol.identifier(), ).From(idpTemplateTable.identifier()). LeftJoin(join(OAuthIDCol, IDPTemplateIDCol)). + LeftJoin(join(OIDCIDCol, IDPTemplateIDCol)). + LeftJoin(join(JWTIDCol, IDPTemplateIDCol)). LeftJoin(join(GoogleIDCol, IDPTemplateIDCol)). LeftJoin(join(LDAPIDCol, IDPTemplateIDCol)). PlaceholderFormat(sq.Dollar), @@ -474,6 +568,18 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla oauthUserEndpoint := sql.NullString{} oauthScopes := database.StringArray{} + oidcID := sql.NullString{} + oidcIssuer := sql.NullString{} + oidcClientID := sql.NullString{} + oidcClientSecret := new(crypto.CryptoValue) + oidcScopes := database.StringArray{} + + jwtID := sql.NullString{} + jwtIssuer := sql.NullString{} + jwtEndpoint := sql.NullString{} + jwtKeysEndpoint := sql.NullString{} + jwtHeaderName := sql.NullString{} + googleID := sql.NullString{} googleClientID := sql.NullString{} googleClientSecret := new(crypto.CryptoValue) @@ -524,6 +630,18 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla &oauthTokenEndpoint, &oauthUserEndpoint, &oauthScopes, + // oidc + &oidcID, + &oidcIssuer, + &oidcClientID, + &oidcClientSecret, + &oidcScopes, + // jwt + &jwtID, + &jwtIssuer, + &jwtEndpoint, + &jwtKeysEndpoint, + &jwtHeaderName, // google &googleID, &googleClientID, @@ -573,6 +691,24 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla Scopes: oauthScopes, } } + if oidcID.Valid { + idpTemplate.OIDCIDPTemplate = &OIDCIDPTemplate{ + IDPID: oidcID.String, + ClientID: oidcClientID.String, + ClientSecret: oidcClientSecret, + Issuer: oidcIssuer.String, + Scopes: oidcScopes, + } + } + if jwtID.Valid { + idpTemplate.JWTIDPTemplate = &JWTIDPTemplate{ + IDPID: jwtID.String, + Issuer: jwtIssuer.String, + KeysEndpoint: jwtKeysEndpoint.String, + HeaderName: jwtHeaderName.String, + Endpoint: jwtEndpoint.String, + } + } if googleID.Valid { idpTemplate.GoogleIDPTemplate = &GoogleIDPTemplate{ IDPID: googleID.String, @@ -637,6 +773,18 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate OAuthTokenEndpointCol.identifier(), OAuthUserEndpointCol.identifier(), OAuthScopesCol.identifier(), + // oidc + OIDCIDCol.identifier(), + OIDCIssuerCol.identifier(), + OIDCClientIDCol.identifier(), + OIDCClientSecretCol.identifier(), + OIDCScopesCol.identifier(), + // jwt + JWTIDCol.identifier(), + JWTIssuerCol.identifier(), + JWTEndpointCol.identifier(), + JWTKeysEndpointCol.identifier(), + JWTHeaderNameCol.identifier(), // google GoogleIDCol.identifier(), GoogleClientIDCol.identifier(), @@ -668,6 +816,8 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate countColumn.identifier(), ).From(idpTemplateTable.identifier()). LeftJoin(join(OAuthIDCol, IDPTemplateIDCol)). + LeftJoin(join(OIDCIDCol, IDPTemplateIDCol)). + LeftJoin(join(JWTIDCol, IDPTemplateIDCol)). LeftJoin(join(GoogleIDCol, IDPTemplateIDCol)). LeftJoin(join(LDAPIDCol, IDPTemplateIDCol)). PlaceholderFormat(sq.Dollar), @@ -687,6 +837,18 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate oauthUserEndpoint := sql.NullString{} oauthScopes := database.StringArray{} + oidcID := sql.NullString{} + oidcIssuer := sql.NullString{} + oidcClientID := sql.NullString{} + oidcClientSecret := new(crypto.CryptoValue) + oidcScopes := database.StringArray{} + + jwtID := sql.NullString{} + jwtIssuer := sql.NullString{} + jwtEndpoint := sql.NullString{} + jwtKeysEndpoint := sql.NullString{} + jwtHeaderName := sql.NullString{} + googleID := sql.NullString{} googleClientID := sql.NullString{} googleClientSecret := new(crypto.CryptoValue) @@ -737,6 +899,18 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate &oauthTokenEndpoint, &oauthUserEndpoint, &oauthScopes, + // oidc + &oidcID, + &oidcIssuer, + &oidcClientID, + &oidcClientSecret, + &oidcScopes, + // jwt + &jwtID, + &jwtIssuer, + &jwtEndpoint, + &jwtKeysEndpoint, + &jwtHeaderName, // google &googleID, &googleClientID, @@ -785,6 +959,24 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate Scopes: oauthScopes, } } + if oidcID.Valid { + idpTemplate.OIDCIDPTemplate = &OIDCIDPTemplate{ + IDPID: oidcID.String, + ClientID: oidcClientID.String, + ClientSecret: oidcClientSecret, + Issuer: oidcIssuer.String, + Scopes: oidcScopes, + } + } + if jwtID.Valid { + idpTemplate.JWTIDPTemplate = &JWTIDPTemplate{ + IDPID: jwtID.String, + Issuer: jwtIssuer.String, + KeysEndpoint: jwtKeysEndpoint.String, + HeaderName: jwtHeaderName.String, + Endpoint: jwtEndpoint.String, + } + } if googleID.Valid { idpTemplate.GoogleIDPTemplate = &GoogleIDPTemplate{ IDPID: googleID.String, diff --git a/internal/query/idp_template_test.go b/internal/query/idp_template_test.go index 2efe3b4d7..a70aa7ef5 100644 --- a/internal/query/idp_template_test.go +++ b/internal/query/idp_template_test.go @@ -15,59 +15,73 @@ import ( ) var ( - idpTemplateQuery = `SELECT projections.idp_templates.id,` + - ` projections.idp_templates.resource_owner,` + - ` projections.idp_templates.creation_date,` + - ` projections.idp_templates.change_date,` + - ` projections.idp_templates.sequence,` + - ` projections.idp_templates.state,` + - ` projections.idp_templates.name,` + - ` projections.idp_templates.type,` + - ` projections.idp_templates.owner_type,` + - ` projections.idp_templates.is_creation_allowed,` + - ` projections.idp_templates.is_linking_allowed,` + - ` projections.idp_templates.is_auto_creation,` + - ` projections.idp_templates.is_auto_update,` + + idpTemplateQuery = `SELECT projections.idp_templates2.id,` + + ` projections.idp_templates2.resource_owner,` + + ` projections.idp_templates2.creation_date,` + + ` projections.idp_templates2.change_date,` + + ` projections.idp_templates2.sequence,` + + ` projections.idp_templates2.state,` + + ` projections.idp_templates2.name,` + + ` projections.idp_templates2.type,` + + ` projections.idp_templates2.owner_type,` + + ` projections.idp_templates2.is_creation_allowed,` + + ` projections.idp_templates2.is_linking_allowed,` + + ` projections.idp_templates2.is_auto_creation,` + + ` projections.idp_templates2.is_auto_update,` + // oauth - ` projections.idp_templates_oauth.idp_id,` + - ` projections.idp_templates_oauth.client_id,` + - ` projections.idp_templates_oauth.client_secret,` + - ` projections.idp_templates_oauth.authorization_endpoint,` + - ` projections.idp_templates_oauth.token_endpoint,` + - ` projections.idp_templates_oauth.user_endpoint,` + - ` projections.idp_templates_oauth.scopes,` + + ` projections.idp_templates2_oauth.idp_id,` + + ` projections.idp_templates2_oauth.client_id,` + + ` projections.idp_templates2_oauth.client_secret,` + + ` projections.idp_templates2_oauth.authorization_endpoint,` + + ` projections.idp_templates2_oauth.token_endpoint,` + + ` projections.idp_templates2_oauth.user_endpoint,` + + ` projections.idp_templates2_oauth.scopes,` + + // oidc + ` projections.idp_templates2_oidc.idp_id,` + + ` projections.idp_templates2_oidc.issuer,` + + ` projections.idp_templates2_oidc.client_id,` + + ` projections.idp_templates2_oidc.client_secret,` + + ` projections.idp_templates2_oidc.scopes,` + + // jwt + ` projections.idp_templates2_jwt.idp_id,` + + ` projections.idp_templates2_jwt.issuer,` + + ` projections.idp_templates2_jwt.jwt_endpoint,` + + ` projections.idp_templates2_jwt.keys_endpoint,` + + ` projections.idp_templates2_jwt.header_name,` + // google - ` projections.idp_templates_google.idp_id,` + - ` projections.idp_templates_google.client_id,` + - ` projections.idp_templates_google.client_secret,` + - ` projections.idp_templates_google.scopes,` + + ` projections.idp_templates2_google.idp_id,` + + ` projections.idp_templates2_google.client_id,` + + ` projections.idp_templates2_google.client_secret,` + + ` projections.idp_templates2_google.scopes,` + // ldap - ` projections.idp_templates_ldap.idp_id,` + - ` projections.idp_templates_ldap.host,` + - ` projections.idp_templates_ldap.port,` + - ` projections.idp_templates_ldap.tls,` + - ` projections.idp_templates_ldap.base_dn,` + - ` projections.idp_templates_ldap.user_object_class,` + - ` projections.idp_templates_ldap.user_unique_attribute,` + - ` projections.idp_templates_ldap.admin,` + - ` projections.idp_templates_ldap.password,` + - ` projections.idp_templates_ldap.id_attribute,` + - ` projections.idp_templates_ldap.first_name_attribute,` + - ` projections.idp_templates_ldap.last_name_attribute,` + - ` projections.idp_templates_ldap.display_name_attribute,` + - ` projections.idp_templates_ldap.nick_name_attribute,` + - ` projections.idp_templates_ldap.preferred_username_attribute,` + - ` projections.idp_templates_ldap.email_attribute,` + - ` projections.idp_templates_ldap.email_verified,` + - ` projections.idp_templates_ldap.phone_attribute,` + - ` projections.idp_templates_ldap.phone_verified_attribute,` + - ` projections.idp_templates_ldap.preferred_language_attribute,` + - ` projections.idp_templates_ldap.avatar_url_attribute,` + - ` projections.idp_templates_ldap.profile_attribute` + - ` FROM projections.idp_templates` + - ` LEFT JOIN projections.idp_templates_oauth ON projections.idp_templates.id = projections.idp_templates_oauth.idp_id AND projections.idp_templates.instance_id = projections.idp_templates_oauth.instance_id` + - ` LEFT JOIN projections.idp_templates_google ON projections.idp_templates.id = projections.idp_templates_google.idp_id AND projections.idp_templates.instance_id = projections.idp_templates_google.instance_id` + - ` LEFT JOIN projections.idp_templates_ldap ON projections.idp_templates.id = projections.idp_templates_ldap.idp_id AND projections.idp_templates.instance_id = projections.idp_templates_ldap.instance_id` + ` projections.idp_templates2_ldap.idp_id,` + + ` projections.idp_templates2_ldap.host,` + + ` projections.idp_templates2_ldap.port,` + + ` projections.idp_templates2_ldap.tls,` + + ` projections.idp_templates2_ldap.base_dn,` + + ` projections.idp_templates2_ldap.user_object_class,` + + ` projections.idp_templates2_ldap.user_unique_attribute,` + + ` projections.idp_templates2_ldap.admin,` + + ` projections.idp_templates2_ldap.password,` + + ` projections.idp_templates2_ldap.id_attribute,` + + ` projections.idp_templates2_ldap.first_name_attribute,` + + ` projections.idp_templates2_ldap.last_name_attribute,` + + ` projections.idp_templates2_ldap.display_name_attribute,` + + ` projections.idp_templates2_ldap.nick_name_attribute,` + + ` projections.idp_templates2_ldap.preferred_username_attribute,` + + ` projections.idp_templates2_ldap.email_attribute,` + + ` projections.idp_templates2_ldap.email_verified,` + + ` projections.idp_templates2_ldap.phone_attribute,` + + ` projections.idp_templates2_ldap.phone_verified_attribute,` + + ` projections.idp_templates2_ldap.preferred_language_attribute,` + + ` projections.idp_templates2_ldap.avatar_url_attribute,` + + ` projections.idp_templates2_ldap.profile_attribute` + + ` FROM projections.idp_templates2` + + ` LEFT JOIN projections.idp_templates2_oauth ON projections.idp_templates2.id = projections.idp_templates2_oauth.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_oauth.instance_id` + + ` LEFT JOIN projections.idp_templates2_oidc ON projections.idp_templates2.id = projections.idp_templates2_oidc.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_oidc.instance_id` + + ` LEFT JOIN projections.idp_templates2_jwt ON projections.idp_templates2.id = projections.idp_templates2_jwt.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_jwt.instance_id` + + ` LEFT JOIN projections.idp_templates2_google ON projections.idp_templates2.id = projections.idp_templates2_google.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_google.instance_id` + + ` LEFT JOIN projections.idp_templates2_ldap ON projections.idp_templates2.id = projections.idp_templates2_ldap.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_ldap.instance_id` idpTemplateCols = []string{ "id", "resource_owner", @@ -90,6 +104,18 @@ var ( "token_endpoint", "user_endpoint", "scopes", + // oidc config + "id_id", + "issuer", + "client_id", + "client_secret", + "scopes", + // jwt + "idp_id", + "issuer", + "jwt_endpoint", + "keys_endpoint", + "header_name", // google config "idp_id", "client_id", @@ -119,60 +145,74 @@ var ( "avatar_url_attribute", "profile_attribute", } - idpTemplatesQuery = `SELECT projections.idp_templates.id,` + - ` projections.idp_templates.resource_owner,` + - ` projections.idp_templates.creation_date,` + - ` projections.idp_templates.change_date,` + - ` projections.idp_templates.sequence,` + - ` projections.idp_templates.state,` + - ` projections.idp_templates.name,` + - ` projections.idp_templates.type,` + - ` projections.idp_templates.owner_type,` + - ` projections.idp_templates.is_creation_allowed,` + - ` projections.idp_templates.is_linking_allowed,` + - ` projections.idp_templates.is_auto_creation,` + - ` projections.idp_templates.is_auto_update,` + + idpTemplatesQuery = `SELECT projections.idp_templates2.id,` + + ` projections.idp_templates2.resource_owner,` + + ` projections.idp_templates2.creation_date,` + + ` projections.idp_templates2.change_date,` + + ` projections.idp_templates2.sequence,` + + ` projections.idp_templates2.state,` + + ` projections.idp_templates2.name,` + + ` projections.idp_templates2.type,` + + ` projections.idp_templates2.owner_type,` + + ` projections.idp_templates2.is_creation_allowed,` + + ` projections.idp_templates2.is_linking_allowed,` + + ` projections.idp_templates2.is_auto_creation,` + + ` projections.idp_templates2.is_auto_update,` + // oauth - ` projections.idp_templates_oauth.idp_id,` + - ` projections.idp_templates_oauth.client_id,` + - ` projections.idp_templates_oauth.client_secret,` + - ` projections.idp_templates_oauth.authorization_endpoint,` + - ` projections.idp_templates_oauth.token_endpoint,` + - ` projections.idp_templates_oauth.user_endpoint,` + - ` projections.idp_templates_oauth.scopes,` + + ` projections.idp_templates2_oauth.idp_id,` + + ` projections.idp_templates2_oauth.client_id,` + + ` projections.idp_templates2_oauth.client_secret,` + + ` projections.idp_templates2_oauth.authorization_endpoint,` + + ` projections.idp_templates2_oauth.token_endpoint,` + + ` projections.idp_templates2_oauth.user_endpoint,` + + ` projections.idp_templates2_oauth.scopes,` + + // oidc + ` projections.idp_templates2_oidc.idp_id,` + + ` projections.idp_templates2_oidc.issuer,` + + ` projections.idp_templates2_oidc.client_id,` + + ` projections.idp_templates2_oidc.client_secret,` + + ` projections.idp_templates2_oidc.scopes,` + + // jwt + ` projections.idp_templates2_jwt.idp_id,` + + ` projections.idp_templates2_jwt.issuer,` + + ` projections.idp_templates2_jwt.jwt_endpoint,` + + ` projections.idp_templates2_jwt.keys_endpoint,` + + ` projections.idp_templates2_jwt.header_name,` + // google - ` projections.idp_templates_google.idp_id,` + - ` projections.idp_templates_google.client_id,` + - ` projections.idp_templates_google.client_secret,` + - ` projections.idp_templates_google.scopes,` + + ` projections.idp_templates2_google.idp_id,` + + ` projections.idp_templates2_google.client_id,` + + ` projections.idp_templates2_google.client_secret,` + + ` projections.idp_templates2_google.scopes,` + // ldap - ` projections.idp_templates_ldap.idp_id,` + - ` projections.idp_templates_ldap.host,` + - ` projections.idp_templates_ldap.port,` + - ` projections.idp_templates_ldap.tls,` + - ` projections.idp_templates_ldap.base_dn,` + - ` projections.idp_templates_ldap.user_object_class,` + - ` projections.idp_templates_ldap.user_unique_attribute,` + - ` projections.idp_templates_ldap.admin,` + - ` projections.idp_templates_ldap.password,` + - ` projections.idp_templates_ldap.id_attribute,` + - ` projections.idp_templates_ldap.first_name_attribute,` + - ` projections.idp_templates_ldap.last_name_attribute,` + - ` projections.idp_templates_ldap.display_name_attribute,` + - ` projections.idp_templates_ldap.nick_name_attribute,` + - ` projections.idp_templates_ldap.preferred_username_attribute,` + - ` projections.idp_templates_ldap.email_attribute,` + - ` projections.idp_templates_ldap.email_verified,` + - ` projections.idp_templates_ldap.phone_attribute,` + - ` projections.idp_templates_ldap.phone_verified_attribute,` + - ` projections.idp_templates_ldap.preferred_language_attribute,` + - ` projections.idp_templates_ldap.avatar_url_attribute,` + - ` projections.idp_templates_ldap.profile_attribute,` + + ` projections.idp_templates2_ldap.idp_id,` + + ` projections.idp_templates2_ldap.host,` + + ` projections.idp_templates2_ldap.port,` + + ` projections.idp_templates2_ldap.tls,` + + ` projections.idp_templates2_ldap.base_dn,` + + ` projections.idp_templates2_ldap.user_object_class,` + + ` projections.idp_templates2_ldap.user_unique_attribute,` + + ` projections.idp_templates2_ldap.admin,` + + ` projections.idp_templates2_ldap.password,` + + ` projections.idp_templates2_ldap.id_attribute,` + + ` projections.idp_templates2_ldap.first_name_attribute,` + + ` projections.idp_templates2_ldap.last_name_attribute,` + + ` projections.idp_templates2_ldap.display_name_attribute,` + + ` projections.idp_templates2_ldap.nick_name_attribute,` + + ` projections.idp_templates2_ldap.preferred_username_attribute,` + + ` projections.idp_templates2_ldap.email_attribute,` + + ` projections.idp_templates2_ldap.email_verified,` + + ` projections.idp_templates2_ldap.phone_attribute,` + + ` projections.idp_templates2_ldap.phone_verified_attribute,` + + ` projections.idp_templates2_ldap.preferred_language_attribute,` + + ` projections.idp_templates2_ldap.avatar_url_attribute,` + + ` projections.idp_templates2_ldap.profile_attribute,` + ` COUNT(*) OVER ()` + - ` FROM projections.idp_templates` + - ` LEFT JOIN projections.idp_templates_oauth ON projections.idp_templates.id = projections.idp_templates_oauth.idp_id AND projections.idp_templates.instance_id = projections.idp_templates_oauth.instance_id` + - ` LEFT JOIN projections.idp_templates_google ON projections.idp_templates.id = projections.idp_templates_google.idp_id AND projections.idp_templates.instance_id = projections.idp_templates_google.instance_id` + - ` LEFT JOIN projections.idp_templates_ldap ON projections.idp_templates.id = projections.idp_templates_ldap.idp_id AND projections.idp_templates.instance_id = projections.idp_templates_ldap.instance_id` + ` FROM projections.idp_templates2` + + ` LEFT JOIN projections.idp_templates2_oauth ON projections.idp_templates2.id = projections.idp_templates2_oauth.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_oauth.instance_id` + + ` LEFT JOIN projections.idp_templates2_oidc ON projections.idp_templates2.id = projections.idp_templates2_oidc.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_oidc.instance_id` + + ` LEFT JOIN projections.idp_templates2_jwt ON projections.idp_templates2.id = projections.idp_templates2_jwt.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_jwt.instance_id` + + ` LEFT JOIN projections.idp_templates2_google ON projections.idp_templates2.id = projections.idp_templates2_google.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_google.instance_id` + + ` LEFT JOIN projections.idp_templates2_ldap ON projections.idp_templates2.id = projections.idp_templates2_ldap.idp_id AND projections.idp_templates2.instance_id = projections.idp_templates2_ldap.instance_id` idpTemplatesCols = []string{ "id", "resource_owner", @@ -195,6 +235,18 @@ var ( "token_endpoint", "user_endpoint", "scopes", + // oidc config + "id_id", + "issuer", + "client_id", + "client_secret", + "scopes", + // jwt + "idp_id", + "issuer", + "jwt_endpoint", + "keys_endpoint", + "header_name", // google config "idp_id", "client_id", @@ -285,6 +337,18 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "token", "user", database.StringArray{"profile"}, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -341,6 +405,196 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { }, }, }, + { + name: "prepareIDPTemplateByIDQuery oidc idp", + prepare: prepareIDPTemplateByIDQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(idpTemplateQuery), + idpTemplateCols, + []driver.Value{ + "idp-id", + "ro", + testNow, + testNow, + uint64(20211109), + domain.IDPConfigStateActive, + "idp-name", + domain.IDPTypeOIDC, + domain.IdentityProviderTypeOrg, + true, + true, + true, + true, + // oauth + nil, + nil, + nil, + nil, + nil, + nil, + nil, + // oidc + "idp-id", + "issuer", + "client_id", + nil, + database.StringArray{"profile"}, + // jwt + nil, + nil, + nil, + nil, + nil, + // google + nil, + nil, + nil, + nil, + // ldap config + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + ), + }, + object: &IDPTemplate{ + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211109, + ResourceOwner: "ro", + ID: "idp-id", + State: domain.IDPStateActive, + Name: "idp-name", + Type: domain.IDPTypeOIDC, + OwnerType: domain.IdentityProviderTypeOrg, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + OIDCIDPTemplate: &OIDCIDPTemplate{ + IDPID: "idp-id", + Issuer: "issuer", + ClientID: "client_id", + ClientSecret: nil, + Scopes: []string{"profile"}, + }, + }, + }, + { + name: "prepareIDPTemplateByIDQuery oidc idp", + prepare: prepareIDPTemplateByIDQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(idpTemplateQuery), + idpTemplateCols, + []driver.Value{ + "idp-id", + "ro", + testNow, + testNow, + uint64(20211109), + domain.IDPConfigStateActive, + "idp-name", + domain.IDPTypeJWT, + domain.IdentityProviderTypeOrg, + true, + true, + true, + true, + // oauth + nil, + nil, + nil, + nil, + nil, + nil, + nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + "idp-id", + "issuer", + "jwt", + "keys", + "header", + // google + nil, + nil, + nil, + nil, + // ldap config + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + ), + }, + object: &IDPTemplate{ + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211109, + ResourceOwner: "ro", + ID: "idp-id", + State: domain.IDPStateActive, + Name: "idp-name", + Type: domain.IDPTypeJWT, + OwnerType: domain.IdentityProviderTypeOrg, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + JWTIDPTemplate: &JWTIDPTemplate{ + IDPID: "idp-id", + Issuer: "issuer", + Endpoint: "jwt", + KeysEndpoint: "keys", + HeaderName: "header", + }, + }, + }, { name: "prepareIDPTemplateByIDQuery google idp", prepare: prepareIDPTemplateByIDQuery, @@ -370,6 +624,18 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, // google "idp-id", "client_id", @@ -452,6 +718,18 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, // google nil, nil, @@ -553,6 +831,18 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -665,6 +955,18 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -775,6 +1077,18 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -860,6 +1174,18 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, // google config nil, nil, @@ -911,6 +1237,18 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, // google "idp-id-google", "client_id", @@ -962,6 +1300,144 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { "token", "user", database.StringArray{"profile"}, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + nil, + nil, + nil, + nil, + nil, + // google + nil, + nil, + nil, + nil, + // ldap config + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + { + "idp-id-oidc", + "ro", + testNow, + testNow, + uint64(20211109), + domain.IDPConfigStateActive, + "idp-name", + domain.IDPTypeOIDC, + domain.IdentityProviderTypeOrg, + true, + true, + true, + true, + // oauth + nil, + nil, + nil, + nil, + nil, + nil, + nil, + // oidc + "idp-id-oidc", + "issuer", + "client_id", + nil, + database.StringArray{"profile"}, + // jwt + nil, + nil, + nil, + nil, + nil, + // google + nil, + nil, + nil, + nil, + // ldap config + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + }, + { + "idp-id-jwt", + "ro", + testNow, + testNow, + uint64(20211109), + domain.IDPConfigStateActive, + "idp-name", + domain.IDPTypeJWT, + domain.IdentityProviderTypeOrg, + true, + true, + true, + true, + // oauth + nil, + nil, + nil, + nil, + nil, + nil, + nil, + // oidc + nil, + nil, + nil, + nil, + nil, + // jwt + "idp-id-jwt", + "issuer", + "jwt", + "keys", + "header", // google nil, nil, @@ -996,7 +1472,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { }, object: &IDPTemplates{ SearchResponse: SearchResponse{ - Count: 3, + Count: 5, }, Templates: []*IDPTemplate{ { @@ -1085,6 +1561,50 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { Scopes: []string{"profile"}, }, }, + { + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211109, + ResourceOwner: "ro", + ID: "idp-id-oidc", + State: domain.IDPStateActive, + Name: "idp-name", + Type: domain.IDPTypeOIDC, + OwnerType: domain.IdentityProviderTypeOrg, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + OIDCIDPTemplate: &OIDCIDPTemplate{ + IDPID: "idp-id-oidc", + Issuer: "issuer", + ClientID: "client_id", + ClientSecret: nil, + Scopes: []string{"profile"}, + }, + }, + { + CreationDate: testNow, + ChangeDate: testNow, + Sequence: 20211109, + ResourceOwner: "ro", + ID: "idp-id-jwt", + State: domain.IDPStateActive, + Name: "idp-name", + Type: domain.IDPTypeJWT, + OwnerType: domain.IdentityProviderTypeOrg, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + JWTIDPTemplate: &JWTIDPTemplate{ + IDPID: "idp-id-jwt", + Issuer: "issuer", + Endpoint: "jwt", + KeysEndpoint: "keys", + HeaderName: "header", + }, + }, }, }, }, diff --git a/internal/query/projection/idp_template.go b/internal/query/projection/idp_template.go index 4ef51450c..c1d541646 100644 --- a/internal/query/projection/idp_template.go +++ b/internal/query/projection/idp_template.go @@ -11,17 +11,22 @@ import ( "github.com/zitadel/zitadel/internal/eventstore/handler" "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" "github.com/zitadel/zitadel/internal/repository/idp" + "github.com/zitadel/zitadel/internal/repository/idpconfig" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" ) const ( - IDPTemplateTable = "projections.idp_templates" + IDPTemplateTable = "projections.idp_templates2" IDPTemplateOAuthTable = IDPTemplateTable + "_" + IDPTemplateOAuthSuffix + IDPTemplateOIDCTable = IDPTemplateTable + "_" + IDPTemplateOIDCSuffix + IDPTemplateJWTTable = IDPTemplateTable + "_" + IDPTemplateJWTSuffix IDPTemplateGoogleTable = IDPTemplateTable + "_" + IDPTemplateGoogleSuffix IDPTemplateLDAPTable = IDPTemplateTable + "_" + IDPTemplateLDAPSuffix IDPTemplateOAuthSuffix = "oauth" + IDPTemplateOIDCSuffix = "oidc" + IDPTemplateJWTSuffix = "jwt" IDPTemplateGoogleSuffix = "google" IDPTemplateLDAPSuffix = "ldap" @@ -50,6 +55,20 @@ const ( OAuthUserEndpointCol = "user_endpoint" OAuthScopesCol = "scopes" + OIDCIDCol = "idp_id" + OIDCInstanceIDCol = "instance_id" + OIDCIssuerCol = "issuer" + OIDCClientIDCol = "client_id" + OIDCClientSecretCol = "client_secret" + OIDCScopesCol = "scopes" + + JWTIDCol = "idp_id" + JWTInstanceIDCol = "instance_id" + JWTIssuerCol = "issuer" + JWTEndpointCol = "jwt_endpoint" + JWTKeysEndpointCol = "keys_endpoint" + JWTHeaderNameCol = "header_name" + GoogleIDCol = "idp_id" GoogleInstanceIDCol = "instance_id" GoogleClientIDCol = "client_id" @@ -125,6 +144,30 @@ func newIDPTemplateProjection(ctx context.Context, config crdb.StatementHandlerC IDPTemplateOAuthSuffix, crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), ), + crdb.NewSuffixedTable([]*crdb.Column{ + crdb.NewColumn(OIDCIDCol, crdb.ColumnTypeText), + crdb.NewColumn(OIDCInstanceIDCol, crdb.ColumnTypeText), + crdb.NewColumn(OIDCIssuerCol, crdb.ColumnTypeText), + crdb.NewColumn(OIDCClientIDCol, crdb.ColumnTypeText), + crdb.NewColumn(OIDCClientSecretCol, crdb.ColumnTypeJSONB), + crdb.NewColumn(OIDCScopesCol, crdb.ColumnTypeTextArray, crdb.Nullable()), + }, + crdb.NewPrimaryKey(OIDCInstanceIDCol, OIDCIDCol), + IDPTemplateOIDCSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + ), + crdb.NewSuffixedTable([]*crdb.Column{ + crdb.NewColumn(JWTIDCol, crdb.ColumnTypeText), + crdb.NewColumn(JWTInstanceIDCol, crdb.ColumnTypeText), + crdb.NewColumn(JWTIssuerCol, crdb.ColumnTypeText), + crdb.NewColumn(JWTEndpointCol, crdb.ColumnTypeText), + crdb.NewColumn(JWTKeysEndpointCol, crdb.ColumnTypeText), + crdb.NewColumn(JWTHeaderNameCol, crdb.ColumnTypeText, crdb.Nullable()), + }, + crdb.NewPrimaryKey(JWTInstanceIDCol, JWTIDCol), + IDPTemplateJWTSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()), + ), crdb.NewSuffixedTable([]*crdb.Column{ crdb.NewColumn(GoogleIDCol, crdb.ColumnTypeText), crdb.NewColumn(GoogleInstanceIDCol, crdb.ColumnTypeText), @@ -183,6 +226,46 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { Event: instance.OAuthIDPChangedEventType, Reduce: p.reduceOAuthIDPChanged, }, + { + Event: instance.OIDCIDPAddedEventType, + Reduce: p.reduceOIDCIDPAdded, + }, + { + Event: instance.OIDCIDPChangedEventType, + Reduce: p.reduceOIDCIDPChanged, + }, + { + Event: instance.JWTIDPAddedEventType, + Reduce: p.reduceJWTIDPAdded, + }, + { + Event: instance.JWTIDPAddedEventType, + Reduce: p.reduceJWTIDPChanged, + }, + { + Event: instance.IDPConfigAddedEventType, + Reduce: p.reduceOldConfigAdded, + }, + { + Event: instance.IDPConfigChangedEventType, + Reduce: p.reduceOldConfigChanged, + }, + { + Event: instance.IDPOIDCConfigAddedEventType, + Reduce: p.reduceOldOIDCConfigAdded, + }, + { + Event: instance.IDPOIDCConfigChangedEventType, + Reduce: p.reduceOldOIDCConfigChanged, + }, + { + Event: instance.IDPJWTConfigAddedEventType, + Reduce: p.reduceOldJWTConfigAdded, + }, + { + Event: instance.IDPJWTConfigChangedEventType, + Reduce: p.reduceOldJWTConfigChanged, + }, { Event: instance.GoogleIDPAddedEventType, Reduce: p.reduceGoogleIDPAdded, @@ -220,6 +303,47 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer { Event: org.OAuthIDPChangedEventType, Reduce: p.reduceOAuthIDPChanged, }, + { + Event: org.OIDCIDPAddedEventType, + Reduce: p.reduceOIDCIDPAdded, + }, + { + Event: org.OIDCIDPChangedEventType, + Reduce: p.reduceOIDCIDPChanged, + }, + + { + Event: org.JWTIDPAddedEventType, + Reduce: p.reduceJWTIDPAdded, + }, + { + Event: org.JWTIDPAddedEventType, + Reduce: p.reduceJWTIDPChanged, + }, + { + Event: org.IDPConfigAddedEventType, + Reduce: p.reduceOldConfigAdded, + }, + { + Event: org.IDPConfigChangedEventType, + Reduce: p.reduceOldConfigChanged, + }, + { + Event: org.IDPOIDCConfigAddedEventType, + Reduce: p.reduceOldOIDCConfigAdded, + }, + { + Event: org.IDPOIDCConfigChangedEventType, + Reduce: p.reduceOldOIDCConfigChanged, + }, + { + Event: org.IDPJWTConfigAddedEventType, + Reduce: p.reduceOldJWTConfigAdded, + }, + { + Event: org.IDPJWTConfigChangedEventType, + Reduce: p.reduceOldJWTConfigChanged, + }, { Event: org.GoogleIDPAddedEventType, Reduce: p.reduceGoogleIDPAdded, @@ -340,6 +464,438 @@ func (p *idpTemplateProjection) reduceOAuthIDPChanged(event eventstore.Event) (* ), nil } +func (p *idpTemplateProjection) reduceOIDCIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.OIDCIDPAddedEvent + var idpOwnerType domain.IdentityProviderType + switch e := event.(type) { + case *org.OIDCIDPAddedEvent: + idpEvent = e.OIDCIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeOrg + case *instance.OIDCIDPAddedEvent: + idpEvent = e.OIDCIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeSystem + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-9s02m1", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPAddedEventType, instance.OIDCIDPAddedEventType}) + } + + return crdb.NewMultiStatement( + &idpEvent, + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + handler.NewCol(IDPTemplateResourceOwnerCol, idpEvent.Aggregate().ResourceOwner), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateOwnerTypeCol, idpOwnerType), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeOIDC), + handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), + }, + ), + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(OIDCIDCol, idpEvent.ID), + handler.NewCol(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(OIDCIssuerCol, idpEvent.Issuer), + handler.NewCol(OIDCClientIDCol, idpEvent.ClientID), + handler.NewCol(OIDCClientSecretCol, idpEvent.ClientSecret), + handler.NewCol(OIDCScopesCol, database.StringArray(idpEvent.Scopes)), + }, + crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceOIDCIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.OIDCIDPChangedEvent + switch e := event.(type) { + case *org.OIDCIDPChangedEvent: + idpEvent = e.OIDCIDPChangedEvent + case *instance.OIDCIDPChangedEvent: + idpEvent = e.OIDCIDPChangedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPChangedEventType, instance.OIDCIDPChangedEventType}) + } + + ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops = append(ops, + crdb.AddUpdateStatement( + reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + ) + oidcCols := reduceOIDCIDPChangedColumns(idpEvent) + if len(oidcCols) > 0 { + ops = append(ops, + crdb.AddUpdateStatement( + oidcCols, + []handler.Condition{ + handler.NewCond(OIDCIDCol, idpEvent.ID), + handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + ), + ) + } + + return crdb.NewMultiStatement( + &idpEvent, + ops..., + ), nil +} + +func (p *idpTemplateProjection) reduceJWTIDPAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.JWTIDPAddedEvent + var idpOwnerType domain.IdentityProviderType + switch e := event.(type) { + case *org.JWTIDPAddedEvent: + idpEvent = e.JWTIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeOrg + case *instance.JWTIDPAddedEvent: + idpEvent = e.JWTIDPAddedEvent + idpOwnerType = domain.IdentityProviderTypeSystem + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-xopi2s", "reduce.wrong.event.type %v", []eventstore.EventType{org.JWTIDPAddedEventType, instance.JWTIDPAddedEventType}) + } + + return crdb.NewMultiStatement( + &idpEvent, + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ID), + handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + handler.NewCol(IDPTemplateResourceOwnerCol, idpEvent.Aggregate().ResourceOwner), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateOwnerTypeCol, idpOwnerType), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeJWT), + handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed), + handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed), + handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation), + handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate), + }, + ), + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(JWTIDCol, idpEvent.ID), + handler.NewCol(JWTInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(JWTIssuerCol, idpEvent.Issuer), + handler.NewCol(JWTEndpointCol, idpEvent.JWTEndpoint), + handler.NewCol(JWTKeysEndpointCol, idpEvent.KeysEndpoint), + handler.NewCol(JWTHeaderNameCol, idpEvent.HeaderName), + }, + crdb.WithTableSuffix(IDPTemplateJWTSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceJWTIDPChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idp.JWTIDPChangedEvent + switch e := event.(type) { + case *org.JWTIDPChangedEvent: + idpEvent = e.JWTIDPChangedEvent + case *instance.JWTIDPChangedEvent: + idpEvent = e.JWTIDPChangedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.JWTIDPChangedEventType, instance.JWTIDPChangedEventType}) + } + + ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops = append(ops, + crdb.AddUpdateStatement( + reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges), + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.ID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + ) + jwtCols := reduceJWTIDPChangedColumns(idpEvent) + if len(jwtCols) > 0 { + ops = append(ops, + crdb.AddUpdateStatement( + jwtCols, + []handler.Condition{ + handler.NewCond(JWTIDCol, idpEvent.ID), + handler.NewCond(JWTInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateJWTSuffix), + ), + ) + } + + return crdb.NewMultiStatement( + &idpEvent, + ops..., + ), nil +} + +func (p *idpTemplateProjection) reduceOldConfigAdded(event eventstore.Event) (*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 *instance.IDPConfigAddedEvent: + idpEvent = e.IDPConfigAddedEvent + idpOwnerType = domain.IdentityProviderTypeSystem + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ADfeg", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigAddedEventType, instance.IDPConfigAddedEventType}) + } + + return crdb.NewCreateStatement( + event, + []handler.Column{ + handler.NewCol(IDPTemplateIDCol, idpEvent.ConfigID), + handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + handler.NewCol(IDPTemplateResourceOwnerCol, idpEvent.Aggregate().ResourceOwner), + handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive), + handler.NewCol(IDPTemplateNameCol, idpEvent.Name), + handler.NewCol(IDPTemplateOwnerTypeCol, idpOwnerType), + handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeOIDC), + handler.NewCol(IDPTemplateIsCreationAllowedCol, true), + handler.NewCol(IDPTemplateIsLinkingAllowedCol, true), + handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.AutoRegister), + handler.NewCol(IDPTemplateIsAutoUpdateCol, false), + }, + ), nil +} + +func (p *idpTemplateProjection) reduceOldConfigChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idpconfig.IDPConfigChangedEvent + switch e := event.(type) { + case *org.IDPConfigChangedEvent: + idpEvent = e.IDPConfigChangedEvent + case *instance.IDPConfigChangedEvent: + idpEvent = e.IDPConfigChangedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-SAfg2", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPConfigChangedEventType, instance.IDPConfigChangedEventType}) + } + + cols := make([]handler.Column, 0, 4) + if idpEvent.Name != nil { + cols = append(cols, handler.NewCol(IDPTemplateNameCol, *idpEvent.Name)) + } + if idpEvent.AutoRegister != nil { + cols = append(cols, handler.NewCol(IDPTemplateIsAutoCreationCol, *idpEvent.AutoRegister)) + } + cols = append(cols, + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + ) + + return crdb.NewUpdateStatement( + event, + cols, + []handler.Condition{ + handler.NewCond(OIDCIDCol, idpEvent.ConfigID), + handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), nil +} + +func (p *idpTemplateProjection) reduceOldOIDCConfigAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idpconfig.OIDCConfigAddedEvent + switch e := event.(type) { + case *org.IDPOIDCConfigAddedEvent: + idpEvent = e.OIDCConfigAddedEvent + case *instance.IDPOIDCConfigAddedEvent: + idpEvent = e.OIDCConfigAddedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ASFdq2", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPOIDCConfigAddedEventType, instance.IDPOIDCConfigAddedEventType}) + } + + return crdb.NewMultiStatement( + &idpEvent, + crdb.AddUpdateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + }, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(OIDCIDCol, idpEvent.IDPConfigID), + handler.NewCol(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(OIDCIssuerCol, idpEvent.Issuer), + handler.NewCol(OIDCClientIDCol, idpEvent.ClientID), + handler.NewCol(OIDCClientSecretCol, idpEvent.ClientSecret), + handler.NewCol(OIDCScopesCol, database.StringArray(idpEvent.Scopes)), + }, + crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceOldOIDCConfigChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idpconfig.OIDCConfigChangedEvent + switch e := event.(type) { + case *org.IDPOIDCConfigChangedEvent: + idpEvent = e.OIDCConfigChangedEvent + case *instance.IDPOIDCConfigChangedEvent: + idpEvent = e.OIDCConfigChangedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.OIDCIDPChangedEventType, instance.OIDCIDPChangedEventType}) + } + + ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops = append(ops, + crdb.AddUpdateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + }, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + ) + oidcCols := make([]handler.Column, 0, 4) + if idpEvent.ClientID != nil { + oidcCols = append(oidcCols, handler.NewCol(OIDCClientIDCol, *idpEvent.ClientID)) + } + if idpEvent.ClientSecret != nil { + oidcCols = append(oidcCols, handler.NewCol(OIDCClientSecretCol, *idpEvent.ClientSecret)) + } + if idpEvent.Issuer != nil { + oidcCols = append(oidcCols, handler.NewCol(OIDCIssuerCol, *idpEvent.Issuer)) + } + if idpEvent.Scopes != nil { + oidcCols = append(oidcCols, handler.NewCol(OIDCScopesCol, database.StringArray(idpEvent.Scopes))) + } + if len(oidcCols) > 0 { + ops = append(ops, + crdb.AddUpdateStatement( + oidcCols, + []handler.Condition{ + handler.NewCond(OIDCIDCol, idpEvent.IDPConfigID), + handler.NewCond(OIDCInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateOIDCSuffix), + ), + ) + } + + return crdb.NewMultiStatement( + &idpEvent, + ops..., + ), nil +} + +func (p *idpTemplateProjection) reduceOldJWTConfigAdded(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idpconfig.JWTConfigAddedEvent + switch e := event.(type) { + case *org.IDPJWTConfigAddedEvent: + idpEvent = e.JWTConfigAddedEvent + case *instance.IDPJWTConfigAddedEvent: + idpEvent = e.JWTConfigAddedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-ASFdq2", "reduce.wrong.event.type %v", []eventstore.EventType{org.IDPJWTConfigAddedEventType, instance.IDPJWTConfigAddedEventType}) + } + + return crdb.NewMultiStatement( + &idpEvent, + crdb.AddUpdateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + }, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + crdb.AddCreateStatement( + []handler.Column{ + handler.NewCol(JWTIDCol, idpEvent.IDPConfigID), + handler.NewCol(JWTInstanceIDCol, idpEvent.Aggregate().InstanceID), + handler.NewCol(JWTIssuerCol, idpEvent.Issuer), + handler.NewCol(JWTEndpointCol, idpEvent.JWTEndpoint), + handler.NewCol(JWTKeysEndpointCol, idpEvent.KeysEndpoint), + handler.NewCol(JWTHeaderNameCol, idpEvent.HeaderName), + }, + crdb.WithTableSuffix(IDPTemplateJWTSuffix), + ), + ), nil +} + +func (p *idpTemplateProjection) reduceOldJWTConfigChanged(event eventstore.Event) (*handler.Statement, error) { + var idpEvent idpconfig.JWTConfigChangedEvent + switch e := event.(type) { + case *org.IDPJWTConfigChangedEvent: + idpEvent = e.JWTConfigChangedEvent + case *instance.IDPJWTConfigChangedEvent: + idpEvent = e.JWTConfigChangedEvent + default: + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.JWTIDPChangedEventType, instance.JWTIDPChangedEventType}) + } + + ops := make([]func(eventstore.Event) crdb.Exec, 0, 2) + ops = append(ops, + crdb.AddUpdateStatement( + []handler.Column{ + handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()), + handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()), + }, + []handler.Condition{ + handler.NewCond(IDPTemplateIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + ), + ) + jwtCols := make([]handler.Column, 0, 4) + if idpEvent.JWTEndpoint != nil { + jwtCols = append(jwtCols, handler.NewCol(JWTEndpointCol, *idpEvent.JWTEndpoint)) + } + if idpEvent.KeysEndpoint != nil { + jwtCols = append(jwtCols, handler.NewCol(JWTKeysEndpointCol, *idpEvent.KeysEndpoint)) + } + if idpEvent.HeaderName != nil { + jwtCols = append(jwtCols, handler.NewCol(JWTHeaderNameCol, *idpEvent.HeaderName)) + } + if idpEvent.Issuer != nil { + jwtCols = append(jwtCols, handler.NewCol(JWTIssuerCol, *idpEvent.Issuer)) + } + if len(jwtCols) > 0 { + ops = append(ops, + crdb.AddUpdateStatement( + jwtCols, + []handler.Condition{ + handler.NewCond(JWTIDCol, idpEvent.IDPConfigID), + handler.NewCond(JWTInstanceIDCol, idpEvent.Aggregate().InstanceID), + }, + crdb.WithTableSuffix(IDPTemplateJWTSuffix), + ), + ) + } + + return crdb.NewMultiStatement( + &idpEvent, + ops..., + ), nil +} + func (p *idpTemplateProjection) reduceGoogleIDPAdded(event eventstore.Event) (*handler.Statement, error) { var idpEvent idp.GoogleIDPAddedEvent var idpOwnerType domain.IdentityProviderType @@ -621,6 +1177,40 @@ func reduceOAuthIDPChangedColumns(idpEvent idp.OAuthIDPChangedEvent) []handler.C return oauthCols } +func reduceOIDCIDPChangedColumns(idpEvent idp.OIDCIDPChangedEvent) []handler.Column { + oidcCols := make([]handler.Column, 0, 4) + if idpEvent.ClientID != nil { + oidcCols = append(oidcCols, handler.NewCol(OIDCClientIDCol, *idpEvent.ClientID)) + } + if idpEvent.ClientSecret != nil { + oidcCols = append(oidcCols, handler.NewCol(OIDCClientSecretCol, *idpEvent.ClientSecret)) + } + if idpEvent.Issuer != nil { + oidcCols = append(oidcCols, handler.NewCol(OIDCIssuerCol, *idpEvent.Issuer)) + } + if idpEvent.Scopes != nil { + oidcCols = append(oidcCols, handler.NewCol(OIDCScopesCol, database.StringArray(idpEvent.Scopes))) + } + return oidcCols +} + +func reduceJWTIDPChangedColumns(idpEvent idp.JWTIDPChangedEvent) []handler.Column { + jwtCols := make([]handler.Column, 0, 4) + if idpEvent.JWTEndpoint != nil { + jwtCols = append(jwtCols, handler.NewCol(JWTEndpointCol, *idpEvent.JWTEndpoint)) + } + if idpEvent.KeysEndpoint != nil { + jwtCols = append(jwtCols, handler.NewCol(JWTKeysEndpointCol, *idpEvent.KeysEndpoint)) + } + if idpEvent.HeaderName != nil { + jwtCols = append(jwtCols, handler.NewCol(JWTHeaderNameCol, *idpEvent.HeaderName)) + } + if idpEvent.Issuer != nil { + jwtCols = append(jwtCols, handler.NewCol(JWTIssuerCol, *idpEvent.Issuer)) + } + return jwtCols +} + func reduceGoogleIDPChangedColumns(idpEvent idp.GoogleIDPChangedEvent) []handler.Column { googleCols := make([]handler.Column, 0, 3) if idpEvent.ClientID != nil { diff --git a/internal/query/projection/idp_template_test.go b/internal/query/projection/idp_template_test.go index b7b53ff4f..a44d58b71 100644 --- a/internal/query/projection/idp_template_test.go +++ b/internal/query/projection/idp_template_test.go @@ -41,7 +41,7 @@ func TestIDPTemplateProjection_reducesRemove(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_templates WHERE (instance_id = $1)", + expectedStmt: "DELETE FROM projections.idp_templates2 WHERE (instance_id = $1)", expectedArgs: []interface{}{ "agg-id", }, @@ -67,7 +67,7 @@ func TestIDPTemplateProjection_reducesRemove(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", + expectedStmt: "UPDATE projections.idp_templates2 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -99,7 +99,7 @@ func TestIDPTemplateProjection_reducesRemove(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idp_templates WHERE (id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.idp_templates2 WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -144,8 +144,8 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", - "client_id": "client_id", - "client_secret": { + "clientId": "client_id", + "clientSecret": { "cryptoType": 0, "algorithm": "RSA-265", "keyId": "key-id" @@ -169,7 +169,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -188,7 +188,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates_oauth (idp_id, instance_id, client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedStmt: "INSERT INTO projections.idp_templates2_oauth (idp_id, instance_id, client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -213,8 +213,8 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", - "client_id": "client_id", - "client_secret": { + "clientId": "client_id", + "clientSecret": { "cryptoType": 0, "algorithm": "RSA-265", "keyId": "key-id" @@ -238,7 +238,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -257,7 +257,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates_oauth (idp_id, instance_id, client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedStmt: "INSERT INTO projections.idp_templates2_oauth (idp_id, instance_id, client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -282,7 +282,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { []byte(`{ "id": "idp-id", "isCreationAllowed": true, - "client_id": "id" + "clientId": "id" }`), ), instance.OAuthIDPChangedEventMapper), }, @@ -294,7 +294,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.idp_templates2 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ true, anyArg{}, @@ -304,7 +304,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates_oauth SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.idp_templates2_oauth SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "id", "idp-id", @@ -324,8 +324,8 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { []byte(`{ "id": "idp-id", "name": "custom-zitadel-instance", - "client_id": "client_id", - "client_secret": { + "clientId": "client_id", + "clientSecret": { "cryptoType": 0, "algorithm": "RSA-265", "keyId": "key-id" @@ -349,7 +349,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)", + expectedStmt: "UPDATE projections.idp_templates2 SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)", expectedArgs: []interface{}{ "custom-zitadel-instance", true, @@ -363,7 +363,7 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates_oauth SET (client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) = ($1, $2, $3, $4, $5, $6) WHERE (idp_id = $7) AND (instance_id = $8)", + expectedStmt: "UPDATE projections.idp_templates2_oauth SET (client_id, client_secret, authorization_endpoint, token_endpoint, user_endpoint, scopes) = ($1, $2, $3, $4, $5, $6) WHERE (idp_id = $7) AND (instance_id = $8)", expectedArgs: []interface{}{ "client_id", anyArg{}, @@ -413,7 +413,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { instance.AggregateType, []byte(`{ "id": "idp-id", - "clientID": "client_id", + "clientId": "client_id", "clientSecret": { "cryptoType": 0, "algorithm": "RSA-265", @@ -435,7 +435,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -454,7 +454,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates_google (idp_id, instance_id, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.idp_templates2_google (idp_id, instance_id, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -475,7 +475,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { org.AggregateType, []byte(`{ "id": "idp-id", - "clientID": "client_id", + "clientId": "client_id", "clientSecret": { "cryptoType": 0, "algorithm": "RSA-265", @@ -497,7 +497,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -516,7 +516,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates_google (idp_id, instance_id, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.idp_templates2_google (idp_id, instance_id, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -538,7 +538,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { []byte(`{ "id": "idp-id", "isCreationAllowed": true, - "clientID": "id" + "clientId": "id" }`), ), instance.GoogleIDPChangedEventMapper), }, @@ -550,7 +550,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.idp_templates2 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ true, anyArg{}, @@ -560,7 +560,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates_google SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.idp_templates2_google SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "id", "idp-id", @@ -579,7 +579,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { instance.AggregateType, []byte(`{ "id": "idp-id", - "clientID": "client_id", + "clientId": "client_id", "clientSecret": { "cryptoType": 0, "algorithm": "RSA-265", @@ -601,7 +601,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates SET (is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)", + expectedStmt: "UPDATE projections.idp_templates2 SET (is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)", expectedArgs: []interface{}{ true, true, @@ -614,7 +614,7 @@ func TestIDPTemplateProjection_reducesGoogle(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates_google SET (client_id, client_secret, scopes) = ($1, $2, $3) WHERE (idp_id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.idp_templates2_google SET (client_id, client_secret, scopes) = ($1, $2, $3) WHERE (idp_id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ "client_id", anyArg{}, @@ -702,7 +702,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -721,7 +721,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates_ldap (idp_id, instance_id, host, port, tls, base_dn, user_object_class, user_unique_attribute, admin, password, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)", + expectedStmt: "INSERT INTO projections.idp_templates2_ldap (idp_id, instance_id, host, port, tls, base_dn, user_object_class, user_unique_attribute, admin, password, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -801,7 +801,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.idp_templates (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", expectedArgs: []interface{}{ "idp-id", anyArg{}, @@ -820,7 +820,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates_ldap (idp_id, instance_id, host, port, tls, base_dn, user_object_class, user_unique_attribute, admin, password, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)", + expectedStmt: "INSERT INTO projections.idp_templates2_ldap (idp_id, instance_id, host, port, tls, base_dn, user_object_class, user_unique_attribute, admin, password, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -872,7 +872,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.idp_templates2 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ "custom-zitadel-instance", anyArg{}, @@ -882,7 +882,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates_ldap SET host = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.idp_templates2_ldap SET host = $1 WHERE (idp_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "host", "idp-id", @@ -942,7 +942,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)", + expectedStmt: "UPDATE projections.idp_templates2 SET (name, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6, $7) WHERE (id = $8) AND (instance_id = $9)", expectedArgs: []interface{}{ "custom-zitadel-instance", true, @@ -956,7 +956,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates_ldap SET (host, port, tls, base_dn, user_object_class, user_unique_attribute, admin, password, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) WHERE (idp_id = $22) AND (instance_id = $23)", + expectedStmt: "UPDATE projections.idp_templates2_ldap SET (host, port, tls, base_dn, user_object_class, user_unique_attribute, admin, password, id_attribute, first_name_attribute, last_name_attribute, display_name_attribute, nick_name_attribute, preferred_username_attribute, email_attribute, email_verified, phone_attribute, phone_verified_attribute, preferred_language_attribute, avatar_url_attribute, profile_attribute) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) WHERE (idp_id = $22) AND (instance_id = $23)", expectedArgs: []interface{}{ "host", "port", @@ -1004,7 +1004,7 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idp_templates SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", + expectedStmt: "UPDATE projections.idp_templates2 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1032,3 +1032,499 @@ func TestIDPTemplateProjection_reducesLDAP(t *testing.T) { }) } } + +func TestIDPTemplateProjection_reducesOIDC(t *testing.T) { + type args struct { + event func(t *testing.T) eventstore.Event + } + tests := []struct { + name string + args args + reduce func(event eventstore.Event) (*handler.Statement, error) + want wantReduce + }{ + { + name: "instance reduceOIDCIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.OIDCIDPAddedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "issuer": "issuer", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.OIDCIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceOIDCIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "", + domain.IdentityProviderTypeSystem, + domain.IDPTypeOIDC, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates2_oidc (idp_id, instance_id, issuer, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5, $6)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "issuer", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + }, + }, + }, + }, + }, + }, + { + name: "org reduceOIDCIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.OIDCIDPAddedEventType), + org.AggregateType, + []byte(`{ + "id": "idp-id", + "issuer": "issuer", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), org.OIDCIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceOIDCIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "", + domain.IdentityProviderTypeOrg, + domain.IDPTypeOIDC, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates2_oidc (idp_id, instance_id, issuer, client_id, client_secret, scopes) VALUES ($1, $2, $3, $4, $5, $6)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "issuer", + "client_id", + anyArg{}, + database.StringArray{"profile"}, + }, + }, + }, + }, + }, + }, + { + name: "instance reduceOIDCIDPChanged minimal", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.OIDCIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "isCreationAllowed": true, + "clientId": "id" +}`), + ), instance.OIDCIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceOIDCIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates2 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedArgs: []interface{}{ + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates2_oidc SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + "id", + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, + { + name: "instance reduceOIDCIDPChanged", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.OIDCIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "issuer": "issuer", + "clientId": "client_id", + "clientSecret": { + "cryptoType": 0, + "algorithm": "RSA-265", + "keyId": "key-id" + }, + "scopes": ["profile"], + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.OIDCIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceOIDCIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates2 SET (is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)", + expectedArgs: []interface{}{ + true, + true, + true, + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates2_oidc SET (client_id, client_secret, issuer, scopes) = ($1, $2, $3, $4) WHERE (idp_id = $5) AND (instance_id = $6)", + expectedArgs: []interface{}{ + "client_id", + anyArg{}, + "issuer", + database.StringArray{"profile"}, + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + event := baseEvent(t) + got, err := tt.reduce(event) + if !errors.IsErrorInvalidArgument(err) { + 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, IDPTemplateTable, tt.want) + }) + } +} + +func TestIDPTemplateProjection_reducesJWT(t *testing.T) { + type args struct { + event func(t *testing.T) eventstore.Event + } + tests := []struct { + name string + args args + reduce func(event eventstore.Event) (*handler.Statement, error) + want wantReduce + }{ + { + name: "instance reduceJWTIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.JWTIDPAddedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "issuer": "issuer", + "jwtEndpoint": "jwt", + "keysEndpoint": "keys", + "headerName": "header", + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.JWTIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceJWTIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "", + domain.IdentityProviderTypeSystem, + domain.IDPTypeJWT, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates2_jwt (idp_id, instance_id, issuer, jwt_endpoint, keys_endpoint, header_name) VALUES ($1, $2, $3, $4, $5, $6)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "issuer", + "jwt", + "keys", + "header", + }, + }, + }, + }, + }, + }, + { + name: "org reduceJWTIDPAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.JWTIDPAddedEventType), + org.AggregateType, + []byte(`{ + "id": "idp-id", + "issuer": "issuer", + "jwtEndpoint": "jwt", + "keysEndpoint": "keys", + "headerName": "header", + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), org.JWTIDPAddedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceJWTIDPAdded, + want: wantReduce{ + aggregateType: eventstore.AggregateType("org"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO projections.idp_templates2 (id, creation_date, change_date, sequence, resource_owner, instance_id, state, name, owner_type, type, is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)", + expectedArgs: []interface{}{ + "idp-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + "instance-id", + domain.IDPStateActive, + "", + domain.IdentityProviderTypeOrg, + domain.IDPTypeJWT, + true, + true, + true, + true, + }, + }, + { + expectedStmt: "INSERT INTO projections.idp_templates2_jwt (idp_id, instance_id, issuer, jwt_endpoint, keys_endpoint, header_name) VALUES ($1, $2, $3, $4, $5, $6)", + expectedArgs: []interface{}{ + "idp-id", + "instance-id", + "issuer", + "jwt", + "keys", + "header", + }, + }, + }, + }, + }, + }, + { + name: "instance reduceJWTIDPChanged minimal", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.JWTIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "isCreationAllowed": true, + "jwtEndpoint": "jwt" +}`), + ), instance.JWTIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceJWTIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates2 SET (is_creation_allowed, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedArgs: []interface{}{ + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates2_jwt SET jwt_endpoint = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + "jwt", + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, + { + name: "instance reduceJWTIDPChanged", + args: args{ + event: getEvent(testEvent( + repository.EventType(instance.JWTIDPChangedEventType), + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "issuer": "issuer", + "jwtEndpoint": "jwt", + "keysEndpoint": "keys", + "headerName": "header", + "isCreationAllowed": true, + "isLinkingAllowed": true, + "isAutoCreation": true, + "isAutoUpdate": true +}`), + ), instance.JWTIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceJWTIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + previousSequence: 10, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates2 SET (is_creation_allowed, is_linking_allowed, is_auto_creation, is_auto_update, change_date, sequence) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)", + expectedArgs: []interface{}{ + true, + true, + true, + true, + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates2_jwt SET (jwt_endpoint, keys_endpoint, header_name, issuer) = ($1, $2, $3, $4) WHERE (idp_id = $5) AND (instance_id = $6)", + expectedArgs: []interface{}{ + "jwt", + "keys", + "header", + "issuer", + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + event := baseEvent(t) + got, err := tt.reduce(event) + if !errors.IsErrorInvalidArgument(err) { + 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, IDPTemplateTable, tt.want) + }) + } +} diff --git a/internal/repository/idp/google.go b/internal/repository/idp/google.go index 5cff7eb33..4bb427ac8 100644 --- a/internal/repository/idp/google.go +++ b/internal/repository/idp/google.go @@ -14,7 +14,7 @@ type GoogleIDPAddedEvent struct { ID string `json:"id"` Name string `json:"name,omitempty"` - ClientID string `json:"clientID"` + ClientID string `json:"clientId"` ClientSecret *crypto.CryptoValue `json:"clientSecret"` Scopes []string `json:"scopes,omitempty"` Options @@ -66,7 +66,7 @@ type GoogleIDPChangedEvent struct { ID string `json:"id"` Name *string `json:"name,omitempty"` - ClientID *string `json:"clientID,omitempty"` + ClientID *string `json:"clientId,omitempty"` ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` Scopes []string `json:"scopes,omitempty"` OptionChanges diff --git a/internal/repository/idp/idp.go b/internal/repository/idp/idp.go index a2d1a7b98..540e2d243 100644 --- a/internal/repository/idp/idp.go +++ b/internal/repository/idp/idp.go @@ -6,7 +6,6 @@ import ( "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore/repository" - "github.com/zitadel/zitadel/internal/repository/idpconfig" ) type Options struct { @@ -63,19 +62,15 @@ type RemovedEvent struct { eventstore.BaseEvent `json:"-"` ID string `json:"id"` - - name string } func NewRemovedEvent( base *eventstore.BaseEvent, id string, - name string, ) *RemovedEvent { return &RemovedEvent{ BaseEvent: *base, ID: id, - name: name, } } @@ -84,10 +79,7 @@ func (e *RemovedEvent) Data() interface{} { } func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - if e.name == "" { - return nil - } - return []*eventstore.EventUniqueConstraint{idpconfig.NewRemoveIDPConfigNameUniqueConstraint(e.name, e.Aggregate().ResourceOwner)} + return nil } func RemovedEventMapper(event *repository.Event) (eventstore.Event, error) { diff --git a/internal/repository/idp/jwt.go b/internal/repository/idp/jwt.go new file mode 100644 index 000000000..076c10909 --- /dev/null +++ b/internal/repository/idp/jwt.go @@ -0,0 +1,153 @@ +package idp + +import ( + "encoding/json" + + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/repository" +) + +type JWTIDPAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name string `json:"name,omitempty"` + Issuer string `json:"issuer,omitempty"` + JWTEndpoint string `json:"jwtEndpoint,omitempty"` + KeysEndpoint string `json:"keysEndpoint,omitempty"` + HeaderName string `json:"headerName,omitempty"` + Options +} + +func NewJWTIDPAddedEvent( + base *eventstore.BaseEvent, + id, + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName string, + options Options, +) *JWTIDPAddedEvent { + return &JWTIDPAddedEvent{ + BaseEvent: *base, + ID: id, + Name: name, + Issuer: issuer, + JWTEndpoint: jwtEndpoint, + KeysEndpoint: keysEndpoint, + HeaderName: headerName, + Options: options, + } +} + +func (e *JWTIDPAddedEvent) Data() interface{} { + return e +} + +func (e *JWTIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func JWTIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &JWTIDPAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-Et1dq", "unable to unmarshal event") + } + + return e, nil +} + +type JWTIDPChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name *string `json:"name,omitempty"` + Issuer *string `json:"issuer,omitempty"` + JWTEndpoint *string `json:"jwtEndpoint,omitempty"` + KeysEndpoint *string `json:"keysEndpoint,omitempty"` + HeaderName *string `json:"headerName,omitempty"` + OptionChanges +} + +func NewJWTIDPChangedEvent( + base *eventstore.BaseEvent, + id string, + changes []JWTIDPChanges, +) (*JWTIDPChangedEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "IDP-BH3dl", "Errors.NoChangesFound") + } + changedEvent := &JWTIDPChangedEvent{ + BaseEvent: *base, + ID: id, + } + for _, change := range changes { + change(changedEvent) + } + return changedEvent, nil +} + +type JWTIDPChanges func(*JWTIDPChangedEvent) + +func ChangeJWTName(name string) func(*JWTIDPChangedEvent) { + return func(e *JWTIDPChangedEvent) { + e.Name = &name + } +} + +func ChangeJWTIssuer(issuer string) func(*JWTIDPChangedEvent) { + return func(e *JWTIDPChangedEvent) { + e.Issuer = &issuer + } +} + +func ChangeJWTEndpoint(jwtEndpoint string) func(*JWTIDPChangedEvent) { + return func(e *JWTIDPChangedEvent) { + e.JWTEndpoint = &jwtEndpoint + } +} + +func ChangeJWTKeysEndpoint(keysEndpoint string) func(*JWTIDPChangedEvent) { + return func(e *JWTIDPChangedEvent) { + e.KeysEndpoint = &keysEndpoint + } +} + +func ChangeJWTHeaderName(headerName string) func(*JWTIDPChangedEvent) { + return func(e *JWTIDPChangedEvent) { + e.HeaderName = &headerName + } +} + +func ChangeJWTOptions(options OptionChanges) func(*JWTIDPChangedEvent) { + return func(e *JWTIDPChangedEvent) { + e.OptionChanges = options + } +} + +func (e *JWTIDPChangedEvent) Data() interface{} { + return e +} + +func (e *JWTIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &JWTIDPChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-D3gjzh", "unable to unmarshal event") + } + + return e, nil +} diff --git a/internal/repository/idp/oauth.go b/internal/repository/idp/oauth.go index 6fa6979cc..85271818e 100644 --- a/internal/repository/idp/oauth.go +++ b/internal/repository/idp/oauth.go @@ -14,8 +14,8 @@ type OAuthIDPAddedEvent struct { ID string `json:"id"` Name string `json:"name,omitempty"` - ClientID string `json:"client_id,omitempty"` - ClientSecret *crypto.CryptoValue `json:"client_secret,omitempty"` + ClientID string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` AuthorizationEndpoint string `json:"authorizationEndpoint,omitempty"` TokenEndpoint string `json:"tokenEndpoint,omitempty"` UserEndpoint string `json:"userEndpoint,omitempty"` @@ -75,8 +75,8 @@ type OAuthIDPChangedEvent struct { ID string `json:"id"` Name *string `json:"name,omitempty"` - ClientID *string `json:"client_id,omitempty"` - ClientSecret *crypto.CryptoValue `json:"client_secret,omitempty"` + ClientID *string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` AuthorizationEndpoint *string `json:"authorizationEndpoint,omitempty"` TokenEndpoint *string `json:"tokenEndpoint,omitempty"` UserEndpoint *string `json:"userEndpoint,omitempty"` diff --git a/internal/repository/idp/oidc.go b/internal/repository/idp/oidc.go new file mode 100644 index 000000000..630e32515 --- /dev/null +++ b/internal/repository/idp/oidc.go @@ -0,0 +1,154 @@ +package idp + +import ( + "encoding/json" + + "github.com/zitadel/zitadel/internal/crypto" + "github.com/zitadel/zitadel/internal/errors" + "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/eventstore/repository" +) + +type OIDCIDPAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name string `json:"name"` + Issuer string `json:"issuer"` + ClientID string `json:"clientId"` + ClientSecret *crypto.CryptoValue `json:"clientSecret"` + Scopes []string `json:"scopes,omitempty"` + Options +} + +func NewOIDCIDPAddedEvent( + base *eventstore.BaseEvent, + id, + name, + issuer, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options Options, +) *OIDCIDPAddedEvent { + return &OIDCIDPAddedEvent{ + BaseEvent: *base, + ID: id, + Name: name, + Issuer: issuer, + ClientID: clientID, + ClientSecret: clientSecret, + Scopes: scopes, + Options: options, + } +} + +func (e *OIDCIDPAddedEvent) Data() interface{} { + return e +} + +func (e *OIDCIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func OIDCIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &OIDCIDPAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-Et1dq", "unable to unmarshal event") + } + + return e, nil +} + +type OIDCIDPChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + ID string `json:"id"` + Name *string `json:"name,omitempty"` + Issuer *string `json:"issuer,omitempty"` + ClientID *string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + Scopes []string `json:"scopes,omitempty"` + OptionChanges +} + +func NewOIDCIDPChangedEvent( + base *eventstore.BaseEvent, + id string, + changes []OIDCIDPChanges, +) (*OIDCIDPChangedEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "IDP-BH3dl", "Errors.NoChangesFound") + } + changedEvent := &OIDCIDPChangedEvent{ + BaseEvent: *base, + ID: id, + } + for _, change := range changes { + change(changedEvent) + } + return changedEvent, nil +} + +type OIDCIDPChanges func(*OIDCIDPChangedEvent) + +func ChangeOIDCName(name string) func(*OIDCIDPChangedEvent) { + return func(e *OIDCIDPChangedEvent) { + e.Name = &name + } +} + +func ChangeOIDCIssuer(issuer string) func(*OIDCIDPChangedEvent) { + return func(e *OIDCIDPChangedEvent) { + e.Issuer = &issuer + } +} + +func ChangeOIDCClientID(clientID string) func(*OIDCIDPChangedEvent) { + return func(e *OIDCIDPChangedEvent) { + e.ClientID = &clientID + } +} + +func ChangeOIDCClientSecret(clientSecret *crypto.CryptoValue) func(*OIDCIDPChangedEvent) { + return func(e *OIDCIDPChangedEvent) { + e.ClientSecret = clientSecret + } +} + +func ChangeOIDCOptions(options OptionChanges) func(*OIDCIDPChangedEvent) { + return func(e *OIDCIDPChangedEvent) { + e.OptionChanges = options + } +} + +func ChangeOIDCScopes(scopes []string) func(*OIDCIDPChangedEvent) { + return func(e *OIDCIDPChangedEvent) { + e.Scopes = scopes + } +} + +func (e *OIDCIDPChangedEvent) Data() interface{} { + return e +} + +func (e *OIDCIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e := &OIDCIDPChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "IDP-D3gjzh", "unable to unmarshal event") + } + + return e, nil +} diff --git a/internal/repository/instance/eventstore.go b/internal/repository/instance/eventstore.go index 66ee70f54..6d552f52e 100644 --- a/internal/repository/instance/eventstore.go +++ b/internal/repository/instance/eventstore.go @@ -72,6 +72,10 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, IDPJWTConfigChangedEventType, IDPJWTConfigChangedEventMapper). RegisterFilterEventMapper(AggregateType, OAuthIDPAddedEventType, OAuthIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, OAuthIDPChangedEventType, OAuthIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, OIDCIDPAddedEventType, OIDCIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, OIDCIDPChangedEventType, OIDCIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, JWTIDPAddedEventType, JWTIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, JWTIDPChangedEventType, JWTIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, GoogleIDPAddedEventType, GoogleIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, GoogleIDPChangedEventType, GoogleIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, LDAPIDPAddedEventType, LDAPIDPAddedEventMapper). diff --git a/internal/repository/instance/idp.go b/internal/repository/instance/idp.go index d8d390a09..9dafb3225 100644 --- a/internal/repository/instance/idp.go +++ b/internal/repository/instance/idp.go @@ -12,6 +12,10 @@ import ( const ( OAuthIDPAddedEventType eventstore.EventType = "instance.idp.oauth.added" OAuthIDPChangedEventType eventstore.EventType = "instance.idp.oauth.changed" + OIDCIDPAddedEventType eventstore.EventType = "instance.idp.oidc.added" + OIDCIDPChangedEventType eventstore.EventType = "instance.idp.oidc.changed" + JWTIDPAddedEventType eventstore.EventType = "instance.idp.jwt.added" + JWTIDPChangedEventType eventstore.EventType = "instance.idp.jwt.changed" GoogleIDPAddedEventType eventstore.EventType = "instance.idp.google.added" GoogleIDPChangedEventType eventstore.EventType = "instance.idp.google.changed" LDAPIDPAddedEventType eventstore.EventType = "instance.idp.ldap.added" @@ -101,6 +105,162 @@ func OAuthIDPChangedEventMapper(event *repository.Event) (eventstore.Event, erro return &OAuthIDPChangedEvent{OAuthIDPChangedEvent: *e.(*idp.OAuthIDPChangedEvent)}, nil } +type OIDCIDPAddedEvent struct { + idp.OIDCIDPAddedEvent +} + +func NewOIDCIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *OIDCIDPAddedEvent { + + return &OIDCIDPAddedEvent{ + OIDCIDPAddedEvent: *idp.NewOIDCIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + OIDCIDPAddedEventType, + ), + id, + name, + issuer, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func OIDCIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.OIDCIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPAddedEvent{OIDCIDPAddedEvent: *e.(*idp.OIDCIDPAddedEvent)}, nil +} + +type OIDCIDPChangedEvent struct { + idp.OIDCIDPChangedEvent +} + +func NewOIDCIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.OIDCIDPChanges, +) (*OIDCIDPChangedEvent, error) { + + changedEvent, err := idp.NewOIDCIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + OIDCIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &OIDCIDPChangedEvent{OIDCIDPChangedEvent: *changedEvent}, nil +} + +func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.OIDCIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPChangedEvent{OIDCIDPChangedEvent: *e.(*idp.OIDCIDPChangedEvent)}, nil +} + +type JWTIDPAddedEvent struct { + idp.JWTIDPAddedEvent +} + +func NewJWTIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName string, + options idp.Options, +) *JWTIDPAddedEvent { + + return &JWTIDPAddedEvent{ + JWTIDPAddedEvent: *idp.NewJWTIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + JWTIDPAddedEventType, + ), + id, + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName, + options, + ), + } +} + +func JWTIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.JWTIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &JWTIDPAddedEvent{JWTIDPAddedEvent: *e.(*idp.JWTIDPAddedEvent)}, nil +} + +type JWTIDPChangedEvent struct { + idp.JWTIDPChangedEvent +} + +func NewJWTIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.JWTIDPChanges, +) (*JWTIDPChangedEvent, error) { + + changedEvent, err := idp.NewJWTIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + JWTIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &JWTIDPChangedEvent{JWTIDPChangedEvent: *changedEvent}, nil +} + +func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.JWTIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &JWTIDPChangedEvent{JWTIDPChangedEvent: *e.(*idp.JWTIDPChangedEvent)}, nil +} + type GoogleIDPAddedEvent struct { idp.GoogleIDPAddedEvent } @@ -275,7 +435,6 @@ func NewIDPRemovedEvent( ctx context.Context, aggregate *eventstore.Aggregate, id string, - name string, ) *IDPRemovedEvent { return &IDPRemovedEvent{ RemovedEvent: *idp.NewRemovedEvent( @@ -285,7 +444,6 @@ func NewIDPRemovedEvent( IDPRemovedEventType, ), id, - name, ), } } diff --git a/internal/repository/org/eventstore.go b/internal/repository/org/eventstore.go index 5652a2aa7..f9ac6e76e 100644 --- a/internal/repository/org/eventstore.go +++ b/internal/repository/org/eventstore.go @@ -80,6 +80,10 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(AggregateType, IDPJWTConfigChangedEventType, IDPJWTConfigChangedEventMapper). RegisterFilterEventMapper(AggregateType, OAuthIDPAddedEventType, OAuthIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, OAuthIDPChangedEventType, OAuthIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, OIDCIDPAddedEventType, OIDCIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, OIDCIDPChangedEventType, OIDCIDPChangedEventMapper). + RegisterFilterEventMapper(AggregateType, JWTIDPAddedEventType, JWTIDPAddedEventMapper). + RegisterFilterEventMapper(AggregateType, JWTIDPChangedEventType, JWTIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, GoogleIDPAddedEventType, GoogleIDPAddedEventMapper). RegisterFilterEventMapper(AggregateType, GoogleIDPChangedEventType, GoogleIDPChangedEventMapper). RegisterFilterEventMapper(AggregateType, LDAPIDPAddedEventType, LDAPIDPAddedEventMapper). diff --git a/internal/repository/org/idp.go b/internal/repository/org/idp.go index d360e2cb2..f0dda3efe 100644 --- a/internal/repository/org/idp.go +++ b/internal/repository/org/idp.go @@ -12,6 +12,10 @@ import ( const ( OAuthIDPAddedEventType eventstore.EventType = "org.idp.oauth.added" OAuthIDPChangedEventType eventstore.EventType = "org.idp.oauth.changed" + OIDCIDPAddedEventType eventstore.EventType = "org.idp.oidc.added" + OIDCIDPChangedEventType eventstore.EventType = "org.idp.oidc.changed" + JWTIDPAddedEventType eventstore.EventType = "org.idp.jwt.added" + JWTIDPChangedEventType eventstore.EventType = "org.idp.jwt.changed" GoogleIDPAddedEventType eventstore.EventType = "org.idp.google.added" GoogleIDPChangedEventType eventstore.EventType = "org.idp.google.changed" LDAPIDPAddedEventType eventstore.EventType = "org.idp.ldap.added" @@ -101,6 +105,162 @@ func OAuthIDPChangedEventMapper(event *repository.Event) (eventstore.Event, erro return &OAuthIDPChangedEvent{OAuthIDPChangedEvent: *e.(*idp.OAuthIDPChangedEvent)}, nil } +type OIDCIDPAddedEvent struct { + idp.OIDCIDPAddedEvent +} + +func NewOIDCIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + clientID string, + clientSecret *crypto.CryptoValue, + scopes []string, + options idp.Options, +) *OIDCIDPAddedEvent { + + return &OIDCIDPAddedEvent{ + OIDCIDPAddedEvent: *idp.NewOIDCIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + OIDCIDPAddedEventType, + ), + id, + name, + issuer, + clientID, + clientSecret, + scopes, + options, + ), + } +} + +func OIDCIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.OIDCIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPAddedEvent{OIDCIDPAddedEvent: *e.(*idp.OIDCIDPAddedEvent)}, nil +} + +type OIDCIDPChangedEvent struct { + idp.OIDCIDPChangedEvent +} + +func NewOIDCIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.OIDCIDPChanges, +) (*OIDCIDPChangedEvent, error) { + + changedEvent, err := idp.NewOIDCIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + OIDCIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &OIDCIDPChangedEvent{OIDCIDPChangedEvent: *changedEvent}, nil +} + +func OIDCIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.OIDCIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &OIDCIDPChangedEvent{OIDCIDPChangedEvent: *e.(*idp.OIDCIDPChangedEvent)}, nil +} + +type JWTIDPAddedEvent struct { + idp.JWTIDPAddedEvent +} + +func NewJWTIDPAddedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id, + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName string, + options idp.Options, +) *JWTIDPAddedEvent { + + return &JWTIDPAddedEvent{ + JWTIDPAddedEvent: *idp.NewJWTIDPAddedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + JWTIDPAddedEventType, + ), + id, + name, + issuer, + jwtEndpoint, + keysEndpoint, + headerName, + options, + ), + } +} + +func JWTIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.JWTIDPAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &JWTIDPAddedEvent{JWTIDPAddedEvent: *e.(*idp.JWTIDPAddedEvent)}, nil +} + +type JWTIDPChangedEvent struct { + idp.JWTIDPChangedEvent +} + +func NewJWTIDPChangedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + id string, + changes []idp.JWTIDPChanges, +) (*JWTIDPChangedEvent, error) { + + changedEvent, err := idp.NewJWTIDPChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + JWTIDPChangedEventType, + ), + id, + changes, + ) + if err != nil { + return nil, err + } + return &JWTIDPChangedEvent{JWTIDPChangedEvent: *changedEvent}, nil +} + +func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) { + e, err := idp.JWTIDPChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &JWTIDPChangedEvent{JWTIDPChangedEvent: *e.(*idp.JWTIDPChangedEvent)}, nil +} + type GoogleIDPAddedEvent struct { idp.GoogleIDPAddedEvent } @@ -275,7 +435,6 @@ func NewIDPRemovedEvent( ctx context.Context, aggregate *eventstore.Aggregate, id string, - name string, ) *IDPRemovedEvent { return &IDPRemovedEvent{ RemovedEvent: *idp.NewRemovedEvent( @@ -285,7 +444,6 @@ func NewIDPRemovedEvent( IDPRemovedEventType, ), id, - name, ), } } diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 895862681..2384d28dc 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -1272,6 +1272,54 @@ service AdminService { }; } + // Add a new OIDC identity provider on the instance + rpc AddGenericOIDCProvider(AddGenericOIDCProviderRequest) returns (AddGenericOIDCProviderResponse) { + option (google.api.http) = { + post: "/idps/generic_oidc" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Change an existing OIDC identity provider on the instance + rpc UpdateGenericOIDCProvider(UpdateGenericOIDCProviderRequest) returns (UpdateGenericOIDCProviderResponse) { + option (google.api.http) = { + put: "/idps/generic_oidc/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Add a new JWT identity provider on the instance + rpc AddJWTProvider(AddJWTProviderRequest) returns (AddJWTProviderResponse) { + option (google.api.http) = { + post: "/idps/generic_jwt" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + + // Change an existing JWT identity provider on the instance + rpc UpdateJWTProvider(UpdateJWTProviderRequest) returns (UpdateJWTProviderResponse) { + option (google.api.http) = { + put: "/idps/generic_jwt/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.idp.write" + }; + } + // Add a new Google identity provider on the instance rpc AddGoogleProvider(AddGoogleProviderRequest) returns (AddGoogleProviderResponse) { option (google.api.http) = { @@ -4316,6 +4364,63 @@ message UpdateGenericOAuthProviderResponse { zitadel.v1.ObjectDetails details = 1; } +message AddGenericOIDCProviderRequest { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 5 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 6; +} + +message AddGenericOIDCProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGenericOIDCProviderRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_id = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + // client_secret will only be updated if provided + string client_secret = 5 [(validate.rules).string = {max_len: 200}]; + repeated string scopes = 6 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 7; +} + +message UpdateGenericOIDCProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message AddJWTProviderRequest { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string jwt_endpoint = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string keys_endpoint = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string header_name = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + zitadel.idp.v1.Options provider_options = 6 [(validate.rules).message = {required: true}]; +} + +message AddJWTProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateJWTProviderRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string jwt_endpoint = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string keys_endpoint = 5 [(validate.rules).string = {max_len: 200}]; + string header_name = 6 [(validate.rules).string = {min_len: 1, max_len: 200}]; + zitadel.idp.v1.Options provider_options = 7 [(validate.rules).message = {required: true}]; +} + +message UpdateJWTProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + message AddGoogleProviderRequest { // Google will be used as default, if no name is provided string name = 1 [(validate.rules).string = {max_len: 200}]; diff --git a/proto/zitadel/idp.proto b/proto/zitadel/idp.proto index fdfe2edaf..3cb50afbb 100644 --- a/proto/zitadel/idp.proto +++ b/proto/zitadel/idp.proto @@ -264,6 +264,8 @@ message ProviderConfig { LDAPConfig ldap = 2; GoogleConfig google = 3; OAuthConfig oauth = 4; + GenericOIDCConfig oidc = 5; + JWTConfig jwt = 6; } } message OAuthConfig { @@ -274,6 +276,12 @@ message OAuthConfig { repeated string scopes = 5; } +message GenericOIDCConfig { + string issuer = 1; + string client_id = 2; + repeated string scopes = 3; +} + message GoogleConfig { string client_id = 1; repeated string scopes = 2; diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index ba3f02be9..1c9c57ae5 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -4393,6 +4393,54 @@ service ManagementService { }; } + // Add a new OIDC identity provider in the organisation + rpc AddGenericOIDCProvider(AddGenericOIDCProviderRequest) returns (AddGenericOIDCProviderResponse) { + option (google.api.http) = { + post: "/idps/generic_oidc" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Change an existing OIDC identity provider in the organisation + rpc UpdateGenericOIDCProvider(UpdateGenericOIDCProviderRequest) returns (UpdateGenericOIDCProviderResponse) { + option (google.api.http) = { + put: "/idps/generic_oidc/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Add a new JWT identity provider in the organisation + rpc AddJWTProvider(AddJWTProviderRequest) returns (AddJWTProviderResponse) { + option (google.api.http) = { + post: "/idps/generic_jwt" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + + // Change an existing JWT identity provider in the organisation + rpc UpdateJWTProvider(UpdateJWTProviderRequest) returns (UpdateJWTProviderResponse) { + option (google.api.http) = { + put: "/idps/generic_jwt/{id}" + body: "*" + }; + + option (zitadel.v1.auth_option) = { + permission: "org.idp.write" + }; + } + // Add a new Google identity provider in the organisation rpc AddGoogleProvider(AddGoogleProviderRequest) returns (AddGoogleProviderResponse) { option (google.api.http) = { @@ -7930,6 +7978,63 @@ message UpdateGenericOAuthProviderResponse { zitadel.v1.ObjectDetails details = 1; } +message AddGenericOIDCProviderRequest { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_secret = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + repeated string scopes = 5 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 6; +} + +message AddGenericOIDCProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateGenericOIDCProviderRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string client_id = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + // client_secret will only be updated if provided + string client_secret = 5 [(validate.rules).string = {max_len: 200}]; + repeated string scopes = 6 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}]; + zitadel.idp.v1.Options provider_options = 7; +} + +message UpdateGenericOIDCProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + +message AddJWTProviderRequest { + string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string jwt_endpoint = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string keys_endpoint = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string header_name = 5 [(validate.rules).string = {min_len: 1, max_len: 200}]; + zitadel.idp.v1.Options provider_options = 6; +} + +message AddJWTProviderResponse { + zitadel.v1.ObjectDetails details = 1; + string id = 2; +} + +message UpdateJWTProviderRequest { + string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string issuer = 3 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string jwt_endpoint = 4 [(validate.rules).string = {min_len: 1, max_len: 200}]; + string keys_endpoint = 5 [(validate.rules).string = {max_len: 200}]; + string header_name = 6 [(validate.rules).string = {min_len: 1, max_len: 200}]; + zitadel.idp.v1.Options provider_options = 7; +} + +message UpdateJWTProviderResponse { + zitadel.v1.ObjectDetails details = 1; +} + message AddGoogleProviderRequest { // Google will be used as default, if no name is provided string name = 1 [(validate.rules).string = {max_len: 200}];