mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-17 12:07:02 +02: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>
159 lines
4.1 KiB
Go
159 lines
4.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package random
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"unicode/utf8"
|
|
|
|
"github.com/hashicorp/hcl"
|
|
"github.com/mitchellh/mapstructure"
|
|
)
|
|
|
|
// ParsePolicy is a convenience function for parsing HCL into a StringGenerator.
|
|
// See PolicyParser.ParsePolicy for details.
|
|
func ParsePolicy(raw string) (gen StringGenerator, err error) {
|
|
parser := PolicyParser{
|
|
RuleRegistry: Registry{
|
|
Rules: defaultRuleNameMapping,
|
|
},
|
|
}
|
|
return parser.ParsePolicy(raw)
|
|
}
|
|
|
|
// ParsePolicyBytes is a convenience function for parsing HCL into a StringGenerator.
|
|
// See PolicyParser.ParsePolicy for details.
|
|
func ParsePolicyBytes(raw []byte) (gen StringGenerator, err error) {
|
|
return ParsePolicy(string(raw))
|
|
}
|
|
|
|
// PolicyParser parses string generator configuration from HCL.
|
|
type PolicyParser struct {
|
|
// RuleRegistry maps rule names in HCL to Rule constructors.
|
|
RuleRegistry Registry
|
|
}
|
|
|
|
// ParsePolicy parses the provided HCL into a StringGenerator.
|
|
func (p PolicyParser) ParsePolicy(raw string) (sg StringGenerator, err error) {
|
|
rawData := map[string]interface{}{}
|
|
err = hcl.Decode(&rawData, raw)
|
|
if err != nil {
|
|
return sg, fmt.Errorf("unable to decode: %w", err)
|
|
}
|
|
|
|
// Decode the top level items
|
|
gen := StringGenerator{}
|
|
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
Result: &gen,
|
|
DecodeHook: stringToRunesFunc,
|
|
})
|
|
if err != nil {
|
|
return sg, fmt.Errorf("unable to decode configuration: %w", err)
|
|
}
|
|
|
|
err = decoder.Decode(rawData)
|
|
if err != nil {
|
|
return sg, fmt.Errorf("failed to decode configuration: %w", err)
|
|
}
|
|
|
|
// Decode & parse rules
|
|
rawRules, err := getMapSlice(rawData, "rule")
|
|
if err != nil {
|
|
return sg, fmt.Errorf("unable to retrieve rules: %w", err)
|
|
}
|
|
|
|
rules, err := parseRules(p.RuleRegistry, rawRules)
|
|
if err != nil {
|
|
return sg, fmt.Errorf("unable to parse rules: %w", err)
|
|
}
|
|
|
|
gen = StringGenerator{
|
|
Length: gen.Length,
|
|
Rules: rules,
|
|
}
|
|
|
|
err = gen.validateConfig()
|
|
if err != nil {
|
|
return sg, err
|
|
}
|
|
|
|
return gen, nil
|
|
}
|
|
|
|
func parseRules(registry Registry, rawRules []map[string]interface{}) (rules []Rule, err error) {
|
|
for _, rawRule := range rawRules {
|
|
info, err := getRuleInfo(rawRule)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to get rule info: %w", err)
|
|
}
|
|
|
|
rule, err := registry.parseRule(info.ruleType, info.data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to parse rule %s: %w", info.ruleType, err)
|
|
}
|
|
rules = append(rules, rule)
|
|
}
|
|
|
|
return rules, nil
|
|
}
|
|
|
|
// getMapSlice from the provided map. This will retrieve and type-assert a []map[string]interface{} from the map
|
|
// This will not error if the key does not exist
|
|
// This will return an error if the value at the provided key is not of type []map[string]interface{}
|
|
func getMapSlice(m map[string]interface{}, key string) (mapSlice []map[string]interface{}, err error) {
|
|
rawSlice, exists := m[key]
|
|
if !exists {
|
|
return nil, nil
|
|
}
|
|
|
|
mapSlice = []map[string]interface{}{}
|
|
err = mapstructure.Decode(rawSlice, &mapSlice)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return mapSlice, nil
|
|
}
|
|
|
|
type ruleInfo struct {
|
|
ruleType string
|
|
data map[string]interface{}
|
|
}
|
|
|
|
// getRuleInfo splits the provided HCL-decoded rule into its rule type along with the data associated with it
|
|
func getRuleInfo(rule map[string]interface{}) (data ruleInfo, err error) {
|
|
// There should only be one key, but it's a dynamic key yay!
|
|
for key := range rule {
|
|
slice, err := getMapSlice(rule, key)
|
|
if err != nil {
|
|
return data, fmt.Errorf("unable to get rule data: %w", err)
|
|
}
|
|
|
|
if len(slice) == 0 {
|
|
return data, fmt.Errorf("rule info cannot be empty")
|
|
}
|
|
|
|
data = ruleInfo{
|
|
ruleType: key,
|
|
data: slice[0],
|
|
}
|
|
return data, nil
|
|
}
|
|
return data, fmt.Errorf("rule is empty")
|
|
}
|
|
|
|
// stringToRunesFunc converts a string to a []rune for use in the mapstructure library
|
|
func stringToRunesFunc(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) {
|
|
if from != reflect.String || to != reflect.Slice {
|
|
return data, nil
|
|
}
|
|
|
|
raw := data.(string)
|
|
|
|
if !utf8.ValidString(raw) {
|
|
return nil, fmt.Errorf("invalid UTF8 string")
|
|
}
|
|
return []rune(raw), nil
|
|
}
|