feat(templates): templating functions (#4635)
This adds several functions which are available in most areas that use templates.pull/4637/head
parent
a916b65357
commit
55a6794370
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/templates"
|
"github.com/authelia/authelia/v4/internal/templates"
|
||||||
|
@ -24,13 +23,11 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTMPL(name string) (tmpl *template.Template, err error) {
|
func newTMPL(name string) (tmpl *template.Template, err error) {
|
||||||
return template.New(name).
|
funcs := templates.FuncMap()
|
||||||
Funcs(template.FuncMap{
|
|
||||||
"stringsContains": strings.Contains,
|
funcs["joinX"] = templates.FuncStringJoinX
|
||||||
"join": strings.Join,
|
|
||||||
"joinX": templates.StringJoinXFunc,
|
return template.New(name).Funcs(funcs).Parse(mustLoadTmplFS(name))
|
||||||
}).
|
|
||||||
Parse(mustLoadTmplFS(name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustLoadTmplFS(tmpl string) string {
|
func mustLoadTmplFS(tmpl string) string {
|
||||||
|
|
|
@ -50,7 +50,7 @@ for, and the structure it must have.
|
||||||
│ │
|
│ │
|
||||||
│ └─⫸ Commit Scope: {{ joinX .Scopes.All "|" 70 "\n │ " }}
|
│ └─⫸ Commit Scope: {{ joinX .Scopes.All "|" 70 "\n │ " }}
|
||||||
│
|
│
|
||||||
└─⫸ Commit Type: {{ join .Types.List "|" }}
|
└─⫸ Commit Type: {{ join "|" .Types.List }}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
|
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
|
||||||
|
@ -59,7 +59,7 @@ The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is opti
|
||||||
{{ range .Types.Details }}
|
{{ range .Types.Details }}
|
||||||
* __{{ .Name }}__ {{ .Description }}
|
* __{{ .Name }}__ {{ .Description }}
|
||||||
{{- if .Scopes }}
|
{{- if .Scopes }}
|
||||||
(example scopes: {{ join .Scopes ", " }})
|
(example scopes: {{ join ", " .Scopes }})
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ module.exports = {
|
||||||
"type-enum": [
|
"type-enum": [
|
||||||
2,
|
2,
|
||||||
"always",
|
"always",
|
||||||
["{{ join .Types.List "\", \"" }}"],
|
["{{ join "\", \"" .Types.List }}"],
|
||||||
],
|
],
|
||||||
"scope-enum": [
|
"scope-enum": [
|
||||||
2,
|
2,
|
||||||
|
|
|
@ -37,7 +37,7 @@ i18n.use(Backend)
|
||||||
default: ["{{ .Defaults.Language.Locale }}"],
|
default: ["{{ .Defaults.Language.Locale }}"],
|
||||||
{{- range .Languages }}
|
{{- range .Languages }}
|
||||||
{{- if and (not (eq .Locale "en")) (not (eq (len .Fallbacks) 0)) }}
|
{{- if and (not (eq .Locale "en")) (not (eq (len .Fallbacks) 0)) }}
|
||||||
{{ if stringsContains .Locale "-" }}"{{ .Locale }}"{{ else }}{{ .Locale }}{{ end }}: [{{ range $i, $value := .Fallbacks }}{{ if eq $i 0 }}"{{ $value }}"{{ else }}, "{{ $value }}"{{ end }}{{ end }}],
|
{{ if contains "-" .Locale }}"{{ .Locale }}"{{ else }}{{ .Locale }}{{ end }}: [{{ range $i, $value := .Fallbacks }}{{ if eq $i 0 }}"{{ $value }}"{{ else }}, "{{ $value }}"{{ end }}{{ end }}],
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
},
|
},
|
||||||
|
|
|
@ -187,121 +187,6 @@ output at each filter stage as a base64 string when trace logging is enabled.
|
||||||
|
|
||||||
#### Functions
|
#### Functions
|
||||||
|
|
||||||
In addition to the standard builtin functions we support several other functions.
|
In addition to the standard builtin functions we support several other functions which should operate similar.
|
||||||
|
|
||||||
##### iterate
|
See the [Templating Reference Guide](../../reference/guides/templating.md) for more information.
|
||||||
|
|
||||||
The `iterate` function generates a list of numbers from 0 to the input provided. Useful for ranging over a list of
|
|
||||||
numbers.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
numbers:
|
|
||||||
{{- range $i := iterate 5 }}
|
|
||||||
- {{ $i }}
|
|
||||||
{{- end }}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### env
|
|
||||||
|
|
||||||
The `env` function returns the value of an environment variable or a blank string.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
default_redirection_url: 'https://{{ env "DOMAIN" }}'
|
|
||||||
```
|
|
||||||
|
|
||||||
##### split
|
|
||||||
|
|
||||||
The `split` function splits a string by the separator.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
access_control:
|
|
||||||
rules:
|
|
||||||
- domain: 'app.{{ env "DOMAIN" }}'
|
|
||||||
policy: bypass
|
|
||||||
methods:
|
|
||||||
{{ range _, $method := split "GET,POST" "," }}
|
|
||||||
- {{ $method }}
|
|
||||||
{{ end }}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### join
|
|
||||||
|
|
||||||
The `join` function is similar to [split](#split) but does the complete oppiste, joining an array of strings with a
|
|
||||||
separator.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
access_control:
|
|
||||||
rules:
|
|
||||||
- domain: ['app.{{ join (split (env "DOMAINS") ",") "', 'app." }}']
|
|
||||||
policy: bypass
|
|
||||||
```
|
|
||||||
|
|
||||||
##### contains
|
|
||||||
|
|
||||||
The `contains` function is a test function which checks if one string contains another string.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
{{ if contains (env "DOMAIN") "https://" }}
|
|
||||||
default_redirection_url: '{{ env "DOMAIN" }}'
|
|
||||||
{{ else }}
|
|
||||||
default_redirection_url: 'https://{{ env "DOMAIN" }}'
|
|
||||||
{{ end }}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### hasPrefix
|
|
||||||
|
|
||||||
The `hasPrefix` function is a test function which checks if one string is prefixed with another string.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
{{ if hasPrefix (env "DOMAIN") "https://" }}
|
|
||||||
default_redirection_url: '{{ env "DOMAIN" }}'
|
|
||||||
{{ else }}
|
|
||||||
default_redirection_url: 'https://{{ env "DOMAIN" }}'
|
|
||||||
{{ end }}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### hasSuffix
|
|
||||||
|
|
||||||
The `hasSuffix` function is a test function which checks if one string is suffixed with another string.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
{{ if hasSuffix (env "DOMAIN") "/" }}
|
|
||||||
default_redirection_url: 'https://{{ env "DOMAIN" }}'
|
|
||||||
{{ else }}
|
|
||||||
default_redirection_url: 'https://{{ env "DOMAIN" }}/'
|
|
||||||
{{ end }}
|
|
||||||
```
|
|
||||||
|
|
||||||
##### lower
|
|
||||||
|
|
||||||
The `lower` function is a conversion function which converts a string to all lowercase.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
default_redirection_url: 'https://{{ env "DOMAIN" | lower }}'
|
|
||||||
```
|
|
||||||
|
|
||||||
##### upper
|
|
||||||
|
|
||||||
The `upper` function is a conversion function which converts a string to all uppercase.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
default_redirection_url: 'https://{{ env "DOMAIN" | upper }}'
|
|
||||||
```
|
|
||||||
|
|
|
@ -73,6 +73,11 @@ Some Additional examples for specific purposes can be found in the
|
||||||
The original template content can be found on
|
The original template content can be found on
|
||||||
[GitHub](https://github.com/authelia/authelia/tree/master/internal/templates/src/notification).
|
[GitHub](https://github.com/authelia/authelia/tree/master/internal/templates/src/notification).
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
Several functions are implemented with the email templates. See the
|
||||||
|
[Templating Reference Guide](../../reference/guides/templating.md) for more information.
|
||||||
|
|
||||||
[host]: ../../configuration/notifications/smtp.md#host
|
[host]: ../../configuration/notifications/smtp.md#host
|
||||||
[server_name]: ../../configuration/notifications/smtp.md#tls
|
[server_name]: ../../configuration/notifications/smtp.md#tls
|
||||||
[sender]: ../../configuration/notifications/smtp.md#sender
|
[sender]: ../../configuration/notifications/smtp.md#sender
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
title: "Templating"
|
||||||
|
description: "A reference guide on the templates system"
|
||||||
|
lead: "This section contains reference documentation for Authelia's templating capabilities."
|
||||||
|
date: 2022-12-23T18:31:05+11:00
|
||||||
|
draft: false
|
||||||
|
images: []
|
||||||
|
menu:
|
||||||
|
reference:
|
||||||
|
parent: "guides"
|
||||||
|
weight: 220
|
||||||
|
toc: true
|
||||||
|
---
|
||||||
|
|
||||||
|
Authelia has several methods where users can interact with templates.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
Functions can be used to perform specific actions when executing templates. The following is a simple guide on which
|
||||||
|
functions exist.
|
||||||
|
|
||||||
|
### Standard Functions
|
||||||
|
|
||||||
|
Go has a set of standard functions which can be used. See the [Go Documentation](https://pkg.go.dev/text/template#hdr-Functions)
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
### Helm-like Functions
|
||||||
|
|
||||||
|
The following functions which mimic the behaviour of helm exist in most templating areas:
|
||||||
|
|
||||||
|
- env
|
||||||
|
- expandenv
|
||||||
|
- split
|
||||||
|
- splitList
|
||||||
|
- join
|
||||||
|
- contains
|
||||||
|
- hasPrefix
|
||||||
|
- hasSuffix
|
||||||
|
- lower
|
||||||
|
- upper
|
||||||
|
- title
|
||||||
|
- trim
|
||||||
|
- trimAll
|
||||||
|
- trimSuffix
|
||||||
|
- trimPrefix
|
||||||
|
- replace
|
||||||
|
- quote
|
||||||
|
- sha1sum
|
||||||
|
- sha256sum
|
||||||
|
- sha512sum
|
||||||
|
- squote
|
||||||
|
- now
|
||||||
|
|
||||||
|
See the [Helm Documentation](https://helm.sh/docs/chart_template_guide/function_list/) for more information. Please
|
||||||
|
note that only the functions listed above are supported.
|
||||||
|
|
||||||
|
__*Special Note:* The `env` and `expandenv` function automatically excludes environment variables that start with
|
||||||
|
`AUTHELIA_` or `X_AUTHELIA_` and end with one of `KEY`, `SECRET`, `PASSWORD`, `TOKEN`, or `CERTIFICATE_CHAIN`.__
|
||||||
|
|
||||||
|
### Special Functions
|
||||||
|
|
||||||
|
The following is a list of special functions and their syntax.
|
||||||
|
|
||||||
|
#### iterate
|
||||||
|
|
||||||
|
Input is a single uint. Returns a slice of uints from 0 to the provided uint.
|
|
@ -36,4 +36,7 @@ const (
|
||||||
errFmtDecodeHookCouldNotParseEmptyValue = "could not decode an empty value to a %s%s: %w"
|
errFmtDecodeHookCouldNotParseEmptyValue = "could not decode an empty value to a %s%s: %w"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IMPORTANT: There is an uppercase copy of this in github.com/authelia/authelia/internal/templates named
|
||||||
|
// envSecretSuffixes.
|
||||||
|
// Make sure you update these at the same time.
|
||||||
var secretSuffixes = []string{"key", "secret", "password", "token", "certificate_chain"}
|
var secretSuffixes = []string{"key", "secret", "password", "token", "certificate_chain"}
|
||||||
|
|
|
@ -112,32 +112,7 @@ func NewExpandEnvFileFilter() FileFilter {
|
||||||
|
|
||||||
// NewTemplateFileFilter is a FileFilter which passes the bytes through text/template.
|
// NewTemplateFileFilter is a FileFilter which passes the bytes through text/template.
|
||||||
func NewTemplateFileFilter() FileFilter {
|
func NewTemplateFileFilter() FileFilter {
|
||||||
data := &TemplateFileFilterData{
|
t := template.New("config.template").Funcs(templates.FuncMap())
|
||||||
Env: map[string]string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range os.Environ() {
|
|
||||||
kv := strings.SplitN(e, "=", 2)
|
|
||||||
|
|
||||||
if len(kv) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Env[kv[0]] = kv[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
t := template.New("config.template").
|
|
||||||
Funcs(template.FuncMap{
|
|
||||||
"env": templates.StringMapLookupDefaultEmptyFunc(data.Env),
|
|
||||||
"split": templates.StringsSplitFunc,
|
|
||||||
"iterate": templates.IterateFunc,
|
|
||||||
"join": strings.Join,
|
|
||||||
"contains": strings.Contains,
|
|
||||||
"hasPrefix": strings.HasPrefix,
|
|
||||||
"hasSuffix": strings.HasSuffix,
|
|
||||||
"lower": strings.ToLower,
|
|
||||||
"upper": strings.ToUpper,
|
|
||||||
})
|
|
||||||
|
|
||||||
log := logging.Logger()
|
log := logging.Logger()
|
||||||
|
|
||||||
|
@ -148,7 +123,7 @@ func NewTemplateFileFilter() FileFilter {
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
if err = t.Execute(buf, data); err != nil {
|
if err = t.Execute(buf, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,8 +138,3 @@ func NewTemplateFileFilter() FileFilter {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateFileFilterData is the data available to the Go Template FileFilter.
|
|
||||||
type TemplateFileFilterData struct {
|
|
||||||
Env map[string]string
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,41 +1,193 @@
|
||||||
package templates
|
package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha1" //nolint:gosec
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StringMapLookupDefaultEmptyFunc is function which takes a map[string]string and returns a template function which
|
// FuncMap returns the template FuncMap commonly used in several templates.
|
||||||
// takes a string which is used as a key lookup for the map[string]string. If the value isn't found it returns an empty
|
func FuncMap() map[string]any {
|
||||||
// string.
|
return map[string]any{
|
||||||
func StringMapLookupDefaultEmptyFunc(m map[string]string) func(key string) (value string) {
|
"iterate": FuncIterate,
|
||||||
return func(key string) (value string) {
|
"env": FuncGetEnv,
|
||||||
var ok bool
|
"expandenv": FuncExpandEnv,
|
||||||
|
"split": FuncStringSplit,
|
||||||
|
"splitList": FuncStringSplitList,
|
||||||
|
"join": FuncElemsJoin,
|
||||||
|
"contains": FuncStringContains,
|
||||||
|
"hasPrefix": FuncStringHasPrefix,
|
||||||
|
"hasSuffix": FuncStringHasSuffix,
|
||||||
|
"lower": strings.ToLower,
|
||||||
|
"upper": strings.ToUpper,
|
||||||
|
"title": strings.ToTitle,
|
||||||
|
"trim": strings.TrimSpace,
|
||||||
|
"trimAll": FuncStringTrimAll,
|
||||||
|
"trimSuffix": FuncStringTrimSuffix,
|
||||||
|
"trimPrefix": FuncStringTrimPrefix,
|
||||||
|
"replace": FuncStringReplace,
|
||||||
|
"quote": FuncStringQuote,
|
||||||
|
"sha1sum": FuncHashSum(sha1.New),
|
||||||
|
"sha256sum": FuncHashSum(sha256.New),
|
||||||
|
"sha512sum": FuncHashSum(sha512.New),
|
||||||
|
"squote": FuncStringSQuote,
|
||||||
|
"now": time.Now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if value, ok = m[key]; !ok {
|
// 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 isSecretEnvKey(key) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return os.Getenv(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringMapLookupFunc is function which takes a map[string]string and returns a template function which
|
// FuncStringReplace is a helper function that provides similar functionality to the helm replace func.
|
||||||
// takes a string which is used as a key lookup for the map[string]string. If the value isn't found it returns an error.
|
func FuncStringReplace(old, new, s string) string {
|
||||||
func StringMapLookupFunc(m map[string]string) func(key string) (value string, err error) {
|
return strings.ReplaceAll(s, old, new)
|
||||||
return func(key string) (value string, err error) {
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if value, ok = m[key]; !ok {
|
|
||||||
return value, fmt.Errorf("failed to lookup key '%s' from map", key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value, nil
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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("%q", strval(s)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IterateFunc is a template function which takes a single uint returning a slice of units from 0 up to that number.
|
return strings.Join(out, " ")
|
||||||
func IterateFunc(count *uint) (out []uint) {
|
}
|
||||||
|
|
||||||
|
// 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, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func strval(v interface{}) string {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case []byte:
|
||||||
|
return string(v)
|
||||||
|
case fmt.Stringer:
|
||||||
|
return v.String()
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func strslice(v any) []string {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case []string:
|
||||||
|
return v
|
||||||
|
case []interface{}:
|
||||||
|
b := make([]string, 0, len(v))
|
||||||
|
|
||||||
|
for _, s := range v {
|
||||||
|
if s != nil {
|
||||||
|
b = append(b, strval(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
default:
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
switch val.Kind() {
|
||||||
|
case reflect.Array, reflect.Slice:
|
||||||
|
l := val.Len()
|
||||||
|
b := make([]string, 0, l)
|
||||||
|
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
value := val.Index(i).Interface()
|
||||||
|
if value != nil {
|
||||||
|
b = append(b, strval(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
default:
|
||||||
|
if v == nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{strval(v)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
var i uint
|
||||||
|
|
||||||
for i = 0; i < (*count); i++ {
|
for i = 0; i < (*count); i++ {
|
||||||
|
@ -45,14 +197,26 @@ func IterateFunc(count *uint) (out []uint) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringsSplitFunc is a template function which takes sep and value, splitting the value by the sep into a slice.
|
// FuncStringSplit is a template function which takes sep and value, splitting the value by the sep into a slice.
|
||||||
func StringsSplitFunc(value, sep string) []string {
|
func FuncStringSplit(sep, value string) map[string]string {
|
||||||
return strings.Split(value, sep)
|
parts := strings.Split(value, sep)
|
||||||
|
res := make(map[string]string, len(parts))
|
||||||
|
|
||||||
|
for i, v := range parts {
|
||||||
|
res["_"+strconv.Itoa(i)] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringJoinXFunc takes a list of string elements, joins them by the sep string, before every int n characters are
|
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.
|
// written it writes string p. This is useful for line breaks mostly.
|
||||||
func StringJoinXFunc(elems []string, sep string, n int, p string) string {
|
func FuncStringJoinX(elems []string, sep string, n int, p string) string {
|
||||||
buf := strings.Builder{}
|
buf := strings.Builder{}
|
||||||
|
|
||||||
c := 0
|
c := 0
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1" //nolint:gosec
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"hash"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuncGetEnv(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have map[string]string
|
||||||
|
expected map[string]string
|
||||||
|
}{
|
||||||
|
{"ShouldGetEnv",
|
||||||
|
map[string]string{
|
||||||
|
"AN_ENV": "a",
|
||||||
|
"ANOTHER_ENV": "b",
|
||||||
|
},
|
||||||
|
map[string]string{
|
||||||
|
"AN_ENV": "a",
|
||||||
|
"ANOTHER_ENV": "b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{"ShouldNotGetSecretEnv",
|
||||||
|
map[string]string{
|
||||||
|
"AUTHELIA_ENV_SECRET": "a",
|
||||||
|
"ANOTHER_ENV": "b",
|
||||||
|
},
|
||||||
|
map[string]string{
|
||||||
|
"AUTHELIA_ENV_SECRET": "",
|
||||||
|
"ANOTHER_ENV": "b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
for key, value := range tc.have {
|
||||||
|
assert.NoError(t, os.Setenv(key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, expected := range tc.expected {
|
||||||
|
assert.Equal(t, expected, FuncGetEnv(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
for key := range tc.have {
|
||||||
|
assert.NoError(t, os.Unsetenv(key))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncExpandEnv(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
env map[string]string
|
||||||
|
have string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"ShouldExpandEnv",
|
||||||
|
map[string]string{
|
||||||
|
"AN_ENV": "a",
|
||||||
|
"ANOTHER_ENV": "b",
|
||||||
|
},
|
||||||
|
"This is ${AN_ENV} and ${ANOTHER_ENV}",
|
||||||
|
"This is a and b",
|
||||||
|
},
|
||||||
|
{"ShouldNotExpandSecretEnv",
|
||||||
|
map[string]string{
|
||||||
|
"AUTHELIA_ENV_SECRET": "a",
|
||||||
|
"X_AUTHELIA_ENV_SECRET": "a",
|
||||||
|
"ANOTHER_ENV": "b",
|
||||||
|
},
|
||||||
|
"This is ${AUTHELIA_ENV_SECRET} and ${ANOTHER_ENV} without ${X_AUTHELIA_ENV_SECRET}",
|
||||||
|
"This is and b without ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
for key, value := range tc.env {
|
||||||
|
assert.NoError(t, os.Setenv(key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, tc.expected, FuncExpandEnv(tc.have))
|
||||||
|
|
||||||
|
for key := range tc.env {
|
||||||
|
assert.NoError(t, os.Unsetenv(key))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncHashSum(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
new func() hash.Hash
|
||||||
|
have []string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{"ShouldHashSHA1", sha1.New, []string{"abc", "123", "authelia"}, []string{"616263da39a3ee5e6b4b0d3255bfef95601890afd80709", "313233da39a3ee5e6b4b0d3255bfef95601890afd80709", "61757468656c6961da39a3ee5e6b4b0d3255bfef95601890afd80709"}},
|
||||||
|
{"ShouldHashSHA256", sha256.New, []string{"abc", "123", "authelia"}, []string{"616263e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "313233e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "61757468656c6961e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}},
|
||||||
|
{"ShouldHashSHA512", sha512.New, []string{"abc", "123", "authelia"}, []string{"616263cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", "313233cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", "61757468656c6961cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
require.Equal(t, len(tc.have), len(tc.expected))
|
||||||
|
|
||||||
|
h := FuncHashSum(tc.new)
|
||||||
|
|
||||||
|
for i := 0; i < len(tc.have); i++ {
|
||||||
|
assert.Equal(t, tc.expected[i], h(tc.have[i]))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringReplace(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
old, new string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"ShouldReplaceSingle", "ABC123", "123", "456", "ABC456"},
|
||||||
|
{"ShouldReplaceMultiple", "123ABC123123", "123", "456", "456ABC456456"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringReplace(tc.old, tc.new, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringContains(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
substr string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"ShouldMatchNormal", "abc123", "c12", true},
|
||||||
|
{"ShouldNotMatchWrongCase", "abc123", "C12", false},
|
||||||
|
{"ShouldNotMatchNotContains", "abc123", "xyz", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringContains(tc.substr, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringHasPrefix(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
substr string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"ShouldMatchNormal", "abc123", "abc", true},
|
||||||
|
{"ShouldNotMatchWrongCase", "abc123", "ABC", false},
|
||||||
|
{"ShouldNotMatchNotPrefix", "abc123", "123", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringHasPrefix(tc.substr, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringHasSuffix(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
substr string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"ShouldMatchNormal", "abc123xyz", "xyz", true},
|
||||||
|
{"ShouldNotMatchWrongCase", "abc123xyz", "XYZ", false},
|
||||||
|
{"ShouldNotMatchNotSuffix", "abc123xyz", "123", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringHasSuffix(tc.substr, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringTrimAll(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
cutset string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"ShouldTrimSuffix", "abc123xyz", "xyz", "abc123"},
|
||||||
|
{"ShouldTrimPrefix", "xyzabc123", "xyz", "abc123"},
|
||||||
|
{"ShouldNotTrimMiddle", "abcxyz123", "xyz", "abcxyz123"},
|
||||||
|
{"ShouldNotTrimWrongCase", "xyzabcxyz123xyz", "XYZ", "xyzabcxyz123xyz"},
|
||||||
|
{"ShouldNotTrimWrongChars", "abc123xyz", "456", "abc123xyz"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringTrimAll(tc.cutset, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringTrimPrefix(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
cutset string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"ShouldNotTrimSuffix", "abc123xyz", "xyz", "abc123xyz"},
|
||||||
|
{"ShouldTrimPrefix", "xyzabc123", "xyz", "abc123"},
|
||||||
|
{"ShouldNotTrimMiddle", "abcxyz123", "xyz", "abcxyz123"},
|
||||||
|
{"ShouldNotTrimWrongCase", "xyzabcxyz123xyz", "XYZ", "xyzabcxyz123xyz"},
|
||||||
|
{"ShouldNotTrimWrongChars", "abc123xyz", "456", "abc123xyz"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringTrimPrefix(tc.cutset, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringTrimSuffix(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
cutset string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"ShouldTrimSuffix", "abc123xyz", "xyz", "abc123"},
|
||||||
|
{"ShouldNotTrimPrefix", "xyzabc123", "xyz", "xyzabc123"},
|
||||||
|
{"ShouldNotTrimMiddle", "abcxyz123", "xyz", "abcxyz123"},
|
||||||
|
{"ShouldNotTrimWrongCase", "xyzabcxyz123xyz", "XYZ", "xyzabcxyz123xyz"},
|
||||||
|
{"ShouldNotTrimWrongChars", "abc123xyz", "456", "abc123xyz"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringTrimSuffix(tc.cutset, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncElemsJoin(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have any
|
||||||
|
sep string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"ShouldNotJoinNonElements", "abc123xyz", "xyz", "abc123xyz"},
|
||||||
|
{"ShouldJoinStrings", []string{"abc", "123"}, "xyz", "abcxyz123"},
|
||||||
|
{"ShouldJoinInts", []int{1, 2, 3}, ",", "1,2,3"},
|
||||||
|
{"ShouldJoinBooleans", []bool{true, false, true}, ".", "true.false.true"},
|
||||||
|
{"ShouldJoinBytes", [][]byte{[]byte("abc"), []byte("123"), []byte("a")}, "$", "abc$123$a"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncElemsJoin(tc.sep, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncIterate(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have uint
|
||||||
|
expected []uint
|
||||||
|
}{
|
||||||
|
{"ShouldGiveZeroResults", 0, nil},
|
||||||
|
{"ShouldGive10Results", 10, []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncIterate(&tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringsSplit(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
sep string
|
||||||
|
expected map[string]string
|
||||||
|
}{
|
||||||
|
{"ShouldSplit", "abc,123,456", ",", map[string]string{"_0": "abc", "_1": "123", "_2": "456"}},
|
||||||
|
{"ShouldNotSplit", "abc,123,456", "$", map[string]string{"_0": "abc,123,456"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringSplit(tc.sep, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncStringSplitList(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have string
|
||||||
|
sep string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{"ShouldSplit", "abc,123,456", ",", []string{"abc", "123", "456"}},
|
||||||
|
{"ShouldNotSplit", "abc,123,456", "$", []string{"abc,123,456"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, FuncStringSplitList(tc.sep, tc.have))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,39 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
tt "text/template"
|
tt "text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envPrefix = "AUTHELIA_"
|
||||||
|
envXPrefix = "X_AUTHELIA_"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IMPORTANT: This is a copy of github.com/authelia/authelia/internal/configuration's secretSuffixes except all uppercase.
|
||||||
|
// Make sure you update these at the same time.
|
||||||
|
var envSecretSuffixes = []string{
|
||||||
|
"KEY", "SECRET", "PASSWORD", "TOKEN", "CERTIFICATE_CHAIN",
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSecretEnvKey(key string) (isSecretEnvKey bool) {
|
||||||
|
key = strings.ToUpper(key)
|
||||||
|
|
||||||
|
if !strings.HasPrefix(key, envPrefix) && !strings.HasPrefix(key, envXPrefix) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range envSecretSuffixes {
|
||||||
|
suffix := strings.ToUpper(s)
|
||||||
|
|
||||||
|
if strings.HasSuffix(key, suffix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func templateExists(path string) (exists bool) {
|
func templateExists(path string) (exists bool) {
|
||||||
info, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -45,7 +75,7 @@ func readTemplate(name, ext, category, overridePath string) (tPath string, embed
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTextTemplate(name, tPath string, embed bool, data []byte) (t *tt.Template, err error) {
|
func parseTextTemplate(name, tPath string, embed bool, data []byte) (t *tt.Template, err error) {
|
||||||
if t, err = tt.New(name + extText).Parse(string(data)); err != nil {
|
if t, err = tt.New(name + extText).Funcs(FuncMap()).Parse(string(data)); err != nil {
|
||||||
if embed {
|
if embed {
|
||||||
return nil, fmt.Errorf("failed to parse embedded template '%s': %w", tPath, err)
|
return nil, fmt.Errorf("failed to parse embedded template '%s': %w", tPath, err)
|
||||||
}
|
}
|
||||||
|
@ -57,7 +87,7 @@ func parseTextTemplate(name, tPath string, embed bool, data []byte) (t *tt.Templ
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseHTMLTemplate(name, tPath string, embed bool, data []byte) (t *th.Template, err error) {
|
func parseHTMLTemplate(name, tPath string, embed bool, data []byte) (t *th.Template, err error) {
|
||||||
if t, err = th.New(name + extHTML).Parse(string(data)); err != nil {
|
if t, err = th.New(name + extHTML).Funcs(FuncMap()).Parse(string(data)); err != nil {
|
||||||
if embed {
|
if embed {
|
||||||
return nil, fmt.Errorf("failed to parse embedded template '%s': %w", tPath, err)
|
return nil, fmt.Errorf("failed to parse embedded template '%s': %w", tPath, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsSecretEnvKey(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
have []string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"ShouldReturnFalseForKeysWithoutPrefix", []string{"A_KEY", "A_SECRET", "A_PASSWORD", "NOT_AUTHELIA_A_PASSWORD"}, false},
|
||||||
|
{"ShouldReturnFalseForKeysWithoutSuffix", []string{"AUTHELIA_EXAMPLE", "X_AUTHELIA_EXAMPLE", "X_AUTHELIA_PASSWORD_NOT"}, false},
|
||||||
|
{"ShouldReturnTrueForSecretKeys", []string{"AUTHELIA_JWT_SECRET", "AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET", "AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN", "X_AUTHELIA_JWT_SECRET", "X_AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET", "X_AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN"}, true},
|
||||||
|
{"ShouldReturnTrueForSecretKeysEvenWithMixedCase", []string{"aUTHELIA_JWT_SECRET", "aUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET", "aUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN", "X_aUTHELIA_JWT_SECREt", "X_aUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET", "x_AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN"}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
for _, env := range tc.have {
|
||||||
|
t.Run(env, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, isSecretEnvKey(env))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue