vault: Support policy CRUD

This commit is contained in:
Armon Dadgar 2015-03-23 14:43:31 -07:00
parent 192dcf7d39
commit f40ed182c4
3 changed files with 225 additions and 5 deletions

View File

@ -17,6 +17,8 @@ func NewSystemBackend(core *Core) logical.Backend {
"auth/*",
"remount",
"revoke-prefix/*",
"policy",
"policy/*",
},
Paths: []*framework.Path{
@ -176,6 +178,41 @@ func NewSystemBackend(core *Core) logical.Backend {
HelpSynopsis: strings.TrimSpace(sysHelp["auth"][0]),
HelpDescription: strings.TrimSpace(sysHelp["auth"][1]),
},
&framework.Path{
Pattern: "policy$",
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: b.handlePolicyList,
},
HelpSynopsis: strings.TrimSpace(sysHelp["policy-list"][0]),
HelpDescription: strings.TrimSpace(sysHelp["policy-list"][1]),
},
&framework.Path{
Pattern: "policy/(?P<name>.+)",
Fields: map[string]*framework.FieldSchema{
"name": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["policy-name"][0]),
},
"rules": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["policy-rules"][0]),
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: b.handlePolicyRead,
logical.WriteOperation: b.handlePolicySet,
logical.DeleteOperation: b.handlePolicyDelete,
},
HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]),
HelpDescription: strings.TrimSpace(sysHelp["policy"][1]),
},
},
}
}
@ -383,6 +420,71 @@ func (b *SystemBackend) handleDisableAuth(
return nil, nil
}
// handlePolicyList handles the "policy" endpoint to provide the enabled policies
func (b *SystemBackend) handlePolicyList(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Get all the configured policies
policies, err := b.Core.policy.ListPolicies()
// Add the special "root" policy
policies = append(policies, "root")
return logical.ListResponse(policies), err
}
// handlePolicyRead handles the "policy/<name>" endpoint to read a policy
func (b *SystemBackend) handlePolicyRead(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
name := data.Get("name").(string)
policy, err := b.Core.policy.GetPolicy(name)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
if policy == nil {
return nil, nil
}
return &logical.Response{
Data: map[string]interface{}{
"name": name,
"rules": policy.Raw,
},
}, nil
}
// handlePolicySet handles the "policy/<name>" endpoint to set a policy
func (b *SystemBackend) handlePolicySet(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
name := data.Get("name").(string)
rules := data.Get("rules").(string)
// Validate the rules parse
parse, err := Parse(rules)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
// Override the name
parse.Name = name
// Update the policy
if err := b.Core.policy.SetPolicy(parse); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
return nil, nil
}
// handlePolicyDelete handles the "policy/<name>" endpoint to delete a policy
func (b *SystemBackend) handlePolicyDelete(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
name := data.Get("name").(string)
if err := b.Core.policy.DeletePolicy(name); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
return nil, nil
}
// sysHelp is all the help text for the sys backend.
var sysHelp = map[string][2]string{
"mounts": {
@ -513,4 +615,30 @@ Example: you might have an OAuth backend for GitHub, and one for Google Apps.
`User-friendly description for this crential backend.`,
"",
},
"policy-list": {
`List the configured access control policies.`,
`
List the names of the configured access control policies. Policies are associated
with client tokens to limit access to keys in the Vault.
`,
},
"policy": {
`Read, Modify, or Delete an access control policy.`,
`
Read the rules of an existing policy, create or update the rules of a policy,
or delete a policy.
`,
},
"policy-name": {
`The name of the policy. Example: "ops"`,
"",
},
"policy-rules": {
`The rules of the policy. Either given in HCL or JSON format.`,
"",
},
}

View File

@ -14,6 +14,8 @@ func TestSystemBackend_RootPaths(t *testing.T) {
"auth/*",
"remount",
"revoke-prefix/*",
"policy",
"policy/*",
}
b := testSystemBackend(t)
@ -392,6 +394,101 @@ func TestSystemBackend_disableAuth_invalid(t *testing.T) {
}
}
func TestSystemBackend_policyList(t *testing.T) {
b := testSystemBackend(t)
req := logical.TestRequest(t, logical.ReadOperation, "policy")
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
exp := map[string]interface{}{
"keys": []string{"root"},
}
if !reflect.DeepEqual(resp.Data, exp) {
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
}
}
func TestSystemBackend_policyCRUD(t *testing.T) {
b := testSystemBackend(t)
// Create the policy
rules := `path "foo/" { policy = "read" }`
req := logical.TestRequest(t, logical.WriteOperation, "policy/foo")
req.Data["rules"] = rules
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v %#v", err, resp)
}
if resp != nil {
t.Fatalf("bad: %#v", resp)
}
// Read the policy
req = logical.TestRequest(t, logical.ReadOperation, "policy/foo")
resp, err = b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
exp := map[string]interface{}{
"name": "foo",
"rules": rules,
}
if !reflect.DeepEqual(resp.Data, exp) {
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
}
// List the policies
req = logical.TestRequest(t, logical.ReadOperation, "policy")
resp, err = b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
exp = map[string]interface{}{
"keys": []string{"foo", "root"},
}
if !reflect.DeepEqual(resp.Data, exp) {
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
}
// Delete the policy
req = logical.TestRequest(t, logical.DeleteOperation, "policy/foo")
resp, err = b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %#v", resp)
}
// Read the policy (deleted)
req = logical.TestRequest(t, logical.ReadOperation, "policy/foo")
resp, err = b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %#v", resp)
}
// List the policies (deleted)
req = logical.TestRequest(t, logical.ReadOperation, "policy")
resp, err = b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
exp = map[string]interface{}{
"keys": []string{"root"},
}
if !reflect.DeepEqual(resp.Data, exp) {
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
}
}
func testSystemBackend(t *testing.T) logical.Backend {
c, _ := TestCoreUnsealed(t)
return NewSystemBackend(c)

View File

@ -46,11 +46,6 @@ func Parse(rules string) (*Policy, error) {
return nil, fmt.Errorf("Failed to parse ACL rules: %v", err)
}
// Validate a name is given
if p.Name == "" {
return nil, fmt.Errorf("Policy name is missing")
}
// Validate the path policy
for _, pp := range p.Paths {
switch pp.Policy {