Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sanitize policy behavior across backends #3324

Merged
merged 4 commits into from
Sep 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion builtin/credential/approle/path_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -1359,7 +1359,7 @@ func (b *backend) pathRolePoliciesDelete(req *logical.Request, data *framework.F
lock.Lock()
defer lock.Unlock()

role.Policies = policyutil.ParsePolicies(data.GetDefaultOrZero("policies"))
role.Policies = []string{}

return nil, b.setRoleEntry(req.Storage, roleName, role, "")
}
Expand Down
6 changes: 3 additions & 3 deletions builtin/credential/approle/path_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ func TestAppRole_RoleCRUD(t *testing.T) {

expected := map[string]interface{}{
"bind_secret_id": true,
"policies": []string{"default", "p", "q", "r", "s"},
"policies": []string{"p", "q", "r", "s"},
"secret_id_num_uses": 10,
"secret_id_ttl": 300,
"token_ttl": 400,
Expand Down Expand Up @@ -656,7 +656,7 @@ func TestAppRole_RoleCRUD(t *testing.T) {
}

expected = map[string]interface{}{
"policies": []string{"a", "b", "c", "d", "default"},
"policies": []string{"a", "b", "c", "d"},
"secret_id_num_uses": 100,
"secret_id_ttl": 3000,
"token_ttl": 4000,
Expand Down Expand Up @@ -764,7 +764,7 @@ func TestAppRole_RoleCRUD(t *testing.T) {
t.Fatalf("err:%v resp:%#v", err, resp)
}

if !reflect.DeepEqual(resp.Data["policies"].([]string), []string{"a1", "b1", "c1", "d1", "default"}) {
if !reflect.DeepEqual(resp.Data["policies"].([]string), []string{"a1", "b1", "c1", "d1"}) {
t.Fatalf("bad: policies: actual:%s\n", resp.Data["policies"].([]string))
}
roleReq.Operation = logical.DeleteOperation
Expand Down
8 changes: 4 additions & 4 deletions builtin/credential/aws/path_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ to 0, in which case the value will fallback to the system/mount defaults.`,
Description: "The maximum allowed lifetime of tokens issued using this role.",
},
"policies": {
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Default: "default",
Description: "Policies to be set on tokens issued using this role.",
},
Expand Down Expand Up @@ -626,11 +626,11 @@ func (b *backend) pathRoleCreateUpdate(
return logical.ErrorResponse("at least be one bound parameter should be specified on the role"), nil
}

policiesStr, ok := data.GetOk("policies")
policiesRaw, ok := data.GetOk("policies")
if ok {
roleEntry.Policies = policyutil.ParsePolicies(policiesStr.(string))
roleEntry.Policies = policyutil.ParsePolicies(policiesRaw)
} else if req.Operation == logical.CreateOperation {
roleEntry.Policies = []string{"default"}
roleEntry.Policies = []string{}
}

disallowReauthenticationBool, ok := data.GetOk("disallow_reauthentication")
Expand Down
6 changes: 3 additions & 3 deletions builtin/credential/aws/path_role_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ If set, the created tag can only be used by the instance with the given ID.`,
},

"policies": &framework.FieldSchema{
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Description: "Policies to be associated with the tag. If set, must be a subset of the role's policies. If set, but set to an empty value, only the 'default' policy will be given to issued tokens.",
},

Expand Down Expand Up @@ -107,9 +107,9 @@ func (b *backend) pathRoleTagUpdate(
// should be inherited. So, by leaving the policies var unset to anything when it is not
// supplied, we ensure that it inherits all the policies on the role.
var policies []string
policiesStr, ok := data.GetOk("policies")
policiesRaw, ok := data.GetOk("policies")
if ok {
policies = policyutil.ParsePolicies(policiesStr.(string))
policies = policyutil.ParsePolicies(policiesRaw)
}
if !strutil.StrListSubset(roleEntry.Policies, policies) {
resp.AddWarning("Policies on the tag are not a subset of the policies set on the role. Login will not be allowed with this tag unless the role policies are updated.")
Expand Down
2 changes: 1 addition & 1 deletion builtin/credential/aws/path_role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ func TestAwsEc2_RoleCrud(t *testing.T) {
"allow_instance_migration": true,
"ttl": time.Duration(600),
"max_ttl": time.Duration(1200),
"policies": []string{"default", "testpolicy1", "testpolicy2"},
"policies": []string{"testpolicy1", "testpolicy2"},
"disallow_reauthentication": true,
"period": time.Duration(60),
}
Expand Down
6 changes: 3 additions & 3 deletions builtin/credential/cert/path_certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ certificate.`,
},

"policies": &framework.FieldSchema{
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Description: "Comma-seperated list of policies.",
},

Expand Down Expand Up @@ -133,7 +133,7 @@ func (b *backend) pathCertRead(
Data: map[string]interface{}{
"certificate": cert.Certificate,
"display_name": cert.DisplayName,
"policies": strings.Join(cert.Policies, ","),
"policies": cert.Policies,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any concern with the behavior change of the return here? I like this better but this does break compatibility.

"ttl": duration / time.Second,
},
}, nil
Expand All @@ -144,7 +144,7 @@ func (b *backend) pathCertWrite(
name := strings.ToLower(d.Get("name").(string))
certificate := d.Get("certificate").(string)
displayName := d.Get("display_name").(string)
policies := policyutil.ParsePolicies(d.Get("policies").(string))
policies := policyutil.ParsePolicies(d.Get("policies"))
allowedNames := d.Get("allowed_names").([]string)

// Default the display name to the certificate name if not given
Expand Down
9 changes: 5 additions & 4 deletions builtin/credential/ldap/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"
"time"

"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/logical"
logicaltest "github.com/hashicorp/vault/logical/testing"
"github.com/mitchellh/mapstructure"
Expand Down Expand Up @@ -94,7 +95,7 @@ func TestLdapAuthBackend_UserPolicies(t *testing.T) {
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
expected := []string{"default", "grouppolicy", "userpolicy"}
expected := []string{"grouppolicy", "userpolicy"}
if !reflect.DeepEqual(expected, resp.Auth.Policies) {
t.Fatalf("bad: policies: expected: %q, actual: %q", expected, resp.Auth.Policies)
}
Expand Down Expand Up @@ -211,7 +212,7 @@ func TestBackend_groupCrud(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepGroup(t, "g1", "foo"),
testAccStepReadGroup(t, "g1", "default,foo"),
testAccStepReadGroup(t, "g1", "foo"),
testAccStepDeleteGroup(t, "g1"),
testAccStepReadGroup(t, "g1", ""),
},
Expand Down Expand Up @@ -357,13 +358,13 @@ func testAccStepReadGroup(t *testing.T, group string, policies string) logicalte
}

var d struct {
Policies string `mapstructure:"policies"`
Policies []string `mapstructure:"policies"`
}
if err := mapstructure.Decode(resp.Data, &d); err != nil {
return err
}

if d.Policies != policies {
if !reflect.DeepEqual(d.Policies, policyutil.ParsePolicies(policies)) {
return fmt.Errorf("bad: %#v", resp)
}

Expand Down
8 changes: 3 additions & 5 deletions builtin/credential/ldap/path_groups.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package ldap

import (
"strings"

"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
Expand Down Expand Up @@ -31,7 +29,7 @@ func pathGroups(b *backend) *framework.Path {
},

"policies": &framework.FieldSchema{
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Description: "Comma-separated list of policies associated to the group.",
},
},
Expand Down Expand Up @@ -86,7 +84,7 @@ func (b *backend) pathGroupRead(

return &logical.Response{
Data: map[string]interface{}{
"policies": strings.Join(group.Policies, ","),
"policies": group.Policies,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same backwards compatibility concerns as above.

},
}, nil
}
Expand All @@ -95,7 +93,7 @@ func (b *backend) pathGroupWrite(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
// Store it
entry, err := logical.StorageEntryJSON("group/"+d.Get("name").(string), &GroupEntry{
Policies: policyutil.ParsePolicies(d.Get("policies").(string)),
Policies: policyutil.ParsePolicies(d.Get("policies")),
})
if err != nil {
return nil, err
Expand Down
2 changes: 0 additions & 2 deletions builtin/credential/ldap/path_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package ldap
import (
"fmt"
"sort"
"strings"

"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/logical"
Expand Down Expand Up @@ -59,7 +58,6 @@ func (b *backend) pathLogin(
Policies: policies,
Metadata: map[string]string{
"username": username,
"policies": strings.Join(policies, ","),
},
InternalData: map[string]interface{}{
"password": password,
Expand Down
6 changes: 3 additions & 3 deletions builtin/credential/ldap/path_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func pathUsers(b *backend) *framework.Path {
},

"policies": &framework.FieldSchema{
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Description: "Comma-separated list of policies associated with the user.",
},
},
Expand Down Expand Up @@ -93,7 +93,7 @@ func (b *backend) pathUserRead(
return &logical.Response{
Data: map[string]interface{}{
"groups": strings.Join(user.Groups, ","),
"policies": strings.Join(user.Policies, ","),
"policies": user.Policies,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same backwards compatibility concerns as above.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it breaks compatibility. I'm inclined to just make the change though rather than move to another field. (As I mentioned in chat, we'd definitely note this in CHANGES.)

I don't think in most cases these lookup endpoints are used by non-humans (and humans can easily understand it either way), and for anyone trying to feed them from a read into a write, the writes now accept slices so that will still work.

},
}, nil
}
Expand All @@ -102,7 +102,7 @@ func (b *backend) pathUserWrite(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
name := d.Get("name").(string)
groups := strutil.RemoveDuplicates(strutil.ParseStringSlice(d.Get("groups").(string), ","), false)
policies := policyutil.ParsePolicies(d.Get("policies").(string))
policies := policyutil.ParsePolicies(d.Get("policies"))
for i, g := range groups {
groups[i] = strings.TrimSpace(g)
}
Expand Down
4 changes: 2 additions & 2 deletions builtin/credential/okta/path_groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func pathGroups(b *backend) *framework.Path {
},

"policies": &framework.FieldSchema{
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Description: "Comma-separated list of policies associated to the group.",
},
},
Expand Down Expand Up @@ -146,7 +146,7 @@ func (b *backend) pathGroupWrite(
}

entry, err := logical.StorageEntryJSON("group/"+name, &GroupEntry{
Policies: policyutil.ParsePolicies(d.Get("policies").(string)),
Policies: policyutil.ParsePolicies(d.Get("policies")),
})
if err != nil {
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions builtin/credential/radius/path_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func pathUsers(b *backend) *framework.Path {
},

"policies": &framework.FieldSchema{
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Description: "Comma-separated list of policies associated to the user.",
},
},
Expand Down Expand Up @@ -111,7 +111,7 @@ func (b *backend) pathUserRead(
func (b *backend) pathUserWrite(
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

var policies = policyutil.ParsePolicies(d.Get("policies").(string))
var policies = policyutil.ParsePolicies(d.Get("policies"))
for _, policy := range policies {
if policy == "root" {
return logical.ErrorResponse("root policy cannot be granted by an authentication backend"), nil
Expand Down
13 changes: 7 additions & 6 deletions builtin/credential/userpass/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"
"time"

"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/logical"
logicaltest "github.com/hashicorp/vault/logical/testing"
"github.com/mitchellh/mapstructure"
Expand Down Expand Up @@ -106,7 +107,7 @@ func TestBackend_userCrud(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepUser(t, "web", "password", "foo"),
testAccStepReadUser(t, "web", "default,foo"),
testAccStepReadUser(t, "web", "foo"),
testAccStepDeleteUser(t, "web"),
testAccStepReadUser(t, "web", ""),
},
Expand Down Expand Up @@ -150,7 +151,7 @@ func TestBackend_passwordUpdate(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepUser(t, "web", "password", "foo"),
testAccStepReadUser(t, "web", "default,foo"),
testAccStepReadUser(t, "web", "foo"),
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
testUpdatePassword(t, "web", "newpassword"),
testAccStepLogin(t, "web", "newpassword", []string{"default", "foo"}),
Expand All @@ -175,10 +176,10 @@ func TestBackend_policiesUpdate(t *testing.T) {
Backend: b,
Steps: []logicaltest.TestStep{
testAccStepUser(t, "web", "password", "foo"),
testAccStepReadUser(t, "web", "default,foo"),
testAccStepReadUser(t, "web", "foo"),
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
testUpdatePolicies(t, "web", "foo,bar"),
testAccStepReadUser(t, "web", "bar,default,foo"),
testAccStepReadUser(t, "web", "bar,foo"),
testAccStepLogin(t, "web", "password", []string{"bar", "default", "foo"}),
},
})
Expand Down Expand Up @@ -311,13 +312,13 @@ func testAccStepReadUser(t *testing.T, name string, policies string) logicaltest
}

var d struct {
Policies string `mapstructure:"policies"`
Policies []string `mapstructure:"policies"`
}
if err := mapstructure.Decode(resp.Data, &d); err != nil {
return err
}

if d.Policies != policies {
if !reflect.DeepEqual(d.Policies, policyutil.ParsePolicies(policies)) {
return fmt.Errorf("bad: %#v", resp)
}

Expand Down
4 changes: 2 additions & 2 deletions builtin/credential/userpass/path_user_policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func pathUserPolicies(b *backend) *framework.Path {
Description: "Username for this user.",
},
"policies": &framework.FieldSchema{
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Description: "Comma-separated list of policies",
},
},
Expand All @@ -44,7 +44,7 @@ func (b *backend) pathUserPoliciesUpdate(
return nil, fmt.Errorf("username does not exist")
}

userEntry.Policies = policyutil.ParsePolicies(d.Get("policies").(string))
userEntry.Policies = policyutil.ParsePolicies(d.Get("policies"))

return nil, b.setUser(req.Storage, username, userEntry)
}
Expand Down
6 changes: 3 additions & 3 deletions builtin/credential/userpass/path_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func pathUsers(b *backend) *framework.Path {
},

"policies": &framework.FieldSchema{
Type: framework.TypeString,
Type: framework.TypeCommaStringSlice,
Description: "Comma-separated list of policies",
},
"ttl": &framework.FieldSchema{
Expand Down Expand Up @@ -137,7 +137,7 @@ func (b *backend) pathUserRead(

return &logical.Response{
Data: map[string]interface{}{
"policies": strings.Join(user.Policies, ","),
"policies": user.Policies,
"ttl": user.TTL.Seconds(),
"max_ttl": user.MaxTTL.Seconds(),
},
Expand Down Expand Up @@ -166,7 +166,7 @@ func (b *backend) userCreateUpdate(req *logical.Request, d *framework.FieldData)
}

if policiesRaw, ok := d.GetOk("policies"); ok {
userEntry.Policies = policyutil.ParsePolicies(policiesRaw.(string))
userEntry.Policies = policyutil.ParsePolicies(policiesRaw)
}

ttlStr := userEntry.TTL.String()
Expand Down
4 changes: 2 additions & 2 deletions helper/policyutil/policyutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ func ParsePolicies(policiesRaw interface{}) []string {
switch policiesRaw.(type) {
case string:
if policiesRaw.(string) == "" {
return []string{"default"}
return []string{}
}
policies = strings.Split(policiesRaw.(string), ",")
case []string:
policies = policiesRaw.([]string)
}

return SanitizePolicies(policies, true)
return SanitizePolicies(policies, false)
}

// SanitizePolicies performs the common input validation tasks
Expand Down