mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 21:07:22 +00:00
perf(oidc): optimize the introspection endpoint (#6909)
* get key by id and cache them * userinfo from events for v2 tokens * improve keyset caching * concurrent token and client checks * client and project in single query * logging and otel * drop owner_removed column on apps and authN tables * userinfo and project roles in go routines * get oidc user info from projections and add actions * add avatar URL * some cleanup * pull oidc work branch * remove storage from server * add config flag for experimental introspection * legacy introspection flag * drop owner_removed column on user projections * drop owner_removed column on useer_metadata * query userinfo unit test * query introspection client test * add user_grants to the userinfo query * handle PAT scopes * bring triggers back * test instance keys query * add userinfo unit tests * unit test keys * go mod tidy * solve some bugs * fix missing preferred login name * do not run triggers in go routines, they seem to deadlock * initialize the trigger handlers late with a sync.OnceValue * Revert "do not run triggers in go routines, they seem to deadlock" This reverts commit 2a03da2127b7dc74552ec25d4772282a82cc1cba. * add missing translations * chore: update go version for linting * pin oidc version * parse a global time location for query test * fix linter complains * upgrade go lint * fix more linting issues --------- Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
This commit is contained in:
parent
ad3563d58b
commit
ba9b807854
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -64,7 +64,7 @@ jobs:
|
||||
go_version: "1.21"
|
||||
node_version: "18"
|
||||
buf_version: "latest"
|
||||
go_lint_version: "v1.53.2"
|
||||
go_lint_version: "v1.55.2"
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
|
||||
|
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -85,7 +85,7 @@ jobs:
|
||||
-
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ github.event.inputs.go_version }}
|
||||
go-version: ${{ inputs.go_version }}
|
||||
-
|
||||
uses: actions/cache/restore@v3
|
||||
timeout-minutes: 1
|
||||
|
@ -314,6 +314,11 @@ OIDC:
|
||||
Path: /oauth/v2/device_authorization # ZITADEL_OIDC_CUSTOMENDPOINTS_DEVICEAUTH_PATH
|
||||
DefaultLoginURLV2: "/login?authRequest=" # ZITADEL_OIDC_DEFAULTLOGINURLV2
|
||||
DefaultLogoutURLV2: "/logout?post_logout_redirect=" # ZITADEL_OIDC_DEFAULTLOGOUTURLV2
|
||||
Features:
|
||||
# Wheter projection triggers are used in the new Introspection implementation.
|
||||
TriggerIntrospectionProjections: false
|
||||
# Allows fallback to the Legacy Introspection implementation
|
||||
LegacyIntrospection: false
|
||||
|
||||
SAML:
|
||||
ProviderConfig:
|
||||
|
10
go.mod
10
go.mod
@ -61,19 +61,19 @@ require (
|
||||
github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203
|
||||
github.com/ttacon/libphonenumber v1.2.1
|
||||
github.com/zitadel/logging v0.5.0
|
||||
github.com/zitadel/oidc/v3 v3.2.1
|
||||
github.com/zitadel/oidc/v3 v3.4.0
|
||||
github.com/zitadel/passwap v0.4.0
|
||||
github.com/zitadel/saml v0.1.2
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0
|
||||
go.opentelemetry.io/otel v1.20.0
|
||||
go.opentelemetry.io/otel v1.21.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.43.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.20.0
|
||||
go.opentelemetry.io/otel/metric v1.20.0
|
||||
go.opentelemetry.io/otel/metric v1.21.0
|
||||
go.opentelemetry.io/otel/sdk v1.20.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.20.0
|
||||
go.opentelemetry.io/otel/trace v1.20.0
|
||||
go.opentelemetry.io/otel/trace v1.21.0
|
||||
golang.org/x/crypto v0.15.0
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678
|
||||
golang.org/x/net v0.18.0
|
||||
@ -168,7 +168,7 @@ require (
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.4.0
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7 // indirect
|
||||
github.com/kevinburke/rest v0.0.0-20231107185522-a9c371f90234 // indirect
|
||||
|
16
go.sum
16
go.sum
@ -866,8 +866,8 @@ github.com/zenazn/goji v1.0.1 h1:4lbD8Mx2h7IvloP7r2C0D6ltZP6Ufip8Hn0wmSK5LR8=
|
||||
github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/zitadel/logging v0.5.0 h1:Kunouvqse/efXy4UDvFw5s3vP+Z4AlHo3y8wF7stXHA=
|
||||
github.com/zitadel/logging v0.5.0/go.mod h1:IzP5fzwFhzzyxHkSmfF8dsyqFsQRJLLcQmwhIBzlGsE=
|
||||
github.com/zitadel/oidc/v3 v3.2.1 h1:1Nw8D/vBM8huFteBYdG8777/OQd0m+mfB3U5JUr61ZA=
|
||||
github.com/zitadel/oidc/v3 v3.2.1/go.mod h1:1snUiV6wqAWJ1psTrVrBpBittxf3WNoLwm0KCf6S8C4=
|
||||
github.com/zitadel/oidc/v3 v3.4.0 h1:JkbNnrk/7IG+NOBoZp/P0kx6tPcBvnCekSqDTPCOok4=
|
||||
github.com/zitadel/oidc/v3 v3.4.0/go.mod h1:jUnLnx5ihKlo88cSEduZkKlzeMrjzcWVZ8fTzKBxZKY=
|
||||
github.com/zitadel/passwap v0.4.0 h1:cMaISx+Ve7ilgG7Q8xOli4Z6IWr8Gndss+jeBk5A3O0=
|
||||
github.com/zitadel/passwap v0.4.0/go.mod h1:yHaDM4A68yRkdic5BZ4iUNoc19hT+kYt8n1/Nz+I87g=
|
||||
github.com/zitadel/saml v0.1.2 h1:RICwNTuP2upX4A1sZ8iq1rv4/x3DhZHzFx1e5bTKoTo=
|
||||
@ -890,8 +890,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 h1:1eHu3/pUSWaOgltNK3WJFaywKsTIr/PwvHyDmi0lQA0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0/go.mod h1:HyABWq60Uy1kjJSa2BVOxUVao8Cdick5AWSKPutqy6U=
|
||||
go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
|
||||
go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU=
|
||||
@ -900,14 +900,14 @@ go.opentelemetry.io/otel/exporters/prometheus v0.43.0 h1:Skkl6akzvdWweXX6LLAY29t
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.43.0/go.mod h1:nZStMoc1H/YJpRjSx9IEX4abBMekORTLQcTUT1CgLkg=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.20.0 h1:4s9HxB4azeeQkhY0GE5wZlMj4/pz8tE5gx2OQpGUw58=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.20.0/go.mod h1:djVA3TUJ2fSdMX0JE5XxFBOaZzprElJoP7fD4vnV2SU=
|
||||
go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA=
|
||||
go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM=
|
||||
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM=
|
||||
go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.20.0 h1:5eD40l/H2CqdKmbSV7iht2KMK0faAIL2pVYzJOWobGk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.20.0/go.mod h1:AGvpC+YF/jblITiafMTYgvRBUiwi9hZf0EYE2E5XlS8=
|
||||
go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ=
|
||||
go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU=
|
||||
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
|
@ -34,6 +34,27 @@ func UserMetadataListFromQuery(c *actions.FieldConfig, metadata *query.UserMetad
|
||||
return c.Runtime.ToValue(result)
|
||||
}
|
||||
|
||||
func UserMetadataListFromSlice(c *actions.FieldConfig, metadata []query.UserMetadata) goja.Value {
|
||||
result := &userMetadataList{
|
||||
// Count was the only field ever queried from the DB in the old implementation,
|
||||
// so Sequence and LastRun are omitted.
|
||||
Count: uint64(len(metadata)),
|
||||
Metadata: make([]*userMetadata, len(metadata)),
|
||||
}
|
||||
for i, md := range metadata {
|
||||
result.Metadata[i] = &userMetadata{
|
||||
CreationDate: md.CreationDate,
|
||||
ChangeDate: md.ChangeDate,
|
||||
ResourceOwner: md.ResourceOwner,
|
||||
Sequence: md.Sequence,
|
||||
Key: md.Key,
|
||||
Value: metadataByteArrayToValue(md.Value, c.Runtime),
|
||||
}
|
||||
}
|
||||
|
||||
return c.Runtime.ToValue(result)
|
||||
}
|
||||
|
||||
func metadataByteArrayToValue(val []byte, runtime *goja.Runtime) goja.Value {
|
||||
var value interface{}
|
||||
if !json.Valid(val) {
|
||||
|
@ -90,6 +90,36 @@ func UserGrantsFromQuery(c *actions.FieldConfig, userGrants *query.UserGrants) g
|
||||
return c.Runtime.ToValue(grantList)
|
||||
}
|
||||
|
||||
func UserGrantsFromSlice(c *actions.FieldConfig, userGrants []query.UserGrant) goja.Value {
|
||||
if userGrants == nil {
|
||||
return c.Runtime.ToValue(nil)
|
||||
}
|
||||
grantList := &userGrantList{
|
||||
Count: uint64(len(userGrants)),
|
||||
Grants: make([]*userGrant, len(userGrants)),
|
||||
}
|
||||
|
||||
for i, grant := range userGrants {
|
||||
grantList.Grants[i] = &userGrant{
|
||||
Id: grant.ID,
|
||||
ProjectGrantId: grant.GrantID,
|
||||
State: grant.State,
|
||||
CreationDate: grant.CreationDate,
|
||||
ChangeDate: grant.ChangeDate,
|
||||
Sequence: grant.Sequence,
|
||||
UserId: grant.UserID,
|
||||
Roles: grant.Roles,
|
||||
UserResourceOwner: grant.UserResourceOwner,
|
||||
UserGrantResourceOwner: grant.ResourceOwner,
|
||||
UserGrantResourceOwnerName: grant.OrgName,
|
||||
ProjectId: grant.ProjectID,
|
||||
ProjectName: grant.ProjectName,
|
||||
}
|
||||
}
|
||||
|
||||
return c.Runtime.ToValue(grantList)
|
||||
}
|
||||
|
||||
func UserGrantsToDomain(userID string, actionUserGrants []UserGrant) []*domain.UserGrant {
|
||||
if actionUserGrants == nil {
|
||||
return nil
|
||||
|
@ -549,7 +549,7 @@ func (s *Server) getUsers(ctx context.Context, org string, withPasswords bool, w
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
users, err := s.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: []query.SearchQuery{orgSearch}}, false)
|
||||
users, err := s.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: []query.SearchQuery{orgSearch}})
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
@ -630,7 +630,7 @@ func (s *Server) getUsers(ctx context.Context, org string, withPasswords bool, w
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
keys, err := s.query.SearchAuthNKeysData(ctx, &query.AuthNKeySearchQueries{Queries: []query.SearchQuery{userIDQuery, orgIDQuery}}, false)
|
||||
keys, err := s.query.SearchAuthNKeysData(ctx, &query.AuthNKeySearchQueries{Queries: []query.SearchQuery{userIDQuery, orgIDQuery}})
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
@ -836,7 +836,7 @@ func (s *Server) getProjectsAndApps(ctx context.Context, org string) ([]*v1_pb.D
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, err
|
||||
}
|
||||
keys, err := s.query.SearchAuthNKeysData(ctx, &query.AuthNKeySearchQueries{Queries: []query.SearchQuery{appIDQuery, projectIDQuery, orgIDQuery}}, false)
|
||||
keys, err := s.query.SearchAuthNKeysData(ctx, &query.AuthNKeySearchQueries{Queries: []query.SearchQuery{appIDQuery, projectIDQuery, orgIDQuery}})
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ func (s *Server) getClaimedUserIDsOfOrgDomain(ctx context.Context, orgDomain str
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users, err := s.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: []query.SearchQuery{loginName}}, false)
|
||||
users, err := s.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: []query.SearchQuery{loginName}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) GetMyEmail(ctx context.Context, _ *auth_pb.GetMyEmailRequest) (*auth_pb.GetMyEmailResponse, error) {
|
||||
email, err := s.query.GetHumanEmail(ctx, authz.GetCtxData(ctx).UserID, false)
|
||||
email, err := s.query.GetHumanEmail(ctx, authz.GetCtxData(ctx).UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) GetMyPhone(ctx context.Context, _ *auth_pb.GetMyPhoneRequest) (*auth_pb.GetMyPhoneResponse, error) {
|
||||
phone, err := s.query.GetHumanPhone(ctx, authz.GetCtxData(ctx).UserID, false)
|
||||
phone, err := s.query.GetHumanPhone(ctx, authz.GetCtxData(ctx).UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) GetMyProfile(ctx context.Context, req *auth_pb.GetMyProfileRequest) (*auth_pb.GetMyProfileResponse, error) {
|
||||
profile, err := s.query.GetHumanProfile(ctx, authz.GetCtxData(ctx).UserID, false)
|
||||
profile, err := s.query.GetHumanProfile(ctx, authz.GetCtxData(ctx).UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) GetMyUser(ctx context.Context, _ *auth_pb.GetMyUserRequest) (*auth_pb.GetMyUserResponse, error) {
|
||||
user, err := s.query.GetUserByID(ctx, true, authz.GetCtxData(ctx).UserID, false)
|
||||
user, err := s.query.GetUserByID(ctx, true, authz.GetCtxData(ctx).UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ func (s *Server) getClaimedUserIDsOfOrgDomain(ctx context.Context, orgDomain, or
|
||||
}
|
||||
queries = append(queries, owner)
|
||||
}
|
||||
users, err := s.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: queries}, false)
|
||||
users, err := s.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: queries})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) GetAppByID(ctx context.Context, req *mgmt_pb.GetAppByIDRequest) (*mgmt_pb.GetAppByIDResponse, error) {
|
||||
app, err := s.query.AppByProjectAndAppID(ctx, true, req.ProjectId, req.AppId, false)
|
||||
app, err := s.query.AppByProjectAndAppID(ctx, true, req.ProjectId, req.AppId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -259,7 +259,7 @@ func (s *Server) GetAppKey(ctx context.Context, req *mgmt_pb.GetAppKeyRequest) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := s.query.GetAuthNKeyByID(ctx, true, req.KeyId, false, resourceOwner, aggregateID, objectID)
|
||||
key, err := s.query.GetAuthNKeyByID(ctx, true, req.KeyId, resourceOwner, aggregateID, objectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func (s *Server) getUserByID(ctx context.Context, id string) (*query.User, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := s.query.GetUserByID(ctx, true, id, false, owner)
|
||||
user, err := s.query.GetUserByID(ctx, true, id, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -53,7 +53,7 @@ func (s *Server) GetUserByLoginNameGlobal(ctx context.Context, req *mgmt_pb.GetU
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := s.query.GetUser(ctx, true, false, loginName)
|
||||
user, err := s.query.GetUser(ctx, true, loginName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -72,7 +72,7 @@ func (s *Server) ListUsers(ctx context.Context, req *mgmt_pb.ListUsersRequest) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.SearchUsers(ctx, queries, false)
|
||||
res, err := s.query.SearchUsers(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -128,7 +128,7 @@ func (s *Server) IsUserUnique(ctx context.Context, req *mgmt_pb.IsUserUniqueRequ
|
||||
if !policy.UserLoginMustBeDomain {
|
||||
orgID = ""
|
||||
}
|
||||
unique, err := s.query.IsUserUnique(ctx, req.UserName, req.Email, orgID, false)
|
||||
unique, err := s.query.IsUserUnique(ctx, req.UserName, req.Email, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -406,7 +406,7 @@ func (s *Server) GetHumanProfile(ctx context.Context, req *mgmt_pb.GetHumanProfi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
profile, err := s.query.GetHumanProfile(ctx, req.UserId, false, owner)
|
||||
profile, err := s.query.GetHumanProfile(ctx, req.UserId, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -440,7 +440,7 @@ func (s *Server) GetHumanEmail(ctx context.Context, req *mgmt_pb.GetHumanEmailRe
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
email, err := s.query.GetHumanEmail(ctx, req.UserId, false, owner)
|
||||
email, err := s.query.GetHumanEmail(ctx, req.UserId, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -506,7 +506,7 @@ func (s *Server) GetHumanPhone(ctx context.Context, req *mgmt_pb.GetHumanPhoneRe
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
phone, err := s.query.GetHumanPhone(ctx, req.UserId, false, owner)
|
||||
phone, err := s.query.GetHumanPhone(ctx, req.UserId, owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -753,7 +753,7 @@ func (s *Server) GetMachineKeyByIDs(ctx context.Context, req *mgmt_pb.GetMachine
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := s.query.GetAuthNKeyByID(ctx, true, req.KeyId, false, resourceOwner, aggregateID)
|
||||
key, err := s.query.GetAuthNKeyByID(ctx, true, req.KeyId, resourceOwner, aggregateID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -490,7 +490,7 @@ type userSearchByID struct {
|
||||
}
|
||||
|
||||
func (u userSearchByID) search(ctx context.Context, q *query.Queries) (*query.User, error) {
|
||||
return q.GetUserByID(ctx, true, u.id, false)
|
||||
return q.GetUserByID(ctx, true, u.id)
|
||||
}
|
||||
|
||||
type userSearchByLoginName struct {
|
||||
@ -498,5 +498,5 @@ type userSearchByLoginName struct {
|
||||
}
|
||||
|
||||
func (u userSearchByLoginName) search(ctx context.Context, q *query.Queries) (*query.User, error) {
|
||||
return q.GetUser(ctx, true, false, u.loginNameQuery)
|
||||
return q.GetUser(ctx, true, u.loginNameQuery)
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ func (s *Server) checkIntentToken(token string, intentID string) error {
|
||||
}
|
||||
|
||||
func (s *Server) ListAuthenticationMethodTypes(ctx context.Context, req *user.ListAuthenticationMethodTypesRequest) (*user.ListAuthenticationMethodTypesResponse, error) {
|
||||
authMethods, err := s.query.ListActiveUserAuthMethodTypes(ctx, req.GetUserId(), false)
|
||||
authMethods, err := s.query.ListActiveUserAuthMethodTypes(ctx, req.GetUserId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
104
internal/api/oidc/access_token.go
Normal file
104
internal/api/oidc/access_token.go
Normal file
@ -0,0 +1,104 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
errz "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
type accessToken struct {
|
||||
tokenID string
|
||||
userID string
|
||||
subject string
|
||||
clientID string
|
||||
audience []string
|
||||
scope []string
|
||||
tokenCreation time.Time
|
||||
tokenExpiration time.Time
|
||||
isPAT bool
|
||||
}
|
||||
|
||||
func (s *Server) verifyAccessToken(ctx context.Context, tkn string) (*accessToken, error) {
|
||||
var tokenID, subject string
|
||||
|
||||
if tokenIDSubject, err := s.Provider().Crypto().Decrypt(tkn); err == nil {
|
||||
split := strings.Split(tokenIDSubject, ":")
|
||||
if len(split) != 2 {
|
||||
return nil, errors.New("invalid token format")
|
||||
}
|
||||
tokenID, subject = split[0], split[1]
|
||||
} else {
|
||||
verifier := op.NewAccessTokenVerifier(op.IssuerFromContext(ctx), s.keySet)
|
||||
claims, err := op.VerifyAccessToken[*oidc.AccessTokenClaims](ctx, tkn, verifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenID, subject = claims.JWTID, claims.Subject
|
||||
}
|
||||
|
||||
if strings.HasPrefix(tokenID, command.IDPrefixV2) {
|
||||
token, err := s.query.ActiveAccessTokenByToken(ctx, tokenID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return accessTokenV2(tokenID, subject, token), nil
|
||||
}
|
||||
|
||||
token, err := s.repo.TokenByIDs(ctx, subject, tokenID)
|
||||
if err != nil {
|
||||
return nil, errz.ThrowPermissionDenied(err, "OIDC-Dsfb2", "token is not valid or has expired")
|
||||
}
|
||||
return accessTokenV1(tokenID, subject, token), nil
|
||||
}
|
||||
|
||||
func accessTokenV1(tokenID, subject string, token *model.TokenView) *accessToken {
|
||||
return &accessToken{
|
||||
tokenID: tokenID,
|
||||
userID: token.UserID,
|
||||
subject: subject,
|
||||
clientID: token.ApplicationID,
|
||||
audience: token.Audience,
|
||||
scope: token.Scopes,
|
||||
tokenCreation: token.CreationDate,
|
||||
tokenExpiration: token.Expiration,
|
||||
isPAT: token.IsPAT,
|
||||
}
|
||||
}
|
||||
|
||||
func accessTokenV2(tokenID, subject string, token *query.OIDCSessionAccessTokenReadModel) *accessToken {
|
||||
return &accessToken{
|
||||
tokenID: tokenID,
|
||||
userID: token.UserID,
|
||||
subject: subject,
|
||||
clientID: token.ClientID,
|
||||
audience: token.Audience,
|
||||
scope: token.Scope,
|
||||
tokenCreation: token.AccessTokenCreation,
|
||||
tokenExpiration: token.AccessTokenExpiration,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) assertClientScopesForPAT(ctx context.Context, token *accessToken, clientID, projectID string) error {
|
||||
token.audience = append(token.audience, clientID)
|
||||
projectIDQuery, err := query.NewProjectRoleProjectIDSearchQuery(projectID)
|
||||
if err != nil {
|
||||
return errz.ThrowInternal(err, "OIDC-Cyc78", "Errors.Internal")
|
||||
}
|
||||
roles, err := s.query.SearchProjectRoles(ctx, s.features.TriggerIntrospectionProjections, &query.ProjectRoleSearchQueries{Queries: []query.SearchQuery{projectIDQuery}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, role := range roles.ProjectRoles {
|
||||
token.scope = append(token.scope, ScopeProjectRolePrefix+role.Key)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -102,7 +102,7 @@ func (o *OPStorage) audienceFromProjectID(ctx context.Context, projectID string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appIDs, err := o.query.SearchClientIDs(ctx, &query.AppSearchQueries{Queries: []query.SearchQuery{projectIDQuery}}, false)
|
||||
appIDs, err := o.query.SearchClientIDs(ctx, &query.AppSearchQueries{Queries: []query.SearchQuery{projectIDQuery}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -432,7 +432,7 @@ func (o *OPStorage) assertProjectRoleScopes(ctx context.Context, clientID string
|
||||
return scopes, nil
|
||||
}
|
||||
}
|
||||
projectID, err := o.query.ProjectIDFromOIDCClientID(ctx, clientID, false)
|
||||
projectID, err := o.query.ProjectIDFromOIDCClientID(ctx, clientID)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-AEG4d", "Errors.Internal")
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ const (
|
||||
func (o *OPStorage) GetClientByClientID(ctx context.Context, id string) (_ op.Client, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
client, err := o.query.AppByOIDCClientID(ctx, id, false)
|
||||
client, err := o.query.AppByOIDCClientID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -94,7 +94,7 @@ func (o *OPStorage) GetKeyByIDAndIssuer(ctx context.Context, keyID, issuer strin
|
||||
}
|
||||
|
||||
func (o *OPStorage) ValidateJWTProfileScopes(ctx context.Context, subject string, scopes []string) ([]string, error) {
|
||||
user, err := o.query.GetUserByID(ctx, true, subject, false)
|
||||
user, err := o.query.GetUserByID(ctx, true, subject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -108,7 +108,7 @@ func (o *OPStorage) AuthorizeClientIDSecret(ctx context.Context, id string, secr
|
||||
UserID: oidcCtx,
|
||||
OrgID: oidcCtx,
|
||||
})
|
||||
app, err := o.query.AppByClientID(ctx, id, false)
|
||||
app, err := o.query.AppByClientID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -149,7 +149,7 @@ func (o *OPStorage) SetUserinfoFromScopes(ctx context.Context, userInfo *oidc.Us
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
if applicationID != "" {
|
||||
app, err := o.query.AppByOIDCClientID(ctx, applicationID, false)
|
||||
app, err := o.query.AppByOIDCClientID(ctx, applicationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -184,7 +184,7 @@ func (o *OPStorage) SetIntrospectionFromToken(ctx context.Context, introspection
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectID, err := o.query.ProjectIDFromClientID(ctx, clientID, false)
|
||||
projectID, err := o.query.ProjectIDFromClientID(ctx, clientID)
|
||||
if err != nil {
|
||||
return errors.ThrowPermissionDenied(nil, "OIDC-Adfg5", "client not found")
|
||||
}
|
||||
@ -198,7 +198,7 @@ func (o *OPStorage) SetIntrospectionFromToken(ctx context.Context, introspection
|
||||
if err != nil {
|
||||
return errors.ThrowPermissionDenied(nil, "OIDC-Dsfb2", "token is not valid or has expired")
|
||||
}
|
||||
projectID, err := o.query.ProjectIDFromClientID(ctx, clientID, false)
|
||||
projectID, err := o.query.ProjectIDFromClientID(ctx, clientID)
|
||||
if err != nil {
|
||||
return errors.ThrowPermissionDenied(nil, "OIDC-Adfg5", "client not found")
|
||||
}
|
||||
@ -219,7 +219,7 @@ func (o *OPStorage) ClientCredentialsTokenRequest(ctx context.Context, clientID
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := o.query.GetUser(ctx, false, false, loginname)
|
||||
user, err := o.query.GetUser(ctx, false, loginname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -240,7 +240,7 @@ func (o *OPStorage) ClientCredentials(ctx context.Context, clientID, clientSecre
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := o.query.GetUser(ctx, false, false, loginname)
|
||||
user, err := o.query.GetUser(ctx, false, loginname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -259,7 +259,7 @@ func (o *OPStorage) isOriginAllowed(ctx context.Context, clientID, origin string
|
||||
if origin == "" {
|
||||
return nil
|
||||
}
|
||||
app, err := o.query.AppByOIDCClientID(ctx, clientID, false)
|
||||
app, err := o.query.AppByOIDCClientID(ctx, clientID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -331,7 +331,7 @@ func (o *OPStorage) checkOrgScopes(ctx context.Context, user *query.User, scopes
|
||||
func (o *OPStorage) setUserinfo(ctx context.Context, userInfo *oidc.UserInfo, userID, applicationID string, scopes []string, roleAudience []string) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
user, err := o.query.GetUserByID(ctx, true, userID, false)
|
||||
user, err := o.query.GetUserByID(ctx, true, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -645,7 +645,7 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie
|
||||
}
|
||||
|
||||
func (o *OPStorage) privateClaimsFlows(ctx context.Context, userID string, userGrants *query.UserGrants, claims map[string]interface{}) (map[string]interface{}, error) {
|
||||
user, err := o.query.GetUserByID(ctx, true, userID, false)
|
||||
user, err := o.query.GetUserByID(ctx, true, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -764,7 +764,7 @@ func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID strin
|
||||
if (applicationID == "" || len(requestedRoles) == 0) && len(roleAudience) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
projectID, err := o.query.ProjectIDFromClientID(ctx, applicationID, false)
|
||||
projectID, err := o.query.ProjectIDFromClientID(ctx, applicationID)
|
||||
// applicationID might contain a username (e.g. client credentials) -> ignore the not found
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return nil, nil, err
|
||||
@ -795,7 +795,7 @@ func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID strin
|
||||
if len(requestedRoles) > 0 {
|
||||
for _, requestedRole := range requestedRoles {
|
||||
for _, grant := range grants.UserGrants {
|
||||
checkGrantedRoles(roles, grant, requestedRole, grant.ProjectID == projectID)
|
||||
checkGrantedRoles(roles, *grant, requestedRole, grant.ProjectID == projectID)
|
||||
}
|
||||
}
|
||||
return grants, roles, nil
|
||||
@ -823,7 +823,7 @@ func (o *OPStorage) assertUserMetaData(ctx context.Context, userID string) (map[
|
||||
}
|
||||
|
||||
func (o *OPStorage) assertUserResourceOwner(ctx context.Context, userID string) (map[string]string, error) {
|
||||
user, err := o.query.GetUserByID(ctx, true, userID, false)
|
||||
user, err := o.query.GetUserByID(ctx, true, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -838,7 +838,7 @@ func (o *OPStorage) assertUserResourceOwner(ctx context.Context, userID string)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkGrantedRoles(roles *projectsRoles, grant *query.UserGrant, requestedRole string, isRequested bool) {
|
||||
func checkGrantedRoles(roles *projectsRoles, grant query.UserGrant, requestedRole string, isRequested bool) {
|
||||
for _, grantedRole := range grant.Roles {
|
||||
if requestedRole == grantedRole {
|
||||
roles.Add(grant.ProjectID, grantedRole, grant.ResourceOwner, grant.OrgPrimaryDomain, isRequested)
|
||||
@ -854,6 +854,26 @@ type projectsRoles struct {
|
||||
requestProjectID string
|
||||
}
|
||||
|
||||
func newProjectRoles(projectID string, grants []query.UserGrant, requestedRoles []string) *projectsRoles {
|
||||
roles := new(projectsRoles)
|
||||
// if specific roles where requested, check if they are granted and append them in the roles list
|
||||
if len(requestedRoles) > 0 {
|
||||
for _, requestedRole := range requestedRoles {
|
||||
for _, grant := range grants {
|
||||
checkGrantedRoles(roles, grant, requestedRole, grant.ProjectID == projectID)
|
||||
}
|
||||
}
|
||||
return roles
|
||||
}
|
||||
// no specific roles were requested, so convert any grants into roles
|
||||
for _, grant := range grants {
|
||||
for _, role := range grant.Roles {
|
||||
roles.Add(grant.ProjectID, role, grant.ResourceOwner, grant.OrgPrimaryDomain, grant.ProjectID == projectID)
|
||||
}
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
func (p *projectsRoles) Add(projectID, roleKey, orgID, domain string, isRequested bool) {
|
||||
if p.projects == nil {
|
||||
p.projects = make(map[string]projectRoles, 1)
|
||||
|
@ -48,7 +48,7 @@ func TestOPStorage_SetUserinfoFromToken(t *testing.T) {
|
||||
assertUserinfo(t, userinfo)
|
||||
}
|
||||
|
||||
func TestOPStorage_SetIntrospectionFromToken(t *testing.T) {
|
||||
func TestServer_Introspect(t *testing.T) {
|
||||
project, err := Tester.CreateProject(CTX)
|
||||
require.NoError(t, err)
|
||||
app, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId())
|
||||
|
200
internal/api/oidc/introspect.go
Normal file
200
internal/api/oidc/introspect.go
Normal file
@ -0,0 +1,200 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
errz "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionRequest]) (resp *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if s.features.LegacyIntrospection {
|
||||
return s.LegacyServer.Introspect(ctx, r)
|
||||
}
|
||||
if s.features.TriggerIntrospectionProjections {
|
||||
// Execute all triggers in one concurrent sweep.
|
||||
query.TriggerIntrospectionProjections(ctx)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
clientChan := make(chan *instrospectionClientResult)
|
||||
go s.instrospectionClientAuth(ctx, r.Data.ClientCredentials, clientChan)
|
||||
|
||||
tokenChan := make(chan *introspectionTokenResult)
|
||||
go s.introspectionToken(ctx, r.Data.Token, tokenChan)
|
||||
|
||||
var (
|
||||
client *instrospectionClientResult
|
||||
token *introspectionTokenResult
|
||||
)
|
||||
|
||||
// make sure both channels are always read,
|
||||
// and cancel the context on first error
|
||||
for i := 0; i < 2; i++ {
|
||||
var resErr error
|
||||
|
||||
select {
|
||||
case client = <-clientChan:
|
||||
resErr = client.err
|
||||
case token = <-tokenChan:
|
||||
resErr = token.err
|
||||
}
|
||||
|
||||
if resErr == nil {
|
||||
continue
|
||||
}
|
||||
cancel()
|
||||
|
||||
// we only care for the first error that occurred,
|
||||
// as the next error is most probably a context error.
|
||||
if err == nil {
|
||||
err = resErr
|
||||
}
|
||||
}
|
||||
|
||||
// only client auth errors should be returned
|
||||
var target *oidc.Error
|
||||
if errors.As(err, &target) && target.ErrorType == oidc.UnauthorizedClient {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// remaining errors shoudn't be returned to the client,
|
||||
// so we catch errors here, log them and return the response
|
||||
// with active: false
|
||||
defer func() {
|
||||
if err != nil {
|
||||
s.getLogger(ctx).ErrorContext(ctx, "oidc introspection", "err", err)
|
||||
resp, err = op.NewResponse(new(oidc.IntrospectionResponse)), nil
|
||||
}
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: can we get rid of this separate query?
|
||||
if token.isPAT {
|
||||
if err = s.assertClientScopesForPAT(ctx, token.accessToken, client.clientID, client.projectID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err = validateIntrospectionAudience(token.audience, client.clientID, client.projectID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userInfo, err := s.userInfo(ctx, token.userID, client.projectID, token.scope, []string{client.projectID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
introspectionResp := &oidc.IntrospectionResponse{
|
||||
Active: true,
|
||||
Scope: token.scope,
|
||||
ClientID: token.clientID,
|
||||
TokenType: oidc.BearerToken,
|
||||
Expiration: oidc.FromTime(token.tokenExpiration),
|
||||
IssuedAt: oidc.FromTime(token.tokenCreation),
|
||||
NotBefore: oidc.FromTime(token.tokenCreation),
|
||||
Audience: token.audience,
|
||||
Issuer: op.IssuerFromContext(ctx),
|
||||
JWTID: token.tokenID,
|
||||
}
|
||||
introspectionResp.SetUserInfo(userInfo)
|
||||
return op.NewResponse(introspectionResp), nil
|
||||
}
|
||||
|
||||
type instrospectionClientResult struct {
|
||||
clientID string
|
||||
projectID string
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *Server) instrospectionClientAuth(ctx context.Context, cc *op.ClientCredentials, rc chan<- *instrospectionClientResult) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
|
||||
clientID, projectID, err := func() (string, string, error) {
|
||||
client, err := s.clientFromCredentials(ctx, cc)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if cc.ClientAssertion != "" {
|
||||
verifier := op.NewJWTProfileVerifierKeySet(keySetMap(client.PublicKeys), op.IssuerFromContext(ctx), time.Hour, time.Second)
|
||||
if _, err := op.VerifyJWTAssertion(ctx, cc.ClientAssertion, verifier); err != nil {
|
||||
return "", "", oidc.ErrUnauthorizedClient().WithParent(err)
|
||||
}
|
||||
} else {
|
||||
if err := crypto.CompareHash(client.ClientSecret, []byte(cc.ClientSecret), s.hashAlg); err != nil {
|
||||
return "", "", oidc.ErrUnauthorizedClient().WithParent(err)
|
||||
}
|
||||
}
|
||||
|
||||
return client.ClientID, client.ProjectID, nil
|
||||
}()
|
||||
|
||||
span.EndWithError(err)
|
||||
|
||||
rc <- &instrospectionClientResult{
|
||||
clientID: clientID,
|
||||
projectID: projectID,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// clientFromCredentials parses the client ID early,
|
||||
// and makes a single query for the client for either auth methods.
|
||||
func (s *Server) clientFromCredentials(ctx context.Context, cc *op.ClientCredentials) (client *query.IntrospectionClient, err error) {
|
||||
if cc.ClientAssertion != "" {
|
||||
claims := new(oidc.JWTTokenRequest)
|
||||
if _, err := oidc.ParseToken(cc.ClientAssertion, claims); err != nil {
|
||||
return nil, oidc.ErrUnauthorizedClient().WithParent(err)
|
||||
}
|
||||
client, err = s.query.GetIntrospectionClientByID(ctx, claims.Issuer, true)
|
||||
} else {
|
||||
client, err = s.query.GetIntrospectionClientByID(ctx, cc.ClientID, false)
|
||||
}
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, oidc.ErrUnauthorizedClient().WithParent(err)
|
||||
}
|
||||
// any other error is regarded internal and should not be reported back to the client.
|
||||
return client, err
|
||||
}
|
||||
|
||||
type introspectionTokenResult struct {
|
||||
*accessToken
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *Server) introspectionToken(ctx context.Context, tkn string, rc chan<- *introspectionTokenResult) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
token, err := s.verifyAccessToken(ctx, tkn)
|
||||
span.EndWithError(err)
|
||||
|
||||
rc <- &introspectionTokenResult{
|
||||
accessToken: token,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func validateIntrospectionAudience(audience []string, clientID, projectID string) error {
|
||||
if slices.ContainsFunc(audience, func(entry string) bool {
|
||||
return entry == clientID || entry == projectID
|
||||
}) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errz.ThrowPermissionDenied(nil, "OIDC-sdg3G", "token is not valid for this client")
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
|
||||
func (o *OPStorage) JWTProfileTokenType(ctx context.Context, request op.TokenRequest) (op.AccessTokenType, error) {
|
||||
mapJWTProfileScopesToAudience(ctx, request)
|
||||
user, err := o.query.GetUserByID(ctx, false, request.GetSubject(), false)
|
||||
user, err := o.query.GetUserByID(ctx, false, request.GetSubject())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -3,14 +3,17 @@ package oidc
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/zitadel/logging"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
@ -19,6 +22,145 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
// keySetCache implements oidc.KeySet for Access Token verification.
|
||||
// Public Keys are cached in a 2-dimensional map of Instance ID and Key ID.
|
||||
// When a key is not present the queryKey function is called to obtain the key
|
||||
// from the database.
|
||||
type keySetCache struct {
|
||||
mtx sync.RWMutex
|
||||
instanceKeys map[string]map[string]query.PublicKey
|
||||
queryKey func(ctx context.Context, keyID string, current time.Time) (query.PublicKey, error)
|
||||
clock clockwork.Clock
|
||||
}
|
||||
|
||||
// newKeySet initializes a keySetCache and starts a purging Go routine,
|
||||
// which runs once every purgeInterval.
|
||||
// When the passed context is done, the purge routine will terminate.
|
||||
func newKeySet(background context.Context, purgeInterval time.Duration, queryKey func(ctx context.Context, keyID string, current time.Time) (query.PublicKey, error)) *keySetCache {
|
||||
k := &keySetCache{
|
||||
instanceKeys: make(map[string]map[string]query.PublicKey),
|
||||
queryKey: queryKey,
|
||||
clock: clockwork.FromContext(background), // defaults to real clock
|
||||
}
|
||||
go k.purgeOnInterval(background, k.clock.NewTicker(purgeInterval))
|
||||
return k
|
||||
}
|
||||
|
||||
func (k *keySetCache) purgeOnInterval(background context.Context, ticker clockwork.Ticker) {
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-background.Done():
|
||||
return
|
||||
case <-ticker.Chan():
|
||||
}
|
||||
|
||||
// do the actual purging
|
||||
k.mtx.Lock()
|
||||
for instanceID, keys := range k.instanceKeys {
|
||||
for keyID, key := range keys {
|
||||
if key.Expiry().Before(k.clock.Now()) {
|
||||
delete(keys, keyID)
|
||||
}
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
delete(k.instanceKeys, instanceID)
|
||||
}
|
||||
}
|
||||
k.mtx.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (k *keySetCache) setKey(instanceID, keyID string, key query.PublicKey) {
|
||||
k.mtx.Lock()
|
||||
defer k.mtx.Unlock()
|
||||
|
||||
if keys, ok := k.instanceKeys[instanceID]; ok {
|
||||
keys[keyID] = key
|
||||
return
|
||||
}
|
||||
|
||||
k.instanceKeys[instanceID] = map[string]query.PublicKey{keyID: key}
|
||||
}
|
||||
|
||||
func (k *keySetCache) getKey(ctx context.Context, keyID string) (_ *jose.JSONWebKey, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
|
||||
k.mtx.RLock()
|
||||
key, ok := k.instanceKeys[instanceID][keyID]
|
||||
k.mtx.RUnlock()
|
||||
|
||||
if ok {
|
||||
if key.Expiry().After(k.clock.Now()) {
|
||||
return jsonWebkey(key), nil
|
||||
}
|
||||
return nil, errors.ThrowInvalidArgument(nil, "OIDC-Zoh9E", "Errors.Key.ExpireBeforeNow")
|
||||
}
|
||||
|
||||
key, err = k.queryKey(ctx, keyID, k.clock.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k.setKey(instanceID, keyID, key)
|
||||
return jsonWebkey(key), nil
|
||||
}
|
||||
|
||||
// VerifySignature implements the oidc.KeySet interface.
|
||||
func (k *keySetCache) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (_ []byte, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if len(jws.Signatures) != 1 {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "OIDC-Gid9s", "Errors.Token.Invalid")
|
||||
}
|
||||
key, err := k.getKey(ctx, jws.Signatures[0].Header.KeyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return jws.Verify(key)
|
||||
}
|
||||
|
||||
func jsonWebkey(key query.PublicKey) *jose.JSONWebKey {
|
||||
return &jose.JSONWebKey{
|
||||
KeyID: key.ID(),
|
||||
Algorithm: key.Algorithm(),
|
||||
Use: key.Use().String(),
|
||||
Key: key.Key(),
|
||||
}
|
||||
}
|
||||
|
||||
// keySetMap is a mapping of key IDs to public key data.
|
||||
type keySetMap map[string][]byte
|
||||
|
||||
// getKey finds the keyID and parses the public key data
|
||||
// into a JSONWebKey.
|
||||
func (k keySetMap) getKey(keyID string) (*jose.JSONWebKey, error) {
|
||||
pubKey, err := crypto.BytesToPublicKey(k[keyID])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &jose.JSONWebKey{
|
||||
Key: pubKey,
|
||||
KeyID: keyID,
|
||||
Use: domain.KeyUsageSigning.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifySignature implements the oidc.KeySet interface.
|
||||
func (k keySetMap) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
|
||||
if len(jws.Signatures) != 1 {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "OIDC-Eeth6", "Errors.Token.Invalid")
|
||||
}
|
||||
key, err := k.getKey(jws.Signatures[0].Header.KeyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return jws.Verify(key)
|
||||
}
|
||||
|
||||
const (
|
||||
locksTable = "projections.locks"
|
||||
signingKey = "signing_key"
|
||||
|
244
internal/api/oidc/key_test.go
Normal file
244
internal/api/oidc/key_test.go
Normal file
@ -0,0 +1,244 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
type publicKey struct {
|
||||
id string
|
||||
alg string
|
||||
use domain.KeyUsage
|
||||
seq uint64
|
||||
expiry time.Time
|
||||
key any
|
||||
}
|
||||
|
||||
func (k *publicKey) ID() string {
|
||||
return k.id
|
||||
}
|
||||
|
||||
func (k *publicKey) Algorithm() string {
|
||||
return k.alg
|
||||
}
|
||||
|
||||
func (k *publicKey) Use() domain.KeyUsage {
|
||||
return k.use
|
||||
}
|
||||
|
||||
func (k *publicKey) Sequence() uint64 {
|
||||
return k.seq
|
||||
}
|
||||
|
||||
func (k *publicKey) Expiry() time.Time {
|
||||
return k.expiry
|
||||
}
|
||||
|
||||
func (k *publicKey) Key() any {
|
||||
return k.key
|
||||
}
|
||||
|
||||
var (
|
||||
clock = clockwork.NewFakeClock()
|
||||
keyDB = map[string]*publicKey{
|
||||
"key1": {
|
||||
id: "key1",
|
||||
alg: "alg",
|
||||
use: domain.KeyUsageSigning,
|
||||
seq: 1,
|
||||
expiry: clock.Now().Add(time.Minute),
|
||||
},
|
||||
"key2": {
|
||||
id: "key2",
|
||||
alg: "alg",
|
||||
use: domain.KeyUsageSigning,
|
||||
seq: 3,
|
||||
expiry: clock.Now().Add(10 * time.Hour),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func queryKeyDB(_ context.Context, keyID string, current time.Time) (query.PublicKey, error) {
|
||||
if key, ok := keyDB[keyID]; ok {
|
||||
return key, nil
|
||||
}
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
||||
func Test_keySetCache(t *testing.T) {
|
||||
background, cancel := context.WithCancel(
|
||||
clockwork.AddToContext(context.Background(), clock),
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
// create an empty keySet with a purge go routine, runs every Hour
|
||||
keySet := newKeySet(background, time.Hour, queryKeyDB)
|
||||
ctx := authz.NewMockContext("instanceID", "orgID", "userID")
|
||||
|
||||
// query error
|
||||
_, err := keySet.getKey(ctx, "key9")
|
||||
require.Error(t, err)
|
||||
|
||||
want := &jose.JSONWebKey{
|
||||
KeyID: "key1",
|
||||
Algorithm: "alg",
|
||||
Use: domain.KeyUsageSigning.String(),
|
||||
}
|
||||
|
||||
// get key first time, populate the cache
|
||||
got, err := keySet.getKey(ctx, "key1")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, want, got)
|
||||
|
||||
// move time forward
|
||||
clock.Advance(5 * time.Minute)
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
// key should still be in cache
|
||||
keySet.mtx.RLock()
|
||||
_, ok := keySet.instanceKeys["instanceID"]["key1"]
|
||||
require.True(t, ok)
|
||||
keySet.mtx.RUnlock()
|
||||
|
||||
// the key is expired, should error
|
||||
_, err = keySet.getKey(ctx, "key1")
|
||||
require.Error(t, err)
|
||||
|
||||
want = &jose.JSONWebKey{
|
||||
KeyID: "key2",
|
||||
Algorithm: "alg",
|
||||
Use: domain.KeyUsageSigning.String(),
|
||||
}
|
||||
|
||||
// get the second key from DB
|
||||
got, err = keySet.getKey(ctx, "key2")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, want, got)
|
||||
|
||||
// move time forward
|
||||
clock.Advance(time.Hour)
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
// first key shoud be purged, second still present
|
||||
keySet.mtx.RLock()
|
||||
_, ok = keySet.instanceKeys["instanceID"]["key1"]
|
||||
require.False(t, ok)
|
||||
_, ok = keySet.instanceKeys["instanceID"]["key2"]
|
||||
require.True(t, ok)
|
||||
keySet.mtx.RUnlock()
|
||||
|
||||
// get the second key from cache
|
||||
got, err = keySet.getKey(ctx, "key2")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, want, got)
|
||||
|
||||
// move time forward
|
||||
clock.Advance(10 * time.Hour)
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
// now the cache should be empty
|
||||
keySet.mtx.RLock()
|
||||
assert.Empty(t, keySet.instanceKeys)
|
||||
keySet.mtx.RUnlock()
|
||||
}
|
||||
|
||||
func Test_keySetCache_VerifySignature(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
k := newKeySet(ctx, time.Second, queryKeyDB)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
jws *jose.JSONWebSignature
|
||||
}{
|
||||
{
|
||||
name: "invalid token",
|
||||
jws: &jose.JSONWebSignature{},
|
||||
},
|
||||
{
|
||||
name: "key not found",
|
||||
jws: &jose.JSONWebSignature{
|
||||
Signatures: []jose.Signature{{
|
||||
Header: jose.Header{
|
||||
KeyID: "xxx",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "verify error",
|
||||
jws: &jose.JSONWebSignature{
|
||||
Signatures: []jose.Signature{{
|
||||
Header: jose.Header{
|
||||
KeyID: "key1",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := k.VerifySignature(ctx, tt.jws)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_keySetMap_VerifySignature(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
k keySetMap
|
||||
jws *jose.JSONWebSignature
|
||||
}{
|
||||
{
|
||||
name: "invalid signature",
|
||||
k: keySetMap{
|
||||
"key1": []byte("foo"),
|
||||
},
|
||||
jws: &jose.JSONWebSignature{},
|
||||
},
|
||||
{
|
||||
name: "parse error",
|
||||
k: keySetMap{
|
||||
"key1": []byte("foo"),
|
||||
},
|
||||
jws: &jose.JSONWebSignature{
|
||||
Signatures: []jose.Signature{{
|
||||
Header: jose.Header{
|
||||
KeyID: "key1",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "verify error",
|
||||
k: keySetMap{
|
||||
"key1": []byte("-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsvX9P58JFxEs5C+L+H7W\nduFSWL5EPzber7C2m94klrSV6q0bAcrYQnGwFOlveThsY200hRbadKaKjHD7qIKH\nDEe0IY2PSRht33Jye52AwhkRw+M3xuQH/7R8LydnsNFk2KHpr5X2SBv42e37LjkE\nslKSaMRgJW+v0KZ30piY8QsdFRKKaVg5/Ajt1YToM1YVsdHXJ3vmXFMtypLdxwUD\ndIaLEX6pFUkU75KSuEQ/E2luT61Q3ta9kOWm9+0zvi7OMcbdekJT7mzcVnh93R1c\n13ZhQCLbh9A7si8jKFtaMWevjayrvqQABEcTN9N4Hoxcyg6l4neZtRDk75OMYcqm\nDQIDAQAB\n-----END RSA PUBLIC KEY-----\n"),
|
||||
},
|
||||
jws: &jose.JSONWebSignature{
|
||||
Signatures: []jose.Signature{{
|
||||
Header: jose.Header{
|
||||
KeyID: "key1",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := tt.k.VerifySignature(context.Background(), tt.jws)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ type Config struct {
|
||||
DeviceAuth *DeviceAuthorizationConfig
|
||||
DefaultLoginURLV2 string
|
||||
DefaultLogoutURLV2 string
|
||||
Features Features
|
||||
}
|
||||
|
||||
type EndpointConfig struct {
|
||||
@ -63,6 +64,11 @@ type Endpoint struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
type Features struct {
|
||||
TriggerIntrospectionProjections bool
|
||||
LegacyIntrospection bool
|
||||
}
|
||||
|
||||
type OPStorage struct {
|
||||
repo repository.Repository
|
||||
command *command.Commands
|
||||
@ -120,7 +126,15 @@ func NewServer(
|
||||
|
||||
server := &Server{
|
||||
LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
|
||||
features: config.Features,
|
||||
repo: repo,
|
||||
query: query,
|
||||
command: command,
|
||||
keySet: newKeySet(context.TODO(), time.Hour, query.GetActivePublicKeyByID),
|
||||
fallbackLogger: fallbackLogger,
|
||||
hashAlg: crypto.NewBCrypt(10), // as we are only verifying in oidc, the cost is already part of the hash string and the config here is irrelevant.
|
||||
signingKeyAlgorithm: config.SigningKeyAlgorithm,
|
||||
assetAPIPrefix: assets.AssetAPI(externalSecure),
|
||||
}
|
||||
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
|
||||
server.Handler = op.RegisterLegacyServer(server, op.WithHTTPMiddleware(
|
||||
|
@ -4,16 +4,32 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
"golang.org/x/exp/slog"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/auth/repository"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
http.Handler
|
||||
*op.LegacyServer
|
||||
features Features
|
||||
|
||||
repo repository.Repository
|
||||
query *query.Queries
|
||||
command *command.Commands
|
||||
keySet *keySetCache
|
||||
|
||||
fallbackLogger *slog.Logger
|
||||
hashAlg crypto.HashAlgorithm
|
||||
signingKeyAlgorithm string
|
||||
assetAPIPrefix func(ctx context.Context) string
|
||||
}
|
||||
|
||||
func endpoints(endpointConfig *EndpointConfig) op.Endpoints {
|
||||
@ -59,6 +75,13 @@ func endpoints(endpointConfig *EndpointConfig) op.Endpoints {
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func (s *Server) getLogger(ctx context.Context) *slog.Logger {
|
||||
if logger, ok := logging.FromContext(ctx); ok {
|
||||
return logger
|
||||
}
|
||||
return s.fallbackLogger
|
||||
}
|
||||
|
||||
func (s *Server) IssuerFromRequest(r *http.Request) string {
|
||||
return s.Provider().IssuerFromRequest(r)
|
||||
}
|
||||
@ -161,13 +184,6 @@ func (s *Server) DeviceToken(ctx context.Context, r *op.ClientRequest[oidc.Devic
|
||||
return s.LegacyServer.DeviceToken(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) Introspect(ctx context.Context, r *op.Request[op.IntrospectionRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Introspect(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) UserInfo(ctx context.Context, r *op.Request[oidc.UserInfoRequest]) (_ *op.Response, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
276
internal/api/oidc/userinfo.go
Normal file
276
internal/api/oidc/userinfo.go
Normal file
@ -0,0 +1,276 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
"github.com/zitadel/logging"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/actions"
|
||||
"github.com/zitadel/zitadel/internal/actions/object"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
func (s *Server) userInfo(ctx context.Context, userID, projectID string, scope, roleAudience []string) (_ *oidc.UserInfo, err error) {
|
||||
roleAudience, requestedRoles := prepareRoles(ctx, projectID, scope, roleAudience)
|
||||
qu, err := s.query.GetOIDCUserInfo(ctx, userID, roleAudience)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userInfo := userInfoToOIDC(projectID, qu, scope, roleAudience, requestedRoles, s.assetAPIPrefix(ctx))
|
||||
return userInfo, s.userinfoFlows(ctx, qu, userInfo)
|
||||
}
|
||||
|
||||
// prepareRoles scans the requested scopes, appends to roleAudiendce and returns the requestedRoles.
|
||||
//
|
||||
// When [ScopeProjectsRoles] is present and roleAudience was empty,
|
||||
// project IDs with the [domain.ProjectIDScope] prefix are added to the roleAudience.
|
||||
//
|
||||
// Scopes with [ScopeProjectRolePrefix] are added to requestedRoles.
|
||||
//
|
||||
// If the resulting requestedRoles or roleAudience are not not empty,
|
||||
// the current projectID will always be parts or roleAudience.
|
||||
// Else nil, nil is returned.
|
||||
func prepareRoles(ctx context.Context, projectID string, scope, roleAudience []string) (ra, requestedRoles []string) {
|
||||
// if all roles are requested take the audience for those from the scopes
|
||||
if slices.Contains(scope, ScopeProjectsRoles) && len(roleAudience) == 0 {
|
||||
roleAudience = domain.AddAudScopeToAudience(ctx, roleAudience, scope)
|
||||
}
|
||||
requestedRoles = make([]string, 0, len(scope))
|
||||
for _, s := range scope {
|
||||
if role, ok := strings.CutPrefix(s, ScopeProjectRolePrefix); ok {
|
||||
requestedRoles = append(requestedRoles, role)
|
||||
}
|
||||
}
|
||||
if len(requestedRoles) == 0 && len(roleAudience) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if projectID != "" && !slices.Contains(roleAudience, projectID) {
|
||||
roleAudience = append(roleAudience, projectID)
|
||||
}
|
||||
return roleAudience, requestedRoles
|
||||
}
|
||||
|
||||
func userInfoToOIDC(projectID string, user *query.OIDCUserInfo, scope, roleAudience, requestedRoles []string, assetPrefix string) *oidc.UserInfo {
|
||||
out := new(oidc.UserInfo)
|
||||
for _, s := range scope {
|
||||
switch s {
|
||||
case oidc.ScopeOpenID:
|
||||
out.Subject = user.User.ID
|
||||
case oidc.ScopeEmail:
|
||||
out.UserInfoEmail = userInfoEmailToOIDC(user.User)
|
||||
case oidc.ScopeProfile:
|
||||
out.UserInfoProfile = userInfoProfileToOidc(user.User, assetPrefix)
|
||||
case oidc.ScopePhone:
|
||||
out.UserInfoPhone = userInfoPhoneToOIDC(user.User)
|
||||
case oidc.ScopeAddress:
|
||||
//TODO: handle address for human users as soon as implemented
|
||||
case ScopeUserMetaData:
|
||||
setUserInfoMetadata(user.Metadata, out)
|
||||
case ScopeResourceOwner:
|
||||
setUserInfoOrgClaims(user, out)
|
||||
default:
|
||||
if claim, ok := strings.CutPrefix(s, domain.OrgDomainPrimaryScope); ok {
|
||||
out.AppendClaims(domain.OrgDomainPrimaryClaim, claim)
|
||||
}
|
||||
if claim, ok := strings.CutPrefix(s, domain.OrgIDScope); ok {
|
||||
out.AppendClaims(domain.OrgIDClaim, claim)
|
||||
setUserInfoOrgClaims(user, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prevent returning obtained grants if none where requested
|
||||
if (projectID != "" && len(requestedRoles) > 0) || len(roleAudience) > 0 {
|
||||
setUserInfoRoleClaims(out, newProjectRoles(projectID, user.UserGrants, requestedRoles))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func userInfoEmailToOIDC(user *query.User) oidc.UserInfoEmail {
|
||||
if human := user.Human; human != nil {
|
||||
return oidc.UserInfoEmail{
|
||||
Email: string(human.Email),
|
||||
EmailVerified: oidc.Bool(human.IsEmailVerified),
|
||||
}
|
||||
}
|
||||
return oidc.UserInfoEmail{}
|
||||
}
|
||||
|
||||
func userInfoProfileToOidc(user *query.User, assetPrefix string) oidc.UserInfoProfile {
|
||||
if human := user.Human; human != nil {
|
||||
return oidc.UserInfoProfile{
|
||||
Name: human.DisplayName,
|
||||
GivenName: human.FirstName,
|
||||
FamilyName: human.LastName,
|
||||
Nickname: human.NickName,
|
||||
Picture: domain.AvatarURL(assetPrefix, user.ResourceOwner, user.Human.AvatarKey),
|
||||
Gender: getGender(human.Gender),
|
||||
Locale: oidc.NewLocale(human.PreferredLanguage),
|
||||
UpdatedAt: oidc.FromTime(user.ChangeDate),
|
||||
PreferredUsername: user.PreferredLoginName,
|
||||
}
|
||||
}
|
||||
if machine := user.Machine; machine != nil {
|
||||
return oidc.UserInfoProfile{
|
||||
Name: machine.Name,
|
||||
UpdatedAt: oidc.FromTime(user.ChangeDate),
|
||||
PreferredUsername: user.PreferredLoginName,
|
||||
}
|
||||
}
|
||||
return oidc.UserInfoProfile{}
|
||||
}
|
||||
|
||||
func userInfoPhoneToOIDC(user *query.User) oidc.UserInfoPhone {
|
||||
if human := user.Human; human != nil {
|
||||
return oidc.UserInfoPhone{
|
||||
PhoneNumber: string(human.Phone),
|
||||
PhoneNumberVerified: human.IsPhoneVerified,
|
||||
}
|
||||
}
|
||||
return oidc.UserInfoPhone{}
|
||||
}
|
||||
|
||||
func setUserInfoMetadata(metadata []query.UserMetadata, out *oidc.UserInfo) {
|
||||
if len(metadata) == 0 {
|
||||
return
|
||||
}
|
||||
mdmap := make(map[string]string, len(metadata))
|
||||
for _, md := range metadata {
|
||||
mdmap[md.Key] = base64.RawURLEncoding.EncodeToString(md.Value)
|
||||
}
|
||||
out.AppendClaims(ClaimUserMetaData, mdmap)
|
||||
}
|
||||
|
||||
func setUserInfoOrgClaims(user *query.OIDCUserInfo, out *oidc.UserInfo) {
|
||||
if org := user.Org; org != nil {
|
||||
out.AppendClaims(ClaimResourceOwner+"id", org.ID)
|
||||
out.AppendClaims(ClaimResourceOwner+"name", org.Name)
|
||||
out.AppendClaims(ClaimResourceOwner+"primary_domain", org.PrimaryDomain)
|
||||
}
|
||||
}
|
||||
|
||||
func setUserInfoRoleClaims(userInfo *oidc.UserInfo, roles *projectsRoles) {
|
||||
if roles != nil && len(roles.projects) > 0 {
|
||||
if roles, ok := roles.projects[roles.requestProjectID]; ok {
|
||||
userInfo.AppendClaims(ClaimProjectRoles, roles)
|
||||
}
|
||||
for projectID, roles := range roles.projects {
|
||||
userInfo.AppendClaims(fmt.Sprintf(ClaimProjectRolesFormat, projectID), roles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, userInfo *oidc.UserInfo) error {
|
||||
queriedActions, err := s.query.GetActiveActionsByFlowAndTriggerType(ctx, domain.FlowTypeCustomiseToken, domain.TriggerTypePreUserinfoCreation, qu.User.ResourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctxFields := actions.SetContextFields(
|
||||
actions.SetFields("v1",
|
||||
actions.SetFields("claims", userinfoClaims(userInfo)),
|
||||
actions.SetFields("getUser", func(c *actions.FieldConfig) interface{} {
|
||||
return func(call goja.FunctionCall) goja.Value {
|
||||
return object.UserFromQuery(c, qu.User)
|
||||
}
|
||||
}),
|
||||
actions.SetFields("user",
|
||||
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
|
||||
return func(goja.FunctionCall) goja.Value {
|
||||
return object.UserMetadataListFromSlice(c, qu.Metadata)
|
||||
}
|
||||
}),
|
||||
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
|
||||
return object.UserGrantsFromSlice(c, qu.UserGrants)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
for _, action := range queriedActions {
|
||||
actionCtx, cancel := context.WithTimeout(ctx, action.Timeout())
|
||||
claimLogs := []string{}
|
||||
|
||||
apiFields := actions.WithAPIFields(
|
||||
actions.SetFields("v1",
|
||||
actions.SetFields("userinfo",
|
||||
actions.SetFields("setClaim", func(key string, value interface{}) {
|
||||
if userInfo.Claims[key] == nil {
|
||||
userInfo.AppendClaims(key, value)
|
||||
return
|
||||
}
|
||||
claimLogs = append(claimLogs, fmt.Sprintf("key %q already exists", key))
|
||||
}),
|
||||
actions.SetFields("appendLogIntoClaims", func(entry string) {
|
||||
claimLogs = append(claimLogs, entry)
|
||||
}),
|
||||
),
|
||||
actions.SetFields("claims",
|
||||
actions.SetFields("setClaim", func(key string, value interface{}) {
|
||||
if userInfo.Claims[key] == nil {
|
||||
userInfo.AppendClaims(key, value)
|
||||
return
|
||||
}
|
||||
claimLogs = append(claimLogs, fmt.Sprintf("key %q already exists", key))
|
||||
}),
|
||||
actions.SetFields("appendLogIntoClaims", func(entry string) {
|
||||
claimLogs = append(claimLogs, entry)
|
||||
}),
|
||||
),
|
||||
actions.SetFields("user",
|
||||
actions.SetFields("setMetadata", func(call goja.FunctionCall) goja.Value {
|
||||
if len(call.Arguments) != 2 {
|
||||
panic("exactly 2 (key, value) arguments expected")
|
||||
}
|
||||
key := call.Arguments[0].Export().(string)
|
||||
val := call.Arguments[1].Export()
|
||||
|
||||
value, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
logging.WithError(err).Debug("unable to marshal")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
metadata := &domain.Metadata{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
if _, err = s.command.SetUserMetadata(ctx, metadata, userInfo.Subject, qu.User.ResourceOwner); err != nil {
|
||||
logging.WithError(err).Info("unable to set md in action")
|
||||
panic(err)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
err = actions.Run(
|
||||
actionCtx,
|
||||
ctxFields,
|
||||
apiFields,
|
||||
action.Script,
|
||||
action.Name,
|
||||
append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))...,
|
||||
)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(claimLogs) > 0 {
|
||||
userInfo.AppendClaims(fmt.Sprintf(ClaimActionLogFormat, action.Name), claimLogs)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
434
internal/api/oidc/userinfo_test.go
Normal file
434
internal/api/oidc/userinfo_test.go
Normal file
@ -0,0 +1,434 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
func Test_prepareRoles(t *testing.T) {
|
||||
type args struct {
|
||||
projectID string
|
||||
scope []string
|
||||
roleAudience []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantRa []string
|
||||
wantRequestedRoles []string
|
||||
}{
|
||||
{
|
||||
name: "empty scope and roleAudience",
|
||||
args: args{
|
||||
projectID: "projID",
|
||||
scope: nil,
|
||||
roleAudience: nil,
|
||||
},
|
||||
wantRa: nil,
|
||||
wantRequestedRoles: nil,
|
||||
},
|
||||
{
|
||||
name: "some scope and roleAudience",
|
||||
args: args{
|
||||
projectID: "projID",
|
||||
scope: []string{"openid", "profile"},
|
||||
roleAudience: []string{"project2"},
|
||||
},
|
||||
wantRa: []string{"project2", "projID"},
|
||||
wantRequestedRoles: []string{},
|
||||
},
|
||||
{
|
||||
name: "scope projects roles",
|
||||
args: args{
|
||||
projectID: "projID",
|
||||
scope: []string{ScopeProjectsRoles, domain.ProjectIDScope + "project2" + domain.AudSuffix},
|
||||
roleAudience: nil,
|
||||
},
|
||||
wantRa: []string{"project2", "projID"},
|
||||
wantRequestedRoles: []string{},
|
||||
},
|
||||
{
|
||||
name: "scope project role prefix",
|
||||
args: args{
|
||||
projectID: "projID",
|
||||
scope: []string{"openid", "profile", ScopeProjectRolePrefix + "foo", ScopeProjectRolePrefix + "bar"},
|
||||
roleAudience: nil,
|
||||
},
|
||||
wantRa: []string{"projID"},
|
||||
wantRequestedRoles: []string{"foo", "bar"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotRa, gotRequestedRoles := prepareRoles(context.Background(), tt.args.projectID, tt.args.scope, tt.args.roleAudience)
|
||||
assert.Equal(t, tt.wantRa, gotRa, "roleAudience")
|
||||
assert.Equal(t, tt.wantRequestedRoles, gotRequestedRoles, "requestedRoles")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_userInfoToOIDC(t *testing.T) {
|
||||
metadata := []query.UserMetadata{
|
||||
{
|
||||
Key: "key1",
|
||||
Value: []byte{1, 2, 3},
|
||||
},
|
||||
{
|
||||
Key: "key2",
|
||||
Value: []byte{4, 5, 6},
|
||||
},
|
||||
}
|
||||
organization := &query.UserInfoOrg{
|
||||
ID: "orgID",
|
||||
Name: "orgName",
|
||||
PrimaryDomain: "orgDomain",
|
||||
}
|
||||
humanUserInfo := &query.OIDCUserInfo{
|
||||
User: &query.User{
|
||||
ID: "human1",
|
||||
CreationDate: time.Unix(123, 456),
|
||||
ChangeDate: time.Unix(567, 890),
|
||||
ResourceOwner: "orgID",
|
||||
Sequence: 22,
|
||||
State: domain.UserStateActive,
|
||||
Type: domain.UserTypeHuman,
|
||||
Username: "username",
|
||||
LoginNames: []string{"foo", "bar"},
|
||||
PreferredLoginName: "foo",
|
||||
Human: &query.Human{
|
||||
FirstName: "user",
|
||||
LastName: "name",
|
||||
NickName: "foobar",
|
||||
DisplayName: "xxx",
|
||||
AvatarKey: "picture.png",
|
||||
PreferredLanguage: language.Dutch,
|
||||
Gender: domain.GenderDiverse,
|
||||
Email: "foo@bar.com",
|
||||
IsEmailVerified: true,
|
||||
Phone: "+31123456789",
|
||||
IsPhoneVerified: true,
|
||||
},
|
||||
},
|
||||
Metadata: metadata,
|
||||
Org: organization,
|
||||
UserGrants: []query.UserGrant{
|
||||
{
|
||||
ID: "ug1",
|
||||
CreationDate: time.Unix(444, 444),
|
||||
ChangeDate: time.Unix(555, 555),
|
||||
Sequence: 55,
|
||||
Roles: []string{"role1", "role2"},
|
||||
GrantID: "grantID",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "human1",
|
||||
Username: "username",
|
||||
ResourceOwner: "orgID",
|
||||
ProjectID: "project1",
|
||||
OrgName: "orgName",
|
||||
OrgPrimaryDomain: "orgDomain",
|
||||
ProjectName: "projectName",
|
||||
UserResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
}
|
||||
machineUserInfo := &query.OIDCUserInfo{
|
||||
User: &query.User{
|
||||
ID: "machine1",
|
||||
CreationDate: time.Unix(123, 456),
|
||||
ChangeDate: time.Unix(567, 890),
|
||||
ResourceOwner: "orgID",
|
||||
Sequence: 23,
|
||||
State: domain.UserStateActive,
|
||||
Type: domain.UserTypeMachine,
|
||||
Username: "machine",
|
||||
PreferredLoginName: "meanMachine",
|
||||
Machine: &query.Machine{
|
||||
Name: "machine",
|
||||
Description: "I'm a robot",
|
||||
},
|
||||
},
|
||||
Org: organization,
|
||||
UserGrants: []query.UserGrant{
|
||||
{
|
||||
ID: "ug1",
|
||||
CreationDate: time.Unix(444, 444),
|
||||
ChangeDate: time.Unix(555, 555),
|
||||
Sequence: 55,
|
||||
Roles: []string{"role1", "role2"},
|
||||
GrantID: "grantID",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "human1",
|
||||
Username: "username",
|
||||
ResourceOwner: "orgID",
|
||||
ProjectID: "project1",
|
||||
OrgName: "orgName",
|
||||
OrgPrimaryDomain: "orgDomain",
|
||||
ProjectName: "projectName",
|
||||
UserResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type args struct {
|
||||
projectID string
|
||||
user *query.OIDCUserInfo
|
||||
scope []string
|
||||
roleAudience []string
|
||||
requestedRoles []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *oidc.UserInfo
|
||||
}{
|
||||
{
|
||||
name: "human, empty",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
},
|
||||
want: &oidc.UserInfo{},
|
||||
},
|
||||
{
|
||||
name: "machine, empty",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: machineUserInfo,
|
||||
},
|
||||
want: &oidc.UserInfo{},
|
||||
},
|
||||
{
|
||||
name: "human, scope openid",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
scope: []string{oidc.ScopeOpenID},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
Subject: "human1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "machine, scope openid",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: machineUserInfo,
|
||||
scope: []string{oidc.ScopeOpenID},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
Subject: "machine1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human, scope email",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
scope: []string{oidc.ScopeEmail},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
UserInfoEmail: oidc.UserInfoEmail{
|
||||
Email: "foo@bar.com",
|
||||
EmailVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "machine, scope email",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: machineUserInfo,
|
||||
scope: []string{oidc.ScopeEmail},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
UserInfoEmail: oidc.UserInfoEmail{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human, scope profile",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
scope: []string{oidc.ScopeProfile},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
UserInfoProfile: oidc.UserInfoProfile{
|
||||
Name: "xxx",
|
||||
GivenName: "user",
|
||||
FamilyName: "name",
|
||||
Nickname: "foobar",
|
||||
Picture: "https://foo.com/assets/orgID/picture.png",
|
||||
Gender: "diverse",
|
||||
Locale: oidc.NewLocale(language.Dutch),
|
||||
UpdatedAt: oidc.FromTime(time.Unix(567, 890)),
|
||||
PreferredUsername: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "machine, scope profile",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: machineUserInfo,
|
||||
scope: []string{oidc.ScopeProfile},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
UserInfoProfile: oidc.UserInfoProfile{
|
||||
Name: "machine",
|
||||
UpdatedAt: oidc.FromTime(time.Unix(567, 890)),
|
||||
PreferredUsername: "meanMachine",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human, scope phone",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
scope: []string{oidc.ScopePhone},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
UserInfoPhone: oidc.UserInfoPhone{
|
||||
PhoneNumber: "+31123456789",
|
||||
PhoneNumberVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "machine, scope phone",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: machineUserInfo,
|
||||
scope: []string{oidc.ScopePhone},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
UserInfoPhone: oidc.UserInfoPhone{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human, scope metadata",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
scope: []string{ScopeUserMetaData},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
Claims: map[string]any{
|
||||
ClaimUserMetaData: map[string]string{
|
||||
"key1": base64.RawURLEncoding.EncodeToString([]byte{1, 2, 3}),
|
||||
"key2": base64.RawURLEncoding.EncodeToString([]byte{4, 5, 6}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "machine, scope metadata, none found",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: machineUserInfo,
|
||||
scope: []string{ScopeUserMetaData},
|
||||
},
|
||||
want: &oidc.UserInfo{},
|
||||
},
|
||||
{
|
||||
name: "machine, scope resource owner",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: machineUserInfo,
|
||||
scope: []string{ScopeResourceOwner},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
Claims: map[string]any{
|
||||
ClaimResourceOwner + "id": "orgID",
|
||||
ClaimResourceOwner + "name": "orgName",
|
||||
ClaimResourceOwner + "primary_domain": "orgDomain",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human, scope org primary domain prefix",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
scope: []string{domain.OrgDomainPrimaryScope + "foo.com"},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
Claims: map[string]any{
|
||||
domain.OrgDomainPrimaryClaim: "foo.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "machine, scope org id",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: machineUserInfo,
|
||||
scope: []string{domain.OrgIDScope + "orgID"},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
Claims: map[string]any{
|
||||
domain.OrgIDClaim: "orgID",
|
||||
ClaimResourceOwner + "id": "orgID",
|
||||
ClaimResourceOwner + "name": "orgName",
|
||||
ClaimResourceOwner + "primary_domain": "orgDomain",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human, roleAudience",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
roleAudience: []string{"project1"},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
Claims: map[string]any{
|
||||
ClaimProjectRoles: projectRoles{
|
||||
"role1": {"orgID": "orgDomain"},
|
||||
"role2": {"orgID": "orgDomain"},
|
||||
},
|
||||
fmt.Sprintf(ClaimProjectRolesFormat, "project1"): projectRoles{
|
||||
"role1": {"orgID": "orgDomain"},
|
||||
"role2": {"orgID": "orgDomain"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human, requested roles",
|
||||
args: args{
|
||||
projectID: "project1",
|
||||
user: humanUserInfo,
|
||||
roleAudience: []string{"project1"},
|
||||
requestedRoles: []string{"role2"},
|
||||
},
|
||||
want: &oidc.UserInfo{
|
||||
Claims: map[string]any{
|
||||
ClaimProjectRoles: projectRoles{
|
||||
"role2": {"orgID": "orgDomain"},
|
||||
},
|
||||
fmt.Sprintf(ClaimProjectRolesFormat, "project1"): projectRoles{
|
||||
"role2": {"orgID": "orgDomain"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assetPrefix := "https://foo.com/assets"
|
||||
got := userInfoToOIDC(tt.args.projectID, tt.args.user, tt.args.scope, tt.args.roleAudience, tt.args.requestedRoles, assetPrefix)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@ type Storage struct {
|
||||
}
|
||||
|
||||
func (p *Storage) GetEntityByID(ctx context.Context, entityID string) (*serviceprovider.ServiceProvider, error) {
|
||||
app, err := p.query.AppBySAMLEntityID(ctx, entityID, false)
|
||||
app, err := p.query.AppBySAMLEntityID(ctx, entityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -72,7 +72,7 @@ func (p *Storage) GetEntityByID(ctx context.Context, entityID string) (*servicep
|
||||
}
|
||||
|
||||
func (p *Storage) GetEntityIDByAppID(ctx context.Context, appID string) (string, error) {
|
||||
app, err := p.query.AppByID(ctx, appID, false)
|
||||
app, err := p.query.AppByID(ctx, appID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -133,7 +133,7 @@ func (p *Storage) AuthRequestByID(ctx context.Context, id string) (_ models.Auth
|
||||
func (p *Storage) SetUserinfoWithUserID(ctx context.Context, applicationID string, userinfo models.AttributeSetter, userID string, attributes []int) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
user, err := p.query.GetUserByID(ctx, true, userID, false)
|
||||
user, err := p.query.GetUserByID(ctx, true, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -163,7 +163,7 @@ func (p *Storage) SetUserinfoWithLoginName(ctx context.Context, userinfo models.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := p.query.GetUser(ctx, true, false, loginNameSQ)
|
||||
user, err := p.query.GetUser(ctx, true, loginNameSQ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -314,7 +314,7 @@ func (p *Storage) getCustomAttributes(ctx context.Context, user *query.User, use
|
||||
}
|
||||
|
||||
func (p *Storage) getGrants(ctx context.Context, userID, applicationID string) (*query.UserGrants, error) {
|
||||
projectID, err := p.query.ProjectIDFromClientID(ctx, applicationID, false)
|
||||
projectID, err := p.query.ProjectIDFromClientID(ctx, applicationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ func (l *Login) runPostCreationActions(
|
||||
actions.SetFields("v1",
|
||||
actions.SetFields("getUser", func(c *actions.FieldConfig) interface{} {
|
||||
return func(call goja.FunctionCall) goja.Value {
|
||||
user, err := l.query.GetUserByID(actionCtx, true, userID, false)
|
||||
user, err := l.query.GetUserByID(actionCtx, true, userID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -677,7 +677,7 @@ func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, aut
|
||||
|
||||
// updateExternalUser will update the existing user (email, phone, profile) with data provided by the IDP
|
||||
func (l *Login) updateExternalUser(ctx context.Context, authReq *domain.AuthRequest, externalUser *domain.ExternalUser) error {
|
||||
user, err := l.query.GetUserByID(ctx, true, authReq.UserID, false)
|
||||
user, err := l.query.GetUserByID(ctx, true, authReq.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authRe
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
return
|
||||
}
|
||||
user, err := l.query.GetUser(setContext(r.Context(), userOrg), false, false, loginName)
|
||||
user, err := l.query.GetUser(setContext(r.Context(), userOrg), false, loginName)
|
||||
if err != nil {
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
return
|
||||
@ -144,7 +144,7 @@ func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authR
|
||||
}
|
||||
}
|
||||
if authReq == nil {
|
||||
user, err := l.query.GetUserByID(r.Context(), false, userID, false)
|
||||
user, err := l.query.GetUserByID(r.Context(), false, userID)
|
||||
if err == nil {
|
||||
l.customTexts(r.Context(), translator, user.ResourceOwner)
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *
|
||||
}
|
||||
}
|
||||
if authReq == nil {
|
||||
user, err := l.query.GetUserByID(r.Context(), false, userID, false)
|
||||
user, err := l.query.GetUserByID(r.Context(), false, userID)
|
||||
if err == nil {
|
||||
l.customTexts(r.Context(), translator, user.ResourceOwner)
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ func (l *Login) getClaimedUserIDsOfOrgDomain(ctx context.Context, orgName string
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users, err := l.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: []query.SearchQuery{loginName}}, false)
|
||||
users, err := l.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: []query.SearchQuery{loginName}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, a
|
||||
profileData: l.getProfileData(authReq),
|
||||
}
|
||||
if authReq == nil {
|
||||
user, err := l.query.GetUserByID(r.Context(), false, userID, false)
|
||||
user, err := l.query.GetUserByID(r.Context(), false, userID)
|
||||
if err == nil {
|
||||
l.customTexts(r.Context(), translator, user.ResourceOwner)
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ type smsInitFormData struct {
|
||||
// It will also add a successful OTP SMS check to the auth request.
|
||||
// If there's no verified phone number, the potential last phone number will be used to render the registration page
|
||||
func (l *Login) handleRegisterOTPSMS(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) {
|
||||
user, err := l.query.GetNotifyUserByID(r.Context(), true, authReq.UserID, false)
|
||||
user, err := l.query.GetNotifyUserByID(r.Context(), true, authReq.UserID)
|
||||
if err != nil {
|
||||
l.renderError(w, r, authReq, err)
|
||||
return
|
||||
|
@ -22,7 +22,7 @@ func (l *Login) getPasswordComplexityPolicy(r *http.Request, orgID string) *iam_
|
||||
}
|
||||
|
||||
func (l *Login) getPasswordComplexityPolicyByUserID(r *http.Request, userID string) *iam_model.PasswordComplexityPolicyView {
|
||||
user, err := l.query.GetUserByID(r.Context(), false, userID, false)
|
||||
user, err := l.query.GetUserByID(r.Context(), false, userID)
|
||||
if err != nil {
|
||||
logging.WithFields("userID", userID).OnError(err).Error("could not load user for password complexity policy")
|
||||
return nil
|
||||
|
@ -23,7 +23,7 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
return
|
||||
}
|
||||
user, err := l.query.GetUser(setContext(r.Context(), authReq.UserOrgID), true, false, loginName)
|
||||
user, err := l.query.GetUser(setContext(r.Context(), authReq.UserOrgID), true, loginName)
|
||||
if err != nil {
|
||||
if authReq.LoginPolicy.IgnoreUnknownUsernames && errors.IsNotFound(err) {
|
||||
err = nil
|
||||
|
@ -113,7 +113,7 @@ type projectProvider interface {
|
||||
}
|
||||
|
||||
type applicationProvider interface {
|
||||
AppByOIDCClientID(context.Context, string, bool) (*query.App, error)
|
||||
AppByOIDCClientID(context.Context, string) (*query.App, error)
|
||||
}
|
||||
|
||||
type customTextProvider interface {
|
||||
@ -140,7 +140,7 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *dom
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appIDs, err := repo.Query.SearchClientIDs(ctx, &query.AppSearchQueries{Queries: []query.SearchQuery{projectIDQuery}}, false)
|
||||
appIDs, err := repo.Query.SearchClientIDs(ctx, &query.AppSearchQueries{Queries: []query.SearchQuery{projectIDQuery}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1351,7 +1351,7 @@ func (repo *AuthRequestRepo) hasSucceededPage(ctx context.Context, request *doma
|
||||
if _, ok := request.Request.(*domain.AuthRequestOIDC); !ok {
|
||||
return false, nil
|
||||
}
|
||||
app, err := provider.AppByOIDCClientID(ctx, request.ApplicationID, false)
|
||||
app, err := provider.AppByOIDCClientID(ctx, request.ApplicationID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -272,7 +272,7 @@ type mockApp struct {
|
||||
app *query.App
|
||||
}
|
||||
|
||||
func (m *mockApp) AppByOIDCClientID(ctx context.Context, id string, _ bool) (*query.App, error) {
|
||||
func (m *mockApp) AppByOIDCClientID(ctx context.Context, id string) (*query.App, error) {
|
||||
if m.app != nil {
|
||||
return m.app, nil
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ func (v *View) UserByPhoneAndResourceOwner(ctx context.Context, phone, resourceO
|
||||
}
|
||||
|
||||
func (v *View) userByID(ctx context.Context, instanceID string, queries ...query.SearchQuery) (*model.UserView, error) {
|
||||
queriedUser, err := v.query.GetNotifyUser(ctx, true, false, queries...)
|
||||
queriedUser, err := v.query.GetNotifyUser(ctx, true, queries...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ func (repo *TokenVerifierRepo) checkAuthentication(ctx context.Context, authMeth
|
||||
if domain.HasMFA(authMethods) {
|
||||
return nil
|
||||
}
|
||||
availableAuthMethods, forceMFA, forceMFALocalOnly, err := repo.Query.ListUserAuthMethodTypesRequired(setCallerCtx(ctx, userID), userID, false)
|
||||
availableAuthMethods, forceMFA, forceMFALocalOnly, err := repo.Query.ListUserAuthMethodTypesRequired(setCallerCtx(ctx, userID), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func (v *View) ApplicationByOIDCClientID(ctx context.Context, clientID string) (*query.App, error) {
|
||||
return v.Query.AppByOIDCClientID(ctx, clientID, false)
|
||||
return v.Query.AppByOIDCClientID(ctx, clientID)
|
||||
}
|
||||
|
||||
func (v *View) ApplicationByProjecIDAndAppName(ctx context.Context, projectID, appName string) (_ *query.App, err error) {
|
||||
|
@ -456,3 +456,8 @@ func (h *Handler) eventQuery(currentState *state) *eventstore.SearchQueryBuilder
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
// ProjectionName returns the name of the unlying projection.
|
||||
func (h *Handler) ProjectionName() string {
|
||||
return h.projection.Name()
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ func (s *Tester) createMachineUser(ctx context.Context, username string, userTyp
|
||||
|
||||
usernameQuery, err := query.NewUserUsernameSearchQuery(username, query.TextEquals)
|
||||
logging.OnError(err).Fatal("user query")
|
||||
user, err := s.Queries.GetUser(ctx, true, true, usernameQuery)
|
||||
user, err := s.Queries.GetUser(ctx, true, usernameQuery)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
_, err = s.Commands.AddMachine(ctx, &command.Machine{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
@ -227,7 +227,7 @@ func (s *Tester) createMachineUser(ctx context.Context, username string, userTyp
|
||||
AccessTokenType: domain.OIDCTokenTypeJWT,
|
||||
})
|
||||
logging.WithFields("username", username).OnError(err).Fatal("add machine user")
|
||||
user, err = s.Queries.GetUser(ctx, true, true, usernameQuery)
|
||||
user, err = s.Queries.GetUser(ctx, true, usernameQuery)
|
||||
}
|
||||
logging.WithFields("username", username).OnError(err).Fatal("get user")
|
||||
|
||||
|
@ -6,37 +6,38 @@ package mock
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
domain "github.com/zitadel/zitadel/internal/domain"
|
||||
query "github.com/zitadel/zitadel/internal/query"
|
||||
language "golang.org/x/text/language"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockQueries is a mock of Queries interface
|
||||
// MockQueries is a mock of Queries interface.
|
||||
type MockQueries struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockQueriesMockRecorder
|
||||
}
|
||||
|
||||
// MockQueriesMockRecorder is the mock recorder for MockQueries
|
||||
// MockQueriesMockRecorder is the mock recorder for MockQueries.
|
||||
type MockQueriesMockRecorder struct {
|
||||
mock *MockQueries
|
||||
}
|
||||
|
||||
// NewMockQueries creates a new mock instance
|
||||
// NewMockQueries creates a new mock instance.
|
||||
func NewMockQueries(ctrl *gomock.Controller) *MockQueries {
|
||||
mock := &MockQueries{ctrl: ctrl}
|
||||
mock.recorder = &MockQueriesMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockQueries) EXPECT() *MockQueriesMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// ActiveLabelPolicyByOrg mocks base method
|
||||
// ActiveLabelPolicyByOrg mocks base method.
|
||||
func (m *MockQueries) ActiveLabelPolicyByOrg(arg0 context.Context, arg1 string, arg2 bool) (*query.LabelPolicy, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ActiveLabelPolicyByOrg", arg0, arg1, arg2)
|
||||
@ -45,13 +46,13 @@ func (m *MockQueries) ActiveLabelPolicyByOrg(arg0 context.Context, arg1 string,
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ActiveLabelPolicyByOrg indicates an expected call of ActiveLabelPolicyByOrg
|
||||
// ActiveLabelPolicyByOrg indicates an expected call of ActiveLabelPolicyByOrg.
|
||||
func (mr *MockQueriesMockRecorder) ActiveLabelPolicyByOrg(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActiveLabelPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).ActiveLabelPolicyByOrg), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// CustomTextListByTemplate mocks base method
|
||||
// CustomTextListByTemplate mocks base method.
|
||||
func (m *MockQueries) CustomTextListByTemplate(arg0 context.Context, arg1, arg2 string, arg3 bool) (*query.CustomTexts, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CustomTextListByTemplate", arg0, arg1, arg2, arg3)
|
||||
@ -60,13 +61,13 @@ func (m *MockQueries) CustomTextListByTemplate(arg0 context.Context, arg1, arg2
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// CustomTextListByTemplate indicates an expected call of CustomTextListByTemplate
|
||||
// CustomTextListByTemplate indicates an expected call of CustomTextListByTemplate.
|
||||
func (mr *MockQueriesMockRecorder) CustomTextListByTemplate(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CustomTextListByTemplate", reflect.TypeOf((*MockQueries)(nil).CustomTextListByTemplate), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// GetDefaultLanguage mocks base method
|
||||
// GetDefaultLanguage mocks base method.
|
||||
func (m *MockQueries) GetDefaultLanguage(arg0 context.Context) language.Tag {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetDefaultLanguage", arg0)
|
||||
@ -74,17 +75,17 @@ func (m *MockQueries) GetDefaultLanguage(arg0 context.Context) language.Tag {
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetDefaultLanguage indicates an expected call of GetDefaultLanguage
|
||||
// GetDefaultLanguage indicates an expected call of GetDefaultLanguage.
|
||||
func (mr *MockQueriesMockRecorder) GetDefaultLanguage(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultLanguage", reflect.TypeOf((*MockQueries)(nil).GetDefaultLanguage), arg0)
|
||||
}
|
||||
|
||||
// GetNotifyUserByID mocks base method
|
||||
func (m *MockQueries) GetNotifyUserByID(arg0 context.Context, arg1 bool, arg2 string, arg3 bool, arg4 ...query.SearchQuery) (*query.NotifyUser, error) {
|
||||
// GetNotifyUserByID mocks base method.
|
||||
func (m *MockQueries) GetNotifyUserByID(arg0 context.Context, arg1 bool, arg2 string, arg3 ...query.SearchQuery) (*query.NotifyUser, error) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{arg0, arg1, arg2, arg3}
|
||||
for _, a := range arg4 {
|
||||
varargs := []interface{}{arg0, arg1, arg2}
|
||||
for _, a := range arg3 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "GetNotifyUserByID", varargs...)
|
||||
@ -93,14 +94,14 @@ func (m *MockQueries) GetNotifyUserByID(arg0 context.Context, arg1 bool, arg2 st
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetNotifyUserByID indicates an expected call of GetNotifyUserByID
|
||||
func (mr *MockQueriesMockRecorder) GetNotifyUserByID(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call {
|
||||
// GetNotifyUserByID indicates an expected call of GetNotifyUserByID.
|
||||
func (mr *MockQueriesMockRecorder) GetNotifyUserByID(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...)
|
||||
varargs := append([]interface{}{arg0, arg1, arg2}, arg3...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotifyUserByID", reflect.TypeOf((*MockQueries)(nil).GetNotifyUserByID), varargs...)
|
||||
}
|
||||
|
||||
// MailTemplateByOrg mocks base method
|
||||
// MailTemplateByOrg mocks base method.
|
||||
func (m *MockQueries) MailTemplateByOrg(arg0 context.Context, arg1 string, arg2 bool) (*query.MailTemplate, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "MailTemplateByOrg", arg0, arg1, arg2)
|
||||
@ -109,13 +110,13 @@ func (m *MockQueries) MailTemplateByOrg(arg0 context.Context, arg1 string, arg2
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// MailTemplateByOrg indicates an expected call of MailTemplateByOrg
|
||||
// MailTemplateByOrg indicates an expected call of MailTemplateByOrg.
|
||||
func (mr *MockQueriesMockRecorder) MailTemplateByOrg(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MailTemplateByOrg", reflect.TypeOf((*MockQueries)(nil).MailTemplateByOrg), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// NotificationPolicyByOrg mocks base method
|
||||
// NotificationPolicyByOrg mocks base method.
|
||||
func (m *MockQueries) NotificationPolicyByOrg(arg0 context.Context, arg1 bool, arg2 string, arg3 bool) (*query.NotificationPolicy, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "NotificationPolicyByOrg", arg0, arg1, arg2, arg3)
|
||||
@ -124,13 +125,13 @@ func (m *MockQueries) NotificationPolicyByOrg(arg0 context.Context, arg1 bool, a
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// NotificationPolicyByOrg indicates an expected call of NotificationPolicyByOrg
|
||||
// NotificationPolicyByOrg indicates an expected call of NotificationPolicyByOrg.
|
||||
func (mr *MockQueriesMockRecorder) NotificationPolicyByOrg(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).NotificationPolicyByOrg), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// NotificationProviderByIDAndType mocks base method
|
||||
// NotificationProviderByIDAndType mocks base method.
|
||||
func (m *MockQueries) NotificationProviderByIDAndType(arg0 context.Context, arg1 string, arg2 domain.NotificationProviderType) (*query.DebugNotificationProvider, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "NotificationProviderByIDAndType", arg0, arg1, arg2)
|
||||
@ -139,13 +140,13 @@ func (m *MockQueries) NotificationProviderByIDAndType(arg0 context.Context, arg1
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// NotificationProviderByIDAndType indicates an expected call of NotificationProviderByIDAndType
|
||||
// NotificationProviderByIDAndType indicates an expected call of NotificationProviderByIDAndType.
|
||||
func (mr *MockQueriesMockRecorder) NotificationProviderByIDAndType(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationProviderByIDAndType", reflect.TypeOf((*MockQueries)(nil).NotificationProviderByIDAndType), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// SMSProviderConfig mocks base method
|
||||
// SMSProviderConfig mocks base method.
|
||||
func (m *MockQueries) SMSProviderConfig(arg0 context.Context, arg1 ...query.SearchQuery) (*query.SMSConfig, error) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{arg0}
|
||||
@ -158,14 +159,14 @@ func (m *MockQueries) SMSProviderConfig(arg0 context.Context, arg1 ...query.Sear
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SMSProviderConfig indicates an expected call of SMSProviderConfig
|
||||
// SMSProviderConfig indicates an expected call of SMSProviderConfig.
|
||||
func (mr *MockQueriesMockRecorder) SMSProviderConfig(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]interface{}{arg0}, arg1...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMSProviderConfig", reflect.TypeOf((*MockQueries)(nil).SMSProviderConfig), varargs...)
|
||||
}
|
||||
|
||||
// SMTPConfigByAggregateID mocks base method
|
||||
// SMTPConfigByAggregateID mocks base method.
|
||||
func (m *MockQueries) SMTPConfigByAggregateID(arg0 context.Context, arg1 string) (*query.SMTPConfig, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SMTPConfigByAggregateID", arg0, arg1)
|
||||
@ -174,13 +175,13 @@ func (m *MockQueries) SMTPConfigByAggregateID(arg0 context.Context, arg1 string)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SMTPConfigByAggregateID indicates an expected call of SMTPConfigByAggregateID
|
||||
// SMTPConfigByAggregateID indicates an expected call of SMTPConfigByAggregateID.
|
||||
func (mr *MockQueriesMockRecorder) SMTPConfigByAggregateID(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMTPConfigByAggregateID", reflect.TypeOf((*MockQueries)(nil).SMTPConfigByAggregateID), arg0, arg1)
|
||||
}
|
||||
|
||||
// SearchInstanceDomains mocks base method
|
||||
// SearchInstanceDomains mocks base method.
|
||||
func (m *MockQueries) SearchInstanceDomains(arg0 context.Context, arg1 *query.InstanceDomainSearchQueries) (*query.InstanceDomains, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SearchInstanceDomains", arg0, arg1)
|
||||
@ -189,13 +190,13 @@ func (m *MockQueries) SearchInstanceDomains(arg0 context.Context, arg1 *query.In
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SearchInstanceDomains indicates an expected call of SearchInstanceDomains
|
||||
// SearchInstanceDomains indicates an expected call of SearchInstanceDomains.
|
||||
func (mr *MockQueriesMockRecorder) SearchInstanceDomains(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchInstanceDomains", reflect.TypeOf((*MockQueries)(nil).SearchInstanceDomains), arg0, arg1)
|
||||
}
|
||||
|
||||
// SearchMilestones mocks base method
|
||||
// SearchMilestones mocks base method.
|
||||
func (m *MockQueries) SearchMilestones(arg0 context.Context, arg1 []string, arg2 *query.MilestonesSearchQueries) (*query.Milestones, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SearchMilestones", arg0, arg1, arg2)
|
||||
@ -204,13 +205,13 @@ func (m *MockQueries) SearchMilestones(arg0 context.Context, arg1 []string, arg2
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SearchMilestones indicates an expected call of SearchMilestones
|
||||
// SearchMilestones indicates an expected call of SearchMilestones.
|
||||
func (mr *MockQueriesMockRecorder) SearchMilestones(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchMilestones", reflect.TypeOf((*MockQueries)(nil).SearchMilestones), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// SessionByID mocks base method
|
||||
// SessionByID mocks base method.
|
||||
func (m *MockQueries) SessionByID(arg0 context.Context, arg1 bool, arg2, arg3 string) (*query.Session, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SessionByID", arg0, arg1, arg2, arg3)
|
||||
@ -219,7 +220,7 @@ func (m *MockQueries) SessionByID(arg0 context.Context, arg1 bool, arg2, arg3 st
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SessionByID indicates an expected call of SessionByID
|
||||
// SessionByID indicates an expected call of SessionByID.
|
||||
func (mr *MockQueriesMockRecorder) SessionByID(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SessionByID", reflect.TypeOf((*MockQueries)(nil).SessionByID), arg0, arg1, arg2, arg3)
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
type Queries interface {
|
||||
ActiveLabelPolicyByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (*query.LabelPolicy, error)
|
||||
MailTemplateByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (*query.MailTemplate, error)
|
||||
GetNotifyUserByID(ctx context.Context, shouldTriggered bool, userID string, withOwnerRemoved bool, queries ...query.SearchQuery) (*query.NotifyUser, error)
|
||||
GetNotifyUserByID(ctx context.Context, shouldTriggered bool, userID string, queries ...query.SearchQuery) (*query.NotifyUser, error)
|
||||
CustomTextListByTemplate(ctx context.Context, aggregateID, template string, withOwnerRemoved bool) (*query.CustomTexts, error)
|
||||
SearchInstanceDomains(ctx context.Context, queries *query.InstanceDomainSearchQueries) (*query.InstanceDomains, error)
|
||||
SessionByID(ctx context.Context, shouldTriggerBulk bool, id, sessionToken string) (*query.Session, error)
|
||||
|
@ -155,7 +155,7 @@ func (u *userNotifier) reduceInitCodeAdded(event eventstore.Event) (*handler.Sta
|
||||
return err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -212,7 +212,7 @@ func (u *userNotifier) reduceEmailCodeAdded(event eventstore.Event) (*handler.St
|
||||
return err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -268,7 +268,7 @@ func (u *userNotifier) reducePasswordCodeAdded(event eventstore.Event) (*handler
|
||||
return err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -361,7 +361,7 @@ func (u *userNotifier) reduceOTPSMS(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, userID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -479,7 +479,7 @@ func (u *userNotifier) reduceOTPEmail(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, userID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -532,7 +532,7 @@ func (u *userNotifier) reduceDomainClaimed(event eventstore.Event) (*handler.Sta
|
||||
return err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -586,7 +586,7 @@ func (u *userNotifier) reducePasswordlessCodeRequested(event eventstore.Event) (
|
||||
return err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -646,7 +646,7 @@ func (u *userNotifier) reducePasswordChanged(event eventstore.Event) (*handler.S
|
||||
return err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -696,7 +696,7 @@ func (u *userNotifier) reducePhoneCodeAdded(event eventstore.Event) (*handler.St
|
||||
return err
|
||||
}
|
||||
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID, false)
|
||||
notifyUser, err := u.queries.GetNotifyUserByID(ctx, true, e.Aggregate().ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1373,7 +1373,7 @@ func expectTemplateQueries(queries *mock.MockQueries, template string) {
|
||||
},
|
||||
}, nil)
|
||||
queries.EXPECT().MailTemplateByOrg(gomock.Any(), gomock.Any(), gomock.Any()).Return(&query.MailTemplate{Template: []byte(template)}, nil)
|
||||
queries.EXPECT().GetNotifyUserByID(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&query.NotifyUser{
|
||||
queries.EXPECT().GetNotifyUserByID(gomock.Any(), gomock.Any(), gomock.Any()).Return(&query.NotifyUser{
|
||||
ID: userID,
|
||||
ResourceOwner: orgID,
|
||||
LastEmail: lastEmail,
|
||||
|
@ -126,10 +126,6 @@ var (
|
||||
name: projection.AppColumnSequence,
|
||||
table: appsTable,
|
||||
}
|
||||
AppColumnOwnerRemoved = Column{
|
||||
name: projection.AppColumnOwnerRemoved,
|
||||
table: appsTable,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
@ -249,7 +245,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bool, projectID, appID string, withOwnerRemoved bool) (app *App, err error) {
|
||||
func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bool, projectID, appID string) (app *App, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -266,9 +262,6 @@ func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bo
|
||||
AppColumnProjectID.identifier(): projectID,
|
||||
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
query, args, err := stmt.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-AFDgg", "Errors.Query.SQLStatement")
|
||||
@ -281,7 +274,7 @@ func (q *Queries) AppByProjectAndAppID(ctx context.Context, shouldTriggerBulk bo
|
||||
return app, err
|
||||
}
|
||||
|
||||
func (q *Queries) AppByID(ctx context.Context, appID string, withOwnerRemoved bool) (app *App, err error) {
|
||||
func (q *Queries) AppByID(ctx context.Context, appID string) (app *App, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -290,9 +283,6 @@ func (q *Queries) AppByID(ctx context.Context, appID string, withOwnerRemoved bo
|
||||
AppColumnID.identifier(): appID,
|
||||
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
query, args, err := stmt.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-immt9", "Errors.Query.SQLStatement")
|
||||
@ -305,7 +295,7 @@ func (q *Queries) AppByID(ctx context.Context, appID string, withOwnerRemoved bo
|
||||
return app, err
|
||||
}
|
||||
|
||||
func (q *Queries) AppBySAMLEntityID(ctx context.Context, entityID string, withOwnerRemoved bool) (app *App, err error) {
|
||||
func (q *Queries) AppBySAMLEntityID(ctx context.Context, entityID string) (app *App, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -314,9 +304,6 @@ func (q *Queries) AppBySAMLEntityID(ctx context.Context, entityID string, withOw
|
||||
AppSAMLConfigColumnEntityID.identifier(): entityID,
|
||||
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
query, args, err := stmt.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-JgUop", "Errors.Query.SQLStatement")
|
||||
@ -354,7 +341,7 @@ func (q *Queries) ProjectByClientID(ctx context.Context, appID string) (project
|
||||
return project, err
|
||||
}
|
||||
|
||||
func (q *Queries) ProjectIDFromOIDCClientID(ctx context.Context, appID string, withOwnerRemoved bool) (id string, err error) {
|
||||
func (q *Queries) ProjectIDFromOIDCClientID(ctx context.Context, appID string) (id string, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -363,9 +350,6 @@ func (q *Queries) ProjectIDFromOIDCClientID(ctx context.Context, appID string, w
|
||||
AppOIDCConfigColumnClientID.identifier(): appID,
|
||||
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
query, args, err := stmt.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return "", errors.ThrowInternal(err, "QUERY-7d92U", "Errors.Query.SQLStatement")
|
||||
@ -378,15 +362,12 @@ func (q *Queries) ProjectIDFromOIDCClientID(ctx context.Context, appID string, w
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (q *Queries) ProjectIDFromClientID(ctx context.Context, appID string, withOwnerRemoved bool) (id string, err error) {
|
||||
func (q *Queries) ProjectIDFromClientID(ctx context.Context, appID string) (id string, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
stmt, scan := prepareProjectIDByAppQuery(ctx, q.client)
|
||||
eq := sq.Eq{AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
where := sq.And{
|
||||
eq,
|
||||
sq.Or{
|
||||
@ -407,7 +388,7 @@ func (q *Queries) ProjectIDFromClientID(ctx context.Context, appID string, withO
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (q *Queries) ProjectByOIDCClientID(ctx context.Context, id string, withOwnerRemoved bool) (project *Project, err error) {
|
||||
func (q *Queries) ProjectByOIDCClientID(ctx context.Context, id string) (project *Project, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -416,9 +397,6 @@ func (q *Queries) ProjectByOIDCClientID(ctx context.Context, id string, withOwne
|
||||
AppOIDCConfigColumnClientID.identifier(): id,
|
||||
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
query, args, err := stmt.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-XhJi4", "Errors.Query.SQLStatement")
|
||||
@ -431,7 +409,7 @@ func (q *Queries) ProjectByOIDCClientID(ctx context.Context, id string, withOwne
|
||||
return project, err
|
||||
}
|
||||
|
||||
func (q *Queries) AppByOIDCClientID(ctx context.Context, clientID string, withOwnerRemoved bool) (app *App, err error) {
|
||||
func (q *Queries) AppByOIDCClientID(ctx context.Context, clientID string) (app *App, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -440,9 +418,6 @@ func (q *Queries) AppByOIDCClientID(ctx context.Context, clientID string, withOw
|
||||
AppOIDCConfigColumnClientID.identifier(): clientID,
|
||||
AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
query, args, err := stmt.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-JgVop", "Errors.Query.SQLStatement")
|
||||
@ -455,15 +430,12 @@ func (q *Queries) AppByOIDCClientID(ctx context.Context, clientID string, withOw
|
||||
return app, err
|
||||
}
|
||||
|
||||
func (q *Queries) AppByClientID(ctx context.Context, clientID string, withOwnerRemoved bool) (app *App, err error) {
|
||||
func (q *Queries) AppByClientID(ctx context.Context, clientID string) (app *App, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
stmt, scan := prepareAppQuery(ctx, q.client)
|
||||
eq := sq.Eq{AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
query, args, err := stmt.Where(sq.And{
|
||||
eq,
|
||||
sq.Or{
|
||||
@ -488,9 +460,6 @@ func (q *Queries) SearchApps(ctx context.Context, queries *AppSearchQueries, wit
|
||||
|
||||
query, scan := prepareAppsQuery(ctx, q.client)
|
||||
eq := sq.Eq{AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
stmt, args, err := queries.toQuery(query).Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInvalidArgument(err, "QUERY-fajp8", "Errors.Query.InvalidRequest")
|
||||
@ -507,15 +476,12 @@ func (q *Queries) SearchApps(ctx context.Context, queries *AppSearchQueries, wit
|
||||
return apps, err
|
||||
}
|
||||
|
||||
func (q *Queries) SearchClientIDs(ctx context.Context, queries *AppSearchQueries, withOwnerRemoved bool) (ids []string, err error) {
|
||||
func (q *Queries) SearchClientIDs(ctx context.Context, queries *AppSearchQueries) (ids []string, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
query, scan := prepareClientIDsQuery(ctx, q.client)
|
||||
eq := sq.Eq{AppColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}
|
||||
if !withOwnerRemoved {
|
||||
eq[AppColumnOwnerRemoved.identifier()] = false
|
||||
}
|
||||
stmt, args, err := queries.toQuery(query).Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInvalidArgument(err, "QUERY-fajp8", "Errors.Query.InvalidRequest")
|
||||
|
@ -15,98 +15,98 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps5.id,` +
|
||||
` projections.apps5.name,` +
|
||||
` projections.apps5.project_id,` +
|
||||
` projections.apps5.creation_date,` +
|
||||
` projections.apps5.change_date,` +
|
||||
` projections.apps5.resource_owner,` +
|
||||
` projections.apps5.state,` +
|
||||
` projections.apps5.sequence,` +
|
||||
expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps6.id,` +
|
||||
` projections.apps6.name,` +
|
||||
` projections.apps6.project_id,` +
|
||||
` projections.apps6.creation_date,` +
|
||||
` projections.apps6.change_date,` +
|
||||
` projections.apps6.resource_owner,` +
|
||||
` projections.apps6.state,` +
|
||||
` projections.apps6.sequence,` +
|
||||
// api config
|
||||
` projections.apps5_api_configs.app_id,` +
|
||||
` projections.apps5_api_configs.client_id,` +
|
||||
` projections.apps5_api_configs.auth_method,` +
|
||||
` projections.apps6_api_configs.app_id,` +
|
||||
` projections.apps6_api_configs.client_id,` +
|
||||
` projections.apps6_api_configs.auth_method,` +
|
||||
// oidc config
|
||||
` projections.apps5_oidc_configs.app_id,` +
|
||||
` projections.apps5_oidc_configs.version,` +
|
||||
` projections.apps5_oidc_configs.client_id,` +
|
||||
` projections.apps5_oidc_configs.redirect_uris,` +
|
||||
` projections.apps5_oidc_configs.response_types,` +
|
||||
` projections.apps5_oidc_configs.grant_types,` +
|
||||
` projections.apps5_oidc_configs.application_type,` +
|
||||
` projections.apps5_oidc_configs.auth_method_type,` +
|
||||
` projections.apps5_oidc_configs.post_logout_redirect_uris,` +
|
||||
` projections.apps5_oidc_configs.is_dev_mode,` +
|
||||
` projections.apps5_oidc_configs.access_token_type,` +
|
||||
` projections.apps5_oidc_configs.access_token_role_assertion,` +
|
||||
` projections.apps5_oidc_configs.id_token_role_assertion,` +
|
||||
` projections.apps5_oidc_configs.id_token_userinfo_assertion,` +
|
||||
` projections.apps5_oidc_configs.clock_skew,` +
|
||||
` projections.apps5_oidc_configs.additional_origins,` +
|
||||
` projections.apps5_oidc_configs.skip_native_app_success_page,` +
|
||||
` projections.apps6_oidc_configs.app_id,` +
|
||||
` projections.apps6_oidc_configs.version,` +
|
||||
` projections.apps6_oidc_configs.client_id,` +
|
||||
` projections.apps6_oidc_configs.redirect_uris,` +
|
||||
` projections.apps6_oidc_configs.response_types,` +
|
||||
` projections.apps6_oidc_configs.grant_types,` +
|
||||
` projections.apps6_oidc_configs.application_type,` +
|
||||
` projections.apps6_oidc_configs.auth_method_type,` +
|
||||
` projections.apps6_oidc_configs.post_logout_redirect_uris,` +
|
||||
` projections.apps6_oidc_configs.is_dev_mode,` +
|
||||
` projections.apps6_oidc_configs.access_token_type,` +
|
||||
` projections.apps6_oidc_configs.access_token_role_assertion,` +
|
||||
` projections.apps6_oidc_configs.id_token_role_assertion,` +
|
||||
` projections.apps6_oidc_configs.id_token_userinfo_assertion,` +
|
||||
` projections.apps6_oidc_configs.clock_skew,` +
|
||||
` projections.apps6_oidc_configs.additional_origins,` +
|
||||
` projections.apps6_oidc_configs.skip_native_app_success_page,` +
|
||||
//saml config
|
||||
` projections.apps5_saml_configs.app_id,` +
|
||||
` projections.apps5_saml_configs.entity_id,` +
|
||||
` projections.apps5_saml_configs.metadata,` +
|
||||
` projections.apps5_saml_configs.metadata_url` +
|
||||
` FROM projections.apps5` +
|
||||
` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` +
|
||||
` projections.apps6_saml_configs.app_id,` +
|
||||
` projections.apps6_saml_configs.entity_id,` +
|
||||
` projections.apps6_saml_configs.metadata,` +
|
||||
` projections.apps6_saml_configs.metadata_url` +
|
||||
` FROM projections.apps6` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_saml_configs ON projections.apps6.id = projections.apps6_saml_configs.app_id AND projections.apps6.instance_id = projections.apps6_saml_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps5.id,` +
|
||||
` projections.apps5.name,` +
|
||||
` projections.apps5.project_id,` +
|
||||
` projections.apps5.creation_date,` +
|
||||
` projections.apps5.change_date,` +
|
||||
` projections.apps5.resource_owner,` +
|
||||
` projections.apps5.state,` +
|
||||
` projections.apps5.sequence,` +
|
||||
expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps6.id,` +
|
||||
` projections.apps6.name,` +
|
||||
` projections.apps6.project_id,` +
|
||||
` projections.apps6.creation_date,` +
|
||||
` projections.apps6.change_date,` +
|
||||
` projections.apps6.resource_owner,` +
|
||||
` projections.apps6.state,` +
|
||||
` projections.apps6.sequence,` +
|
||||
// api config
|
||||
` projections.apps5_api_configs.app_id,` +
|
||||
` projections.apps5_api_configs.client_id,` +
|
||||
` projections.apps5_api_configs.auth_method,` +
|
||||
` projections.apps6_api_configs.app_id,` +
|
||||
` projections.apps6_api_configs.client_id,` +
|
||||
` projections.apps6_api_configs.auth_method,` +
|
||||
// oidc config
|
||||
` projections.apps5_oidc_configs.app_id,` +
|
||||
` projections.apps5_oidc_configs.version,` +
|
||||
` projections.apps5_oidc_configs.client_id,` +
|
||||
` projections.apps5_oidc_configs.redirect_uris,` +
|
||||
` projections.apps5_oidc_configs.response_types,` +
|
||||
` projections.apps5_oidc_configs.grant_types,` +
|
||||
` projections.apps5_oidc_configs.application_type,` +
|
||||
` projections.apps5_oidc_configs.auth_method_type,` +
|
||||
` projections.apps5_oidc_configs.post_logout_redirect_uris,` +
|
||||
` projections.apps5_oidc_configs.is_dev_mode,` +
|
||||
` projections.apps5_oidc_configs.access_token_type,` +
|
||||
` projections.apps5_oidc_configs.access_token_role_assertion,` +
|
||||
` projections.apps5_oidc_configs.id_token_role_assertion,` +
|
||||
` projections.apps5_oidc_configs.id_token_userinfo_assertion,` +
|
||||
` projections.apps5_oidc_configs.clock_skew,` +
|
||||
` projections.apps5_oidc_configs.additional_origins,` +
|
||||
` projections.apps5_oidc_configs.skip_native_app_success_page,` +
|
||||
` projections.apps6_oidc_configs.app_id,` +
|
||||
` projections.apps6_oidc_configs.version,` +
|
||||
` projections.apps6_oidc_configs.client_id,` +
|
||||
` projections.apps6_oidc_configs.redirect_uris,` +
|
||||
` projections.apps6_oidc_configs.response_types,` +
|
||||
` projections.apps6_oidc_configs.grant_types,` +
|
||||
` projections.apps6_oidc_configs.application_type,` +
|
||||
` projections.apps6_oidc_configs.auth_method_type,` +
|
||||
` projections.apps6_oidc_configs.post_logout_redirect_uris,` +
|
||||
` projections.apps6_oidc_configs.is_dev_mode,` +
|
||||
` projections.apps6_oidc_configs.access_token_type,` +
|
||||
` projections.apps6_oidc_configs.access_token_role_assertion,` +
|
||||
` projections.apps6_oidc_configs.id_token_role_assertion,` +
|
||||
` projections.apps6_oidc_configs.id_token_userinfo_assertion,` +
|
||||
` projections.apps6_oidc_configs.clock_skew,` +
|
||||
` projections.apps6_oidc_configs.additional_origins,` +
|
||||
` projections.apps6_oidc_configs.skip_native_app_success_page,` +
|
||||
//saml config
|
||||
` projections.apps5_saml_configs.app_id,` +
|
||||
` projections.apps5_saml_configs.entity_id,` +
|
||||
` projections.apps5_saml_configs.metadata,` +
|
||||
` projections.apps5_saml_configs.metadata_url,` +
|
||||
` projections.apps6_saml_configs.app_id,` +
|
||||
` projections.apps6_saml_configs.entity_id,` +
|
||||
` projections.apps6_saml_configs.metadata,` +
|
||||
` projections.apps6_saml_configs.metadata_url,` +
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.apps5` +
|
||||
` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` +
|
||||
` FROM projections.apps6` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_saml_configs ON projections.apps6.id = projections.apps6_saml_configs.app_id AND projections.apps6.instance_id = projections.apps6_saml_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedAppIDsQuery = regexp.QuoteMeta(`SELECT projections.apps5_api_configs.client_id,` +
|
||||
` projections.apps5_oidc_configs.client_id` +
|
||||
` FROM projections.apps5` +
|
||||
` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` +
|
||||
expectedAppIDsQuery = regexp.QuoteMeta(`SELECT projections.apps6_api_configs.client_id,` +
|
||||
` projections.apps6_oidc_configs.client_id` +
|
||||
` FROM projections.apps6` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedProjectIDByAppQuery = regexp.QuoteMeta(`SELECT projections.apps5.project_id` +
|
||||
` FROM projections.apps5` +
|
||||
` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` +
|
||||
expectedProjectIDByAppQuery = regexp.QuoteMeta(`SELECT projections.apps6.project_id` +
|
||||
` FROM projections.apps6` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_saml_configs ON projections.apps6.id = projections.apps6_saml_configs.app_id AND projections.apps6.instance_id = projections.apps6_saml_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedProjectByAppQuery = regexp.QuoteMeta(`SELECT projections.projects4.id,` +
|
||||
` projections.projects4.creation_date,` +
|
||||
@ -120,10 +120,10 @@ var (
|
||||
` projections.projects4.has_project_check,` +
|
||||
` projections.projects4.private_labeling_setting` +
|
||||
` FROM projections.projects4` +
|
||||
` JOIN projections.apps5 ON projections.projects4.id = projections.apps5.project_id AND projections.projects4.instance_id = projections.apps5.instance_id` +
|
||||
` LEFT JOIN projections.apps5_api_configs ON projections.apps5.id = projections.apps5_api_configs.app_id AND projections.apps5.instance_id = projections.apps5_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_oidc_configs ON projections.apps5.id = projections.apps5_oidc_configs.app_id AND projections.apps5.instance_id = projections.apps5_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps5_saml_configs ON projections.apps5.id = projections.apps5_saml_configs.app_id AND projections.apps5.instance_id = projections.apps5_saml_configs.instance_id` +
|
||||
` JOIN projections.apps6 ON projections.projects4.id = projections.apps6.project_id AND projections.projects4.instance_id = projections.apps6.instance_id` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_saml_configs ON projections.apps6.id = projections.apps6_saml_configs.app_id AND projections.apps6.instance_id = projections.apps6_saml_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
|
||||
appCols = database.TextArray[string]{
|
||||
|
@ -76,10 +76,6 @@ var (
|
||||
name: projection.AuthNKeyEnabledCol,
|
||||
table: authNKeyTable,
|
||||
}
|
||||
AuthNKeyOwnerRemovedCol = Column{
|
||||
name: projection.AuthNKeyOwnerRemovedCol,
|
||||
table: authNKeyTable,
|
||||
}
|
||||
)
|
||||
|
||||
type AuthNKeys struct {
|
||||
@ -139,9 +135,6 @@ func (q *Queries) SearchAuthNKeys(ctx context.Context, queries *AuthNKeySearchQu
|
||||
AuthNKeyColumnEnabled.identifier(): true,
|
||||
AuthNKeyColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AuthNKeyOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInvalidArgument(err, "QUERY-SAf3f", "Errors.Query.InvalidRequest")
|
||||
@ -159,7 +152,7 @@ func (q *Queries) SearchAuthNKeys(ctx context.Context, queries *AuthNKeySearchQu
|
||||
return authNKeys, err
|
||||
}
|
||||
|
||||
func (q *Queries) SearchAuthNKeysData(ctx context.Context, queries *AuthNKeySearchQueries, withOwnerRemoved bool) (authNKeys *AuthNKeysData, err error) {
|
||||
func (q *Queries) SearchAuthNKeysData(ctx context.Context, queries *AuthNKeySearchQueries) (authNKeys *AuthNKeysData, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -169,9 +162,6 @@ func (q *Queries) SearchAuthNKeysData(ctx context.Context, queries *AuthNKeySear
|
||||
AuthNKeyColumnEnabled.identifier(): true,
|
||||
AuthNKeyColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AuthNKeyOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInvalidArgument(err, "QUERY-SAg3f", "Errors.Query.InvalidRequest")
|
||||
@ -188,7 +178,7 @@ func (q *Queries) SearchAuthNKeysData(ctx context.Context, queries *AuthNKeySear
|
||||
return authNKeys, err
|
||||
}
|
||||
|
||||
func (q *Queries) GetAuthNKeyByID(ctx context.Context, shouldTriggerBulk bool, id string, withOwnerRemoved bool, queries ...SearchQuery) (key *AuthNKey, err error) {
|
||||
func (q *Queries) GetAuthNKeyByID(ctx context.Context, shouldTriggerBulk bool, id string, queries ...SearchQuery) (key *AuthNKey, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -208,9 +198,6 @@ func (q *Queries) GetAuthNKeyByID(ctx context.Context, shouldTriggerBulk bool, i
|
||||
AuthNKeyColumnEnabled.identifier(): true,
|
||||
AuthNKeyColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[AuthNKeyOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-AGhg4", "Errors.Query.SQLStatement")
|
||||
@ -239,20 +226,6 @@ func (q *Queries) GetAuthNKeyPublicKeyByIDAndIdentifier(ctx context.Context, id
|
||||
AuthNKeyColumnExpiration.identifier(): time.Now(),
|
||||
},
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq = sq.And{
|
||||
sq.Eq{
|
||||
AuthNKeyColumnID.identifier(): id,
|
||||
AuthNKeyColumnIdentifier.identifier(): identifier,
|
||||
AuthNKeyColumnEnabled.identifier(): true,
|
||||
AuthNKeyColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
AuthNKeyOwnerRemovedCol.identifier(): false,
|
||||
},
|
||||
sq.Gt{
|
||||
AuthNKeyColumnExpiration.identifier(): time.Now(),
|
||||
},
|
||||
}
|
||||
}
|
||||
query, args, err := stmt.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-DAb32", "Errors.Query.SQLStatement")
|
||||
|
23
internal/query/embed/introspection_client_by_id.sql
Normal file
23
internal/query/embed/introspection_client_by_id.sql
Normal file
@ -0,0 +1,23 @@
|
||||
with config as (
|
||||
select app_id, client_id, client_secret
|
||||
from projections.apps6_api_configs
|
||||
where instance_id = $1
|
||||
and client_id = $2
|
||||
union
|
||||
select app_id, client_id, client_secret
|
||||
from projections.apps6_oidc_configs
|
||||
where instance_id = $1
|
||||
and client_id = $2
|
||||
),
|
||||
keys as (
|
||||
select identifier as client_id, json_object_agg(id, encode(public_key, 'base64')) as public_keys
|
||||
from projections.authn_keys2
|
||||
where $3 = true -- when argument is false, don't waste time on trying to query for keys.
|
||||
and instance_id = $1
|
||||
and identifier = $2
|
||||
and expiration > current_timestamp
|
||||
group by identifier
|
||||
)
|
||||
select config.client_id, config.client_secret, apps.project_id, keys.public_keys from config
|
||||
join projections.apps6 apps on apps.id = config.app_id
|
||||
left join keys on keys.client_id = config.client_id;
|
92
internal/query/embed/userinfo_by_id.sql
Normal file
92
internal/query/embed/userinfo_by_id.sql
Normal file
@ -0,0 +1,92 @@
|
||||
-- deallocate q;
|
||||
-- prepare q (text, text, text[]) as
|
||||
|
||||
with usr as (
|
||||
select u.id, u.creation_date, u.change_date, u.sequence, u.state, u.resource_owner, u.username, n.login_name as preferred_login_name
|
||||
from projections.users9 u
|
||||
left join projections.login_names3 n on u.id = n.user_id and u.instance_id = n.instance_id
|
||||
where u.id = $1
|
||||
and u.instance_id = $2
|
||||
and n.is_primary = true
|
||||
),
|
||||
human as (
|
||||
select $1 as user_id, row_to_json(r) as human from (
|
||||
select first_name, last_name, nick_name, display_name, avatar_key, email, is_email_verified, phone, is_phone_verified
|
||||
from projections.users9_humans
|
||||
where user_id = $1
|
||||
and instance_id = $2
|
||||
) r
|
||||
),
|
||||
machine as (
|
||||
select $1 as user_id, row_to_json(r) as machine from (
|
||||
select name, description
|
||||
from projections.users9_machines
|
||||
where user_id = $1
|
||||
and instance_id = $2
|
||||
) r
|
||||
),
|
||||
-- find the user's metadata
|
||||
metadata as (
|
||||
select json_agg(row_to_json(r)) as metadata from (
|
||||
select creation_date, change_date, sequence, resource_owner, key, encode(value, 'base64') as value
|
||||
from projections.user_metadata5
|
||||
where user_id = $1
|
||||
and instance_id = $2
|
||||
) r
|
||||
),
|
||||
-- get all user grants, needed for the orgs query
|
||||
user_grants as (
|
||||
select id, grant_id, state, creation_date, change_date, sequence, user_id, roles, resource_owner, project_id
|
||||
from projections.user_grants3
|
||||
where user_id = $1
|
||||
and instance_id = $2
|
||||
and project_id = any($3)
|
||||
),
|
||||
-- filter all orgs we are interested in.
|
||||
orgs as (
|
||||
select id, name, primary_domain
|
||||
from projections.orgs1
|
||||
where id in (
|
||||
select resource_owner from user_grants
|
||||
union
|
||||
select resource_owner from usr
|
||||
)
|
||||
and instance_id = $2
|
||||
),
|
||||
-- find the user's org
|
||||
user_org as (
|
||||
select row_to_json(r) as organization from (
|
||||
select name, primary_domain
|
||||
from orgs o
|
||||
join usr u on o.id = u.resource_owner
|
||||
) r
|
||||
),
|
||||
-- join user grants to orgs, projects and user
|
||||
grants as (
|
||||
select json_agg(row_to_json(r)) as grants from (
|
||||
select g.*,
|
||||
o.name as org_name, o.primary_domain as org_primary_domain,
|
||||
p.name as project_name, u.resource_owner as user_resource_owner
|
||||
from user_grants g
|
||||
left join orgs o on o.id = g.resource_owner
|
||||
left join projections.projects4 p on p.id = g.project_id
|
||||
left join usr u on u.id = g.user_id
|
||||
where p.instance_id = $2
|
||||
) r
|
||||
)
|
||||
-- build the final result JSON
|
||||
select json_build_object(
|
||||
'user', (
|
||||
select row_to_json(r) as usr from (
|
||||
select u.*, h.human, m.machine
|
||||
from usr u
|
||||
left join human h on u.id = h.user_id
|
||||
left join machine m on u.id = m.user_id
|
||||
) r
|
||||
),
|
||||
'org', (select organization from user_org),
|
||||
'metadata', (select metadata from metadata),
|
||||
'user_grants', (select grants from grants)
|
||||
);
|
||||
|
||||
-- execute q('231965491734773762','230690539048009730', '{"236645808328409090","240762134579904514"}')
|
@ -120,7 +120,7 @@ func (q *Queries) convertEvent(ctx context.Context, event eventstore.Event, user
|
||||
}
|
||||
|
||||
func (q *Queries) editorUserByID(ctx context.Context, userID string) *EventEditor {
|
||||
user, err := q.GetUserByID(ctx, false, userID, false)
|
||||
user, err := q.GetUserByID(ctx, false, userID)
|
||||
if err != nil {
|
||||
return &EventEditor{ID: userID}
|
||||
}
|
||||
|
@ -21,21 +21,21 @@ var (
|
||||
", members.user_id" +
|
||||
", members.roles" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.users8_humans.email" +
|
||||
", projections.users8_humans.first_name" +
|
||||
", projections.users8_humans.last_name" +
|
||||
", projections.users8_humans.display_name" +
|
||||
", projections.users8_machines.name" +
|
||||
", projections.users8_humans.avatar_key" +
|
||||
", projections.users8.type" +
|
||||
", projections.users9_humans.email" +
|
||||
", projections.users9_humans.first_name" +
|
||||
", projections.users9_humans.last_name" +
|
||||
", projections.users9_humans.display_name" +
|
||||
", projections.users9_machines.name" +
|
||||
", projections.users9_humans.avatar_key" +
|
||||
", projections.users9.type" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM projections.instance_members4 AS members " +
|
||||
"LEFT JOIN projections.users8_humans " +
|
||||
"ON members.user_id = projections.users8_humans.user_id AND members.instance_id = projections.users8_humans.instance_id " +
|
||||
"LEFT JOIN projections.users8_machines " +
|
||||
"ON members.user_id = projections.users8_machines.user_id AND members.instance_id = projections.users8_machines.instance_id " +
|
||||
"LEFT JOIN projections.users8 " +
|
||||
"ON members.user_id = projections.users8.id AND members.instance_id = projections.users8.instance_id " +
|
||||
"LEFT JOIN projections.users9_humans " +
|
||||
"ON members.user_id = projections.users9_humans.user_id AND members.instance_id = projections.users9_humans.instance_id " +
|
||||
"LEFT JOIN projections.users9_machines " +
|
||||
"ON members.user_id = projections.users9_machines.user_id AND members.instance_id = projections.users9_machines.instance_id " +
|
||||
"LEFT JOIN projections.users9 " +
|
||||
"ON members.user_id = projections.users9.id AND members.instance_id = projections.users9.instance_id " +
|
||||
"LEFT JOIN projections.login_names3 " +
|
||||
"ON members.user_id = projections.login_names3.user_id AND members.instance_id = projections.login_names3.instance_id " +
|
||||
"AS OF SYSTEM TIME '-1 ms' " +
|
||||
|
63
internal/query/introspection.go
Normal file
63
internal/query/introspection.go
Normal file
@ -0,0 +1,63 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"sync"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
// introspectionTriggerHandlers slice can only be created after zitadel
|
||||
// is fully initialized, otherwise the handlers are nil.
|
||||
// OnceValue takes care of creating the slice on the first request
|
||||
// and than will always return the same slice on subsequent requests.
|
||||
var introspectionTriggerHandlers = sync.OnceValue(func() []*handler.Handler {
|
||||
return append(oidcUserInfoTriggerHandlers(),
|
||||
projection.AppProjection,
|
||||
projection.OIDCSettingsProjection,
|
||||
projection.AuthNKeyProjection,
|
||||
)
|
||||
})
|
||||
|
||||
func TriggerIntrospectionProjections(ctx context.Context) {
|
||||
triggerBatch(ctx, introspectionTriggerHandlers()...)
|
||||
}
|
||||
|
||||
type IntrospectionClient struct {
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
ProjectID string
|
||||
PublicKeys database.Map[[]byte]
|
||||
}
|
||||
|
||||
//go:embed embed/introspection_client_by_id.sql
|
||||
var introspectionClientByIDQuery string
|
||||
|
||||
func (q *Queries) GetIntrospectionClientByID(ctx context.Context, clientID string, getKeys bool) (_ *IntrospectionClient, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
var (
|
||||
instanceID = authz.GetInstance(ctx).InstanceID()
|
||||
client = new(IntrospectionClient)
|
||||
)
|
||||
|
||||
err = q.client.QueryRowContext(ctx, func(row *sql.Row) error {
|
||||
return row.Scan(&client.ClientID, &client.ClientSecret, &client.ProjectID, &client.PublicKeys)
|
||||
},
|
||||
introspectionClientByIDQuery,
|
||||
instanceID, clientID, getKeys,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
108
internal/query/introspection_test.go
Normal file
108
internal/query/introspection_test.go
Normal file
@ -0,0 +1,108 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
func TestQueries_GetIntrospectionClientByID(t *testing.T) {
|
||||
secret := &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeHash,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("secret"),
|
||||
}
|
||||
encSecret, err := json.Marshal(secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
pubkeys := database.Map[[]byte]{
|
||||
"key1": {1, 2, 3},
|
||||
"key2": {4, 5, 6},
|
||||
}
|
||||
encPubkeys, err := pubkeys.Value()
|
||||
require.NoError(t, err)
|
||||
|
||||
expQuery := regexp.QuoteMeta(introspectionClientByIDQuery)
|
||||
type args struct {
|
||||
clientID string
|
||||
getKeys bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
mock sqlExpectation
|
||||
want *IntrospectionClient
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "query error",
|
||||
args: args{
|
||||
clientID: "clientID",
|
||||
getKeys: false,
|
||||
},
|
||||
mock: mockQueryErr(expQuery, sql.ErrConnDone, "instanceID", "clientID", false),
|
||||
wantErr: sql.ErrConnDone,
|
||||
},
|
||||
{
|
||||
name: "success, secret",
|
||||
args: args{
|
||||
clientID: "clientID",
|
||||
getKeys: false,
|
||||
},
|
||||
mock: mockQuery(expQuery,
|
||||
[]string{"client_id", "client_secret", "project_id", "public_keys"},
|
||||
[]driver.Value{"clientID", encSecret, "projectID", nil},
|
||||
"instanceID", "clientID", false),
|
||||
want: &IntrospectionClient{
|
||||
ClientID: "clientID",
|
||||
ClientSecret: secret,
|
||||
ProjectID: "projectID",
|
||||
PublicKeys: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success, keys",
|
||||
args: args{
|
||||
clientID: "clientID",
|
||||
getKeys: true,
|
||||
},
|
||||
mock: mockQuery(expQuery,
|
||||
[]string{"client_id", "client_secret", "project_id", "public_keys"},
|
||||
[]driver.Value{"clientID", nil, "projectID", encPubkeys},
|
||||
"instanceID", "clientID", true),
|
||||
want: &IntrospectionClient{
|
||||
ClientID: "clientID",
|
||||
ClientSecret: nil,
|
||||
ProjectID: "projectID",
|
||||
PublicKeys: pubkeys,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
execMock(t, tt.mock, func(db *sql.DB) {
|
||||
q := &Queries{
|
||||
client: &database.DB{
|
||||
DB: db,
|
||||
Database: &prepareDB{},
|
||||
},
|
||||
}
|
||||
ctx := authz.NewMockContext("instanceID", "orgID", "userID")
|
||||
got, err := q.GetIntrospectionClientByID(ctx, tt.args.clientID, tt.args.getKeys)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -13,7 +13,9 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/repository/keypair"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
@ -349,3 +351,88 @@ func preparePrivateKeysQuery(ctx context.Context, db prepareDatabase) (sq.Select
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type PublicKeyReadModel struct {
|
||||
eventstore.ReadModel
|
||||
|
||||
Algorithm string
|
||||
Key *crypto.CryptoValue
|
||||
Expiry time.Time
|
||||
Usage domain.KeyUsage
|
||||
}
|
||||
|
||||
func NewPublicKeyReadModel(keyID, resourceOwner string) *PublicKeyReadModel {
|
||||
return &PublicKeyReadModel{
|
||||
ReadModel: eventstore.ReadModel{
|
||||
AggregateID: keyID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *PublicKeyReadModel) AppendEvents(events ...eventstore.Event) {
|
||||
wm.ReadModel.AppendEvents(events...)
|
||||
}
|
||||
|
||||
func (wm *PublicKeyReadModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *keypair.AddedEvent:
|
||||
wm.Algorithm = e.Algorithm
|
||||
wm.Key = e.PublicKey.Key
|
||||
wm.Expiry = e.PublicKey.Expiry
|
||||
wm.Usage = e.Usage
|
||||
default:
|
||||
}
|
||||
}
|
||||
return wm.ReadModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *PublicKeyReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
AwaitOpenTransactions().
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(keypair.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(keypair.AddedEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (q *Queries) GetActivePublicKeyByID(ctx context.Context, keyID string, current time.Time) (_ PublicKey, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
model := NewPublicKeyReadModel(keyID, authz.GetInstance(ctx).InstanceID())
|
||||
if err := q.eventstore.FilterToQueryReducer(ctx, model); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if model.Algorithm == "" || model.Key == nil {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-Ahf7x", "Errors.Key.NotFound")
|
||||
}
|
||||
if model.Expiry.Before(current) {
|
||||
return nil, errors.ThrowInvalidArgument(err, "QUERY-ciF4k", "Errors.Key.ExpireBeforeNow")
|
||||
}
|
||||
keyValue, err := crypto.Decrypt(model.Key, q.keyEncryptionAlgorithm)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Ie4oh", "Errors.Internal")
|
||||
}
|
||||
publicKey, err := crypto.BytesToPublicKey(keyValue)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Kai2Z", "Errors.Internal")
|
||||
}
|
||||
|
||||
return &rsaPublicKey{
|
||||
key: key{
|
||||
id: model.AggregateID,
|
||||
creationDate: model.CreationDate,
|
||||
changeDate: model.ChangeDate,
|
||||
sequence: model.ProcessedSequence,
|
||||
resourceOwner: model.ResourceOwner,
|
||||
algorithm: model.Algorithm,
|
||||
use: model.Usage,
|
||||
},
|
||||
expiry: model.Expiry,
|
||||
publicKey: publicKey,
|
||||
}, nil
|
||||
}
|
||||
|
@ -1,18 +1,28 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rsa"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
key_repo "github.com/zitadel/zitadel/internal/repository/keypair"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -247,3 +257,232 @@ func fromBase16(base16 string) *big.Int {
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
const pubKey = `-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs38btwb3c7r0tMaQpGvB
|
||||
mY+mPwMU/LpfuPoC0k2t4RsKp0fv40SMl50CRrHgk395wch8PMPYbl3+8TtYAJuy
|
||||
rFALIj3Ff1UcKIk0hOH5DDsfh7/q2wFuncTmS6bifYo8CfSq2vDGnM7nZnEvxY/M
|
||||
fSydZdcmIqlkUpfQmtzExw9+tSe5Dxq6gn5JtlGgLgZGt69r5iMMrTEGhhVAXzNu
|
||||
MZbmlCoBru+rC8ITlTX/0V1ZcsSbL8tYWhthyu9x6yjo1bH85wiVI4gs0MhU8f2a
|
||||
+kjL/KGZbR14Ua2eo6tonBZLC5DHWM2TkYXgRCDPufjcgmzN0Lm91E4P8KvBcvly
|
||||
6QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
|
||||
func TestQueries_GetActivePublicKeyByID(t *testing.T) {
|
||||
now := time.Now()
|
||||
future := now.Add(time.Hour)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
encryption func(*testing.T) *crypto.MockEncryptionAlgorithm
|
||||
want *rsaPublicKey
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "filter error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilterError(io.ErrClosedPipe),
|
||||
),
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
{
|
||||
name: "not found error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
wantErr: errs.ThrowNotFound(nil, "QUERY-Ahf7x", "Errors.Key.NotFound"),
|
||||
},
|
||||
{
|
||||
name: "expired error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(key_repo.NewAddedEvent(context.Background(),
|
||||
&eventstore.Aggregate{
|
||||
ID: "keyID",
|
||||
Type: key_repo.AggregateType,
|
||||
ResourceOwner: "instanceID",
|
||||
InstanceID: "instanceID",
|
||||
Version: key_repo.AggregateVersion,
|
||||
},
|
||||
domain.KeyUsageSigning, "alg",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("private"),
|
||||
},
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("public"),
|
||||
},
|
||||
now.Add(-time.Hour),
|
||||
now.Add(-time.Hour),
|
||||
)),
|
||||
),
|
||||
),
|
||||
wantErr: errs.ThrowInvalidArgument(nil, "QUERY-ciF4k", "Errors.Key.ExpireBeforeNow"),
|
||||
},
|
||||
{
|
||||
name: "decrypt error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(key_repo.NewAddedEvent(context.Background(),
|
||||
&eventstore.Aggregate{
|
||||
ID: "keyID",
|
||||
Type: key_repo.AggregateType,
|
||||
ResourceOwner: "instanceID",
|
||||
InstanceID: "instanceID",
|
||||
Version: key_repo.AggregateVersion,
|
||||
},
|
||||
domain.KeyUsageSigning, "alg",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("private"),
|
||||
},
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("public"),
|
||||
},
|
||||
future,
|
||||
future,
|
||||
)),
|
||||
),
|
||||
),
|
||||
encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm {
|
||||
encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t))
|
||||
expect := encryption.EXPECT()
|
||||
expect.Algorithm().Return("alg")
|
||||
expect.DecryptionKeyIDs().Return([]string{})
|
||||
return encryption
|
||||
},
|
||||
wantErr: errs.ThrowInternal(nil, "QUERY-Ie4oh", "Errors.Internal"),
|
||||
},
|
||||
{
|
||||
name: "parse error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(key_repo.NewAddedEvent(context.Background(),
|
||||
&eventstore.Aggregate{
|
||||
ID: "keyID",
|
||||
Type: key_repo.AggregateType,
|
||||
ResourceOwner: "instanceID",
|
||||
InstanceID: "instanceID",
|
||||
Version: key_repo.AggregateVersion,
|
||||
},
|
||||
domain.KeyUsageSigning, "alg",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("private"),
|
||||
},
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("public"),
|
||||
},
|
||||
future,
|
||||
future,
|
||||
)),
|
||||
),
|
||||
),
|
||||
encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm {
|
||||
encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t))
|
||||
expect := encryption.EXPECT()
|
||||
expect.Algorithm().Return("alg")
|
||||
expect.DecryptionKeyIDs().Return([]string{"keyID"})
|
||||
expect.Decrypt([]byte("public"), "keyID").Return([]byte("foo"), nil)
|
||||
return encryption
|
||||
},
|
||||
wantErr: errs.ThrowInternal(nil, "QUERY-Kai2Z", "Errors.Internal"),
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(key_repo.NewAddedEvent(context.Background(),
|
||||
&eventstore.Aggregate{
|
||||
ID: "keyID",
|
||||
Type: key_repo.AggregateType,
|
||||
ResourceOwner: "instanceID",
|
||||
InstanceID: "instanceID",
|
||||
Version: key_repo.AggregateVersion,
|
||||
},
|
||||
domain.KeyUsageSigning, "alg",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("private"),
|
||||
},
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("public"),
|
||||
},
|
||||
future,
|
||||
future,
|
||||
)),
|
||||
),
|
||||
),
|
||||
encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm {
|
||||
encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t))
|
||||
expect := encryption.EXPECT()
|
||||
expect.Algorithm().Return("alg")
|
||||
expect.DecryptionKeyIDs().Return([]string{"keyID"})
|
||||
expect.Decrypt([]byte("public"), "keyID").Return([]byte(pubKey), nil)
|
||||
return encryption
|
||||
},
|
||||
want: &rsaPublicKey{
|
||||
key: key{
|
||||
id: "keyID",
|
||||
resourceOwner: "instanceID",
|
||||
algorithm: "alg",
|
||||
use: domain.KeyUsageSigning,
|
||||
},
|
||||
expiry: future,
|
||||
publicKey: func() *rsa.PublicKey {
|
||||
publicKey, err := crypto.BytesToPublicKey([]byte(pubKey))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return publicKey
|
||||
}(),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
q := &Queries{
|
||||
eventstore: tt.eventstore(t),
|
||||
}
|
||||
if tt.encryption != nil {
|
||||
q.keyEncryptionAlgorithm = tt.encryption(t)
|
||||
}
|
||||
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
||||
key, err := q.GetActivePublicKeyByID(ctx, "keyID", now)
|
||||
if tt.wantErr != nil {
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, key)
|
||||
|
||||
got := key.(*rsaPublicKey)
|
||||
assert.WithinDuration(t, tt.want.expiry, got.expiry, time.Second)
|
||||
tt.want.expiry = time.Time{}
|
||||
got.expiry = time.Time{}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -21,24 +21,24 @@ var (
|
||||
", members.user_id" +
|
||||
", members.roles" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.users8_humans.email" +
|
||||
", projections.users8_humans.first_name" +
|
||||
", projections.users8_humans.last_name" +
|
||||
", projections.users8_humans.display_name" +
|
||||
", projections.users8_machines.name" +
|
||||
", projections.users8_humans.avatar_key" +
|
||||
", projections.users8.type" +
|
||||
", projections.users9_humans.email" +
|
||||
", projections.users9_humans.first_name" +
|
||||
", projections.users9_humans.last_name" +
|
||||
", projections.users9_humans.display_name" +
|
||||
", projections.users9_machines.name" +
|
||||
", projections.users9_humans.avatar_key" +
|
||||
", projections.users9.type" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM projections.org_members4 AS members " +
|
||||
"LEFT JOIN projections.users8_humans " +
|
||||
"ON members.user_id = projections.users8_humans.user_id " +
|
||||
"AND members.instance_id = projections.users8_humans.instance_id " +
|
||||
"LEFT JOIN projections.users8_machines " +
|
||||
"ON members.user_id = projections.users8_machines.user_id " +
|
||||
"AND members.instance_id = projections.users8_machines.instance_id " +
|
||||
"LEFT JOIN projections.users8 " +
|
||||
"ON members.user_id = projections.users8.id " +
|
||||
"AND members.instance_id = projections.users8.instance_id " +
|
||||
"LEFT JOIN projections.users9_humans " +
|
||||
"ON members.user_id = projections.users9_humans.user_id " +
|
||||
"AND members.instance_id = projections.users9_humans.instance_id " +
|
||||
"LEFT JOIN projections.users9_machines " +
|
||||
"ON members.user_id = projections.users9_machines.user_id " +
|
||||
"AND members.instance_id = projections.users9_machines.instance_id " +
|
||||
"LEFT JOIN projections.users9 " +
|
||||
"ON members.user_id = projections.users9.id " +
|
||||
"AND members.instance_id = projections.users9.instance_id " +
|
||||
"LEFT JOIN projections.login_names3 " +
|
||||
"ON members.user_id = projections.login_names3.user_id " +
|
||||
"AND members.instance_id = projections.login_names3.instance_id " +
|
||||
|
@ -21,24 +21,24 @@ var (
|
||||
", members.user_id" +
|
||||
", members.roles" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.users8_humans.email" +
|
||||
", projections.users8_humans.first_name" +
|
||||
", projections.users8_humans.last_name" +
|
||||
", projections.users8_humans.display_name" +
|
||||
", projections.users8_machines.name" +
|
||||
", projections.users8_humans.avatar_key" +
|
||||
", projections.users8.type" +
|
||||
", projections.users9_humans.email" +
|
||||
", projections.users9_humans.first_name" +
|
||||
", projections.users9_humans.last_name" +
|
||||
", projections.users9_humans.display_name" +
|
||||
", projections.users9_machines.name" +
|
||||
", projections.users9_humans.avatar_key" +
|
||||
", projections.users9.type" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM projections.project_grant_members4 AS members " +
|
||||
"LEFT JOIN projections.users8_humans " +
|
||||
"ON members.user_id = projections.users8_humans.user_id " +
|
||||
"AND members.instance_id = projections.users8_humans.instance_id " +
|
||||
"LEFT JOIN projections.users8_machines " +
|
||||
"ON members.user_id = projections.users8_machines.user_id " +
|
||||
"AND members.instance_id = projections.users8_machines.instance_id " +
|
||||
"LEFT JOIN projections.users8 " +
|
||||
"ON members.user_id = projections.users8.id " +
|
||||
"AND members.instance_id = projections.users8.instance_id " +
|
||||
"LEFT JOIN projections.users9_humans " +
|
||||
"ON members.user_id = projections.users9_humans.user_id " +
|
||||
"AND members.instance_id = projections.users9_humans.instance_id " +
|
||||
"LEFT JOIN projections.users9_machines " +
|
||||
"ON members.user_id = projections.users9_machines.user_id " +
|
||||
"AND members.instance_id = projections.users9_machines.instance_id " +
|
||||
"LEFT JOIN projections.users9 " +
|
||||
"ON members.user_id = projections.users9.id " +
|
||||
"AND members.instance_id = projections.users9.instance_id " +
|
||||
"LEFT JOIN projections.login_names3 " +
|
||||
"ON members.user_id = projections.login_names3.user_id " +
|
||||
"AND members.instance_id = projections.login_names3.instance_id " +
|
||||
|
@ -21,24 +21,24 @@ var (
|
||||
", members.user_id" +
|
||||
", members.roles" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.users8_humans.email" +
|
||||
", projections.users8_humans.first_name" +
|
||||
", projections.users8_humans.last_name" +
|
||||
", projections.users8_humans.display_name" +
|
||||
", projections.users8_machines.name" +
|
||||
", projections.users8_humans.avatar_key" +
|
||||
", projections.users8.type" +
|
||||
", projections.users9_humans.email" +
|
||||
", projections.users9_humans.first_name" +
|
||||
", projections.users9_humans.last_name" +
|
||||
", projections.users9_humans.display_name" +
|
||||
", projections.users9_machines.name" +
|
||||
", projections.users9_humans.avatar_key" +
|
||||
", projections.users9.type" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM projections.project_members4 AS members " +
|
||||
"LEFT JOIN projections.users8_humans " +
|
||||
"ON members.user_id = projections.users8_humans.user_id " +
|
||||
"AND members.instance_id = projections.users8_humans.instance_id " +
|
||||
"LEFT JOIN projections.users8_machines " +
|
||||
"ON members.user_id = projections.users8_machines.user_id " +
|
||||
"AND members.instance_id = projections.users8_machines.instance_id " +
|
||||
"LEFT JOIN projections.users8 " +
|
||||
"ON members.user_id = projections.users8.id " +
|
||||
"AND members.instance_id = projections.users8.instance_id " +
|
||||
"LEFT JOIN projections.users9_humans " +
|
||||
"ON members.user_id = projections.users9_humans.user_id " +
|
||||
"AND members.instance_id = projections.users9_humans.instance_id " +
|
||||
"LEFT JOIN projections.users9_machines " +
|
||||
"ON members.user_id = projections.users9_machines.user_id " +
|
||||
"AND members.instance_id = projections.users9_machines.instance_id " +
|
||||
"LEFT JOIN projections.users9 " +
|
||||
"ON members.user_id = projections.users9.id " +
|
||||
"AND members.instance_id = projections.users9.instance_id " +
|
||||
"LEFT JOIN projections.login_names3 " +
|
||||
"ON members.user_id = projections.login_names3.user_id " +
|
||||
"AND members.instance_id = projections.login_names3.instance_id " +
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
AppProjectionTable = "projections.apps5"
|
||||
AppProjectionTable = "projections.apps6"
|
||||
AppAPITable = AppProjectionTable + "_" + appAPITableSuffix
|
||||
AppOIDCTable = AppProjectionTable + "_" + appOIDCTableSuffix
|
||||
AppSAMLTable = AppProjectionTable + "_" + appSAMLTableSuffix
|
||||
@ -29,7 +29,6 @@ const (
|
||||
AppColumnInstanceID = "instance_id"
|
||||
AppColumnState = "state"
|
||||
AppColumnSequence = "sequence"
|
||||
AppColumnOwnerRemoved = "owner_removed"
|
||||
|
||||
appAPITableSuffix = "api_configs"
|
||||
AppAPIConfigColumnAppID = "app_id"
|
||||
@ -89,11 +88,9 @@ func (*appProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(AppColumnInstanceID, handler.ColumnTypeText),
|
||||
handler.NewColumn(AppColumnState, handler.ColumnTypeEnum),
|
||||
handler.NewColumn(AppColumnSequence, handler.ColumnTypeInt64),
|
||||
handler.NewColumn(AppColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)),
|
||||
},
|
||||
handler.NewPrimaryKey(AppColumnInstanceID, AppColumnID),
|
||||
handler.WithIndex(handler.NewIndex("project_id", []string{AppColumnProjectID})),
|
||||
handler.WithIndex(handler.NewIndex("owner_removed", []string{AppColumnOwnerRemoved})),
|
||||
),
|
||||
handler.NewSuffixedTable([]*handler.InitColumn{
|
||||
handler.NewColumn(AppAPIConfigColumnAppID, handler.ColumnTypeText),
|
||||
|
@ -46,7 +46,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps5 (id, name, project_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.apps6 (id, name, project_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"my-app",
|
||||
@ -83,7 +83,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"my-app",
|
||||
anyArg{},
|
||||
@ -136,7 +136,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.AppStateInactive,
|
||||
anyArg{},
|
||||
@ -168,7 +168,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.AppStateActive,
|
||||
anyArg{},
|
||||
@ -200,7 +200,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.apps5 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.apps6 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
@ -227,7 +227,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.apps5 WHERE (project_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.apps6 WHERE (project_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -254,7 +254,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.apps5 WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.apps6 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
@ -285,7 +285,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps5_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.apps6_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
@ -295,7 +295,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -329,7 +329,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps6_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
@ -338,7 +338,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -391,7 +391,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.apps6_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
"app-id",
|
||||
@ -399,7 +399,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -447,7 +447,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps5_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||
expectedStmt: "INSERT INTO projections.apps6_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
@ -471,7 +471,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -518,7 +518,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (app_id = $16) AND (instance_id = $17)",
|
||||
expectedStmt: "UPDATE projections.apps6_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (app_id = $16) AND (instance_id = $17)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.OIDCVersionV1,
|
||||
database.TextArray[string]{"redirect.one.ch", "redirect.two.ch"},
|
||||
@ -540,7 +540,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -593,7 +593,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.apps6_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
"app-id",
|
||||
@ -601,7 +601,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps5 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -630,7 +630,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.apps5 WHERE (instance_id = $1) AND (resource_owner = $2)",
|
||||
expectedStmt: "DELETE FROM projections.apps6 WHERE (instance_id = $1) AND (resource_owner = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
"agg-id",
|
||||
|
@ -30,7 +30,6 @@ const (
|
||||
AuthNKeyPublicKeyCol = "public_key"
|
||||
AuthNKeyTypeCol = "type"
|
||||
AuthNKeyEnabledCol = "enabled"
|
||||
AuthNKeyOwnerRemovedCol = "owner_removed"
|
||||
)
|
||||
|
||||
type authNKeyProjection struct{}
|
||||
@ -59,12 +58,10 @@ func (*authNKeyProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(AuthNKeyPublicKeyCol, handler.ColumnTypeBytes),
|
||||
handler.NewColumn(AuthNKeyEnabledCol, handler.ColumnTypeBool, handler.Default(true)),
|
||||
handler.NewColumn(AuthNKeyTypeCol, handler.ColumnTypeEnum, handler.Default(0)),
|
||||
handler.NewColumn(AuthNKeyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)),
|
||||
},
|
||||
handler.NewPrimaryKey(AuthNKeyInstanceIDCol, AuthNKeyIDCol),
|
||||
handler.WithIndex(handler.NewIndex("enabled", []string{AuthNKeyEnabledCol})),
|
||||
handler.WithIndex(handler.NewIndex("identifier", []string{AuthNKeyIdentifierCol})),
|
||||
handler.WithIndex(handler.NewIndex("owner_removed", []string{AuthNKeyOwnerRemovedCol})),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UserTable = "projections.users8"
|
||||
UserTable = "projections.users9"
|
||||
UserHumanTable = UserTable + "_" + UserHumanSuffix
|
||||
UserMachineTable = UserTable + "_" + UserMachineSuffix
|
||||
UserNotifyTable = UserTable + "_" + UserNotifySuffix
|
||||
@ -29,7 +29,6 @@ const (
|
||||
UserInstanceIDCol = "instance_id"
|
||||
UserUsernameCol = "username"
|
||||
UserTypeCol = "type"
|
||||
UserOwnerRemovedCol = "owner_removed"
|
||||
|
||||
UserHumanSuffix = "humans"
|
||||
HumanUserIDCol = "user_id"
|
||||
@ -94,12 +93,10 @@ func (*userProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(UserInstanceIDCol, handler.ColumnTypeText),
|
||||
handler.NewColumn(UserUsernameCol, handler.ColumnTypeText),
|
||||
handler.NewColumn(UserTypeCol, handler.ColumnTypeEnum),
|
||||
handler.NewColumn(UserOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)),
|
||||
},
|
||||
handler.NewPrimaryKey(UserInstanceIDCol, UserIDCol),
|
||||
handler.WithIndex(handler.NewIndex("username", []string{UserUsernameCol})),
|
||||
handler.WithIndex(handler.NewIndex("resource_owner", []string{UserResourceOwnerCol})),
|
||||
handler.WithIndex(handler.NewIndex("owner_removed", []string{UserOwnerRemovedCol})),
|
||||
),
|
||||
handler.NewSuffixedTable([]*handler.InitColumn{
|
||||
handler.NewColumn(HumanUserIDCol, handler.ColumnTypeText),
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UserMetadataProjectionTable = "projections.user_metadata4"
|
||||
UserMetadataProjectionTable = "projections.user_metadata5"
|
||||
|
||||
UserMetadataColumnUserID = "user_id"
|
||||
UserMetadataColumnCreationDate = "creation_date"
|
||||
@ -23,7 +23,6 @@ const (
|
||||
UserMetadataColumnInstanceID = "instance_id"
|
||||
UserMetadataColumnKey = "key"
|
||||
UserMetadataColumnValue = "value"
|
||||
UserMetadataColumnOwnerRemoved = "owner_removed"
|
||||
)
|
||||
|
||||
type userMetadataProjection struct{}
|
||||
@ -47,11 +46,9 @@ func (*userMetadataProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(UserMetadataColumnInstanceID, handler.ColumnTypeText),
|
||||
handler.NewColumn(UserMetadataColumnKey, handler.ColumnTypeText),
|
||||
handler.NewColumn(UserMetadataColumnValue, handler.ColumnTypeBytes, handler.Nullable()),
|
||||
handler.NewColumn(UserMetadataColumnOwnerRemoved, handler.ColumnTypeBool, handler.Default(false)),
|
||||
},
|
||||
handler.NewPrimaryKey(UserMetadataColumnInstanceID, UserMetadataColumnUserID, UserMetadataColumnKey),
|
||||
handler.WithIndex(handler.NewIndex("resource_owner", []string{UserGrantResourceOwner})),
|
||||
handler.WithIndex(handler.NewIndex("owner_removed", []string{UserMetadataColumnOwnerRemoved})),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ func TestUserMetadataProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.user_metadata4 (instance_id, user_id, key, resource_owner, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (instance_id, user_id, key) DO UPDATE SET (resource_owner, creation_date, change_date, sequence, value) = (EXCLUDED.resource_owner, EXCLUDED.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
|
||||
expectedStmt: "INSERT INTO projections.user_metadata5 (instance_id, user_id, key, resource_owner, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (instance_id, user_id, key) DO UPDATE SET (resource_owner, creation_date, change_date, sequence, value) = (EXCLUDED.resource_owner, EXCLUDED.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
"agg-id",
|
||||
@ -76,7 +76,7 @@ func TestUserMetadataProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.user_metadata4 WHERE (user_id = $1) AND (key = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "DELETE FROM projections.user_metadata5 WHERE (user_id = $1) AND (key = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"key",
|
||||
@ -104,7 +104,7 @@ func TestUserMetadataProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.user_metadata4 WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.user_metadata5 WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -131,7 +131,7 @@ func TestUserMetadataProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.user_metadata4 WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.user_metadata5 WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -158,7 +158,7 @@ func TestUserMetadataProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.user_metadata4 WHERE (instance_id = $1) AND (resource_owner = $2)",
|
||||
expectedStmt: "DELETE FROM projections.user_metadata5 WHERE (instance_id = $1) AND (resource_owner = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
"agg-id",
|
||||
@ -185,7 +185,7 @@ func TestUserMetadataProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.user_metadata4 WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.user_metadata5 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
|
@ -50,7 +50,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.users9 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -64,7 +64,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedStmt: "INSERT INTO projections.users9_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -79,7 +79,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.users9_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -119,7 +119,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.users9 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -133,7 +133,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedStmt: "INSERT INTO projections.users9_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -148,7 +148,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.users9_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -183,7 +183,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.users9 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -197,7 +197,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedStmt: "INSERT INTO projections.users9_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -212,7 +212,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.users9_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -252,7 +252,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.users9 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -266,7 +266,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedStmt: "INSERT INTO projections.users9_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -281,7 +281,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.users9_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -321,7 +321,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.users9 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -335,7 +335,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedStmt: "INSERT INTO projections.users9_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -350,7 +350,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.users9_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -385,7 +385,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.users9 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -399,7 +399,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedStmt: "INSERT INTO projections.users9_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -414,7 +414,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.users9_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -444,7 +444,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET state = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9 SET state = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.UserStateInitial,
|
||||
"agg-id",
|
||||
@ -472,7 +472,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET state = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9 SET state = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.UserStateInitial,
|
||||
"agg-id",
|
||||
@ -500,7 +500,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET state = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9 SET state = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.UserStateActive,
|
||||
"agg-id",
|
||||
@ -528,7 +528,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET state = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9 SET state = $1 WHERE (id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.UserStateActive,
|
||||
"agg-id",
|
||||
@ -556,7 +556,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
domain.UserStateLocked,
|
||||
@ -586,7 +586,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
domain.UserStateActive,
|
||||
@ -616,7 +616,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
domain.UserStateInactive,
|
||||
@ -646,7 +646,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
domain.UserStateActive,
|
||||
@ -676,7 +676,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.users8 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.users9 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -705,7 +705,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
"username",
|
||||
@ -737,7 +737,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
"id@temporary.domain",
|
||||
@ -774,7 +774,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -783,7 +783,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"first-name",
|
||||
"last-name",
|
||||
@ -823,7 +823,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -832,7 +832,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"first-name",
|
||||
"last-name",
|
||||
@ -867,7 +867,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -876,7 +876,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.PhoneNumber("+41 00 000 00 00"),
|
||||
false,
|
||||
@ -885,7 +885,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET last_phone = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET last_phone = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||
"agg-id",
|
||||
@ -915,7 +915,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -924,7 +924,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.PhoneNumber("+41 00 000 00 00"),
|
||||
false,
|
||||
@ -933,7 +933,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET last_phone = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET last_phone = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||
"agg-id",
|
||||
@ -961,7 +961,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -970,7 +970,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
nil,
|
||||
nil,
|
||||
@ -979,7 +979,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET (last_phone, verified_phone) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET (last_phone, verified_phone) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
nil,
|
||||
nil,
|
||||
@ -1008,7 +1008,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1017,7 +1017,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
nil,
|
||||
nil,
|
||||
@ -1026,7 +1026,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET (last_phone, verified_phone) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET (last_phone, verified_phone) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
nil,
|
||||
nil,
|
||||
@ -1055,7 +1055,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1064,7 +1064,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET is_phone_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET is_phone_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
true,
|
||||
"agg-id",
|
||||
@ -1072,7 +1072,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET verified_phone = last_phone WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET verified_phone = last_phone WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -1099,7 +1099,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1108,7 +1108,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET is_phone_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET is_phone_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
true,
|
||||
"agg-id",
|
||||
@ -1116,7 +1116,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET verified_phone = last_phone WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET verified_phone = last_phone WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -1145,7 +1145,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1154,7 +1154,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
false,
|
||||
@ -1163,7 +1163,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET last_email = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET last_email = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
&sql.NullString{String: "email@zitadel.com", Valid: true},
|
||||
"agg-id",
|
||||
@ -1193,7 +1193,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1202,7 +1202,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
false,
|
||||
@ -1211,7 +1211,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET last_email = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET last_email = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
&sql.NullString{String: "email@zitadel.com", Valid: true},
|
||||
"agg-id",
|
||||
@ -1239,7 +1239,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1248,7 +1248,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET is_email_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET is_email_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
true,
|
||||
"agg-id",
|
||||
@ -1256,7 +1256,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET verified_email = last_email WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET verified_email = last_email WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -1283,7 +1283,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1292,7 +1292,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET is_email_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET is_email_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
true,
|
||||
"agg-id",
|
||||
@ -1300,7 +1300,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_notifications SET verified_email = last_email WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "UPDATE projections.users9_notifications SET verified_email = last_email WHERE (user_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -1329,7 +1329,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1338,7 +1338,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET avatar_key = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET avatar_key = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"users/agg-id/avatar",
|
||||
"agg-id",
|
||||
@ -1366,7 +1366,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1375,7 +1375,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_humans SET avatar_key = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_humans SET avatar_key = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
nil,
|
||||
"agg-id",
|
||||
@ -1406,7 +1406,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.users9 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -1420,7 +1420,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_machines (user_id, instance_id, name, description, access_token_type) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.users9_machines (user_id, instance_id, name, description, access_token_type) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -1454,7 +1454,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.users9 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -1468,7 +1468,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.users8_machines (user_id, instance_id, name, description, access_token_type) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.users9_machines (user_id, instance_id, name, description, access_token_type) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -1501,7 +1501,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1510,7 +1510,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_machines SET (name, description) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9_machines SET (name, description) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
"machine-name",
|
||||
"description",
|
||||
@ -1541,7 +1541,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1550,7 +1550,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_machines SET name = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_machines SET name = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"machine-name",
|
||||
"agg-id",
|
||||
@ -1580,7 +1580,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1589,7 +1589,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_machines SET description = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_machines SET description = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"description",
|
||||
"agg-id",
|
||||
@ -1638,7 +1638,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1647,7 +1647,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_machines SET has_secret = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_machines SET has_secret = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
true,
|
||||
"agg-id",
|
||||
@ -1675,7 +1675,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.users9 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -1684,7 +1684,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.users8_machines SET has_secret = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.users9_machines SET has_secret = $1 WHERE (user_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
false,
|
||||
"agg-id",
|
||||
@ -1712,7 +1712,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.users8 WHERE (instance_id = $1) AND (resource_owner = $2)",
|
||||
expectedStmt: "DELETE FROM projections.users9 WHERE (instance_id = $1) AND (resource_owner = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
"agg-id",
|
||||
@ -1739,7 +1739,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.users8 WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.users9 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
|
@ -9,15 +9,16 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/rakyll/statik/fs"
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
|
||||
sd "github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/repository/action"
|
||||
"github.com/zitadel/zitadel/internal/repository/authrequest"
|
||||
@ -32,15 +33,17 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/repository/session"
|
||||
usr_repo "github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/repository/usergrant"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
type Queries struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
client *database.DB
|
||||
|
||||
idpConfigEncryption crypto.EncryptionAlgorithm
|
||||
sessionTokenVerifier func(ctx context.Context, sessionToken string, sessionID string, tokenID string) (err error)
|
||||
checkPermission domain.PermissionCheck
|
||||
keyEncryptionAlgorithm crypto.EncryptionAlgorithm
|
||||
idpConfigEncryption crypto.EncryptionAlgorithm
|
||||
sessionTokenVerifier func(ctx context.Context, sessionToken string, sessionID string, tokenID string) (err error)
|
||||
checkPermission domain.PermissionCheck
|
||||
|
||||
DefaultLanguage language.Tag
|
||||
LoginDir http.FileSystem
|
||||
@ -65,7 +68,7 @@ func StartQueries(
|
||||
sessionTokenVerifier func(ctx context.Context, sessionToken string, sessionID string, tokenID string) (err error),
|
||||
permissionCheck func(q *Queries) domain.PermissionCheck,
|
||||
defaultAuditLogRetention time.Duration,
|
||||
systemAPIUsers map[string]*internal_authz.SystemAPIUser,
|
||||
systemAPIUsers map[string]*authz.SystemAPIUser,
|
||||
) (repo *Queries, err error) {
|
||||
statikLoginFS, err := fs.NewWithNamespace("login")
|
||||
if err != nil {
|
||||
@ -86,8 +89,16 @@ func StartQueries(
|
||||
LoginTranslationFileContents: make(map[string][]byte),
|
||||
NotificationTranslationFileContents: make(map[string][]byte),
|
||||
zitadelRoles: zitadelRoles,
|
||||
keyEncryptionAlgorithm: keyEncryptionAlgorithm,
|
||||
idpConfigEncryption: idpConfigEncryption,
|
||||
sessionTokenVerifier: sessionTokenVerifier,
|
||||
defaultAuditLogRetention: defaultAuditLogRetention,
|
||||
multifactors: domain.MultifactorConfigs{
|
||||
OTP: domain.OTPConfig{
|
||||
CryptoMFA: otpEncryption,
|
||||
Issuer: defaults.Multifactors.OTP.Issuer,
|
||||
},
|
||||
},
|
||||
defaultAuditLogRetention: defaultAuditLogRetention,
|
||||
}
|
||||
iam_repo.RegisterEventMappers(repo.eventstore)
|
||||
usr_repo.RegisterEventMappers(repo.eventstore)
|
||||
@ -103,14 +114,6 @@ func StartQueries(
|
||||
quota.RegisterEventMappers(repo.eventstore)
|
||||
limits.RegisterEventMappers(repo.eventstore)
|
||||
|
||||
repo.idpConfigEncryption = idpConfigEncryption
|
||||
repo.multifactors = domain.MultifactorConfigs{
|
||||
OTP: domain.OTPConfig{
|
||||
CryptoMFA: otpEncryption,
|
||||
Issuer: defaults.Multifactors.OTP.Issuer,
|
||||
},
|
||||
}
|
||||
|
||||
repo.checkPermission = permissionCheck(repo)
|
||||
|
||||
err = projection.Create(ctx, sqlClient, es, projections, keyEncryptionAlgorithm, certEncryptionAlgorithm, systemAPIUsers)
|
||||
@ -145,3 +148,24 @@ func init() {
|
||||
&authRequestByIDQuery,
|
||||
)
|
||||
}
|
||||
|
||||
// triggerBatch calls Trigger on every handler in a separate Go routine.
|
||||
// The returned context is the context returned by the Trigger that finishes last.
|
||||
func triggerBatch(ctx context.Context, handlers ...*handler.Handler) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(handlers))
|
||||
|
||||
for _, h := range handlers {
|
||||
go func(ctx context.Context, h *handler.Handler) {
|
||||
name := h.ProjectionName()
|
||||
_, traceSpan := tracing.NewNamedSpan(ctx, fmt.Sprintf("Trigger%s", name))
|
||||
_, err := h.Trigger(ctx, handler.WithAwaitRunning())
|
||||
logging.OnError(err).WithField("projection", name).Debug("trigger failed")
|
||||
traceSpan.EndWithError(err)
|
||||
|
||||
wg.Done()
|
||||
}(ctx, h)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
@ -1,11 +1,92 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository/mock"
|
||||
action_repo "github.com/zitadel/zitadel/internal/repository/action"
|
||||
"github.com/zitadel/zitadel/internal/repository/authrequest"
|
||||
"github.com/zitadel/zitadel/internal/repository/feature"
|
||||
"github.com/zitadel/zitadel/internal/repository/idpintent"
|
||||
iam_repo "github.com/zitadel/zitadel/internal/repository/instance"
|
||||
key_repo "github.com/zitadel/zitadel/internal/repository/keypair"
|
||||
"github.com/zitadel/zitadel/internal/repository/limits"
|
||||
"github.com/zitadel/zitadel/internal/repository/oidcsession"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
proj_repo "github.com/zitadel/zitadel/internal/repository/project"
|
||||
quota_repo "github.com/zitadel/zitadel/internal/repository/quota"
|
||||
"github.com/zitadel/zitadel/internal/repository/session"
|
||||
usr_repo "github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/repository/usergrant"
|
||||
)
|
||||
|
||||
type expect func(mockRepository *mock.MockRepository)
|
||||
|
||||
func expectEventstore(expects ...expect) func(*testing.T) *eventstore.Eventstore {
|
||||
return func(t *testing.T) *eventstore.Eventstore {
|
||||
m := mock.NewRepo(t)
|
||||
for _, e := range expects {
|
||||
e(m)
|
||||
}
|
||||
es := eventstore.NewEventstore(
|
||||
&eventstore.Config{
|
||||
Querier: m.MockQuerier,
|
||||
Pusher: m.MockPusher,
|
||||
},
|
||||
)
|
||||
iam_repo.RegisterEventMappers(es)
|
||||
org.RegisterEventMappers(es)
|
||||
usr_repo.RegisterEventMappers(es)
|
||||
proj_repo.RegisterEventMappers(es)
|
||||
usergrant.RegisterEventMappers(es)
|
||||
key_repo.RegisterEventMappers(es)
|
||||
action_repo.RegisterEventMappers(es)
|
||||
session.RegisterEventMappers(es)
|
||||
idpintent.RegisterEventMappers(es)
|
||||
authrequest.RegisterEventMappers(es)
|
||||
oidcsession.RegisterEventMappers(es)
|
||||
quota_repo.RegisterEventMappers(es)
|
||||
limits.RegisterEventMappers(es)
|
||||
feature.RegisterEventMappers(es)
|
||||
return es
|
||||
}
|
||||
}
|
||||
|
||||
func expectFilter(events ...eventstore.Event) expect {
|
||||
return func(m *mock.MockRepository) {
|
||||
m.ExpectFilterEvents(events...)
|
||||
}
|
||||
}
|
||||
func expectFilterError(err error) expect {
|
||||
return func(m *mock.MockRepository) {
|
||||
m.ExpectFilterEventsError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func eventFromEventPusher(event eventstore.Command) *repository.Event {
|
||||
data, _ := eventstore.EventData(event)
|
||||
return &repository.Event{
|
||||
InstanceID: event.Aggregate().InstanceID,
|
||||
ID: "",
|
||||
Seq: 0,
|
||||
CreationDate: time.Time{},
|
||||
Typ: event.Type(),
|
||||
Data: data,
|
||||
EditorUser: event.Creator(),
|
||||
Version: event.Aggregate().Version,
|
||||
AggregateID: event.Aggregate().ID,
|
||||
AggregateType: event.Aggregate().Type,
|
||||
ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""},
|
||||
Constraints: event.UniqueConstraints(),
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cleanStaticQueries(t *testing.T) {
|
||||
query := `select
|
||||
foo,
|
||||
|
@ -31,7 +31,7 @@ var (
|
||||
` projections.sessions8.user_resource_owner,` +
|
||||
` projections.sessions8.user_checked_at,` +
|
||||
` projections.login_names3.login_name,` +
|
||||
` projections.users8_humans.display_name,` +
|
||||
` projections.users9_humans.display_name,` +
|
||||
` projections.sessions8.password_checked_at,` +
|
||||
` projections.sessions8.intent_checked_at,` +
|
||||
` projections.sessions8.webauthn_checked_at,` +
|
||||
@ -48,8 +48,8 @@ var (
|
||||
` projections.sessions8.expiration` +
|
||||
` FROM projections.sessions8` +
|
||||
` LEFT JOIN projections.login_names3 ON projections.sessions8.user_id = projections.login_names3.user_id AND projections.sessions8.instance_id = projections.login_names3.instance_id` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.sessions8.user_id = projections.users8_humans.user_id AND projections.sessions8.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8 ON projections.sessions8.user_id = projections.users8.id AND projections.sessions8.instance_id = projections.users8.instance_id` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.sessions8.user_id = projections.users9_humans.user_id AND projections.sessions8.instance_id = projections.users9_humans.instance_id` +
|
||||
` LEFT JOIN projections.users9 ON projections.sessions8.user_id = projections.users9.id AND projections.sessions8.instance_id = projections.users9.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions8.id,` +
|
||||
` projections.sessions8.creation_date,` +
|
||||
@ -62,7 +62,7 @@ var (
|
||||
` projections.sessions8.user_resource_owner,` +
|
||||
` projections.sessions8.user_checked_at,` +
|
||||
` projections.login_names3.login_name,` +
|
||||
` projections.users8_humans.display_name,` +
|
||||
` projections.users9_humans.display_name,` +
|
||||
` projections.sessions8.password_checked_at,` +
|
||||
` projections.sessions8.intent_checked_at,` +
|
||||
` projections.sessions8.webauthn_checked_at,` +
|
||||
@ -75,8 +75,8 @@ var (
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.sessions8` +
|
||||
` LEFT JOIN projections.login_names3 ON projections.sessions8.user_id = projections.login_names3.user_id AND projections.sessions8.instance_id = projections.login_names3.instance_id` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.sessions8.user_id = projections.users8_humans.user_id AND projections.sessions8.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8 ON projections.sessions8.user_id = projections.users8.id AND projections.sessions8.instance_id = projections.users8.instance_id` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.sessions8.user_id = projections.users9_humans.user_id AND projections.sessions8.instance_id = projections.users9_humans.instance_id` +
|
||||
` LEFT JOIN projections.users9 ON projections.sessions8.user_id = projections.users9.id AND projections.sessions8.instance_id = projections.users9.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
|
||||
sessionCols = []string{
|
||||
|
47
internal/query/testdata/userinfo_human.json
vendored
Normal file
47
internal/query/testdata/userinfo_human.json
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"user": {
|
||||
"id": "231965491734773762",
|
||||
"creation_date": "2023-09-15T06:10:07.434142+00:00",
|
||||
"change_date": "2023-11-14T13:27:02.072318+00:00",
|
||||
"sequence": 1148,
|
||||
"state": 1,
|
||||
"resource_owner": "231848297847848962",
|
||||
"username": "tim+tesmail@zitadel.com",
|
||||
"preferred_login_name": "tim+tesmail@zitadel.com@demo.localhost",
|
||||
"human": {
|
||||
"first_name": "Tim",
|
||||
"last_name": "Mohlmann",
|
||||
"nick_name": "muhlemmer",
|
||||
"display_name": "Tim Mohlmann",
|
||||
"avatar_key": null,
|
||||
"email": "tim+tesmail@zitadel.com",
|
||||
"is_email_verified": true,
|
||||
"phone": "+40123456789",
|
||||
"is_phone_verified": false
|
||||
},
|
||||
"machine": null
|
||||
},
|
||||
"org": {
|
||||
"name": "demo",
|
||||
"primary_domain": "demo.localhost"
|
||||
},
|
||||
"metadata": [
|
||||
{
|
||||
"creation_date": "2023-11-14T13:26:03.553702+00:00",
|
||||
"change_date": "2023-11-14T13:26:03.553702+00:00",
|
||||
"sequence": 1147,
|
||||
"resource_owner": "231848297847848962",
|
||||
"key": "bar",
|
||||
"value": "Zm9v"
|
||||
},
|
||||
{
|
||||
"creation_date": "2023-11-14T13:25:57.171368+00:00",
|
||||
"change_date": "2023-11-14T13:25:57.171368+00:00",
|
||||
"sequence": 1146,
|
||||
"resource_owner": "231848297847848962",
|
||||
"key": "foo",
|
||||
"value": "YmFy"
|
||||
}
|
||||
],
|
||||
"user_grants": null
|
||||
}
|
86
internal/query/testdata/userinfo_human_grants.json
vendored
Normal file
86
internal/query/testdata/userinfo_human_grants.json
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"user": {
|
||||
"id": "231965491734773762",
|
||||
"creation_date": "2023-09-15T06:10:07.434142+00:00",
|
||||
"change_date": "2023-11-14T13:27:02.072318+00:00",
|
||||
"sequence": 1148,
|
||||
"state": 1,
|
||||
"resource_owner": "231848297847848962",
|
||||
"username": "tim+tesmail@zitadel.com",
|
||||
"preferred_login_name": "tim+tesmail@zitadel.com@demo.localhost",
|
||||
"human": {
|
||||
"first_name": "Tim",
|
||||
"last_name": "Mohlmann",
|
||||
"nick_name": "muhlemmer",
|
||||
"display_name": "Tim Mohlmann",
|
||||
"avatar_key": null,
|
||||
"email": "tim+tesmail@zitadel.com",
|
||||
"is_email_verified": true,
|
||||
"phone": "+40123456789",
|
||||
"is_phone_verified": false
|
||||
},
|
||||
"machine": null
|
||||
},
|
||||
"org": {
|
||||
"name": "demo",
|
||||
"primary_domain": "demo.localhost"
|
||||
},
|
||||
"metadata": [
|
||||
{
|
||||
"creation_date": "2023-11-14T13:26:03.553702+00:00",
|
||||
"change_date": "2023-11-14T13:26:03.553702+00:00",
|
||||
"sequence": 1147,
|
||||
"resource_owner": "231848297847848962",
|
||||
"key": "bar",
|
||||
"value": "Zm9v"
|
||||
},
|
||||
{
|
||||
"creation_date": "2023-11-14T13:25:57.171368+00:00",
|
||||
"change_date": "2023-11-14T13:25:57.171368+00:00",
|
||||
"sequence": 1146,
|
||||
"resource_owner": "231848297847848962",
|
||||
"key": "foo",
|
||||
"value": "YmFy"
|
||||
}
|
||||
],
|
||||
"user_grants": [
|
||||
{
|
||||
"id": "240749256523120642",
|
||||
"grant_id": "",
|
||||
"state": 1,
|
||||
"creation_date": "2023-11-14T20:28:59.168208+00:00",
|
||||
"change_date": "2023-11-14T20:50:58.822391+00:00",
|
||||
"sequence": 2,
|
||||
"user_id": "231965491734773762",
|
||||
"roles": [
|
||||
"role1",
|
||||
"role2"
|
||||
],
|
||||
"resource_owner": "231848297847848962",
|
||||
"project_id": "236645808328409090",
|
||||
"org_name": "demo",
|
||||
"org_primary_domain": "demo.localhost",
|
||||
"project_name": "tests",
|
||||
"user_resource_owner": "231848297847848962"
|
||||
},
|
||||
{
|
||||
"id": "240762315572510722",
|
||||
"grant_id": "",
|
||||
"state": 1,
|
||||
"creation_date": "2023-11-14T22:38:42.967317+00:00",
|
||||
"change_date": "2023-11-14T22:38:42.967317+00:00",
|
||||
"sequence": 1,
|
||||
"user_id": "231965491734773762",
|
||||
"roles": [
|
||||
"role3",
|
||||
"role4"
|
||||
],
|
||||
"resource_owner": "231848297847848962",
|
||||
"project_id": "240762134579904514",
|
||||
"org_name": "demo",
|
||||
"org_primary_domain": "demo.localhost",
|
||||
"project_name": "tests2",
|
||||
"user_resource_owner": "231848297847848962"
|
||||
}
|
||||
]
|
||||
}
|
30
internal/query/testdata/userinfo_human_no_md.json
vendored
Normal file
30
internal/query/testdata/userinfo_human_no_md.json
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"user": {
|
||||
"id": "231965491734773762",
|
||||
"creation_date": "2023-09-15T06:10:07.434142+00:00",
|
||||
"change_date": "2023-11-14T13:27:02.072318+00:00",
|
||||
"sequence": 1148,
|
||||
"state": 1,
|
||||
"resource_owner": "231848297847848962",
|
||||
"username": "tim+tesmail@zitadel.com",
|
||||
"preferred_login_name": "tim+tesmail@zitadel.com@demo.localhost",
|
||||
"human": {
|
||||
"first_name": "Tim",
|
||||
"last_name": "Mohlmann",
|
||||
"nick_name": "muhlemmer",
|
||||
"display_name": "Tim Mohlmann",
|
||||
"avatar_key": null,
|
||||
"email": "tim+tesmail@zitadel.com",
|
||||
"is_email_verified": true,
|
||||
"phone": "+40123456789",
|
||||
"is_phone_verified": false
|
||||
},
|
||||
"machine": null
|
||||
},
|
||||
"org": {
|
||||
"name": "demo",
|
||||
"primary_domain": "demo.localhost"
|
||||
},
|
||||
"metadata": null,
|
||||
"user_grants": null
|
||||
}
|
40
internal/query/testdata/userinfo_machine.json
vendored
Normal file
40
internal/query/testdata/userinfo_machine.json
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"user": {
|
||||
"id": "240707570677841922",
|
||||
"creation_date": "2023-11-14T13:34:52.473732+00:00",
|
||||
"change_date": "2023-11-14T13:35:02.861342+00:00",
|
||||
"sequence": 2,
|
||||
"state": 1,
|
||||
"resource_owner": "231848297847848962",
|
||||
"username": "tests",
|
||||
"preferred_login_name": "tests@demo.localhost",
|
||||
"human": null,
|
||||
"machine": {
|
||||
"name": "tests",
|
||||
"description": "My test service user"
|
||||
}
|
||||
},
|
||||
"org": {
|
||||
"name": "demo",
|
||||
"primary_domain": "demo.localhost"
|
||||
},
|
||||
"metadata": [
|
||||
{
|
||||
"creation_date": "2023-11-14T13:35:30.126849+00:00",
|
||||
"change_date": "2023-11-14T13:35:30.126849+00:00",
|
||||
"sequence": 3,
|
||||
"resource_owner": "231848297847848962",
|
||||
"key": "first",
|
||||
"value": "SGVsbG8gV29ybGQh"
|
||||
},
|
||||
{
|
||||
"creation_date": "2023-11-14T13:35:44.028343+00:00",
|
||||
"change_date": "2023-11-14T13:35:44.028343+00:00",
|
||||
"sequence": 4,
|
||||
"resource_owner": "231848297847848962",
|
||||
"key": "second",
|
||||
"value": "QnllIFdvcmxkIQ=="
|
||||
}
|
||||
],
|
||||
"user_grants": null
|
||||
}
|
6
internal/query/testdata/userinfo_not_found.json
vendored
Normal file
6
internal/query/testdata/userinfo_not_found.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"user": null,
|
||||
"org": null,
|
||||
"metadata": null,
|
||||
"user_grants": null
|
||||
}
|
@ -5,11 +5,9 @@ import (
|
||||
"database/sql"
|
||||
errs "errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
@ -17,7 +15,6 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
@ -28,32 +25,32 @@ type Users struct {
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
Sequence uint64
|
||||
State domain.UserState
|
||||
Type domain.UserType
|
||||
Username string
|
||||
LoginNames database.TextArray[string]
|
||||
PreferredLoginName string
|
||||
Human *Human
|
||||
Machine *Machine
|
||||
ID string `json:"id,omitempty"`
|
||||
CreationDate time.Time `json:"creation_date,omitempty"`
|
||||
ChangeDate time.Time `json:"change_date,omitempty"`
|
||||
ResourceOwner string `json:"resource_owner,omitempty"`
|
||||
Sequence uint64 `json:"sequence,omitempty"`
|
||||
State domain.UserState `json:"state,omitempty"`
|
||||
Type domain.UserType `json:"type,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
LoginNames database.TextArray[string] `json:"login_names,omitempty"`
|
||||
PreferredLoginName string `json:"preferred_login_name,omitempty"`
|
||||
Human *Human `json:"human,omitempty"`
|
||||
Machine *Machine `json:"machine,omitempty"`
|
||||
}
|
||||
|
||||
type Human struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
DisplayName string
|
||||
AvatarKey string
|
||||
PreferredLanguage language.Tag
|
||||
Gender domain.Gender
|
||||
Email domain.EmailAddress
|
||||
IsEmailVerified bool
|
||||
Phone domain.PhoneNumber
|
||||
IsPhoneVerified bool
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
NickName string `json:"nick_name,omitempty"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
AvatarKey string `json:"avatar_key,omitempty"`
|
||||
PreferredLanguage language.Tag `json:"preferred_language,omitempty"`
|
||||
Gender domain.Gender `json:"gender,omitempty"`
|
||||
Email domain.EmailAddress `json:"email,omitempty"`
|
||||
IsEmailVerified bool `json:"is_email_verified,omitempty"`
|
||||
Phone domain.PhoneNumber `json:"phone,omitempty"`
|
||||
IsPhoneVerified bool `json:"is_phone_verified,omitempty"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
@ -92,10 +89,10 @@ type Phone struct {
|
||||
}
|
||||
|
||||
type Machine struct {
|
||||
Name string
|
||||
Description string
|
||||
HasSecret bool
|
||||
AccessTokenType domain.OIDCTokenType
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
HasSecret bool `json:"has_secret,omitempty"`
|
||||
AccessTokenType domain.OIDCTokenType `json:"access_token_type,omitempty"`
|
||||
}
|
||||
|
||||
type NotifyUser struct {
|
||||
@ -170,10 +167,6 @@ var (
|
||||
name: projection.UserTypeCol,
|
||||
table: userTable,
|
||||
}
|
||||
UserOwnerRemovedCol = Column{
|
||||
name: projection.UserOwnerRemovedCol,
|
||||
table: userTable,
|
||||
}
|
||||
|
||||
userLoginNamesTable = loginNameTable.setAlias("login_names")
|
||||
userLoginNamesUserIDCol = LoginNameUserIDCol.setTable(userLoginNamesTable)
|
||||
@ -320,11 +313,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func addUserWithoutOwnerRemoved(eq map[string]interface{}) {
|
||||
eq[UserOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userID string, withOwnerRemoved bool, queries ...SearchQuery) (user *User, err error) {
|
||||
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userID string, queries ...SearchQuery) (user *User, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -340,9 +329,6 @@ func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userI
|
||||
UserIDCol.identifier(): userID,
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
addUserWithoutOwnerRemoved(eq)
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-FBg21", "Errors.Query.SQLStatment")
|
||||
@ -355,7 +341,7 @@ func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userI
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, withOwnerRemoved bool, queries ...SearchQuery) (user *User, err error) {
|
||||
func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, queries ...SearchQuery) (user *User, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -370,9 +356,6 @@ func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, withOwner
|
||||
eq := sq.Eq{
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
addUserWithoutOwnerRemoved(eq)
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dnhr2", "Errors.Query.SQLStatment")
|
||||
@ -385,7 +368,7 @@ func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, withOwner
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (q *Queries) GetHumanProfile(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (profile *Profile, err error) {
|
||||
func (q *Queries) GetHumanProfile(ctx context.Context, userID string, queries ...SearchQuery) (profile *Profile, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -397,9 +380,6 @@ func (q *Queries) GetHumanProfile(ctx context.Context, userID string, withOwnerR
|
||||
UserIDCol.identifier(): userID,
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[UserOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dgbg2", "Errors.Query.SQLStatment")
|
||||
@ -412,7 +392,7 @@ func (q *Queries) GetHumanProfile(ctx context.Context, userID string, withOwnerR
|
||||
return profile, err
|
||||
}
|
||||
|
||||
func (q *Queries) GetHumanEmail(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (email *Email, err error) {
|
||||
func (q *Queries) GetHumanEmail(ctx context.Context, userID string, queries ...SearchQuery) (email *Email, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -424,9 +404,6 @@ func (q *Queries) GetHumanEmail(ctx context.Context, userID string, withOwnerRem
|
||||
UserIDCol.identifier(): userID,
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[UserOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-BHhj3", "Errors.Query.SQLStatment")
|
||||
@ -439,7 +416,7 @@ func (q *Queries) GetHumanEmail(ctx context.Context, userID string, withOwnerRem
|
||||
return email, err
|
||||
}
|
||||
|
||||
func (q *Queries) GetHumanPhone(ctx context.Context, userID string, withOwnerRemoved bool, queries ...SearchQuery) (phone *Phone, err error) {
|
||||
func (q *Queries) GetHumanPhone(ctx context.Context, userID string, queries ...SearchQuery) (phone *Phone, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -451,9 +428,6 @@ func (q *Queries) GetHumanPhone(ctx context.Context, userID string, withOwnerRem
|
||||
UserIDCol.identifier(): userID,
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[UserOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Dg43g", "Errors.Query.SQLStatment")
|
||||
@ -466,7 +440,7 @@ func (q *Queries) GetHumanPhone(ctx context.Context, userID string, withOwnerRem
|
||||
return phone, err
|
||||
}
|
||||
|
||||
func (q *Queries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, userID string, withOwnerRemoved bool, queries ...SearchQuery) (user *NotifyUser, err error) {
|
||||
func (q *Queries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, userID string, queries ...SearchQuery) (user *NotifyUser, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -482,9 +456,6 @@ func (q *Queries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, u
|
||||
UserIDCol.identifier(): userID,
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
addUserWithoutOwnerRemoved(eq)
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Err3g", "Errors.Query.SQLStatment")
|
||||
@ -497,7 +468,7 @@ func (q *Queries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, u
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, withOwnerRemoved bool, queries ...SearchQuery) (user *NotifyUser, err error) {
|
||||
func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, queries ...SearchQuery) (user *NotifyUser, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -512,9 +483,6 @@ func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, withO
|
||||
eq := sq.Eq{
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
addUserWithoutOwnerRemoved(eq)
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Err3g", "Errors.Query.SQLStatment")
|
||||
@ -527,15 +495,12 @@ func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, withO
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, withOwnerRemoved bool) (users *Users, err error) {
|
||||
func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries) (users *Users, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
query, scan := prepareUsersQuery(ctx, q.client)
|
||||
eq := sq.Eq{UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID()}
|
||||
if !withOwnerRemoved {
|
||||
addUserWithoutOwnerRemoved(eq)
|
||||
}
|
||||
stmt, args, err := queries.toQuery(query).Where(eq).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
@ -554,7 +519,7 @@ func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, w
|
||||
return users, err
|
||||
}
|
||||
|
||||
func (q *Queries) IsUserUnique(ctx context.Context, username, email, resourceOwner string, withOwnerRemoved bool) (isUnique bool, err error) {
|
||||
func (q *Queries) IsUserUnique(ctx context.Context, username, email, resourceOwner string) (isUnique bool, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@ -585,9 +550,6 @@ func (q *Queries) IsUserUnique(ctx context.Context, username, email, resourceOwn
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
eq := sq.Eq{UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID()}
|
||||
if !withOwnerRemoved {
|
||||
eq[UserOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return false, errors.ThrowInternal(err, "QUERY-Dg43g", "Errors.Query.SQLStatment")
|
||||
@ -715,23 +677,7 @@ func NewUserLoginNameExistsQuery(value string, comparison TextComparison) (Searc
|
||||
}
|
||||
|
||||
func triggerUserProjections(ctx context.Context) {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
func() {
|
||||
_, traceSpan := tracing.NewNamedSpan(ctx, "TriggerUserProjection")
|
||||
_, err := projection.UserProjection.Trigger(ctx, handler.WithAwaitRunning())
|
||||
logging.OnError(err).Debug("trigger failed")
|
||||
traceSpan.EndWithError(err)
|
||||
wg.Done()
|
||||
}()
|
||||
func() {
|
||||
_, traceSpan := tracing.NewNamedSpan(ctx, "TriggerLoginNameProjection")
|
||||
_, err := projection.LoginNameProjection.Trigger(ctx, handler.WithAwaitRunning())
|
||||
traceSpan.EndWithError(err)
|
||||
logging.OnError(err).Debug("trigger failed")
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
triggerBatch(ctx, projection.UserProjection, projection.LoginNameProjection)
|
||||
}
|
||||
|
||||
func prepareLoginNamesQuery() (string, []interface{}, error) {
|
||||
|
@ -141,7 +141,7 @@ func (q *Queries) SearchUserAuthMethods(ctx context.Context, queries *UserAuthMe
|
||||
return userAuthMethods, err
|
||||
}
|
||||
|
||||
func (q *Queries) ListActiveUserAuthMethodTypes(ctx context.Context, userID string, withOwnerRemoved bool) (userAuthMethodTypes *AuthMethodTypes, err error) {
|
||||
func (q *Queries) ListActiveUserAuthMethodTypes(ctx context.Context, userID string) (userAuthMethodTypes *AuthMethodTypes, err error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
if ctxData.UserID != userID {
|
||||
if err := q.checkPermission(ctx, domain.PermissionUserRead, ctxData.OrgID, userID); err != nil {
|
||||
@ -156,9 +156,6 @@ func (q *Queries) ListActiveUserAuthMethodTypes(ctx context.Context, userID stri
|
||||
UserIDCol.identifier(): userID,
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[UserOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInvalidArgument(err, "QUERY-Sfdrg", "Errors.Query.InvalidRequest")
|
||||
@ -175,7 +172,7 @@ func (q *Queries) ListActiveUserAuthMethodTypes(ctx context.Context, userID stri
|
||||
return userAuthMethodTypes, err
|
||||
}
|
||||
|
||||
func (q *Queries) ListUserAuthMethodTypesRequired(ctx context.Context, userID string, withOwnerRemoved bool) (userAuthMethodTypes []domain.UserAuthMethodType, forceMFA, forceMFALocalOnly bool, err error) {
|
||||
func (q *Queries) ListUserAuthMethodTypesRequired(ctx context.Context, userID string) (userAuthMethodTypes []domain.UserAuthMethodType, forceMFA, forceMFALocalOnly bool, err error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
if ctxData.UserID != userID {
|
||||
if err := q.checkPermission(ctx, domain.PermissionUserRead, ctxData.OrgID, userID); err != nil {
|
||||
@ -190,9 +187,6 @@ func (q *Queries) ListUserAuthMethodTypesRequired(ctx context.Context, userID st
|
||||
UserIDCol.identifier(): userID,
|
||||
UserInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[UserOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, false, false, errors.ThrowInvalidArgument(err, "QUERY-E5ut4", "Errors.Query.InvalidRequest")
|
||||
|
@ -39,38 +39,38 @@ var (
|
||||
"method_type",
|
||||
"count",
|
||||
}
|
||||
prepareActiveAuthMethodTypesStmt = `SELECT projections.users8_notifications.password_set,` +
|
||||
prepareActiveAuthMethodTypesStmt = `SELECT projections.users9_notifications.password_set,` +
|
||||
` auth_method_types.method_type,` +
|
||||
` user_idps_count.count` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_notifications ON projections.users8.id = projections.users8_notifications.user_id AND projections.users8.instance_id = projections.users8_notifications.instance_id` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_notifications ON projections.users9.id = projections.users9_notifications.user_id AND projections.users9.instance_id = projections.users9_notifications.instance_id` +
|
||||
` LEFT JOIN (SELECT DISTINCT(auth_method_types.method_type), auth_method_types.user_id, auth_method_types.instance_id FROM projections.user_auth_methods4 AS auth_method_types` +
|
||||
` WHERE auth_method_types.state = $1) AS auth_method_types` +
|
||||
` ON auth_method_types.user_id = projections.users8.id AND auth_method_types.instance_id = projections.users8.instance_id` +
|
||||
` ON auth_method_types.user_id = projections.users9.id AND auth_method_types.instance_id = projections.users9.instance_id` +
|
||||
` LEFT JOIN (SELECT user_idps_count.user_id, user_idps_count.instance_id, COUNT(user_idps_count.user_id) AS count FROM projections.idp_user_links3 AS user_idps_count` +
|
||||
` GROUP BY user_idps_count.user_id, user_idps_count.instance_id) AS user_idps_count` +
|
||||
` ON user_idps_count.user_id = projections.users8.id AND user_idps_count.instance_id = projections.users8.instance_id` +
|
||||
` ON user_idps_count.user_id = projections.users9.id AND user_idps_count.instance_id = projections.users9.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms`
|
||||
prepareActiveAuthMethodTypesCols = []string{
|
||||
"password_set",
|
||||
"method_type",
|
||||
"idps_count",
|
||||
}
|
||||
prepareAuthMethodTypesRequiredStmt = `SELECT projections.users8_notifications.password_set,` +
|
||||
prepareAuthMethodTypesRequiredStmt = `SELECT projections.users9_notifications.password_set,` +
|
||||
` auth_method_types.method_type,` +
|
||||
` user_idps_count.count,` +
|
||||
` auth_methods_force_mfa.force_mfa,` +
|
||||
` auth_methods_force_mfa.force_mfa_local_only` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_notifications ON projections.users8.id = projections.users8_notifications.user_id AND projections.users8.instance_id = projections.users8_notifications.instance_id` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_notifications ON projections.users9.id = projections.users9_notifications.user_id AND projections.users9.instance_id = projections.users9_notifications.instance_id` +
|
||||
` LEFT JOIN (SELECT DISTINCT(auth_method_types.method_type), auth_method_types.user_id, auth_method_types.instance_id FROM projections.user_auth_methods4 AS auth_method_types` +
|
||||
` WHERE auth_method_types.state = $1) AS auth_method_types` +
|
||||
` ON auth_method_types.user_id = projections.users8.id AND auth_method_types.instance_id = projections.users8.instance_id` +
|
||||
` ON auth_method_types.user_id = projections.users9.id AND auth_method_types.instance_id = projections.users9.instance_id` +
|
||||
` LEFT JOIN (SELECT user_idps_count.user_id, user_idps_count.instance_id, COUNT(user_idps_count.user_id) AS count FROM projections.idp_user_links3 AS user_idps_count` +
|
||||
` GROUP BY user_idps_count.user_id, user_idps_count.instance_id) AS user_idps_count` +
|
||||
` ON user_idps_count.user_id = projections.users8.id AND user_idps_count.instance_id = projections.users8.instance_id` +
|
||||
` ON user_idps_count.user_id = projections.users9.id AND user_idps_count.instance_id = projections.users9.instance_id` +
|
||||
` LEFT JOIN (SELECT auth_methods_force_mfa.force_mfa, auth_methods_force_mfa.force_mfa_local_only, auth_methods_force_mfa.instance_id, auth_methods_force_mfa.aggregate_id FROM projections.login_policies5 AS auth_methods_force_mfa ORDER BY auth_methods_force_mfa.is_default) AS auth_methods_force_mfa` +
|
||||
` ON (auth_methods_force_mfa.aggregate_id = projections.users8.instance_id OR auth_methods_force_mfa.aggregate_id = projections.users8.resource_owner) AND auth_methods_force_mfa.instance_id = projections.users8.instance_id` +
|
||||
` ON (auth_methods_force_mfa.aggregate_id = projections.users9.instance_id OR auth_methods_force_mfa.aggregate_id = projections.users9.resource_owner) AND auth_methods_force_mfa.instance_id = projections.users9.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms
|
||||
`
|
||||
prepareAuthMethodTypesRequiredCols = []string{
|
||||
|
@ -22,32 +22,32 @@ import (
|
||||
|
||||
type UserGrant struct {
|
||||
// ID represents the aggregate id (id of the user grant)
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
Sequence uint64
|
||||
Roles database.TextArray[string]
|
||||
ID string `json:"id,omitempty"`
|
||||
CreationDate time.Time `json:"creation_date,omitempty"`
|
||||
ChangeDate time.Time `json:"change_date,omitempty"`
|
||||
Sequence uint64 `json:"sequence,omitempty"`
|
||||
Roles database.TextArray[string] `json:"roles,omitempty"`
|
||||
// GrantID represents the project grant id
|
||||
GrantID string
|
||||
State domain.UserGrantState
|
||||
GrantID string `json:"grant_id,omitempty"`
|
||||
State domain.UserGrantState `json:"state,omitempty"`
|
||||
|
||||
UserID string
|
||||
Username string
|
||||
UserType domain.UserType
|
||||
UserResourceOwner string
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
DisplayName string
|
||||
AvatarURL string
|
||||
PreferredLoginName string
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
UserType domain.UserType `json:"user_type,omitempty"`
|
||||
UserResourceOwner string `json:"user_resource_owner,omitempty"`
|
||||
FirstName string `json:"first_name,omitempty"`
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
AvatarURL string `json:"avatar_url,omitempty"`
|
||||
PreferredLoginName string `json:"preferred_login_name,omitempty"`
|
||||
|
||||
ResourceOwner string
|
||||
OrgName string
|
||||
OrgPrimaryDomain string
|
||||
ResourceOwner string `json:"resource_owner,omitempty"`
|
||||
OrgName string `json:"org_name,omitempty"`
|
||||
OrgPrimaryDomain string `json:"org_primary_domain,omitempty"`
|
||||
|
||||
ProjectID string
|
||||
ProjectName string
|
||||
ProjectID string `json:"project_id,omitempty"`
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
}
|
||||
|
||||
type UserGrants struct {
|
||||
|
@ -23,14 +23,14 @@ var (
|
||||
", projections.user_grants3.roles" +
|
||||
", projections.user_grants3.state" +
|
||||
", projections.user_grants3.user_id" +
|
||||
", projections.users8.username" +
|
||||
", projections.users8.type" +
|
||||
", projections.users8.resource_owner" +
|
||||
", projections.users8_humans.first_name" +
|
||||
", projections.users8_humans.last_name" +
|
||||
", projections.users8_humans.email" +
|
||||
", projections.users8_humans.display_name" +
|
||||
", projections.users8_humans.avatar_key" +
|
||||
", projections.users9.username" +
|
||||
", projections.users9.type" +
|
||||
", projections.users9.resource_owner" +
|
||||
", projections.users9_humans.first_name" +
|
||||
", projections.users9_humans.last_name" +
|
||||
", projections.users9_humans.email" +
|
||||
", projections.users9_humans.display_name" +
|
||||
", projections.users9_humans.avatar_key" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.user_grants3.resource_owner" +
|
||||
", projections.orgs1.name" +
|
||||
@ -38,8 +38,8 @@ var (
|
||||
", projections.user_grants3.project_id" +
|
||||
", projections.projects4.name" +
|
||||
" FROM projections.user_grants3" +
|
||||
" LEFT JOIN projections.users8 ON projections.user_grants3.user_id = projections.users8.id AND projections.user_grants3.instance_id = projections.users8.instance_id" +
|
||||
" LEFT JOIN projections.users8_humans ON projections.user_grants3.user_id = projections.users8_humans.user_id AND projections.user_grants3.instance_id = projections.users8_humans.instance_id" +
|
||||
" LEFT JOIN projections.users9 ON projections.user_grants3.user_id = projections.users9.id AND projections.user_grants3.instance_id = projections.users9.instance_id" +
|
||||
" LEFT JOIN projections.users9_humans ON projections.user_grants3.user_id = projections.users9_humans.user_id AND projections.user_grants3.instance_id = projections.users9_humans.instance_id" +
|
||||
" LEFT JOIN projections.orgs1 ON projections.user_grants3.resource_owner = projections.orgs1.id AND projections.user_grants3.instance_id = projections.orgs1.instance_id" +
|
||||
" LEFT JOIN projections.projects4 ON projections.user_grants3.project_id = projections.projects4.id AND projections.user_grants3.instance_id = projections.projects4.instance_id" +
|
||||
" LEFT JOIN projections.login_names3 ON projections.user_grants3.user_id = projections.login_names3.user_id AND projections.user_grants3.instance_id = projections.login_names3.instance_id" +
|
||||
@ -78,14 +78,14 @@ var (
|
||||
", projections.user_grants3.roles" +
|
||||
", projections.user_grants3.state" +
|
||||
", projections.user_grants3.user_id" +
|
||||
", projections.users8.username" +
|
||||
", projections.users8.type" +
|
||||
", projections.users8.resource_owner" +
|
||||
", projections.users8_humans.first_name" +
|
||||
", projections.users8_humans.last_name" +
|
||||
", projections.users8_humans.email" +
|
||||
", projections.users8_humans.display_name" +
|
||||
", projections.users8_humans.avatar_key" +
|
||||
", projections.users9.username" +
|
||||
", projections.users9.type" +
|
||||
", projections.users9.resource_owner" +
|
||||
", projections.users9_humans.first_name" +
|
||||
", projections.users9_humans.last_name" +
|
||||
", projections.users9_humans.email" +
|
||||
", projections.users9_humans.display_name" +
|
||||
", projections.users9_humans.avatar_key" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.user_grants3.resource_owner" +
|
||||
", projections.orgs1.name" +
|
||||
@ -94,8 +94,8 @@ var (
|
||||
", projections.projects4.name" +
|
||||
", COUNT(*) OVER ()" +
|
||||
" FROM projections.user_grants3" +
|
||||
" LEFT JOIN projections.users8 ON projections.user_grants3.user_id = projections.users8.id AND projections.user_grants3.instance_id = projections.users8.instance_id" +
|
||||
" LEFT JOIN projections.users8_humans ON projections.user_grants3.user_id = projections.users8_humans.user_id AND projections.user_grants3.instance_id = projections.users8_humans.instance_id" +
|
||||
" LEFT JOIN projections.users9 ON projections.user_grants3.user_id = projections.users9.id AND projections.user_grants3.instance_id = projections.users9.instance_id" +
|
||||
" LEFT JOIN projections.users9_humans ON projections.user_grants3.user_id = projections.users9_humans.user_id AND projections.user_grants3.instance_id = projections.users9_humans.instance_id" +
|
||||
" LEFT JOIN projections.orgs1 ON projections.user_grants3.resource_owner = projections.orgs1.id AND projections.user_grants3.instance_id = projections.orgs1.instance_id" +
|
||||
" LEFT JOIN projections.projects4 ON projections.user_grants3.project_id = projections.projects4.id AND projections.user_grants3.instance_id = projections.projects4.instance_id" +
|
||||
" LEFT JOIN projections.login_names3 ON projections.user_grants3.user_id = projections.login_names3.user_id AND projections.user_grants3.instance_id = projections.login_names3.instance_id" +
|
||||
|
@ -24,12 +24,12 @@ type UserMetadataList struct {
|
||||
}
|
||||
|
||||
type UserMetadata struct {
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
Sequence uint64
|
||||
Key string
|
||||
Value []byte
|
||||
CreationDate time.Time `json:"creation_date,omitempty"`
|
||||
ChangeDate time.Time `json:"change_date,omitempty"`
|
||||
ResourceOwner string `json:"resource_owner,omitempty"`
|
||||
Sequence uint64 `json:"sequence,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
Value []byte `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
type UserMetadataSearchQueries struct {
|
||||
@ -74,10 +74,6 @@ var (
|
||||
name: projection.UserMetadataColumnValue,
|
||||
table: userMetadataTable,
|
||||
}
|
||||
UserMetadataOwnerRemovedCol = Column{
|
||||
name: projection.UserMetadataColumnOwnerRemoved,
|
||||
table: userMetadataTable,
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) GetUserMetadataByKey(ctx context.Context, shouldTriggerBulk bool, userID, key string, withOwnerRemoved bool, queries ...SearchQuery) (metadata *UserMetadata, err error) {
|
||||
@ -100,9 +96,6 @@ func (q *Queries) GetUserMetadataByKey(ctx context.Context, shouldTriggerBulk bo
|
||||
UserMetadataKeyCol.identifier(): key,
|
||||
UserMetadataInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[UserMetadataOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := query.Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-aDGG2", "Errors.Query.SQLStatment")
|
||||
@ -131,9 +124,6 @@ func (q *Queries) SearchUserMetadata(ctx context.Context, shouldTriggerBulk bool
|
||||
UserMetadataUserIDCol.identifier(): userID,
|
||||
UserMetadataInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
|
||||
}
|
||||
if !withOwnerRemoved {
|
||||
eq[UserMetadataOwnerRemovedCol.identifier()] = false
|
||||
}
|
||||
stmt, args, err := queries.toQuery(query).Where(eq).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Egbgd", "Errors.Query.SQLStatment")
|
||||
|
@ -12,13 +12,13 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
userMetadataQuery = `SELECT projections.user_metadata4.creation_date,` +
|
||||
` projections.user_metadata4.change_date,` +
|
||||
` projections.user_metadata4.resource_owner,` +
|
||||
` projections.user_metadata4.sequence,` +
|
||||
` projections.user_metadata4.key,` +
|
||||
` projections.user_metadata4.value` +
|
||||
` FROM projections.user_metadata4` +
|
||||
userMetadataQuery = `SELECT projections.user_metadata5.creation_date,` +
|
||||
` projections.user_metadata5.change_date,` +
|
||||
` projections.user_metadata5.resource_owner,` +
|
||||
` projections.user_metadata5.sequence,` +
|
||||
` projections.user_metadata5.key,` +
|
||||
` projections.user_metadata5.value` +
|
||||
` FROM projections.user_metadata5` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
userMetadataCols = []string{
|
||||
"creation_date",
|
||||
@ -28,14 +28,14 @@ var (
|
||||
"key",
|
||||
"value",
|
||||
}
|
||||
userMetadataListQuery = `SELECT projections.user_metadata4.creation_date,` +
|
||||
` projections.user_metadata4.change_date,` +
|
||||
` projections.user_metadata4.resource_owner,` +
|
||||
` projections.user_metadata4.sequence,` +
|
||||
` projections.user_metadata4.key,` +
|
||||
` projections.user_metadata4.value,` +
|
||||
userMetadataListQuery = `SELECT projections.user_metadata5.creation_date,` +
|
||||
` projections.user_metadata5.change_date,` +
|
||||
` projections.user_metadata5.resource_owner,` +
|
||||
` projections.user_metadata5.sequence,` +
|
||||
` projections.user_metadata5.key,` +
|
||||
` projections.user_metadata5.value,` +
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.user_metadata4`
|
||||
` FROM projections.user_metadata5`
|
||||
userMetadataListCols = []string{
|
||||
"creation_date",
|
||||
"change_date",
|
||||
|
@ -22,43 +22,43 @@ var (
|
||||
preferredLoginNameQuery = `SELECT preferred_login_name.user_id, preferred_login_name.login_name, preferred_login_name.instance_id` +
|
||||
` FROM projections.login_names3 AS preferred_login_name` +
|
||||
` WHERE preferred_login_name.is_primary = $1`
|
||||
userQuery = `SELECT projections.users8.id,` +
|
||||
` projections.users8.creation_date,` +
|
||||
` projections.users8.change_date,` +
|
||||
` projections.users8.resource_owner,` +
|
||||
` projections.users8.sequence,` +
|
||||
` projections.users8.state,` +
|
||||
` projections.users8.type,` +
|
||||
` projections.users8.username,` +
|
||||
userQuery = `SELECT projections.users9.id,` +
|
||||
` projections.users9.creation_date,` +
|
||||
` projections.users9.change_date,` +
|
||||
` projections.users9.resource_owner,` +
|
||||
` projections.users9.sequence,` +
|
||||
` projections.users9.state,` +
|
||||
` projections.users9.type,` +
|
||||
` projections.users9.username,` +
|
||||
` login_names.loginnames,` +
|
||||
` preferred_login_name.login_name,` +
|
||||
` projections.users8_humans.user_id,` +
|
||||
` projections.users8_humans.first_name,` +
|
||||
` projections.users8_humans.last_name,` +
|
||||
` projections.users8_humans.nick_name,` +
|
||||
` projections.users8_humans.display_name,` +
|
||||
` projections.users8_humans.preferred_language,` +
|
||||
` projections.users8_humans.gender,` +
|
||||
` projections.users8_humans.avatar_key,` +
|
||||
` projections.users8_humans.email,` +
|
||||
` projections.users8_humans.is_email_verified,` +
|
||||
` projections.users8_humans.phone,` +
|
||||
` projections.users8_humans.is_phone_verified,` +
|
||||
` projections.users8_machines.user_id,` +
|
||||
` projections.users8_machines.name,` +
|
||||
` projections.users8_machines.description,` +
|
||||
` projections.users8_machines.has_secret,` +
|
||||
` projections.users8_machines.access_token_type,` +
|
||||
` projections.users9_humans.user_id,` +
|
||||
` projections.users9_humans.first_name,` +
|
||||
` projections.users9_humans.last_name,` +
|
||||
` projections.users9_humans.nick_name,` +
|
||||
` projections.users9_humans.display_name,` +
|
||||
` projections.users9_humans.preferred_language,` +
|
||||
` projections.users9_humans.gender,` +
|
||||
` projections.users9_humans.avatar_key,` +
|
||||
` projections.users9_humans.email,` +
|
||||
` projections.users9_humans.is_email_verified,` +
|
||||
` projections.users9_humans.phone,` +
|
||||
` projections.users9_humans.is_phone_verified,` +
|
||||
` projections.users9_machines.user_id,` +
|
||||
` projections.users9_machines.name,` +
|
||||
` projections.users9_machines.description,` +
|
||||
` projections.users9_machines.has_secret,` +
|
||||
` projections.users9_machines.access_token_type,` +
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.users8.id = projections.users8_humans.user_id AND projections.users8.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8_machines ON projections.users8.id = projections.users8_machines.user_id AND projections.users8.instance_id = projections.users8_machines.instance_id` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.users9.id = projections.users9_humans.user_id AND projections.users9.instance_id = projections.users9_humans.instance_id` +
|
||||
` LEFT JOIN projections.users9_machines ON projections.users9.id = projections.users9_machines.user_id AND projections.users9.instance_id = projections.users9_machines.instance_id` +
|
||||
` LEFT JOIN` +
|
||||
` (` + loginNamesQuery + `) AS login_names` +
|
||||
` ON login_names.user_id = projections.users8.id AND login_names.instance_id = projections.users8.instance_id` +
|
||||
` ON login_names.user_id = projections.users9.id AND login_names.instance_id = projections.users9.instance_id` +
|
||||
` LEFT JOIN` +
|
||||
` (` + preferredLoginNameQuery + `) AS preferred_login_name` +
|
||||
` ON preferred_login_name.user_id = projections.users8.id AND preferred_login_name.instance_id = projections.users8.instance_id` +
|
||||
` ON preferred_login_name.user_id = projections.users9.id AND preferred_login_name.instance_id = projections.users9.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
userCols = []string{
|
||||
"id",
|
||||
@ -92,21 +92,21 @@ var (
|
||||
"access_token_type",
|
||||
"count",
|
||||
}
|
||||
profileQuery = `SELECT projections.users8.id,` +
|
||||
` projections.users8.creation_date,` +
|
||||
` projections.users8.change_date,` +
|
||||
` projections.users8.resource_owner,` +
|
||||
` projections.users8.sequence,` +
|
||||
` projections.users8_humans.user_id,` +
|
||||
` projections.users8_humans.first_name,` +
|
||||
` projections.users8_humans.last_name,` +
|
||||
` projections.users8_humans.nick_name,` +
|
||||
` projections.users8_humans.display_name,` +
|
||||
` projections.users8_humans.preferred_language,` +
|
||||
` projections.users8_humans.gender,` +
|
||||
` projections.users8_humans.avatar_key` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.users8.id = projections.users8_humans.user_id AND projections.users8.instance_id = projections.users8_humans.instance_id` +
|
||||
profileQuery = `SELECT projections.users9.id,` +
|
||||
` projections.users9.creation_date,` +
|
||||
` projections.users9.change_date,` +
|
||||
` projections.users9.resource_owner,` +
|
||||
` projections.users9.sequence,` +
|
||||
` projections.users9_humans.user_id,` +
|
||||
` projections.users9_humans.first_name,` +
|
||||
` projections.users9_humans.last_name,` +
|
||||
` projections.users9_humans.nick_name,` +
|
||||
` projections.users9_humans.display_name,` +
|
||||
` projections.users9_humans.preferred_language,` +
|
||||
` projections.users9_humans.gender,` +
|
||||
` projections.users9_humans.avatar_key` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.users9.id = projections.users9_humans.user_id AND projections.users9.instance_id = projections.users9_humans.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
profileCols = []string{
|
||||
"id",
|
||||
@ -123,16 +123,16 @@ var (
|
||||
"gender",
|
||||
"avatar_key",
|
||||
}
|
||||
emailQuery = `SELECT projections.users8.id,` +
|
||||
` projections.users8.creation_date,` +
|
||||
` projections.users8.change_date,` +
|
||||
` projections.users8.resource_owner,` +
|
||||
` projections.users8.sequence,` +
|
||||
` projections.users8_humans.user_id,` +
|
||||
` projections.users8_humans.email,` +
|
||||
` projections.users8_humans.is_email_verified` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.users8.id = projections.users8_humans.user_id AND projections.users8.instance_id = projections.users8_humans.instance_id` +
|
||||
emailQuery = `SELECT projections.users9.id,` +
|
||||
` projections.users9.creation_date,` +
|
||||
` projections.users9.change_date,` +
|
||||
` projections.users9.resource_owner,` +
|
||||
` projections.users9.sequence,` +
|
||||
` projections.users9_humans.user_id,` +
|
||||
` projections.users9_humans.email,` +
|
||||
` projections.users9_humans.is_email_verified` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.users9.id = projections.users9_humans.user_id AND projections.users9.instance_id = projections.users9_humans.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
emailCols = []string{
|
||||
"id",
|
||||
@ -144,16 +144,16 @@ var (
|
||||
"email",
|
||||
"is_email_verified",
|
||||
}
|
||||
phoneQuery = `SELECT projections.users8.id,` +
|
||||
` projections.users8.creation_date,` +
|
||||
` projections.users8.change_date,` +
|
||||
` projections.users8.resource_owner,` +
|
||||
` projections.users8.sequence,` +
|
||||
` projections.users8_humans.user_id,` +
|
||||
` projections.users8_humans.phone,` +
|
||||
` projections.users8_humans.is_phone_verified` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.users8.id = projections.users8_humans.user_id AND projections.users8.instance_id = projections.users8_humans.instance_id` +
|
||||
phoneQuery = `SELECT projections.users9.id,` +
|
||||
` projections.users9.creation_date,` +
|
||||
` projections.users9.change_date,` +
|
||||
` projections.users9.resource_owner,` +
|
||||
` projections.users9.sequence,` +
|
||||
` projections.users9_humans.user_id,` +
|
||||
` projections.users9_humans.phone,` +
|
||||
` projections.users9_humans.is_phone_verified` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.users9.id = projections.users9_humans.user_id AND projections.users9.instance_id = projections.users9_humans.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
phoneCols = []string{
|
||||
"id",
|
||||
@ -165,14 +165,14 @@ var (
|
||||
"phone",
|
||||
"is_phone_verified",
|
||||
}
|
||||
userUniqueQuery = `SELECT projections.users8.id,` +
|
||||
` projections.users8.state,` +
|
||||
` projections.users8.username,` +
|
||||
` projections.users8_humans.user_id,` +
|
||||
` projections.users8_humans.email,` +
|
||||
` projections.users8_humans.is_email_verified` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.users8.id = projections.users8_humans.user_id AND projections.users8.instance_id = projections.users8_humans.instance_id` +
|
||||
userUniqueQuery = `SELECT projections.users9.id,` +
|
||||
` projections.users9.state,` +
|
||||
` projections.users9.username,` +
|
||||
` projections.users9_humans.user_id,` +
|
||||
` projections.users9_humans.email,` +
|
||||
` projections.users9_humans.is_email_verified` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.users9.id = projections.users9_humans.user_id AND projections.users9.instance_id = projections.users9_humans.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
userUniqueCols = []string{
|
||||
"id",
|
||||
@ -182,40 +182,40 @@ var (
|
||||
"email",
|
||||
"is_email_verified",
|
||||
}
|
||||
notifyUserQuery = `SELECT projections.users8.id,` +
|
||||
` projections.users8.creation_date,` +
|
||||
` projections.users8.change_date,` +
|
||||
` projections.users8.resource_owner,` +
|
||||
` projections.users8.sequence,` +
|
||||
` projections.users8.state,` +
|
||||
` projections.users8.type,` +
|
||||
` projections.users8.username,` +
|
||||
notifyUserQuery = `SELECT projections.users9.id,` +
|
||||
` projections.users9.creation_date,` +
|
||||
` projections.users9.change_date,` +
|
||||
` projections.users9.resource_owner,` +
|
||||
` projections.users9.sequence,` +
|
||||
` projections.users9.state,` +
|
||||
` projections.users9.type,` +
|
||||
` projections.users9.username,` +
|
||||
` login_names.loginnames,` +
|
||||
` preferred_login_name.login_name,` +
|
||||
` projections.users8_humans.user_id,` +
|
||||
` projections.users8_humans.first_name,` +
|
||||
` projections.users8_humans.last_name,` +
|
||||
` projections.users8_humans.nick_name,` +
|
||||
` projections.users8_humans.display_name,` +
|
||||
` projections.users8_humans.preferred_language,` +
|
||||
` projections.users8_humans.gender,` +
|
||||
` projections.users8_humans.avatar_key,` +
|
||||
` projections.users8_notifications.user_id,` +
|
||||
` projections.users8_notifications.last_email,` +
|
||||
` projections.users8_notifications.verified_email,` +
|
||||
` projections.users8_notifications.last_phone,` +
|
||||
` projections.users8_notifications.verified_phone,` +
|
||||
` projections.users8_notifications.password_set,` +
|
||||
` projections.users9_humans.user_id,` +
|
||||
` projections.users9_humans.first_name,` +
|
||||
` projections.users9_humans.last_name,` +
|
||||
` projections.users9_humans.nick_name,` +
|
||||
` projections.users9_humans.display_name,` +
|
||||
` projections.users9_humans.preferred_language,` +
|
||||
` projections.users9_humans.gender,` +
|
||||
` projections.users9_humans.avatar_key,` +
|
||||
` projections.users9_notifications.user_id,` +
|
||||
` projections.users9_notifications.last_email,` +
|
||||
` projections.users9_notifications.verified_email,` +
|
||||
` projections.users9_notifications.last_phone,` +
|
||||
` projections.users9_notifications.verified_phone,` +
|
||||
` projections.users9_notifications.password_set,` +
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.users8.id = projections.users8_humans.user_id AND projections.users8.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8_notifications ON projections.users8.id = projections.users8_notifications.user_id AND projections.users8.instance_id = projections.users8_notifications.instance_id` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.users9.id = projections.users9_humans.user_id AND projections.users9.instance_id = projections.users9_humans.instance_id` +
|
||||
` LEFT JOIN projections.users9_notifications ON projections.users9.id = projections.users9_notifications.user_id AND projections.users9.instance_id = projections.users9_notifications.instance_id` +
|
||||
` LEFT JOIN` +
|
||||
` (` + loginNamesQuery + `) AS login_names` +
|
||||
` ON login_names.user_id = projections.users8.id AND login_names.instance_id = projections.users8.instance_id` +
|
||||
` ON login_names.user_id = projections.users9.id AND login_names.instance_id = projections.users9.instance_id` +
|
||||
` LEFT JOIN` +
|
||||
` (` + preferredLoginNameQuery + `) AS preferred_login_name` +
|
||||
` ON preferred_login_name.user_id = projections.users8.id AND preferred_login_name.instance_id = projections.users8.instance_id` +
|
||||
` ON preferred_login_name.user_id = projections.users9.id AND preferred_login_name.instance_id = projections.users9.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
notifyUserCols = []string{
|
||||
"id",
|
||||
@ -246,43 +246,43 @@ var (
|
||||
"password_set",
|
||||
"count",
|
||||
}
|
||||
usersQuery = `SELECT projections.users8.id,` +
|
||||
` projections.users8.creation_date,` +
|
||||
` projections.users8.change_date,` +
|
||||
` projections.users8.resource_owner,` +
|
||||
` projections.users8.sequence,` +
|
||||
` projections.users8.state,` +
|
||||
` projections.users8.type,` +
|
||||
` projections.users8.username,` +
|
||||
usersQuery = `SELECT projections.users9.id,` +
|
||||
` projections.users9.creation_date,` +
|
||||
` projections.users9.change_date,` +
|
||||
` projections.users9.resource_owner,` +
|
||||
` projections.users9.sequence,` +
|
||||
` projections.users9.state,` +
|
||||
` projections.users9.type,` +
|
||||
` projections.users9.username,` +
|
||||
` login_names.loginnames,` +
|
||||
` preferred_login_name.login_name,` +
|
||||
` projections.users8_humans.user_id,` +
|
||||
` projections.users8_humans.first_name,` +
|
||||
` projections.users8_humans.last_name,` +
|
||||
` projections.users8_humans.nick_name,` +
|
||||
` projections.users8_humans.display_name,` +
|
||||
` projections.users8_humans.preferred_language,` +
|
||||
` projections.users8_humans.gender,` +
|
||||
` projections.users8_humans.avatar_key,` +
|
||||
` projections.users8_humans.email,` +
|
||||
` projections.users8_humans.is_email_verified,` +
|
||||
` projections.users8_humans.phone,` +
|
||||
` projections.users8_humans.is_phone_verified,` +
|
||||
` projections.users8_machines.user_id,` +
|
||||
` projections.users8_machines.name,` +
|
||||
` projections.users8_machines.description,` +
|
||||
` projections.users8_machines.has_secret,` +
|
||||
` projections.users8_machines.access_token_type,` +
|
||||
` projections.users9_humans.user_id,` +
|
||||
` projections.users9_humans.first_name,` +
|
||||
` projections.users9_humans.last_name,` +
|
||||
` projections.users9_humans.nick_name,` +
|
||||
` projections.users9_humans.display_name,` +
|
||||
` projections.users9_humans.preferred_language,` +
|
||||
` projections.users9_humans.gender,` +
|
||||
` projections.users9_humans.avatar_key,` +
|
||||
` projections.users9_humans.email,` +
|
||||
` projections.users9_humans.is_email_verified,` +
|
||||
` projections.users9_humans.phone,` +
|
||||
` projections.users9_humans.is_phone_verified,` +
|
||||
` projections.users9_machines.user_id,` +
|
||||
` projections.users9_machines.name,` +
|
||||
` projections.users9_machines.description,` +
|
||||
` projections.users9_machines.has_secret,` +
|
||||
` projections.users9_machines.access_token_type,` +
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.users8` +
|
||||
` LEFT JOIN projections.users8_humans ON projections.users8.id = projections.users8_humans.user_id AND projections.users8.instance_id = projections.users8_humans.instance_id` +
|
||||
` LEFT JOIN projections.users8_machines ON projections.users8.id = projections.users8_machines.user_id AND projections.users8.instance_id = projections.users8_machines.instance_id` +
|
||||
` FROM projections.users9` +
|
||||
` LEFT JOIN projections.users9_humans ON projections.users9.id = projections.users9_humans.user_id AND projections.users9.instance_id = projections.users9_humans.instance_id` +
|
||||
` LEFT JOIN projections.users9_machines ON projections.users9.id = projections.users9_machines.user_id AND projections.users9.instance_id = projections.users9_machines.instance_id` +
|
||||
` LEFT JOIN` +
|
||||
` (` + loginNamesQuery + `) AS login_names` +
|
||||
` ON login_names.user_id = projections.users8.id AND login_names.instance_id = projections.users8.instance_id` +
|
||||
` ON login_names.user_id = projections.users9.id AND login_names.instance_id = projections.users9.instance_id` +
|
||||
` LEFT JOIN` +
|
||||
` (` + preferredLoginNameQuery + `) AS preferred_login_name` +
|
||||
` ON preferred_login_name.user_id = projections.users8.id AND preferred_login_name.instance_id = projections.users8.instance_id` +
|
||||
` ON preferred_login_name.user_id = projections.users9.id AND preferred_login_name.instance_id = projections.users9.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`
|
||||
usersCols = []string{
|
||||
"id",
|
||||
|
76
internal/query/userinfo.go
Normal file
76
internal/query/userinfo.go
Normal file
@ -0,0 +1,76 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
// oidcUserInfoTriggerHandlers slice can only be created after zitadel
|
||||
// is fully initialized, otherwise the handlers are nil.
|
||||
// OnceValue takes care of creating the slice on the first request
|
||||
// and than will always return the same slice on subsequent requests.
|
||||
var oidcUserInfoTriggerHandlers = sync.OnceValue(func() []*handler.Handler {
|
||||
return []*handler.Handler{
|
||||
projection.UserProjection,
|
||||
projection.UserMetadataProjection,
|
||||
projection.UserGrantProjection,
|
||||
projection.OrgProjection,
|
||||
projection.ProjectProjection,
|
||||
}
|
||||
})
|
||||
|
||||
func TriggerOIDCUserInfoProjections(ctx context.Context) {
|
||||
triggerBatch(ctx, oidcUserInfoTriggerHandlers()...)
|
||||
}
|
||||
|
||||
//go:embed embed/userinfo_by_id.sql
|
||||
var oidcUserInfoQuery string
|
||||
|
||||
func (q *Queries) GetOIDCUserInfo(ctx context.Context, userID string, roleAudience []string) (_ *OIDCUserInfo, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
var data []byte
|
||||
err = q.client.QueryRowContext(ctx, func(row *sql.Row) error {
|
||||
return row.Scan(&data)
|
||||
},
|
||||
oidcUserInfoQuery,
|
||||
userID, authz.GetInstance(ctx).InstanceID(), database.TextArray[string](roleAudience),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Oath6", "Errors.Internal")
|
||||
}
|
||||
|
||||
userInfo := new(OIDCUserInfo)
|
||||
if err = json.Unmarshal(data, userInfo); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Vohs6", "Errors.Internal")
|
||||
}
|
||||
if userInfo.User == nil {
|
||||
return nil, errors.ThrowNotFound(nil, "QUERY-ahs4S", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
type OIDCUserInfo struct {
|
||||
User *User `json:"user,omitempty"`
|
||||
Metadata []UserMetadata `json:"metadata,omitempty"`
|
||||
Org *UserInfoOrg `json:"org,omitempty"`
|
||||
UserGrants []UserGrant `json:"user_grants,omitempty"`
|
||||
}
|
||||
|
||||
type UserInfoOrg struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
PrimaryDomain string `json:"primary_domain,omitempty"`
|
||||
}
|
336
internal/query/userinfo_test.go
Normal file
336
internal/query/userinfo_test.go
Normal file
@ -0,0 +1,336 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed testdata/userinfo_not_found.json
|
||||
testdataUserInfoNotFound string
|
||||
//go:embed testdata/userinfo_human_no_md.json
|
||||
testdataUserInfoHumanNoMD string
|
||||
//go:embed testdata/userinfo_human.json
|
||||
testdataUserInfoHuman string
|
||||
//go:embed testdata/userinfo_human_grants.json
|
||||
testdataUserInfoHumanGrants string
|
||||
//go:embed testdata/userinfo_machine.json
|
||||
testdataUserInfoMachine string
|
||||
|
||||
// timeLocation does a single parse of the testdata and extracts a time.Location,
|
||||
// so that it may be used during test assertion.
|
||||
// Because depending on the environment json.Unmarshal parses
|
||||
// the 00:00 timezones differently.
|
||||
// On my local system is parses to an empty timezone with 0 offset,
|
||||
// but in github action is pares into UTC.
|
||||
timeLocation = func() *time.Location {
|
||||
referenceInfo := new(OIDCUserInfo)
|
||||
err := json.Unmarshal([]byte(testdataUserInfoHumanNoMD), referenceInfo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return referenceInfo.User.CreationDate.Location()
|
||||
}()
|
||||
)
|
||||
|
||||
func TestQueries_GetOIDCUserInfo(t *testing.T) {
|
||||
expQuery := regexp.QuoteMeta(oidcUserInfoQuery)
|
||||
type args struct {
|
||||
userID string
|
||||
roleAudience []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
mock sqlExpectation
|
||||
want *OIDCUserInfo
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "query error",
|
||||
args: args{
|
||||
userID: "231965491734773762",
|
||||
},
|
||||
mock: mockQueryErr(expQuery, sql.ErrConnDone, "231965491734773762", "instanceID", nil),
|
||||
wantErr: sql.ErrConnDone,
|
||||
},
|
||||
{
|
||||
name: "unmarshal error",
|
||||
args: args{
|
||||
userID: "231965491734773762",
|
||||
},
|
||||
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{`~~~`}, "231965491734773762", "instanceID", nil),
|
||||
wantErr: errors.ThrowInternal(nil, "QUERY-Vohs6", "Errors.Internal"),
|
||||
},
|
||||
{
|
||||
name: "user not found",
|
||||
args: args{
|
||||
userID: "231965491734773762",
|
||||
},
|
||||
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoNotFound}, "231965491734773762", "instanceID", nil),
|
||||
wantErr: errors.ThrowNotFound(nil, "QUERY-ahs4S", "Errors.User.NotFound"),
|
||||
},
|
||||
{
|
||||
name: "human without metadata",
|
||||
args: args{
|
||||
userID: "231965491734773762",
|
||||
},
|
||||
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHumanNoMD}, "231965491734773762", "instanceID", nil),
|
||||
want: &OIDCUserInfo{
|
||||
User: &User{
|
||||
ID: "231965491734773762",
|
||||
CreationDate: time.Date(2023, time.September, 15, 6, 10, 7, 434142000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, timeLocation),
|
||||
Sequence: 1148,
|
||||
State: 1,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Username: "tim+tesmail@zitadel.com",
|
||||
PreferredLoginName: "tim+tesmail@zitadel.com@demo.localhost",
|
||||
Human: &Human{
|
||||
FirstName: "Tim",
|
||||
LastName: "Mohlmann",
|
||||
NickName: "muhlemmer",
|
||||
DisplayName: "Tim Mohlmann",
|
||||
AvatarKey: "",
|
||||
Email: "tim+tesmail@zitadel.com",
|
||||
IsEmailVerified: true,
|
||||
Phone: "+40123456789",
|
||||
IsPhoneVerified: false,
|
||||
},
|
||||
Machine: nil,
|
||||
},
|
||||
Org: &UserInfoOrg{
|
||||
Name: "demo",
|
||||
PrimaryDomain: "demo.localhost",
|
||||
},
|
||||
Metadata: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human with metadata",
|
||||
args: args{
|
||||
userID: "231965491734773762",
|
||||
},
|
||||
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHuman}, "231965491734773762", "instanceID", nil),
|
||||
want: &OIDCUserInfo{
|
||||
User: &User{
|
||||
ID: "231965491734773762",
|
||||
CreationDate: time.Date(2023, time.September, 15, 6, 10, 7, 434142000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, timeLocation),
|
||||
Sequence: 1148,
|
||||
State: 1,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Username: "tim+tesmail@zitadel.com",
|
||||
PreferredLoginName: "tim+tesmail@zitadel.com@demo.localhost",
|
||||
Human: &Human{
|
||||
FirstName: "Tim",
|
||||
LastName: "Mohlmann",
|
||||
NickName: "muhlemmer",
|
||||
DisplayName: "Tim Mohlmann",
|
||||
AvatarKey: "",
|
||||
Email: "tim+tesmail@zitadel.com",
|
||||
IsEmailVerified: true,
|
||||
Phone: "+40123456789",
|
||||
IsPhoneVerified: false,
|
||||
},
|
||||
Machine: nil,
|
||||
},
|
||||
Org: &UserInfoOrg{
|
||||
Name: "demo",
|
||||
PrimaryDomain: "demo.localhost",
|
||||
},
|
||||
Metadata: []UserMetadata{
|
||||
{
|
||||
CreationDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, timeLocation),
|
||||
Sequence: 1147,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Key: "bar",
|
||||
Value: []byte("foo"),
|
||||
},
|
||||
{
|
||||
CreationDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, timeLocation),
|
||||
Sequence: 1146,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Key: "foo",
|
||||
Value: []byte("bar"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "human with metadata and grants",
|
||||
args: args{
|
||||
userID: "231965491734773762",
|
||||
roleAudience: []string{"236645808328409090", "240762134579904514"},
|
||||
},
|
||||
mock: mockQuery(expQuery,
|
||||
[]string{"json_build_object"},
|
||||
[]driver.Value{testdataUserInfoHumanGrants},
|
||||
"231965491734773762", "instanceID", database.TextArray[string]{"236645808328409090", "240762134579904514"},
|
||||
),
|
||||
want: &OIDCUserInfo{
|
||||
User: &User{
|
||||
ID: "231965491734773762",
|
||||
CreationDate: time.Date(2023, time.September, 15, 6, 10, 7, 434142000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, timeLocation),
|
||||
Sequence: 1148,
|
||||
State: 1,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Username: "tim+tesmail@zitadel.com",
|
||||
PreferredLoginName: "tim+tesmail@zitadel.com@demo.localhost",
|
||||
Human: &Human{
|
||||
FirstName: "Tim",
|
||||
LastName: "Mohlmann",
|
||||
NickName: "muhlemmer",
|
||||
DisplayName: "Tim Mohlmann",
|
||||
AvatarKey: "",
|
||||
Email: "tim+tesmail@zitadel.com",
|
||||
IsEmailVerified: true,
|
||||
Phone: "+40123456789",
|
||||
IsPhoneVerified: false,
|
||||
},
|
||||
Machine: nil,
|
||||
},
|
||||
Org: &UserInfoOrg{
|
||||
Name: "demo",
|
||||
PrimaryDomain: "demo.localhost",
|
||||
},
|
||||
Metadata: []UserMetadata{
|
||||
{
|
||||
CreationDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, timeLocation),
|
||||
Sequence: 1147,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Key: "bar",
|
||||
Value: []byte("foo"),
|
||||
},
|
||||
{
|
||||
CreationDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, timeLocation),
|
||||
Sequence: 1146,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Key: "foo",
|
||||
Value: []byte("bar"),
|
||||
},
|
||||
},
|
||||
UserGrants: []UserGrant{
|
||||
{
|
||||
ID: "240749256523120642",
|
||||
GrantID: "",
|
||||
State: 1,
|
||||
CreationDate: time.Date(2023, time.November, 14, 20, 28, 59, 168208000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 20, 50, 58, 822391000, timeLocation),
|
||||
Sequence: 2,
|
||||
UserID: "231965491734773762",
|
||||
Roles: []string{
|
||||
"role1",
|
||||
"role2",
|
||||
},
|
||||
ResourceOwner: "231848297847848962",
|
||||
ProjectID: "236645808328409090",
|
||||
OrgName: "demo",
|
||||
OrgPrimaryDomain: "demo.localhost",
|
||||
ProjectName: "tests",
|
||||
UserResourceOwner: "231848297847848962",
|
||||
},
|
||||
{
|
||||
ID: "240762315572510722",
|
||||
GrantID: "",
|
||||
State: 1,
|
||||
CreationDate: time.Date(2023, time.November, 14, 22, 38, 42, 967317000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 22, 38, 42, 967317000, timeLocation),
|
||||
Sequence: 1,
|
||||
UserID: "231965491734773762",
|
||||
Roles: []string{
|
||||
"role3",
|
||||
"role4",
|
||||
},
|
||||
ResourceOwner: "231848297847848962",
|
||||
ProjectID: "240762134579904514",
|
||||
OrgName: "demo",
|
||||
OrgPrimaryDomain: "demo.localhost",
|
||||
ProjectName: "tests2",
|
||||
UserResourceOwner: "231848297847848962",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "machine with metadata",
|
||||
args: args{
|
||||
userID: "240707570677841922",
|
||||
},
|
||||
mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoMachine}, "240707570677841922", "instanceID", nil),
|
||||
want: &OIDCUserInfo{
|
||||
User: &User{
|
||||
ID: "240707570677841922",
|
||||
CreationDate: time.Date(2023, time.November, 14, 13, 34, 52, 473732000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 35, 2, 861342000, timeLocation),
|
||||
Sequence: 2,
|
||||
State: 1,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Username: "tests",
|
||||
PreferredLoginName: "tests@demo.localhost",
|
||||
Human: nil,
|
||||
Machine: &Machine{
|
||||
Name: "tests",
|
||||
Description: "My test service user",
|
||||
},
|
||||
},
|
||||
Org: &UserInfoOrg{
|
||||
Name: "demo",
|
||||
PrimaryDomain: "demo.localhost",
|
||||
},
|
||||
Metadata: []UserMetadata{
|
||||
{
|
||||
CreationDate: time.Date(2023, time.November, 14, 13, 35, 30, 126849000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 35, 30, 126849000, timeLocation),
|
||||
Sequence: 3,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Key: "first",
|
||||
Value: []byte("Hello World!"),
|
||||
},
|
||||
{
|
||||
CreationDate: time.Date(2023, time.November, 14, 13, 35, 44, 28343000, timeLocation),
|
||||
ChangeDate: time.Date(2023, time.November, 14, 13, 35, 44, 28343000, timeLocation),
|
||||
Sequence: 4,
|
||||
ResourceOwner: "231848297847848962",
|
||||
Key: "second",
|
||||
Value: []byte("Bye World!"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
execMock(t, tt.mock, func(db *sql.DB) {
|
||||
q := &Queries{
|
||||
client: &database.DB{
|
||||
DB: db,
|
||||
Database: &prepareDB{},
|
||||
},
|
||||
}
|
||||
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
|
||||
|
||||
got, err := q.GetOIDCUserInfo(ctx, tt.args.userID, tt.args.roleAudience)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -441,6 +441,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: UserSession не е намерена
|
||||
Key:
|
||||
NotFound: Ключът не е намерен
|
||||
ExpireBeforeNow: Срокът на годност е в миналото
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -428,6 +428,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: UserSession nenalezena
|
||||
Key:
|
||||
NotFound: Klíč nenalezen
|
||||
ExpireBeforeNow: Datum expirace je v minulosti
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -429,6 +429,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: Benutzer Sitzung konnte nicht gefunden werden
|
||||
Key:
|
||||
NotFound: Schlüssel nicht gefunden
|
||||
ExpireBeforeNow: Das Ablaufdatum liegt in der Vergangenheit
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -429,6 +429,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: UserSession not found
|
||||
Key:
|
||||
NotFound: Key not found
|
||||
ExpireBeforeNow: The expiration date is in the past
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -429,6 +429,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: UserSession no encontrado
|
||||
Key:
|
||||
NotFound: Clave no encontrada
|
||||
ExpireBeforeNow: La fecha de caducidad está en el pasado
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -429,6 +429,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: UserSession non trouvé
|
||||
Key:
|
||||
NotFound: Clé introuvable
|
||||
ExpireBeforeNow: La date d'expiration est dans le passé
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -430,6 +430,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: Sessione non trovata
|
||||
Key:
|
||||
NotFound: Chiave non trovata
|
||||
ExpireBeforeNow: La data di scadenza è passata
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -418,6 +418,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: ユーザーが見つかりません
|
||||
Key:
|
||||
NotFound: キーが見つかりません
|
||||
ExpireBeforeNow: 有効期限が過去です
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -429,6 +429,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: Корисничката сесија не е пронајдена
|
||||
Key:
|
||||
NotFound: Клучот не е пронајден
|
||||
ExpireBeforeNow: Датумот на истекување е во минатото
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
@ -429,6 +429,7 @@ Errors:
|
||||
UserSession:
|
||||
NotFound: Sesja użytkownika nie znaleziona
|
||||
Key:
|
||||
NotFound: Klucz nie odnaleziony
|
||||
ExpireBeforeNow: Data ważności jest już przeszła
|
||||
Login:
|
||||
LoginPolicy:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user