github.com/hashicorp/vault/sdk@v0.11.0/helper/template/template.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package template 5 6 import ( 7 "fmt" 8 "strings" 9 "text/template" 10 11 "github.com/hashicorp/go-multierror" 12 "github.com/hashicorp/go-secure-stdlib/base62" 13 ) 14 15 type Opt func(*StringTemplate) error 16 17 func Template(rawTemplate string) Opt { 18 return func(up *StringTemplate) error { 19 up.rawTemplate = rawTemplate 20 return nil 21 } 22 } 23 24 // Function allows the user to specify functions for use in the template. If the name provided is a function that 25 // already exists in the function map, this will override the previously specified function. 26 func Function(name string, f interface{}) Opt { 27 return func(up *StringTemplate) error { 28 if name == "" { 29 return fmt.Errorf("missing function name") 30 } 31 if f == nil { 32 return fmt.Errorf("missing function") 33 } 34 up.funcMap[name] = f 35 return nil 36 } 37 } 38 39 // StringTemplate creates strings based on the provided template. 40 // This uses the go templating language, so anything that adheres to that language will function in this struct. 41 // There are several custom functions available for use in the template: 42 // - random 43 // - Randomly generated characters. This uses the charset specified in RandomCharset. Must include a length. 44 // Example: {{ rand 20 }} 45 // 46 // - truncate 47 // - Truncates the previous value to the specified length. Must include a maximum length. 48 // Example: {{ .DisplayName | truncate 10 }} 49 // 50 // - truncate_sha256 51 // - Truncates the previous value to the specified length. If the original length is greater than the length 52 // 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 53 // be no longer than the length specified. 54 // Example: {{ .DisplayName | truncate_sha256 30 }} 55 // 56 // - uppercase 57 // - Uppercases the previous value. 58 // Example: {{ .RoleName | uppercase }} 59 // 60 // - lowercase 61 // - Lowercases the previous value. 62 // Example: {{ .DisplayName | lowercase }} 63 // 64 // - replace 65 // - Performs a string find & replace 66 // Example: {{ .DisplayName | replace - _ }} 67 // 68 // - sha256 69 // - SHA256 hashes the previous value. 70 // Example: {{ .DisplayName | sha256 }} 71 // 72 // - base64 73 // - base64 encodes the previous value. 74 // Example: {{ .DisplayName | base64 }} 75 // 76 // - unix_time 77 // - Provides the current unix time in seconds. 78 // Example: {{ unix_time }} 79 // 80 // - unix_time_millis 81 // - Provides the current unix time in milliseconds. 82 // Example: {{ unix_time_millis }} 83 // 84 // - timestamp 85 // - Provides the current time. Must include a standard Go format string 86 // 87 // - uuid 88 // - Generates a UUID 89 // Example: {{ uuid }} 90 type StringTemplate struct { 91 rawTemplate string 92 tmpl *template.Template 93 funcMap template.FuncMap 94 } 95 96 // NewTemplate creates a StringTemplate. No arguments are required 97 // as this has reasonable defaults for all values. 98 // The default template is specified in the DefaultTemplate constant. 99 func NewTemplate(opts ...Opt) (up StringTemplate, err error) { 100 up = StringTemplate{ 101 funcMap: map[string]interface{}{ 102 "random": base62.Random, 103 "truncate": truncate, 104 "truncate_sha256": truncateSHA256, 105 "uppercase": uppercase, 106 "lowercase": lowercase, 107 "replace": replace, 108 "sha256": hashSHA256, 109 "base64": encodeBase64, 110 111 "unix_time": unixTime, 112 "unix_time_millis": unixTimeMillis, 113 "timestamp": timestamp, 114 "uuid": uuid, 115 }, 116 } 117 118 merr := &multierror.Error{} 119 for _, opt := range opts { 120 merr = multierror.Append(merr, opt(&up)) 121 } 122 123 err = merr.ErrorOrNil() 124 if err != nil { 125 return up, err 126 } 127 128 if up.rawTemplate == "" { 129 return StringTemplate{}, fmt.Errorf("missing template") 130 } 131 132 tmpl, err := template.New("template"). 133 Funcs(up.funcMap). 134 Parse(up.rawTemplate) 135 if err != nil { 136 return StringTemplate{}, fmt.Errorf("unable to parse template: %w", err) 137 } 138 up.tmpl = tmpl 139 140 return up, nil 141 } 142 143 // Generate based on the provided template 144 func (up StringTemplate) Generate(data interface{}) (string, error) { 145 if up.tmpl == nil || up.rawTemplate == "" { 146 return "", fmt.Errorf("failed to generate: template not initialized") 147 } 148 str := &strings.Builder{} 149 err := up.tmpl.Execute(str, data) 150 if err != nil { 151 return "", fmt.Errorf("unable to apply template: %w", err) 152 } 153 154 return str.String(), nil 155 }