Refactors ListCredentials and adds the new ListCredentialsRefresh method to the Repository, supporting pagination of credentials. Also adds the EstimateCount and ListDeletedCredentialIds methods used during pagination.
159 lines
4.6 KiB
Go
159 lines
4.6 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package static
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/hashicorp/boundary/internal/credential"
|
|
"github.com/hashicorp/boundary/internal/credential/static/store"
|
|
"github.com/hashicorp/boundary/internal/db/timestamp"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/libs/crypto"
|
|
"github.com/hashicorp/boundary/internal/oplog"
|
|
"github.com/hashicorp/boundary/internal/types/resource"
|
|
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
|
"github.com/hashicorp/go-kms-wrapping/v2/extras/structwrapping"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
var _ credential.Static = (*JsonCredential)(nil)
|
|
|
|
// A JsonCredential contains the credential with a json secret.
|
|
// It is owned by a credential store.
|
|
type JsonCredential struct {
|
|
*store.JsonCredential
|
|
tableName string `gorm:"-"`
|
|
}
|
|
|
|
// NewJsonCredential creates a new in memory static Credential containing a
|
|
// json secret that is assigned to storeId. Name and description are the only
|
|
// valid options. All other options are ignored.
|
|
func NewJsonCredential(
|
|
ctx context.Context,
|
|
storeId string,
|
|
object credential.JsonObject,
|
|
opt ...Option,
|
|
) (*JsonCredential, error) {
|
|
const op = "static.NewJsonCredential"
|
|
var objectB []byte
|
|
var err error
|
|
|
|
// Since the secret is an unordered map of dynamically typed values, the hmac value will not be consistent.
|
|
// In order to calculate a consistent hmac value, the input must be deterministic,
|
|
// which is done by marshalling the secret.
|
|
if len(object.AsMap()) > 0 {
|
|
objectB, err = json.Marshal(object.AsMap())
|
|
if err != nil {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "invalid secret")
|
|
}
|
|
}
|
|
|
|
opts := getOpts(opt...)
|
|
jsonCred := &JsonCredential{
|
|
JsonCredential: &store.JsonCredential{
|
|
StoreId: storeId,
|
|
Name: opts.withName,
|
|
Description: opts.withDescription,
|
|
Object: objectB,
|
|
},
|
|
}
|
|
|
|
return jsonCred, nil
|
|
}
|
|
|
|
func allocJsonCredential() *JsonCredential {
|
|
return &JsonCredential{
|
|
JsonCredential: &store.JsonCredential{},
|
|
}
|
|
}
|
|
|
|
func (c *JsonCredential) clone() *JsonCredential {
|
|
cp := proto.Clone(c.JsonCredential)
|
|
return &JsonCredential{
|
|
JsonCredential: cp.(*store.JsonCredential),
|
|
}
|
|
}
|
|
|
|
// TableName returns the table name.
|
|
func (c *JsonCredential) TableName() string {
|
|
if c.tableName != "" {
|
|
return c.tableName
|
|
}
|
|
return "credential_static_json_credential"
|
|
}
|
|
|
|
// SetTableName sets the table name.
|
|
func (c *JsonCredential) SetTableName(n string) {
|
|
c.tableName = n
|
|
}
|
|
|
|
// GetResourceType returns the resource type of the Credential
|
|
func (c *JsonCredential) GetResourceType() resource.Type {
|
|
return resource.Credential
|
|
}
|
|
|
|
func (c *JsonCredential) oplog(op oplog.OpType) oplog.Metadata {
|
|
metadata := oplog.Metadata{
|
|
"resource-public-id": []string{c.PublicId},
|
|
"resource-type": []string{"credential-static-json"},
|
|
"op-type": []string{op.String()},
|
|
}
|
|
if c.StoreId != "" {
|
|
metadata["store-id"] = []string{c.StoreId}
|
|
}
|
|
return metadata
|
|
}
|
|
|
|
func (c *JsonCredential) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
|
const op = "static.(JsonCredential).encrypt"
|
|
if len(c.Object) == 0 {
|
|
return errors.New(ctx, errors.InvalidParameter, op, "no object defined")
|
|
}
|
|
if err := structwrapping.WrapStruct(ctx, cipher, c.JsonCredential, nil); err != nil {
|
|
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt))
|
|
}
|
|
keyId, err := cipher.KeyId(ctx)
|
|
if err != nil {
|
|
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("error reading cipher key id"))
|
|
}
|
|
c.KeyId = keyId
|
|
if err := c.hmacObject(ctx, cipher); err != nil {
|
|
return errors.Wrap(ctx, err, op)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *JsonCredential) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
|
const op = "static.(JsonCredential).decrypt"
|
|
if err := structwrapping.UnwrapStruct(ctx, cipher, c.JsonCredential, nil); err != nil {
|
|
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *JsonCredential) hmacObject(ctx context.Context, cipher wrapping.Wrapper) error {
|
|
const op = "static.(JsonCredential).hmacObject"
|
|
if cipher == nil {
|
|
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
|
|
}
|
|
hm, err := crypto.HmacSha256(ctx, c.Object, cipher, []byte(c.StoreId), nil)
|
|
if err != nil {
|
|
return errors.Wrap(ctx, err, op)
|
|
}
|
|
c.ObjectHmac = []byte(hm)
|
|
return nil
|
|
}
|
|
|
|
type deletedJSONCredential struct {
|
|
PublicId string `gorm:"primary_key"`
|
|
DeleteTime *timestamp.Timestamp
|
|
}
|
|
|
|
// TableName returns the tablename to override the default gorm table name
|
|
func (s *deletedJSONCredential) TableName() string {
|
|
return "credential_static_json_credential_deleted"
|
|
}
|