authelia/internal/templates/funcs.go

493 lines
13 KiB
Go

package templates
import (
"crypto/sha1" //nolint:gosec
"crypto/sha256"
"crypto/sha512"
"encoding/base32"
"encoding/base64"
"encoding/hex"
"fmt"
"hash"
"net/url"
"os"
"path"
"path/filepath"
"reflect"
"sort"
"strconv"
"strings"
"syscall"
"time"
"github.com/google/uuid"
)
// FuncMap returns the template FuncMap commonly used in several templates.
func FuncMap() map[string]any {
return map[string]any{
"iterate": FuncIterate,
"fileContent": FuncFileContent,
"secret": FuncSecret,
"env": FuncGetEnv,
"mustEnv": FuncMustGetEnv,
"expandenv": FuncExpandEnv,
"split": FuncStringSplit,
"splitList": FuncStringSplitList,
"join": FuncElemsJoin,
"contains": FuncStringContains,
"hasPrefix": FuncStringHasPrefix,
"hasSuffix": FuncStringHasSuffix,
"lower": strings.ToLower,
"keys": FuncKeys,
"sortAlpha": FuncSortAlpha,
"upper": strings.ToUpper,
"title": strings.ToTitle,
"trim": strings.TrimSpace,
"trimAll": FuncStringTrimAll,
"trimSuffix": FuncStringTrimSuffix,
"trimPrefix": FuncStringTrimPrefix,
"replace": FuncStringReplace,
"quote": FuncStringQuote,
"mquote": FuncStringQuoteMultiLine(rune(34)),
"sha1sum": FuncHashSum(sha1.New),
"sha256sum": FuncHashSum(sha256.New),
"sha512sum": FuncHashSum(sha512.New),
"squote": FuncStringSQuote,
"msquote": FuncStringQuoteMultiLine(rune(39)),
"now": time.Now,
"b64enc": FuncB64Enc,
"b64dec": FuncB64Dec,
"b32enc": FuncB32Enc,
"b32dec": FuncB32Dec,
"list": FuncList,
"dict": FuncDict,
"get": FuncGet,
"set": FuncSet,
"isAbs": path.IsAbs,
"base": path.Base,
"dir": path.Dir,
"ext": path.Ext,
"clean": path.Clean,
"osBase": filepath.Base,
"osClean": filepath.Clean,
"osDir": filepath.Dir,
"osExt": filepath.Ext,
"osIsAbs": filepath.IsAbs,
"deepEqual": reflect.DeepEqual,
"typeOf": FuncTypeOf,
"typeIs": FuncTypeIs,
"typeIsLike": FuncTypeIsLike,
"kindOf": FuncKindOf,
"kindIs": FuncKindIs,
"default": FuncDefault,
"empty": FuncEmpty,
"indent": FuncIndent,
"nindent": FuncNewlineIndent,
"mindent": FuncMultilineIndent,
"uuidv4": FuncUUIDv4,
"urlquery": url.QueryEscape,
"urlunquery": url.QueryUnescape,
}
}
// FuncB64Enc is a helper function that provides similar functionality to the helm b64enc func.
func FuncB64Enc(input string) string {
return base64.StdEncoding.EncodeToString([]byte(input))
}
// FuncB64Dec is a helper function that provides similar functionality to the helm b64dec func.
func FuncB64Dec(input string) (string, error) {
data, err := base64.StdEncoding.DecodeString(input)
if err != nil {
return "", err
}
return string(data), nil
}
// FuncB32Enc is a helper function that provides similar functionality to the helm b32enc func.
func FuncB32Enc(input string) string {
return base32.StdEncoding.EncodeToString([]byte(input))
}
// FuncB32Dec is a helper function that provides similar functionality to the helm b32dec func.
func FuncB32Dec(input string) (string, error) {
data, err := base32.StdEncoding.DecodeString(input)
if err != nil {
return "", err
}
return string(data), nil
}
// FuncExpandEnv is a special version of os.ExpandEnv that excludes secret keys.
func FuncExpandEnv(s string) string {
return os.Expand(s, FuncGetEnv)
}
// FuncGetEnv is a special version of os.GetEnv that excludes secret keys.
func FuncGetEnv(key string) string {
if key == "$" {
return key
}
if isSecretEnvKey(key) {
return ""
}
value, _ := syscall.Getenv(key)
return value
}
// FuncMustGetEnv is a special version of os.GetEnv that excludes secret keys and returns an error if it doesn't exist.
func FuncMustGetEnv(key string) (string, error) {
if isSecretEnvKey(key) {
return "", nil
}
value, found := syscall.Getenv(key)
if !found {
return "", fmt.Errorf("environment variable '%s' isn't set", key)
}
return value, nil
}
// FuncHashSum is a helper function that provides similar functionality to helm sum funcs.
func FuncHashSum(new func() hash.Hash) func(data string) string {
hasher := new()
return func(data string) string {
sum := hasher.Sum([]byte(data))
return hex.EncodeToString(sum)
}
}
// FuncKeys is a helper function that provides similar functionality to the helm keys func.
func FuncKeys(maps ...map[string]any) []string {
var keys []string
for _, m := range maps {
for k := range m {
keys = append(keys, k)
}
}
return keys
}
// FuncSortAlpha is a helper function that provides similar functionality to the helm sortAlpha func.
func FuncSortAlpha(slice any) []string {
kind := reflect.Indirect(reflect.ValueOf(slice)).Kind()
switch kind {
case reflect.Slice, reflect.Array:
unsorted := strslice(slice)
sorted := sort.StringSlice(unsorted)
sorted.Sort()
return sorted
}
return []string{strval(slice)}
}
// FuncStringReplace is a helper function that provides similar functionality to the helm replace func.
func FuncStringReplace(old, new, s string) string {
return strings.ReplaceAll(s, old, new)
}
// FuncStringContains is a helper function that provides similar functionality to the helm contains func.
func FuncStringContains(substr string, s string) bool {
return strings.Contains(s, substr)
}
// FuncStringHasPrefix is a helper function that provides similar functionality to the helm hasPrefix func.
func FuncStringHasPrefix(prefix string, s string) bool {
return strings.HasPrefix(s, prefix)
}
// FuncStringHasSuffix is a helper function that provides similar functionality to the helm hasSuffix func.
func FuncStringHasSuffix(suffix string, s string) bool {
return strings.HasSuffix(s, suffix)
}
// FuncStringTrimAll is a helper function that provides similar functionality to the helm trimAll func.
func FuncStringTrimAll(cutset, s string) string {
return strings.Trim(s, cutset)
}
// FuncStringTrimSuffix is a helper function that provides similar functionality to the helm trimSuffix func.
func FuncStringTrimSuffix(suffix, s string) string {
return strings.TrimSuffix(s, suffix)
}
// FuncStringTrimPrefix is a helper function that provides similar functionality to the helm trimPrefix func.
func FuncStringTrimPrefix(prefix, s string) string {
return strings.TrimPrefix(s, prefix)
}
// FuncElemsJoin is a helper function that provides similar functionality to the helm join func.
func FuncElemsJoin(sep string, elems any) string {
return strings.Join(strslice(elems), sep)
}
// FuncStringQuote is a helper function that provides similar functionality to the helm quote func.
func FuncStringQuote(in ...any) string {
out := make([]string, 0, len(in))
for _, s := range in {
if s != nil {
out = append(out, fmt.Sprintf("%q", strval(s)))
}
}
return strings.Join(out, " ")
}
// FuncStringSQuote is a helper function that provides similar functionality to the helm squote func.
func FuncStringSQuote(in ...any) string {
out := make([]string, 0, len(in))
for _, s := range in {
if s != nil {
out = append(out, fmt.Sprintf("'%s'", strval(s)))
}
}
return strings.Join(out, " ")
}
// FuncStringQuoteMultiLine is a helper function that provides similar functionality
// to FuncStringQuote and FuncStringSQuote, however it skips quoting if the string contains multiple lines.
func FuncStringQuoteMultiLine(char rune) func(in ...any) string {
return func(in ...any) string {
out := make([]string, 0, len(in))
for _, s := range in {
if s != nil {
sv := strval(s)
if strings.Contains(sv, "\n") {
out = append(out, sv)
} else {
out = append(out, fmt.Sprintf("%c%s%c", char, sv, char))
}
}
}
return strings.Join(out, " ")
}
}
// FuncIterate is a template function which takes a single uint returning a slice of units from 0 up to that number.
func FuncIterate(count *uint) (out []uint) {
var i uint
for i = 0; i < (*count); i++ {
out = append(out, i)
}
return
}
// FuncStringSplit is a template function which takes sep and value, splitting the value by the sep into a slice.
func FuncStringSplit(sep, value string) map[string]string {
parts := strings.Split(value, sep)
res := make(map[string]string, len(parts))
for i, v := range parts {
res["_"+strconv.Itoa(i)] = v
}
return res
}
// FuncStringSplitList is a special split func that reverses the inputs to match helm templates.
func FuncStringSplitList(sep, s string) []string {
return strings.Split(s, sep)
}
// FuncStringJoinX takes a list of string elements, joins them by the sep string, before every int n characters are
// written it writes string p. This is useful for line breaks mostly.
func FuncStringJoinX(elems []string, sep string, n int, p string) string {
buf := strings.Builder{}
c := 0
e := len(elems) - 1
for i := 0; i <= e; i++ {
if c+len(elems[i])+1 > n {
c = 0
buf.WriteString(p)
}
c += len(elems[i]) + 1
buf.WriteString(elems[i])
if i < e {
buf.WriteString(sep)
}
}
return buf.String()
}
// FuncTypeIs is a helper function that provides similar functionality to the helm typeIs func.
func FuncTypeIs(is string, v any) bool {
return is == FuncTypeOf(v)
}
// FuncTypeIsLike is a helper function that provides similar functionality to the helm typeIsLike func.
func FuncTypeIsLike(is string, v any) bool {
t := FuncTypeOf(v)
return is == t || "*"+is == t
}
// FuncTypeOf is a helper function that provides similar functionality to the helm typeOf func.
func FuncTypeOf(v any) string {
return reflect.ValueOf(v).Type().String()
}
// FuncKindIs is a helper function that provides similar functionality to the helm kindIs func.
func FuncKindIs(is string, v any) bool {
return is == FuncKindOf(v)
}
// FuncKindOf is a helper function that provides similar functionality to the helm kindOf func.
func FuncKindOf(v any) string {
return reflect.ValueOf(v).Kind().String()
}
// FuncList is a helper function that provides similar functionality to the helm list func.
func FuncList(items ...any) []any {
return items
}
// FuncDict is a helper function that provides similar functionality to the helm dict func.
func FuncDict(pairs ...any) map[string]any {
m := map[string]any{}
p := len(pairs)
for i := 0; i < p; i += 2 {
key := strval(pairs[i])
if i+1 >= p {
m[key] = ""
continue
}
m[key] = pairs[i+1]
}
return m
}
// FuncGet is a helper function that provides similar functionality to the helm get func.
func FuncGet(m map[string]any, key string) any {
if val, ok := m[key]; ok {
return val
}
return ""
}
// FuncSet is a helper function that provides similar functionality to the helm set func.
func FuncSet(m map[string]any, key string, value any) map[string]any {
m[key] = value
return m
}
// FuncDefault is a helper function that provides similar functionality to the helm default func.
func FuncDefault(d any, vals ...any) any {
if FuncEmpty(vals) || FuncEmpty(vals[0]) {
return d
}
return vals[0]
}
// FuncEmpty is a helper function that provides similar functionality to the helm empty func.
func FuncEmpty(v any) bool {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return true
}
switch rv.Kind() {
default:
return rv.IsNil()
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
return rv.Len() == 0
case reflect.Bool:
return !rv.Bool()
case reflect.Complex64, reflect.Complex128:
return rv.Complex() == 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return rv.Uint() == 0
case reflect.Float32, reflect.Float64:
return rv.Float() == 0
case reflect.Struct:
return false
}
}
// FuncIndent is a helper function that provides similar functionality to the helm indent func.
func FuncIndent(indent int, value string) string {
padding := strings.Repeat(" ", indent)
return padding + strings.ReplaceAll(value, "\n", "\n"+padding)
}
// FuncNewlineIndent is a helper function that provides similar functionality to the helm nindent func.
func FuncNewlineIndent(indent int, value string) string {
return "\n" + FuncIndent(indent, value)
}
// FuncMultilineIndent is a helper function that performs YAML multiline intending with a multiline format input such as
// |, |+, |-, >, >+, >-, etc. This is only true if the value has newline characters otherwise it just returns the same
// output as the indent function.
func FuncMultilineIndent(indent int, multiline, value string) string {
if !strings.Contains(value, "\n") {
return value
}
return multiline + "\n" + FuncIndent(indent, value)
}
// FuncUUIDv4 is a helper function that provides similar functionality to the helm uuidv4 func.
func FuncUUIDv4() string {
return uuid.New().String()
}
// FuncFileContent returns the file content.
func FuncFileContent(path string) (data string, err error) {
var raw []byte
if raw, err = os.ReadFile(path); err != nil {
return "", err
}
return string(raw), nil
}
// FuncSecret returns the file content stripping the newlines from the end of the content.
func FuncSecret(path string) (data string, err error) {
if data, err = FuncFileContent(path); err != nil {
return "", err
}
return strings.TrimRight(data, "\n"), nil
}