mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-03 20:17:59 +00:00 
			
		
		
		
	* Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License. Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUS-1.1 * Fix test that expected exact offset on hcl file --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Co-authored-by: Sarah Thompson <sthompson@hashicorp.com> Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
		
			
				
	
	
		
			194 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) HashiCorp, Inc.
 | 
						|
// SPDX-License-Identifier: BUSL-1.1
 | 
						|
 | 
						|
package configutil
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/hashicorp/go-multierror"
 | 
						|
	"github.com/hashicorp/go-secure-stdlib/parseutil"
 | 
						|
	"github.com/hashicorp/hcl"
 | 
						|
	"github.com/hashicorp/hcl/hcl/ast"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	UserLockoutThresholdDefault    = 5
 | 
						|
	UserLockoutDurationDefault     = 15 * time.Minute
 | 
						|
	UserLockoutCounterResetDefault = 15 * time.Minute
 | 
						|
	DisableUserLockoutDefault      = false
 | 
						|
)
 | 
						|
 | 
						|
type UserLockout struct {
 | 
						|
	Type                   string
 | 
						|
	LockoutThreshold       uint64        `hcl:"-"`
 | 
						|
	LockoutThresholdRaw    interface{}   `hcl:"lockout_threshold"`
 | 
						|
	LockoutDuration        time.Duration `hcl:"-"`
 | 
						|
	LockoutDurationRaw     interface{}   `hcl:"lockout_duration"`
 | 
						|
	LockoutCounterReset    time.Duration `hcl:"-"`
 | 
						|
	LockoutCounterResetRaw interface{}   `hcl:"lockout_counter_reset"`
 | 
						|
	DisableLockout         bool          `hcl:"-"`
 | 
						|
	DisableLockoutRaw      interface{}   `hcl:"disable_lockout"`
 | 
						|
}
 | 
						|
 | 
						|
func GetSupportedUserLockoutsAuthMethods() []string {
 | 
						|
	return []string{"userpass", "approle", "ldap"}
 | 
						|
}
 | 
						|
 | 
						|
func ParseUserLockouts(result *SharedConfig, list *ast.ObjectList) error {
 | 
						|
	var err error
 | 
						|
	result.UserLockouts = make([]*UserLockout, 0, len(list.Items))
 | 
						|
	userLockoutsMap := make(map[string]*UserLockout)
 | 
						|
	for i, item := range list.Items {
 | 
						|
		var userLockoutConfig UserLockout
 | 
						|
		if err := hcl.DecodeObject(&userLockoutConfig, item.Val); err != nil {
 | 
						|
			return multierror.Prefix(err, fmt.Sprintf("userLockouts.%d:", i))
 | 
						|
		}
 | 
						|
 | 
						|
		// Base values
 | 
						|
		{
 | 
						|
			switch {
 | 
						|
			case userLockoutConfig.Type != "":
 | 
						|
			case len(item.Keys) == 1:
 | 
						|
				userLockoutConfig.Type = strings.ToLower(item.Keys[0].Token.Value().(string))
 | 
						|
			default:
 | 
						|
				return multierror.Prefix(errors.New("auth type for user lockout must be specified, if it applies to all auth methods specify \"all\" "), fmt.Sprintf("user_lockouts.%d:", i))
 | 
						|
			}
 | 
						|
 | 
						|
			userLockoutConfig.Type = strings.ToLower(userLockoutConfig.Type)
 | 
						|
			// Supported auth methods for user lockout configuration: ldap, approle, userpass
 | 
						|
			// "all" is used to apply the configuration to all supported auth methods
 | 
						|
			switch userLockoutConfig.Type {
 | 
						|
			case "all", "ldap", "approle", "userpass":
 | 
						|
				result.found(userLockoutConfig.Type, userLockoutConfig.Type)
 | 
						|
			default:
 | 
						|
				return multierror.Prefix(fmt.Errorf("unsupported auth type %q", userLockoutConfig.Type), fmt.Sprintf("user_lockouts.%d:", i))
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Lockout Parameters
 | 
						|
 | 
						|
		// Not setting raw entries to nil here as soon as they are parsed
 | 
						|
		// as they are used to set the missing user lockout configuration values later.
 | 
						|
		{
 | 
						|
			if userLockoutConfig.LockoutThresholdRaw != nil {
 | 
						|
				userLockoutThresholdString := fmt.Sprintf("%v", userLockoutConfig.LockoutThresholdRaw)
 | 
						|
				if userLockoutConfig.LockoutThreshold, err = strconv.ParseUint(userLockoutThresholdString, 10, 64); err != nil {
 | 
						|
					return multierror.Prefix(fmt.Errorf("error parsing lockout_threshold: %w", err), fmt.Sprintf("user_lockouts.%d", i))
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if userLockoutConfig.LockoutDurationRaw != nil {
 | 
						|
				if userLockoutConfig.LockoutDuration, err = parseutil.ParseDurationSecond(userLockoutConfig.LockoutDurationRaw); err != nil {
 | 
						|
					return multierror.Prefix(fmt.Errorf("error parsing lockout_duration: %w", err), fmt.Sprintf("user_lockouts.%d", i))
 | 
						|
				}
 | 
						|
				if userLockoutConfig.LockoutDuration < 0 {
 | 
						|
					return multierror.Prefix(errors.New("lockout_duration cannot be negative"), fmt.Sprintf("user_lockouts.%d", i))
 | 
						|
				}
 | 
						|
 | 
						|
			}
 | 
						|
 | 
						|
			if userLockoutConfig.LockoutCounterResetRaw != nil {
 | 
						|
				if userLockoutConfig.LockoutCounterReset, err = parseutil.ParseDurationSecond(userLockoutConfig.LockoutCounterResetRaw); err != nil {
 | 
						|
					return multierror.Prefix(fmt.Errorf("error parsing lockout_counter_reset: %w", err), fmt.Sprintf("user_lockouts.%d", i))
 | 
						|
				}
 | 
						|
				if userLockoutConfig.LockoutCounterReset < 0 {
 | 
						|
					return multierror.Prefix(errors.New("lockout_counter_reset cannot be negative"), fmt.Sprintf("user_lockouts.%d", i))
 | 
						|
				}
 | 
						|
 | 
						|
			}
 | 
						|
			if userLockoutConfig.DisableLockoutRaw != nil {
 | 
						|
				if userLockoutConfig.DisableLockout, err = parseutil.ParseBool(userLockoutConfig.DisableLockoutRaw); err != nil {
 | 
						|
					return multierror.Prefix(fmt.Errorf("invalid value for disable_lockout: %w", err), fmt.Sprintf("user_lockouts.%d", i))
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		userLockoutsMap[userLockoutConfig.Type] = &userLockoutConfig
 | 
						|
	}
 | 
						|
 | 
						|
	// Use raw entries to set values for user lockout configurations fields
 | 
						|
	// that were not configured using config file.
 | 
						|
	// The raw entries would mean that the entry was configured by the user using the config file.
 | 
						|
	// If any of these fields are not configured using the config file (missing fields),
 | 
						|
	// we set values for these fields with defaults
 | 
						|
	// The issue with not being able to use non-raw entries is because of fields lockout threshold
 | 
						|
	// and disable lockout. We cannot differentiate using non-raw entries if the user configured these fields
 | 
						|
	// with values (0 and false) or if the the user did not configure these values in config file at all.
 | 
						|
	// The raw fields are set to nil after setting missing values in setNilValuesForRawUserLockoutFields function
 | 
						|
	userLockoutsMap = setMissingUserLockoutValuesInMap(userLockoutsMap)
 | 
						|
	for _, userLockoutValues := range userLockoutsMap {
 | 
						|
		result.UserLockouts = append(result.UserLockouts, userLockoutValues)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// setUserLockoutValueAllInMap sets default user lockout values for key "all" (all auth methods)
 | 
						|
// for user lockout fields that are not configured using config file
 | 
						|
func setUserLockoutValueAllInMap(userLockoutAll *UserLockout) *UserLockout {
 | 
						|
	if userLockoutAll.Type == "" {
 | 
						|
		userLockoutAll.Type = "all"
 | 
						|
	}
 | 
						|
	if userLockoutAll.LockoutThresholdRaw == nil {
 | 
						|
		userLockoutAll.LockoutThreshold = UserLockoutThresholdDefault
 | 
						|
	}
 | 
						|
	if userLockoutAll.LockoutDurationRaw == nil {
 | 
						|
		userLockoutAll.LockoutDuration = UserLockoutDurationDefault
 | 
						|
	}
 | 
						|
	if userLockoutAll.LockoutCounterResetRaw == nil {
 | 
						|
		userLockoutAll.LockoutCounterReset = UserLockoutCounterResetDefault
 | 
						|
	}
 | 
						|
	if userLockoutAll.DisableLockoutRaw == nil {
 | 
						|
		userLockoutAll.DisableLockout = DisableUserLockoutDefault
 | 
						|
	}
 | 
						|
	return setNilValuesForRawUserLockoutFields(userLockoutAll)
 | 
						|
}
 | 
						|
 | 
						|
// setDefaultUserLockoutValuesInMap sets missing user lockout fields for auth methods
 | 
						|
// with default values (from key "all") that are not configured using config file
 | 
						|
func setMissingUserLockoutValuesInMap(userLockoutsMap map[string]*UserLockout) map[string]*UserLockout {
 | 
						|
	// set values for "all" key with default values for "all" user lockout fields that are not configured
 | 
						|
	// the "all" key values will be used as default values for other auth methods
 | 
						|
	userLockoutAll, ok := userLockoutsMap["all"]
 | 
						|
	switch ok {
 | 
						|
	case true:
 | 
						|
		userLockoutsMap["all"] = setUserLockoutValueAllInMap(userLockoutAll)
 | 
						|
	default:
 | 
						|
		userLockoutsMap["all"] = setUserLockoutValueAllInMap(&UserLockout{})
 | 
						|
	}
 | 
						|
 | 
						|
	for _, userLockoutAuth := range userLockoutsMap {
 | 
						|
		if userLockoutAuth.Type == "all" {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		// set missing values
 | 
						|
		if userLockoutAuth.LockoutThresholdRaw == nil {
 | 
						|
			userLockoutAuth.LockoutThreshold = userLockoutsMap["all"].LockoutThreshold
 | 
						|
		}
 | 
						|
		if userLockoutAuth.LockoutDurationRaw == nil {
 | 
						|
			userLockoutAuth.LockoutDuration = userLockoutsMap["all"].LockoutDuration
 | 
						|
		}
 | 
						|
		if userLockoutAuth.LockoutCounterResetRaw == nil {
 | 
						|
			userLockoutAuth.LockoutCounterReset = userLockoutsMap["all"].LockoutCounterReset
 | 
						|
		}
 | 
						|
		if userLockoutAuth.DisableLockoutRaw == nil {
 | 
						|
			userLockoutAuth.DisableLockout = userLockoutsMap["all"].DisableLockout
 | 
						|
		}
 | 
						|
		userLockoutAuth = setNilValuesForRawUserLockoutFields(userLockoutAuth)
 | 
						|
		userLockoutsMap[userLockoutAuth.Type] = userLockoutAuth
 | 
						|
	}
 | 
						|
	return userLockoutsMap
 | 
						|
}
 | 
						|
 | 
						|
// setNilValuesForRawUserLockoutFields sets nil values for user lockout Raw fields
 | 
						|
func setNilValuesForRawUserLockoutFields(userLockout *UserLockout) *UserLockout {
 | 
						|
	userLockout.LockoutThresholdRaw = nil
 | 
						|
	userLockout.LockoutDurationRaw = nil
 | 
						|
	userLockout.LockoutCounterResetRaw = nil
 | 
						|
	userLockout.DisableLockoutRaw = nil
 | 
						|
	return userLockout
 | 
						|
}
 |