authelia/internal/configuration/decode_hooks.go

255 lines
5.3 KiB
Go

package configuration
import (
"fmt"
"net/mail"
"net/url"
"reflect"
"regexp"
"time"
"github.com/mitchellh/mapstructure"
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/utils"
)
// StringToMailAddressHookFunc decodes a string into a mail.Address or *mail.Address.
func StringToMailAddressHookFunc() mapstructure.DecodeHookFuncType {
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
var ptr bool
if f.Kind() != reflect.String {
return data, nil
}
kindStr := "mail.Address (RFC5322)"
if t.Kind() == reflect.Ptr {
ptr = true
kindStr = "*" + kindStr
}
expectedType := reflect.TypeOf(mail.Address{})
if ptr && t.Elem() != expectedType {
return data, nil
} else if !ptr && t != expectedType {
return data, nil
}
dataStr := data.(string)
var result *mail.Address
if dataStr != "" {
if result, err = mail.ParseAddress(dataStr); err != nil {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
}
}
if ptr {
return result, nil
}
if result == nil {
return mail.Address{}, nil
}
return *result, nil
}
}
// StringToURLHookFunc converts string types into a url.URL or *url.URL.
func StringToURLHookFunc() mapstructure.DecodeHookFuncType {
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
var ptr bool
if f.Kind() != reflect.String {
return data, nil
}
kindStr := "url.URL"
if t.Kind() == reflect.Ptr {
ptr = true
kindStr = "*" + kindStr
}
expectedType := reflect.TypeOf(url.URL{})
if ptr && t.Elem() != expectedType {
return data, nil
} else if !ptr && t != expectedType {
return data, nil
}
dataStr := data.(string)
var result *url.URL
if dataStr != "" {
if result, err = url.Parse(dataStr); err != nil {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
}
}
if ptr {
return result, nil
}
if result == nil {
return url.URL{}, nil
}
return *result, nil
}
}
// ToTimeDurationHookFunc converts string and integer types to a time.Duration.
func ToTimeDurationHookFunc() mapstructure.DecodeHookFuncType {
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
var ptr bool
switch f.Kind() {
case reflect.String, reflect.Int, reflect.Int32, reflect.Int64:
// We only allow string and integer from kinds to match.
break
default:
return data, nil
}
kindStr := "time.Duration"
if t.Kind() == reflect.Ptr {
ptr = true
kindStr = "*" + kindStr
}
expectedType := reflect.TypeOf(time.Duration(0))
if ptr && t.Elem() != expectedType {
return data, nil
} else if !ptr && t != expectedType {
return data, nil
}
var result time.Duration
switch {
case f.Kind() == reflect.String:
dataStr := data.(string)
if result, err = utils.ParseDurationString(dataStr); err != nil {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
}
case f.Kind() == reflect.Int:
seconds := data.(int)
result = time.Second * time.Duration(seconds)
case f.Kind() == reflect.Int32:
seconds := data.(int32)
result = time.Second * time.Duration(seconds)
case f == expectedType:
result = data.(time.Duration)
case f.Kind() == reflect.Int64:
seconds := data.(int64)
result = time.Second * time.Duration(seconds)
}
if ptr {
return &result, nil
}
return result, nil
}
}
// StringToRegexpHookFunc decodes a string into a *regexp.Regexp or regexp.Regexp.
func StringToRegexpHookFunc() mapstructure.DecodeHookFuncType {
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
var ptr bool
if f.Kind() != reflect.String {
return data, nil
}
kindStr := "regexp.Regexp"
if t.Kind() == reflect.Ptr {
ptr = true
kindStr = "*" + kindStr
}
expectedType := reflect.TypeOf(regexp.Regexp{})
if ptr && t.Elem() != expectedType {
return data, nil
} else if !ptr && t != expectedType {
return data, nil
}
dataStr := data.(string)
var result *regexp.Regexp
if dataStr != "" {
if result, err = regexp.Compile(dataStr); err != nil {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
}
}
if ptr {
return result, nil
}
if result == nil {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseEmptyValue, kindStr, errDecodeNonPtrMustHaveValue)
}
return *result, nil
}
}
// StringToAddressHookFunc decodes a string into an Address or *Address.
func StringToAddressHookFunc() mapstructure.DecodeHookFuncType {
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
var ptr bool
if f.Kind() != reflect.String {
return data, nil
}
kindStr := "Address"
if t.Kind() == reflect.Ptr {
ptr = true
kindStr = "*" + kindStr
}
expectedType := reflect.TypeOf(schema.Address{})
if ptr && t.Elem() != expectedType {
return data, nil
} else if !ptr && t != expectedType {
return data, nil
}
dataStr := data.(string)
var result *schema.Address
if result, err = schema.NewAddressFromString(dataStr); err != nil {
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, kindStr, err)
}
if ptr {
return result, nil
}
return *result, nil
}
}