open-vault/sdk/helper/template/template.go

156 lines
4.3 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package template
import (
"fmt"
"strings"
"text/template"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-secure-stdlib/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
}