1675 lines
41 KiB
Go
1675 lines
41 KiB
Go
package configuration_test
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/rsa"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"math"
|
|
"net/mail"
|
|
"net/url"
|
|
"reflect"
|
|
"regexp"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/authelia/authelia/v4/internal/configuration"
|
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
|
)
|
|
|
|
func TestStringToMailAddressHookFunc(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeMailAddress",
|
|
have: "james@example.com",
|
|
want: mail.Address{Name: "", Address: "james@example.com"},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeMailAddressWithName",
|
|
have: "James <james@example.com>",
|
|
want: mail.Address{Name: "James", Address: "james@example.com"},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeMailAddressWithEmptyString",
|
|
have: "",
|
|
want: mail.Address{},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeInvalidMailAddress",
|
|
have: "fred",
|
|
want: mail.Address{},
|
|
err: "could not decode 'fred' to a mail.Address (RFC5322): mail: missing '@' or angle-addr",
|
|
decode: true,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToMailAddressHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToMailAddressHookFuncPointer(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeMailAddress",
|
|
have: "james@example.com",
|
|
want: &mail.Address{Name: "", Address: "james@example.com"},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeMailAddressWithName",
|
|
have: "James <james@example.com>",
|
|
want: &mail.Address{Name: "James", Address: "james@example.com"},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeMailAddressWithEmptyString",
|
|
have: "",
|
|
want: (*mail.Address)(nil),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeInvalidMailAddress",
|
|
have: "fred",
|
|
want: &mail.Address{},
|
|
err: "could not decode 'fred' to a *mail.Address (RFC5322): mail: missing '@' or angle-addr",
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToInt",
|
|
have: "fred",
|
|
want: testInt32Ptr(4),
|
|
decode: false,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToMailAddressHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToURLHookFunc(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeURL",
|
|
have: "https://www.example.com:9090/abc?test=true",
|
|
want: url.URL{Scheme: "https", Host: "www.example.com:9090", Path: "/abc", RawQuery: "test=true"},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeURLEmptyString",
|
|
have: "",
|
|
want: url.URL{},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToString",
|
|
have: "abc",
|
|
want: "",
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeURLWithUserAndPassword",
|
|
have: "https://john:abc123@www.example.com:9090/abc?test=true",
|
|
want: url.URL{Scheme: "https", Host: "www.example.com:9090", Path: "/abc", RawQuery: "test=true", User: url.UserPassword("john", "abc123")},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeInt",
|
|
have: 5,
|
|
want: url.URL{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBool",
|
|
have: true,
|
|
want: url.URL{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBadURL",
|
|
have: "*(!&@#(!*^$%",
|
|
want: url.URL{},
|
|
err: "could not decode '*(!&@#(!*^$%' to a url.URL: parse \"*(!&@#(!*^$%\": invalid URL escape \"%\"",
|
|
decode: true,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToURLHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToURLHookFuncPointer(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeURL",
|
|
have: "https://www.example.com:9090/abc?test=true",
|
|
want: &url.URL{Scheme: "https", Host: "www.example.com:9090", Path: "/abc", RawQuery: "test=true"},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeURLEmptyString",
|
|
have: "",
|
|
want: (*url.URL)(nil),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeURLWithUserAndPassword",
|
|
have: "https://john:abc123@www.example.com:9090/abc?test=true",
|
|
want: &url.URL{Scheme: "https", Host: "www.example.com:9090", Path: "/abc", RawQuery: "test=true", User: url.UserPassword("john", "abc123")},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeInt",
|
|
have: 5,
|
|
want: &url.URL{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBool",
|
|
have: true,
|
|
want: &url.URL{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBadURL",
|
|
have: "*(!&@#(!*^$%",
|
|
want: &url.URL{},
|
|
err: "could not decode '*(!&@#(!*^$%' to a *url.URL: parse \"*(!&@#(!*^$%\": invalid URL escape \"%\"",
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToInt",
|
|
have: "fred",
|
|
want: testInt32Ptr(4),
|
|
decode: false,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToURLHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestToTimeDurationHookFunc(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeFourtyFiveSeconds",
|
|
have: "45s",
|
|
want: time.Second * 45,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeOneMinute",
|
|
have: "1m",
|
|
want: time.Minute,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeTwoHours",
|
|
have: "2h",
|
|
want: time.Hour * 2,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeThreeDays",
|
|
have: "3d",
|
|
want: time.Hour * 24 * 3,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeFourWeeks",
|
|
have: "4w",
|
|
want: time.Hour * 24 * 7 * 4,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeFiveMonths",
|
|
have: "5M",
|
|
want: time.Hour * 24 * 30 * 5,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeSixYears",
|
|
have: "6y",
|
|
want: time.Hour * 24 * 365 * 6,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeInvalidString",
|
|
have: "abc",
|
|
want: time.Duration(0),
|
|
err: "could not decode 'abc' to a time.Duration: could not parse 'abc' as a duration",
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeIntToSeconds",
|
|
have: 60,
|
|
want: time.Second * 60,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeInt8ToSeconds",
|
|
have: int8(90),
|
|
want: time.Second * 90,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeInt16ToSeconds",
|
|
have: int16(90),
|
|
want: time.Second * 90,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeInt32ToSeconds",
|
|
have: int32(90),
|
|
want: time.Second * 90,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeFloat64ToSeconds",
|
|
have: float64(90),
|
|
want: time.Second * 90,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeFloat64ToSeconds",
|
|
have: math.MaxFloat64,
|
|
want: time.Duration(math.MaxInt64),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeInt64ToSeconds",
|
|
have: int64(120),
|
|
want: time.Second * 120,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeTimeDuration",
|
|
have: time.Second * 30,
|
|
want: time.Second * 30,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToString",
|
|
have: int64(30),
|
|
want: "",
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeFromIntZero",
|
|
have: 0,
|
|
want: time.Duration(0),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldSkipParsingBoolean",
|
|
have: true,
|
|
want: time.Duration(0),
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeFromBool",
|
|
have: true,
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
hook := configuration.ToTimeDurationHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestToTimeDurationHookFuncPointer(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeFourtyFiveSeconds",
|
|
have: "45s",
|
|
want: testTimeDurationPtr(time.Second * 45),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeOneMinute",
|
|
have: "1m",
|
|
want: testTimeDurationPtr(time.Minute),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeTwoHours",
|
|
have: "2h",
|
|
want: testTimeDurationPtr(time.Hour * 2),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeThreeDays",
|
|
have: "3d",
|
|
want: testTimeDurationPtr(time.Hour * 24 * 3),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeFourWeeks",
|
|
have: "4w",
|
|
want: testTimeDurationPtr(time.Hour * 24 * 7 * 4),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeFiveMonths",
|
|
have: "5M",
|
|
want: testTimeDurationPtr(time.Hour * 24 * 30 * 5),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeSixYears",
|
|
have: "6y",
|
|
want: testTimeDurationPtr(time.Hour * 24 * 365 * 6),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeInvalidString",
|
|
have: "abc",
|
|
want: testTimeDurationPtr(time.Duration(0)),
|
|
err: "could not decode 'abc' to a *time.Duration: could not parse 'abc' as a duration",
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeIntToSeconds",
|
|
have: 60,
|
|
want: testTimeDurationPtr(time.Second * 60),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeInt32ToSeconds",
|
|
have: int32(90),
|
|
want: testTimeDurationPtr(time.Second * 90),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeInt64ToSeconds",
|
|
have: int64(120),
|
|
want: testTimeDurationPtr(time.Second * 120),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeTimeDuration",
|
|
have: time.Second * 30,
|
|
want: testTimeDurationPtr(time.Second * 30),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToString",
|
|
have: int64(30),
|
|
want: &testString,
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeFromIntZero",
|
|
have: 0,
|
|
want: testTimeDurationPtr(time.Duration(0)),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeFromBool",
|
|
have: true,
|
|
want: &testTrue,
|
|
decode: false,
|
|
},
|
|
}
|
|
|
|
hook := configuration.ToTimeDurationHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToRegexpFunc(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
wantGrps []string
|
|
}{
|
|
{
|
|
desc: "ShouldNotDecodeRegexpWithOpenParenthesis",
|
|
have: "hello(test one two",
|
|
want: regexp.Regexp{},
|
|
err: "could not decode 'hello(test one two' to a regexp.Regexp: error parsing regexp: missing closing ): `hello(test one two`",
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeValidRegex",
|
|
have: "^(api|admin)$",
|
|
want: *regexp.MustCompile(`^(api|admin)$`),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeValidRegexWithGroupNames",
|
|
have: "^(?P<area>api|admin)(one|two)$",
|
|
want: *regexp.MustCompile(`^(?P<area>api|admin)(one|two)$`),
|
|
decode: true,
|
|
wantGrps: []string{"area"},
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeFromInt32",
|
|
have: int32(20),
|
|
want: regexp.Regexp{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeFromBool",
|
|
have: false,
|
|
want: regexp.Regexp{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToBool",
|
|
have: "^(?P<area>api|admin)(one|two)$",
|
|
want: testTrue,
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToInt32",
|
|
have: "^(?P<area>api|admin)(one|two)$",
|
|
want: testInt32Ptr(0),
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToMailAddress",
|
|
have: "^(?P<area>api|admin)(one|two)$",
|
|
want: mail.Address{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldErrOnDecodeEmptyString",
|
|
have: "",
|
|
want: regexp.Regexp{},
|
|
err: "could not decode an empty value to a regexp.Regexp: must have a non-empty value",
|
|
decode: true,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToRegexpHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
|
|
pattern := result.(regexp.Regexp)
|
|
|
|
var names []string
|
|
for _, name := range pattern.SubexpNames() {
|
|
if name != "" {
|
|
names = append(names, name)
|
|
}
|
|
}
|
|
|
|
if len(tc.wantGrps) != 0 {
|
|
t.Run("MustHaveAllExpectedSubexpGroupNames", func(t *testing.T) {
|
|
for _, name := range tc.wantGrps {
|
|
assert.Contains(t, names, name)
|
|
}
|
|
})
|
|
t.Run("MustNotHaveUnexpectedSubexpGroupNames", func(t *testing.T) {
|
|
for _, name := range names {
|
|
assert.Contains(t, tc.wantGrps, name)
|
|
}
|
|
})
|
|
} else {
|
|
t.Run("MustHaveNoSubexpGroupNames", func(t *testing.T) {
|
|
assert.Len(t, names, 0)
|
|
})
|
|
}
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToRegexpFuncPointers(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
wantGrps []string
|
|
}{
|
|
{
|
|
desc: "ShouldNotDecodeRegexpWithOpenParenthesis",
|
|
have: "hello(test one two",
|
|
want: ®exp.Regexp{},
|
|
err: "could not decode 'hello(test one two' to a *regexp.Regexp: error parsing regexp: missing closing ): `hello(test one two`",
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeValidRegex",
|
|
have: "^(api|admin)$",
|
|
want: regexp.MustCompile(`^(api|admin)$`),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeValidRegexWithGroupNames",
|
|
have: "^(?P<area>api|admin)(one|two)$",
|
|
want: regexp.MustCompile(`^(?P<area>api|admin)(one|two)$`),
|
|
decode: true,
|
|
wantGrps: []string{"area"},
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeFromInt32",
|
|
have: int32(20),
|
|
want: ®exp.Regexp{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeFromBool",
|
|
have: false,
|
|
want: ®exp.Regexp{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToBool",
|
|
have: "^(?P<area>api|admin)(one|two)$",
|
|
want: &testTrue,
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToInt32",
|
|
have: "^(?P<area>api|admin)(one|two)$",
|
|
want: &testZero,
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToMailAddress",
|
|
have: "^(?P<area>api|admin)(one|two)$",
|
|
want: &mail.Address{},
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeEmptyStringToNil",
|
|
have: "",
|
|
want: (*regexp.Regexp)(nil),
|
|
decode: true,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToRegexpHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
|
|
pattern := result.(*regexp.Regexp)
|
|
|
|
if tc.want == (*regexp.Regexp)(nil) {
|
|
assert.Nil(t, pattern)
|
|
} else {
|
|
var names []string
|
|
for _, name := range pattern.SubexpNames() {
|
|
if name != "" {
|
|
names = append(names, name)
|
|
}
|
|
}
|
|
|
|
if len(tc.wantGrps) != 0 {
|
|
t.Run("MustHaveAllExpectedSubexpGroupNames", func(t *testing.T) {
|
|
for _, name := range tc.wantGrps {
|
|
assert.Contains(t, names, name)
|
|
}
|
|
})
|
|
t.Run("MustNotHaveUnexpectedSubexpGroupNames", func(t *testing.T) {
|
|
for _, name := range names {
|
|
assert.Contains(t, tc.wantGrps, name)
|
|
}
|
|
})
|
|
} else {
|
|
t.Run("MustHaveNoSubexpGroupNames", func(t *testing.T) {
|
|
assert.Len(t, names, 0)
|
|
})
|
|
}
|
|
}
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToAddressHookFunc(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
have any
|
|
expected any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
name: "ShouldDecodeNonPtr",
|
|
have: "tcp://0.0.0.0:2020",
|
|
expected: MustParseAddress("tcp://0.0.0.0:2020"),
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldDecodePtr",
|
|
have: "tcp://0.0.0.0:2020",
|
|
expected: MustParseAddressPtr("tcp://0.0.0.0:2020"),
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldNotDecodeIntegerToCorrectType",
|
|
have: 1,
|
|
expected: schema.Address{},
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldNotDecodeIntegerToCorrectTypePtr",
|
|
have: 1,
|
|
expected: &schema.Address{},
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldNotDecodeIntegerPtrToCorrectType",
|
|
have: testInt32Ptr(1),
|
|
expected: schema.Address{},
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldNotDecodeIntegerPtrToCorrectTypePtr",
|
|
have: testInt32Ptr(1),
|
|
expected: &schema.Address{},
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldNotDecodeToString",
|
|
have: "tcp://0.0.0.0:2020",
|
|
expected: "",
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldNotDecodeToIntPtr",
|
|
have: "tcp://0.0.0.0:2020",
|
|
expected: testInt32Ptr(1),
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldNotDecodeToIntPtr",
|
|
have: "tcp://0.0.0.0:2020",
|
|
expected: testInt32Ptr(1),
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldFailDecode",
|
|
have: "tcp://&!@^#*&!@#&*@!:2020",
|
|
expected: schema.Address{},
|
|
err: "could not decode 'tcp://&!@^#*&!@#&*@!:2020' to a schema.Address: could not parse string 'tcp://&!@^#*&!@#&*@!:2020' as address: expected format is [<scheme>://]<hostname>[:<port>]: parse \"tcp://&!@^\": invalid character \"^\" in host name",
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldDecodeTCP",
|
|
have: "tcp://127.0.0.1",
|
|
expected: schema.AddressTCP{Address: MustParseAddress("tcp://127.0.0.1")},
|
|
err: "",
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldDecodeTCPPtr",
|
|
have: "tcp://127.0.0.1",
|
|
expected: &schema.AddressTCP{Address: MustParseAddress("tcp://127.0.0.1")},
|
|
err: "",
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldDecodeUDP",
|
|
have: "udp://127.0.0.1",
|
|
expected: schema.AddressUDP{Address: MustParseAddress("udp://127.0.0.1")},
|
|
err: "",
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldDecodeUDPPtr",
|
|
have: "udp://127.0.0.1",
|
|
expected: &schema.AddressUDP{Address: MustParseAddress("udp://127.0.0.1")},
|
|
err: "",
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldDecodeLDAP",
|
|
have: "ldap://127.0.0.1",
|
|
expected: schema.AddressLDAP{Address: MustParseAddress("ldap://127.0.0.1")},
|
|
err: "",
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldDecodeLDAPPtr",
|
|
have: "ldap://127.0.0.1",
|
|
expected: &schema.AddressLDAP{Address: MustParseAddress("ldap://127.0.0.1")},
|
|
err: "",
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldDecodeSMTP",
|
|
have: "smtp://127.0.0.1",
|
|
expected: schema.AddressSMTP{Address: MustParseAddress("smtp://127.0.0.1")},
|
|
err: "",
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldDecodeSMTPPtr",
|
|
have: "smtp://127.0.0.1",
|
|
expected: &schema.AddressSMTP{Address: MustParseAddress("smtp://127.0.0.1")},
|
|
err: "",
|
|
decode: true,
|
|
},
|
|
{
|
|
name: "ShouldFailDecodeTCP",
|
|
have: "@@@@@@@",
|
|
expected: schema.AddressTCP{Address: MustParseAddress("tcp://127.0.0.1")},
|
|
err: "could not decode '@@@@@@@' to a schema.AddressTCP: error validating the address: the url 'tcp://%40%40%40%40%40%40@' appears to have user info but this is not valid for addresses",
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldFailDecodeUDP",
|
|
have: "@@@@@@@",
|
|
expected: schema.AddressUDP{Address: MustParseAddress("udp://127.0.0.1")},
|
|
err: "could not decode '@@@@@@@' to a schema.AddressUDP: error validating the address: the url 'udp://%40%40%40%40%40%40@' appears to have user info but this is not valid for addresses",
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldFailDecodeLDAP",
|
|
have: "@@@@@@@",
|
|
expected: schema.AddressLDAP{Address: MustParseAddress("ldap://127.0.0.1")},
|
|
err: "could not decode '@@@@@@@' to a schema.AddressLDAP: error validating the address: the url 'ldaps://%40%40%40%40%40%40@' appears to have user info but this is not valid for addresses",
|
|
decode: false,
|
|
},
|
|
{
|
|
name: "ShouldFailDecodeSMTP",
|
|
have: "@@@@@@@",
|
|
expected: schema.AddressSMTP{Address: MustParseAddress("smtp://127.0.0.1")},
|
|
err: "could not decode '@@@@@@@' to a schema.AddressSMTP: error validating the address: the url 'smtp://%40%40%40%40%40%40@' appears to have user info but this is not valid for addresses",
|
|
decode: false,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToAddressHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
actual, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.expected), tc.have)
|
|
if tc.err != "" {
|
|
assert.EqualError(t, err, tc.err)
|
|
|
|
if !tc.decode {
|
|
assert.Nil(t, actual)
|
|
}
|
|
} else {
|
|
assert.NoError(t, err)
|
|
|
|
if tc.decode {
|
|
assert.Equal(t, tc.expected, actual)
|
|
} else {
|
|
assert.Equal(t, tc.have, actual)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToPrivateKeyHookFunc(t *testing.T) {
|
|
var (
|
|
nilRSA *rsa.PrivateKey
|
|
nilECDSA *ecdsa.PrivateKey
|
|
nilCert *x509.Certificate
|
|
)
|
|
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeRSAPrivateKey",
|
|
have: x509PrivateKeyRSA1,
|
|
want: MustParseRSAPrivateKey(x509PrivateKeyRSA1),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeECDSAPrivateKey",
|
|
have: x509PrivateKeyEC1,
|
|
want: MustParseECDSAPrivateKey(x509PrivateKeyEC1),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeToECDSAPrivateKey",
|
|
have: x509PrivateKeyRSA1,
|
|
want: &ecdsa.PrivateKey{},
|
|
decode: true,
|
|
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *rsa.PrivateKey not a *ecdsa.PrivateKey",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeEmptyRSAKey",
|
|
have: "",
|
|
want: nilRSA,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeEmptyECDSAKey",
|
|
have: "",
|
|
want: nilECDSA,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeECDSAKeyToRSAKey",
|
|
have: x509PrivateKeyEC1,
|
|
want: nilRSA,
|
|
decode: true,
|
|
err: "could not decode to a *rsa.PrivateKey: the data is for a *ecdsa.PrivateKey not a *rsa.PrivateKey",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeRSAKeyToECDSAKey",
|
|
have: x509PrivateKeyRSA1,
|
|
want: nilECDSA,
|
|
decode: true,
|
|
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *rsa.PrivateKey not a *ecdsa.PrivateKey",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBadRSAPrivateKey",
|
|
have: x509PrivateKeyRSA2,
|
|
want: nilRSA,
|
|
decode: true,
|
|
err: "could not decode to a *rsa.PrivateKey: failed to parse PEM block containing the key",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBadECDSAPrivateKey",
|
|
have: x509PrivateKeyEC2,
|
|
want: nilECDSA,
|
|
decode: true,
|
|
err: "could not decode to a *ecdsa.PrivateKey: failed to parse PEM block containing the key",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeCertificateToRSAPrivateKey",
|
|
have: x509CertificateRSA1,
|
|
want: nilRSA,
|
|
decode: true,
|
|
err: "could not decode to a *rsa.PrivateKey: the data is for a *x509.Certificate not a *rsa.PrivateKey",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeCertificateToECDSAPrivateKey",
|
|
have: x509CertificateRSA1,
|
|
want: nilECDSA,
|
|
decode: true,
|
|
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *x509.Certificate not a *ecdsa.PrivateKey",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeRSAKeyToCertificate",
|
|
have: x509PrivateKeyRSA1,
|
|
want: nilCert,
|
|
decode: false,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeECDSAKeyToCertificate",
|
|
have: x509PrivateKeyEC1,
|
|
want: nilCert,
|
|
decode: false,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToPrivateKeyHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToX509CertificateHookFunc(t *testing.T) {
|
|
var nilkey *x509.Certificate
|
|
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
want any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeRSACertificate",
|
|
have: x509CertificateRSA1,
|
|
want: MustParseX509Certificate(x509CertificateRSA1),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeECDSACertificate",
|
|
have: x509CACertificateECDSA,
|
|
want: MustParseX509Certificate(x509CACertificateECDSA),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeRSACACertificate",
|
|
have: x509CACertificateRSA,
|
|
want: MustParseX509Certificate(x509CACertificateRSA),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeECDSACACertificate",
|
|
have: x509CACertificateECDSA,
|
|
want: MustParseX509Certificate(x509CACertificateECDSA),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeEmptyCertificateToNil",
|
|
have: "",
|
|
want: nilkey,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeECDSAKeyToCertificate",
|
|
have: x509PrivateKeyEC1,
|
|
want: nilkey,
|
|
decode: true,
|
|
err: "could not decode to a *x509.Certificate: the data is for a *ecdsa.PrivateKey not a *x509.Certificate",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBadRSAPrivateKeyToCertificate",
|
|
have: x509PrivateKeyRSA2,
|
|
want: nilkey,
|
|
decode: true,
|
|
err: "could not decode to a *x509.Certificate: failed to parse PEM block containing the key",
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToX509CertificateHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
result, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.want), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, result)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.want, result)
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToPasswordDigestHookFunc(t *testing.T) {
|
|
var nilvalue *schema.PasswordDigest
|
|
|
|
testCases := []struct {
|
|
name string
|
|
have any
|
|
expected any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
"ShouldParse",
|
|
"$plaintext$example",
|
|
MustParsePasswordDigest("$plaintext$example"),
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"ShouldParsePtr",
|
|
"$plaintext$example",
|
|
MustParsePasswordDigestPtr("$plaintext$example"),
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"ShouldNotParseUnknown",
|
|
"$abc$example",
|
|
schema.PasswordDigest{},
|
|
"could not decode '$abc$example' to a schema.PasswordDigest: provided encoded hash has an invalid identifier: the identifier 'abc' is unknown to the decoder",
|
|
false,
|
|
},
|
|
{
|
|
"ShouldNotParseWrongType",
|
|
"$abc$example",
|
|
schema.TLSVersion{},
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"ShouldNotParseWrongTypePtr",
|
|
"$abc$example",
|
|
&schema.TLSVersion{},
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"ShouldNotParseEmptyString",
|
|
"",
|
|
schema.PasswordDigest{},
|
|
"could not decode an empty value to a schema.PasswordDigest: must have a non-empty value",
|
|
false,
|
|
},
|
|
{
|
|
"ShouldParseEmptyStringPtr",
|
|
"",
|
|
nilvalue,
|
|
"",
|
|
true,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToPasswordDigestHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
actual, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.expected), tc.have)
|
|
if tc.err != "" {
|
|
assert.EqualError(t, err, tc.err)
|
|
|
|
if !tc.decode {
|
|
assert.Nil(t, actual)
|
|
}
|
|
} else {
|
|
assert.NoError(t, err)
|
|
|
|
if tc.decode {
|
|
assert.Equal(t, tc.expected, actual)
|
|
} else {
|
|
assert.Equal(t, tc.have, actual)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToTLSVersionHookFunc(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
have any
|
|
expected any
|
|
err string
|
|
decode bool
|
|
}{
|
|
{
|
|
"ShouldParseTLS1.3",
|
|
"TLS1.3",
|
|
schema.TLSVersion{Value: tls.VersionTLS13},
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"ShouldParseTLS1.3PTR",
|
|
"TLS1.3",
|
|
&schema.TLSVersion{Value: tls.VersionTLS13},
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"ShouldParseTLS1.2",
|
|
"TLS1.2",
|
|
schema.TLSVersion{Value: tls.VersionTLS12},
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"ShouldNotParseInt",
|
|
1,
|
|
&schema.TLSVersion{},
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"ShouldNotParseNonVersion",
|
|
"1",
|
|
&schema.TLSVersion{},
|
|
"could not decode '1' to a *schema.TLSVersion: supplied tls version isn't supported",
|
|
false,
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToTLSVersionHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
actual, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.expected), tc.have)
|
|
if tc.err != "" {
|
|
assert.EqualError(t, err, tc.err)
|
|
|
|
if !tc.decode {
|
|
assert.Nil(t, actual)
|
|
}
|
|
} else {
|
|
assert.NoError(t, err)
|
|
|
|
if tc.decode {
|
|
assert.Equal(t, tc.expected, actual)
|
|
} else {
|
|
assert.Equal(t, tc.have, actual)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringToX509CertificateChainHookFunc(t *testing.T) {
|
|
var nilkey *schema.X509CertificateChain
|
|
|
|
testCases := []struct {
|
|
desc string
|
|
have any
|
|
expected any
|
|
err, verr string
|
|
decode bool
|
|
}{
|
|
{
|
|
desc: "ShouldDecodeRSACertificate",
|
|
have: x509CertificateRSA1,
|
|
expected: MustParseX509CertificateChain(x509CertificateRSA1),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeRSACertificateNoPtr",
|
|
have: x509CertificateRSA1,
|
|
expected: *MustParseX509CertificateChain(x509CertificateRSA1),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeRSACertificateChain",
|
|
have: BuildChain(x509CertificateRSA1, x509CACertificateRSA),
|
|
expected: MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeRSACertificateChainNoPtr",
|
|
have: BuildChain(x509CertificateRSA1, x509CACertificateRSA),
|
|
expected: *MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBadRSACertificateChain",
|
|
have: BuildChain(x509CertificateRSA1, x509CACertificateECDSA),
|
|
expected: MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateECDSA),
|
|
verr: "certificate #1 in chain is not signed properly by certificate #2 in chain: x509: signature algorithm specifies an RSA public key, but have public key of type *ecdsa.PublicKey",
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeECDSACertificate",
|
|
have: x509CACertificateECDSA,
|
|
expected: MustParseX509CertificateChain(x509CACertificateECDSA),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeRSACACertificate",
|
|
have: x509CACertificateRSA,
|
|
expected: MustParseX509CertificateChain(x509CACertificateRSA),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeECDSACACertificate",
|
|
have: x509CACertificateECDSA,
|
|
expected: MustParseX509CertificateChain(x509CACertificateECDSA),
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeEmptyCertificateToNil",
|
|
have: "",
|
|
expected: nilkey,
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldDecodeEmptyCertificateToEmptyStruct",
|
|
have: "",
|
|
expected: schema.X509CertificateChain{},
|
|
decode: true,
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeECDSAKeyToCertificate",
|
|
have: x509PrivateKeyEC1,
|
|
expected: nilkey,
|
|
decode: true,
|
|
err: "could not decode to a *schema.X509CertificateChain: the PEM data chain contains a EC PRIVATE KEY but only certificates are expected",
|
|
},
|
|
{
|
|
desc: "ShouldNotDecodeBadRSAPrivateKeyToCertificate",
|
|
have: x509PrivateKeyRSA2,
|
|
expected: nilkey,
|
|
decode: true,
|
|
err: "could not decode to a *schema.X509CertificateChain: invalid PEM block",
|
|
},
|
|
}
|
|
|
|
hook := configuration.StringToX509CertificateChainHookFunc()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
actual, err := hook(reflect.TypeOf(tc.have), reflect.TypeOf(tc.expected), tc.have)
|
|
switch {
|
|
case !tc.decode:
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.have, actual)
|
|
case tc.err == "":
|
|
assert.NoError(t, err)
|
|
require.Equal(t, tc.expected, actual)
|
|
|
|
if tc.expected == nilkey {
|
|
break
|
|
}
|
|
|
|
switch chain := actual.(type) {
|
|
case *schema.X509CertificateChain:
|
|
require.NotNil(t, chain)
|
|
if tc.verr == "" {
|
|
assert.NoError(t, chain.Validate())
|
|
} else {
|
|
assert.EqualError(t, chain.Validate(), tc.verr)
|
|
}
|
|
case schema.X509CertificateChain:
|
|
require.NotNil(t, chain)
|
|
if tc.verr == "" {
|
|
assert.NoError(t, chain.Validate())
|
|
} else {
|
|
assert.EqualError(t, chain.Validate(), tc.verr)
|
|
}
|
|
}
|
|
default:
|
|
assert.EqualError(t, err, tc.err)
|
|
assert.Nil(t, actual)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
var (
|
|
x509PrivateKeyRSA1 = `
|
|
-----BEGIN RSA PRIVATE KEY-----
|
|
MIIEpAIBAAKCAQEA6z1LOg1ZCqb0lytXWZ+MRBpMHEXOoTOLYgfZXt1IYyE3Z758
|
|
cyalk0NYQhY5cZDsXPYWPvAHiPMUxutWkoxFwby56S+AbIMa3/Is+ILrHRJs8Exn
|
|
ZkpyrYFxPX12app2kErdmAkHSx0Z5/kuXiz96PHs8S8/ZbyZolLHzdfLtSzjvRm5
|
|
Zue5iFzsf19NJz5CIBfv8g5lRwtE8wNJoRSpn1xq7fqfuA0weDNFPzjlNWRLy6aa
|
|
rK7qJexRkmkCs4sLgyl+9NODYJpvmN8E1yhyC27E0joI6rBFVW7Ihv+cSPCdDzGp
|
|
EWe81x3AeqAa3mjVqkiq4u4Z2i8JDgBaPboqJwIDAQABAoIBAAFdLZ58jVOefDSU
|
|
L8F5R1rtvBs93GDa56f926jNJ6pLewLC+/2+757W+SAI+PRLntM7Kg3bXm/Q2QH+
|
|
Q1Y+MflZmspbWCdI61L5GIGoYKyeers59i+FpvySj5GHtLQRiTZ0+Kv1AXHSDWBm
|
|
9XneUOqU3IbZe0ifu1RRno72/VtjkGXbW8Mkkw+ohyGbIeTx/0/JQ6sSNZTT3Vk7
|
|
8i4IXptq3HSF0/vqZuah8rShoeNq72pD1YLM9YPdL5by1QkDLnqATDiCpLBTCaNV
|
|
I8sqYEun+HYbQzBj8ZACG2JVZpEEidONWQHw5BPWO95DSZYrVnEkuCqeH+u5vYt7
|
|
CHuJ3AECgYEA+W3v5z+j91w1VPHS0VB3SCDMouycAMIUnJPAbt+0LPP0scUFsBGE
|
|
hPAKddC54pmMZRQ2KIwBKiyWfCrJ8Xz8Yogn7fJgmwTHidJBr2WQpIEkNGlK3Dzi
|
|
jXL2sh0yC7sHvn0DqiQ79l/e7yRbSnv2wrTJEczOOH2haD7/tBRyCYECgYEA8W+q
|
|
E9YyGvEltnPFaOxofNZ8LHVcZSsQI5b6fc0iE7fjxFqeXPXEwGSOTwqQLQRiHn9b
|
|
CfPmIG4Vhyq0otVmlPvUnfBZ2OK+tl5X2/mQFO3ROMdvpi0KYa994uqfJdSTaqLn
|
|
jjoKFB906UFHnDQDLZUNiV1WwnkTglgLc+xrd6cCgYEAqqthyv6NyBTM3Tm2gcio
|
|
Ra9Dtntl51LlXZnvwy3IkDXBCd6BHM9vuLKyxZiziGx+Vy90O1xI872cnot8sINQ
|
|
Am+dur/tAEVN72zxyv0Y8qb2yfH96iKy9gxi5s75TnOEQgAygLnYWaWR2lorKRUX
|
|
bHTdXBOiS58S0UzCFEslGIECgYBqkO4SKWYeTDhoKvuEj2yjRYyzlu28XeCWxOo1
|
|
otiauX0YSyNBRt2cSgYiTzhKFng0m+QUJYp63/wymB/5C5Zmxi0XtWIDADpLhqLj
|
|
HmmBQ2Mo26alQ5YkffBju0mZyhVzaQop1eZi8WuKFV1FThPlB7hc3E0SM5zv2Grd
|
|
tQnOWwKBgQC40yZY0PcjuILhy+sIc0Wvh7LUA7taSdTye149kRvbvsCDN7Jh75lM
|
|
USjhLXY0Nld2zBm9r8wMb81mXH29uvD+tDqqsICvyuKlA/tyzXR+QTr7dCVKVwu0
|
|
1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw==
|
|
-----END RSA PRIVATE KEY-----`
|
|
|
|
x509PrivateKeyRSA2 = `
|
|
-----BEGIN RSA PRIVATE KEY-----
|
|
bad key
|
|
-----END RSA PRIVATE KEY-----`
|
|
|
|
x509PrivateKeyEC1 = `
|
|
-----BEGIN EC PRIVATE KEY-----
|
|
MHcCAQEEIMn970LSn8aKVhBM4vyUmpZyEdCT4riN+Lp4QU04zUhYoAoGCCqGSM49
|
|
AwEHoUQDQgAEMD69n22nd78GmaRDzy/s7muqhbc/OEnFS2mNtiRAA5FaX+kbkCB5
|
|
8pu/k2jkaSVNZtBYKPVAibHkhvakjVb66A==
|
|
-----END EC PRIVATE KEY-----`
|
|
|
|
x509PrivateKeyEC2 = `
|
|
-----BEGIN EC PRIVATE KEY-----
|
|
bad key
|
|
-----END EC PRIVATE KEY-----`
|
|
|
|
x509CertificateRSA1 = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIC5TCCAc2gAwIBAgIQfBUmKLmEvMqS6S9auKCY2DANBgkqhkiG9w0BAQsFADAT
|
|
MREwDwYDVQQKEwhBdXRoZWxpYTAeFw0yMjA5MDgxMDA5MThaFw0yMzA5MDgxMDA5
|
|
MThaMBMxETAPBgNVBAoTCEF1dGhlbGlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
|
MIIBCgKCAQEApqno1cOpDcgKmOJqeDQGIGH5/ZnqcJ4xud6eOUfbDqel3b0RkAQX
|
|
mFYWEDO/PDOAOjYk/xSwZGo3jDofOHGhrKstQqLdweHGfme5NXYHJda7nGv/OY5q
|
|
zUuEG4xBVgUsvbshWZ18H+bIQpwiP6tDAabxc0B7J15F1pArK8QN4pDTfsqZDwMi
|
|
Qyo638XfUbDzEVZRbdDKxHz5g0w2vFdXon8uOxRRb0+zlHF9nM4PiESNgiUIYeua
|
|
8Q5yP10SY2k9zlQ/OFJ4XhQmioCJvNjJE/TSc5/ECub2n7hTZhN5TGKagukZ5NAy
|
|
KgbvNYW+CN+H4pFJt/9WptiDfBqhlUvjnwIDAQABozUwMzAOBgNVHQ8BAf8EBAMC
|
|
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0B
|
|
AQsFAAOCAQEAH9veGzfWqXxsa5s2KHV2Jzed9V8KSs1Qy9QKRez1i2OMvMPh2DRM
|
|
RLzAAp/XigjxLQF/LFXuoFW0Qg8BRb44iRgZrCiqVOUnd3xTrS/CcFExnpQI4F12
|
|
/U70o97rkTonCOHmUUW6vQfWSXR/GU3/faRLJjiqcpWLZhTQNrnsip1ym3B2NMdk
|
|
gMKkT8Acx1DX48MvTE4+DyqCS8TlJbacBJ2RFFELKu3jYnVNyrb0ywLxoCtWqBBE
|
|
veVj+VMn9hNY1u5uydLsUDOlT5QyQcEuUzjjdhsJKEgDE5daNtB2OJJnd9IOMzUA
|
|
hasPZETCCKabTpWiEPw1Cn/ZRqya0SZqFg==
|
|
-----END CERTIFICATE-----
|
|
`
|
|
|
|
x509CACertificateRSA = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIDBDCCAeygAwIBAgIRAJfz0dHS9UkDngE55lUPdu4wDQYJKoZIhvcNAQELBQAw
|
|
EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNMjIwOTA4MDk1OTI1WhcNMjMwOTA4MDk1
|
|
OTI1WjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
|
ADCCAQoCggEBALfivbwq9r5X8N+NSbNHVuKbCb9f9vD5Xw2pOjSVvVjFkWQ1YKJu
|
|
JGx9yskhHBZTBt76cInipA+0PqCBrBrjij1lh2StvzRVuQwgFG6H01LxBPi0JyYv
|
|
Is94F6PHr6fSBgFWB5GNQ797KQIOdIr057uEFbp0eBMxxqiQ9gdyD0HPretrx1Uy
|
|
kHuF6jck958combn9luHW0i53mt8706j7UAhxFqu9YUeklTM1VqUiRm5+nJKIdNA
|
|
LiDMGVAuoxjhF6aIgY0yh5mL5mKtYYzhtA8WryrMzBgFRUGzHCSI1TNisA8wSf2T
|
|
Z2JhbFHrFPR5fiSqAEHok3UXu++wsfl/lisCAwEAAaNTMFEwDgYDVR0PAQH/BAQD
|
|
AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
|
OSXG42bCPNuWeP0ahScUMVjxe/wwDQYJKoZIhvcNAQELBQADggEBAFRnubHiYy1H
|
|
PGODKA++UY9eAmmaCJxzuWpY8FY9fBz8/VBzcp8vaURPmmQ/34QcqfaSHDM2jIaL
|
|
dQ2o9Ae5NjbRzLB6a5DcVO50oHG4BHP1ix4Bt3POr8J80KgA9pOIyAQqbAlFBSzQ
|
|
l9yrzVULyf+qpUmByRf5qy2kQJOBfMJbn5j+BprWKwbcI8OAZWWSLItTXqJDrFTk
|
|
OMZK4wZ6KiZM07KWMlwW/CE0QRzDk5MXfbwRt4D8pyx6rGKqI7QRusjm5osIpHZV
|
|
26FdBdBvEhq4i8UsmDsQqH3iMY1AKmojZToZb5rStOZWHO/BZZ7nT2bscNjwm0E8
|
|
6E2l6czk8ss=
|
|
-----END CERTIFICATE-----`
|
|
|
|
x509CACertificateECDSA = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIBdzCCAR2gAwIBAgIQUzb62irYb/7B2H0c1AbriDAKBggqhkjOPQQDAjATMREw
|
|
DwYDVQQKEwhBdXRoZWxpYTAeFw0yMjA5MDgxMDEzNDZaFw0yMzA5MDgxMDEzNDZa
|
|
MBMxETAPBgNVBAoTCEF1dGhlbGlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
|
|
b/EiIBpmifCI34JdI7luetygue2rTtoNH0QXhtrjMuZNugT29LUz+DobZQxvGsOY
|
|
4TXzAQXq4gnTb7enNWFgsaNTMFEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdJQQIMAYG
|
|
BFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUxlDPBKHKawuvhtQTN874
|
|
TeCEKjkwCgYIKoZIzj0EAwIDSAAwRQIgAQeV01FZ/VkSERwaRKTeXAXxmKyc/05O
|
|
uDv6M2spMi0CIQC8uOSMcv11vp1ylsGg38N6XYA+GQa1BHRd79+91hC+7w==
|
|
-----END CERTIFICATE-----`
|
|
)
|
|
|
|
func MustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
|
block, _ := pem.Decode([]byte(data))
|
|
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
|
panic("not pem encoded")
|
|
}
|
|
|
|
if block.Type != "RSA PRIVATE KEY" {
|
|
panic("not rsa private key")
|
|
}
|
|
|
|
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return key
|
|
}
|
|
|
|
func MustParseECDSAPrivateKey(data string) *ecdsa.PrivateKey {
|
|
block, _ := pem.Decode([]byte(data))
|
|
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
|
panic("not pem encoded")
|
|
}
|
|
|
|
if block.Type != "EC PRIVATE KEY" {
|
|
panic("not ecdsa private key")
|
|
}
|
|
|
|
key, err := x509.ParseECPrivateKey(block.Bytes)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return key
|
|
}
|
|
|
|
func MustParseX509Certificate(data string) *x509.Certificate {
|
|
block, _ := pem.Decode([]byte(data))
|
|
if block == nil || len(block.Bytes) == 0 {
|
|
panic("not a PEM")
|
|
}
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return cert
|
|
}
|
|
|
|
func BuildChain(pems ...string) string {
|
|
buf := bytes.Buffer{}
|
|
|
|
for i, data := range pems {
|
|
if i != 0 {
|
|
buf.WriteString("\n")
|
|
}
|
|
|
|
buf.WriteString(data)
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func MustParseX509CertificateChain(datas ...string) *schema.X509CertificateChain {
|
|
chain, err := schema.NewX509CertificateChain(BuildChain(datas...))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return chain
|
|
}
|
|
|
|
func testInt32Ptr(i int32) *int32 {
|
|
return &i
|
|
}
|
|
|
|
func testTimeDurationPtr(t time.Duration) *time.Duration {
|
|
return &t
|
|
}
|
|
|
|
var (
|
|
testTrue = true
|
|
testZero int32
|
|
testString = ""
|
|
)
|
|
|
|
func MustParseAddress(input string) schema.Address {
|
|
address, err := schema.NewAddress(input)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fmt.Println(address.String())
|
|
addr := *address
|
|
|
|
return addr
|
|
}
|
|
|
|
func MustParseAddressPtr(input string) *schema.Address {
|
|
address, err := schema.NewAddress(input)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return address
|
|
}
|
|
|
|
func MustParsePasswordDigest(input string) schema.PasswordDigest {
|
|
digest, err := schema.DecodePasswordDigest(input)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return *digest
|
|
}
|
|
|
|
func MustParsePasswordDigestPtr(input string) *schema.PasswordDigest {
|
|
digest, err := schema.DecodePasswordDigest(input)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return digest
|
|
}
|