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.
144 lines
4.6 KiB
Go
144 lines
4.6 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package static
|
|
|
|
import (
|
|
"context"
|
|
|
|
"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 = (*UsernamePasswordCredential)(nil)
|
|
|
|
// A UsernamePasswordCredential contains the credential with a username and password.
|
|
// It is owned by a credential store.
|
|
type UsernamePasswordCredential struct {
|
|
*store.UsernamePasswordCredential
|
|
tableName string `gorm:"-"`
|
|
}
|
|
|
|
// NewUsernamePasswordCredential creates a new in memory static Credential containing a
|
|
// username and password that is assigned to storeId. Name and description are the only
|
|
// valid options. All other options are ignored.
|
|
func NewUsernamePasswordCredential(
|
|
storeId string,
|
|
username string,
|
|
password credential.Password,
|
|
opt ...Option,
|
|
) (*UsernamePasswordCredential, error) {
|
|
opts := getOpts(opt...)
|
|
l := &UsernamePasswordCredential{
|
|
UsernamePasswordCredential: &store.UsernamePasswordCredential{
|
|
StoreId: storeId,
|
|
Name: opts.withName,
|
|
Description: opts.withDescription,
|
|
Username: username,
|
|
Password: []byte(password),
|
|
},
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
func allocUsernamePasswordCredential() *UsernamePasswordCredential {
|
|
return &UsernamePasswordCredential{
|
|
UsernamePasswordCredential: &store.UsernamePasswordCredential{},
|
|
}
|
|
}
|
|
|
|
func (c *UsernamePasswordCredential) clone() *UsernamePasswordCredential {
|
|
cp := proto.Clone(c.UsernamePasswordCredential)
|
|
return &UsernamePasswordCredential{
|
|
UsernamePasswordCredential: cp.(*store.UsernamePasswordCredential),
|
|
}
|
|
}
|
|
|
|
// TableName returns the table name.
|
|
func (c *UsernamePasswordCredential) TableName() string {
|
|
if c.tableName != "" {
|
|
return c.tableName
|
|
}
|
|
return "credential_static_username_password_credential"
|
|
}
|
|
|
|
// SetTableName sets the table name.
|
|
func (c *UsernamePasswordCredential) SetTableName(n string) {
|
|
c.tableName = n
|
|
}
|
|
|
|
// GetResourceType returns the resource type of the Credential
|
|
func (c *UsernamePasswordCredential) GetResourceType() resource.Type {
|
|
return resource.Credential
|
|
}
|
|
|
|
func (c *UsernamePasswordCredential) oplog(op oplog.OpType) oplog.Metadata {
|
|
metadata := oplog.Metadata{
|
|
"resource-public-id": []string{c.PublicId},
|
|
"resource-type": []string{"credential-static-username-password"},
|
|
"op-type": []string{op.String()},
|
|
}
|
|
if c.StoreId != "" {
|
|
metadata["store-id"] = []string{c.StoreId}
|
|
}
|
|
return metadata
|
|
}
|
|
|
|
func (c *UsernamePasswordCredential) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
|
const op = "static.(UsernamePasswordCredential).encrypt"
|
|
if len(c.Password) == 0 {
|
|
return errors.New(ctx, errors.InvalidParameter, op, "no password defined")
|
|
}
|
|
if err := structwrapping.WrapStruct(ctx, cipher, c.UsernamePasswordCredential, 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.hmacPassword(ctx, cipher); err != nil {
|
|
return errors.Wrap(ctx, err, op)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *UsernamePasswordCredential) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
|
const op = "static.(UsernamePasswordCredential).decrypt"
|
|
if err := structwrapping.UnwrapStruct(ctx, cipher, c.UsernamePasswordCredential, nil); err != nil {
|
|
return errors.Wrap(ctx, err, op, errors.WithCode(errors.Decrypt))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *UsernamePasswordCredential) hmacPassword(ctx context.Context, cipher wrapping.Wrapper) error {
|
|
const op = "static.(UsernamePasswordCredential).hmacPassword"
|
|
if cipher == nil {
|
|
return errors.New(ctx, errors.InvalidParameter, op, "missing cipher")
|
|
}
|
|
hm, err := crypto.HmacSha256(ctx, c.Password, cipher, []byte(c.StoreId), nil, crypto.WithEd25519())
|
|
if err != nil {
|
|
return errors.Wrap(ctx, err, op)
|
|
}
|
|
c.PasswordHmac = []byte(hm)
|
|
return nil
|
|
}
|
|
|
|
type deletedUsernamePasswordCredential struct {
|
|
PublicId string `gorm:"primary_key"`
|
|
DeleteTime *timestamp.Timestamp
|
|
}
|
|
|
|
// TableName returns the tablename to override the default gorm table name
|
|
func (s *deletedUsernamePasswordCredential) TableName() string {
|
|
return "credential_static_username_password_credential_deleted"
|
|
}
|