open-vault/helper/random/parser_test.go
Michael Golowka f77bcc53c4
Move sdk/helper/random -> helper/random (#9226)
* This package is new for 1.5 so this is not a breaking change.
* This is being moved because this code was originally intended to be used
within plugins, however the design of password policies has changed such
that this is no longer needed. Thus, this code doesn't need to be in the
public SDK.
2020-06-17 14:24:38 -06:00

579 lines
12 KiB
Go

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)
}