225 lines
8.1 KiB
Go
225 lines
8.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package vault
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/boundary/globals"
|
|
"github.com/hashicorp/boundary/internal/db"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/kms"
|
|
"github.com/hashicorp/boundary/internal/oplog"
|
|
"github.com/hashicorp/go-dbw"
|
|
)
|
|
|
|
// CreateLdapCredentialLibrary creates a new Vault LDAP credential library. It
|
|
// inserts the input domain object into the repository and returns the inserted
|
|
// domain object. No options are supported.
|
|
//
|
|
// The input domain object is not mutated, must contain a valid credential store
|
|
// id and must not contain a public id.
|
|
func (r *Repository) CreateLdapCredentialLibrary(ctx context.Context, projectId string, l *LdapCredentialLibrary, _ ...Option) (*LdapCredentialLibrary, error) {
|
|
const op = "vault.(Repository).CreateLdapCredentialLibrary"
|
|
if projectId == "" {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "no project id")
|
|
}
|
|
if l == nil || l.LdapCredentialLibrary == nil {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "nil input domain object")
|
|
}
|
|
if l.PublicId != "" {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
|
|
}
|
|
if l.StoreId == "" {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "no store id")
|
|
}
|
|
if l.VaultPath == "" {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "no vault path")
|
|
}
|
|
l = l.clone()
|
|
|
|
if l.GetCredentialType() == "" {
|
|
l.LdapCredentialLibrary.CredentialType = string(globals.UsernamePasswordDomainCredentialType)
|
|
}
|
|
if l.GetCredentialType() != string(globals.UsernamePasswordDomainCredentialType) {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "invalid credential type")
|
|
}
|
|
|
|
id, err := newLdapCredentialLibraryId(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op)
|
|
}
|
|
l.setId(id)
|
|
|
|
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
|
|
}
|
|
|
|
var insertedLdapCredLib *LdapCredentialLibrary
|
|
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
|
func(_ db.Reader, w db.Writer) error {
|
|
insertedLdapCredLib = l.clone()
|
|
err = w.Create(ctx, insertedLdapCredLib,
|
|
db.WithOplog(oplogWrapper, insertedLdapCredLib.oplog(oplog.OpType_OP_TYPE_CREATE)),
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(ctx, err, op)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
)
|
|
if err != nil {
|
|
if errors.IsUniqueError(err) {
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("in credential store: %s: name %s already exists", l.StoreId, l.Name))
|
|
}
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("credential store: %s", l.StoreId))
|
|
}
|
|
|
|
return insertedLdapCredLib, nil
|
|
}
|
|
|
|
// UpdateLdapCredentialLibrary updates an existing Vault LDAP credential library
|
|
// in the repository. It returns a domain object containing the updated state
|
|
// and the number of updated records. No options are supported.
|
|
//
|
|
// The input domain object is not mutated and must contain a valid public id.
|
|
func (r *Repository) UpdateLdapCredentialLibrary(ctx context.Context, projectId string, l *LdapCredentialLibrary, version uint32, fieldMaskPaths []string, _ ...Option) (*LdapCredentialLibrary, int, error) {
|
|
const op = "vault.(Repository).UpdateLdapCredentialLibrary"
|
|
if projectId == "" {
|
|
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no project id")
|
|
}
|
|
if version == 0 {
|
|
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
|
|
}
|
|
if l == nil || l.LdapCredentialLibrary == nil {
|
|
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "nil domain object")
|
|
}
|
|
if l.PublicId == "" {
|
|
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
|
|
}
|
|
|
|
for _, f := range fieldMaskPaths {
|
|
switch {
|
|
case strings.EqualFold(nameField, f):
|
|
case strings.EqualFold(descriptionField, f):
|
|
case strings.EqualFold(vaultPathField, f):
|
|
default:
|
|
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("%q field mask path is unsupported", f))
|
|
}
|
|
}
|
|
|
|
dbMask, nullFields := dbw.BuildUpdatePaths(
|
|
map[string]any{
|
|
nameField: l.Name,
|
|
descriptionField: l.Description,
|
|
vaultPathField: l.VaultPath,
|
|
}, fieldMaskPaths, nil,
|
|
)
|
|
if len(dbMask) == 0 && len(nullFields) == 0 {
|
|
return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask")
|
|
}
|
|
|
|
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
|
if err != nil {
|
|
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
|
|
}
|
|
|
|
var updatedCredLib *LdapCredentialLibrary
|
|
var rowsUpdated int
|
|
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
|
func(r db.Reader, w db.Writer) error {
|
|
updated, err := w.Update(ctx, l.clone(), dbMask, nullFields,
|
|
db.WithOplog(oplogWrapper, l.oplog(oplog.OpType_OP_TYPE_UPDATE)),
|
|
db.WithVersion(&version),
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to update credential library"))
|
|
}
|
|
if updated > 1 {
|
|
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 credential library would have been updated")
|
|
}
|
|
rowsUpdated = updated
|
|
|
|
updatedCredLib = allocLdapCredentialLibrary()
|
|
updatedCredLib.PublicId = l.GetPublicId()
|
|
err = r.LookupByPublicId(ctx, updatedCredLib)
|
|
if err != nil {
|
|
return errors.Wrap(ctx, err, op, errors.WithMsg("unable to retrieve updated credential library"))
|
|
}
|
|
return nil
|
|
},
|
|
)
|
|
if err != nil {
|
|
if errors.IsUniqueError(err) {
|
|
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("name %s already exists: %s", l.Name, l.PublicId))
|
|
}
|
|
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("failed to update credential library %s", l.PublicId))
|
|
}
|
|
|
|
return updatedCredLib, rowsUpdated, nil
|
|
}
|
|
|
|
// LookupLdapCredentialLibrary looks up an existing Vault LDAP credential
|
|
// library in the repository. It returns the domain object for the given public
|
|
// id, if it exists. Returns nil, nil if not found. No options are supported.
|
|
func (r *Repository) LookupLdapCredentialLibrary(ctx context.Context, publicId string, _ ...Option) (*LdapCredentialLibrary, error) {
|
|
const op = "vault.(Repository).LookupLdapCredentialLibrary"
|
|
if publicId == "" {
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
|
}
|
|
|
|
l := allocLdapCredentialLibrary()
|
|
l.PublicId = publicId
|
|
err := r.reader.LookupByPublicId(ctx, l)
|
|
if err != nil {
|
|
if errors.IsNotFoundError(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("lookup failed for id %s", publicId))
|
|
}
|
|
|
|
return l, nil
|
|
}
|
|
|
|
// DeleteLdapCredentialLibrary deletes the Vault LDAP credential library with
|
|
// the given public id from the repository. Returns the number of records
|
|
// deleted. No options are supported.
|
|
func (r *Repository) DeleteLdapCredentialLibrary(ctx context.Context, projectId string, publicId string, _ ...Option) (int, error) {
|
|
const op = "vault.(Repository).DeleteLdapCredentialLibrary"
|
|
if publicId == "" {
|
|
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no public id")
|
|
}
|
|
if projectId == "" {
|
|
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "no project id")
|
|
}
|
|
|
|
l := allocLdapCredentialLibrary()
|
|
l.PublicId = publicId
|
|
|
|
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
|
if err != nil {
|
|
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
|
|
}
|
|
|
|
var rowsDeleted int
|
|
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
|
func(_ db.Reader, w db.Writer) (err error) {
|
|
rowsDeleted, err = w.Delete(ctx, l, db.WithOplog(oplogWrapper, l.oplog(oplog.OpType_OP_TYPE_DELETE)))
|
|
if err == nil && rowsDeleted > 1 {
|
|
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 credential library would have been deleted")
|
|
}
|
|
return err
|
|
},
|
|
)
|
|
if err != nil {
|
|
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("delete failed for id %s", l.PublicId))
|
|
}
|
|
|
|
return rowsDeleted, nil
|
|
}
|