update ad secrets plugin for check-out feature (#7617)
This commit is contained in:
parent
dbdf65e5bc
commit
e8432f1ebe
2
go.mod
2
go.mod
|
@ -75,7 +75,7 @@ require (
|
|||
github.com/hashicorp/vault-plugin-auth-kubernetes v0.5.2-0.20190826163451-8461c66275a9
|
||||
github.com/hashicorp/vault-plugin-auth-oci v0.0.0-20190904175623-97c0c0187c5c
|
||||
github.com/hashicorp/vault-plugin-database-elasticsearch v0.0.0-20190814210117-e079e01fbb93
|
||||
github.com/hashicorp/vault-plugin-secrets-ad v0.5.3-0.20190814210122-0f2fd536b250
|
||||
github.com/hashicorp/vault-plugin-secrets-ad v0.6.0
|
||||
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.2-0.20190814210129-4d18bec92f56
|
||||
github.com/hashicorp/vault-plugin-secrets-azure v0.5.2-0.20190814210135-54b8afbc42ae
|
||||
github.com/hashicorp/vault-plugin-secrets-gcp v0.5.3-0.20190814210141-d2086ff79b04
|
||||
|
|
2
go.sum
2
go.sum
|
@ -362,6 +362,8 @@ github.com/hashicorp/vault-plugin-database-elasticsearch v0.0.0-20190814210117-e
|
|||
github.com/hashicorp/vault-plugin-database-elasticsearch v0.0.0-20190814210117-e079e01fbb93/go.mod h1:N9XpfMXjeLHBgUd8iy4avOC4mCSqUC7B/R8AtCYhcfE=
|
||||
github.com/hashicorp/vault-plugin-secrets-ad v0.5.3-0.20190814210122-0f2fd536b250 h1:+mm2cM5msg/USImbvnMS2yzCMBYMCO3CrvsATWGtHtY=
|
||||
github.com/hashicorp/vault-plugin-secrets-ad v0.5.3-0.20190814210122-0f2fd536b250/go.mod h1:F8hKHqcB7stN2OhnqE3emwFYtKO0IDNxMBbPs2n8vr0=
|
||||
github.com/hashicorp/vault-plugin-secrets-ad v0.6.0 h1:N0AtdV3w6VCtU7rZiTbPxsxhluJXrzpYH9B1pLZhG6g=
|
||||
github.com/hashicorp/vault-plugin-secrets-ad v0.6.0/go.mod h1:qm2QDW9KNY+pFoxBEYGYvcHnVjdiOr3tXeO9DMeo3mI=
|
||||
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.2-0.20190814210129-4d18bec92f56 h1:PGE26//x1eiAbZ1ExffhKa4y9xgDKLd9BHDZRkOzbEY=
|
||||
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.2-0.20190814210129-4d18bec92f56/go.mod h1:hJ42zFd3bHyE8O2liBUG+VPY0JxdMrj51TOwVGViUIU=
|
||||
github.com/hashicorp/vault-plugin-secrets-azure v0.5.2-0.20190814210135-54b8afbc42ae h1:LtRJy7H/9ftjHGo5SMLG8/7DI7CYL1Zur9jBJTyzXg8=
|
||||
|
|
|
@ -8,13 +8,16 @@ import (
|
|||
"github.com/hashicorp/vault-plugin-secrets-ad/plugin/client"
|
||||
"github.com/hashicorp/vault-plugin-secrets-ad/plugin/util"
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/locksutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
|
||||
backend := newBackend(util.NewSecretsClient(conf.Logger))
|
||||
backend.Setup(ctx, conf)
|
||||
if err := backend.Setup(ctx, conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return backend, nil
|
||||
}
|
||||
|
||||
|
@ -24,6 +27,10 @@ func newBackend(client secretsClient) *backend {
|
|||
roleCache: cache.New(roleCacheExpiration, roleCacheCleanup),
|
||||
credCache: cache.New(credCacheExpiration, credCacheCleanup),
|
||||
rotateRootLock: new(int32),
|
||||
checkOutHandler: &checkOutHandler{
|
||||
client: client,
|
||||
},
|
||||
checkOutLocks: locksutil.CreateLocks(),
|
||||
}
|
||||
adBackend.Backend = &framework.Backend{
|
||||
Help: backendHelp,
|
||||
|
@ -33,6 +40,14 @@ func newBackend(client secretsClient) *backend {
|
|||
adBackend.pathListRoles(),
|
||||
adBackend.pathCreds(),
|
||||
adBackend.pathRotateCredentials(),
|
||||
|
||||
// The following paths are for AD credential checkout.
|
||||
adBackend.pathSetCheckIn(),
|
||||
adBackend.pathSetManageCheckIn(),
|
||||
adBackend.pathSetCheckOut(),
|
||||
adBackend.pathSetStatus(),
|
||||
adBackend.pathSets(),
|
||||
adBackend.pathListSets(),
|
||||
},
|
||||
PathsSpecial: &logical.Paths{
|
||||
SealWrapStorage: []string{
|
||||
|
@ -42,12 +57,15 @@ func newBackend(client secretsClient) *backend {
|
|||
},
|
||||
Invalidate: adBackend.Invalidate,
|
||||
BackendType: logical.TypeLogical,
|
||||
Secrets: []*framework.Secret{
|
||||
adBackend.secretAccessKeys(),
|
||||
},
|
||||
}
|
||||
return adBackend
|
||||
}
|
||||
|
||||
type backend struct {
|
||||
logical.Backend
|
||||
*framework.Backend
|
||||
|
||||
client secretsClient
|
||||
|
||||
|
@ -55,6 +73,11 @@ type backend struct {
|
|||
credCache *cache.Cache
|
||||
credLock sync.Mutex
|
||||
rotateRootLock *int32
|
||||
|
||||
checkOutHandler *checkOutHandler
|
||||
// checkOutLocks are used for avoiding races
|
||||
// when working with sets through the check-out system.
|
||||
checkOutLocks []*locksutil.LockEntry
|
||||
}
|
||||
|
||||
func (b *backend) Invalidate(ctx context.Context, key string) {
|
||||
|
|
192
vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/checkout_handler.go
generated
vendored
Normal file
192
vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/checkout_handler.go
generated
vendored
Normal file
|
@ -0,0 +1,192 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/hashicorp/vault-plugin-secrets-ad/plugin/util"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
const (
|
||||
checkoutStoragePrefix = "checkout/"
|
||||
passwordStoragePrefix = "password/"
|
||||
)
|
||||
|
||||
var (
|
||||
// errCheckedOut is returned when a check-out request is received
|
||||
// for a service account that's already checked out.
|
||||
errCheckedOut = errors.New("checked out")
|
||||
|
||||
// errNotFound is used when a requested item doesn't exist.
|
||||
errNotFound = errors.New("not found")
|
||||
)
|
||||
|
||||
// CheckOut provides information for a service account that is currently
|
||||
// checked out.
|
||||
type CheckOut struct {
|
||||
IsAvailable bool `json:"is_available"`
|
||||
BorrowerEntityID string `json:"borrower_entity_id"`
|
||||
BorrowerClientToken string `json:"borrower_client_token"`
|
||||
}
|
||||
|
||||
// checkOutHandler manages checkouts. It's not thread-safe and expects the caller to handle locking because
|
||||
// locking may span multiple calls.
|
||||
type checkOutHandler struct {
|
||||
client secretsClient
|
||||
}
|
||||
|
||||
// CheckOut attempts to check out a service account. If the account is unavailable, it returns
|
||||
// errCheckedOut. If the service account isn't managed by this plugin, it returns
|
||||
// errNotFound.
|
||||
func (h *checkOutHandler) CheckOut(ctx context.Context, storage logical.Storage, serviceAccountName string, checkOut *CheckOut) error {
|
||||
if ctx == nil {
|
||||
return errors.New("ctx must be provided")
|
||||
}
|
||||
if storage == nil {
|
||||
return errors.New("storage must be provided")
|
||||
}
|
||||
if serviceAccountName == "" {
|
||||
return errors.New("service account name must be provided")
|
||||
}
|
||||
if checkOut == nil {
|
||||
return errors.New("check-out must be provided")
|
||||
}
|
||||
|
||||
// Check if the service account is currently checked out.
|
||||
currentEntry, err := storage.Get(ctx, checkoutStoragePrefix+serviceAccountName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currentEntry == nil {
|
||||
return errNotFound
|
||||
}
|
||||
currentCheckOut := &CheckOut{}
|
||||
if err := currentEntry.DecodeJSON(currentCheckOut); err != nil {
|
||||
return err
|
||||
}
|
||||
if !currentCheckOut.IsAvailable {
|
||||
return errCheckedOut
|
||||
}
|
||||
|
||||
// Since it's not, store the new check-out.
|
||||
entry, err := logical.StorageEntryJSON(checkoutStoragePrefix+serviceAccountName, checkOut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storage.Put(ctx, entry)
|
||||
}
|
||||
|
||||
// CheckIn attempts to check in a service account. If an error occurs, the account remains checked out
|
||||
// and can either be retried by the caller, or eventually may be checked in if it has a ttl
|
||||
// that ends.
|
||||
func (h *checkOutHandler) CheckIn(ctx context.Context, storage logical.Storage, serviceAccountName string) error {
|
||||
if ctx == nil {
|
||||
return errors.New("ctx must be provided")
|
||||
}
|
||||
if storage == nil {
|
||||
return errors.New("storage must be provided")
|
||||
}
|
||||
if serviceAccountName == "" {
|
||||
return errors.New("service account name must be provided")
|
||||
}
|
||||
|
||||
// On check-ins, a new AD password is generated, updated in AD, and stored.
|
||||
engineConf, err := readConfig(ctx, storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if engineConf == nil {
|
||||
return errors.New("the config is currently unset")
|
||||
}
|
||||
newPassword, err := util.GeneratePassword(engineConf.PasswordConf.Formatter, engineConf.PasswordConf.Length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.client.UpdatePassword(engineConf.ADConf, serviceAccountName, newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
pwdEntry, err := logical.StorageEntryJSON(passwordStoragePrefix+serviceAccountName, newPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := storage.Put(ctx, pwdEntry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// That ends the password-handling leg of our journey, now let's deal with the stored check-out itself.
|
||||
// Store a check-out status indicating it's available.
|
||||
checkOut := &CheckOut{
|
||||
IsAvailable: true,
|
||||
}
|
||||
entry, err := logical.StorageEntryJSON(checkoutStoragePrefix+serviceAccountName, checkOut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storage.Put(ctx, entry)
|
||||
}
|
||||
|
||||
// LoadCheckOut returns either:
|
||||
// - A *CheckOut and nil error if the serviceAccountName is currently managed by this engine.
|
||||
// - A nil *Checkout and errNotFound if the serviceAccountName is not currently managed by this engine.
|
||||
func (h *checkOutHandler) LoadCheckOut(ctx context.Context, storage logical.Storage, serviceAccountName string) (*CheckOut, error) {
|
||||
if ctx == nil {
|
||||
return nil, errors.New("ctx must be provided")
|
||||
}
|
||||
if storage == nil {
|
||||
return nil, errors.New("storage must be provided")
|
||||
}
|
||||
if serviceAccountName == "" {
|
||||
return nil, errors.New("service account name must be provided")
|
||||
}
|
||||
|
||||
entry, err := storage.Get(ctx, checkoutStoragePrefix+serviceAccountName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry == nil {
|
||||
return nil, errNotFound
|
||||
}
|
||||
checkOut := &CheckOut{}
|
||||
if err := entry.DecodeJSON(checkOut); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return checkOut, nil
|
||||
}
|
||||
|
||||
// Delete cleans up anything we were tracking from the service account that we will no longer need.
|
||||
func (h *checkOutHandler) Delete(ctx context.Context, storage logical.Storage, serviceAccountName string) error {
|
||||
if ctx == nil {
|
||||
return errors.New("ctx must be provided")
|
||||
}
|
||||
if storage == nil {
|
||||
return errors.New("storage must be provided")
|
||||
}
|
||||
if serviceAccountName == "" {
|
||||
return errors.New("service account name must be provided")
|
||||
}
|
||||
|
||||
if err := storage.Delete(ctx, passwordStoragePrefix+serviceAccountName); err != nil {
|
||||
return err
|
||||
}
|
||||
return storage.Delete(ctx, checkoutStoragePrefix+serviceAccountName)
|
||||
}
|
||||
|
||||
// retrievePassword is a utility function for grabbing a service account's password from storage.
|
||||
// retrievePassword will return:
|
||||
// - "password", nil if it was successfully able to retrieve the password.
|
||||
// - errNotFound if there's no password presently.
|
||||
// - Some other err if it was unable to complete successfully.
|
||||
func retrievePassword(ctx context.Context, storage logical.Storage, serviceAccountName string) (string, error) {
|
||||
entry, err := storage.Get(ctx, passwordStoragePrefix+serviceAccountName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if entry == nil {
|
||||
return "", errNotFound
|
||||
}
|
||||
password := ""
|
||||
if err := entry.DecodeJSON(&password); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return password, nil
|
||||
}
|
376
vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_checkout_sets.go
generated
vendored
Normal file
376
vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_checkout_sets.go
generated
vendored
Normal file
|
@ -0,0 +1,376 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/locksutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
const libraryPrefix = "library/"
|
||||
|
||||
type librarySet struct {
|
||||
ServiceAccountNames []string `json:"service_account_names"`
|
||||
TTL time.Duration `json:"ttl"`
|
||||
MaxTTL time.Duration `json:"max_ttl"`
|
||||
DisableCheckInEnforcement bool `json:"disable_check_in_enforcement"`
|
||||
}
|
||||
|
||||
// Validates ensures that a set meets our code assumptions that TTLs are set in
|
||||
// a way that makes sense, and that there's at least one service account.
|
||||
func (l *librarySet) Validate() error {
|
||||
if len(l.ServiceAccountNames) < 1 {
|
||||
return fmt.Errorf(`at least one service account must be configured`)
|
||||
}
|
||||
if l.MaxTTL > 0 {
|
||||
if l.MaxTTL < l.TTL {
|
||||
return fmt.Errorf(`max_ttl (%d seconds) may not be less than ttl (%d seconds)`, l.MaxTTL, l.TTL)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *backend) pathListSets() *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: libraryPrefix + "?$",
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ListOperation: &framework.PathOperation{
|
||||
Callback: b.setListOperation,
|
||||
},
|
||||
},
|
||||
HelpSynopsis: pathListSetsHelpSyn,
|
||||
HelpDescription: pathListSetsHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) setListOperation(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
|
||||
keys, err := req.Storage.List(ctx, libraryPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return logical.ListResponse(keys), nil
|
||||
}
|
||||
|
||||
func (b *backend) pathSets() *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: libraryPrefix + framework.GenericNameRegex("name"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": {
|
||||
Type: framework.TypeLowerCaseString,
|
||||
Description: "Name of the set.",
|
||||
Required: true,
|
||||
},
|
||||
"service_account_names": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: "The username/logon name for the service accounts with which this set will be associated.",
|
||||
},
|
||||
"ttl": {
|
||||
Type: framework.TypeDurationSecond,
|
||||
Description: "In seconds, the amount of time a check-out should last. Defaults to 24 hours.",
|
||||
Default: 24 * 60 * 60, // 24 hours
|
||||
},
|
||||
"max_ttl": {
|
||||
Type: framework.TypeDurationSecond,
|
||||
Description: "In seconds, the max amount of time a check-out's renewals should last. Defaults to 24 hours.",
|
||||
Default: 24 * 60 * 60, // 24 hours
|
||||
},
|
||||
"disable_check_in_enforcement": {
|
||||
Type: framework.TypeBool,
|
||||
Description: "Disable the default behavior of requiring that check-ins are performed by the entity that checked them out.",
|
||||
Default: false,
|
||||
},
|
||||
},
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.CreateOperation: &framework.PathOperation{
|
||||
Callback: b.operationSetCreate,
|
||||
Summary: "Create a library set.",
|
||||
},
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.operationSetUpdate,
|
||||
Summary: "Update a library set.",
|
||||
},
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.operationSetRead,
|
||||
Summary: "Read a library set.",
|
||||
},
|
||||
logical.DeleteOperation: &framework.PathOperation{
|
||||
Callback: b.operationSetDelete,
|
||||
Summary: "Delete a library set.",
|
||||
},
|
||||
},
|
||||
ExistenceCheck: b.operationSetExistenceCheck,
|
||||
HelpSynopsis: setHelpSynopsis,
|
||||
HelpDescription: setHelpDescription,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) operationSetExistenceCheck(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (bool, error) {
|
||||
set, err := readSet(ctx, req.Storage, fieldData.Get("name").(string))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return set != nil, nil
|
||||
}
|
||||
|
||||
func (b *backend) operationSetCreate(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := fieldData.Get("name").(string)
|
||||
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
serviceAccountNames := fieldData.Get("service_account_names").([]string)
|
||||
ttl := time.Duration(fieldData.Get("ttl").(int)) * time.Second
|
||||
maxTTL := time.Duration(fieldData.Get("max_ttl").(int)) * time.Second
|
||||
disableCheckInEnforcement := fieldData.Get("disable_check_in_enforcement").(bool)
|
||||
|
||||
if len(serviceAccountNames) == 0 {
|
||||
return logical.ErrorResponse(`"service_account_names" must be provided`), nil
|
||||
}
|
||||
|
||||
// Ensure these service accounts aren't already managed by another check-out set.
|
||||
for _, serviceAccountName := range serviceAccountNames {
|
||||
if _, err := b.checkOutHandler.LoadCheckOut(ctx, req.Storage, serviceAccountName); err != nil {
|
||||
if err == errNotFound {
|
||||
// This is what we want to see.
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return logical.ErrorResponse(fmt.Sprintf("%q is already managed by another set", serviceAccountName)), nil
|
||||
}
|
||||
|
||||
set := &librarySet{
|
||||
ServiceAccountNames: serviceAccountNames,
|
||||
TTL: ttl,
|
||||
MaxTTL: maxTTL,
|
||||
DisableCheckInEnforcement: disableCheckInEnforcement,
|
||||
}
|
||||
if err := set.Validate(); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
for _, serviceAccountName := range serviceAccountNames {
|
||||
if err := b.checkOutHandler.CheckIn(ctx, req.Storage, serviceAccountName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := storeSet(ctx, req.Storage, setName, set); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *backend) operationSetUpdate(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := fieldData.Get("name").(string)
|
||||
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
newServiceAccountNamesRaw, newServiceAccountNamesSent := fieldData.GetOk("service_account_names")
|
||||
var newServiceAccountNames []string
|
||||
if newServiceAccountNamesSent {
|
||||
newServiceAccountNames = newServiceAccountNamesRaw.([]string)
|
||||
}
|
||||
|
||||
ttlRaw, ttlSent := fieldData.GetOk("ttl")
|
||||
if !ttlSent {
|
||||
ttlRaw = fieldData.Schema["ttl"].Default
|
||||
}
|
||||
ttl := time.Duration(ttlRaw.(int)) * time.Second
|
||||
|
||||
maxTTLRaw, maxTTLSent := fieldData.GetOk("max_ttl")
|
||||
if !maxTTLSent {
|
||||
maxTTLRaw = fieldData.Schema["max_ttl"].Default
|
||||
}
|
||||
maxTTL := time.Duration(maxTTLRaw.(int)) * time.Second
|
||||
|
||||
disableCheckInEnforcementRaw, enforcementSent := fieldData.GetOk("disable_check_in_enforcement")
|
||||
if !enforcementSent {
|
||||
disableCheckInEnforcementRaw = false
|
||||
}
|
||||
disableCheckInEnforcement := disableCheckInEnforcementRaw.(bool)
|
||||
|
||||
set, err := readSet(ctx, req.Storage, setName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(`%q doesn't exist`, setName)), nil
|
||||
}
|
||||
|
||||
var beingAdded []string
|
||||
var beingDeleted []string
|
||||
if newServiceAccountNamesSent {
|
||||
|
||||
// For new service accounts we receive, before we check them in, ensure they're not in another set.
|
||||
beingAdded = strutil.Difference(newServiceAccountNames, set.ServiceAccountNames, true)
|
||||
for _, newServiceAccountName := range beingAdded {
|
||||
if _, err := b.checkOutHandler.LoadCheckOut(ctx, req.Storage, newServiceAccountName); err != nil {
|
||||
if err == errNotFound {
|
||||
// Great, this validates that it's not in use in another set.
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return logical.ErrorResponse(fmt.Sprintf("%q is already managed by another set", newServiceAccountName)), nil
|
||||
}
|
||||
|
||||
// For service accounts we won't be handling anymore, before we delete them, ensure they're not checked out.
|
||||
beingDeleted = strutil.Difference(set.ServiceAccountNames, newServiceAccountNames, true)
|
||||
for _, prevServiceAccountName := range beingDeleted {
|
||||
checkOut, err := b.checkOutHandler.LoadCheckOut(ctx, req.Storage, prevServiceAccountName)
|
||||
if err != nil {
|
||||
if err == errNotFound {
|
||||
// Nothing else to do here.
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if !checkOut.IsAvailable {
|
||||
return logical.ErrorResponse(fmt.Sprintf(`"%s" can't be deleted because it is currently checked out'`, prevServiceAccountName)), nil
|
||||
}
|
||||
}
|
||||
set.ServiceAccountNames = newServiceAccountNames
|
||||
}
|
||||
|
||||
if ttlSent {
|
||||
set.TTL = ttl
|
||||
}
|
||||
if maxTTLSent {
|
||||
set.MaxTTL = maxTTL
|
||||
}
|
||||
if enforcementSent {
|
||||
set.DisableCheckInEnforcement = disableCheckInEnforcement
|
||||
}
|
||||
if err := set.Validate(); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
||||
// Now that we know we can take all these actions, let's take them.
|
||||
for _, newServiceAccountName := range beingAdded {
|
||||
if err := b.checkOutHandler.CheckIn(ctx, req.Storage, newServiceAccountName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, prevServiceAccountName := range beingDeleted {
|
||||
if err := b.checkOutHandler.Delete(ctx, req.Storage, prevServiceAccountName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := storeSet(ctx, req.Storage, setName, set); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *backend) operationSetRead(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := fieldData.Get("name").(string)
|
||||
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.RLock()
|
||||
defer lock.RUnlock()
|
||||
|
||||
set, err := readSet(ctx, req.Storage, setName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"service_account_names": set.ServiceAccountNames,
|
||||
"ttl": int64(set.TTL.Seconds()),
|
||||
"max_ttl": int64(set.MaxTTL.Seconds()),
|
||||
"disable_check_in_enforcement": set.DisableCheckInEnforcement,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *backend) operationSetDelete(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := fieldData.Get("name").(string)
|
||||
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
set, err := readSet(ctx, req.Storage, setName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set == nil {
|
||||
return nil, nil
|
||||
}
|
||||
// We need to remove all the items we'd stored for these service accounts.
|
||||
for _, serviceAccountName := range set.ServiceAccountNames {
|
||||
checkOut, err := b.checkOutHandler.LoadCheckOut(ctx, req.Storage, serviceAccountName)
|
||||
if err != nil {
|
||||
if err == errNotFound {
|
||||
// Nothing else to do here.
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if !checkOut.IsAvailable {
|
||||
return logical.ErrorResponse(fmt.Sprintf(`"%s" can't be deleted because it is currently checked out'`, serviceAccountName)), nil
|
||||
}
|
||||
}
|
||||
for _, serviceAccountName := range set.ServiceAccountNames {
|
||||
if err := b.checkOutHandler.Delete(ctx, req.Storage, serviceAccountName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := req.Storage.Delete(ctx, libraryPrefix+setName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// readSet is a helper method for reading a set from storage by name.
|
||||
// It's intended to be used anywhere in the plugin. It may return nil, nil if
|
||||
// a librarySet doesn't currently exist for a given setName.
|
||||
func readSet(ctx context.Context, storage logical.Storage, setName string) (*librarySet, error) {
|
||||
entry, err := storage.Get(ctx, libraryPrefix+setName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry == nil {
|
||||
return nil, nil
|
||||
}
|
||||
set := &librarySet{}
|
||||
if err := entry.DecodeJSON(set); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
|
||||
// storeSet stores a librarySet.
|
||||
func storeSet(ctx context.Context, storage logical.Storage, setName string, set *librarySet) error {
|
||||
entry, err := logical.StorageEntryJSON(libraryPrefix+setName, set)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storage.Put(ctx, entry)
|
||||
}
|
||||
|
||||
const (
|
||||
setHelpSynopsis = `
|
||||
Manage sets to build a library of service accounts that can be checked out.
|
||||
`
|
||||
setHelpDescription = `
|
||||
This endpoint allows you to read, write, and delete individual sets that are used for checking out service accounts.
|
||||
Deleting a set can only be performed if all of its service accounts are currently checked in.
|
||||
`
|
||||
pathListSetsHelpSyn = `
|
||||
List the name of each set currently stored.
|
||||
`
|
||||
pathListSetsHelpDesc = `
|
||||
To learn which service accounts are being managed by Vault, list the set names using
|
||||
this endpoint. Then read any individual set by name to learn more.
|
||||
`
|
||||
)
|
377
vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_checkouts.go
generated
vendored
Normal file
377
vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_checkouts.go
generated
vendored
Normal file
|
@ -0,0 +1,377 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
metrics "github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/locksutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
const secretAccessKeyType = "creds"
|
||||
|
||||
func (b *backend) pathSetCheckOut() *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: libraryPrefix + framework.GenericNameRegex("name") + "/check-out$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": {
|
||||
Type: framework.TypeLowerCaseString,
|
||||
Description: "Name of the set",
|
||||
Required: true,
|
||||
},
|
||||
"ttl": {
|
||||
Type: framework.TypeDurationSecond,
|
||||
Description: "The length of time before the check-out will expire, in seconds.",
|
||||
},
|
||||
},
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.operationSetCheckOut,
|
||||
Summary: "Check a service account out from the library.",
|
||||
},
|
||||
},
|
||||
HelpSynopsis: `Check a service account out from the library.`,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) operationSetCheckOut(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := fieldData.Get("name").(string)
|
||||
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
ttlPeriodRaw, ttlPeriodSent := fieldData.GetOk("ttl")
|
||||
if !ttlPeriodSent {
|
||||
ttlPeriodRaw = 0
|
||||
}
|
||||
requestedTTL := time.Duration(ttlPeriodRaw.(int)) * time.Second
|
||||
|
||||
set, err := readSet(ctx, req.Storage, setName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(`%q doesn't exist`, setName)), nil
|
||||
}
|
||||
|
||||
// Prepare the check-out we'd like to execute.
|
||||
ttl := set.TTL
|
||||
if ttlPeriodSent {
|
||||
switch {
|
||||
case set.TTL <= 0 && requestedTTL > 0:
|
||||
// The set's TTL is infinite and the caller requested a finite TTL.
|
||||
ttl = requestedTTL
|
||||
case set.TTL > 0 && requestedTTL < set.TTL:
|
||||
// The set's TTL isn't infinite and the caller requested a shorter TTL.
|
||||
ttl = requestedTTL
|
||||
}
|
||||
}
|
||||
newCheckOut := &CheckOut{
|
||||
IsAvailable: false,
|
||||
BorrowerEntityID: req.EntityID,
|
||||
BorrowerClientToken: req.ClientToken,
|
||||
}
|
||||
|
||||
// Check out the first service account available.
|
||||
for _, serviceAccountName := range set.ServiceAccountNames {
|
||||
if err := b.checkOutHandler.CheckOut(ctx, req.Storage, serviceAccountName, newCheckOut); err != nil {
|
||||
if err == errCheckedOut {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
password, err := retrievePassword(ctx, req.Storage, serviceAccountName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
respData := map[string]interface{}{
|
||||
"service_account_name": serviceAccountName,
|
||||
"password": password,
|
||||
}
|
||||
internalData := map[string]interface{}{
|
||||
"service_account_name": serviceAccountName,
|
||||
"set_name": setName,
|
||||
}
|
||||
resp := b.Backend.Secret(secretAccessKeyType).Response(respData, internalData)
|
||||
resp.Secret.Renewable = true
|
||||
resp.Secret.TTL = ttl
|
||||
resp.Secret.MaxTTL = set.MaxTTL
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// If we arrived here, it's because we never had a hit for a service account that was available.
|
||||
// In case of customer issues, we need to make this easy to see and diagnose.
|
||||
b.Logger().Debug(fmt.Sprintf(`%q had no check-outs available`, setName))
|
||||
metrics.IncrCounter([]string{"active directory", "check-out", "unavailable", setName}, 1)
|
||||
|
||||
return logical.RespondWithStatusCode(&logical.Response{
|
||||
Warnings: []string{"No service accounts available for check-out."},
|
||||
}, req, 400)
|
||||
}
|
||||
|
||||
func (b *backend) secretAccessKeys() *framework.Secret {
|
||||
return &framework.Secret{
|
||||
Type: secretAccessKeyType,
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"service_account_name": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Service account name",
|
||||
},
|
||||
"password": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Password",
|
||||
},
|
||||
},
|
||||
Renew: b.renewCheckOut,
|
||||
Revoke: b.endCheckOut,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) renewCheckOut(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := req.Secret.InternalData["set_name"].(string)
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.RLock()
|
||||
defer lock.RUnlock()
|
||||
|
||||
set, err := readSet(ctx, req.Storage, setName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(`%q doesn't exist`, setName)), nil
|
||||
}
|
||||
|
||||
serviceAccountName := req.Secret.InternalData["service_account_name"].(string)
|
||||
checkOut, err := b.checkOutHandler.LoadCheckOut(ctx, req.Storage, serviceAccountName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if checkOut.IsAvailable {
|
||||
// It's possible that this renewal could be attempted after a check-in occurred either by this entity or by
|
||||
// another user with access to the "manage check-ins" endpoint that forcibly checked it back in.
|
||||
return logical.ErrorResponse(fmt.Sprintf("%s is already checked in, please call check-out to regain it", serviceAccountName)), nil
|
||||
}
|
||||
resp := &logical.Response{Secret: req.Secret}
|
||||
resp.Secret.TTL = set.TTL
|
||||
resp.Secret.MaxTTL = set.MaxTTL
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *backend) endCheckOut(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := req.Secret.InternalData["set_name"].(string)
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
serviceAccountName := req.Secret.InternalData["service_account_name"].(string)
|
||||
if err := b.checkOutHandler.CheckIn(ctx, req.Storage, serviceAccountName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathSetCheckIn() *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: libraryPrefix + framework.GenericNameRegex("name") + "/check-in$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": {
|
||||
Type: framework.TypeLowerCaseString,
|
||||
Description: "Name of the set.",
|
||||
Required: true,
|
||||
},
|
||||
"service_account_names": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: "The username/logon name for the service accounts to check in.",
|
||||
},
|
||||
},
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.operationCheckIn(false),
|
||||
Summary: "Check service accounts in to the library.",
|
||||
},
|
||||
},
|
||||
HelpSynopsis: `Check service accounts in to the library.`,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) pathSetManageCheckIn() *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: libraryPrefix + "manage/" + framework.GenericNameRegex("name") + "/check-in$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": {
|
||||
Type: framework.TypeLowerCaseString,
|
||||
Description: "Name of the set.",
|
||||
Required: true,
|
||||
},
|
||||
"service_account_names": {
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: "The username/logon name for the service accounts to check in.",
|
||||
},
|
||||
},
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.operationCheckIn(true),
|
||||
Summary: "Check service accounts in to the library.",
|
||||
},
|
||||
},
|
||||
HelpSynopsis: `Force checking service accounts in to the library.`,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) operationCheckIn(overrideCheckInEnforcement bool) framework.OperationFunc {
|
||||
return func(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := fieldData.Get("name").(string)
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
serviceAccountNamesRaw, serviceAccountNamesSent := fieldData.GetOk("service_account_names")
|
||||
var serviceAccountNames []string
|
||||
if serviceAccountNamesSent {
|
||||
serviceAccountNames = serviceAccountNamesRaw.([]string)
|
||||
}
|
||||
|
||||
set, err := readSet(ctx, req.Storage, setName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(`%q doesn't exist`, setName)), nil
|
||||
}
|
||||
|
||||
// If check-in enforcement is overridden or disabled at the set level, we should consider it disabled.
|
||||
disableCheckInEnforcement := overrideCheckInEnforcement || set.DisableCheckInEnforcement
|
||||
|
||||
// Track the service accounts we check in so we can include it in our response.
|
||||
toCheckIn := make([]string, 0)
|
||||
|
||||
// Build and validate a list of service account names that we will be checking in.
|
||||
if len(serviceAccountNames) == 0 {
|
||||
// It's okay if the caller doesn't tell us which service accounts they
|
||||
// want to check in as long as they only have one checked out.
|
||||
// We'll assume that's the one they want to check in.
|
||||
for _, setServiceAccount := range set.ServiceAccountNames {
|
||||
checkOut, err := b.checkOutHandler.LoadCheckOut(ctx, req.Storage, setServiceAccount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if checkOut.IsAvailable {
|
||||
continue
|
||||
}
|
||||
if !disableCheckInEnforcement && !checkinAuthorized(req, checkOut) {
|
||||
continue
|
||||
}
|
||||
toCheckIn = append(toCheckIn, setServiceAccount)
|
||||
}
|
||||
if len(toCheckIn) > 1 {
|
||||
return logical.ErrorResponse(`when multiple service accounts are checked out, the "service_account_names" to check in must be provided`), nil
|
||||
}
|
||||
} else {
|
||||
for _, serviceAccountName := range serviceAccountNames {
|
||||
checkOut, err := b.checkOutHandler.LoadCheckOut(ctx, req.Storage, serviceAccountName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// First guard that they should be able to do anything at all.
|
||||
if !checkOut.IsAvailable && !disableCheckInEnforcement && !checkinAuthorized(req, checkOut) {
|
||||
return logical.ErrorResponse("%q can't be checked in because it wasn't checked out by the caller", serviceAccountName), nil
|
||||
}
|
||||
if checkOut.IsAvailable {
|
||||
continue
|
||||
}
|
||||
toCheckIn = append(toCheckIn, serviceAccountName)
|
||||
}
|
||||
}
|
||||
for _, serviceAccountName := range toCheckIn {
|
||||
if err := b.checkOutHandler.CheckIn(ctx, req.Storage, serviceAccountName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"check_ins": toCheckIn,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) pathSetStatus() *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: libraryPrefix + framework.GenericNameRegex("name") + "/status$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": {
|
||||
Type: framework.TypeLowerCaseString,
|
||||
Description: "Name of the set.",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.operationSetStatus,
|
||||
Summary: "Check the status of the service accounts in a library set.",
|
||||
},
|
||||
},
|
||||
HelpSynopsis: `Check the status of the service accounts in a library.`,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) operationSetStatus(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
setName := fieldData.Get("name").(string)
|
||||
lock := locksutil.LockForKey(b.checkOutLocks, setName)
|
||||
lock.RLock()
|
||||
defer lock.RUnlock()
|
||||
|
||||
set, err := readSet(ctx, req.Storage, setName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(`%q doesn't exist`, setName)), nil
|
||||
}
|
||||
respData := make(map[string]interface{})
|
||||
|
||||
for _, serviceAccountName := range set.ServiceAccountNames {
|
||||
checkOut, err := b.checkOutHandler.LoadCheckOut(ctx, req.Storage, serviceAccountName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
status := map[string]interface{}{
|
||||
"available": checkOut.IsAvailable,
|
||||
}
|
||||
if checkOut.IsAvailable {
|
||||
// We only omit all other fields if the checkout is currently available,
|
||||
// because they're only relevant to accounts that aren't checked out.
|
||||
respData[serviceAccountName] = status
|
||||
continue
|
||||
}
|
||||
if checkOut.BorrowerClientToken != "" {
|
||||
status["borrower_client_token"] = checkOut.BorrowerClientToken
|
||||
}
|
||||
if checkOut.BorrowerEntityID != "" {
|
||||
status["borrower_entity_id"] = checkOut.BorrowerEntityID
|
||||
}
|
||||
respData[serviceAccountName] = status
|
||||
}
|
||||
return &logical.Response{
|
||||
Data: respData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkinAuthorized(req *logical.Request, checkOut *CheckOut) bool {
|
||||
if checkOut.BorrowerEntityID != "" && req.EntityID != "" {
|
||||
if checkOut.BorrowerEntityID == req.EntityID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if checkOut.BorrowerClientToken != "" && req.ClientToken != "" {
|
||||
if checkOut.BorrowerClientToken == req.ClientToken {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -24,7 +24,7 @@ const (
|
|||
defaultTLSVersion = "tls12"
|
||||
)
|
||||
|
||||
func (b *backend) readConfig(ctx context.Context, storage logical.Storage) (*configuration, error) {
|
||||
func readConfig(ctx context.Context, storage logical.Storage) (*configuration, error) {
|
||||
entry, err := storage.Get(ctx, configStorageKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -145,7 +145,7 @@ func (b *backend) configUpdateOperation(ctx context.Context, req *logical.Reques
|
|||
}
|
||||
|
||||
func (b *backend) configReadOperation(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
|
||||
config, err := b.readConfig(ctx, req.Storage)
|
||||
config, err := readConfig(ctx, req.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func (b *backend) pathCreds() *framework.Path {
|
|||
func (b *backend) credReadOperation(ctx context.Context, req *logical.Request, fieldData *framework.FieldData) (*logical.Response, error) {
|
||||
cred := make(map[string]interface{})
|
||||
|
||||
engineConf, err := b.readConfig(ctx, req.Storage)
|
||||
engineConf, err := readConfig(ctx, req.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ func (b *backend) readRole(ctx context.Context, storage logical.Storage, roleNam
|
|||
}
|
||||
|
||||
// Always check when ActiveDirectory shows the password as last set on the fly.
|
||||
engineConf, err := b.readConfig(ctx, storage)
|
||||
engineConf, err := readConfig(ctx, storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ func (b *backend) roleUpdateOperation(ctx context.Context, req *logical.Request,
|
|||
// Get everything we need to construct the role.
|
||||
roleName := fieldData.Get("name").(string)
|
||||
|
||||
engineConf, err := b.readConfig(ctx, req.Storage)
|
||||
engineConf, err := readConfig(ctx, req.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
2
vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_rotate_root_creds.go
generated
vendored
2
vendor/github.com/hashicorp/vault-plugin-secrets-ad/plugin/path_rotate_root_creds.go
generated
vendored
|
@ -25,7 +25,7 @@ func (b *backend) pathRotateCredentials() *framework.Path {
|
|||
}
|
||||
|
||||
func (b *backend) pathRotateCredentialsUpdate(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) {
|
||||
engineConf, err := b.readConfig(ctx, req.Storage)
|
||||
engineConf, err := readConfig(ctx, req.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -361,7 +361,7 @@ github.com/hashicorp/vault-plugin-auth-kubernetes
|
|||
github.com/hashicorp/vault-plugin-auth-oci
|
||||
# github.com/hashicorp/vault-plugin-database-elasticsearch v0.0.0-20190814210117-e079e01fbb93
|
||||
github.com/hashicorp/vault-plugin-database-elasticsearch
|
||||
# github.com/hashicorp/vault-plugin-secrets-ad v0.5.3-0.20190814210122-0f2fd536b250
|
||||
# github.com/hashicorp/vault-plugin-secrets-ad v0.6.0
|
||||
github.com/hashicorp/vault-plugin-secrets-ad/plugin
|
||||
github.com/hashicorp/vault-plugin-secrets-ad/plugin/client
|
||||
github.com/hashicorp/vault-plugin-secrets-ad/plugin/util
|
||||
|
|
Loading…
Reference in New Issue