fix(queries): authn keys (#2820)

* begin authn keys

* single table for state change

* add key type

* begin authn keys query

* query

* tests

* fix merge

* remove wrong migration version

* improve filter

* Update projection.go

* cleanup
This commit is contained in:
Livio Amstutz 2021-12-14 10:57:20 +01:00 committed by GitHub
parent 79f7c1198b
commit ae840f364c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 684 additions and 589 deletions

View File

@ -1,58 +1,38 @@
package authn package authn
import ( import (
"github.com/caos/logging" "google.golang.org/protobuf/types/known/timestamppb"
"github.com/golang/protobuf/ptypes"
"github.com/caos/zitadel/internal/api/grpc/object" "github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
key_model "github.com/caos/zitadel/internal/key/model" key_model "github.com/caos/zitadel/internal/key/model"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/pkg/grpc/authn" "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)) k := make([]*authn.Key, len(keys))
for i, key := range keys { for i, key := range keys {
k[i] = KeyViewToPb(key) k[i] = KeyToPb(key)
} }
return k return k
} }
func KeyViewToPb(key *key_model.AuthNKeyView) *authn.Key { func KeyToPb(key *query.AuthNKey) *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")
return &authn.Key{ return &authn.Key{
Id: key.ID, Id: key.ID,
Type: KeyTypeToPb(key.Type), Type: KeyTypeToPb(key.Type),
ExpirationDate: expDate, ExpirationDate: timestamppb.New(key.Expiration),
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
key.Sequence, key.Sequence,
key.CreationDate, key.CreationDate,
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 { switch typ {
case key_model.AuthNKeyTypeJSON: case key_model.AuthNKeyTypeJSON:
return authn.KeyType_KEY_TYPE_JSON return authn.KeyType_KEY_TYPE_JSON

View File

@ -8,6 +8,7 @@ import (
change_grpc "github.com/caos/zitadel/internal/api/grpc/change" change_grpc "github.com/caos/zitadel/internal/api/grpc/change"
object_grpc "github.com/caos/zitadel/internal/api/grpc/object" object_grpc "github.com/caos/zitadel/internal/api/grpc/object"
project_grpc "github.com/caos/zitadel/internal/api/grpc/project" 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" 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) { 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 { if err != nil {
return nil, err 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) { 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 { if err != nil {
return nil, err return nil, err
} }
keys, err := s.project.SearchClientKeys(ctx, queries) keys, err := s.query.SearchAuthNKeys(ctx, queries)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &mgmt_pb.ListAppKeysResponse{ return &mgmt_pb.ListAppKeysResponse{
Result: authn_grpc.KeyViewsToPb(keys.Result), Result: authn_grpc.KeysToPb(keys.AuthNKeys),
Details: object_grpc.ToListDetails( Details: object_grpc.ToListDetails(
keys.TotalResult, keys.Count,
keys.Sequence, keys.Sequence,
keys.Timestamp, keys.Timestamp,
), ),

View File

@ -1,14 +1,15 @@
package management package management
import ( import (
"context"
"time" "time"
"github.com/caos/zitadel/internal/api/authz"
authn_grpc "github.com/caos/zitadel/internal/api/grpc/authn" authn_grpc "github.com/caos/zitadel/internal/api/grpc/authn"
"github.com/caos/zitadel/internal/api/grpc/object" "github.com/caos/zitadel/internal/api/grpc/object"
app_grpc "github.com/caos/zitadel/internal/api/grpc/project" app_grpc "github.com/caos/zitadel/internal/api/grpc/project"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
key_model "github.com/caos/zitadel/internal/key/model"
"github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/internal/query"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management" 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) offset, limit, asc := object.ListQueryToModel(req.Query)
queries := make([]*key_model.AuthNKeySearchQuery, 0) return &query.AuthNKeySearchQueries{
queries = append(queries, &key_model.AuthNKeySearchQuery{ SearchRequest: query.SearchRequest{
Key: key_model.AuthNKeyObjectID, Offset: offset,
Method: domain.SearchMethodEquals, Limit: limit,
Value: req.AppId, Asc: asc,
}) },
return &key_model.AuthNKeySearchRequest{ Queries: []query.SearchQuery{
Offset: offset, resourcOwner,
Limit: limit, projectID,
Asc: asc, appID,
//SortingColumn: //TODO: sorting },
Queries: queries,
}, nil }, nil
} }

View File

@ -15,6 +15,7 @@ import (
"github.com/caos/zitadel/internal/api/grpc/user" "github.com/caos/zitadel/internal/api/grpc/user"
user_grpc "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/domain"
"github.com/caos/zitadel/internal/query"
grant_model "github.com/caos/zitadel/internal/usergrant/model" grant_model "github.com/caos/zitadel/internal/usergrant/model"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management" 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) { 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 { if err != nil {
return nil, err 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) { 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 { if err != nil {
return nil, err return nil, err
} }
return &mgmt_pb.ListMachineKeysResponse{ return &mgmt_pb.ListMachineKeysResponse{
Result: authn.KeyViewsToPb(result.Result), Result: authn.KeysToPb(result.AuthNKeys),
Details: obj_grpc.ToListDetails( Details: obj_grpc.ToListDetails(
result.TotalResult, result.Count,
result.Sequence, result.Sequence,
result.Timestamp, result.Timestamp,
), ),

View File

@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/golang/protobuf/ptypes"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/caos/zitadel/internal/api/authz" "github.com/caos/zitadel/internal/api/authz"
@ -15,7 +14,6 @@ import (
user_grpc "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/domain"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
key_model "github.com/caos/zitadel/internal/key/model"
"github.com/caos/zitadel/internal/query" "github.com/caos/zitadel/internal/query"
user_model "github.com/caos/zitadel/internal/user/model" user_model "github.com/caos/zitadel/internal/user/model"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management" 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 { func ListMachineKeysRequestToQuery(ctx context.Context, req *mgmt_pb.ListMachineKeysRequest) (*query.AuthNKeySearchQueries, error) {
offset, limit, asc := object.ListQueryToModel(req.Query) resourcOwner, err := query.NewAuthNKeyResourceOwnerQuery(authz.GetCtxData(ctx).OrgID)
return &key_model.AuthNKeySearchRequest{ if err != nil {
Offset: offset, return nil, err
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,
},
},
} }
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 { func AddMachineKeyRequestToDomain(req *mgmt_pb.AddMachineKeyRequest) *domain.MachineKey {
expDate := time.Time{} expDate := time.Time{}
if req.ExpirationDate != nil { if req.ExpirationDate != nil {
var err error expDate = req.ExpirationDate.AsTime()
expDate, err = ptypes.Timestamp(req.ExpirationDate)
logging.Log("MANAG-iNshR").OnError(err).Debug("unable to parse expiration date")
} }
return &domain.MachineKey{ return &domain.MachineKey{

View File

@ -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) { func (o *OPStorage) GetKeyByIDAndIssuer(ctx context.Context, keyID, issuer string) (_ *jose.JSONWebKey, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
key, err := o.repo.MachineKeyByID(ctx, keyID) publicKeyData, err := o.query.GetAuthNKeyPublicKeyByIDAndIdentifier(ctx, keyID, issuer)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if key.AuthIdentifier != issuer { publicKey, err := crypto.BytesToPublicKey(publicKeyData)
return nil, errors.ThrowPermissionDenied(nil, "OIDC-24jm3", "key from different user")
}
publicKey, err := crypto.BytesToPublicKey(key.PublicKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &jose.JSONWebKey{ return &jose.JSONWebKey{
KeyID: key.ID, KeyID: keyID,
Use: "sig", Use: "sig",
Key: publicKey, Key: publicKey,
}, nil }, nil

View File

@ -15,8 +15,6 @@ import (
v1 "github.com/caos/zitadel/internal/eventstore/v1" v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" 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/telemetry/tracing"
"github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/model"
usr_view "github.com/caos/zitadel/internal/user/repository/view" 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 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) { func (repo *UserRepo) SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error) {
sequence, sequenceErr := repo.View.GetLatestUserSequence() sequence, sequenceErr := repo.View.GetLatestUserSequence()
logging.Log("EVENT-Gdgsw").OnError(sequenceErr).Warn("could not read latest user sequence") logging.Log("EVENT-Gdgsw").OnError(sequenceErr).Warn("could not read latest user sequence")

View File

@ -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)
}

View File

@ -47,8 +47,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
newUserGrant( newUserGrant(
handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}, handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es},
systemDefaults.IamID), systemDefaults.IamID),
newAuthNKeys(
handler{view, bulkLimit, configs.cycleDuration("MachineKey"), errorCount, es}),
newIDPConfig( newIDPConfig(
handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}),
newIDPProvider( newIDPProvider(

View File

@ -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)
}

View File

@ -5,8 +5,6 @@ import (
"time" "time"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
key_model "github.com/caos/zitadel/internal/key/model"
"github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/model"
) )
@ -18,8 +16,6 @@ type UserRepository interface {
UserByID(ctx context.Context, userID string) (*model.UserView, error) UserByID(ctx context.Context, userID string) (*model.UserView, error)
UserByLoginName(ctx context.Context, loginName 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) SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error)
SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error) SearchUserMetadata(ctx context.Context, userID string) (*domain.MetadataSearchResponse, error)

View File

@ -18,8 +18,6 @@ import (
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
iam_view "github.com/caos/zitadel/internal/iam/repository/view" 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" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
proj_model "github.com/caos/zitadel/internal/project/model" proj_model "github.com/caos/zitadel/internal/project/model"
proj_view "github.com/caos/zitadel/internal/project/repository/view" 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 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) { func (repo *ProjectRepo) ProjectGrantMemberByID(ctx context.Context, projectID, userID string) (*proj_model.ProjectGrantMemberView, error) {
member, err := repo.View.ProjectGrantMemberByIDs(projectID, userID) member, err := repo.View.ProjectGrantMemberByIDs(projectID, userID)
if err != nil { if err != nil {

View File

@ -14,8 +14,6 @@ import (
v1 "github.com/caos/zitadel/internal/eventstore/v1" v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model" 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" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
usr_view "github.com/caos/zitadel/internal/user/repository/view" 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 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) { func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.Email, error) {
user, err := repo.UserByID(ctx, userID) user, err := repo.UserByID(ctx, userID)
if err != nil { if err != nil {

View File

@ -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)
}

View File

@ -42,8 +42,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount, es}),
newUserMembership( newUserMembership(
handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount, es}),
newAuthNKeys(
handler{view, bulkLimit, configs.cycleDuration("MachineKeys"), errorCount, es}),
newIDPConfig( newIDPConfig(
handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}), handler{view, bulkLimit, configs.cycleDuration("IDPConfig"), errorCount, es}),
newIDPProvider( newIDPProvider(

View File

@ -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)
}

View File

@ -6,7 +6,6 @@ import (
iam_model "github.com/caos/zitadel/internal/iam/model" 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" "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) 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) 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) SearchProjectGrantMembers(ctx context.Context, request *model.ProjectGrantMemberSearchRequest) (*model.ProjectGrantMemberSearchResponse, error)

View File

@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
key_model "github.com/caos/zitadel/internal/key/model"
"github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/model"
) )
@ -33,9 +32,6 @@ type UserRepository interface {
ExternalIDPsByIDPConfigID(ctx context.Context, idpConfigID string) ([]*model.ExternalIDPView, error) ExternalIDPsByIDPConfigID(ctx context.Context, idpConfigID string) ([]*model.ExternalIDPView, error)
ExternalIDPsByIDPConfigIDAndResourceOwner(ctx context.Context, idpConfigID, resourceOwner 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) EmailByID(ctx context.Context, userID string) (*model.Email, error)
PhoneByID(ctx context.Context, userID string) (*model.Phone, error) PhoneByID(ctx context.Context, userID string) (*model.Phone, error)

259
internal/query/authn_key.go Normal file
View File

@ -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
}
}

View File

@ -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)
})
}
}