func TestKeys(t *testing.T) { policy, _ := acl.Parse(testFilterRules) aclR, _ := acl.New(acl.DenyAll(), policy) type tcase struct { in []string out []string } cases := []tcase{ tcase{ in: []string{"foo/test", "foo/priv/nope", "foo/other", "zoo"}, out: []string{"foo/test", "foo/other"}, }, tcase{ in: []string{"abe", "lincoln"}, out: []string{}, }, tcase{ in: []string{"abe", "foo/1", "foo/2", "foo/3", "nope"}, out: []string{"foo/1", "foo/2", "foo/3"}, }, } for _, tc := range cases { out := FilterKeys(aclR, tc.in) if !reflect.DeepEqual(out, tc.out) { t.Fatalf("bad: %#v %#v", out, tc.out) } } }
func TestFilterDirEnt(t *testing.T) { policy, _ := acl.Parse(testFilterRules) aclR, _ := acl.New(acl.DenyAll(), policy) type tcase struct { in []string out []string } cases := []tcase{ tcase{ in: []string{"foo/test", "foo/priv/nope", "foo/other", "zoo"}, out: []string{"foo/test", "foo/other"}, }, tcase{ in: []string{"abe", "lincoln"}, out: nil, }, tcase{ in: []string{"abe", "foo/1", "foo/2", "foo/3", "nope"}, out: []string{"foo/1", "foo/2", "foo/3"}, }, } for _, tc := range cases { ents := structs.DirEntries{} for _, in := range tc.in { ents = append(ents, &structs.DirEntry{Key: in}) } ents = FilterDirEnt(aclR, ents) var outL []string for _, e := range ents { outL = append(outL, e.Key) } if !reflect.DeepEqual(outL, tc.out) { t.Fatalf("bad: %#v %#v", outL, tc.out) } } }
// Apply is used to apply a modifying request to the data store. This should // only be used for operations that modify the data func (a *ACL) Apply(args *structs.ACLRequest, reply *string) error { if done, err := a.srv.forward("ACL.Apply", args, args, reply); done { return err } defer metrics.MeasureSince([]string{"consul", "acl", "apply"}, time.Now()) // Verify we are allowed to serve this request if a.srv.config.ACLDatacenter != a.srv.config.Datacenter { return fmt.Errorf(aclDisabled) } // Verify token is permitted to modify ACLs if acl, err := a.srv.resolveToken(args.Token); err != nil { return err } else if acl == nil || !acl.ACLModify() { return permissionDeniedErr } switch args.Op { case structs.ACLSet: // Verify the ACL type switch args.ACL.Type { case structs.ACLTypeClient: case structs.ACLTypeManagement: default: return fmt.Errorf("Invalid ACL Type") } // Verify this is not a root ACL if acl.RootACL(args.ACL.ID) != nil { return fmt.Errorf("%s: Cannot modify root ACL", permissionDenied) } // Validate the rules compile _, err := acl.Parse(args.ACL.Rules) if err != nil { return fmt.Errorf("ACL rule compilation failed: %v", err) } // If no ID is provided, generate a new ID. This must // be done prior to appending to the raft log, because the ID is not // deterministic. Once the entry is in the log, the state update MUST // be deterministic or the followers will not converge. if args.ACL.ID == "" { state := a.srv.fsm.State() for { args.ACL.ID = generateUUID() _, acl, err := state.ACLGet(args.ACL.ID) if err != nil { a.srv.logger.Printf("[ERR] consul.acl: ACL lookup failed: %v", err) return err } if acl == nil { break } } } case structs.ACLDelete: if args.ACL.ID == "" { return fmt.Errorf("Missing ACL ID") } else if args.ACL.ID == anonymousToken { return fmt.Errorf("%s: Cannot delete anonymous token", permissionDenied) } default: return fmt.Errorf("Invalid ACL Operation") } // Apply the update resp, err := a.srv.raftApply(structs.ACLRequestType, args) if err != nil { a.srv.logger.Printf("[ERR] consul.acl: Apply failed: %v", err) return err } if respErr, ok := resp.(error); ok { return respErr } // Clear the cache if applicable if args.ACL.ID != "" { a.srv.aclAuthCache.ClearACL(args.ACL.ID) } // Check if the return type is a string if respString, ok := resp.(string); ok { *reply = respString } return nil }