pull/32619/merge
Shashank Varma 2 days ago committed by GitHub
commit 876c60edcf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -18,6 +18,8 @@ package engine
import ( import (
"bytes" "bytes"
"crypto/sha1"
"encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -31,6 +33,7 @@ import (
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/Masterminds/sprig/v3" "github.com/Masterminds/sprig/v3"
"golang.org/x/crypto/bcrypt"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
goYaml "sigs.k8s.io/yaml/goyaml.v3" goYaml "sigs.k8s.io/yaml/goyaml.v3"
) )
@ -67,6 +70,7 @@ func funcMap() template.FuncMap {
"mustToJson": mustToJSON, "mustToJson": mustToJSON,
"fromJson": fromJSON, "fromJson": fromJSON,
"fromJsonArray": fromJSONArray, "fromJsonArray": fromJSONArray,
"htpasswd": htpasswd,
// Duration helpers // Duration helpers
"mustToDuration": mustToDuration, "mustToDuration": mustToDuration,
@ -99,6 +103,34 @@ func funcMap() template.FuncMap {
return f return f
} }
// htpasswd generates an Apache htpasswd entry for username and password.
// algorithm is optional and defaults to bcrypt. Supported values are
// "bcrypt", "sha", and "sha1".
func htpasswd(username, password string, algorithm ...string) string {
if strings.Contains(username, ":") {
return "invalid username: " + username
}
algo := "bcrypt"
if len(algorithm) > 0 && algorithm[0] != "" {
algo = algorithm[0]
}
switch algo {
case "bcrypt":
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "failed to encrypt string with bcrypt: " + err.Error()
}
return username + ":" + string(hash)
case "sha", "sha1":
sum := sha1.Sum([]byte(password))
return username + ":{SHA}" + base64.StdEncoding.EncodeToString(sum[:])
default:
return "invalid algorithm: " + algo
}
}
// toYAML takes an interface, marshals it to yaml, and returns a string. It will // toYAML takes an interface, marshals it to yaml, and returns a string. It will
// always return a string, even on marshal error (empty string). // always return a string, even on marshal error (empty string).
// //

@ -17,6 +17,8 @@ limitations under the License.
package engine package engine
import ( import (
"crypto/sha1"
"encoding/base64"
"math" "math"
"strings" "strings"
"testing" "testing"
@ -195,6 +197,59 @@ keyInElement1 = "valueInElement1"`,
} }
} }
func TestHtpasswd(t *testing.T) {
const username = "testuser"
const password = "testpassword"
shaSum := sha1.Sum([]byte(password))
shaExpected := username + ":{SHA}" + base64.StdEncoding.EncodeToString(shaSum[:])
tests := []struct {
name string
tpl string
expect string
bcrypt bool
}{{
name: "two-arg defaults to bcrypt",
tpl: `{{ htpasswd "testuser" "testpassword" }}`,
bcrypt: true,
}, {
name: "three-arg bcrypt",
tpl: `{{ htpasswd "testuser" "testpassword" "bcrypt" }}`,
bcrypt: true,
}, {
name: "three-arg sha",
tpl: `{{ htpasswd "testuser" "testpassword" "sha" }}`,
expect: shaExpected,
}, {
name: "three-arg sha1",
tpl: `{{ htpasswd "testuser" "testpassword" "sha1" }}`,
expect: shaExpected,
}, {
name: "invalid algorithm",
tpl: `{{ htpasswd "testuser" "testpassword" "md5" }}`,
expect: "invalid algorithm: md5",
}, {
name: "username containing colon",
tpl: `{{ htpasswd "user:name" "testpassword" }}`,
expect: "invalid username: user:name",
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var b strings.Builder
require.NoError(t, template.Must(template.New("test").Funcs(funcMap()).Parse(tt.tpl)).Execute(&b, nil), tt.tpl)
got := b.String()
if tt.bcrypt {
assert.True(t, strings.HasPrefix(got, username+":$"))
assert.NotContains(t, got, "{SHA}")
return
}
assert.Equal(t, tt.expect, got, tt.tpl)
})
}
}
func TestDurationHelpers(t *testing.T) { func TestDurationHelpers(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

Loading…
Cancel
Save