Add user configurable password policies available to secret engines (#8637)
* Add random string generator with rules engine This adds a random string generation library that validates random strings against a set of rules. The library is designed for use as generating passwords, but can be used to generate any random strings.
This commit is contained in:
parent
d8c52a4b44
commit
b52950f884
|
@ -12,8 +12,8 @@ require (
|
|||
github.com/hashicorp/go-retryablehttp v0.6.2
|
||||
github.com/hashicorp/go-rootcerts v1.0.1
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200514144402-4bfac290c352
|
||||
github.com/mitchellh/mapstructure v1.2.2
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
|
||||
gopkg.in/square/go-jose.v2 v2.3.1
|
||||
|
|
|
@ -75,8 +75,8 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
|||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
|
@ -95,6 +95,7 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo
|
|||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
|
|
2
go.mod
2
go.mod
|
@ -109,7 +109,7 @@ require (
|
|||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/go-testing-interface v1.0.0
|
||||
github.com/mitchellh/gox v1.0.1
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
github.com/mitchellh/mapstructure v1.2.2
|
||||
github.com/mitchellh/reflectwalk v1.0.1
|
||||
github.com/natefinch/atomic v0.0.0-20150920032501-a62ce929ffcc
|
||||
github.com/ncw/swift v1.0.47
|
||||
|
|
3
go.sum
3
go.sum
|
@ -305,6 +305,7 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18h
|
|||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
|
@ -635,6 +636,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
|||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8 h1:1CO5wil3HuiVLrUQ2ovSTO+6AfNOA5EMkHHVyHE9IwA=
|
||||
github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
|
|
|
@ -24,8 +24,9 @@ require (
|
|||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/mitchellh/copystructure v1.0.0
|
||||
github.com/mitchellh/go-testing-interface v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
github.com/mitchellh/mapstructure v1.2.2
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/ryanuber/go-glob v1.0.0
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be
|
||||
|
|
|
@ -82,8 +82,8 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.
|
|||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
|
@ -92,6 +92,7 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw
|
|||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
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)
|
||||
}
|
||||
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
|
||||
}
|
|
@ -0,0 +1,578 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParsePolicy(t *testing.T) {
|
||||
type testCase struct {
|
||||
rawConfig string
|
||||
expected StringGenerator
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"unrecognized rule": {
|
||||
rawConfig: `
|
||||
length = 20
|
||||
rule "testrule" {
|
||||
string = "teststring"
|
||||
int = 123
|
||||
}`,
|
||||
expected: StringGenerator{},
|
||||
expectErr: true,
|
||||
},
|
||||
|
||||
"charset restrictions": {
|
||||
rawConfig: `
|
||||
length = 20
|
||||
rule "charset" {
|
||||
charset = "abcde"
|
||||
min-chars = 2
|
||||
}`,
|
||||
expected: StringGenerator{
|
||||
Length: 20,
|
||||
charset: []rune("abcde"),
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actual, err := ParsePolicy(test.rawConfig)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, test.expected) {
|
||||
t.Fatalf("Actual: %#v\nExpected:%#v", actual, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_ParsePolicy(t *testing.T) {
|
||||
type testCase struct {
|
||||
registry map[string]ruleConstructor
|
||||
|
||||
rawConfig string
|
||||
expected StringGenerator
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"empty config": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawConfig: "",
|
||||
expected: StringGenerator{},
|
||||
expectErr: true,
|
||||
},
|
||||
"bogus config": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawConfig: "asdf",
|
||||
expected: StringGenerator{},
|
||||
expectErr: true,
|
||||
},
|
||||
"config with only length": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawConfig: `
|
||||
length = 20`,
|
||||
expected: StringGenerator{},
|
||||
expectErr: true,
|
||||
},
|
||||
"config with zero length": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawConfig: `
|
||||
length = 0
|
||||
rule "charset" {
|
||||
charset = "abcde"
|
||||
}`,
|
||||
expected: StringGenerator{},
|
||||
expectErr: true,
|
||||
},
|
||||
"config with negative length": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawConfig: `
|
||||
length = -2
|
||||
rule "charset" {
|
||||
charset = "abcde"
|
||||
}`,
|
||||
expected: StringGenerator{},
|
||||
expectErr: true,
|
||||
},
|
||||
"charset restrictions": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawConfig: `
|
||||
length = 20
|
||||
rule "charset" {
|
||||
charset = "abcde"
|
||||
min-chars = 2
|
||||
}`,
|
||||
expected: StringGenerator{
|
||||
Length: 20,
|
||||
charset: []rune("abcde"),
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test rule": {
|
||||
registry: map[string]ruleConstructor{
|
||||
"testrule": newTestRule,
|
||||
},
|
||||
rawConfig: `
|
||||
length = 20
|
||||
rule "testrule" {
|
||||
string = "teststring"
|
||||
int = 123
|
||||
}`,
|
||||
expected: StringGenerator{
|
||||
Length: 20,
|
||||
charset: deduplicateRunes([]rune("teststring")),
|
||||
Rules: []Rule{
|
||||
testCharsetRule{
|
||||
String: "teststring",
|
||||
Integer: 123,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"test rule and charset restrictions": {
|
||||
registry: map[string]ruleConstructor{
|
||||
"testrule": newTestRule,
|
||||
"charset": ParseCharset,
|
||||
},
|
||||
rawConfig: `
|
||||
length = 20
|
||||
rule "testrule" {
|
||||
string = "teststring"
|
||||
int = 123
|
||||
}
|
||||
rule "charset" {
|
||||
charset = "abcde"
|
||||
min-chars = 2
|
||||
}`,
|
||||
expected: StringGenerator{
|
||||
Length: 20,
|
||||
charset: deduplicateRunes([]rune("abcdeteststring")),
|
||||
Rules: []Rule{
|
||||
testCharsetRule{
|
||||
String: "teststring",
|
||||
Integer: 123,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"unrecognized rule": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawConfig: `
|
||||
length = 20
|
||||
rule "testrule" {
|
||||
string = "teststring"
|
||||
int = 123
|
||||
}`,
|
||||
expected: StringGenerator{},
|
||||
expectErr: true,
|
||||
},
|
||||
|
||||
// /////////////////////////////////////////////////
|
||||
// JSON data
|
||||
"manually JSONified HCL": {
|
||||
registry: map[string]ruleConstructor{
|
||||
"testrule": newTestRule,
|
||||
"charset": ParseCharset,
|
||||
},
|
||||
rawConfig: `
|
||||
{
|
||||
"charset": "abcde",
|
||||
"length": 20,
|
||||
"rule": [
|
||||
{
|
||||
"testrule": [
|
||||
{
|
||||
"string": "teststring",
|
||||
"int": 123
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"charset": [
|
||||
{
|
||||
"charset": "abcde",
|
||||
"min-chars": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
expected: StringGenerator{
|
||||
Length: 20,
|
||||
charset: deduplicateRunes([]rune("abcdeteststring")),
|
||||
Rules: []Rule{
|
||||
testCharsetRule{
|
||||
String: "teststring",
|
||||
Integer: 123,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"JSONified HCL": {
|
||||
registry: map[string]ruleConstructor{
|
||||
"testrule": newTestRule,
|
||||
"charset": ParseCharset,
|
||||
},
|
||||
rawConfig: toJSON(t, StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{
|
||||
testCharsetRule{
|
||||
String: "teststring",
|
||||
Integer: 123,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 2,
|
||||
},
|
||||
},
|
||||
}),
|
||||
expected: StringGenerator{
|
||||
Length: 20,
|
||||
charset: deduplicateRunes([]rune("abcdeteststring")),
|
||||
Rules: []Rule{
|
||||
testCharsetRule{
|
||||
String: "teststring",
|
||||
Integer: 123,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
"JSON unrecognized rule": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawConfig: `
|
||||
{
|
||||
"charset": "abcde",
|
||||
"length": 20,
|
||||
"rule": [
|
||||
{
|
||||
"testrule": [
|
||||
{
|
||||
"string": "teststring",
|
||||
"int": 123
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}`,
|
||||
expected: StringGenerator{},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
parser := PolicyParser{
|
||||
RuleRegistry: Registry{
|
||||
Rules: test.registry,
|
||||
},
|
||||
}
|
||||
|
||||
actual, err := parser.ParsePolicy(test.rawConfig)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, test.expected) {
|
||||
t.Fatalf("Actual: %#v\nExpected:%#v", actual, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRules(t *testing.T) {
|
||||
type testCase struct {
|
||||
registry map[string]ruleConstructor
|
||||
|
||||
rawRules []map[string]interface{}
|
||||
expectedRules []Rule
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"nil rule data": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawRules: nil,
|
||||
expectedRules: nil,
|
||||
expectErr: false,
|
||||
},
|
||||
"empty rule data": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawRules: []map[string]interface{}{},
|
||||
expectedRules: nil,
|
||||
expectErr: false,
|
||||
},
|
||||
"invalid rule data": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawRules: []map[string]interface{}{
|
||||
{
|
||||
"testrule": map[string]interface{}{
|
||||
"string": "teststring",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRules: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
"unrecognized rule data": {
|
||||
registry: defaultRuleNameMapping,
|
||||
rawRules: []map[string]interface{}{
|
||||
{
|
||||
"testrule": []map[string]interface{}{
|
||||
{
|
||||
"string": "teststring",
|
||||
"int": 123,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRules: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
"recognized rule": {
|
||||
registry: map[string]ruleConstructor{
|
||||
"testrule": newTestRule,
|
||||
},
|
||||
rawRules: []map[string]interface{}{
|
||||
{
|
||||
"testrule": []map[string]interface{}{
|
||||
{
|
||||
"string": "teststring",
|
||||
"int": 123,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRules: []Rule{
|
||||
testCharsetRule{
|
||||
String: "teststring",
|
||||
Integer: 123,
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
registry := Registry{
|
||||
Rules: test.registry,
|
||||
}
|
||||
|
||||
actualRules, err := parseRules(registry, test.rawRules)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actualRules, test.expectedRules) {
|
||||
t.Fatalf("Actual: %#v\nExpected:%#v", actualRules, test.expectedRules)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMapSlice(t *testing.T) {
|
||||
type testCase struct {
|
||||
input map[string]interface{}
|
||||
key string
|
||||
expectedSlice []map[string]interface{}
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"nil map": {
|
||||
input: nil,
|
||||
key: "testkey",
|
||||
expectedSlice: nil,
|
||||
expectErr: false,
|
||||
},
|
||||
"empty map": {
|
||||
input: map[string]interface{}{},
|
||||
key: "testkey",
|
||||
expectedSlice: nil,
|
||||
expectErr: false,
|
||||
},
|
||||
"ignored keys": {
|
||||
input: map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "testkey",
|
||||
expectedSlice: nil,
|
||||
expectErr: false,
|
||||
},
|
||||
"key has wrong type": {
|
||||
input: map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "foo",
|
||||
expectedSlice: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
"good data": {
|
||||
input: map[string]interface{}{
|
||||
"foo": []map[string]interface{}{
|
||||
{
|
||||
"sub-foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
key: "foo",
|
||||
expectedSlice: []map[string]interface{}{
|
||||
{
|
||||
"sub-foo": "bar",
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actualSlice, err := getMapSlice(test.input, test.key)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actualSlice, test.expectedSlice) {
|
||||
t.Fatalf("Actual: %#v\nExpected:%#v", actualSlice, test.expectedSlice)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRuleInfo(t *testing.T) {
|
||||
type testCase struct {
|
||||
rule map[string]interface{}
|
||||
expectedInfo ruleInfo
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"nil rule": {
|
||||
rule: nil,
|
||||
expectedInfo: ruleInfo{},
|
||||
expectErr: true,
|
||||
},
|
||||
"empty rule": {
|
||||
rule: map[string]interface{}{},
|
||||
expectedInfo: ruleInfo{},
|
||||
expectErr: true,
|
||||
},
|
||||
"rule with invalid type": {
|
||||
rule: map[string]interface{}{
|
||||
"TestRuleType": "wrong type",
|
||||
},
|
||||
expectedInfo: ruleInfo{},
|
||||
expectErr: true,
|
||||
},
|
||||
"rule with good data": {
|
||||
rule: map[string]interface{}{
|
||||
"TestRuleType": []map[string]interface{}{
|
||||
{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedInfo: ruleInfo{
|
||||
ruleType: "TestRuleType",
|
||||
data: map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actualInfo, err := getRuleInfo(test.rule)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actualInfo, test.expectedInfo) {
|
||||
t.Fatalf("Actual: %#v\nExpected:%#v", actualInfo, test.expectedInfo)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParser_Parse(b *testing.B) {
|
||||
config := `length = 20
|
||||
rule "charset" {
|
||||
charset = "abcde"
|
||||
min-chars = 2
|
||||
}`
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
parser := PolicyParser{
|
||||
RuleRegistry: Registry{
|
||||
Rules: defaultRuleNameMapping,
|
||||
},
|
||||
}
|
||||
_, err := parser.ParsePolicy(config)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to parse: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toJSON(t *testing.T, val interface{}) string {
|
||||
t.Helper()
|
||||
b, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to marshal to JSON: %s", err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ruleConstructor func(map[string]interface{}) (Rule, error)
|
||||
|
||||
var (
|
||||
// defaultRuleNameMapping is the default mapping of HCL rule names to the appropriate rule constructor.
|
||||
// Add to this map when adding a new Rule type to be recognized in HCL.
|
||||
defaultRuleNameMapping = map[string]ruleConstructor{
|
||||
"charset": ParseCharset,
|
||||
}
|
||||
|
||||
defaultRegistry = Registry{
|
||||
Rules: defaultRuleNameMapping,
|
||||
}
|
||||
)
|
||||
|
||||
// Registry of HCL rule names to rule constructors.
|
||||
type Registry struct {
|
||||
// Rules maps names of rules to a constructor for the rule
|
||||
Rules map[string]ruleConstructor
|
||||
}
|
||||
|
||||
func (r Registry) parseRule(ruleType string, ruleData map[string]interface{}) (rule Rule, err error) {
|
||||
constructor, exists := r.Rules[ruleType]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("unrecognized rule type %s", ruleType)
|
||||
}
|
||||
|
||||
rule, err = constructor(ruleData)
|
||||
return rule, err
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type testCharsetRule struct {
|
||||
String string `mapstructure:"string" json:"string"`
|
||||
Integer int `mapstructure:"int" json:"int"`
|
||||
|
||||
// Default to passing
|
||||
fail bool
|
||||
}
|
||||
|
||||
func newTestRule(data map[string]interface{}) (rule Rule, err error) {
|
||||
tr := &testCharsetRule{}
|
||||
err = mapstructure.Decode(data, tr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode test rule")
|
||||
}
|
||||
return *tr, nil
|
||||
}
|
||||
|
||||
func (tr testCharsetRule) Pass([]rune) bool { return !tr.fail }
|
||||
func (tr testCharsetRule) Type() string { return "testrule" }
|
||||
func (tr testCharsetRule) Chars() []rune { return []rune(tr.String) }
|
||||
|
||||
func TestParseRule(t *testing.T) {
|
||||
type testCase struct {
|
||||
rules map[string]ruleConstructor
|
||||
|
||||
ruleType string
|
||||
ruleData map[string]interface{}
|
||||
|
||||
expectedRule Rule
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"missing rule": {
|
||||
rules: map[string]ruleConstructor{},
|
||||
ruleType: "testrule",
|
||||
ruleData: map[string]interface{}{
|
||||
"string": "teststring",
|
||||
"int": 123,
|
||||
},
|
||||
expectedRule: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
"nil data": {
|
||||
rules: map[string]ruleConstructor{
|
||||
"testrule": newTestRule,
|
||||
},
|
||||
ruleType: "testrule",
|
||||
ruleData: nil,
|
||||
expectedRule: testCharsetRule{},
|
||||
expectErr: false,
|
||||
},
|
||||
"good rule": {
|
||||
rules: map[string]ruleConstructor{
|
||||
"testrule": newTestRule,
|
||||
},
|
||||
ruleType: "testrule",
|
||||
ruleData: map[string]interface{}{
|
||||
"string": "teststring",
|
||||
"int": 123,
|
||||
},
|
||||
expectedRule: testCharsetRule{
|
||||
String: "teststring",
|
||||
Integer: 123,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
reg := Registry{
|
||||
Rules: test.rules,
|
||||
}
|
||||
|
||||
actualRule, err := reg.parseRule(test.ruleType, test.ruleData)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actualRule, test.expectedRule) {
|
||||
t.Fatalf("Actual: %#v\nExpected:%#v", actualRule, test.expectedRule)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the mappings in the defaultRuleNameMapping are consistent between the keys
|
||||
// in the map and the Type() calls on the Rule values
|
||||
func TestDefaultRuleNameMapping(t *testing.T) {
|
||||
for expectedType, constructor := range defaultRuleNameMapping {
|
||||
// In this case, we don't care about the error since we're checking the types, not the contents
|
||||
instance, _ := constructor(map[string]interface{}{})
|
||||
actualType := instance.Type()
|
||||
if actualType != expectedType {
|
||||
t.Fatalf("Default registry mismatched types: Actual: %s Expected: %s", actualType, expectedType)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// Rule to assert on string values.
|
||||
type Rule interface {
|
||||
// Pass should return true if the provided value passes any assertions this Rule is making.
|
||||
Pass(value []rune) bool
|
||||
|
||||
// Type returns the name of the rule as associated in the registry
|
||||
Type() string
|
||||
}
|
||||
|
||||
// CharsetRule requires a certain number of characters from the specified charset.
|
||||
type CharsetRule struct {
|
||||
// CharsetRule is the list of rules that candidate strings must contain a minimum number of.
|
||||
Charset runes `mapstructure:"charset" json:"charset"`
|
||||
|
||||
// MinChars indicates the minimum (inclusive) number of characters from the charset that should appear in the string.
|
||||
MinChars int `mapstructure:"min-chars" json:"min-chars"`
|
||||
}
|
||||
|
||||
// ParseCharset from the provided data map. The data map is expected to be parsed from HCL.
|
||||
func ParseCharset(data map[string]interface{}) (rule Rule, err error) {
|
||||
cr := &CharsetRule{}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: cr,
|
||||
DecodeHook: stringToRunesFunc,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode charset restriction: %w", err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse charset restriction: %w", err)
|
||||
}
|
||||
|
||||
return *cr, nil
|
||||
}
|
||||
|
||||
func (c CharsetRule) Type() string {
|
||||
return "charset"
|
||||
}
|
||||
|
||||
// Chars returns the charset that this rule is looking for.
|
||||
func (c CharsetRule) Chars() []rune {
|
||||
return c.Charset
|
||||
}
|
||||
|
||||
func (c CharsetRule) MinLength() int {
|
||||
return c.MinChars
|
||||
}
|
||||
|
||||
// Pass returns true if the provided candidate string has a minimum number of chars in it.
|
||||
// This adheres to the Rule interface
|
||||
func (c CharsetRule) Pass(value []rune) bool {
|
||||
if c.MinChars <= 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
count := 0
|
||||
for _, r := range value {
|
||||
// charIn is sometimes faster than a map lookup because the data is so small
|
||||
// This is being kept rather than converted to a map to keep the code cleaner,
|
||||
// otherwise there would need to be additional parsing logic.
|
||||
if charIn(r, c.Charset) {
|
||||
count++
|
||||
if count >= c.MinChars {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func charIn(search rune, charset []rune) bool {
|
||||
for _, r := range charset {
|
||||
if search == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCharset(t *testing.T) {
|
||||
type testCase struct {
|
||||
charset string
|
||||
minChars int
|
||||
input string
|
||||
expected bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"0 minimum, empty input": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 0,
|
||||
input: "",
|
||||
expected: true,
|
||||
},
|
||||
"0 minimum, many matching": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 0,
|
||||
input: LowercaseCharset,
|
||||
expected: true,
|
||||
},
|
||||
"0 minimum, no matching": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 0,
|
||||
input: "0123456789",
|
||||
expected: true,
|
||||
},
|
||||
"1 minimum, empty input": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 1,
|
||||
input: "",
|
||||
expected: false,
|
||||
},
|
||||
"1 minimum, no matching": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 1,
|
||||
input: "0123456789",
|
||||
expected: false,
|
||||
},
|
||||
"1 minimum, exactly 1 matching": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 1,
|
||||
input: "a",
|
||||
expected: true,
|
||||
},
|
||||
"1 minimum, many matching": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 1,
|
||||
input: "abcdefhaaaa",
|
||||
expected: true,
|
||||
},
|
||||
"2 minimum, 1 matching": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 2,
|
||||
input: "f",
|
||||
expected: false,
|
||||
},
|
||||
"2 minimum, 2 matching": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 2,
|
||||
input: "fz",
|
||||
expected: true,
|
||||
},
|
||||
"2 minimum, many matching": {
|
||||
charset: LowercaseCharset,
|
||||
minChars: 2,
|
||||
input: "joixnbonxd",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
cr := CharsetRule{
|
||||
Charset: []rune(test.charset),
|
||||
MinChars: test.minChars,
|
||||
}
|
||||
actual := cr.Pass([]rune(test.input))
|
||||
if actual != test.expected {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// serializableRules is a slice of rules that can be marshalled to JSON in an HCL format
|
||||
type serializableRules []Rule
|
||||
|
||||
// MarshalJSON in an HCL-friendly way
|
||||
func (r serializableRules) MarshalJSON() (b []byte, err error) {
|
||||
// Example:
|
||||
// [
|
||||
// {
|
||||
// "testrule": [
|
||||
// {
|
||||
// "string": "teststring",
|
||||
// "int": 123
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// "charset": [
|
||||
// {
|
||||
// "charset": "abcde",
|
||||
// "min-chars": 2
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
data := []map[string][]map[string]interface{}{} // Totally not confusing at all
|
||||
for _, rule := range r {
|
||||
ruleData := map[string]interface{}{}
|
||||
err = mapstructure.Decode(rule, &ruleData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode rule: %w", err)
|
||||
}
|
||||
|
||||
ruleMap := map[string][]map[string]interface{}{
|
||||
rule.Type(): []map[string]interface{}{
|
||||
ruleData,
|
||||
},
|
||||
}
|
||||
data = append(data, ruleMap)
|
||||
}
|
||||
|
||||
b, err = json.Marshal(data)
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (r *serializableRules) UnmarshalJSON(data []byte) (err error) {
|
||||
mapData := []map[string]interface{}{}
|
||||
err = json.Unmarshal(data, &mapData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rules, err := parseRules(defaultRegistry, mapData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = rules
|
||||
return nil
|
||||
}
|
||||
|
||||
type runes []rune
|
||||
|
||||
func (r runes) Len() int { return len(r) }
|
||||
func (r runes) Less(i, j int) bool { return r[i] < r[j] }
|
||||
func (r runes) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
|
||||
// MarshalJSON converts the runes to a string for smaller JSON and easier readability
|
||||
func (r runes) MarshalJSON() (b []byte, err error) {
|
||||
return json.Marshal(string(r))
|
||||
}
|
||||
|
||||
// UnmarshalJSON converts a string to []rune
|
||||
func (r *runes) UnmarshalJSON(data []byte) (err error) {
|
||||
var str string
|
||||
err = json.Unmarshal(data, &str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = []rune(str)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestJSONMarshalling(t *testing.T) {
|
||||
expected := serializableRules{
|
||||
CharsetRule{
|
||||
Charset: LowercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: UppercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: NumericRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: ShortSymbolRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
}
|
||||
|
||||
marshalled, err := json.Marshal(expected)
|
||||
if err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
actual := serializableRules{}
|
||||
err = json.Unmarshal(marshalled, &actual)
|
||||
if err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("Actual: %#v\nExpected: %#v", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunes_UnmarshalJSON(t *testing.T) {
|
||||
data := []byte(`"noaw8hgfsdjlkfsj3"`)
|
||||
|
||||
expected := runes([]rune("noaw8hgfsdjlkfsj3"))
|
||||
actual := runes{}
|
||||
err := (&actual).UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("Actual: %#v\nExpected: %#v", actual, expected)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
var (
|
||||
LowercaseCharset = sortCharset("abcdefghijklmnopqrstuvwxyz")
|
||||
UppercaseCharset = sortCharset("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
NumericCharset = sortCharset("0123456789")
|
||||
FullSymbolCharset = sortCharset("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
|
||||
ShortSymbolCharset = sortCharset("-")
|
||||
|
||||
AlphabeticCharset = sortCharset(UppercaseCharset + LowercaseCharset)
|
||||
AlphaNumericCharset = sortCharset(AlphabeticCharset + NumericCharset)
|
||||
AlphaNumericShortSymbolCharset = sortCharset(AlphaNumericCharset + ShortSymbolCharset)
|
||||
AlphaNumericFullSymbolCharset = sortCharset(AlphaNumericCharset + FullSymbolCharset)
|
||||
|
||||
LowercaseRuneset = []rune(LowercaseCharset)
|
||||
UppercaseRuneset = []rune(UppercaseCharset)
|
||||
NumericRuneset = []rune(NumericCharset)
|
||||
FullSymbolRuneset = []rune(FullSymbolCharset)
|
||||
ShortSymbolRuneset = []rune(ShortSymbolCharset)
|
||||
|
||||
AlphabeticRuneset = []rune(AlphabeticCharset)
|
||||
AlphaNumericRuneset = []rune(AlphaNumericCharset)
|
||||
AlphaNumericShortSymbolRuneset = []rune(AlphaNumericShortSymbolCharset)
|
||||
AlphaNumericFullSymbolRuneset = []rune(AlphaNumericFullSymbolCharset)
|
||||
|
||||
// DefaultStringGenerator has reasonable default rules for generating strings
|
||||
DefaultStringGenerator = StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: LowercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: UppercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: NumericRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: ShortSymbolRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func sortCharset(chars string) string {
|
||||
r := runes(chars)
|
||||
sort.Sort(r)
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// StringGenerator generats random strings from the provided charset & adhering to a set of rules. The set of rules
|
||||
// are things like CharsetRule which requires a certain number of characters from a sub-charset.
|
||||
type StringGenerator struct {
|
||||
// Length of the string to generate.
|
||||
Length int `mapstructure:"length" json:"length"`
|
||||
|
||||
// Rules the generated strings must adhere to.
|
||||
Rules serializableRules `mapstructure:"-" json:"rule"` // This is "rule" in JSON so it matches the HCL property type
|
||||
|
||||
// CharsetRule to choose runes from. This is computed from the rules, not directly configurable
|
||||
charset runes
|
||||
}
|
||||
|
||||
// Generate a random string from the charset and adhering to the provided rules.
|
||||
// The io.Reader is optional. If not provided, it will default to the reader from crypto/rand
|
||||
func (g *StringGenerator) Generate(ctx context.Context, rng io.Reader) (str string, err error) {
|
||||
if _, hasTimeout := ctx.Deadline(); !hasTimeout {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, 1*time.Second) // Ensure there's a timeout on the context
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// Ensure the generator is configured well since it may be manually created rather than parsed from HCL
|
||||
err = g.validateConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", fmt.Errorf("timed out generating string")
|
||||
default:
|
||||
str, err = g.generate(rng)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if str == "" {
|
||||
continue LOOP
|
||||
}
|
||||
return str, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *StringGenerator) generate(rng io.Reader) (str string, err error) {
|
||||
// If performance improvements need to be made, this can be changed to read a batch of
|
||||
// potential strings at once rather than one at a time. This will significantly
|
||||
// improve performance, but at the cost of added complexity.
|
||||
candidate, err := randomRunes(rng, g.charset, g.Length)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to generate random characters: %w", err)
|
||||
}
|
||||
|
||||
for _, rule := range g.Rules {
|
||||
if !rule.Pass(candidate) {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
// Passed all rules
|
||||
return string(candidate), nil
|
||||
}
|
||||
|
||||
const (
|
||||
// maxCharsetLen is the maximum length a charset is allowed to be when generating a candidate string.
|
||||
// This is the total number of numbers available for selecting an index out of the charset slice.
|
||||
maxCharsetLen = 256
|
||||
)
|
||||
|
||||
// randomRunes creates a random string based on the provided charset. The charset is limited to 255 characters, but
|
||||
// could be expanded if needed. Expanding the maximum charset size will decrease performance because it will need to
|
||||
// combine bytes into a larger integer using binary.BigEndian.Uint16() function.
|
||||
func randomRunes(rng io.Reader, charset []rune, length int) (candidate []rune, err error) {
|
||||
if len(charset) == 0 {
|
||||
return nil, fmt.Errorf("no charset specified")
|
||||
}
|
||||
if len(charset) > maxCharsetLen {
|
||||
return nil, fmt.Errorf("charset is too long: limited to %d characters", math.MaxUint8)
|
||||
}
|
||||
if length <= 0 {
|
||||
return nil, fmt.Errorf("unable to generate a zero or negative length runeset")
|
||||
}
|
||||
|
||||
// This can't always select indexes from [0-maxCharsetLen) because it could introduce bias to the character selection.
|
||||
// For instance, if the length of the charset is [a-zA-Z0-9-] (length of 63):
|
||||
// RNG ranges: [0-62][63-125][126-188][189-251] will equally select from the entirety of the charset. However,
|
||||
// the RNG values [252-255] will select the first 4 characters of the charset while ignoring the remaining 59.
|
||||
// This results in a bias towards the front of the charset.
|
||||
//
|
||||
// To avoid this, we determine the largest integer multiplier of the charset length that is <= maxCharsetLen
|
||||
// For instance, if the maxCharsetLen is 256 (the size of one byte) and the charset is length 63, the multiplier
|
||||
// equals 4:
|
||||
// 256/63 => 4.06
|
||||
// Trunc(4.06) => 4
|
||||
// Multiply by the charset length
|
||||
// Subtract 1 to account for 0-based counting and you get the max index value: 251
|
||||
maxAllowedRNGValue := (maxCharsetLen/len(charset))*len(charset) - 1
|
||||
|
||||
// rngBufferMultiplier increases the size of the RNG buffer to account for lost
|
||||
// indexes due to the maxAllowedRNGValue
|
||||
rngBufferMultiplier := 1.0
|
||||
|
||||
// Don't set a multiplier if we are able to use the entire range of indexes
|
||||
if maxAllowedRNGValue < maxCharsetLen {
|
||||
// Anything more complicated than an arbitrary percentage appears to have little practical performance benefit
|
||||
rngBufferMultiplier = 1.5
|
||||
}
|
||||
|
||||
// Default to the standard crypto reader if one isn't provided
|
||||
if rng == nil {
|
||||
rng = rand.Reader
|
||||
}
|
||||
|
||||
charsetLen := byte(len(charset))
|
||||
|
||||
runes := make([]rune, 0, length)
|
||||
|
||||
for len(runes) < length {
|
||||
// Generate a bunch of indexes
|
||||
data := make([]byte, int(float64(length)*rngBufferMultiplier))
|
||||
numBytes, err := rng.Read(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append characters until either we're out of indexes or the length is long enough
|
||||
for i := 0; i < numBytes; i++ {
|
||||
// Be careful to ensure that maxAllowedRNGValue isn't >= 256 as it will overflow and this
|
||||
// comparison will prevent characters from being selected from the charset
|
||||
if data[i] > byte(maxAllowedRNGValue) {
|
||||
continue
|
||||
}
|
||||
|
||||
index := data[i]
|
||||
if len(charset) != maxCharsetLen {
|
||||
index = index % charsetLen
|
||||
}
|
||||
r := charset[index]
|
||||
runes = append(runes, r)
|
||||
|
||||
if len(runes) == length {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return runes, nil
|
||||
}
|
||||
|
||||
// validateConfig of the generator to ensure that we can successfully generate a string.
|
||||
func (g *StringGenerator) validateConfig() (err error) {
|
||||
merr := &multierror.Error{}
|
||||
|
||||
// Ensure the sum of minimum lengths in the rules doesn't exceed the length specified
|
||||
minLen := getMinLength(g.Rules)
|
||||
if g.Length <= 0 {
|
||||
merr = multierror.Append(merr, fmt.Errorf("length must be > 0"))
|
||||
} else if g.Length < minLen {
|
||||
merr = multierror.Append(merr, fmt.Errorf("specified rules require at least %d characters but %d is specified", minLen, g.Length))
|
||||
}
|
||||
|
||||
// Ensure we have a charset & all characters are printable
|
||||
if len(g.charset) == 0 {
|
||||
// Yes this is mutating the generator but this is done so we don't have to compute this on every generation
|
||||
g.charset = getChars(g.Rules)
|
||||
}
|
||||
if len(g.charset) == 0 {
|
||||
merr = multierror.Append(merr, fmt.Errorf("no charset specified"))
|
||||
} else {
|
||||
for _, r := range g.charset {
|
||||
if !unicode.IsPrint(r) {
|
||||
merr = multierror.Append(merr, fmt.Errorf("non-printable character in charset"))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// getMinLength from the rules using the optional interface: `MinLength() int`
|
||||
func getMinLength(rules []Rule) (minLen int) {
|
||||
type minLengthProvider interface {
|
||||
MinLength() int
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
mlp, ok := rule.(minLengthProvider)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
minLen += mlp.MinLength()
|
||||
}
|
||||
return minLen
|
||||
}
|
||||
|
||||
// getChars from the rules using the optional interface: `Chars() []rune`
|
||||
func getChars(rules []Rule) (chars []rune) {
|
||||
type charsetProvider interface {
|
||||
Chars() []rune
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
cp, ok := rule.(charsetProvider)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
chars = append(chars, cp.Chars()...)
|
||||
}
|
||||
return deduplicateRunes(chars)
|
||||
}
|
||||
|
||||
// deduplicateRunes returns a new slice of sorted & de-duplicated runes
|
||||
func deduplicateRunes(original []rune) (deduped []rune) {
|
||||
if len(original) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[rune]bool{}
|
||||
dedupedRunes := []rune(nil)
|
||||
|
||||
for _, r := range original {
|
||||
if m[r] {
|
||||
continue
|
||||
}
|
||||
m[r] = true
|
||||
dedupedRunes = append(dedupedRunes, r)
|
||||
}
|
||||
|
||||
// They don't have to be sorted, but this is being done to make the charset easier to visualize
|
||||
sort.Sort(runes(dedupedRunes))
|
||||
return dedupedRunes
|
||||
}
|
|
@ -0,0 +1,824 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
MRAND "math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStringGenerator_Generate_successful(t *testing.T) {
|
||||
type testCase struct {
|
||||
timeout time.Duration
|
||||
generator *StringGenerator
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"common rules": {
|
||||
timeout: 1 * time.Second,
|
||||
generator: &StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: LowercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: UppercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: NumericRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: ShortSymbolRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
charset: AlphaNumericShortSymbolRuneset,
|
||||
},
|
||||
},
|
||||
"charset not explicitly specified": {
|
||||
timeout: 1 * time.Second,
|
||||
generator: &StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: LowercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: UppercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: NumericRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// One context to rule them all, one context to find them, one context to bring them all and in the darkness bind them.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), test.timeout)
|
||||
defer cancel()
|
||||
|
||||
runeset := map[rune]bool{}
|
||||
runesFound := []rune{}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
actual, err := test.generator.Generate(ctx, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("no error expected, but got: %s", err)
|
||||
}
|
||||
for _, r := range actual {
|
||||
if runeset[r] {
|
||||
continue
|
||||
}
|
||||
runeset[r] = true
|
||||
runesFound = append(runesFound, r)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(runes(runesFound))
|
||||
|
||||
expectedCharset := getChars(test.generator.Rules)
|
||||
|
||||
if !reflect.DeepEqual(runesFound, expectedCharset) {
|
||||
t.Fatalf("Didn't find all characters from the charset\nActual : [%s]\nExpected: [%s]", string(runesFound), string(expectedCharset))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringGenerator_Generate_errors(t *testing.T) {
|
||||
type testCase struct {
|
||||
timeout time.Duration
|
||||
generator *StringGenerator
|
||||
rng io.Reader
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"already timed out": {
|
||||
timeout: 0,
|
||||
generator: &StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{
|
||||
testCharsetRule{
|
||||
fail: false,
|
||||
},
|
||||
},
|
||||
charset: AlphaNumericShortSymbolRuneset,
|
||||
},
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"impossible rules": {
|
||||
timeout: 10 * time.Millisecond, // Keep this short so the test doesn't take too long
|
||||
generator: &StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{
|
||||
testCharsetRule{
|
||||
fail: true,
|
||||
},
|
||||
},
|
||||
charset: AlphaNumericShortSymbolRuneset,
|
||||
},
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"bad RNG reader": {
|
||||
timeout: 10 * time.Millisecond, // Keep this short so the test doesn't take too long
|
||||
generator: &StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{},
|
||||
charset: AlphaNumericShortSymbolRuneset,
|
||||
},
|
||||
rng: badReader{},
|
||||
},
|
||||
"0 length": {
|
||||
timeout: 10 * time.Millisecond,
|
||||
generator: &StringGenerator{
|
||||
Length: 0,
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 0,
|
||||
},
|
||||
},
|
||||
charset: []rune("abcde"),
|
||||
},
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"-1 length": {
|
||||
timeout: 10 * time.Millisecond,
|
||||
generator: &StringGenerator{
|
||||
Length: -1,
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 0,
|
||||
},
|
||||
},
|
||||
charset: []rune("abcde"),
|
||||
},
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"no charset": {
|
||||
timeout: 10 * time.Millisecond,
|
||||
generator: &StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{},
|
||||
},
|
||||
rng: rand.Reader,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// One context to rule them all, one context to find them, one context to bring them all and in the darkness bind them.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), test.timeout)
|
||||
defer cancel()
|
||||
|
||||
actual, err := test.generator.Generate(ctx, test.rng)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but none found")
|
||||
}
|
||||
if actual != "" {
|
||||
t.Fatalf("Random string returned: %s", actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomRunes_deterministic(t *testing.T) {
|
||||
// These tests are to ensure that the charset selection doesn't do anything weird like selecting the same character
|
||||
// over and over again. The number of test cases here should be kept to a minimum since they are sensitive to changes
|
||||
type testCase struct {
|
||||
rngSeed int64
|
||||
charset string
|
||||
length int
|
||||
expected string
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"small charset": {
|
||||
rngSeed: 1585593298447807000,
|
||||
charset: "abcde",
|
||||
length: 20,
|
||||
expected: "ddddddcdebbeebdbdbcd",
|
||||
},
|
||||
"common charset": {
|
||||
rngSeed: 1585593298447807001,
|
||||
charset: AlphaNumericShortSymbolCharset,
|
||||
length: 20,
|
||||
expected: "ON6lVjnBs84zJbUBVEzb",
|
||||
},
|
||||
"max size charset": {
|
||||
rngSeed: 1585593298447807002,
|
||||
charset: " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ" +
|
||||
"ġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠ" +
|
||||
"šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠",
|
||||
length: 20,
|
||||
expected: "tųŎ℄ņ℃Œ.@řHš-ℍ}ħGIJLℏ",
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
rng := MRAND.New(MRAND.NewSource(test.rngSeed))
|
||||
runes, err := randomRunes(rng, []rune(test.charset), test.length)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, but found: %s", err)
|
||||
}
|
||||
|
||||
str := string(runes)
|
||||
|
||||
if str != test.expected {
|
||||
t.Fatalf("Actual: %s Expected: %s", str, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomRunes_successful(t *testing.T) {
|
||||
type testCase struct {
|
||||
charset []rune // Assumes no duplicate runes
|
||||
length int
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"small charset": {
|
||||
charset: []rune("abcde"),
|
||||
length: 20,
|
||||
},
|
||||
"common charset": {
|
||||
charset: AlphaNumericShortSymbolRuneset,
|
||||
length: 20,
|
||||
},
|
||||
"max size charset": {
|
||||
charset: []rune(
|
||||
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ" +
|
||||
"ġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠ" +
|
||||
"šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠",
|
||||
),
|
||||
length: 20,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
runeset := map[rune]bool{}
|
||||
runesFound := []rune{}
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
actual, err := randomRunes(rand.Reader, test.charset, test.length)
|
||||
if err != nil {
|
||||
t.Fatalf("no error expected, but got: %s", err)
|
||||
}
|
||||
for _, r := range actual {
|
||||
if runeset[r] {
|
||||
continue
|
||||
}
|
||||
runeset[r] = true
|
||||
runesFound = append(runesFound, r)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(runes(runesFound))
|
||||
|
||||
// Sort the input too just to ensure that they can be compared
|
||||
sort.Sort(runes(test.charset))
|
||||
|
||||
if !reflect.DeepEqual(runesFound, test.charset) {
|
||||
t.Fatalf("Didn't find all characters from the charset\nActual : [%s]\nExpected: [%s]", string(runesFound), string(test.charset))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomRunes_errors(t *testing.T) {
|
||||
type testCase struct {
|
||||
charset []rune
|
||||
length int
|
||||
rng io.Reader
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"nil charset": {
|
||||
charset: nil,
|
||||
length: 20,
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"empty charset": {
|
||||
charset: []rune{},
|
||||
length: 20,
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"charset is too long": {
|
||||
charset: []rune(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ" +
|
||||
"ġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠ" +
|
||||
"šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠" +
|
||||
"Σ",
|
||||
),
|
||||
length:20,
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"length is zero": {
|
||||
charset: []rune("abcde"),
|
||||
length: 0,
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"length is negative": {
|
||||
charset: []rune("abcde"),
|
||||
length: -3,
|
||||
rng: rand.Reader,
|
||||
},
|
||||
"reader failed": {
|
||||
charset: []rune("abcde"),
|
||||
length: 20,
|
||||
rng: badReader{},
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actual, err := randomRunes(test.rng, test.charset, test.length)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but none found")
|
||||
}
|
||||
if actual != nil {
|
||||
t.Fatalf("Expected no value, but found [%s]", string(actual))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringGenerator_Generate(b *testing.B) {
|
||||
lengths := []int{
|
||||
8, 12, 16, 20, 24, 28,
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
generator StringGenerator
|
||||
}
|
||||
|
||||
benches := map[string]testCase{
|
||||
"no rules": {
|
||||
generator: StringGenerator{
|
||||
charset: AlphaNumericFullSymbolRuneset,
|
||||
Rules: []Rule{},
|
||||
},
|
||||
},
|
||||
"default generator": {
|
||||
generator: DefaultStringGenerator,
|
||||
},
|
||||
"large symbol set": {
|
||||
generator: StringGenerator{
|
||||
charset: AlphaNumericFullSymbolRuneset,
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: LowercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: UppercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: NumericRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: FullSymbolRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"max symbol set": {
|
||||
generator: StringGenerator{
|
||||
charset: []rune(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ" +
|
||||
"ġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠ" +
|
||||
"šŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠",
|
||||
),
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: LowercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: UppercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: []rune("ĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒ"),
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"restrictive charset rules": {
|
||||
generator: StringGenerator{
|
||||
charset: AlphaNumericShortSymbolRuneset,
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: []rune("A"),
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: []rune("1"),
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: []rune("a"),
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: []rune("-"),
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, bench := range benches {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for _, length := range lengths {
|
||||
bench.generator.Length = length
|
||||
b.Run(fmt.Sprintf("length=%d", length), func(b *testing.B) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
str, err := bench.generator.Generate(ctx, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to generate string: %s", err)
|
||||
}
|
||||
if str == "" {
|
||||
b.Fatalf("Didn't error but didn't generate a string")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Mimic what the SQLCredentialsProducer is doing
|
||||
b.Run("SQLCredentialsProducer", func(b *testing.B) {
|
||||
sg := StringGenerator{
|
||||
Length: 16, // 16 because the SQLCredentialsProducer prepends 4 characters to a 20 character password
|
||||
charset: AlphaNumericRuneset,
|
||||
Rules: nil,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
str, err := sg.Generate(ctx, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to generate string: %s", err)
|
||||
}
|
||||
if str == "" {
|
||||
b.Fatalf("Didn't error but didn't generate a string")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure the StringGenerator can be properly JSON-ified
|
||||
func TestStringGenerator_JSON(t *testing.T) {
|
||||
expected := StringGenerator{
|
||||
Length: 20,
|
||||
charset: deduplicateRunes([]rune("teststring" + ShortSymbolCharset)),
|
||||
Rules: []Rule{
|
||||
testCharsetRule{
|
||||
String: "teststring",
|
||||
Integer: 123,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: ShortSymbolRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b, err := json.Marshal(expected)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal to JSON: %s", err)
|
||||
}
|
||||
|
||||
parser := PolicyParser{
|
||||
RuleRegistry: Registry{
|
||||
Rules: map[string]ruleConstructor{
|
||||
"testrule": newTestRule,
|
||||
"charset": ParseCharset,
|
||||
},
|
||||
},
|
||||
}
|
||||
actual, err := parser.ParsePolicy(string(b))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse JSON: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("Actual: %#v\nExpected: %#v", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
type badReader struct{}
|
||||
|
||||
func (badReader) Read([]byte) (int, error) {
|
||||
return 0, fmt.Errorf("test error")
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
type testCase struct {
|
||||
generator StringGenerator
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"default generator": {
|
||||
generator: DefaultStringGenerator,
|
||||
expectErr: false,
|
||||
},
|
||||
"length is 0": {
|
||||
generator: StringGenerator{
|
||||
Length: 0,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"length is negative": {
|
||||
generator: StringGenerator{
|
||||
Length: -2,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"nil charset, no rules": {
|
||||
generator: StringGenerator{
|
||||
Length: 5,
|
||||
charset: nil,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"zero length charset, no rules": {
|
||||
generator: StringGenerator{
|
||||
Length: 5,
|
||||
charset: []rune{},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"rules require password longer than length": {
|
||||
generator: StringGenerator{
|
||||
Length: 5,
|
||||
charset: []rune("abcde"),
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: []rune("abcde"),
|
||||
MinChars: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
"charset has non-printable characters": {
|
||||
generator: StringGenerator{
|
||||
Length: 0,
|
||||
charset: []rune{
|
||||
'a',
|
||||
'b',
|
||||
0, // Null character
|
||||
'd',
|
||||
'e',
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := test.generator.validateConfig()
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testNonCharsetRule struct {
|
||||
String string `mapstructure:"string" json:"string"`
|
||||
}
|
||||
|
||||
func (tr testNonCharsetRule) Pass([]rune) bool { return true }
|
||||
func (tr testNonCharsetRule) Type() string { return "testNonCharsetRule" }
|
||||
|
||||
func TestGetChars(t *testing.T) {
|
||||
type testCase struct {
|
||||
rules []Rule
|
||||
expected []rune
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"nil rules": {
|
||||
rules: nil,
|
||||
expected: []rune(nil),
|
||||
},
|
||||
"empty rules": {
|
||||
rules: []Rule{},
|
||||
expected: []rune(nil),
|
||||
},
|
||||
"rule without chars": {
|
||||
rules: []Rule{
|
||||
testNonCharsetRule{
|
||||
String: "teststring",
|
||||
},
|
||||
},
|
||||
expected: []rune(nil),
|
||||
},
|
||||
"rule with chars": {
|
||||
rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: []rune("abcdefghij"),
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
expected: []rune("abcdefghij"),
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actual := getChars(test.rules)
|
||||
if !reflect.DeepEqual(actual, test.expected) {
|
||||
t.Fatalf("Actual: %v\nExpected: %v", actual, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeduplicateRunes(t *testing.T) {
|
||||
type testCase struct {
|
||||
input []rune
|
||||
expected []rune
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"empty string": {
|
||||
input: []rune(""),
|
||||
expected: []rune(nil),
|
||||
},
|
||||
"no duplicates": {
|
||||
input: []rune("abcde"),
|
||||
expected: []rune("abcde"),
|
||||
},
|
||||
"in order duplicates": {
|
||||
input: []rune("aaaabbbbcccccccddddeeeee"),
|
||||
expected: []rune("abcde"),
|
||||
},
|
||||
"out of order duplicates": {
|
||||
input: []rune("abcdeabcdeabcdeabcde"),
|
||||
expected: []rune("abcde"),
|
||||
},
|
||||
"unicode no duplicates": {
|
||||
input: []rune("日本語"),
|
||||
expected: []rune("日本語"),
|
||||
},
|
||||
"unicode in order duplicates": {
|
||||
input: []rune("日日日日本本本語語語語語"),
|
||||
expected: []rune("日本語"),
|
||||
},
|
||||
"unicode out of order duplicates": {
|
||||
input: []rune("日本語日本語日本語日本語"),
|
||||
expected: []rune("日本語"),
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actual := deduplicateRunes(test.input)
|
||||
if !reflect.DeepEqual(actual, test.expected) {
|
||||
t.Fatalf("Actual: %#v\nExpected:%#v", actual, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomRunes_Bias(t *testing.T) {
|
||||
type testCase struct {
|
||||
charset []rune
|
||||
maxStdDev float64
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"small charset": {
|
||||
charset: []rune("abcde"),
|
||||
maxStdDev: 2700,
|
||||
},
|
||||
"lowercase characters": {
|
||||
charset: LowercaseRuneset,
|
||||
maxStdDev: 1000,
|
||||
},
|
||||
"alphabetical characters": {
|
||||
charset: AlphabeticRuneset,
|
||||
maxStdDev: 800,
|
||||
},
|
||||
"alphanumeric": {
|
||||
charset: AlphaNumericRuneset,
|
||||
maxStdDev: 800,
|
||||
},
|
||||
"alphanumeric with symbol": {
|
||||
charset: AlphaNumericShortSymbolRuneset,
|
||||
maxStdDev: 800,
|
||||
},
|
||||
"charset evenly divisible into 256": {
|
||||
charset: append(AlphaNumericRuneset, '!', '@'),
|
||||
maxStdDev: 800,
|
||||
},
|
||||
"large charset": {
|
||||
charset: FullSymbolRuneset,
|
||||
maxStdDev: 800,
|
||||
},
|
||||
"just under half size charset": {
|
||||
charset: []rune(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğ"),
|
||||
maxStdDev: 800,
|
||||
},
|
||||
"half size charset": {
|
||||
charset: []rune(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠ"),
|
||||
maxStdDev: 800,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s (%d chars)", name, len(test.charset)), func(t *testing.T) {
|
||||
runeCounts := map[rune]int{}
|
||||
|
||||
generations := 50000
|
||||
length := 100
|
||||
for i := 0; i < generations; i++ {
|
||||
str, err := randomRunes(nil, test.charset, length)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, r := range str {
|
||||
runeCounts[r]++
|
||||
}
|
||||
}
|
||||
|
||||
chars := charCounts{}
|
||||
|
||||
var sum float64
|
||||
for r, count := range runeCounts {
|
||||
chars = append(chars, charCount{r, count})
|
||||
sum += float64(count)
|
||||
}
|
||||
|
||||
mean := sum / float64(len(runeCounts))
|
||||
var stdDev float64
|
||||
for _, count := range runeCounts {
|
||||
stdDev += math.Pow(float64(count)-mean, 2)
|
||||
}
|
||||
|
||||
stdDev = math.Sqrt(stdDev / float64(len(runeCounts)))
|
||||
t.Logf("Mean : %10.4f", mean)
|
||||
|
||||
if stdDev > test.maxStdDev {
|
||||
t.Fatalf("Standard deviation is too large: %.2f > %.2f", stdDev, test.maxStdDev)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type charCount struct {
|
||||
r rune
|
||||
count int
|
||||
}
|
||||
|
||||
type charCounts []charCount
|
||||
|
||||
func (s charCounts) Len() int { return len(s) }
|
||||
func (s charCounts) Less(i, j int) bool { return s[i].r < s[j].r }
|
||||
func (s charCounts) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
@ -62,6 +62,26 @@ func (s *InmemStorage) Underlying() *inmem.InmemBackend {
|
|||
return s.underlying.(*inmem.InmemBackend)
|
||||
}
|
||||
|
||||
func (s *InmemStorage) FailPut(fail bool) *InmemStorage {
|
||||
s.Underlying().FailPut(fail)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *InmemStorage) FailGet(fail bool) *InmemStorage {
|
||||
s.Underlying().FailGet(fail)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *InmemStorage) FailDelete(fail bool) *InmemStorage {
|
||||
s.Underlying().FailDelete(fail)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *InmemStorage) FailList(fail bool) *InmemStorage {
|
||||
s.Underlying().FailList(fail)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *InmemStorage) init() {
|
||||
s.underlying, _ = inmem.NewInmem(nil, nil)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package logical
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
|
@ -68,6 +70,15 @@ type SystemView interface {
|
|||
|
||||
// PluginEnv returns Vault environment information used by plugins
|
||||
PluginEnv(context.Context) (*PluginEnvironment, error)
|
||||
|
||||
// GeneratePasswordFromPolicy generates a password from the policy referenced.
|
||||
// If the policy does not exist, this will return an error.
|
||||
GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error)
|
||||
}
|
||||
|
||||
type PasswordPolicy interface {
|
||||
// Generate a random password
|
||||
Generate(context.Context, io.Reader) (string, error)
|
||||
}
|
||||
|
||||
type ExtendedSystemView interface {
|
||||
|
@ -90,6 +101,7 @@ type StaticSystemView struct {
|
|||
Features license.Features
|
||||
VaultVersion string
|
||||
PluginEnvironment *PluginEnvironment
|
||||
PasswordPolicies map[string]PasswordPolicy
|
||||
}
|
||||
|
||||
type noopAuditor struct{}
|
||||
|
@ -165,3 +177,20 @@ func (d StaticSystemView) HasFeature(feature license.Features) bool {
|
|||
func (d StaticSystemView) PluginEnv(_ context.Context) (*PluginEnvironment, error) {
|
||||
return d.PluginEnvironment, nil
|
||||
}
|
||||
|
||||
func (d StaticSystemView) GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", fmt.Errorf("context timed out")
|
||||
default:
|
||||
}
|
||||
|
||||
if d.PasswordPolicies == nil {
|
||||
return "", fmt.Errorf("password policy not found")
|
||||
}
|
||||
policy, exists := d.PasswordPolicies[policyName]
|
||||
if !exists {
|
||||
return "", fmt.Errorf("password policy not found")
|
||||
}
|
||||
return policy.Generate(ctx, nil)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import (
|
|||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/plugin/pb"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func newGRPCSystemView(conn *grpc.ClientConn) *gRPCSystemViewClient {
|
||||
|
@ -161,6 +163,17 @@ func (s *gRPCSystemViewClient) PluginEnv(ctx context.Context) (*logical.PluginEn
|
|||
return reply.PluginEnvironment, nil
|
||||
}
|
||||
|
||||
func (s *gRPCSystemViewClient) GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error) {
|
||||
req := &pb.GeneratePasswordFromPolicyRequest{
|
||||
PolicyName: policyName,
|
||||
}
|
||||
resp, err := s.client.GeneratePasswordFromPolicy(ctx, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Password, nil
|
||||
}
|
||||
|
||||
type gRPCSystemViewServer struct {
|
||||
impl logical.SystemView
|
||||
}
|
||||
|
@ -274,3 +287,20 @@ func (s *gRPCSystemViewServer) PluginEnv(ctx context.Context, _ *pb.Empty) (*pb.
|
|||
PluginEnvironment: pluginEnv,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *gRPCSystemViewServer) GeneratePasswordFromPolicy(ctx context.Context, req *pb.GeneratePasswordFromPolicyRequest) (*pb.GeneratePasswordFromPolicyReply, error) {
|
||||
policyName := req.PolicyName
|
||||
if policyName == "" {
|
||||
return &pb.GeneratePasswordFromPolicyReply{}, status.Errorf(codes.InvalidArgument, "no password policy specified")
|
||||
}
|
||||
|
||||
password, err := s.impl.GeneratePasswordFromPolicy(ctx, policyName)
|
||||
if err != nil {
|
||||
return &pb.GeneratePasswordFromPolicyReply{}, status.Errorf(codes.Internal, "failed to generate password")
|
||||
}
|
||||
|
||||
resp := &pb.GeneratePasswordFromPolicyReply{
|
||||
Password: password,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
|
|
@ -2,17 +2,17 @@ package plugin
|
|||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/random"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/plugin/pb"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func TestSystem_GRPC_GRPC_impl(t *testing.T) {
|
||||
|
@ -239,3 +239,63 @@ func TestSystem_GRPC_pluginEnv(t *testing.T) {
|
|||
t.Fatalf("expected: %v, got: %v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_GRPC_GeneratePasswordFromPolicy(t *testing.T) {
|
||||
policyName := "testpolicy"
|
||||
expectedPolicy := &random.StringGenerator{
|
||||
Length: 8,
|
||||
Rules: []random.Rule{
|
||||
&random.CharsetRule{
|
||||
Charset: random.LowercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
&random.CharsetRule{
|
||||
Charset: random.UppercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
&random.CharsetRule{
|
||||
Charset: random.NumericRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
&random.CharsetRule{
|
||||
Charset: random.ShortSymbolRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
sys := &logical.StaticSystemView{
|
||||
PasswordPolicies: map[string]logical.PasswordPolicy{
|
||||
policyName: logical.PasswordPolicy(expectedPolicy),
|
||||
},
|
||||
}
|
||||
|
||||
client, server := plugin.TestGRPCConn(t, func(s *grpc.Server) {
|
||||
pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{
|
||||
impl: sys,
|
||||
})
|
||||
})
|
||||
defer server.Stop()
|
||||
defer client.Close()
|
||||
|
||||
testSystemView := newGRPCSystemView(client)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
password, err := testSystemView.GeneratePasswordFromPolicy(ctx, policyName)
|
||||
if err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
passRunes := []rune(password)
|
||||
|
||||
if len(passRunes) != expectedPolicy.Length {
|
||||
t.Fatalf("Generated password should have length %d but was %d", expectedPolicy.Length, len(passRunes))
|
||||
}
|
||||
|
||||
for _, rule := range expectedPolicy.Rules {
|
||||
if !rule.Pass(passRunes) {
|
||||
t.Fatalf("Password [%s] did not pass rule: %#v", password, rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3017,6 +3017,100 @@ func (x *PluginEnvReply) GetErr() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
type GeneratePasswordFromPolicyRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PolicyName string `sentinel:"" protobuf:"bytes,1,opt,name=policy_name,json=policyName,proto3" json:"policy_name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyRequest) Reset() {
|
||||
*x = GeneratePasswordFromPolicyRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[44]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeneratePasswordFromPolicyRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[44]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeneratePasswordFromPolicyRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GeneratePasswordFromPolicyRequest) Descriptor() ([]byte, []int) {
|
||||
return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{44}
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyRequest) GetPolicyName() string {
|
||||
if x != nil {
|
||||
return x.PolicyName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GeneratePasswordFromPolicyReply struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Password string `sentinel:"" protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyReply) Reset() {
|
||||
*x = GeneratePasswordFromPolicyReply{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[45]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeneratePasswordFromPolicyReply) ProtoMessage() {}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[45]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeneratePasswordFromPolicyReply.ProtoReflect.Descriptor instead.
|
||||
func (*GeneratePasswordFromPolicyReply) Descriptor() ([]byte, []int) {
|
||||
return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{45}
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyReply) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
|
@ -3029,7 +3123,7 @@ type Connection struct {
|
|||
func (x *Connection) Reset() {
|
||||
*x = Connection{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[44]
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[46]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -3042,7 +3136,7 @@ func (x *Connection) String() string {
|
|||
func (*Connection) ProtoMessage() {}
|
||||
|
||||
func (x *Connection) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[44]
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[46]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -3055,7 +3149,7 @@ func (x *Connection) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use Connection.ProtoReflect.Descriptor instead.
|
||||
func (*Connection) Descriptor() ([]byte, []int) {
|
||||
return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{44}
|
||||
return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{46}
|
||||
}
|
||||
|
||||
func (x *Connection) GetRemoteAddr() string {
|
||||
|
@ -3423,89 +3517,104 @@ var file_sdk_plugin_pb_backend_proto_rawDesc = []byte{
|
|||
0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x11,
|
||||
0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
||||
0x65, 0x72, 0x72, 0x22, 0x2d, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64,
|
||||
0x64, 0x72, 0x32, 0xa5, 0x03, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3e,
|
||||
0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x15, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64,
|
||||
0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30,
|
||||
0x0a, 0x0c, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x09,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53,
|
||||
0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||
0x12, 0x53, 0x0a, 0x14, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65,
|
||||
0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1c, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61,
|
||||
0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65,
|
||||
0x63, 0x6b, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x1d, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64,
|
||||
0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x07, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70,
|
||||
0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x09, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x31, 0x0a, 0x0d, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69,
|
||||
0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x76,
|
||||
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x09,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x53, 0x65, 0x74,
|
||||
0x75, 0x70, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x41, 0x72, 0x67,
|
||||
0x73, 0x1a, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6c,
|
||||
0x79, 0x12, 0x35, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12,
|
||||
0x12, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x41,
|
||||
0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65,
|
||||
0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xd5, 0x01, 0x0a, 0x07, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x41,
|
||||
0x72, 0x67, 0x73, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x03, 0x47, 0x65, 0x74,
|
||||
0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x47, 0x65, 0x74,
|
||||
0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x03, 0x50, 0x75, 0x74,
|
||||
0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x75, 0x74,
|
||||
0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x50, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x44, 0x65, 0x6c,
|
||||
0x65, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70,
|
||||
0x6c, 0x79, 0x32, 0xc7, 0x04, 0x0a, 0x0a, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x56, 0x69, 0x65,
|
||||
0x77, 0x12, 0x2a, 0x0a, 0x0f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x65, 0x61, 0x73,
|
||||
0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
||||
0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a,
|
||||
0x0b, 0x4d, 0x61, 0x78, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x07, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64,
|
||||
0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x36, 0x0a,
|
||||
0x0f, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||
0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x18, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
|
||||
0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44,
|
||||
0x61, 0x74, 0x61, 0x12, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x19, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44,
|
||||
0x61, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x0c, 0x4d, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2c, 0x0a, 0x0a, 0x4c, 0x6f,
|
||||
0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x0a, 0x45, 0x6e, 0x74, 0x69,
|
||||
0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69,
|
||||
0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
|
||||
0x2a, 0x0a, 0x09, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x12, 0x09, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6c, 0x75,
|
||||
0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x0f, 0x47,
|
||||
0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x12,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72,
|
||||
0x67, 0x73, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f,
|
||||
0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x2a, 0x5a, 0x28,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69,
|
||||
0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70,
|
||||
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x72, 0x72, 0x22, 0x44, 0x0a, 0x21, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50,
|
||||
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||
0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69,
|
||||
0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70,
|
||||
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3d, 0x0a, 0x1f, 0x47, 0x65, 0x6e,
|
||||
0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f,
|
||||
0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x2d, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
|
||||
0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d,
|
||||
0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x32, 0xa5, 0x03, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b,
|
||||
0x65, 0x6e, 0x64, 0x12, 0x3e, 0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65,
|
||||
0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x0c, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61,
|
||||
0x74, 0x68, 0x73, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x53, 0x0a, 0x14, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45,
|
||||
0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1c, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e,
|
||||
0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x1d, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65,
|
||||
0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x07, 0x43, 0x6c,
|
||||
0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x31, 0x0a, 0x0d, 0x49,
|
||||
0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41,
|
||||
0x72, 0x67, 0x73, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x26,
|
||||
0x0a, 0x05, 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74,
|
||||
0x75, 0x70, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75,
|
||||
0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e,
|
||||
0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a,
|
||||
0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32,
|
||||
0xd5, 0x01, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e,
|
||||
0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x47, 0x65, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e,
|
||||
0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x50, 0x75, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x37,
|
||||
0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a,
|
||||
0x16, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65,
|
||||
0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xb1, 0x05, 0x0a, 0x0a, 0x53, 0x79, 0x73, 0x74,
|
||||
0x65, 0x6d, 0x56, 0x69, 0x65, 0x77, 0x12, 0x2a, 0x0a, 0x0f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c,
|
||||
0x74, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70,
|
||||
0x6c, 0x79, 0x12, 0x26, 0x0a, 0x0b, 0x4d, 0x61, 0x78, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x54,
|
||||
0x4c, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x07, 0x54, 0x61,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x70,
|
||||
0x6c, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a, 0x10, 0x52, 0x65,
|
||||
0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x09,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52,
|
||||
0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
|
||||
0x65, 0x70, 0x6c, 0x79, 0x12, 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x41, 0x72,
|
||||
0x67, 0x73, 0x1a, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a,
|
||||
0x0c, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x09, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
|
||||
0x2c, 0x0a, 0x0a, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x09, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f,
|
||||
0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a,
|
||||
0x0a, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x1a,
|
||||
0x13, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
||||
0x65, 0x70, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x09, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e,
|
||||
0x76, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||
0x12, 0x3f, 0x0a, 0x0f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74,
|
||||
0x69, 0x74, 0x79, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x6f,
|
||||
0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c,
|
||||
0x79, 0x12, 0x68, 0x0a, 0x1a, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12,
|
||||
0x25, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65,
|
||||
0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x2a, 0x5a, 0x28, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
|
||||
0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x6c,
|
||||
0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -3520,82 +3629,84 @@ func file_sdk_plugin_pb_backend_proto_rawDescGZIP() []byte {
|
|||
return file_sdk_plugin_pb_backend_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_sdk_plugin_pb_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 50)
|
||||
var file_sdk_plugin_pb_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 52)
|
||||
var file_sdk_plugin_pb_backend_proto_goTypes = []interface{}{
|
||||
(*Empty)(nil), // 0: pb.Empty
|
||||
(*Header)(nil), // 1: pb.Header
|
||||
(*ProtoError)(nil), // 2: pb.ProtoError
|
||||
(*Paths)(nil), // 3: pb.Paths
|
||||
(*Request)(nil), // 4: pb.Request
|
||||
(*Auth)(nil), // 5: pb.Auth
|
||||
(*TokenEntry)(nil), // 6: pb.TokenEntry
|
||||
(*LeaseOptions)(nil), // 7: pb.LeaseOptions
|
||||
(*Secret)(nil), // 8: pb.Secret
|
||||
(*Response)(nil), // 9: pb.Response
|
||||
(*ResponseWrapInfo)(nil), // 10: pb.ResponseWrapInfo
|
||||
(*RequestWrapInfo)(nil), // 11: pb.RequestWrapInfo
|
||||
(*HandleRequestArgs)(nil), // 12: pb.HandleRequestArgs
|
||||
(*HandleRequestReply)(nil), // 13: pb.HandleRequestReply
|
||||
(*InitializeArgs)(nil), // 14: pb.InitializeArgs
|
||||
(*InitializeReply)(nil), // 15: pb.InitializeReply
|
||||
(*SpecialPathsReply)(nil), // 16: pb.SpecialPathsReply
|
||||
(*HandleExistenceCheckArgs)(nil), // 17: pb.HandleExistenceCheckArgs
|
||||
(*HandleExistenceCheckReply)(nil), // 18: pb.HandleExistenceCheckReply
|
||||
(*SetupArgs)(nil), // 19: pb.SetupArgs
|
||||
(*SetupReply)(nil), // 20: pb.SetupReply
|
||||
(*TypeReply)(nil), // 21: pb.TypeReply
|
||||
(*InvalidateKeyArgs)(nil), // 22: pb.InvalidateKeyArgs
|
||||
(*StorageEntry)(nil), // 23: pb.StorageEntry
|
||||
(*StorageListArgs)(nil), // 24: pb.StorageListArgs
|
||||
(*StorageListReply)(nil), // 25: pb.StorageListReply
|
||||
(*StorageGetArgs)(nil), // 26: pb.StorageGetArgs
|
||||
(*StorageGetReply)(nil), // 27: pb.StorageGetReply
|
||||
(*StoragePutArgs)(nil), // 28: pb.StoragePutArgs
|
||||
(*StoragePutReply)(nil), // 29: pb.StoragePutReply
|
||||
(*StorageDeleteArgs)(nil), // 30: pb.StorageDeleteArgs
|
||||
(*StorageDeleteReply)(nil), // 31: pb.StorageDeleteReply
|
||||
(*TTLReply)(nil), // 32: pb.TTLReply
|
||||
(*TaintedReply)(nil), // 33: pb.TaintedReply
|
||||
(*CachingDisabledReply)(nil), // 34: pb.CachingDisabledReply
|
||||
(*ReplicationStateReply)(nil), // 35: pb.ReplicationStateReply
|
||||
(*ResponseWrapDataArgs)(nil), // 36: pb.ResponseWrapDataArgs
|
||||
(*ResponseWrapDataReply)(nil), // 37: pb.ResponseWrapDataReply
|
||||
(*MlockEnabledReply)(nil), // 38: pb.MlockEnabledReply
|
||||
(*LocalMountReply)(nil), // 39: pb.LocalMountReply
|
||||
(*EntityInfoArgs)(nil), // 40: pb.EntityInfoArgs
|
||||
(*EntityInfoReply)(nil), // 41: pb.EntityInfoReply
|
||||
(*GroupsForEntityReply)(nil), // 42: pb.GroupsForEntityReply
|
||||
(*PluginEnvReply)(nil), // 43: pb.PluginEnvReply
|
||||
(*Connection)(nil), // 44: pb.Connection
|
||||
nil, // 45: pb.Request.HeadersEntry
|
||||
nil, // 46: pb.Auth.MetadataEntry
|
||||
nil, // 47: pb.TokenEntry.MetaEntry
|
||||
nil, // 48: pb.Response.HeadersEntry
|
||||
nil, // 49: pb.SetupArgs.ConfigEntry
|
||||
(*logical.Alias)(nil), // 50: logical.Alias
|
||||
(*timestamp.Timestamp)(nil), // 51: google.protobuf.Timestamp
|
||||
(*logical.Entity)(nil), // 52: logical.Entity
|
||||
(*logical.Group)(nil), // 53: logical.Group
|
||||
(*logical.PluginEnvironment)(nil), // 54: logical.PluginEnvironment
|
||||
(*Empty)(nil), // 0: pb.Empty
|
||||
(*Header)(nil), // 1: pb.Header
|
||||
(*ProtoError)(nil), // 2: pb.ProtoError
|
||||
(*Paths)(nil), // 3: pb.Paths
|
||||
(*Request)(nil), // 4: pb.Request
|
||||
(*Auth)(nil), // 5: pb.Auth
|
||||
(*TokenEntry)(nil), // 6: pb.TokenEntry
|
||||
(*LeaseOptions)(nil), // 7: pb.LeaseOptions
|
||||
(*Secret)(nil), // 8: pb.Secret
|
||||
(*Response)(nil), // 9: pb.Response
|
||||
(*ResponseWrapInfo)(nil), // 10: pb.ResponseWrapInfo
|
||||
(*RequestWrapInfo)(nil), // 11: pb.RequestWrapInfo
|
||||
(*HandleRequestArgs)(nil), // 12: pb.HandleRequestArgs
|
||||
(*HandleRequestReply)(nil), // 13: pb.HandleRequestReply
|
||||
(*InitializeArgs)(nil), // 14: pb.InitializeArgs
|
||||
(*InitializeReply)(nil), // 15: pb.InitializeReply
|
||||
(*SpecialPathsReply)(nil), // 16: pb.SpecialPathsReply
|
||||
(*HandleExistenceCheckArgs)(nil), // 17: pb.HandleExistenceCheckArgs
|
||||
(*HandleExistenceCheckReply)(nil), // 18: pb.HandleExistenceCheckReply
|
||||
(*SetupArgs)(nil), // 19: pb.SetupArgs
|
||||
(*SetupReply)(nil), // 20: pb.SetupReply
|
||||
(*TypeReply)(nil), // 21: pb.TypeReply
|
||||
(*InvalidateKeyArgs)(nil), // 22: pb.InvalidateKeyArgs
|
||||
(*StorageEntry)(nil), // 23: pb.StorageEntry
|
||||
(*StorageListArgs)(nil), // 24: pb.StorageListArgs
|
||||
(*StorageListReply)(nil), // 25: pb.StorageListReply
|
||||
(*StorageGetArgs)(nil), // 26: pb.StorageGetArgs
|
||||
(*StorageGetReply)(nil), // 27: pb.StorageGetReply
|
||||
(*StoragePutArgs)(nil), // 28: pb.StoragePutArgs
|
||||
(*StoragePutReply)(nil), // 29: pb.StoragePutReply
|
||||
(*StorageDeleteArgs)(nil), // 30: pb.StorageDeleteArgs
|
||||
(*StorageDeleteReply)(nil), // 31: pb.StorageDeleteReply
|
||||
(*TTLReply)(nil), // 32: pb.TTLReply
|
||||
(*TaintedReply)(nil), // 33: pb.TaintedReply
|
||||
(*CachingDisabledReply)(nil), // 34: pb.CachingDisabledReply
|
||||
(*ReplicationStateReply)(nil), // 35: pb.ReplicationStateReply
|
||||
(*ResponseWrapDataArgs)(nil), // 36: pb.ResponseWrapDataArgs
|
||||
(*ResponseWrapDataReply)(nil), // 37: pb.ResponseWrapDataReply
|
||||
(*MlockEnabledReply)(nil), // 38: pb.MlockEnabledReply
|
||||
(*LocalMountReply)(nil), // 39: pb.LocalMountReply
|
||||
(*EntityInfoArgs)(nil), // 40: pb.EntityInfoArgs
|
||||
(*EntityInfoReply)(nil), // 41: pb.EntityInfoReply
|
||||
(*GroupsForEntityReply)(nil), // 42: pb.GroupsForEntityReply
|
||||
(*PluginEnvReply)(nil), // 43: pb.PluginEnvReply
|
||||
(*GeneratePasswordFromPolicyRequest)(nil), // 44: pb.GeneratePasswordFromPolicyRequest
|
||||
(*GeneratePasswordFromPolicyReply)(nil), // 45: pb.GeneratePasswordFromPolicyReply
|
||||
(*Connection)(nil), // 46: pb.Connection
|
||||
nil, // 47: pb.Request.HeadersEntry
|
||||
nil, // 48: pb.Auth.MetadataEntry
|
||||
nil, // 49: pb.TokenEntry.MetaEntry
|
||||
nil, // 50: pb.Response.HeadersEntry
|
||||
nil, // 51: pb.SetupArgs.ConfigEntry
|
||||
(*logical.Alias)(nil), // 52: logical.Alias
|
||||
(*timestamp.Timestamp)(nil), // 53: google.protobuf.Timestamp
|
||||
(*logical.Entity)(nil), // 54: logical.Entity
|
||||
(*logical.Group)(nil), // 55: logical.Group
|
||||
(*logical.PluginEnvironment)(nil), // 56: logical.PluginEnvironment
|
||||
}
|
||||
var file_sdk_plugin_pb_backend_proto_depIDxs = []int32{
|
||||
8, // 0: pb.Request.secret:type_name -> pb.Secret
|
||||
5, // 1: pb.Request.auth:type_name -> pb.Auth
|
||||
45, // 2: pb.Request.headers:type_name -> pb.Request.HeadersEntry
|
||||
47, // 2: pb.Request.headers:type_name -> pb.Request.HeadersEntry
|
||||
11, // 3: pb.Request.wrap_info:type_name -> pb.RequestWrapInfo
|
||||
44, // 4: pb.Request.connection:type_name -> pb.Connection
|
||||
46, // 4: pb.Request.connection:type_name -> pb.Connection
|
||||
7, // 5: pb.Auth.lease_options:type_name -> pb.LeaseOptions
|
||||
46, // 6: pb.Auth.metadata:type_name -> pb.Auth.MetadataEntry
|
||||
50, // 7: pb.Auth.alias:type_name -> logical.Alias
|
||||
50, // 8: pb.Auth.group_aliases:type_name -> logical.Alias
|
||||
47, // 9: pb.TokenEntry.meta:type_name -> pb.TokenEntry.MetaEntry
|
||||
51, // 10: pb.LeaseOptions.issue_time:type_name -> google.protobuf.Timestamp
|
||||
48, // 6: pb.Auth.metadata:type_name -> pb.Auth.MetadataEntry
|
||||
52, // 7: pb.Auth.alias:type_name -> logical.Alias
|
||||
52, // 8: pb.Auth.group_aliases:type_name -> logical.Alias
|
||||
49, // 9: pb.TokenEntry.meta:type_name -> pb.TokenEntry.MetaEntry
|
||||
53, // 10: pb.LeaseOptions.issue_time:type_name -> google.protobuf.Timestamp
|
||||
7, // 11: pb.Secret.lease_options:type_name -> pb.LeaseOptions
|
||||
8, // 12: pb.Response.secret:type_name -> pb.Secret
|
||||
5, // 13: pb.Response.auth:type_name -> pb.Auth
|
||||
10, // 14: pb.Response.wrap_info:type_name -> pb.ResponseWrapInfo
|
||||
48, // 15: pb.Response.headers:type_name -> pb.Response.HeadersEntry
|
||||
51, // 16: pb.ResponseWrapInfo.creation_time:type_name -> google.protobuf.Timestamp
|
||||
50, // 15: pb.Response.headers:type_name -> pb.Response.HeadersEntry
|
||||
53, // 16: pb.ResponseWrapInfo.creation_time:type_name -> google.protobuf.Timestamp
|
||||
4, // 17: pb.HandleRequestArgs.request:type_name -> pb.Request
|
||||
9, // 18: pb.HandleRequestReply.response:type_name -> pb.Response
|
||||
2, // 19: pb.HandleRequestReply.err:type_name -> pb.ProtoError
|
||||
|
@ -3603,13 +3714,13 @@ var file_sdk_plugin_pb_backend_proto_depIDxs = []int32{
|
|||
3, // 21: pb.SpecialPathsReply.paths:type_name -> pb.Paths
|
||||
4, // 22: pb.HandleExistenceCheckArgs.request:type_name -> pb.Request
|
||||
2, // 23: pb.HandleExistenceCheckReply.err:type_name -> pb.ProtoError
|
||||
49, // 24: pb.SetupArgs.Config:type_name -> pb.SetupArgs.ConfigEntry
|
||||
51, // 24: pb.SetupArgs.Config:type_name -> pb.SetupArgs.ConfigEntry
|
||||
23, // 25: pb.StorageGetReply.entry:type_name -> pb.StorageEntry
|
||||
23, // 26: pb.StoragePutArgs.entry:type_name -> pb.StorageEntry
|
||||
10, // 27: pb.ResponseWrapDataReply.wrap_info:type_name -> pb.ResponseWrapInfo
|
||||
52, // 28: pb.EntityInfoReply.entity:type_name -> logical.Entity
|
||||
53, // 29: pb.GroupsForEntityReply.groups:type_name -> logical.Group
|
||||
54, // 30: pb.PluginEnvReply.plugin_environment:type_name -> logical.PluginEnvironment
|
||||
54, // 28: pb.EntityInfoReply.entity:type_name -> logical.Entity
|
||||
55, // 29: pb.GroupsForEntityReply.groups:type_name -> logical.Group
|
||||
56, // 30: pb.PluginEnvReply.plugin_environment:type_name -> logical.PluginEnvironment
|
||||
1, // 31: pb.Request.HeadersEntry.value:type_name -> pb.Header
|
||||
1, // 32: pb.Response.HeadersEntry.value:type_name -> pb.Header
|
||||
12, // 33: pb.Backend.HandleRequest:input_type -> pb.HandleRequestArgs
|
||||
|
@ -3635,31 +3746,33 @@ var file_sdk_plugin_pb_backend_proto_depIDxs = []int32{
|
|||
40, // 53: pb.SystemView.EntityInfo:input_type -> pb.EntityInfoArgs
|
||||
0, // 54: pb.SystemView.PluginEnv:input_type -> pb.Empty
|
||||
40, // 55: pb.SystemView.GroupsForEntity:input_type -> pb.EntityInfoArgs
|
||||
13, // 56: pb.Backend.HandleRequest:output_type -> pb.HandleRequestReply
|
||||
16, // 57: pb.Backend.SpecialPaths:output_type -> pb.SpecialPathsReply
|
||||
18, // 58: pb.Backend.HandleExistenceCheck:output_type -> pb.HandleExistenceCheckReply
|
||||
0, // 59: pb.Backend.Cleanup:output_type -> pb.Empty
|
||||
0, // 60: pb.Backend.InvalidateKey:output_type -> pb.Empty
|
||||
20, // 61: pb.Backend.Setup:output_type -> pb.SetupReply
|
||||
15, // 62: pb.Backend.Initialize:output_type -> pb.InitializeReply
|
||||
21, // 63: pb.Backend.Type:output_type -> pb.TypeReply
|
||||
25, // 64: pb.Storage.List:output_type -> pb.StorageListReply
|
||||
27, // 65: pb.Storage.Get:output_type -> pb.StorageGetReply
|
||||
29, // 66: pb.Storage.Put:output_type -> pb.StoragePutReply
|
||||
31, // 67: pb.Storage.Delete:output_type -> pb.StorageDeleteReply
|
||||
32, // 68: pb.SystemView.DefaultLeaseTTL:output_type -> pb.TTLReply
|
||||
32, // 69: pb.SystemView.MaxLeaseTTL:output_type -> pb.TTLReply
|
||||
33, // 70: pb.SystemView.Tainted:output_type -> pb.TaintedReply
|
||||
34, // 71: pb.SystemView.CachingDisabled:output_type -> pb.CachingDisabledReply
|
||||
35, // 72: pb.SystemView.ReplicationState:output_type -> pb.ReplicationStateReply
|
||||
37, // 73: pb.SystemView.ResponseWrapData:output_type -> pb.ResponseWrapDataReply
|
||||
38, // 74: pb.SystemView.MlockEnabled:output_type -> pb.MlockEnabledReply
|
||||
39, // 75: pb.SystemView.LocalMount:output_type -> pb.LocalMountReply
|
||||
41, // 76: pb.SystemView.EntityInfo:output_type -> pb.EntityInfoReply
|
||||
43, // 77: pb.SystemView.PluginEnv:output_type -> pb.PluginEnvReply
|
||||
42, // 78: pb.SystemView.GroupsForEntity:output_type -> pb.GroupsForEntityReply
|
||||
56, // [56:79] is the sub-list for method output_type
|
||||
33, // [33:56] is the sub-list for method input_type
|
||||
44, // 56: pb.SystemView.GeneratePasswordFromPolicy:input_type -> pb.GeneratePasswordFromPolicyRequest
|
||||
13, // 57: pb.Backend.HandleRequest:output_type -> pb.HandleRequestReply
|
||||
16, // 58: pb.Backend.SpecialPaths:output_type -> pb.SpecialPathsReply
|
||||
18, // 59: pb.Backend.HandleExistenceCheck:output_type -> pb.HandleExistenceCheckReply
|
||||
0, // 60: pb.Backend.Cleanup:output_type -> pb.Empty
|
||||
0, // 61: pb.Backend.InvalidateKey:output_type -> pb.Empty
|
||||
20, // 62: pb.Backend.Setup:output_type -> pb.SetupReply
|
||||
15, // 63: pb.Backend.Initialize:output_type -> pb.InitializeReply
|
||||
21, // 64: pb.Backend.Type:output_type -> pb.TypeReply
|
||||
25, // 65: pb.Storage.List:output_type -> pb.StorageListReply
|
||||
27, // 66: pb.Storage.Get:output_type -> pb.StorageGetReply
|
||||
29, // 67: pb.Storage.Put:output_type -> pb.StoragePutReply
|
||||
31, // 68: pb.Storage.Delete:output_type -> pb.StorageDeleteReply
|
||||
32, // 69: pb.SystemView.DefaultLeaseTTL:output_type -> pb.TTLReply
|
||||
32, // 70: pb.SystemView.MaxLeaseTTL:output_type -> pb.TTLReply
|
||||
33, // 71: pb.SystemView.Tainted:output_type -> pb.TaintedReply
|
||||
34, // 72: pb.SystemView.CachingDisabled:output_type -> pb.CachingDisabledReply
|
||||
35, // 73: pb.SystemView.ReplicationState:output_type -> pb.ReplicationStateReply
|
||||
37, // 74: pb.SystemView.ResponseWrapData:output_type -> pb.ResponseWrapDataReply
|
||||
38, // 75: pb.SystemView.MlockEnabled:output_type -> pb.MlockEnabledReply
|
||||
39, // 76: pb.SystemView.LocalMount:output_type -> pb.LocalMountReply
|
||||
41, // 77: pb.SystemView.EntityInfo:output_type -> pb.EntityInfoReply
|
||||
43, // 78: pb.SystemView.PluginEnv:output_type -> pb.PluginEnvReply
|
||||
42, // 79: pb.SystemView.GroupsForEntity:output_type -> pb.GroupsForEntityReply
|
||||
45, // 80: pb.SystemView.GeneratePasswordFromPolicy:output_type -> pb.GeneratePasswordFromPolicyReply
|
||||
57, // [57:81] is the sub-list for method output_type
|
||||
33, // [33:57] is the sub-list for method input_type
|
||||
33, // [33:33] is the sub-list for extension type_name
|
||||
33, // [33:33] is the sub-list for extension extendee
|
||||
0, // [0:33] is the sub-list for field type_name
|
||||
|
@ -4200,6 +4313,30 @@ func file_sdk_plugin_pb_backend_proto_init() {
|
|||
}
|
||||
}
|
||||
file_sdk_plugin_pb_backend_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeneratePasswordFromPolicyRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_sdk_plugin_pb_backend_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeneratePasswordFromPolicyReply); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_sdk_plugin_pb_backend_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Connection); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
|
@ -4218,7 +4355,7 @@ func file_sdk_plugin_pb_backend_proto_init() {
|
|||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_sdk_plugin_pb_backend_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 50,
|
||||
NumMessages: 52,
|
||||
NumExtensions: 0,
|
||||
NumServices: 3,
|
||||
},
|
||||
|
@ -4838,6 +4975,8 @@ type SystemViewClient interface {
|
|||
// GroupsForEntity returns the group membership information for the given
|
||||
// entity id
|
||||
GroupsForEntity(ctx context.Context, in *EntityInfoArgs, opts ...grpc.CallOption) (*GroupsForEntityReply, error)
|
||||
// GeneratePasswordFromPolicy generates a password from an existing password policy
|
||||
GeneratePasswordFromPolicy(ctx context.Context, in *GeneratePasswordFromPolicyRequest, opts ...grpc.CallOption) (*GeneratePasswordFromPolicyReply, error)
|
||||
}
|
||||
|
||||
type systemViewClient struct {
|
||||
|
@ -4947,6 +5086,15 @@ func (c *systemViewClient) GroupsForEntity(ctx context.Context, in *EntityInfoAr
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *systemViewClient) GeneratePasswordFromPolicy(ctx context.Context, in *GeneratePasswordFromPolicyRequest, opts ...grpc.CallOption) (*GeneratePasswordFromPolicyReply, error) {
|
||||
out := new(GeneratePasswordFromPolicyReply)
|
||||
err := c.cc.Invoke(ctx, "/pb.SystemView/GeneratePasswordFromPolicy", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SystemViewServer is the server API for SystemView service.
|
||||
type SystemViewServer interface {
|
||||
// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
|
||||
|
@ -4985,6 +5133,8 @@ type SystemViewServer interface {
|
|||
// GroupsForEntity returns the group membership information for the given
|
||||
// entity id
|
||||
GroupsForEntity(context.Context, *EntityInfoArgs) (*GroupsForEntityReply, error)
|
||||
// GeneratePasswordFromPolicy generates a password from an existing password policy
|
||||
GeneratePasswordFromPolicy(context.Context, *GeneratePasswordFromPolicyRequest) (*GeneratePasswordFromPolicyReply, error)
|
||||
}
|
||||
|
||||
// UnimplementedSystemViewServer can be embedded to have forward compatible implementations.
|
||||
|
@ -5024,6 +5174,9 @@ func (*UnimplementedSystemViewServer) PluginEnv(context.Context, *Empty) (*Plugi
|
|||
func (*UnimplementedSystemViewServer) GroupsForEntity(context.Context, *EntityInfoArgs) (*GroupsForEntityReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GroupsForEntity not implemented")
|
||||
}
|
||||
func (*UnimplementedSystemViewServer) GeneratePasswordFromPolicy(context.Context, *GeneratePasswordFromPolicyRequest) (*GeneratePasswordFromPolicyReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GeneratePasswordFromPolicy not implemented")
|
||||
}
|
||||
|
||||
func RegisterSystemViewServer(s *grpc.Server, srv SystemViewServer) {
|
||||
s.RegisterService(&_SystemView_serviceDesc, srv)
|
||||
|
@ -5227,6 +5380,24 @@ func _SystemView_GroupsForEntity_Handler(srv interface{}, ctx context.Context, d
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SystemView_GeneratePasswordFromPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GeneratePasswordFromPolicyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SystemViewServer).GeneratePasswordFromPolicy(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/pb.SystemView/GeneratePasswordFromPolicy",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SystemViewServer).GeneratePasswordFromPolicy(ctx, req.(*GeneratePasswordFromPolicyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _SystemView_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "pb.SystemView",
|
||||
HandlerType: (*SystemViewServer)(nil),
|
||||
|
@ -5275,6 +5446,10 @@ var _SystemView_serviceDesc = grpc.ServiceDesc{
|
|||
MethodName: "GroupsForEntity",
|
||||
Handler: _SystemView_GroupsForEntity_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GeneratePasswordFromPolicy",
|
||||
Handler: _SystemView_GeneratePasswordFromPolicy_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "sdk/plugin/pb/backend.proto",
|
||||
|
|
|
@ -554,12 +554,20 @@ message PluginEnvReply {
|
|||
string err = 2;
|
||||
}
|
||||
|
||||
message GeneratePasswordFromPolicyRequest {
|
||||
string policy_name = 1;
|
||||
}
|
||||
|
||||
message GeneratePasswordFromPolicyReply {
|
||||
string password = 1;
|
||||
}
|
||||
|
||||
// SystemView exposes system configuration information in a safe way for plugins
|
||||
// to consume. Plugins should implement the client for this service.
|
||||
service SystemView {
|
||||
// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
|
||||
rpc DefaultLeaseTTL(Empty) returns (TTLReply);
|
||||
|
||||
|
||||
// MaxLeaseTTL returns the max lease TTL set in Vault configuration; backend
|
||||
// authors should take care not to issue credentials that last longer than
|
||||
// this value, as Vault will revoke them
|
||||
|
@ -603,6 +611,9 @@ service SystemView {
|
|||
// GroupsForEntity returns the group membership information for the given
|
||||
// entity id
|
||||
rpc GroupsForEntity(EntityInfoArgs) returns (GroupsForEntityReply);
|
||||
|
||||
// GeneratePasswordFromPolicy generates a password from an existing password policy
|
||||
rpc GeneratePasswordFromPolicy(GeneratePasswordFromPolicyRequest) returns (GeneratePasswordFromPolicyReply);
|
||||
}
|
||||
|
||||
message Connection {
|
||||
|
|
|
@ -6,12 +6,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/license"
|
||||
"github.com/hashicorp/vault/sdk/helper/pluginutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/random"
|
||||
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/version"
|
||||
|
@ -327,3 +327,32 @@ func (d dynamicSystemView) PluginEnv(_ context.Context) (*logical.PluginEnvironm
|
|||
VaultVersion: version.GetVersion().Version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d dynamicSystemView) GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error) {
|
||||
if policyName == "" {
|
||||
return "", fmt.Errorf("missing password policy name")
|
||||
}
|
||||
|
||||
// Ensure there's a timeout on the context of some sort
|
||||
if _, hasTimeout := ctx.Deadline(); !hasTimeout {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
policyCfg, err := retrievePasswordPolicy(ctx, d.core.systemBarrierView, policyName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to retrieve password policy: %w", err)
|
||||
}
|
||||
|
||||
if policyCfg == nil {
|
||||
return "", fmt.Errorf("no password policy found")
|
||||
}
|
||||
|
||||
passPolicy, err := random.ParsePolicy(policyCfg.HCLPolicy)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("stored password policy is invalid: %w", err)
|
||||
}
|
||||
|
||||
return passPolicy.Generate(ctx, nil)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
ldapcred "github.com/hashicorp/vault/builtin/credential/ldap"
|
||||
|
@ -149,3 +155,147 @@ func TestIdentity_BackendTemplating(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicSystemView_GeneratePasswordFromPolicy_successful(t *testing.T) {
|
||||
policyName := "testpolicy"
|
||||
rawPolicy := map[string]interface{}{
|
||||
"policy": `length = 20
|
||||
rule "charset" {
|
||||
charset = "abcdefghijklmnopqrstuvwxyz"
|
||||
min_chars = 1
|
||||
}
|
||||
rule "charset" {
|
||||
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
min_chars = 1
|
||||
}
|
||||
rule "charset" {
|
||||
charset = "0123456789"
|
||||
min_chars = 1
|
||||
}`,
|
||||
}
|
||||
marshalledPolicy, err := json.Marshal(rawPolicy)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to set up test: unable to marshal raw policy to JSON: %s", err)
|
||||
}
|
||||
|
||||
testStorage := fakeBarrier{
|
||||
getEntry: &logical.StorageEntry{
|
||||
Key: getPasswordPolicyKey(policyName),
|
||||
Value: marshalledPolicy,
|
||||
},
|
||||
}
|
||||
|
||||
dsv := dynamicSystemView{
|
||||
core: &Core{
|
||||
systemBarrierView: NewBarrierView(testStorage, "sys/"),
|
||||
},
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
runeset := map[rune]bool{}
|
||||
runesFound := []rune{}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
actual, err := dsv.GeneratePasswordFromPolicy(ctx, policyName)
|
||||
if err != nil {
|
||||
t.Fatalf("no error expected, but got: %s", err)
|
||||
}
|
||||
for _, r := range actual {
|
||||
if runeset[r] {
|
||||
continue
|
||||
}
|
||||
runeset[r] = true
|
||||
runesFound = append(runesFound, r)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(runes(runesFound))
|
||||
|
||||
expectedRunes := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
sort.Sort(runes(expectedRunes)) // Sort it so they can be compared
|
||||
|
||||
if !reflect.DeepEqual(runesFound, expectedRunes) {
|
||||
t.Fatalf("Didn't find all characters from the charset\nActual : [%s]\nExpected: [%s]", string(runesFound), string(expectedRunes))
|
||||
}
|
||||
}
|
||||
|
||||
type runes []rune
|
||||
|
||||
func (r runes) Len() int { return len(r) }
|
||||
func (r runes) Less(i, j int) bool { return r[i] < r[j] }
|
||||
func (r runes) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
|
||||
func TestDynamicSystemView_GeneratePasswordFromPolicy_failed(t *testing.T) {
|
||||
type testCase struct {
|
||||
policyName string
|
||||
getEntry *logical.StorageEntry
|
||||
getErr error
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"no policy name": {
|
||||
policyName: "",
|
||||
},
|
||||
"no policy found": {
|
||||
policyName: "testpolicy",
|
||||
getEntry: nil,
|
||||
getErr: nil,
|
||||
},
|
||||
"error retrieving policy": {
|
||||
policyName: "testpolicy",
|
||||
getEntry: nil,
|
||||
getErr: fmt.Errorf("a test error"),
|
||||
},
|
||||
"saved policy is malformed": {
|
||||
policyName: "testpolicy",
|
||||
getEntry: &logical.StorageEntry{
|
||||
Key: getPasswordPolicyKey("testpolicy"),
|
||||
Value: []byte(`{"policy":"asdfahsdfasdf"}`),
|
||||
},
|
||||
getErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
testStorage := fakeBarrier{
|
||||
getEntry: test.getEntry,
|
||||
getErr: test.getErr,
|
||||
}
|
||||
|
||||
dsv := dynamicSystemView{
|
||||
core: &Core{
|
||||
systemBarrierView: NewBarrierView(testStorage, "sys/"),
|
||||
},
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
actualPassword, err := dsv.GeneratePasswordFromPolicy(ctx, test.policyName)
|
||||
if err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if actualPassword != "" {
|
||||
t.Fatalf("no password expected, got %s", actualPassword)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fakeBarrier struct {
|
||||
getEntry *logical.StorageEntry
|
||||
getErr error
|
||||
}
|
||||
|
||||
func (b fakeBarrier) Get(context.Context, string) (*logical.StorageEntry, error) {
|
||||
return b.getEntry, b.getErr
|
||||
}
|
||||
func (b fakeBarrier) List(context.Context, string) ([]string, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
func (b fakeBarrier) Put(context.Context, *logical.StorageEntry) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
func (b fakeBarrier) Delete(context.Context, string) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/parseutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/random"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/wrapping"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
|
@ -2065,6 +2066,183 @@ func (b *SystemBackend) handlePoliciesDelete(policyType PolicyType) framework.Op
|
|||
}
|
||||
}
|
||||
|
||||
type passwordPolicyConfig struct {
|
||||
HCLPolicy string `json:"policy"`
|
||||
}
|
||||
|
||||
func getPasswordPolicyKey(policyName string) string {
|
||||
return fmt.Sprintf("password_policy/%s", policyName)
|
||||
}
|
||||
|
||||
const (
|
||||
minPasswordLength = 4
|
||||
maxPasswordLength = 100
|
||||
)
|
||||
|
||||
// handlePoliciesPasswordSet saves/updates password policies
|
||||
func (*SystemBackend) handlePoliciesPasswordSet(ctx context.Context, req *logical.Request, data *framework.FieldData) (resp *logical.Response, err error) {
|
||||
policyName := data.Get("name").(string)
|
||||
if policyName == "" {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, "missing policy name")
|
||||
}
|
||||
|
||||
rawPolicy := data.Get("policy").(string)
|
||||
if rawPolicy == "" {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, "missing policy")
|
||||
}
|
||||
|
||||
// Optionally decode base64 string
|
||||
decodedPolicy, err := base64.StdEncoding.DecodeString(rawPolicy)
|
||||
if err == nil {
|
||||
rawPolicy = string(decodedPolicy)
|
||||
}
|
||||
|
||||
// Parse the policy to ensure that it's valid
|
||||
policy, err := random.ParsePolicy(rawPolicy)
|
||||
if err != nil {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("invalid password policy: %s", err))
|
||||
}
|
||||
|
||||
if policy.Length > maxPasswordLength || policy.Length < minPasswordLength {
|
||||
return nil, logical.CodedError(http.StatusBadRequest,
|
||||
fmt.Sprintf("passwords must be between %d and %d characters", minPasswordLength, maxPasswordLength))
|
||||
}
|
||||
|
||||
// Generate some passwords to ensure that we're confident that the policy isn't impossible
|
||||
timeout := 1 * time.Second
|
||||
genCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
attempts := 10
|
||||
failed := 0
|
||||
for i := 0; i < attempts; i++ {
|
||||
_, err = policy.Generate(genCtx, nil)
|
||||
if err != nil {
|
||||
failed++
|
||||
}
|
||||
}
|
||||
|
||||
if failed == attempts {
|
||||
return nil, logical.CodedError(http.StatusBadRequest,
|
||||
fmt.Sprintf("unable to generate password from provided policy in %s: are the rules impossible?", timeout))
|
||||
}
|
||||
|
||||
if failed > 0 {
|
||||
return nil, logical.CodedError(http.StatusBadRequest,
|
||||
fmt.Sprintf("failed to generate passwords %d times out of %d attempts in %s - is the policy too restrictive?", failed, attempts, timeout))
|
||||
}
|
||||
|
||||
cfg := passwordPolicyConfig{
|
||||
HCLPolicy: rawPolicy,
|
||||
}
|
||||
entry, err := logical.StorageEntryJSON(getPasswordPolicyKey(policyName), cfg)
|
||||
if err != nil {
|
||||
return nil, logical.CodedError(http.StatusInternalServerError, fmt.Sprintf("unable to save password policy: %s", err))
|
||||
}
|
||||
|
||||
err = req.Storage.Put(ctx, entry)
|
||||
if err != nil {
|
||||
return nil, logical.CodedError(http.StatusInternalServerError,
|
||||
fmt.Sprintf("failed to save policy to storage backend: %s", err))
|
||||
}
|
||||
|
||||
return logical.RespondWithStatusCode(nil, req, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// handlePoliciesPasswordGet retrieves a password policy if it exists
|
||||
func (*SystemBackend) handlePoliciesPasswordGet(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
policyName := data.Get("name").(string)
|
||||
if policyName == "" {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, "missing policy name")
|
||||
}
|
||||
|
||||
cfg, err := retrievePasswordPolicy(ctx, req.Storage, policyName)
|
||||
if err != nil {
|
||||
return nil, logical.CodedError(http.StatusInternalServerError, "failed to retrieve password policy")
|
||||
}
|
||||
if cfg == nil {
|
||||
return nil, logical.CodedError(http.StatusNotFound, "policy does not exist")
|
||||
}
|
||||
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"policy": cfg.HCLPolicy,
|
||||
},
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// retrievePasswordPolicy retrieves a password policy from the logical storage
|
||||
func retrievePasswordPolicy(ctx context.Context, storage logical.Storage, policyName string) (policyCfg *passwordPolicyConfig, err error) {
|
||||
entry, err := storage.Get(ctx, getPasswordPolicyKey(policyName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
policyCfg = &passwordPolicyConfig{}
|
||||
err = json.Unmarshal(entry.Value, &policyCfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal stored data: %w", err)
|
||||
}
|
||||
|
||||
return policyCfg, nil
|
||||
}
|
||||
|
||||
// handlePoliciesPasswordDelete deletes a password policy if it exists
|
||||
func (*SystemBackend) handlePoliciesPasswordDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
policyName := data.Get("name").(string)
|
||||
if policyName == "" {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, "missing policy name")
|
||||
}
|
||||
|
||||
err := req.Storage.Delete(ctx, getPasswordPolicyKey(policyName))
|
||||
if err != nil {
|
||||
return nil, logical.CodedError(http.StatusInternalServerError,
|
||||
fmt.Sprintf("failed to delete password policy: %s", err))
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handlePoliciesPasswordGenerate generates a password from the specified password policy
|
||||
func (*SystemBackend) handlePoliciesPasswordGenerate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
policyName := data.Get("name").(string)
|
||||
if policyName == "" {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, "missing policy name")
|
||||
}
|
||||
|
||||
cfg, err := retrievePasswordPolicy(ctx, req.Storage, policyName)
|
||||
if err != nil {
|
||||
return nil, logical.CodedError(http.StatusInternalServerError, "failed to retrieve password policy")
|
||||
}
|
||||
if cfg == nil {
|
||||
return nil, logical.CodedError(http.StatusNotFound, "policy does not exist")
|
||||
}
|
||||
|
||||
policy, err := random.ParsePolicy(cfg.HCLPolicy)
|
||||
if err != nil {
|
||||
return nil, logical.CodedError(http.StatusInternalServerError,
|
||||
"stored password policy configuration failed to parse")
|
||||
}
|
||||
|
||||
password, err := policy.Generate(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, logical.CodedError(http.StatusInternalServerError,
|
||||
fmt.Sprintf("failed to generate password from policy: %s", err))
|
||||
}
|
||||
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"password": password,
|
||||
},
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// handleAuditTable handles the "audit" endpoint to provide the audit table
|
||||
func (b *SystemBackend) handleAuditTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
b.Core.auditLock.RLock()
|
||||
|
@ -3836,6 +4014,11 @@ or delete a policy.
|
|||
"",
|
||||
},
|
||||
|
||||
"password-policy-name": {
|
||||
`The name of the password policy.`,
|
||||
"",
|
||||
},
|
||||
|
||||
"audit-hash": {
|
||||
"The hash of the given string via the given audit backend",
|
||||
"",
|
||||
|
|
|
@ -1463,6 +1463,61 @@ func (b *SystemBackend) policyPaths() []*framework.Path {
|
|||
HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]),
|
||||
HelpDescription: strings.TrimSpace(sysHelp["policy"][1]),
|
||||
},
|
||||
|
||||
{
|
||||
Pattern: "policies/password/(?P<name>.+)/generate$",
|
||||
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The name of the password policy.",
|
||||
},
|
||||
},
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.handlePoliciesPasswordGenerate,
|
||||
Summary: "Generate a password from an existing password policy.",
|
||||
},
|
||||
},
|
||||
|
||||
HelpSynopsis: "Generate a password from an existing password policy.",
|
||||
HelpDescription: "Generate a password from an existing password policy.",
|
||||
},
|
||||
|
||||
{
|
||||
Pattern: "policies/password/(?P<name>.+)$",
|
||||
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The name of the password policy.",
|
||||
},
|
||||
"policy": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The password policy",
|
||||
},
|
||||
},
|
||||
|
||||
Operations: map[logical.Operation]framework.OperationHandler{
|
||||
logical.UpdateOperation: &framework.PathOperation{
|
||||
Callback: b.handlePoliciesPasswordSet,
|
||||
Summary: "Add a new or update an existing password policy.",
|
||||
},
|
||||
logical.ReadOperation: &framework.PathOperation{
|
||||
Callback: b.handlePoliciesPasswordGet,
|
||||
Summary: "Retrieve an existing password policy.",
|
||||
},
|
||||
logical.DeleteOperation: &framework.PathOperation{
|
||||
Callback: b.handlePoliciesPasswordDelete,
|
||||
Summary: "Delete a password policy.",
|
||||
},
|
||||
},
|
||||
|
||||
HelpSynopsis: "Read, Modify, or Delete a password policy.",
|
||||
HelpDescription: "Read the rules of an existing password policy, create or update " +
|
||||
"the rules of a password policy, or delete a password policy.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
@ -24,6 +25,7 @@ import (
|
|||
"github.com/hashicorp/vault/sdk/framework"
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/random"
|
||||
"github.com/hashicorp/vault/sdk/helper/salt"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/version"
|
||||
|
@ -2730,3 +2732,776 @@ func TestSystemBackend_PathWildcardPreflight(t *testing.T) {
|
|||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePoliciesPasswordSet(t *testing.T) {
|
||||
type testCase struct {
|
||||
inputData *framework.FieldData
|
||||
|
||||
storage *logical.InmemStorage
|
||||
|
||||
expectedResp *logical.Response
|
||||
expectErr bool
|
||||
expectedStore map[string]*logical.StorageEntry
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"missing policy name": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"policy": `length = 20
|
||||
rule "charset" {
|
||||
charset="abcdefghij"
|
||||
}`,
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"missing policy": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"garbage policy": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
"policy": "hasdukfhiuashdfoiasjdf",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"storage failure": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
"policy": "length = 20\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"abcdefghij\"\n" +
|
||||
"}",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage).FailPut(true),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"impossible policy": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
"policy": "length = 20\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"a\"\n" +
|
||||
" min-chars = 30\n" +
|
||||
"}",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"not base64 encoded": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
"policy": "length = 20\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"abcdefghij\"\n" +
|
||||
"}",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
logical.HTTPContentType: "application/json",
|
||||
logical.HTTPStatusCode: http.StatusNoContent,
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
expectedStore: makeStorageMap(storageEntry(t, "testpolicy", "length = 20\n"+
|
||||
"rule \"charset\" {\n"+
|
||||
" charset=\"abcdefghij\"\n"+
|
||||
"}")),
|
||||
},
|
||||
"base64 encoded": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
"policy": base64Encode(
|
||||
"length = 20\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"abcdefghij\"\n" +
|
||||
"}"),
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
logical.HTTPContentType: "application/json",
|
||||
logical.HTTPStatusCode: http.StatusNoContent,
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
expectedStore: makeStorageMap(storageEntry(t, "testpolicy",
|
||||
"length = 20\n"+
|
||||
"rule \"charset\" {\n"+
|
||||
" charset=\"abcdefghij\"\n"+
|
||||
"}")),
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
req := &logical.Request{
|
||||
Storage: test.storage,
|
||||
}
|
||||
|
||||
b := &SystemBackend{}
|
||||
|
||||
actualResp, err := b.handlePoliciesPasswordSet(ctx, req, test.inputData)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualResp, test.expectedResp) {
|
||||
t.Fatalf("Actual response: %#v\nExpected response: %#v", actualResp, test.expectedResp)
|
||||
}
|
||||
|
||||
actualStore := LogicalToMap(t, ctx, test.storage)
|
||||
if !reflect.DeepEqual(actualStore, test.expectedStore) {
|
||||
t.Fatalf("Actual: %#v\nActual: %#v", dereferenceMap(actualStore), dereferenceMap(test.expectedStore))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePoliciesPasswordGet(t *testing.T) {
|
||||
type testCase struct {
|
||||
inputData *framework.FieldData
|
||||
|
||||
storage *logical.InmemStorage
|
||||
|
||||
expectedResp *logical.Response
|
||||
expectErr bool
|
||||
expectedStore map[string]*logical.StorageEntry
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"missing policy name": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"storage error": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage).FailGet(true),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"missing value": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"good value": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: makeStorage(t, storageEntry(t, "testpolicy",
|
||||
"length = 20\n"+
|
||||
"rule \"charset\" {\n"+
|
||||
" charset=\"abcdefghij\"\n"+
|
||||
"}")),
|
||||
|
||||
expectedResp: &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"policy": "length = 20\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"abcdefghij\"\n" +
|
||||
"}",
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
expectedStore: makeStorageMap(storageEntry(t, "testpolicy",
|
||||
"length = 20\n"+
|
||||
"rule \"charset\" {\n"+
|
||||
" charset=\"abcdefghij\"\n"+
|
||||
"}")),
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
req := &logical.Request{
|
||||
Storage: test.storage,
|
||||
}
|
||||
|
||||
b := &SystemBackend{}
|
||||
|
||||
actualResp, err := b.handlePoliciesPasswordGet(ctx, req, test.inputData)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualResp, test.expectedResp) {
|
||||
t.Fatalf("Actual response: %#v\nExpected response: %#v", actualResp, test.expectedResp)
|
||||
}
|
||||
|
||||
actualStore := LogicalToMap(t, ctx, test.storage)
|
||||
if !reflect.DeepEqual(actualStore, test.expectedStore) {
|
||||
t.Fatalf("Actual: %#v\nActual: %#v", dereferenceMap(actualStore), dereferenceMap(test.expectedStore))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePoliciesPasswordDelete(t *testing.T) {
|
||||
type testCase struct {
|
||||
inputData *framework.FieldData
|
||||
|
||||
storage logical.Storage
|
||||
|
||||
expectedResp *logical.Response
|
||||
expectErr bool
|
||||
expectedStore map[string]*logical.StorageEntry
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"missing policy name": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"storage failure": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage).FailDelete(true),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
expectedStore: map[string]*logical.StorageEntry{},
|
||||
},
|
||||
"successful delete": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: makeStorage(t,
|
||||
&logical.StorageEntry{
|
||||
Key: getPasswordPolicyKey("testpolicy"),
|
||||
Value: toJson(t,
|
||||
passwordPolicyConfig{
|
||||
HCLPolicy: "length = 18\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"ABCDEFGHIJ\"\n" +
|
||||
"}",
|
||||
}),
|
||||
},
|
||||
&logical.StorageEntry{
|
||||
Key: getPasswordPolicyKey("unrelated_policy"),
|
||||
Value: toJson(t,
|
||||
passwordPolicyConfig{
|
||||
HCLPolicy: "length = 20\n" +
|
||||
"rule \"charset\" {\n" +
|
||||
" charset=\"abcdefghij\"\n" +
|
||||
"}",
|
||||
}),
|
||||
},
|
||||
),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: false,
|
||||
expectedStore: makeStorageMap(storageEntry(t, "unrelated_policy",
|
||||
"length = 20\n"+
|
||||
"rule \"charset\" {\n"+
|
||||
" charset=\"abcdefghij\"\n"+
|
||||
"}")),
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
req := &logical.Request{
|
||||
Storage: test.storage,
|
||||
}
|
||||
|
||||
b := &SystemBackend{}
|
||||
|
||||
actualResp, err := b.handlePoliciesPasswordDelete(ctx, req, test.inputData)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualResp, test.expectedResp) {
|
||||
t.Fatalf("Actual response: %#v\nExpected response: %#v", actualResp, test.expectedResp)
|
||||
}
|
||||
|
||||
actualStore := LogicalToMap(t, ctx, test.storage)
|
||||
if !reflect.DeepEqual(actualStore, test.expectedStore) {
|
||||
t.Fatalf("Actual: %#v\nExpected: %#v", dereferenceMap(actualStore), dereferenceMap(test.expectedStore))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePoliciesPasswordGenerate(t *testing.T) {
|
||||
t.Run("errors", func(t *testing.T) {
|
||||
type testCase struct {
|
||||
timeout time.Duration
|
||||
inputData *framework.FieldData
|
||||
|
||||
storage *logical.InmemStorage
|
||||
|
||||
expectedResp *logical.Response
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"missing policy name": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
"storage failure": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage).FailGet(true),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
"policy does not exist": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: new(logical.InmemStorage),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
"policy improperly saved": {
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: makeStorage(t, storageEntry(t, "testpolicy", "badpolicy")),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
"failed to generate": {
|
||||
timeout: 0 * time.Second, // Timeout immediately
|
||||
inputData: passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
}),
|
||||
|
||||
storage: makeStorage(t, storageEntry(t, "testpolicy",
|
||||
"length = 20\n"+
|
||||
"rule \"charset\" {\n"+
|
||||
" charset=\"abcdefghij\"\n"+
|
||||
"}")),
|
||||
|
||||
expectedResp: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), test.timeout)
|
||||
defer cancel()
|
||||
|
||||
req := &logical.Request{
|
||||
Storage: test.storage,
|
||||
}
|
||||
|
||||
b := &SystemBackend{}
|
||||
|
||||
actualResp, err := b.handlePoliciesPasswordGenerate(ctx, req, test.inputData)
|
||||
if test.expectErr && err == nil {
|
||||
t.Fatalf("err expected, got nil")
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualResp, test.expectedResp) {
|
||||
t.Fatalf("Actual response: %#v\nExpected response: %#v", actualResp, test.expectedResp)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
policyEntry := storageEntry(t, "testpolicy",
|
||||
"length = 20\n"+
|
||||
"rule \"charset\" {\n"+
|
||||
" charset=\"abcdefghij\"\n"+
|
||||
"}")
|
||||
storage := makeStorage(t, policyEntry)
|
||||
|
||||
inputData := passwordPoliciesFieldData(map[string]interface{}{
|
||||
"name": "testpolicy",
|
||||
})
|
||||
|
||||
expectedResp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
// Doesn't include the password as that's pulled out and compared separately
|
||||
},
|
||||
}
|
||||
|
||||
// Password assertions
|
||||
expectedPassLen := 20
|
||||
rules := []random.Rule{
|
||||
random.CharsetRule{
|
||||
Charset: []rune("abcdefghij"),
|
||||
MinChars: expectedPassLen,
|
||||
},
|
||||
}
|
||||
|
||||
// Run the test a bunch of times to help ensure we don't have flaky behavior
|
||||
for i := 0; i < 1000; i++ {
|
||||
req := &logical.Request{
|
||||
Storage: storage,
|
||||
}
|
||||
|
||||
b := &SystemBackend{}
|
||||
|
||||
actualResp, err := b.handlePoliciesPasswordGenerate(ctx, req, inputData)
|
||||
if err != nil {
|
||||
t.Fatalf("no error expected, got: %s", err)
|
||||
}
|
||||
|
||||
assert(t, actualResp != nil, "response is nil")
|
||||
assert(t, actualResp.Data != nil, "expected data, got nil")
|
||||
assertHasKey(t, actualResp.Data, "password", "password key not found in data")
|
||||
assertIsString(t, actualResp.Data["password"], "password key should have a string value")
|
||||
password := actualResp.Data["password"].(string)
|
||||
|
||||
// Delete the password so the rest of the response can be compared
|
||||
delete(actualResp.Data, "password")
|
||||
assert(t, reflect.DeepEqual(actualResp, expectedResp), "Actual response: %#v\nExpected response: %#v", actualResp, expectedResp)
|
||||
|
||||
// Check to make sure the password is correctly formatted
|
||||
passwordLength := len([]rune(password))
|
||||
if passwordLength != expectedPassLen {
|
||||
t.Fatalf("password is %d characters but should be %d", passwordLength, expectedPassLen)
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
if !rule.Pass([]rune(password)) {
|
||||
t.Fatalf("password %s does not have the correct characters", password)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func assert(t *testing.T, pass bool, f string, vals ...interface{}) {
|
||||
t.Helper()
|
||||
if !pass {
|
||||
t.Fatalf(f, vals...)
|
||||
}
|
||||
}
|
||||
|
||||
func assertHasKey(t *testing.T, m map[string]interface{}, key string, f string, vals ...interface{}) {
|
||||
t.Helper()
|
||||
_, exists := m[key]
|
||||
if !exists {
|
||||
t.Fatalf(f, vals...)
|
||||
}
|
||||
}
|
||||
|
||||
func assertIsString(t *testing.T, val interface{}, f string, vals ...interface{}) {
|
||||
t.Helper()
|
||||
_, ok := val.(string)
|
||||
if !ok {
|
||||
t.Fatalf(f, vals...)
|
||||
}
|
||||
}
|
||||
|
||||
func passwordPoliciesFieldData(raw map[string]interface{}) *framework.FieldData {
|
||||
return &framework.FieldData{
|
||||
Raw: raw,
|
||||
Schema: map[string]*framework.FieldSchema{
|
||||
"name": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The name of the password policy.",
|
||||
},
|
||||
"policy": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The password policy",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func base64Encode(data string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(data))
|
||||
}
|
||||
|
||||
func toJson(t *testing.T, val interface{}) []byte {
|
||||
t.Helper()
|
||||
|
||||
b, err := jsonutil.EncodeJSON(val)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to marshal to JSON: %s", err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func storageEntry(t *testing.T, key string, policy string) *logical.StorageEntry {
|
||||
return &logical.StorageEntry{
|
||||
Key: getPasswordPolicyKey(key),
|
||||
Value: toJson(t, passwordPolicyConfig{
|
||||
HCLPolicy: policy,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func makeStorageMap(entries ...*logical.StorageEntry) map[string]*logical.StorageEntry {
|
||||
m := map[string]*logical.StorageEntry{}
|
||||
for _, entry := range entries {
|
||||
m[entry.Key] = entry
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func dereferenceMap(store map[string]*logical.StorageEntry) map[string]interface{} {
|
||||
m := map[string]interface{}{}
|
||||
|
||||
for k, v := range store {
|
||||
m[k] = map[string]string{
|
||||
"Key": v.Key,
|
||||
"Value": string(v.Value),
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type walkFunc func(*logical.StorageEntry) error
|
||||
|
||||
// WalkLogicalStorage applies the provided walkFunc against each entry in the logical storage.
|
||||
// This operates as a breadth first search.
|
||||
// TODO: Figure out a place for this to live permanently. This is generic and should be in a helper package somewhere.
|
||||
// At the time of writing, none of these locations work due to import cycles:
|
||||
// - vault/helper/testhelpers
|
||||
// - vault/helper/testhelpers/logical
|
||||
// - vault/helper/testhelpers/teststorage
|
||||
func WalkLogicalStorage(ctx context.Context, store logical.Storage, walker walkFunc) (err error) {
|
||||
if store == nil {
|
||||
return fmt.Errorf("no storage provided")
|
||||
}
|
||||
if walker == nil {
|
||||
return fmt.Errorf("no walk function provided")
|
||||
}
|
||||
|
||||
keys, err := store.List(ctx, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list root keys: %w", err)
|
||||
}
|
||||
|
||||
// Non-recursive breadth-first search through all keys
|
||||
for i := 0; i < len(keys); i++ {
|
||||
key := keys[i]
|
||||
|
||||
entry, err := store.Get(ctx, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve key at [%s]: %w", key, err)
|
||||
}
|
||||
if entry != nil {
|
||||
err = walker(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(key, "/") {
|
||||
// Directory
|
||||
subkeys, err := store.List(ctx, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list keys at [%s]: %w", key, err)
|
||||
}
|
||||
|
||||
// Append the sub-keys to the keys slice so it searches into the sub-directory
|
||||
for _, subkey := range subkeys {
|
||||
// Avoids infinite loop if the subkey is empty which then repeats indefinitely
|
||||
if subkey == "" {
|
||||
continue
|
||||
}
|
||||
subkey = fmt.Sprintf("%s%s", key, subkey)
|
||||
keys = append(keys, subkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogicalToMap retrieves all entries in the store and returns them as a map of key -> StorageEntry
|
||||
func LogicalToMap(t *testing.T, ctx context.Context, store logical.Storage) (data map[string]*logical.StorageEntry) {
|
||||
data = map[string]*logical.StorageEntry{}
|
||||
f := func(entry *logical.StorageEntry) error {
|
||||
data[entry.Key] = entry
|
||||
return nil
|
||||
}
|
||||
|
||||
err := WalkLogicalStorage(ctx, store, f)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to walk the storage: %s", err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// Ensure the WalkLogicalStorage function works
|
||||
func TestWalkLogicalStorage(t *testing.T) {
|
||||
type testCase struct {
|
||||
entries []*logical.StorageEntry
|
||||
}
|
||||
|
||||
tests := map[string]testCase{
|
||||
"no entries": {
|
||||
entries: []*logical.StorageEntry{},
|
||||
},
|
||||
"one entry": {
|
||||
entries: []*logical.StorageEntry{
|
||||
{
|
||||
Key: "root",
|
||||
},
|
||||
},
|
||||
},
|
||||
"many entries": {
|
||||
entries: []*logical.StorageEntry{
|
||||
// Alphabetical, breadth-first
|
||||
{Key: "bar"},
|
||||
{Key: "foo"},
|
||||
{Key: "bar/sub-bar1"},
|
||||
{Key: "bar/sub-bar2"},
|
||||
{Key: "foo/sub-foo1"},
|
||||
{Key: "foo/sub-foo2"},
|
||||
{Key: "foo/sub-foo3"},
|
||||
{Key: "bar/sub-bar1/sub-sub-bar1"},
|
||||
{Key: "bar/sub-bar1/sub-sub-bar2"},
|
||||
{Key: "bar/sub-bar2/sub-sub-bar1"},
|
||||
{Key: "foo/sub-foo1/sub-sub-foo1"},
|
||||
{Key: "foo/sub-foo2/sub-sub-foo1"},
|
||||
{Key: "foo/sub-foo3/sub-sub-foo1"},
|
||||
{Key: "foo/sub-foo3/sub-sub-foo2"},
|
||||
},
|
||||
},
|
||||
"sub key without root key": {
|
||||
entries: []*logical.StorageEntry{
|
||||
{Key: "foo/bar/baz"},
|
||||
},
|
||||
},
|
||||
"key with trailing slash": {
|
||||
entries: []*logical.StorageEntry{
|
||||
{Key: "foo/"},
|
||||
},
|
||||
},
|
||||
"double slash": {
|
||||
entries: []*logical.StorageEntry{
|
||||
{Key: "foo//"},
|
||||
{Key: "foo//bar"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
store := makeStorage(t, test.entries...)
|
||||
|
||||
actualEntries := []*logical.StorageEntry{}
|
||||
f := func(entry *logical.StorageEntry) error {
|
||||
actualEntries = append(actualEntries, entry)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := WalkLogicalStorage(ctx, store, f)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to walk storage: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actualEntries, test.entries) {
|
||||
t.Fatalf("Actual: %#v\nExpected: %#v", actualEntries, test.entries)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func makeStorage(t *testing.T, entries ...*logical.StorageEntry) *logical.InmemStorage {
|
||||
t.Helper()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
store := new(logical.InmemStorage)
|
||||
|
||||
for _, entry := range entries {
|
||||
err := store.Put(ctx, entry)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to load test storage: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ require (
|
|||
github.com/hashicorp/go-retryablehttp v0.6.2
|
||||
github.com/hashicorp/go-rootcerts v1.0.1
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200514144402-4bfac290c352
|
||||
github.com/mitchellh/mapstructure v1.2.2
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
|
||||
gopkg.in/square/go-jose.v2 v2.3.1
|
||||
|
|
|
@ -75,8 +75,8 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
|||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
|
@ -95,6 +95,7 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo
|
|||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
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)
|
||||
}
|
||||
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
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ruleConstructor func(map[string]interface{}) (Rule, error)
|
||||
|
||||
var (
|
||||
// defaultRuleNameMapping is the default mapping of HCL rule names to the appropriate rule constructor.
|
||||
// Add to this map when adding a new Rule type to be recognized in HCL.
|
||||
defaultRuleNameMapping = map[string]ruleConstructor{
|
||||
"charset": ParseCharset,
|
||||
}
|
||||
|
||||
defaultRegistry = Registry{
|
||||
Rules: defaultRuleNameMapping,
|
||||
}
|
||||
)
|
||||
|
||||
// Registry of HCL rule names to rule constructors.
|
||||
type Registry struct {
|
||||
// Rules maps names of rules to a constructor for the rule
|
||||
Rules map[string]ruleConstructor
|
||||
}
|
||||
|
||||
func (r Registry) parseRule(ruleType string, ruleData map[string]interface{}) (rule Rule, err error) {
|
||||
constructor, exists := r.Rules[ruleType]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("unrecognized rule type %s", ruleType)
|
||||
}
|
||||
|
||||
rule, err = constructor(ruleData)
|
||||
return rule, err
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// Rule to assert on string values.
|
||||
type Rule interface {
|
||||
// Pass should return true if the provided value passes any assertions this Rule is making.
|
||||
Pass(value []rune) bool
|
||||
|
||||
// Type returns the name of the rule as associated in the registry
|
||||
Type() string
|
||||
}
|
||||
|
||||
// CharsetRule requires a certain number of characters from the specified charset.
|
||||
type CharsetRule struct {
|
||||
// CharsetRule is the list of rules that candidate strings must contain a minimum number of.
|
||||
Charset runes `mapstructure:"charset" json:"charset"`
|
||||
|
||||
// MinChars indicates the minimum (inclusive) number of characters from the charset that should appear in the string.
|
||||
MinChars int `mapstructure:"min-chars" json:"min-chars"`
|
||||
}
|
||||
|
||||
// ParseCharset from the provided data map. The data map is expected to be parsed from HCL.
|
||||
func ParseCharset(data map[string]interface{}) (rule Rule, err error) {
|
||||
cr := &CharsetRule{}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: cr,
|
||||
DecodeHook: stringToRunesFunc,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode charset restriction: %w", err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse charset restriction: %w", err)
|
||||
}
|
||||
|
||||
return *cr, nil
|
||||
}
|
||||
|
||||
func (c CharsetRule) Type() string {
|
||||
return "charset"
|
||||
}
|
||||
|
||||
// Chars returns the charset that this rule is looking for.
|
||||
func (c CharsetRule) Chars() []rune {
|
||||
return c.Charset
|
||||
}
|
||||
|
||||
func (c CharsetRule) MinLength() int {
|
||||
return c.MinChars
|
||||
}
|
||||
|
||||
// Pass returns true if the provided candidate string has a minimum number of chars in it.
|
||||
// This adheres to the Rule interface
|
||||
func (c CharsetRule) Pass(value []rune) bool {
|
||||
if c.MinChars <= 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
count := 0
|
||||
for _, r := range value {
|
||||
// charIn is sometimes faster than a map lookup because the data is so small
|
||||
// This is being kept rather than converted to a map to keep the code cleaner,
|
||||
// otherwise there would need to be additional parsing logic.
|
||||
if charIn(r, c.Charset) {
|
||||
count++
|
||||
if count >= c.MinChars {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func charIn(search rune, charset []rune) bool {
|
||||
for _, r := range charset {
|
||||
if search == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
88
vendor/github.com/hashicorp/vault/sdk/helper/random/serializing.go
generated
vendored
Normal file
88
vendor/github.com/hashicorp/vault/sdk/helper/random/serializing.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// serializableRules is a slice of rules that can be marshalled to JSON in an HCL format
|
||||
type serializableRules []Rule
|
||||
|
||||
// MarshalJSON in an HCL-friendly way
|
||||
func (r serializableRules) MarshalJSON() (b []byte, err error) {
|
||||
// Example:
|
||||
// [
|
||||
// {
|
||||
// "testrule": [
|
||||
// {
|
||||
// "string": "teststring",
|
||||
// "int": 123
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// "charset": [
|
||||
// {
|
||||
// "charset": "abcde",
|
||||
// "min-chars": 2
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
data := []map[string][]map[string]interface{}{} // Totally not confusing at all
|
||||
for _, rule := range r {
|
||||
ruleData := map[string]interface{}{}
|
||||
err = mapstructure.Decode(rule, &ruleData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode rule: %w", err)
|
||||
}
|
||||
|
||||
ruleMap := map[string][]map[string]interface{}{
|
||||
rule.Type(): []map[string]interface{}{
|
||||
ruleData,
|
||||
},
|
||||
}
|
||||
data = append(data, ruleMap)
|
||||
}
|
||||
|
||||
b, err = json.Marshal(data)
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (r *serializableRules) UnmarshalJSON(data []byte) (err error) {
|
||||
mapData := []map[string]interface{}{}
|
||||
err = json.Unmarshal(data, &mapData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rules, err := parseRules(defaultRegistry, mapData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = rules
|
||||
return nil
|
||||
}
|
||||
|
||||
type runes []rune
|
||||
|
||||
func (r runes) Len() int { return len(r) }
|
||||
func (r runes) Less(i, j int) bool { return r[i] < r[j] }
|
||||
func (r runes) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
||||
|
||||
// MarshalJSON converts the runes to a string for smaller JSON and easier readability
|
||||
func (r runes) MarshalJSON() (b []byte, err error) {
|
||||
return json.Marshal(string(r))
|
||||
}
|
||||
|
||||
// UnmarshalJSON converts a string to []rune
|
||||
func (r *runes) UnmarshalJSON(data []byte) (err error) {
|
||||
var str string
|
||||
err = json.Unmarshal(data, &str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = []rune(str)
|
||||
return nil
|
||||
}
|
302
vendor/github.com/hashicorp/vault/sdk/helper/random/string_generator.go
generated
vendored
Normal file
302
vendor/github.com/hashicorp/vault/sdk/helper/random/string_generator.go
generated
vendored
Normal file
|
@ -0,0 +1,302 @@
|
|||
package random
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
var (
|
||||
LowercaseCharset = sortCharset("abcdefghijklmnopqrstuvwxyz")
|
||||
UppercaseCharset = sortCharset("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
NumericCharset = sortCharset("0123456789")
|
||||
FullSymbolCharset = sortCharset("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
|
||||
ShortSymbolCharset = sortCharset("-")
|
||||
|
||||
AlphabeticCharset = sortCharset(UppercaseCharset + LowercaseCharset)
|
||||
AlphaNumericCharset = sortCharset(AlphabeticCharset + NumericCharset)
|
||||
AlphaNumericShortSymbolCharset = sortCharset(AlphaNumericCharset + ShortSymbolCharset)
|
||||
AlphaNumericFullSymbolCharset = sortCharset(AlphaNumericCharset + FullSymbolCharset)
|
||||
|
||||
LowercaseRuneset = []rune(LowercaseCharset)
|
||||
UppercaseRuneset = []rune(UppercaseCharset)
|
||||
NumericRuneset = []rune(NumericCharset)
|
||||
FullSymbolRuneset = []rune(FullSymbolCharset)
|
||||
ShortSymbolRuneset = []rune(ShortSymbolCharset)
|
||||
|
||||
AlphabeticRuneset = []rune(AlphabeticCharset)
|
||||
AlphaNumericRuneset = []rune(AlphaNumericCharset)
|
||||
AlphaNumericShortSymbolRuneset = []rune(AlphaNumericShortSymbolCharset)
|
||||
AlphaNumericFullSymbolRuneset = []rune(AlphaNumericFullSymbolCharset)
|
||||
|
||||
// DefaultStringGenerator has reasonable default rules for generating strings
|
||||
DefaultStringGenerator = StringGenerator{
|
||||
Length: 20,
|
||||
Rules: []Rule{
|
||||
CharsetRule{
|
||||
Charset: LowercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: UppercaseRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: NumericRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
CharsetRule{
|
||||
Charset: ShortSymbolRuneset,
|
||||
MinChars: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func sortCharset(chars string) string {
|
||||
r := runes(chars)
|
||||
sort.Sort(r)
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// StringGenerator generats random strings from the provided charset & adhering to a set of rules. The set of rules
|
||||
// are things like CharsetRule which requires a certain number of characters from a sub-charset.
|
||||
type StringGenerator struct {
|
||||
// Length of the string to generate.
|
||||
Length int `mapstructure:"length" json:"length"`
|
||||
|
||||
// Rules the generated strings must adhere to.
|
||||
Rules serializableRules `mapstructure:"-" json:"rule"` // This is "rule" in JSON so it matches the HCL property type
|
||||
|
||||
// CharsetRule to choose runes from. This is computed from the rules, not directly configurable
|
||||
charset runes
|
||||
}
|
||||
|
||||
// Generate a random string from the charset and adhering to the provided rules.
|
||||
// The io.Reader is optional. If not provided, it will default to the reader from crypto/rand
|
||||
func (g *StringGenerator) Generate(ctx context.Context, rng io.Reader) (str string, err error) {
|
||||
if _, hasTimeout := ctx.Deadline(); !hasTimeout {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, 1*time.Second) // Ensure there's a timeout on the context
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// Ensure the generator is configured well since it may be manually created rather than parsed from HCL
|
||||
err = g.validateConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", fmt.Errorf("timed out generating string")
|
||||
default:
|
||||
str, err = g.generate(rng)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if str == "" {
|
||||
continue LOOP
|
||||
}
|
||||
return str, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *StringGenerator) generate(rng io.Reader) (str string, err error) {
|
||||
// If performance improvements need to be made, this can be changed to read a batch of
|
||||
// potential strings at once rather than one at a time. This will significantly
|
||||
// improve performance, but at the cost of added complexity.
|
||||
candidate, err := randomRunes(rng, g.charset, g.Length)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to generate random characters: %w", err)
|
||||
}
|
||||
|
||||
for _, rule := range g.Rules {
|
||||
if !rule.Pass(candidate) {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
// Passed all rules
|
||||
return string(candidate), nil
|
||||
}
|
||||
|
||||
const (
|
||||
// maxCharsetLen is the maximum length a charset is allowed to be when generating a candidate string.
|
||||
// This is the total number of numbers available for selecting an index out of the charset slice.
|
||||
maxCharsetLen = 256
|
||||
)
|
||||
|
||||
// randomRunes creates a random string based on the provided charset. The charset is limited to 255 characters, but
|
||||
// could be expanded if needed. Expanding the maximum charset size will decrease performance because it will need to
|
||||
// combine bytes into a larger integer using binary.BigEndian.Uint16() function.
|
||||
func randomRunes(rng io.Reader, charset []rune, length int) (candidate []rune, err error) {
|
||||
if len(charset) == 0 {
|
||||
return nil, fmt.Errorf("no charset specified")
|
||||
}
|
||||
if len(charset) > maxCharsetLen {
|
||||
return nil, fmt.Errorf("charset is too long: limited to %d characters", math.MaxUint8)
|
||||
}
|
||||
if length <= 0 {
|
||||
return nil, fmt.Errorf("unable to generate a zero or negative length runeset")
|
||||
}
|
||||
|
||||
// This can't always select indexes from [0-maxCharsetLen) because it could introduce bias to the character selection.
|
||||
// For instance, if the length of the charset is [a-zA-Z0-9-] (length of 63):
|
||||
// RNG ranges: [0-62][63-125][126-188][189-251] will equally select from the entirety of the charset. However,
|
||||
// the RNG values [252-255] will select the first 4 characters of the charset while ignoring the remaining 59.
|
||||
// This results in a bias towards the front of the charset.
|
||||
//
|
||||
// To avoid this, we determine the largest integer multiplier of the charset length that is <= maxCharsetLen
|
||||
// For instance, if the maxCharsetLen is 256 (the size of one byte) and the charset is length 63, the multiplier
|
||||
// equals 4:
|
||||
// 256/63 => 4.06
|
||||
// Trunc(4.06) => 4
|
||||
// Multiply by the charset length
|
||||
// Subtract 1 to account for 0-based counting and you get the max index value: 251
|
||||
maxAllowedRNGValue := (maxCharsetLen/len(charset))*len(charset) - 1
|
||||
|
||||
// rngBufferMultiplier increases the size of the RNG buffer to account for lost
|
||||
// indexes due to the maxAllowedRNGValue
|
||||
rngBufferMultiplier := 1.0
|
||||
|
||||
// Don't set a multiplier if we are able to use the entire range of indexes
|
||||
if maxAllowedRNGValue < maxCharsetLen {
|
||||
// Anything more complicated than an arbitrary percentage appears to have little practical performance benefit
|
||||
rngBufferMultiplier = 1.5
|
||||
}
|
||||
|
||||
// Default to the standard crypto reader if one isn't provided
|
||||
if rng == nil {
|
||||
rng = rand.Reader
|
||||
}
|
||||
|
||||
charsetLen := byte(len(charset))
|
||||
|
||||
runes := make([]rune, 0, length)
|
||||
|
||||
for len(runes) < length {
|
||||
// Generate a bunch of indexes
|
||||
data := make([]byte, int(float64(length)*rngBufferMultiplier))
|
||||
numBytes, err := rng.Read(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Append characters until either we're out of indexes or the length is long enough
|
||||
for i := 0; i < numBytes; i++ {
|
||||
// Be careful to ensure that maxAllowedRNGValue isn't >= 256 as it will overflow and this
|
||||
// comparison will prevent characters from being selected from the charset
|
||||
if data[i] > byte(maxAllowedRNGValue) {
|
||||
continue
|
||||
}
|
||||
|
||||
index := data[i]
|
||||
if len(charset) != maxCharsetLen {
|
||||
index = index % charsetLen
|
||||
}
|
||||
r := charset[index]
|
||||
runes = append(runes, r)
|
||||
|
||||
if len(runes) == length {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return runes, nil
|
||||
}
|
||||
|
||||
// validateConfig of the generator to ensure that we can successfully generate a string.
|
||||
func (g *StringGenerator) validateConfig() (err error) {
|
||||
merr := &multierror.Error{}
|
||||
|
||||
// Ensure the sum of minimum lengths in the rules doesn't exceed the length specified
|
||||
minLen := getMinLength(g.Rules)
|
||||
if g.Length <= 0 {
|
||||
merr = multierror.Append(merr, fmt.Errorf("length must be > 0"))
|
||||
} else if g.Length < minLen {
|
||||
merr = multierror.Append(merr, fmt.Errorf("specified rules require at least %d characters but %d is specified", minLen, g.Length))
|
||||
}
|
||||
|
||||
// Ensure we have a charset & all characters are printable
|
||||
if len(g.charset) == 0 {
|
||||
// Yes this is mutating the generator but this is done so we don't have to compute this on every generation
|
||||
g.charset = getChars(g.Rules)
|
||||
}
|
||||
if len(g.charset) == 0 {
|
||||
merr = multierror.Append(merr, fmt.Errorf("no charset specified"))
|
||||
} else {
|
||||
for _, r := range g.charset {
|
||||
if !unicode.IsPrint(r) {
|
||||
merr = multierror.Append(merr, fmt.Errorf("non-printable character in charset"))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// getMinLength from the rules using the optional interface: `MinLength() int`
|
||||
func getMinLength(rules []Rule) (minLen int) {
|
||||
type minLengthProvider interface {
|
||||
MinLength() int
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
mlp, ok := rule.(minLengthProvider)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
minLen += mlp.MinLength()
|
||||
}
|
||||
return minLen
|
||||
}
|
||||
|
||||
// getChars from the rules using the optional interface: `Chars() []rune`
|
||||
func getChars(rules []Rule) (chars []rune) {
|
||||
type charsetProvider interface {
|
||||
Chars() []rune
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
cp, ok := rule.(charsetProvider)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
chars = append(chars, cp.Chars()...)
|
||||
}
|
||||
return deduplicateRunes(chars)
|
||||
}
|
||||
|
||||
// deduplicateRunes returns a new slice of sorted & de-duplicated runes
|
||||
func deduplicateRunes(original []rune) (deduped []rune) {
|
||||
if len(original) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := map[rune]bool{}
|
||||
dedupedRunes := []rune(nil)
|
||||
|
||||
for _, r := range original {
|
||||
if m[r] {
|
||||
continue
|
||||
}
|
||||
m[r] = true
|
||||
dedupedRunes = append(dedupedRunes, r)
|
||||
}
|
||||
|
||||
// They don't have to be sorted, but this is being done to make the charset easier to visualize
|
||||
sort.Sort(runes(dedupedRunes))
|
||||
return dedupedRunes
|
||||
}
|
|
@ -62,6 +62,26 @@ func (s *InmemStorage) Underlying() *inmem.InmemBackend {
|
|||
return s.underlying.(*inmem.InmemBackend)
|
||||
}
|
||||
|
||||
func (s *InmemStorage) FailPut(fail bool) *InmemStorage {
|
||||
s.Underlying().FailPut(fail)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *InmemStorage) FailGet(fail bool) *InmemStorage {
|
||||
s.Underlying().FailGet(fail)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *InmemStorage) FailDelete(fail bool) *InmemStorage {
|
||||
s.Underlying().FailDelete(fail)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *InmemStorage) FailList(fail bool) *InmemStorage {
|
||||
s.Underlying().FailList(fail)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *InmemStorage) init() {
|
||||
s.underlying, _ = inmem.NewInmem(nil, nil)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package logical
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||
|
@ -68,6 +70,15 @@ type SystemView interface {
|
|||
|
||||
// PluginEnv returns Vault environment information used by plugins
|
||||
PluginEnv(context.Context) (*PluginEnvironment, error)
|
||||
|
||||
// GeneratePasswordFromPolicy generates a password from the policy referenced.
|
||||
// If the policy does not exist, this will return an error.
|
||||
GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error)
|
||||
}
|
||||
|
||||
type PasswordPolicy interface {
|
||||
// Generate a random password
|
||||
Generate(context.Context, io.Reader) (string, error)
|
||||
}
|
||||
|
||||
type ExtendedSystemView interface {
|
||||
|
@ -90,6 +101,7 @@ type StaticSystemView struct {
|
|||
Features license.Features
|
||||
VaultVersion string
|
||||
PluginEnvironment *PluginEnvironment
|
||||
PasswordPolicies map[string]PasswordPolicy
|
||||
}
|
||||
|
||||
type noopAuditor struct{}
|
||||
|
@ -165,3 +177,20 @@ func (d StaticSystemView) HasFeature(feature license.Features) bool {
|
|||
func (d StaticSystemView) PluginEnv(_ context.Context) (*PluginEnvironment, error) {
|
||||
return d.PluginEnvironment, nil
|
||||
}
|
||||
|
||||
func (d StaticSystemView) GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", fmt.Errorf("context timed out")
|
||||
default:
|
||||
}
|
||||
|
||||
if d.PasswordPolicies == nil {
|
||||
return "", fmt.Errorf("password policy not found")
|
||||
}
|
||||
policy, exists := d.PasswordPolicies[policyName]
|
||||
if !exists {
|
||||
return "", fmt.Errorf("password policy not found")
|
||||
}
|
||||
return policy.Generate(ctx, nil)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import (
|
|||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/hashicorp/vault/sdk/plugin/pb"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func newGRPCSystemView(conn *grpc.ClientConn) *gRPCSystemViewClient {
|
||||
|
@ -161,6 +163,17 @@ func (s *gRPCSystemViewClient) PluginEnv(ctx context.Context) (*logical.PluginEn
|
|||
return reply.PluginEnvironment, nil
|
||||
}
|
||||
|
||||
func (s *gRPCSystemViewClient) GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error) {
|
||||
req := &pb.GeneratePasswordFromPolicyRequest{
|
||||
PolicyName: policyName,
|
||||
}
|
||||
resp, err := s.client.GeneratePasswordFromPolicy(ctx, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Password, nil
|
||||
}
|
||||
|
||||
type gRPCSystemViewServer struct {
|
||||
impl logical.SystemView
|
||||
}
|
||||
|
@ -274,3 +287,20 @@ func (s *gRPCSystemViewServer) PluginEnv(ctx context.Context, _ *pb.Empty) (*pb.
|
|||
PluginEnvironment: pluginEnv,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *gRPCSystemViewServer) GeneratePasswordFromPolicy(ctx context.Context, req *pb.GeneratePasswordFromPolicyRequest) (*pb.GeneratePasswordFromPolicyReply, error) {
|
||||
policyName := req.PolicyName
|
||||
if policyName == "" {
|
||||
return &pb.GeneratePasswordFromPolicyReply{}, status.Errorf(codes.InvalidArgument, "no password policy specified")
|
||||
}
|
||||
|
||||
password, err := s.impl.GeneratePasswordFromPolicy(ctx, policyName)
|
||||
if err != nil {
|
||||
return &pb.GeneratePasswordFromPolicyReply{}, status.Errorf(codes.Internal, "failed to generate password")
|
||||
}
|
||||
|
||||
resp := &pb.GeneratePasswordFromPolicyReply{
|
||||
Password: password,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
|
|
@ -3017,6 +3017,100 @@ func (x *PluginEnvReply) GetErr() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
type GeneratePasswordFromPolicyRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PolicyName string `sentinel:"" protobuf:"bytes,1,opt,name=policy_name,json=policyName,proto3" json:"policy_name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyRequest) Reset() {
|
||||
*x = GeneratePasswordFromPolicyRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[44]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeneratePasswordFromPolicyRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[44]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeneratePasswordFromPolicyRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GeneratePasswordFromPolicyRequest) Descriptor() ([]byte, []int) {
|
||||
return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{44}
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyRequest) GetPolicyName() string {
|
||||
if x != nil {
|
||||
return x.PolicyName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GeneratePasswordFromPolicyReply struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Password string `sentinel:"" protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyReply) Reset() {
|
||||
*x = GeneratePasswordFromPolicyReply{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[45]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeneratePasswordFromPolicyReply) ProtoMessage() {}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[45]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeneratePasswordFromPolicyReply.ProtoReflect.Descriptor instead.
|
||||
func (*GeneratePasswordFromPolicyReply) Descriptor() ([]byte, []int) {
|
||||
return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{45}
|
||||
}
|
||||
|
||||
func (x *GeneratePasswordFromPolicyReply) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
|
@ -3029,7 +3123,7 @@ type Connection struct {
|
|||
func (x *Connection) Reset() {
|
||||
*x = Connection{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[44]
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[46]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
@ -3042,7 +3136,7 @@ func (x *Connection) String() string {
|
|||
func (*Connection) ProtoMessage() {}
|
||||
|
||||
func (x *Connection) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[44]
|
||||
mi := &file_sdk_plugin_pb_backend_proto_msgTypes[46]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
@ -3055,7 +3149,7 @@ func (x *Connection) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use Connection.ProtoReflect.Descriptor instead.
|
||||
func (*Connection) Descriptor() ([]byte, []int) {
|
||||
return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{44}
|
||||
return file_sdk_plugin_pb_backend_proto_rawDescGZIP(), []int{46}
|
||||
}
|
||||
|
||||
func (x *Connection) GetRemoteAddr() string {
|
||||
|
@ -3423,89 +3517,104 @@ var file_sdk_plugin_pb_backend_proto_rawDesc = []byte{
|
|||
0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x11,
|
||||
0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e,
|
||||
0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
||||
0x65, 0x72, 0x72, 0x22, 0x2d, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64,
|
||||
0x64, 0x72, 0x32, 0xa5, 0x03, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3e,
|
||||
0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x15, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64,
|
||||
0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30,
|
||||
0x0a, 0x0c, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x09,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53,
|
||||
0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||
0x12, 0x53, 0x0a, 0x14, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65,
|
||||
0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1c, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61,
|
||||
0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65,
|
||||
0x63, 0x6b, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x1d, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64,
|
||||
0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x07, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70,
|
||||
0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x09, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x31, 0x0a, 0x0d, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69,
|
||||
0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x76,
|
||||
0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x09,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x53, 0x65, 0x74,
|
||||
0x75, 0x70, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x41, 0x72, 0x67,
|
||||
0x73, 0x1a, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6c,
|
||||
0x79, 0x12, 0x35, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12,
|
||||
0x12, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x41,
|
||||
0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65,
|
||||
0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xd5, 0x01, 0x0a, 0x07, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x41,
|
||||
0x72, 0x67, 0x73, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x03, 0x47, 0x65, 0x74,
|
||||
0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x47, 0x65, 0x74,
|
||||
0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e, 0x0a, 0x03, 0x50, 0x75, 0x74,
|
||||
0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x75, 0x74,
|
||||
0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x50, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x44, 0x65, 0x6c,
|
||||
0x65, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70,
|
||||
0x6c, 0x79, 0x32, 0xc7, 0x04, 0x0a, 0x0a, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x56, 0x69, 0x65,
|
||||
0x77, 0x12, 0x2a, 0x0a, 0x0f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4c, 0x65, 0x61, 0x73,
|
||||
0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a,
|
||||
0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a,
|
||||
0x0b, 0x4d, 0x61, 0x78, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x07, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64,
|
||||
0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x36, 0x0a,
|
||||
0x0f, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||
0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x18, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
|
||||
0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44,
|
||||
0x61, 0x74, 0x61, 0x12, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x19, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44,
|
||||
0x61, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x0c, 0x4d, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2c, 0x0a, 0x0a, 0x4c, 0x6f,
|
||||
0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x0a, 0x45, 0x6e, 0x74, 0x69,
|
||||
0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69,
|
||||
0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
|
||||
0x2a, 0x0a, 0x09, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x12, 0x09, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6c, 0x75,
|
||||
0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x0f, 0x47,
|
||||
0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x12,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72,
|
||||
0x67, 0x73, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f,
|
||||
0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x2a, 0x5a, 0x28,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69,
|
||||
0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70,
|
||||
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x72, 0x72, 0x22, 0x44, 0x0a, 0x21, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50,
|
||||
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||
0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69,
|
||||
0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70,
|
||||
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3d, 0x0a, 0x1f, 0x47, 0x65, 0x6e,
|
||||
0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f,
|
||||
0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x2d, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
|
||||
0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d,
|
||||
0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x32, 0xa5, 0x03, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b,
|
||||
0x65, 0x6e, 0x64, 0x12, 0x3e, 0x0a, 0x0d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x16, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65,
|
||||
0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x0c, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61,
|
||||
0x74, 0x68, 0x73, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x73,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x53, 0x0a, 0x14, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45,
|
||||
0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1c, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e,
|
||||
0x63, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x1d, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65,
|
||||
0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x07, 0x43, 0x6c,
|
||||
0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x31, 0x0a, 0x0d, 0x49,
|
||||
0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41,
|
||||
0x72, 0x67, 0x73, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x26,
|
||||
0x0a, 0x05, 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74,
|
||||
0x75, 0x70, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x75,
|
||||
0x70, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e,
|
||||
0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a,
|
||||
0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32,
|
||||
0xd5, 0x01, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e,
|
||||
0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x47, 0x65, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2e,
|
||||
0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x50, 0x75, 0x74, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x50, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x37,
|
||||
0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x1a,
|
||||
0x16, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x65,
|
||||
0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xb1, 0x05, 0x0a, 0x0a, 0x53, 0x79, 0x73, 0x74,
|
||||
0x65, 0x6d, 0x56, 0x69, 0x65, 0x77, 0x12, 0x2a, 0x0a, 0x0f, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c,
|
||||
0x74, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x54, 0x4c, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70,
|
||||
0x6c, 0x79, 0x12, 0x26, 0x0a, 0x0b, 0x4d, 0x61, 0x78, 0x4c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x54,
|
||||
0x4c, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x54, 0x54, 0x4c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x07, 0x54, 0x61,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x64, 0x52, 0x65, 0x70,
|
||||
0x6c, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x69, 0x73,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a, 0x10, 0x52, 0x65,
|
||||
0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x09,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52,
|
||||
0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
|
||||
0x65, 0x70, 0x6c, 0x79, 0x12, 0x47, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x41, 0x72,
|
||||
0x67, 0x73, 0x1a, 0x19, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x57, 0x72, 0x61, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a,
|
||||
0x0c, 0x4d, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x09, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12,
|
||||
0x2c, 0x0a, 0x0a, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x09, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f,
|
||||
0x63, 0x61, 0x6c, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a,
|
||||
0x0a, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x1a,
|
||||
0x13, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
||||
0x65, 0x70, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x09, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e,
|
||||
0x76, 0x12, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||
0x12, 0x3f, 0x0a, 0x0f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74,
|
||||
0x69, 0x74, 0x79, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x41, 0x72, 0x67, 0x73, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x6f,
|
||||
0x75, 0x70, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c,
|
||||
0x79, 0x12, 0x68, 0x0a, 0x1a, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12,
|
||||
0x25, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65,
|
||||
0x72, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x46, 0x72, 0x6f, 0x6d,
|
||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x2a, 0x5a, 0x28, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
|
||||
0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x6c,
|
||||
0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -3520,82 +3629,84 @@ func file_sdk_plugin_pb_backend_proto_rawDescGZIP() []byte {
|
|||
return file_sdk_plugin_pb_backend_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_sdk_plugin_pb_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 50)
|
||||
var file_sdk_plugin_pb_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 52)
|
||||
var file_sdk_plugin_pb_backend_proto_goTypes = []interface{}{
|
||||
(*Empty)(nil), // 0: pb.Empty
|
||||
(*Header)(nil), // 1: pb.Header
|
||||
(*ProtoError)(nil), // 2: pb.ProtoError
|
||||
(*Paths)(nil), // 3: pb.Paths
|
||||
(*Request)(nil), // 4: pb.Request
|
||||
(*Auth)(nil), // 5: pb.Auth
|
||||
(*TokenEntry)(nil), // 6: pb.TokenEntry
|
||||
(*LeaseOptions)(nil), // 7: pb.LeaseOptions
|
||||
(*Secret)(nil), // 8: pb.Secret
|
||||
(*Response)(nil), // 9: pb.Response
|
||||
(*ResponseWrapInfo)(nil), // 10: pb.ResponseWrapInfo
|
||||
(*RequestWrapInfo)(nil), // 11: pb.RequestWrapInfo
|
||||
(*HandleRequestArgs)(nil), // 12: pb.HandleRequestArgs
|
||||
(*HandleRequestReply)(nil), // 13: pb.HandleRequestReply
|
||||
(*InitializeArgs)(nil), // 14: pb.InitializeArgs
|
||||
(*InitializeReply)(nil), // 15: pb.InitializeReply
|
||||
(*SpecialPathsReply)(nil), // 16: pb.SpecialPathsReply
|
||||
(*HandleExistenceCheckArgs)(nil), // 17: pb.HandleExistenceCheckArgs
|
||||
(*HandleExistenceCheckReply)(nil), // 18: pb.HandleExistenceCheckReply
|
||||
(*SetupArgs)(nil), // 19: pb.SetupArgs
|
||||
(*SetupReply)(nil), // 20: pb.SetupReply
|
||||
(*TypeReply)(nil), // 21: pb.TypeReply
|
||||
(*InvalidateKeyArgs)(nil), // 22: pb.InvalidateKeyArgs
|
||||
(*StorageEntry)(nil), // 23: pb.StorageEntry
|
||||
(*StorageListArgs)(nil), // 24: pb.StorageListArgs
|
||||
(*StorageListReply)(nil), // 25: pb.StorageListReply
|
||||
(*StorageGetArgs)(nil), // 26: pb.StorageGetArgs
|
||||
(*StorageGetReply)(nil), // 27: pb.StorageGetReply
|
||||
(*StoragePutArgs)(nil), // 28: pb.StoragePutArgs
|
||||
(*StoragePutReply)(nil), // 29: pb.StoragePutReply
|
||||
(*StorageDeleteArgs)(nil), // 30: pb.StorageDeleteArgs
|
||||
(*StorageDeleteReply)(nil), // 31: pb.StorageDeleteReply
|
||||
(*TTLReply)(nil), // 32: pb.TTLReply
|
||||
(*TaintedReply)(nil), // 33: pb.TaintedReply
|
||||
(*CachingDisabledReply)(nil), // 34: pb.CachingDisabledReply
|
||||
(*ReplicationStateReply)(nil), // 35: pb.ReplicationStateReply
|
||||
(*ResponseWrapDataArgs)(nil), // 36: pb.ResponseWrapDataArgs
|
||||
(*ResponseWrapDataReply)(nil), // 37: pb.ResponseWrapDataReply
|
||||
(*MlockEnabledReply)(nil), // 38: pb.MlockEnabledReply
|
||||
(*LocalMountReply)(nil), // 39: pb.LocalMountReply
|
||||
(*EntityInfoArgs)(nil), // 40: pb.EntityInfoArgs
|
||||
(*EntityInfoReply)(nil), // 41: pb.EntityInfoReply
|
||||
(*GroupsForEntityReply)(nil), // 42: pb.GroupsForEntityReply
|
||||
(*PluginEnvReply)(nil), // 43: pb.PluginEnvReply
|
||||
(*Connection)(nil), // 44: pb.Connection
|
||||
nil, // 45: pb.Request.HeadersEntry
|
||||
nil, // 46: pb.Auth.MetadataEntry
|
||||
nil, // 47: pb.TokenEntry.MetaEntry
|
||||
nil, // 48: pb.Response.HeadersEntry
|
||||
nil, // 49: pb.SetupArgs.ConfigEntry
|
||||
(*logical.Alias)(nil), // 50: logical.Alias
|
||||
(*timestamp.Timestamp)(nil), // 51: google.protobuf.Timestamp
|
||||
(*logical.Entity)(nil), // 52: logical.Entity
|
||||
(*logical.Group)(nil), // 53: logical.Group
|
||||
(*logical.PluginEnvironment)(nil), // 54: logical.PluginEnvironment
|
||||
(*Empty)(nil), // 0: pb.Empty
|
||||
(*Header)(nil), // 1: pb.Header
|
||||
(*ProtoError)(nil), // 2: pb.ProtoError
|
||||
(*Paths)(nil), // 3: pb.Paths
|
||||
(*Request)(nil), // 4: pb.Request
|
||||
(*Auth)(nil), // 5: pb.Auth
|
||||
(*TokenEntry)(nil), // 6: pb.TokenEntry
|
||||
(*LeaseOptions)(nil), // 7: pb.LeaseOptions
|
||||
(*Secret)(nil), // 8: pb.Secret
|
||||
(*Response)(nil), // 9: pb.Response
|
||||
(*ResponseWrapInfo)(nil), // 10: pb.ResponseWrapInfo
|
||||
(*RequestWrapInfo)(nil), // 11: pb.RequestWrapInfo
|
||||
(*HandleRequestArgs)(nil), // 12: pb.HandleRequestArgs
|
||||
(*HandleRequestReply)(nil), // 13: pb.HandleRequestReply
|
||||
(*InitializeArgs)(nil), // 14: pb.InitializeArgs
|
||||
(*InitializeReply)(nil), // 15: pb.InitializeReply
|
||||
(*SpecialPathsReply)(nil), // 16: pb.SpecialPathsReply
|
||||
(*HandleExistenceCheckArgs)(nil), // 17: pb.HandleExistenceCheckArgs
|
||||
(*HandleExistenceCheckReply)(nil), // 18: pb.HandleExistenceCheckReply
|
||||
(*SetupArgs)(nil), // 19: pb.SetupArgs
|
||||
(*SetupReply)(nil), // 20: pb.SetupReply
|
||||
(*TypeReply)(nil), // 21: pb.TypeReply
|
||||
(*InvalidateKeyArgs)(nil), // 22: pb.InvalidateKeyArgs
|
||||
(*StorageEntry)(nil), // 23: pb.StorageEntry
|
||||
(*StorageListArgs)(nil), // 24: pb.StorageListArgs
|
||||
(*StorageListReply)(nil), // 25: pb.StorageListReply
|
||||
(*StorageGetArgs)(nil), // 26: pb.StorageGetArgs
|
||||
(*StorageGetReply)(nil), // 27: pb.StorageGetReply
|
||||
(*StoragePutArgs)(nil), // 28: pb.StoragePutArgs
|
||||
(*StoragePutReply)(nil), // 29: pb.StoragePutReply
|
||||
(*StorageDeleteArgs)(nil), // 30: pb.StorageDeleteArgs
|
||||
(*StorageDeleteReply)(nil), // 31: pb.StorageDeleteReply
|
||||
(*TTLReply)(nil), // 32: pb.TTLReply
|
||||
(*TaintedReply)(nil), // 33: pb.TaintedReply
|
||||
(*CachingDisabledReply)(nil), // 34: pb.CachingDisabledReply
|
||||
(*ReplicationStateReply)(nil), // 35: pb.ReplicationStateReply
|
||||
(*ResponseWrapDataArgs)(nil), // 36: pb.ResponseWrapDataArgs
|
||||
(*ResponseWrapDataReply)(nil), // 37: pb.ResponseWrapDataReply
|
||||
(*MlockEnabledReply)(nil), // 38: pb.MlockEnabledReply
|
||||
(*LocalMountReply)(nil), // 39: pb.LocalMountReply
|
||||
(*EntityInfoArgs)(nil), // 40: pb.EntityInfoArgs
|
||||
(*EntityInfoReply)(nil), // 41: pb.EntityInfoReply
|
||||
(*GroupsForEntityReply)(nil), // 42: pb.GroupsForEntityReply
|
||||
(*PluginEnvReply)(nil), // 43: pb.PluginEnvReply
|
||||
(*GeneratePasswordFromPolicyRequest)(nil), // 44: pb.GeneratePasswordFromPolicyRequest
|
||||
(*GeneratePasswordFromPolicyReply)(nil), // 45: pb.GeneratePasswordFromPolicyReply
|
||||
(*Connection)(nil), // 46: pb.Connection
|
||||
nil, // 47: pb.Request.HeadersEntry
|
||||
nil, // 48: pb.Auth.MetadataEntry
|
||||
nil, // 49: pb.TokenEntry.MetaEntry
|
||||
nil, // 50: pb.Response.HeadersEntry
|
||||
nil, // 51: pb.SetupArgs.ConfigEntry
|
||||
(*logical.Alias)(nil), // 52: logical.Alias
|
||||
(*timestamp.Timestamp)(nil), // 53: google.protobuf.Timestamp
|
||||
(*logical.Entity)(nil), // 54: logical.Entity
|
||||
(*logical.Group)(nil), // 55: logical.Group
|
||||
(*logical.PluginEnvironment)(nil), // 56: logical.PluginEnvironment
|
||||
}
|
||||
var file_sdk_plugin_pb_backend_proto_depIDxs = []int32{
|
||||
8, // 0: pb.Request.secret:type_name -> pb.Secret
|
||||
5, // 1: pb.Request.auth:type_name -> pb.Auth
|
||||
45, // 2: pb.Request.headers:type_name -> pb.Request.HeadersEntry
|
||||
47, // 2: pb.Request.headers:type_name -> pb.Request.HeadersEntry
|
||||
11, // 3: pb.Request.wrap_info:type_name -> pb.RequestWrapInfo
|
||||
44, // 4: pb.Request.connection:type_name -> pb.Connection
|
||||
46, // 4: pb.Request.connection:type_name -> pb.Connection
|
||||
7, // 5: pb.Auth.lease_options:type_name -> pb.LeaseOptions
|
||||
46, // 6: pb.Auth.metadata:type_name -> pb.Auth.MetadataEntry
|
||||
50, // 7: pb.Auth.alias:type_name -> logical.Alias
|
||||
50, // 8: pb.Auth.group_aliases:type_name -> logical.Alias
|
||||
47, // 9: pb.TokenEntry.meta:type_name -> pb.TokenEntry.MetaEntry
|
||||
51, // 10: pb.LeaseOptions.issue_time:type_name -> google.protobuf.Timestamp
|
||||
48, // 6: pb.Auth.metadata:type_name -> pb.Auth.MetadataEntry
|
||||
52, // 7: pb.Auth.alias:type_name -> logical.Alias
|
||||
52, // 8: pb.Auth.group_aliases:type_name -> logical.Alias
|
||||
49, // 9: pb.TokenEntry.meta:type_name -> pb.TokenEntry.MetaEntry
|
||||
53, // 10: pb.LeaseOptions.issue_time:type_name -> google.protobuf.Timestamp
|
||||
7, // 11: pb.Secret.lease_options:type_name -> pb.LeaseOptions
|
||||
8, // 12: pb.Response.secret:type_name -> pb.Secret
|
||||
5, // 13: pb.Response.auth:type_name -> pb.Auth
|
||||
10, // 14: pb.Response.wrap_info:type_name -> pb.ResponseWrapInfo
|
||||
48, // 15: pb.Response.headers:type_name -> pb.Response.HeadersEntry
|
||||
51, // 16: pb.ResponseWrapInfo.creation_time:type_name -> google.protobuf.Timestamp
|
||||
50, // 15: pb.Response.headers:type_name -> pb.Response.HeadersEntry
|
||||
53, // 16: pb.ResponseWrapInfo.creation_time:type_name -> google.protobuf.Timestamp
|
||||
4, // 17: pb.HandleRequestArgs.request:type_name -> pb.Request
|
||||
9, // 18: pb.HandleRequestReply.response:type_name -> pb.Response
|
||||
2, // 19: pb.HandleRequestReply.err:type_name -> pb.ProtoError
|
||||
|
@ -3603,13 +3714,13 @@ var file_sdk_plugin_pb_backend_proto_depIDxs = []int32{
|
|||
3, // 21: pb.SpecialPathsReply.paths:type_name -> pb.Paths
|
||||
4, // 22: pb.HandleExistenceCheckArgs.request:type_name -> pb.Request
|
||||
2, // 23: pb.HandleExistenceCheckReply.err:type_name -> pb.ProtoError
|
||||
49, // 24: pb.SetupArgs.Config:type_name -> pb.SetupArgs.ConfigEntry
|
||||
51, // 24: pb.SetupArgs.Config:type_name -> pb.SetupArgs.ConfigEntry
|
||||
23, // 25: pb.StorageGetReply.entry:type_name -> pb.StorageEntry
|
||||
23, // 26: pb.StoragePutArgs.entry:type_name -> pb.StorageEntry
|
||||
10, // 27: pb.ResponseWrapDataReply.wrap_info:type_name -> pb.ResponseWrapInfo
|
||||
52, // 28: pb.EntityInfoReply.entity:type_name -> logical.Entity
|
||||
53, // 29: pb.GroupsForEntityReply.groups:type_name -> logical.Group
|
||||
54, // 30: pb.PluginEnvReply.plugin_environment:type_name -> logical.PluginEnvironment
|
||||
54, // 28: pb.EntityInfoReply.entity:type_name -> logical.Entity
|
||||
55, // 29: pb.GroupsForEntityReply.groups:type_name -> logical.Group
|
||||
56, // 30: pb.PluginEnvReply.plugin_environment:type_name -> logical.PluginEnvironment
|
||||
1, // 31: pb.Request.HeadersEntry.value:type_name -> pb.Header
|
||||
1, // 32: pb.Response.HeadersEntry.value:type_name -> pb.Header
|
||||
12, // 33: pb.Backend.HandleRequest:input_type -> pb.HandleRequestArgs
|
||||
|
@ -3635,31 +3746,33 @@ var file_sdk_plugin_pb_backend_proto_depIDxs = []int32{
|
|||
40, // 53: pb.SystemView.EntityInfo:input_type -> pb.EntityInfoArgs
|
||||
0, // 54: pb.SystemView.PluginEnv:input_type -> pb.Empty
|
||||
40, // 55: pb.SystemView.GroupsForEntity:input_type -> pb.EntityInfoArgs
|
||||
13, // 56: pb.Backend.HandleRequest:output_type -> pb.HandleRequestReply
|
||||
16, // 57: pb.Backend.SpecialPaths:output_type -> pb.SpecialPathsReply
|
||||
18, // 58: pb.Backend.HandleExistenceCheck:output_type -> pb.HandleExistenceCheckReply
|
||||
0, // 59: pb.Backend.Cleanup:output_type -> pb.Empty
|
||||
0, // 60: pb.Backend.InvalidateKey:output_type -> pb.Empty
|
||||
20, // 61: pb.Backend.Setup:output_type -> pb.SetupReply
|
||||
15, // 62: pb.Backend.Initialize:output_type -> pb.InitializeReply
|
||||
21, // 63: pb.Backend.Type:output_type -> pb.TypeReply
|
||||
25, // 64: pb.Storage.List:output_type -> pb.StorageListReply
|
||||
27, // 65: pb.Storage.Get:output_type -> pb.StorageGetReply
|
||||
29, // 66: pb.Storage.Put:output_type -> pb.StoragePutReply
|
||||
31, // 67: pb.Storage.Delete:output_type -> pb.StorageDeleteReply
|
||||
32, // 68: pb.SystemView.DefaultLeaseTTL:output_type -> pb.TTLReply
|
||||
32, // 69: pb.SystemView.MaxLeaseTTL:output_type -> pb.TTLReply
|
||||
33, // 70: pb.SystemView.Tainted:output_type -> pb.TaintedReply
|
||||
34, // 71: pb.SystemView.CachingDisabled:output_type -> pb.CachingDisabledReply
|
||||
35, // 72: pb.SystemView.ReplicationState:output_type -> pb.ReplicationStateReply
|
||||
37, // 73: pb.SystemView.ResponseWrapData:output_type -> pb.ResponseWrapDataReply
|
||||
38, // 74: pb.SystemView.MlockEnabled:output_type -> pb.MlockEnabledReply
|
||||
39, // 75: pb.SystemView.LocalMount:output_type -> pb.LocalMountReply
|
||||
41, // 76: pb.SystemView.EntityInfo:output_type -> pb.EntityInfoReply
|
||||
43, // 77: pb.SystemView.PluginEnv:output_type -> pb.PluginEnvReply
|
||||
42, // 78: pb.SystemView.GroupsForEntity:output_type -> pb.GroupsForEntityReply
|
||||
56, // [56:79] is the sub-list for method output_type
|
||||
33, // [33:56] is the sub-list for method input_type
|
||||
44, // 56: pb.SystemView.GeneratePasswordFromPolicy:input_type -> pb.GeneratePasswordFromPolicyRequest
|
||||
13, // 57: pb.Backend.HandleRequest:output_type -> pb.HandleRequestReply
|
||||
16, // 58: pb.Backend.SpecialPaths:output_type -> pb.SpecialPathsReply
|
||||
18, // 59: pb.Backend.HandleExistenceCheck:output_type -> pb.HandleExistenceCheckReply
|
||||
0, // 60: pb.Backend.Cleanup:output_type -> pb.Empty
|
||||
0, // 61: pb.Backend.InvalidateKey:output_type -> pb.Empty
|
||||
20, // 62: pb.Backend.Setup:output_type -> pb.SetupReply
|
||||
15, // 63: pb.Backend.Initialize:output_type -> pb.InitializeReply
|
||||
21, // 64: pb.Backend.Type:output_type -> pb.TypeReply
|
||||
25, // 65: pb.Storage.List:output_type -> pb.StorageListReply
|
||||
27, // 66: pb.Storage.Get:output_type -> pb.StorageGetReply
|
||||
29, // 67: pb.Storage.Put:output_type -> pb.StoragePutReply
|
||||
31, // 68: pb.Storage.Delete:output_type -> pb.StorageDeleteReply
|
||||
32, // 69: pb.SystemView.DefaultLeaseTTL:output_type -> pb.TTLReply
|
||||
32, // 70: pb.SystemView.MaxLeaseTTL:output_type -> pb.TTLReply
|
||||
33, // 71: pb.SystemView.Tainted:output_type -> pb.TaintedReply
|
||||
34, // 72: pb.SystemView.CachingDisabled:output_type -> pb.CachingDisabledReply
|
||||
35, // 73: pb.SystemView.ReplicationState:output_type -> pb.ReplicationStateReply
|
||||
37, // 74: pb.SystemView.ResponseWrapData:output_type -> pb.ResponseWrapDataReply
|
||||
38, // 75: pb.SystemView.MlockEnabled:output_type -> pb.MlockEnabledReply
|
||||
39, // 76: pb.SystemView.LocalMount:output_type -> pb.LocalMountReply
|
||||
41, // 77: pb.SystemView.EntityInfo:output_type -> pb.EntityInfoReply
|
||||
43, // 78: pb.SystemView.PluginEnv:output_type -> pb.PluginEnvReply
|
||||
42, // 79: pb.SystemView.GroupsForEntity:output_type -> pb.GroupsForEntityReply
|
||||
45, // 80: pb.SystemView.GeneratePasswordFromPolicy:output_type -> pb.GeneratePasswordFromPolicyReply
|
||||
57, // [57:81] is the sub-list for method output_type
|
||||
33, // [33:57] is the sub-list for method input_type
|
||||
33, // [33:33] is the sub-list for extension type_name
|
||||
33, // [33:33] is the sub-list for extension extendee
|
||||
0, // [0:33] is the sub-list for field type_name
|
||||
|
@ -4200,6 +4313,30 @@ func file_sdk_plugin_pb_backend_proto_init() {
|
|||
}
|
||||
}
|
||||
file_sdk_plugin_pb_backend_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeneratePasswordFromPolicyRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_sdk_plugin_pb_backend_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GeneratePasswordFromPolicyReply); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_sdk_plugin_pb_backend_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Connection); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
|
@ -4218,7 +4355,7 @@ func file_sdk_plugin_pb_backend_proto_init() {
|
|||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_sdk_plugin_pb_backend_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 50,
|
||||
NumMessages: 52,
|
||||
NumExtensions: 0,
|
||||
NumServices: 3,
|
||||
},
|
||||
|
@ -4838,6 +4975,8 @@ type SystemViewClient interface {
|
|||
// GroupsForEntity returns the group membership information for the given
|
||||
// entity id
|
||||
GroupsForEntity(ctx context.Context, in *EntityInfoArgs, opts ...grpc.CallOption) (*GroupsForEntityReply, error)
|
||||
// GeneratePasswordFromPolicy generates a password from an existing password policy
|
||||
GeneratePasswordFromPolicy(ctx context.Context, in *GeneratePasswordFromPolicyRequest, opts ...grpc.CallOption) (*GeneratePasswordFromPolicyReply, error)
|
||||
}
|
||||
|
||||
type systemViewClient struct {
|
||||
|
@ -4947,6 +5086,15 @@ func (c *systemViewClient) GroupsForEntity(ctx context.Context, in *EntityInfoAr
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *systemViewClient) GeneratePasswordFromPolicy(ctx context.Context, in *GeneratePasswordFromPolicyRequest, opts ...grpc.CallOption) (*GeneratePasswordFromPolicyReply, error) {
|
||||
out := new(GeneratePasswordFromPolicyReply)
|
||||
err := c.cc.Invoke(ctx, "/pb.SystemView/GeneratePasswordFromPolicy", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SystemViewServer is the server API for SystemView service.
|
||||
type SystemViewServer interface {
|
||||
// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
|
||||
|
@ -4985,6 +5133,8 @@ type SystemViewServer interface {
|
|||
// GroupsForEntity returns the group membership information for the given
|
||||
// entity id
|
||||
GroupsForEntity(context.Context, *EntityInfoArgs) (*GroupsForEntityReply, error)
|
||||
// GeneratePasswordFromPolicy generates a password from an existing password policy
|
||||
GeneratePasswordFromPolicy(context.Context, *GeneratePasswordFromPolicyRequest) (*GeneratePasswordFromPolicyReply, error)
|
||||
}
|
||||
|
||||
// UnimplementedSystemViewServer can be embedded to have forward compatible implementations.
|
||||
|
@ -5024,6 +5174,9 @@ func (*UnimplementedSystemViewServer) PluginEnv(context.Context, *Empty) (*Plugi
|
|||
func (*UnimplementedSystemViewServer) GroupsForEntity(context.Context, *EntityInfoArgs) (*GroupsForEntityReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GroupsForEntity not implemented")
|
||||
}
|
||||
func (*UnimplementedSystemViewServer) GeneratePasswordFromPolicy(context.Context, *GeneratePasswordFromPolicyRequest) (*GeneratePasswordFromPolicyReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GeneratePasswordFromPolicy not implemented")
|
||||
}
|
||||
|
||||
func RegisterSystemViewServer(s *grpc.Server, srv SystemViewServer) {
|
||||
s.RegisterService(&_SystemView_serviceDesc, srv)
|
||||
|
@ -5227,6 +5380,24 @@ func _SystemView_GroupsForEntity_Handler(srv interface{}, ctx context.Context, d
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SystemView_GeneratePasswordFromPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GeneratePasswordFromPolicyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SystemViewServer).GeneratePasswordFromPolicy(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/pb.SystemView/GeneratePasswordFromPolicy",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SystemViewServer).GeneratePasswordFromPolicy(ctx, req.(*GeneratePasswordFromPolicyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _SystemView_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "pb.SystemView",
|
||||
HandlerType: (*SystemViewServer)(nil),
|
||||
|
@ -5275,6 +5446,10 @@ var _SystemView_serviceDesc = grpc.ServiceDesc{
|
|||
MethodName: "GroupsForEntity",
|
||||
Handler: _SystemView_GroupsForEntity_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GeneratePasswordFromPolicy",
|
||||
Handler: _SystemView_GeneratePasswordFromPolicy_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "sdk/plugin/pb/backend.proto",
|
||||
|
|
|
@ -554,12 +554,20 @@ message PluginEnvReply {
|
|||
string err = 2;
|
||||
}
|
||||
|
||||
message GeneratePasswordFromPolicyRequest {
|
||||
string policy_name = 1;
|
||||
}
|
||||
|
||||
message GeneratePasswordFromPolicyReply {
|
||||
string password = 1;
|
||||
}
|
||||
|
||||
// SystemView exposes system configuration information in a safe way for plugins
|
||||
// to consume. Plugins should implement the client for this service.
|
||||
service SystemView {
|
||||
// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
|
||||
rpc DefaultLeaseTTL(Empty) returns (TTLReply);
|
||||
|
||||
|
||||
// MaxLeaseTTL returns the max lease TTL set in Vault configuration; backend
|
||||
// authors should take care not to issue credentials that last longer than
|
||||
// this value, as Vault will revoke them
|
||||
|
@ -603,6 +611,9 @@ service SystemView {
|
|||
// GroupsForEntity returns the group membership information for the given
|
||||
// entity id
|
||||
rpc GroupsForEntity(EntityInfoArgs) returns (GroupsForEntityReply);
|
||||
|
||||
// GeneratePasswordFromPolicy generates a password from an existing password policy
|
||||
rpc GeneratePasswordFromPolicy(GeneratePasswordFromPolicyRequest) returns (GeneratePasswordFromPolicyReply);
|
||||
}
|
||||
|
||||
message Connection {
|
||||
|
|
|
@ -6,3 +6,4 @@ go:
|
|||
|
||||
script:
|
||||
- go test
|
||||
- go test -bench . -benchmem
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
## 1.2.2
|
||||
|
||||
* Do not add unsettable (unexported) values to the unused metadata key
|
||||
or "remain" value. [GH-150]
|
||||
|
||||
## 1.2.1
|
||||
|
||||
* Go modules checksum mismatch fix
|
||||
|
||||
## 1.2.0
|
||||
|
||||
* Added support to capture unused values in a field using the `",remain"` value
|
||||
in the mapstructure tag. There is an example to showcase usage.
|
||||
* Added `DecoderConfig` option to always squash embedded structs
|
||||
* `json.Number` can decode into `uint` types
|
||||
* Empty slices are preserved and not replaced with nil slices
|
||||
* Fix panic that can occur in when decoding a map into a nil slice of structs
|
||||
* Improved package documentation for godoc
|
||||
|
||||
## 1.1.2
|
||||
|
||||
* Fix error when decode hook decodes interface implementation into interface
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
module github.com/mitchellh/mapstructure
|
||||
|
||||
go 1.14
|
||||
|
|
|
@ -1,10 +1,109 @@
|
|||
// Package mapstructure exposes functionality to convert an arbitrary
|
||||
// map[string]interface{} into a native Go structure.
|
||||
// Package mapstructure exposes functionality to convert one arbitrary
|
||||
// Go type into another, typically to convert a map[string]interface{}
|
||||
// into a native Go structure.
|
||||
//
|
||||
// The Go structure can be arbitrarily complex, containing slices,
|
||||
// other structs, etc. and the decoder will properly decode nested
|
||||
// maps and so on into the proper structures in the native Go struct.
|
||||
// See the examples to see what the decoder is capable of.
|
||||
//
|
||||
// The simplest function to start with is Decode.
|
||||
//
|
||||
// Field Tags
|
||||
//
|
||||
// When decoding to a struct, mapstructure will use the field name by
|
||||
// default to perform the mapping. For example, if a struct has a field
|
||||
// "Username" then mapstructure will look for a key in the source value
|
||||
// of "username" (case insensitive).
|
||||
//
|
||||
// type User struct {
|
||||
// Username string
|
||||
// }
|
||||
//
|
||||
// You can change the behavior of mapstructure by using struct tags.
|
||||
// The default struct tag that mapstructure looks for is "mapstructure"
|
||||
// but you can customize it using DecoderConfig.
|
||||
//
|
||||
// Renaming Fields
|
||||
//
|
||||
// To rename the key that mapstructure looks for, use the "mapstructure"
|
||||
// tag and set a value directly. For example, to change the "username" example
|
||||
// above to "user":
|
||||
//
|
||||
// type User struct {
|
||||
// Username string `mapstructure:"user"`
|
||||
// }
|
||||
//
|
||||
// Embedded Structs and Squashing
|
||||
//
|
||||
// Embedded structs are treated as if they're another field with that name.
|
||||
// By default, the two structs below are equivalent when decoding with
|
||||
// mapstructure:
|
||||
//
|
||||
// type Person struct {
|
||||
// Name string
|
||||
// }
|
||||
//
|
||||
// type Friend struct {
|
||||
// Person
|
||||
// }
|
||||
//
|
||||
// type Friend struct {
|
||||
// Person Person
|
||||
// }
|
||||
//
|
||||
// This would require an input that looks like below:
|
||||
//
|
||||
// map[string]interface{}{
|
||||
// "person": map[string]interface{}{"name": "alice"},
|
||||
// }
|
||||
//
|
||||
// If your "person" value is NOT nested, then you can append ",squash" to
|
||||
// your tag value and mapstructure will treat it as if the embedded struct
|
||||
// were part of the struct directly. Example:
|
||||
//
|
||||
// type Friend struct {
|
||||
// Person `mapstructure:",squash"`
|
||||
// }
|
||||
//
|
||||
// Now the following input would be accepted:
|
||||
//
|
||||
// map[string]interface{}{
|
||||
// "name": "alice",
|
||||
// }
|
||||
//
|
||||
// DecoderConfig has a field that changes the behavior of mapstructure
|
||||
// to always squash embedded structs.
|
||||
//
|
||||
// Remainder Values
|
||||
//
|
||||
// If there are any unmapped keys in the source value, mapstructure by
|
||||
// default will silently ignore them. You can error by setting ErrorUnused
|
||||
// in DecoderConfig. If you're using Metadata you can also maintain a slice
|
||||
// of the unused keys.
|
||||
//
|
||||
// You can also use the ",remain" suffix on your tag to collect all unused
|
||||
// values in a map. The field with this tag MUST be a map type and should
|
||||
// probably be a "map[string]interface{}" or "map[interface{}]interface{}".
|
||||
// See example below:
|
||||
//
|
||||
// type Friend struct {
|
||||
// Name string
|
||||
// Other map[string]interface{} `mapstructure:",remain"`
|
||||
// }
|
||||
//
|
||||
// Given the input below, Other would be populated with the other
|
||||
// values that weren't used (everything but "name"):
|
||||
//
|
||||
// map[string]interface{}{
|
||||
// "name": "bob",
|
||||
// "address": "123 Maple St.",
|
||||
// }
|
||||
//
|
||||
// Other Configuration
|
||||
//
|
||||
// mapstructure is highly configurable. See the DecoderConfig struct
|
||||
// for other features and options that are supported.
|
||||
package mapstructure
|
||||
|
||||
import (
|
||||
|
@ -80,6 +179,14 @@ type DecoderConfig struct {
|
|||
//
|
||||
WeaklyTypedInput bool
|
||||
|
||||
// Squash will squash embedded structs. A squash tag may also be
|
||||
// added to an individual struct field using a tag. For example:
|
||||
//
|
||||
// type Parent struct {
|
||||
// Child `mapstructure:",squash"`
|
||||
// }
|
||||
Squash bool
|
||||
|
||||
// Metadata is the struct that will contain extra metadata about
|
||||
// the decoding. If this is nil, then no metadata will be tracked.
|
||||
Metadata *Metadata
|
||||
|
@ -438,6 +545,7 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
|
|||
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
dataType := dataVal.Type()
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
|
@ -469,6 +577,18 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
|
|||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
|
||||
}
|
||||
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
|
||||
jn := data.(json.Number)
|
||||
i, err := jn.Int64()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"error decoding json.Number into %s: %s", name, err)
|
||||
}
|
||||
if i < 0 && !d.config.WeaklyTypedInput {
|
||||
return fmt.Errorf("cannot parse '%s', %d overflows uint",
|
||||
name, i)
|
||||
}
|
||||
val.SetUint(uint64(i))
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
|
@ -689,16 +809,19 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
|||
keyName = tagParts[0]
|
||||
}
|
||||
|
||||
// If Squash is set in the config, we squash the field down.
|
||||
squash := d.config.Squash && v.Kind() == reflect.Struct
|
||||
// If "squash" is specified in the tag, we squash the field down.
|
||||
squash := false
|
||||
for _, tag := range tagParts[1:] {
|
||||
if tag == "squash" {
|
||||
squash = true
|
||||
break
|
||||
if !squash {
|
||||
for _, tag := range tagParts[1:] {
|
||||
if tag == "squash" {
|
||||
squash = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if squash && v.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
|
||||
}
|
||||
}
|
||||
if squash && v.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
|
@ -805,8 +928,8 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
|||
valElemType := valType.Elem()
|
||||
sliceType := reflect.SliceOf(valElemType)
|
||||
|
||||
valSlice := val
|
||||
if valSlice.IsNil() || d.config.ZeroFields {
|
||||
// If we have a non array/slice type then we first attempt to convert.
|
||||
if dataValKind != reflect.Array && dataValKind != reflect.Slice {
|
||||
if d.config.WeaklyTypedInput {
|
||||
switch {
|
||||
// Slice and array we use the normal logic
|
||||
|
@ -833,18 +956,17 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
|||
}
|
||||
}
|
||||
|
||||
// Check input type
|
||||
if dataValKind != reflect.Array && dataValKind != reflect.Slice {
|
||||
return fmt.Errorf(
|
||||
"'%s': source data must be an array or slice, got %s", name, dataValKind)
|
||||
return fmt.Errorf(
|
||||
"'%s': source data must be an array or slice, got %s", name, dataValKind)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If the input value is empty, then don't allocate since non-nil != nil
|
||||
if dataVal.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
// If the input value is nil, then don't allocate since empty != nil
|
||||
if dataVal.IsNil() {
|
||||
return nil
|
||||
}
|
||||
|
||||
valSlice := val
|
||||
if valSlice.IsNil() || d.config.ZeroFields {
|
||||
// Make a new slice to hold our result, same size as the original data.
|
||||
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
|
||||
}
|
||||
|
@ -1005,6 +1127,11 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
field reflect.StructField
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
// remainField is set to a valid field set with the "remain" tag if
|
||||
// we are keeping track of remaining values.
|
||||
var remainField *field
|
||||
|
||||
fields := []field{}
|
||||
for len(structs) > 0 {
|
||||
structVal := structs[0]
|
||||
|
@ -1017,13 +1144,21 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
fieldKind := fieldType.Type.Kind()
|
||||
|
||||
// If "squash" is specified in the tag, we squash the field down.
|
||||
squash := false
|
||||
squash := d.config.Squash && fieldKind == reflect.Struct
|
||||
remain := false
|
||||
|
||||
// We always parse the tags cause we're looking for other tags too
|
||||
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
|
||||
for _, tag := range tagParts[1:] {
|
||||
if tag == "squash" {
|
||||
squash = true
|
||||
break
|
||||
}
|
||||
|
||||
if tag == "remain" {
|
||||
remain = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if squash {
|
||||
|
@ -1036,8 +1171,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
continue
|
||||
}
|
||||
|
||||
// Normal struct field, store it away
|
||||
fields = append(fields, field{fieldType, structVal.Field(i)})
|
||||
// Build our field
|
||||
fieldCurrent := field{fieldType, structVal.Field(i)}
|
||||
if remain {
|
||||
remainField = &fieldCurrent
|
||||
} else {
|
||||
// Normal struct field, store it away
|
||||
fields = append(fields, field{fieldType, structVal.Field(i)})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1078,9 +1219,6 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
}
|
||||
}
|
||||
|
||||
// Delete the key we're using from the unused map so we stop tracking
|
||||
delete(dataValKeysUnused, rawMapKey.Interface())
|
||||
|
||||
if !fieldValue.IsValid() {
|
||||
// This should never happen
|
||||
panic("field is not valid")
|
||||
|
@ -1092,6 +1230,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
continue
|
||||
}
|
||||
|
||||
// Delete the key we're using from the unused map so we stop tracking
|
||||
delete(dataValKeysUnused, rawMapKey.Interface())
|
||||
|
||||
// If the name is empty string, then we're at the root, and we
|
||||
// don't dot-join the fields.
|
||||
if name != "" {
|
||||
|
@ -1103,6 +1244,25 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
}
|
||||
}
|
||||
|
||||
// If we have a "remain"-tagged field and we have unused keys then
|
||||
// we put the unused keys directly into the remain field.
|
||||
if remainField != nil && len(dataValKeysUnused) > 0 {
|
||||
// Build a map of only the unused values
|
||||
remain := map[interface{}]interface{}{}
|
||||
for key := range dataValKeysUnused {
|
||||
remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()
|
||||
}
|
||||
|
||||
// Decode it as-if we were just decoding this map onto our map.
|
||||
if err := d.decodeMap(name, remain, remainField.val); err != nil {
|
||||
errors = appendErrors(errors, err)
|
||||
}
|
||||
|
||||
// Set the map to nil so we have none so that the next check will
|
||||
// not error (ErrorUnused)
|
||||
dataValKeysUnused = nil
|
||||
}
|
||||
|
||||
if d.config.ErrorUnused && len(dataValKeysUnused) > 0 {
|
||||
keys := make([]string, 0, len(dataValKeysUnused))
|
||||
for rawKey := range dataValKeysUnused {
|
||||
|
|
|
@ -489,6 +489,7 @@ github.com/hashicorp/vault/sdk/helper/pathmanager
|
|||
github.com/hashicorp/vault/sdk/helper/pluginutil
|
||||
github.com/hashicorp/vault/sdk/helper/pointerutil
|
||||
github.com/hashicorp/vault/sdk/helper/policyutil
|
||||
github.com/hashicorp/vault/sdk/helper/random
|
||||
github.com/hashicorp/vault/sdk/helper/salt
|
||||
github.com/hashicorp/vault/sdk/helper/strutil
|
||||
github.com/hashicorp/vault/sdk/helper/tlsutil
|
||||
|
@ -634,7 +635,7 @@ github.com/mitchellh/gox
|
|||
github.com/mitchellh/hashstructure
|
||||
# github.com/mitchellh/iochan v1.0.0
|
||||
github.com/mitchellh/iochan
|
||||
# github.com/mitchellh/mapstructure v1.1.2
|
||||
# github.com/mitchellh/mapstructure v1.2.2
|
||||
github.com/mitchellh/mapstructure
|
||||
# github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8
|
||||
github.com/mitchellh/pointerstructure
|
||||
|
|
Loading…
Reference in New Issue