Skip to content

Commit

Permalink
core: updates to password policy generator (#11596)
Browse files Browse the repository at this point in the history
* core: fix bug in password policies not using namespaces

* Add changelog
  • Loading branch information
jasonodonnell committed May 13, 2021
1 parent b26a365 commit afc0b8e
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 39 deletions.
3 changes: 3 additions & 0 deletions changelog/11596.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
core (enterprise): Fix plugins mounted in namespaces being unable to use password policies
```
4 changes: 2 additions & 2 deletions vault/dynamic_system_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,11 +336,11 @@ func (d dynamicSystemView) GeneratePasswordFromPolicy(ctx context.Context, polic
// Ensure there's a timeout on the context of some sort
if _, hasTimeout := ctx.Deadline(); !hasTimeout {
var cancel func()
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
ctx, cancel = context.WithTimeout(ctx, 1*time.Second)
defer cancel()
}

policyCfg, err := retrievePasswordPolicy(ctx, d.core.systemBarrierView, policyName)
policyCfg, err := d.retrievePasswordPolicy(ctx, policyName)
if err != nil {
return "", fmt.Errorf("failed to retrieve password policy: %w", err)
}
Expand Down
88 changes: 51 additions & 37 deletions vault/dynamic_system_view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package vault

import (
"context"
"encoding/json"
"encoding/base64"
"fmt"
"reflect"
"sort"
Expand All @@ -16,6 +16,22 @@ import (
"github.com/hashicorp/vault/sdk/logical"
)

var testPolicyName = "testpolicy"
var rawTestPasswordPolicy = `
length = 20
rule "charset" {
charset = "abcdefghijklmnopqrstuvwxyz"
min_chars = 1
}
rule "charset" {
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
min_chars = 1
}
rule "charset" {
charset = "0123456789"
min_chars = 1
}`

func TestIdentity_BackendTemplating(t *testing.T) {
var err error
coreConfig := &CoreConfig{
Expand Down Expand Up @@ -157,47 +173,45 @@ func TestIdentity_BackendTemplating(t *testing.T) {
}

func TestDynamicSystemView_GeneratePasswordFromPolicy_successful(t *testing.T) {
policyName := "testpolicy"
rawPolicy := map[string]interface{}{
"policy": `length = 20
rule "charset" {
charset = "abcdefghijklmnopqrstuvwxyz"
min_chars = 1
}
rule "charset" {
charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
min_chars = 1
}
rule "charset" {
charset = "0123456789"
min_chars = 1
}`,
}
marshalledPolicy, err := json.Marshal(rawPolicy)
if err != nil {
t.Fatalf("Unable to set up test: unable to marshal raw policy to JSON: %s", err)
var err error
coreConfig := &CoreConfig{
DisableMlock: true,
DisableCache: true,
Logger: log.NewNullLogger(),
CredentialBackends: map[string]logical.Factory{},
}

testStorage := fakeBarrier{
getEntry: &logical.StorageEntry{
Key: getPasswordPolicyKey(policyName),
Value: marshalledPolicy,
},
}
cluster := NewTestCluster(t, coreConfig, &TestClusterOptions{})

dsv := dynamicSystemView{
core: &Core{
systemBarrierView: NewBarrierView(testStorage, "sys/"),
},
cluster.Start()
defer cluster.Cleanup()

core := cluster.Cores[0].Core
TestWaitActive(t, core)

b64Policy := base64.StdEncoding.EncodeToString([]byte(rawTestPasswordPolicy))

path := fmt.Sprintf("sys/policies/password/%s", testPolicyName)
req := logical.TestRequest(t, logical.CreateOperation, path)
req.ClientToken = cluster.RootToken
req.Data["policy"] = b64Policy

_, err = core.HandleRequest(namespace.RootContext(nil), req)
if err != nil {
t.Fatalf("err: %v", err)
}

ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

ctx = namespace.RootContext(ctx)
dsv := dynamicSystemView{core: cluster.Cores[0].Core}

runeset := map[rune]bool{}
runesFound := []rune{}

for i := 0; i < 100; i++ {
actual, err := dsv.GeneratePasswordFromPolicy(ctx, policyName)
actual, err := dsv.GeneratePasswordFromPolicy(ctx, testPolicyName)
if err != nil {
t.Fatalf("no error expected, but got: %s", err)
}
Expand All @@ -220,12 +234,6 @@ rule "charset" {
}
}

type runes []rune

func (r runes) Len() int { return len(r) }
func (r runes) Less(i, j int) bool { return r[i] < r[j] }
func (r runes) Swap(i, j int) { r[i], r[j] = r[j], r[i] }

func TestDynamicSystemView_GeneratePasswordFromPolicy_failed(t *testing.T) {
type testCase struct {
policyName string
Expand Down Expand Up @@ -282,6 +290,12 @@ func TestDynamicSystemView_GeneratePasswordFromPolicy_failed(t *testing.T) {
}
}

type runes []rune

func (r runes) Len() int { return len(r) }
func (r runes) Less(i, j int) bool { return r[i] < r[j] }
func (r runes) Swap(i, j int) { r[i], r[j] = r[j], r[i] }

type fakeBarrier struct {
getEntry *logical.StorageEntry
getErr error
Expand Down
33 changes: 33 additions & 0 deletions vault/password_policy_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// +build !enterprise

package vault

import (
"context"
"encoding/json"
"fmt"
)

const (
passwordPolicySubPath = "password_policy/"
)

// retrievePasswordPolicy retrieves a password policy from the logical storage
func (d dynamicSystemView) retrievePasswordPolicy(ctx context.Context, policyName string) (*passwordPolicyConfig, error) {
storage := d.core.systemBarrierView.SubView(passwordPolicySubPath)
entry, err := storage.Get(ctx, policyName)
if err != nil {
return nil, err
}
if entry == nil {
return nil, nil
}

policyCfg := &passwordPolicyConfig{}
err = json.Unmarshal(entry.Value, &policyCfg)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal stored data: %w", err)
}

return policyCfg, nil
}

0 comments on commit afc0b8e

Please sign in to comment.