// 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 } // 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.Op == structs.ACLSet && args.ACL.ID == "" { state := a.srv.fsm.State() for { var err error args.ACL.ID, err = uuid.GenerateUUID() if err != nil { a.srv.logger.Printf("[ERR] consul.acl: UUID generation failed: %v", err) return err } _, 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 } } } // Do the apply now that this update is vetted. if err := aclApplyInternal(a.srv, args, reply); err != nil { return err } // Clear the cache if applicable if args.ACL.ID != "" { a.srv.aclAuthCache.ClearACL(args.ACL.ID) } return nil }
// 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) } 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 }
// 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 { if args.ACL.ID, err = uuid.GenerateUUID(); err != nil { a.srv.logger.Printf("[ERR] consul.acl: UUID generation failed: %v", err) return err } _, 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 }