diff --git a/internal/api/grpc/authn/converter.go b/internal/api/grpc/authn/converter.go index c9a15583ff..237999de21 100644 --- a/internal/api/grpc/authn/converter.go +++ b/internal/api/grpc/authn/converter.go @@ -1,58 +1,38 @@ package authn import ( - "github.com/caos/logging" - "github.com/golang/protobuf/ptypes" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/caos/zitadel/internal/api/grpc/object" "github.com/caos/zitadel/internal/domain" key_model "github.com/caos/zitadel/internal/key/model" + "github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/pkg/grpc/authn" ) -func KeyViewsToPb(keys []*key_model.AuthNKeyView) []*authn.Key { +func KeysToPb(keys []*query.AuthNKey) []*authn.Key { k := make([]*authn.Key, len(keys)) for i, key := range keys { - k[i] = KeyViewToPb(key) + k[i] = KeyToPb(key) } return k } -func KeyViewToPb(key *key_model.AuthNKeyView) *authn.Key { - expDate, err := ptypes.TimestampProto(key.ExpirationDate) - logging.Log("AUTHN-uhYmM").OnError(err).Debug("unable to parse expiry") - - return &authn.Key{ - Id: key.ID, - Type: authn.KeyType_KEY_TYPE_JSON, - ExpirationDate: expDate, - Details: object.ToViewDetailsPb( - key.Sequence, - key.CreationDate, - key.CreationDate, - "", //TODO: details - ), - } -} - -func KeyToPb(key *key_model.AuthNKeyView) *authn.Key { - expDate, err := ptypes.TimestampProto(key.ExpirationDate) - logging.Log("AUTHN-4n12g").OnError(err).Debug("unable to parse expiration date") - +func KeyToPb(key *query.AuthNKey) *authn.Key { return &authn.Key{ Id: key.ID, Type: KeyTypeToPb(key.Type), - ExpirationDate: expDate, + ExpirationDate: timestamppb.New(key.Expiration), Details: object.ToViewDetailsPb( key.Sequence, key.CreationDate, key.CreationDate, - "", //TODO: details + key.ResourceOwner, ), } } -func KeyTypeToPb(typ key_model.AuthNKeyType) authn.KeyType { +func KeyTypeToPb(typ domain.AuthNKeyType) authn.KeyType { switch typ { case key_model.AuthNKeyTypeJSON: return authn.KeyType_KEY_TYPE_JSON diff --git a/internal/api/grpc/management/project_application.go b/internal/api/grpc/management/project_application.go index 7f605442a1..3bee22e57f 100644 --- a/internal/api/grpc/management/project_application.go +++ b/internal/api/grpc/management/project_application.go @@ -8,6 +8,7 @@ import ( change_grpc "github.com/caos/zitadel/internal/api/grpc/change" object_grpc "github.com/caos/zitadel/internal/api/grpc/object" project_grpc "github.com/caos/zitadel/internal/api/grpc/project" + "github.com/caos/zitadel/internal/query" mgmt_pb "github.com/caos/zitadel/pkg/grpc/management" ) @@ -182,7 +183,19 @@ func (s *Server) RegenerateAPIClientSecret(ctx context.Context, req *mgmt_pb.Reg } func (s *Server) GetAppKey(ctx context.Context, req *mgmt_pb.GetAppKeyRequest) (*mgmt_pb.GetAppKeyResponse, error) { - key, err := s.project.GetClientKey(ctx, req.ProjectId, req.AppId, req.KeyId) + resourceOwner, err := query.NewAuthNKeyResourceOwnerQuery(authz.GetCtxData(ctx).OrgID) + if err != nil { + return nil, err + } + aggregateID, err := query.NewAuthNKeyAggregateIDQuery(req.ProjectId) + if err != nil { + return nil, err + } + objectID, err := query.NewAuthNKeyObjectIDQuery(req.AppId) + if err != nil { + return nil, err + } + key, err := s.query.GetAuthNKeyByID(ctx, req.KeyId, resourceOwner, aggregateID, objectID) if err != nil { return nil, err } @@ -192,18 +205,18 @@ func (s *Server) GetAppKey(ctx context.Context, req *mgmt_pb.GetAppKeyRequest) ( } func (s *Server) ListAppKeys(ctx context.Context, req *mgmt_pb.ListAppKeysRequest) (*mgmt_pb.ListAppKeysResponse, error) { - queries, err := ListAPIClientKeysRequestToModel(req) + queries, err := ListAPIClientKeysRequestToQuery(ctx, req) if err != nil { return nil, err } - keys, err := s.project.SearchClientKeys(ctx, queries) + keys, err := s.query.SearchAuthNKeys(ctx, queries) if err != nil { return nil, err } return &mgmt_pb.ListAppKeysResponse{ - Result: authn_grpc.KeyViewsToPb(keys.Result), + Result: authn_grpc.KeysToPb(keys.AuthNKeys), Details: object_grpc.ToListDetails( - keys.TotalResult, + keys.Count, keys.Sequence, keys.Timestamp, ), diff --git a/internal/api/grpc/management/project_application_converter.go b/internal/api/grpc/management/project_application_converter.go index bda8e5ede4..40b4ab55af 100644 --- a/internal/api/grpc/management/project_application_converter.go +++ b/internal/api/grpc/management/project_application_converter.go @@ -1,14 +1,15 @@ package management import ( + "context" "time" + "github.com/caos/zitadel/internal/api/authz" authn_grpc "github.com/caos/zitadel/internal/api/grpc/authn" "github.com/caos/zitadel/internal/api/grpc/object" app_grpc "github.com/caos/zitadel/internal/api/grpc/project" "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/eventstore/v1/models" - key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/query" mgmt_pb "github.com/caos/zitadel/pkg/grpc/management" ) @@ -123,19 +124,30 @@ func AddAPIClientKeyRequestToDomain(key *mgmt_pb.AddAppKeyRequest) *domain.Appli } } -func ListAPIClientKeysRequestToModel(req *mgmt_pb.ListAppKeysRequest) (*key_model.AuthNKeySearchRequest, error) { +func ListAPIClientKeysRequestToQuery(ctx context.Context, req *mgmt_pb.ListAppKeysRequest) (*query.AuthNKeySearchQueries, error) { + resourcOwner, err := query.NewAuthNKeyResourceOwnerQuery(authz.GetCtxData(ctx).OrgID) + if err != nil { + return nil, err + } + projectID, err := query.NewAuthNKeyAggregateIDQuery(req.ProjectId) + if err != nil { + return nil, err + } + appID, err := query.NewAuthNKeyObjectIDQuery(req.AppId) + if err != nil { + return nil, err + } offset, limit, asc := object.ListQueryToModel(req.Query) - queries := make([]*key_model.AuthNKeySearchQuery, 0) - queries = append(queries, &key_model.AuthNKeySearchQuery{ - Key: key_model.AuthNKeyObjectID, - Method: domain.SearchMethodEquals, - Value: req.AppId, - }) - return &key_model.AuthNKeySearchRequest{ - Offset: offset, - Limit: limit, - Asc: asc, - //SortingColumn: //TODO: sorting - Queries: queries, + return &query.AuthNKeySearchQueries{ + SearchRequest: query.SearchRequest{ + Offset: offset, + Limit: limit, + Asc: asc, + }, + Queries: []query.SearchQuery{ + resourcOwner, + projectID, + appID, + }, }, nil } diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index f099740428..6e72b72b17 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -15,6 +15,7 @@ import ( "github.com/caos/zitadel/internal/api/grpc/user" user_grpc "github.com/caos/zitadel/internal/api/grpc/user" "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/query" grant_model "github.com/caos/zitadel/internal/usergrant/model" mgmt_pb "github.com/caos/zitadel/pkg/grpc/management" ) @@ -551,7 +552,15 @@ func (s *Server) UpdateMachine(ctx context.Context, req *mgmt_pb.UpdateMachineRe } func (s *Server) GetMachineKeyByIDs(ctx context.Context, req *mgmt_pb.GetMachineKeyByIDsRequest) (*mgmt_pb.GetMachineKeyByIDsResponse, error) { - key, err := s.user.GetMachineKey(ctx, req.UserId, req.KeyId) + resourceOwner, err := query.NewAuthNKeyResourceOwnerQuery(authz.GetCtxData(ctx).OrgID) + if err != nil { + return nil, err + } + aggregateID, err := query.NewAuthNKeyAggregateIDQuery(req.UserId) + if err != nil { + return nil, err + } + key, err := s.query.GetAuthNKeyByID(ctx, req.KeyId, resourceOwner, aggregateID) if err != nil { return nil, err } @@ -561,14 +570,18 @@ func (s *Server) GetMachineKeyByIDs(ctx context.Context, req *mgmt_pb.GetMachine } func (s *Server) ListMachineKeys(ctx context.Context, req *mgmt_pb.ListMachineKeysRequest) (*mgmt_pb.ListMachineKeysResponse, error) { - result, err := s.user.SearchMachineKeys(ctx, ListMachineKeysRequestToModel(req)) + query, err := ListMachineKeysRequestToQuery(ctx, req) + if err != nil { + return nil, err + } + result, err := s.query.SearchAuthNKeys(ctx, query) if err != nil { return nil, err } return &mgmt_pb.ListMachineKeysResponse{ - Result: authn.KeyViewsToPb(result.Result), + Result: authn.KeysToPb(result.AuthNKeys), Details: obj_grpc.ToListDetails( - result.TotalResult, + result.Count, result.Sequence, result.Timestamp, ), diff --git a/internal/api/grpc/management/user_converter.go b/internal/api/grpc/management/user_converter.go index 99bfe0f379..66e916aea1 100644 --- a/internal/api/grpc/management/user_converter.go +++ b/internal/api/grpc/management/user_converter.go @@ -5,7 +5,6 @@ import ( "time" "github.com/caos/logging" - "github.com/golang/protobuf/ptypes" "golang.org/x/text/language" "github.com/caos/zitadel/internal/api/authz" @@ -15,7 +14,6 @@ import ( user_grpc "github.com/caos/zitadel/internal/api/grpc/user" "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/eventstore/v1/models" - key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/query" user_model "github.com/caos/zitadel/internal/user/model" mgmt_pb "github.com/caos/zitadel/pkg/grpc/management" @@ -187,32 +185,34 @@ func UpdateMachineRequestToDomain(ctx context.Context, req *mgmt_pb.UpdateMachin } } -func ListMachineKeysRequestToModel(req *mgmt_pb.ListMachineKeysRequest) *key_model.AuthNKeySearchRequest { - offset, limit, asc := object.ListQueryToModel(req.Query) - return &key_model.AuthNKeySearchRequest{ - Offset: offset, - Limit: limit, - Asc: asc, - Queries: []*key_model.AuthNKeySearchQuery{ - { - Key: key_model.AuthNKeyObjectType, - Method: domain.SearchMethodEquals, - Value: key_model.AuthNKeyObjectTypeUser, - }, { - Key: key_model.AuthNKeyObjectID, - Method: domain.SearchMethodEquals, - Value: req.UserId, - }, - }, +func ListMachineKeysRequestToQuery(ctx context.Context, req *mgmt_pb.ListMachineKeysRequest) (*query.AuthNKeySearchQueries, error) { + resourcOwner, err := query.NewAuthNKeyResourceOwnerQuery(authz.GetCtxData(ctx).OrgID) + if err != nil { + return nil, err } + userID, err := query.NewAuthNKeyAggregateIDQuery(req.UserId) + if err != nil { + return nil, err + } + offset, limit, asc := object.ListQueryToModel(req.Query) + return &query.AuthNKeySearchQueries{ + SearchRequest: query.SearchRequest{ + Offset: offset, + Limit: limit, + Asc: asc, + }, + Queries: []query.SearchQuery{ + resourcOwner, + userID, + }, + }, nil + } func AddMachineKeyRequestToDomain(req *mgmt_pb.AddMachineKeyRequest) *domain.MachineKey { expDate := time.Time{} if req.ExpirationDate != nil { - var err error - expDate, err = ptypes.Timestamp(req.ExpirationDate) - logging.Log("MANAG-iNshR").OnError(err).Debug("unable to parse expiration date") + expDate = req.ExpirationDate.AsTime() } return &domain.MachineKey{ diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 65c1772d5a..461a4b4279 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -65,19 +65,16 @@ func (o *OPStorage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID strin func (o *OPStorage) GetKeyByIDAndIssuer(ctx context.Context, keyID, issuer string) (_ *jose.JSONWebKey, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - key, err := o.repo.MachineKeyByID(ctx, keyID) + publicKeyData, err := o.query.GetAuthNKeyPublicKeyByIDAndIdentifier(ctx, keyID, issuer) if err != nil { return nil, err } - if key.AuthIdentifier != issuer { - return nil, errors.ThrowPermissionDenied(nil, "OIDC-24jm3", "key from different user") - } - publicKey, err := crypto.BytesToPublicKey(key.PublicKey) + publicKey, err := crypto.BytesToPublicKey(publicKeyData) if err != nil { return nil, err } return &jose.JSONWebKey{ - KeyID: key.ID, + KeyID: keyID, Use: "sig", Key: publicKey, }, nil diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go index 4d0ff94cae..c0a8ee6fc0 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user.go +++ b/internal/auth/repository/eventsourcing/eventstore/user.go @@ -15,8 +15,6 @@ import ( v1 "github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1/models" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" - key_model "github.com/caos/zitadel/internal/key/model" - key_view_model "github.com/caos/zitadel/internal/key/repository/view/model" "github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/user/model" usr_view "github.com/caos/zitadel/internal/user/repository/view" @@ -218,14 +216,6 @@ func (repo *UserRepo) MyUserChanges(ctx context.Context, lastSequence uint64, li return changes, nil } -func (repo *UserRepo) MachineKeyByID(ctx context.Context, keyID string) (*key_model.AuthNKeyView, error) { - key, err := repo.View.AuthNKeyByID(keyID) - if err != nil { - return nil, err - } - return key_view_model.AuthNKeyToModel(key), nil -} - func (repo *UserRepo) SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error) { sequence, sequenceErr := repo.View.GetLatestUserSequence() logging.Log("EVENT-Gdgsw").OnError(sequenceErr).Warn("could not read latest user sequence") diff --git a/internal/auth/repository/eventsourcing/handler/authn_keys.go b/internal/auth/repository/eventsourcing/handler/authn_keys.go deleted file mode 100644 index 6e485ac720..0000000000 --- a/internal/auth/repository/eventsourcing/handler/authn_keys.go +++ /dev/null @@ -1,124 +0,0 @@ -package handler - -import ( - "github.com/caos/zitadel/internal/eventstore/v1" - "time" - - "github.com/caos/logging" - - es_models "github.com/caos/zitadel/internal/eventstore/v1/models" - "github.com/caos/zitadel/internal/eventstore/v1/query" - "github.com/caos/zitadel/internal/eventstore/v1/spooler" - key_model "github.com/caos/zitadel/internal/key/repository/view/model" - proj_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" - user_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" -) - -const ( - authnKeysTable = "auth.authn_keys" -) - -type AuthNKeys struct { - handler - subscription *v1.Subscription -} - -func newAuthNKeys(handler handler) *AuthNKeys { - h := &AuthNKeys{ - handler: handler, - } - - h.subscribe() - - return h -} - -func (k *AuthNKeys) subscribe() { - k.subscription = k.es.Subscribe(k.AggregateTypes()...) - go func() { - for event := range k.subscription.Events { - query.ReduceEvent(k, event) - } - }() -} - -func (k *AuthNKeys) ViewModel() string { - return authnKeysTable -} - -func (k *AuthNKeys) Subscription() *v1.Subscription { - return k.subscription -} - -func (_ *AuthNKeys) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{user_model.UserAggregate, proj_model.ProjectAggregate} -} - -func (k *AuthNKeys) CurrentSequence() (uint64, error) { - sequence, err := k.view.GetLatestAuthNKeySequence() - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (k *AuthNKeys) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := k.view.GetLatestAuthNKeySequence() - if err != nil { - return nil, err - } - return es_models.NewSearchQuery(). - AggregateTypeFilter(k.AggregateTypes()...). - LatestSequenceFilter(sequence.CurrentSequence), nil -} - -func (k *AuthNKeys) Reduce(event *es_models.Event) (err error) { - switch event.AggregateType { - case user_model.UserAggregate, - proj_model.ProjectAggregate: - err = k.processAuthNKeys(event) - } - return err -} - -func (k *AuthNKeys) processAuthNKeys(event *es_models.Event) (err error) { - key := new(key_model.AuthNKeyView) - switch event.Type { - case user_model.MachineKeyAdded, - proj_model.ClientKeyAdded: - err = key.AppendEvent(event) - if key.ExpirationDate.Before(time.Now()) { - return k.view.ProcessedAuthNKeySequence(event) - } - case user_model.MachineKeyRemoved: - err = key.SetUserData(event) - if err != nil { - return err - } - return k.view.DeleteAuthNKey(key.ID, event) - case proj_model.ClientKeyRemoved: - err = key.SetClientData(event) - if err != nil { - return err - } - return k.view.DeleteAuthNKey(key.ID, event) - case user_model.UserRemoved, - proj_model.ApplicationRemoved: - return k.view.DeleteAuthNKeysByObjectID(event.AggregateID, event) - default: - return k.view.ProcessedAuthNKeySequence(event) - } - if err != nil { - return err - } - return k.view.PutAuthNKey(key, event) -} - -func (k *AuthNKeys) OnError(event *es_models.Event, err error) error { - logging.LogWithFields("SPOOL-S9fe", "id", event.AggregateID).WithError(err).Warn("something went wrong in authn key handler") - return spooler.HandleError(event, err, k.view.GetLatestAuthNKeyFailedEvent, k.view.ProcessedAuthNKeyFailedEvent, k.view.ProcessedAuthNKeySequence, k.errorCountUntilSkip) -} - -func (k *AuthNKeys) OnSuccess() error { - return spooler.HandleSuccess(k.view.UpdateAuthNKeySpoolerRunTimestamp) -} diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go index e1196acb1b..2ba7fe760f 100644 --- a/internal/auth/repository/eventsourcing/handler/handler.go +++ b/internal/auth/repository/eventsourcing/handler/handler.go @@ -47,8 +47,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es newUserGrant( handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, systemDefaults.IamID), - newAuthNKeys( - handler{view, bulkLimit, configs.cycleDuration("MachineKey"), errorCount, es}), newIDPConfig( handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), newIDPProvider( diff --git a/internal/auth/repository/eventsourcing/view/authn_keys.go b/internal/auth/repository/eventsourcing/view/authn_keys.go deleted file mode 100644 index 71cf505445..0000000000 --- a/internal/auth/repository/eventsourcing/view/authn_keys.go +++ /dev/null @@ -1,74 +0,0 @@ -package view - -import ( - "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/v1/models" - key_model "github.com/caos/zitadel/internal/key/model" - "github.com/caos/zitadel/internal/key/repository/view" - "github.com/caos/zitadel/internal/key/repository/view/model" - "github.com/caos/zitadel/internal/view/repository" -) - -const ( - authNKeyTable = "auth.authn_keys" -) - -func (v *View) AuthNKeyByIDs(userID, keyID string) (*model.AuthNKeyView, error) { - return view.AuthNKeyByIDs(v.Db, authNKeyTable, userID, keyID) -} - -func (v *View) AuthNKeysByObjectID(objectID string) ([]*model.AuthNKeyView, error) { - return view.AuthNKeysByObjectID(v.Db, authNKeyTable, objectID) -} - -func (v *View) AuthNKeyByID(keyID string) (*model.AuthNKeyView, error) { - return view.AuthNKeyByID(v.Db, authNKeyTable, keyID) -} - -func (v *View) SearchAuthNKeys(request *key_model.AuthNKeySearchRequest) ([]*model.AuthNKeyView, uint64, error) { - return view.SearchAuthNKeys(v.Db, authNKeyTable, request) -} - -func (v *View) PutAuthNKey(key *model.AuthNKeyView, event *models.Event) error { - err := view.PutAuthNKey(v.Db, authNKeyTable, key) - if err != nil { - return err - } - return v.ProcessedAuthNKeySequence(event) -} - -func (v *View) DeleteAuthNKey(keyID string, event *models.Event) error { - err := view.DeleteAuthNKey(v.Db, authNKeyTable, keyID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedAuthNKeySequence(event) -} - -func (v *View) DeleteAuthNKeysByObjectID(objectID string, event *models.Event) error { - err := view.DeleteAuthNKey(v.Db, authNKeyTable, objectID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedAuthNKeySequence(event) -} - -func (v *View) GetLatestAuthNKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(authNKeyTable) -} - -func (v *View) ProcessedAuthNKeySequence(event *models.Event) error { - return v.saveCurrentSequence(authNKeyTable, event) -} - -func (v *View) UpdateAuthNKeySpoolerRunTimestamp() error { - return v.updateSpoolerRunSequence(authNKeyTable) -} - -func (v *View) GetLatestAuthNKeyFailedEvent(sequence uint64) (*repository.FailedEvent, error) { - return v.latestFailedEvent(authNKeyTable, sequence) -} - -func (v *View) ProcessedAuthNKeyFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) -} diff --git a/internal/auth/repository/user.go b/internal/auth/repository/user.go index 0f9c573c81..72ab396108 100644 --- a/internal/auth/repository/user.go +++ b/internal/auth/repository/user.go @@ -5,8 +5,6 @@ import ( "time" "github.com/caos/zitadel/internal/domain" - key_model "github.com/caos/zitadel/internal/key/model" - "github.com/caos/zitadel/internal/user/model" ) @@ -18,8 +16,6 @@ type UserRepository interface { UserByID(ctx context.Context, userID string) (*model.UserView, error) UserByLoginName(ctx context.Context, loginName string) (*model.UserView, error) - MachineKeyByID(ctx context.Context, keyID string) (*key_model.AuthNKeyView, error) - SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error) SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error) diff --git a/internal/management/repository/eventsourcing/eventstore/project.go b/internal/management/repository/eventsourcing/eventstore/project.go index 7d4c1db126..d8d82cb84b 100644 --- a/internal/management/repository/eventsourcing/eventstore/project.go +++ b/internal/management/repository/eventsourcing/eventstore/project.go @@ -18,8 +18,6 @@ import ( iam_model "github.com/caos/zitadel/internal/iam/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_view "github.com/caos/zitadel/internal/iam/repository/view" - key_model "github.com/caos/zitadel/internal/key/model" - key_view_model "github.com/caos/zitadel/internal/key/repository/view/model" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" proj_model "github.com/caos/zitadel/internal/project/model" proj_view "github.com/caos/zitadel/internal/project/repository/view" @@ -116,59 +114,6 @@ func (repo *ProjectRepo) ApplicationChanges(ctx context.Context, projectID strin return changes, nil } -func (repo *ProjectRepo) SearchClientKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error) { - err := request.EnsureLimit(repo.SearchLimit) - if err != nil { - return nil, err - } - sequence, sequenceErr := repo.View.GetLatestAuthNKeySequence() - logging.Log("EVENT-ADwgw").OnError(sequenceErr).Warn("could not read latest authn key sequence") - keys, count, err := repo.View.SearchAuthNKeys(request) - if err != nil { - return nil, err - } - result := &key_model.AuthNKeySearchResponse{ - Offset: request.Offset, - Limit: request.Limit, - TotalResult: count, - Result: key_view_model.AuthNKeysToModel(keys), - } - if sequenceErr == nil { - result.Sequence = sequence.CurrentSequence - result.Timestamp = sequence.LastSuccessfulSpoolerRun - } - return result, nil -} - -func (repo *ProjectRepo) GetClientKey(ctx context.Context, projectID, applicationID, keyID string) (*key_model.AuthNKeyView, error) { - key, viewErr := repo.View.AuthNKeyByIDs(applicationID, keyID) - if viewErr != nil { - return nil, viewErr - } - - events, esErr := repo.getProjectEvents(ctx, projectID, key.Sequence) - if caos_errs.IsNotFound(viewErr) && len(events) == 0 { - return nil, caos_errs.ThrowNotFound(nil, "EVENT-SFf2g", "Errors.User.KeyNotFound") - } - - if esErr != nil { - logging.Log("EVENT-ADbf2").WithError(viewErr).Debug("error retrieving new events") - return key_view_model.AuthNKeyToModel(key), nil - } - - viewKey := *key - for _, event := range events { - err := key.AppendEventIfMyClientKey(event) - if err != nil { - return key_view_model.AuthNKeyToModel(&viewKey), nil - } - if key.State != int32(proj_model.AppStateActive) { - return nil, caos_errs.ThrowNotFound(nil, "EVENT-Adfg3", "Errors.User.KeyNotFound") - } - } - return key_view_model.AuthNKeyToModel(key), nil -} - func (repo *ProjectRepo) ProjectGrantMemberByID(ctx context.Context, projectID, userID string) (*proj_model.ProjectGrantMemberView, error) { member, err := repo.View.ProjectGrantMemberByIDs(projectID, userID) if err != nil { diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index 68c335d632..5c9a5ecdeb 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -14,8 +14,6 @@ import ( v1 "github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1/models" iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" - key_model "github.com/caos/zitadel/internal/key/model" - key_view_model "github.com/caos/zitadel/internal/key/repository/view/model" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" usr_model "github.com/caos/zitadel/internal/user/model" usr_view "github.com/caos/zitadel/internal/user/repository/view" @@ -269,38 +267,6 @@ func (repo *UserRepo) ExternalIDPsByIDPConfigIDAndResourceOwner(ctx context.Cont return model.ExternalIDPViewsToModel(externalIDPs), nil } -func (repo *UserRepo) GetMachineKey(ctx context.Context, userID, keyID string) (*key_model.AuthNKeyView, error) { - key, err := repo.View.AuthNKeyByIDs(userID, keyID) - if err != nil { - return nil, err - } - return key_view_model.AuthNKeyToModel(key), nil -} - -func (repo *UserRepo) SearchMachineKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error) { - err := request.EnsureLimit(repo.SearchLimit) - if err != nil { - return nil, err - } - sequence, seqErr := repo.View.GetLatestAuthNKeySequence() - logging.Log("EVENT-Sk8fs").OnError(seqErr).Warn("could not read latest authn key sequence") - keys, count, err := repo.View.SearchAuthNKeys(request) - if err != nil { - return nil, err - } - result := &key_model.AuthNKeySearchResponse{ - Offset: request.Offset, - Limit: request.Limit, - TotalResult: count, - Result: key_view_model.AuthNKeysToModel(keys), - } - if seqErr == nil { - result.Sequence = sequence.CurrentSequence - result.Timestamp = sequence.LastSuccessfulSpoolerRun - } - return result, nil -} - func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.Email, error) { user, err := repo.UserByID(ctx, userID) if err != nil { diff --git a/internal/management/repository/eventsourcing/handler/authn_keys.go b/internal/management/repository/eventsourcing/handler/authn_keys.go deleted file mode 100644 index 82e6edb60f..0000000000 --- a/internal/management/repository/eventsourcing/handler/authn_keys.go +++ /dev/null @@ -1,124 +0,0 @@ -package handler - -import ( - "github.com/caos/zitadel/internal/eventstore/v1" - "time" - - "github.com/caos/logging" - - es_models "github.com/caos/zitadel/internal/eventstore/v1/models" - "github.com/caos/zitadel/internal/eventstore/v1/query" - "github.com/caos/zitadel/internal/eventstore/v1/spooler" - key_model "github.com/caos/zitadel/internal/key/repository/view/model" - proj_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" - user_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" -) - -const ( - authnKeysTable = "management.authn_keys" -) - -type AuthNKeys struct { - handler - subscription *v1.Subscription -} - -func newAuthNKeys(handler handler) *AuthNKeys { - h := &AuthNKeys{ - handler: handler, - } - - h.subscribe() - - return h -} - -func (k *AuthNKeys) subscribe() { - k.subscription = k.es.Subscribe(k.AggregateTypes()...) - go func() { - for event := range k.subscription.Events { - query.ReduceEvent(k, event) - } - }() -} - -func (k *AuthNKeys) ViewModel() string { - return authnKeysTable -} - -func (k *AuthNKeys) Subscription() *v1.Subscription { - return k.subscription -} - -func (_ *AuthNKeys) AggregateTypes() []es_models.AggregateType { - return []es_models.AggregateType{user_model.UserAggregate, proj_model.ProjectAggregate} -} - -func (k *AuthNKeys) CurrentSequence() (uint64, error) { - sequence, err := k.view.GetLatestAuthNKeySequence() - if err != nil { - return 0, err - } - return sequence.CurrentSequence, nil -} - -func (k *AuthNKeys) EventQuery() (*es_models.SearchQuery, error) { - sequence, err := k.view.GetLatestAuthNKeySequence() - if err != nil { - return nil, err - } - return es_models.NewSearchQuery(). - AggregateTypeFilter(k.AggregateTypes()...). - LatestSequenceFilter(sequence.CurrentSequence), nil -} - -func (k *AuthNKeys) Reduce(event *es_models.Event) (err error) { - switch event.AggregateType { - case user_model.UserAggregate, - proj_model.ProjectAggregate: - err = k.processAuthNKeys(event) - } - return err -} - -func (k *AuthNKeys) processAuthNKeys(event *es_models.Event) (err error) { - key := new(key_model.AuthNKeyView) - switch event.Type { - case user_model.MachineKeyAdded, - proj_model.ClientKeyAdded: - err = key.AppendEvent(event) - if key.ExpirationDate.Before(time.Now()) { - return k.view.ProcessedAuthNKeySequence(event) - } - case user_model.MachineKeyRemoved: - err = key.SetUserData(event) - if err != nil { - return err - } - return k.view.DeleteAuthNKey(key.ID, event) - case proj_model.ClientKeyRemoved: - err = key.SetClientData(event) - if err != nil { - return err - } - return k.view.DeleteAuthNKey(key.ID, event) - case user_model.UserRemoved, - proj_model.ApplicationRemoved: - return k.view.DeleteAuthNKeysByObjectID(event.AggregateID, event) - default: - return k.view.ProcessedAuthNKeySequence(event) - } - if err != nil { - return err - } - return k.view.PutAuthNKey(key, event) -} - -func (d *AuthNKeys) OnError(event *es_models.Event, err error) error { - logging.LogWithFields("SPOOL-S9fe", "id", event.AggregateID).WithError(err).Warn("something went wrong in machine key handler") - return spooler.HandleError(event, err, d.view.GetLatestAuthNKeyFailedEvent, d.view.ProcessedAuthNKeyFailedEvent, d.view.ProcessedAuthNKeySequence, d.errorCountUntilSkip) -} - -func (d *AuthNKeys) OnSuccess() error { - return spooler.HandleSuccess(d.view.UpdateAuthNKeySpoolerRunTimestamp) -} diff --git a/internal/management/repository/eventsourcing/handler/handler.go b/internal/management/repository/eventsourcing/handler/handler.go index 130ab93d90..a025c308ac 100644 --- a/internal/management/repository/eventsourcing/handler/handler.go +++ b/internal/management/repository/eventsourcing/handler/handler.go @@ -42,8 +42,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount, es}), newUserMembership( handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount, es}), - newAuthNKeys( - handler{view, bulkLimit, configs.cycleDuration("MachineKeys"), errorCount, es}), newIDPConfig( handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), newIDPProvider( diff --git a/internal/management/repository/eventsourcing/view/authn_keys.go b/internal/management/repository/eventsourcing/view/authn_keys.go deleted file mode 100644 index 720baf6bde..0000000000 --- a/internal/management/repository/eventsourcing/view/authn_keys.go +++ /dev/null @@ -1,74 +0,0 @@ -package view - -import ( - "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/v1/models" - key_model "github.com/caos/zitadel/internal/key/model" - "github.com/caos/zitadel/internal/key/repository/view" - "github.com/caos/zitadel/internal/key/repository/view/model" - "github.com/caos/zitadel/internal/view/repository" -) - -const ( - authNKeyTable = "management.authn_keys" -) - -func (v *View) AuthNKeyByIDs(objectID, keyID string) (*model.AuthNKeyView, error) { - return view.AuthNKeyByIDs(v.Db, authNKeyTable, objectID, keyID) -} - -func (v *View) AuthNKeysByObjectID(objectID string) ([]*model.AuthNKeyView, error) { - return view.AuthNKeysByObjectID(v.Db, authNKeyTable, objectID) -} - -func (v *View) AuthNKeyByID(keyID string) (*model.AuthNKeyView, error) { - return view.AuthNKeyByID(v.Db, authNKeyTable, keyID) -} - -func (v *View) SearchAuthNKeys(request *key_model.AuthNKeySearchRequest) ([]*model.AuthNKeyView, uint64, error) { - return view.SearchAuthNKeys(v.Db, authNKeyTable, request) -} - -func (v *View) PutAuthNKey(key *model.AuthNKeyView, event *models.Event) error { - err := view.PutAuthNKey(v.Db, authNKeyTable, key) - if err != nil { - return err - } - return v.ProcessedAuthNKeySequence(event) -} - -func (v *View) DeleteAuthNKey(keyID string, event *models.Event) error { - err := view.DeleteAuthNKey(v.Db, authNKeyTable, keyID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedAuthNKeySequence(event) -} - -func (v *View) DeleteAuthNKeysByObjectID(objectID string, event *models.Event) error { - err := view.DeleteAuthNKey(v.Db, authNKeyTable, objectID) - if err != nil && !errors.IsNotFound(err) { - return err - } - return v.ProcessedAuthNKeySequence(event) -} - -func (v *View) GetLatestAuthNKeySequence() (*repository.CurrentSequence, error) { - return v.latestSequence(authNKeyTable) -} - -func (v *View) ProcessedAuthNKeySequence(event *models.Event) error { - return v.saveCurrentSequence(authNKeyTable, event) -} - -func (v *View) UpdateAuthNKeySpoolerRunTimestamp() error { - return v.updateSpoolerRunSequence(authNKeyTable) -} - -func (v *View) GetLatestAuthNKeyFailedEvent(sequence uint64) (*repository.FailedEvent, error) { - return v.latestFailedEvent(authNKeyTable, sequence) -} - -func (v *View) ProcessedAuthNKeyFailedEvent(failedEvent *repository.FailedEvent) error { - return v.saveFailedEvent(failedEvent) -} diff --git a/internal/management/repository/project.go b/internal/management/repository/project.go index a0f2388488..3cdb739e49 100644 --- a/internal/management/repository/project.go +++ b/internal/management/repository/project.go @@ -6,7 +6,6 @@ import ( iam_model "github.com/caos/zitadel/internal/iam/model" - key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/project/model" ) @@ -18,8 +17,6 @@ type ProjectRepository interface { ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool, retention time.Duration) (*model.ProjectChanges, error) ApplicationChanges(ctx context.Context, projectID string, appID string, lastSequence uint64, limit uint64, sortAscending bool, retention time.Duration) (*model.ApplicationChanges, error) - SearchClientKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error) - GetClientKey(ctx context.Context, projectID, applicationID, keyID string) (*key_model.AuthNKeyView, error) SearchProjectGrantMembers(ctx context.Context, request *model.ProjectGrantMemberSearchRequest) (*model.ProjectGrantMemberSearchResponse, error) diff --git a/internal/management/repository/user.go b/internal/management/repository/user.go index 31d3040322..b0ec05a48d 100644 --- a/internal/management/repository/user.go +++ b/internal/management/repository/user.go @@ -5,7 +5,6 @@ import ( "time" "github.com/caos/zitadel/internal/domain" - key_model "github.com/caos/zitadel/internal/key/model" "github.com/caos/zitadel/internal/user/model" ) @@ -33,9 +32,6 @@ type UserRepository interface { ExternalIDPsByIDPConfigID(ctx context.Context, idpConfigID string) ([]*model.ExternalIDPView, error) ExternalIDPsByIDPConfigIDAndResourceOwner(ctx context.Context, idpConfigID, resourceOwner string) ([]*model.ExternalIDPView, error) - SearchMachineKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error) - GetMachineKey(ctx context.Context, userID, keyID string) (*key_model.AuthNKeyView, error) - EmailByID(ctx context.Context, userID string) (*model.Email, error) PhoneByID(ctx context.Context, userID string) (*model.Phone, error) diff --git a/internal/query/authn_key.go b/internal/query/authn_key.go new file mode 100644 index 0000000000..af6f3229ea --- /dev/null +++ b/internal/query/authn_key.go @@ -0,0 +1,259 @@ +package query + +import ( + "context" + "database/sql" + errs "errors" + "time" + + sq "github.com/Masterminds/squirrel" + + "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/query/projection" +) + +var ( + authNKeyTable = table{ + name: projection.AuthNKeyTable, + } + AuthNKeyColumnID = Column{ + name: projection.AuthNKeyIDCol, + table: authNKeyTable, + } + AuthNKeyColumnCreationDate = Column{ + name: projection.AuthNKeyCreationDateCol, + table: authNKeyTable, + } + AuthNKeyColumnResourceOwner = Column{ + name: projection.AuthNKeyResourceOwnerCol, + table: authNKeyTable, + } + AuthNKeyColumnAggregateID = Column{ + name: projection.AuthNKeyAggregateIDCol, + table: authNKeyTable, + } + AuthNKeyColumnSequence = Column{ + name: projection.AuthNKeySequenceCol, + table: authNKeyTable, + } + AuthNKeyColumnObjectID = Column{ + name: projection.AuthNKeyObjectIDCol, + table: authNKeyTable, + } + AuthNKeyColumnExpiration = Column{ + name: projection.AuthNKeyExpirationCol, + table: authNKeyTable, + } + AuthNKeyColumnIdentifier = Column{ + name: projection.AuthNKeyIdentifierCol, + table: authNKeyTable, + } + AuthNKeyColumnPublicKey = Column{ + name: projection.AuthNKeyPublicKeyCol, + table: authNKeyTable, + } + AuthNKeyColumnType = Column{ + name: projection.AuthNKeyTypeCol, + table: authNKeyTable, + } + AuthNKeyColumnEnabled = Column{ + name: projection.AuthNKeyEnabledCol, + table: authNKeyTable, + } +) + +type AuthNKeys struct { + SearchResponse + AuthNKeys []*AuthNKey +} + +type AuthNKey struct { + ID string + CreationDate time.Time + ResourceOwner string + Sequence uint64 + + Expiration time.Time + Type domain.AuthNKeyType +} + +type AuthNKeySearchQueries struct { + SearchRequest + Queries []SearchQuery +} + +func (q *AuthNKeySearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder { + query = q.SearchRequest.toQuery(query) + for _, q := range q.Queries { + query = q.toQuery(query) + } + return query +} + +func (q *Queries) SearchAuthNKeys(ctx context.Context, queries *AuthNKeySearchQueries) (authNKeys *AuthNKeys, err error) { + query, scan := prepareAuthNKeysQuery() + query = queries.toQuery(query) + stmt, args, err := query.Where( + sq.Eq{ + AuthNKeyColumnEnabled.identifier(): true, + }, + ).ToSql() + if err != nil { + return nil, errors.ThrowInvalidArgument(err, "QUERY-SAf3f", "Errors.Query.InvalidRequest") + } + + rows, err := q.client.QueryContext(ctx, stmt, args...) + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-Dbg53", "Errors.Internal") + } + authNKeys, err = scan(rows) + if err != nil { + return nil, err + } + authNKeys.LatestSequence, err = q.latestSequence(ctx, authNKeyTable) + return authNKeys, err +} + +func (q *Queries) GetAuthNKeyByID(ctx context.Context, id string, queries ...SearchQuery) (*AuthNKey, error) { + query, scan := prepareAuthNKeyQuery() + for _, q := range queries { + query = q.toQuery(query) + } + stmt, args, err := query.Where( + sq.Eq{ + AuthNKeyColumnID.identifier(): id, + AuthNKeyColumnEnabled.identifier(): true, + }).ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-AGhg4", "Errors.Query.SQLStatement") + } + + row := q.client.QueryRowContext(ctx, stmt, args...) + return scan(row) +} + +func (q *Queries) GetAuthNKeyPublicKeyByIDAndIdentifier(ctx context.Context, id string, identifier string) ([]byte, error) { + stmt, scan := prepareAuthNKeyPublicKeyQuery() + query, args, err := stmt.Where( + sq.And{ + sq.Eq{ + AuthNKeyColumnID.identifier(): id, + AuthNKeyColumnIdentifier.identifier(): identifier, + AuthNKeyColumnEnabled.identifier(): true, + }, + sq.Gt{ + AuthNKeyColumnExpiration.identifier(): time.Now(), + }, + }, + ).ToSql() + if err != nil { + return nil, errors.ThrowInternal(err, "QUERY-DAb32", "Errors.Query.SQLStatement") + } + + row := q.client.QueryRowContext(ctx, query, args...) + return scan(row) +} + +func NewAuthNKeyResourceOwnerQuery(id string) (SearchQuery, error) { + return NewTextQuery(AuthNKeyColumnResourceOwner, id, TextEquals) +} + +func NewAuthNKeyAggregateIDQuery(id string) (SearchQuery, error) { + return NewTextQuery(AuthNKeyColumnAggregateID, id, TextEquals) +} + +func NewAuthNKeyObjectIDQuery(id string) (SearchQuery, error) { + return NewTextQuery(AuthNKeyColumnObjectID, id, TextEquals) +} + +func prepareAuthNKeysQuery() (sq.SelectBuilder, func(rows *sql.Rows) (*AuthNKeys, error)) { + return sq.Select( + AuthNKeyColumnID.identifier(), + AuthNKeyColumnCreationDate.identifier(), + AuthNKeyColumnResourceOwner.identifier(), + AuthNKeyColumnSequence.identifier(), + AuthNKeyColumnExpiration.identifier(), + AuthNKeyColumnType.identifier(), + countColumn.identifier(), + ).From(authNKeyTable.identifier()).PlaceholderFormat(sq.Dollar), + func(rows *sql.Rows) (*AuthNKeys, error) { + authNKeys := make([]*AuthNKey, 0) + var count uint64 + for rows.Next() { + authNKey := new(AuthNKey) + err := rows.Scan( + &authNKey.ID, + &authNKey.CreationDate, + &authNKey.ResourceOwner, + &authNKey.Sequence, + &authNKey.Expiration, + &authNKey.Type, + &count, + ) + if err != nil { + return nil, err + } + authNKeys = append(authNKeys, authNKey) + } + + if err := rows.Close(); err != nil { + return nil, errors.ThrowInternal(err, "QUERY-Dgfn3", "Errors.Query.CloseRows") + } + + return &AuthNKeys{ + AuthNKeys: authNKeys, + SearchResponse: SearchResponse{ + Count: count, + }, + }, nil + } +} + +func prepareAuthNKeyQuery() (sq.SelectBuilder, func(row *sql.Row) (*AuthNKey, error)) { + return sq.Select( + AuthNKeyColumnID.identifier(), + AuthNKeyColumnCreationDate.identifier(), + AuthNKeyColumnResourceOwner.identifier(), + AuthNKeyColumnSequence.identifier(), + AuthNKeyColumnExpiration.identifier(), + AuthNKeyColumnType.identifier(), + ).From(authNKeyTable.identifier()).PlaceholderFormat(sq.Dollar), + func(row *sql.Row) (*AuthNKey, error) { + authNKey := new(AuthNKey) + err := row.Scan( + &authNKey.ID, + &authNKey.CreationDate, + &authNKey.ResourceOwner, + &authNKey.Sequence, + &authNKey.Expiration, + &authNKey.Type, + ) + if err != nil { + if errs.Is(err, sql.ErrNoRows) { + return nil, errors.ThrowNotFound(err, "QUERY-Dgr3g", "Errors.AuthNKey.NotFound") + } + return nil, errors.ThrowInternal(err, "QUERY-BGnbr", "Errors.Internal") + } + return authNKey, nil + } +} + +func prepareAuthNKeyPublicKeyQuery() (sq.SelectBuilder, func(row *sql.Row) ([]byte, error)) { + return sq.Select( + AuthNKeyColumnPublicKey.identifier(), + ).From(authNKeyTable.identifier()).PlaceholderFormat(sq.Dollar), + func(row *sql.Row) ([]byte, error) { + var publicKey []byte + err := row.Scan( + &publicKey, + ) + if err != nil { + if errs.Is(err, sql.ErrNoRows) { + return nil, errors.ThrowNotFound(err, "QUERY-SDf32", "Errors.AuthNKey.NotFound") + } + return nil, errors.ThrowInternal(err, "QUERY-Bfs2a", "Errors.Internal") + } + return publicKey, nil + } +} diff --git a/internal/query/authn_key_test.go b/internal/query/authn_key_test.go new file mode 100644 index 0000000000..31a59a881f --- /dev/null +++ b/internal/query/authn_key_test.go @@ -0,0 +1,331 @@ +package query + +import ( + "database/sql" + "database/sql/driver" + "errors" + "fmt" + "regexp" + "testing" + + "github.com/caos/zitadel/internal/domain" + errs "github.com/caos/zitadel/internal/errors" +) + +func Test_AuthNKeyPrepares(t *testing.T) { + type want struct { + sqlExpectations sqlExpectation + err checkErr + } + tests := []struct { + name string + prepare interface{} + want want + object interface{} + }{ + { + name: "prepareAuthNKeysQuery no result", + prepare: prepareAuthNKeysQuery, + want: want{ + sqlExpectations: mockQueries( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.id,`+ + ` zitadel.projections.authn_keys.creation_date,`+ + ` zitadel.projections.authn_keys.resource_owner,`+ + ` zitadel.projections.authn_keys.sequence,`+ + ` zitadel.projections.authn_keys.expiration,`+ + ` zitadel.projections.authn_keys.type,`+ + ` COUNT(*) OVER ()`+ + ` FROM zitadel.projections.authn_keys`), + nil, + nil, + ), + }, + object: &AuthNKeys{AuthNKeys: []*AuthNKey{}}, + }, + { + name: "prepareAuthNKeysQuery one result", + prepare: prepareAuthNKeysQuery, + want: want{ + sqlExpectations: mockQueries( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.id,`+ + ` zitadel.projections.authn_keys.creation_date,`+ + ` zitadel.projections.authn_keys.resource_owner,`+ + ` zitadel.projections.authn_keys.sequence,`+ + ` zitadel.projections.authn_keys.expiration,`+ + ` zitadel.projections.authn_keys.type,`+ + ` COUNT(*) OVER ()`+ + ` FROM zitadel.projections.authn_keys`), + []string{ + "id", + "creation_date", + "resource_owner", + "sequence", + "expiration", + "type", + "count", + }, + [][]driver.Value{ + { + "id", + testNow, + "ro", + uint64(20211109), + testNow, + 1, + }, + }, + ), + }, + object: &AuthNKeys{ + SearchResponse: SearchResponse{ + Count: 1, + }, + AuthNKeys: []*AuthNKey{ + { + ID: "id", + CreationDate: testNow, + ResourceOwner: "ro", + Sequence: 20211109, + Expiration: testNow, + Type: domain.AuthNKeyTypeJSON, + }, + }, + }, + }, + { + name: "prepareAuthNKeysQuery multiple result", + prepare: prepareAuthNKeysQuery, + want: want{ + sqlExpectations: mockQueries( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.id,`+ + ` zitadel.projections.authn_keys.creation_date,`+ + ` zitadel.projections.authn_keys.resource_owner,`+ + ` zitadel.projections.authn_keys.sequence,`+ + ` zitadel.projections.authn_keys.expiration,`+ + ` zitadel.projections.authn_keys.type,`+ + ` COUNT(*) OVER ()`+ + ` FROM zitadel.projections.authn_keys`), + []string{ + "id", + "creation_date", + "resource_owner", + "sequence", + "expiration", + "type", + "count", + }, + [][]driver.Value{ + { + "id-1", + testNow, + "ro", + uint64(20211109), + testNow, + 1, + }, + { + "id-2", + testNow, + "ro", + uint64(20211109), + testNow, + 1, + }, + }, + ), + }, + object: &AuthNKeys{ + SearchResponse: SearchResponse{ + Count: 2, + }, + AuthNKeys: []*AuthNKey{ + { + ID: "id-1", + CreationDate: testNow, + ResourceOwner: "ro", + Sequence: 20211109, + Expiration: testNow, + Type: domain.AuthNKeyTypeJSON, + }, + { + ID: "id-2", + CreationDate: testNow, + ResourceOwner: "ro", + Sequence: 20211109, + Expiration: testNow, + Type: domain.AuthNKeyTypeJSON, + }, + }, + }, + }, + { + name: "prepareAuthNKeysQuery sql err", + prepare: prepareAuthNKeysQuery, + want: want{ + sqlExpectations: mockQueryErr( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.id,`+ + ` zitadel.projections.authn_keys.creation_date,`+ + ` zitadel.projections.authn_keys.resource_owner,`+ + ` zitadel.projections.authn_keys.sequence,`+ + ` zitadel.projections.authn_keys.expiration,`+ + ` zitadel.projections.authn_keys.type,`+ + ` COUNT(*) OVER ()`+ + ` FROM zitadel.projections.authn_keys`), + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: nil, + }, + { + name: "prepareAuthNKeyQuery no result", + prepare: prepareAuthNKeyQuery, + want: want{ + sqlExpectations: mockQueries( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.id,`+ + ` zitadel.projections.authn_keys.creation_date,`+ + ` zitadel.projections.authn_keys.resource_owner,`+ + ` zitadel.projections.authn_keys.sequence,`+ + ` zitadel.projections.authn_keys.expiration,`+ + ` zitadel.projections.authn_keys.type`+ + ` FROM zitadel.projections.authn_keys`), + nil, + nil, + ), + err: func(err error) (error, bool) { + if !errs.IsNotFound(err) { + return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false + } + return nil, true + }, + }, + object: (*AuthNKey)(nil), + }, + { + name: "prepareAuthNKeyQuery found", + prepare: prepareAuthNKeyQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.id,`+ + ` zitadel.projections.authn_keys.creation_date,`+ + ` zitadel.projections.authn_keys.resource_owner,`+ + ` zitadel.projections.authn_keys.sequence,`+ + ` zitadel.projections.authn_keys.expiration,`+ + ` zitadel.projections.authn_keys.type`+ + ` FROM zitadel.projections.authn_keys`), + []string{ + "id", + "creation_date", + "resource_owner", + "sequence", + "expiration", + "type", + }, + []driver.Value{ + "id", + testNow, + "ro", + uint64(20211109), + testNow, + 1, + }, + ), + }, + object: &AuthNKey{ + ID: "id", + CreationDate: testNow, + ResourceOwner: "ro", + Sequence: 20211109, + Expiration: testNow, + Type: domain.AuthNKeyTypeJSON, + }, + }, + { + name: "prepareAuthNKeyQuery sql err", + prepare: prepareAuthNKeyQuery, + want: want{ + sqlExpectations: mockQueryErr( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.id,`+ + ` zitadel.projections.authn_keys.creation_date,`+ + ` zitadel.projections.authn_keys.resource_owner,`+ + ` zitadel.projections.authn_keys.sequence,`+ + ` zitadel.projections.authn_keys.expiration,`+ + ` zitadel.projections.authn_keys.type`+ + ` FROM zitadel.projections.authn_keys`), + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: nil, + }, + { + name: "prepareAuthNKeyPublicKeyQuery no result", + prepare: prepareAuthNKeyPublicKeyQuery, + want: want{ + sqlExpectations: mockQueries( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.public_key`+ + ` FROM zitadel.projections.authn_keys`), + nil, + nil, + ), + err: func(err error) (error, bool) { + if !errs.IsNotFound(err) { + return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false + } + return nil, true + }, + }, + object: ([]byte)(nil), + }, + { + name: "prepareAuthNKeyPublicKeyQuery found", + prepare: prepareAuthNKeyPublicKeyQuery, + want: want{ + sqlExpectations: mockQuery( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.public_key`+ + ` FROM zitadel.projections.authn_keys`), + []string{ + "public_key", + }, + []driver.Value{ + []byte("publicKey"), + }, + ), + }, + object: []byte("publicKey"), + }, + { + name: "prepareAuthNKeyPublicKeyQuery sql err", + prepare: prepareAuthNKeyPublicKeyQuery, + want: want{ + sqlExpectations: mockQueryErr( + regexp.QuoteMeta(`SELECT zitadel.projections.authn_keys.public_key`+ + ` FROM zitadel.projections.authn_keys`), + sql.ErrConnDone, + ), + err: func(err error) (error, bool) { + if !errors.Is(err, sql.ErrConnDone) { + return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false + } + return nil, true + }, + }, + object: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err) + }) + } +}