mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-13 11:34:26 +00:00
155 lines
4.3 KiB
Go
155 lines
4.3 KiB
Go
|
package query
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"database/sql"
|
||
|
_ "embed"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"time"
|
||
|
|
||
|
"github.com/go-jose/go-jose/v4"
|
||
|
|
||
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||
|
"github.com/zitadel/zitadel/internal/crypto"
|
||
|
"github.com/zitadel/zitadel/internal/domain"
|
||
|
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
//go:embed web_key_by_state.sql
|
||
|
webKeyByStateQuery string
|
||
|
//go:embed web_key_list.sql
|
||
|
webKeyListQuery string
|
||
|
//go:embed web_key_public_keys.sql
|
||
|
webKeyPublicKeysQuery string
|
||
|
)
|
||
|
|
||
|
// GetPublicWebKeyByID gets a public key by it's keyID directly from the eventstore.
|
||
|
func (q *Queries) GetPublicWebKeyByID(ctx context.Context, keyID string) (webKey *jose.JSONWebKey, err error) {
|
||
|
ctx, span := tracing.NewSpan(ctx)
|
||
|
defer func() { span.EndWithError(err) }()
|
||
|
|
||
|
model := NewWebKeyReadModel(keyID, authz.GetInstance(ctx).InstanceID())
|
||
|
if err = q.eventstore.FilterToQueryReducer(ctx, model); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if model.State == domain.WebKeyStateUnspecified || model.State == domain.WebKeyStateRemoved {
|
||
|
return nil, zerrors.ThrowNotFound(nil, "QUERY-AiCh0", "Errors.WebKey.NotFound")
|
||
|
}
|
||
|
return model.PublicKey, nil
|
||
|
}
|
||
|
|
||
|
// GetActiveSigningWebKey gets the current active signing key from the web_keys projection.
|
||
|
// The active signing key is eventual consistent.
|
||
|
func (q *Queries) GetActiveSigningWebKey(ctx context.Context) (webKey *jose.JSONWebKey, err error) {
|
||
|
ctx, span := tracing.NewSpan(ctx)
|
||
|
defer func() { span.EndWithError(err) }()
|
||
|
|
||
|
var keyValue *crypto.CryptoValue
|
||
|
err = q.client.QueryRowContext(ctx, func(row *sql.Row) error {
|
||
|
return row.Scan(&keyValue)
|
||
|
},
|
||
|
webKeyByStateQuery,
|
||
|
authz.GetInstance(ctx).InstanceID(),
|
||
|
domain.WebKeyStateActive,
|
||
|
)
|
||
|
if err != nil {
|
||
|
if errors.Is(err, sql.ErrNoRows) {
|
||
|
return nil, zerrors.ThrowInternal(err, "QUERY-Opoh7", "Errors.WebKey.NoActive")
|
||
|
}
|
||
|
return nil, zerrors.ThrowInternal(err, "QUERY-Shoo0", "Errors.Internal")
|
||
|
}
|
||
|
if err = crypto.DecryptJSON(keyValue, &webKey, q.keyEncryptionAlgorithm); err != nil {
|
||
|
return nil, zerrors.ThrowInternal(err, "QUERY-Iuk0s", "Errors.Internal")
|
||
|
}
|
||
|
return webKey, nil
|
||
|
}
|
||
|
|
||
|
type WebKeyDetails struct {
|
||
|
KeyID string
|
||
|
CreationDate time.Time
|
||
|
ChangeDate time.Time
|
||
|
Sequence int64
|
||
|
State domain.WebKeyState
|
||
|
Config crypto.WebKeyConfig
|
||
|
}
|
||
|
|
||
|
type WebKeyList struct {
|
||
|
Keys []WebKeyDetails
|
||
|
}
|
||
|
|
||
|
// ListWebKeys gets a list of [WebKeyDetails] for the complete instance from the web_keys projection.
|
||
|
// The list is eventual consistent.
|
||
|
func (q *Queries) ListWebKeys(ctx context.Context) (list []WebKeyDetails, err error) {
|
||
|
ctx, span := tracing.NewSpan(ctx)
|
||
|
defer func() { span.EndWithError(err) }()
|
||
|
|
||
|
err = q.client.QueryContext(ctx, func(rows *sql.Rows) error {
|
||
|
for rows.Next() {
|
||
|
var (
|
||
|
configData []byte
|
||
|
configType crypto.WebKeyConfigType
|
||
|
)
|
||
|
var details WebKeyDetails
|
||
|
if err = rows.Scan(
|
||
|
&details.KeyID,
|
||
|
&details.CreationDate,
|
||
|
&details.ChangeDate,
|
||
|
&details.Sequence,
|
||
|
&details.State,
|
||
|
&configData,
|
||
|
&configType,
|
||
|
); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
details.Config, err = crypto.UnmarshalWebKeyConfig(configData, configType)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
list = append(list, details)
|
||
|
}
|
||
|
return rows.Err()
|
||
|
},
|
||
|
webKeyListQuery,
|
||
|
authz.GetInstance(ctx).InstanceID(),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return nil, zerrors.ThrowInternal(err, "QUERY-Ohl3A", "Errors.Internal")
|
||
|
}
|
||
|
return list, nil
|
||
|
}
|
||
|
|
||
|
// GetWebKeySet gets a JSON Web Key set from the web_keys projection.
|
||
|
// The set contains all existing public keys for the instance.
|
||
|
// The set is eventual consistent.
|
||
|
func (q *Queries) GetWebKeySet(ctx context.Context) (_ *jose.JSONWebKeySet, err error) {
|
||
|
ctx, span := tracing.NewSpan(ctx)
|
||
|
defer func() { span.EndWithError(err) }()
|
||
|
|
||
|
var keys []jose.JSONWebKey
|
||
|
|
||
|
err = q.client.QueryContext(ctx, func(rows *sql.Rows) error {
|
||
|
for rows.Next() {
|
||
|
var webKeyData []byte
|
||
|
if err = rows.Scan(&webKeyData); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
var webKey jose.JSONWebKey
|
||
|
if err = json.Unmarshal(webKeyData, &webKey); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
keys = append(keys, webKey)
|
||
|
}
|
||
|
return rows.Err()
|
||
|
},
|
||
|
webKeyPublicKeysQuery,
|
||
|
authz.GetInstance(ctx).InstanceID(),
|
||
|
)
|
||
|
if err != nil {
|
||
|
return nil, zerrors.ThrowInternal(err, "QUERY-Eeng7", "Errors.Internal")
|
||
|
}
|
||
|
return &jose.JSONWebKeySet{Keys: keys}, nil
|
||
|
}
|