142 lines
4.2 KiB
Go
142 lines
4.2 KiB
Go
package template
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/hashicorp/vault/sdk/helper/base62"
|
|
)
|
|
|
|
type Opt func(*StringTemplate) error
|
|
|
|
func Template(rawTemplate string) Opt {
|
|
return func(up *StringTemplate) error {
|
|
up.rawTemplate = rawTemplate
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Function allows the user to specify functions for use in the template. If the name provided is a function that
|
|
// already exists in the function map, this will override the previously specified function.
|
|
func Function(name string, f interface{}) Opt {
|
|
return func(up *StringTemplate) error {
|
|
if name == "" {
|
|
return fmt.Errorf("missing function name")
|
|
}
|
|
if f == nil {
|
|
return fmt.Errorf("missing function")
|
|
}
|
|
up.funcMap[name] = f
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// StringTemplate creates strings based on the provided template.
|
|
// This uses the go templating language, so anything that adheres to that language will function in this struct.
|
|
// There are several custom functions available for use in the template:
|
|
// - random
|
|
// - Randomly generated characters. This uses the charset specified in RandomCharset. Must include a length.
|
|
// Example: {{ rand 20 }}
|
|
// - truncate
|
|
// - Truncates the previous value to the specified length. Must include a maximum length.
|
|
// Example: {{ .DisplayName | truncate 10 }}
|
|
// - truncate_sha256
|
|
// - Truncates the previous value to the specified length. If the original length is greater than the length
|
|
// specified, the remaining characters will be sha256 hashed and appended to the end. The hash will be only the first 8 characters The maximum length will
|
|
// be no longer than the length specified.
|
|
// Example: {{ .DisplayName | truncate_sha256 30 }}
|
|
// - uppercase
|
|
// - Uppercases the previous value.
|
|
// Example: {{ .RoleName | uppercase }}
|
|
// - lowercase
|
|
// - Lowercases the previous value.
|
|
// Example: {{ .DisplayName | lowercase }}
|
|
// - replace
|
|
// - Performs a string find & replace
|
|
// Example: {{ .DisplayName | replace - _ }}
|
|
// - sha256
|
|
// - SHA256 hashes the previous value.
|
|
// Example: {{ .DisplayName | sha256 }}
|
|
// - base64
|
|
// - base64 encodes the previous value.
|
|
// Example: {{ .DisplayName | base64 }}
|
|
// - unix_time
|
|
// - Provides the current unix time in seconds.
|
|
// Example: {{ unix_time }}
|
|
// - unix_time_millis
|
|
// - Provides the current unix time in milliseconds.
|
|
// Example: {{ unix_time_millis }}
|
|
// - timestamp
|
|
// - Provides the current time. Must include a standard Go format string
|
|
// - uuid
|
|
// - Generates a UUID
|
|
// Example: {{ uuid }}
|
|
type StringTemplate struct {
|
|
rawTemplate string
|
|
tmpl *template.Template
|
|
funcMap template.FuncMap
|
|
}
|
|
|
|
// NewTemplate creates a StringTemplate. No arguments are required
|
|
// as this has reasonable defaults for all values.
|
|
// The default template is specified in the DefaultTemplate constant.
|
|
func NewTemplate(opts ...Opt) (up StringTemplate, err error) {
|
|
up = StringTemplate{
|
|
funcMap: map[string]interface{}{
|
|
"random": base62.Random,
|
|
"truncate": truncate,
|
|
"truncate_sha256": truncateSHA256,
|
|
"uppercase": uppercase,
|
|
"lowercase": lowercase,
|
|
"replace": replace,
|
|
"sha256": hashSHA256,
|
|
"base64": encodeBase64,
|
|
|
|
"unix_time": unixTime,
|
|
"unix_time_millis": unixTimeMillis,
|
|
"timestamp": timestamp,
|
|
"uuid": uuid,
|
|
},
|
|
}
|
|
|
|
merr := &multierror.Error{}
|
|
for _, opt := range opts {
|
|
merr = multierror.Append(merr, opt(&up))
|
|
}
|
|
|
|
err = merr.ErrorOrNil()
|
|
if err != nil {
|
|
return up, err
|
|
}
|
|
|
|
if up.rawTemplate == "" {
|
|
return StringTemplate{}, fmt.Errorf("missing template")
|
|
}
|
|
|
|
tmpl, err := template.New("template").
|
|
Funcs(up.funcMap).
|
|
Parse(up.rawTemplate)
|
|
if err != nil {
|
|
return StringTemplate{}, fmt.Errorf("unable to parse template: %w", err)
|
|
}
|
|
up.tmpl = tmpl
|
|
|
|
return up, nil
|
|
}
|
|
|
|
// Generate based on the provided template
|
|
func (up StringTemplate) Generate(data interface{}) (string, error) {
|
|
if up.tmpl == nil || up.rawTemplate == "" {
|
|
return "", fmt.Errorf("failed to generate: template not initialized")
|
|
}
|
|
str := &strings.Builder{}
|
|
err := up.tmpl.Execute(str, data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("unable to apply template: %w", err)
|
|
}
|
|
|
|
return str.String(), nil
|
|
}
|