From d628aab646b3f54f0ed9b78289836d27f1f6cf38 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Tue, 5 Aug 2014 17:50:36 -0700 Subject: [PATCH] agent: ACL endpoint --- command/agent/acl_endpoint.go | 171 ++++++++++++++++++++++++++++++++++ command/agent/http.go | 7 ++ 2 files changed, 178 insertions(+) create mode 100644 command/agent/acl_endpoint.go diff --git a/command/agent/acl_endpoint.go b/command/agent/acl_endpoint.go new file mode 100644 index 000000000..cfec2c8f1 --- /dev/null +++ b/command/agent/acl_endpoint.go @@ -0,0 +1,171 @@ +package agent + +import ( + "fmt" + "github.com/hashicorp/consul/consul/structs" + "net/http" + "strings" +) + +// aclCreateResponse is used to wrap the ACL ID +type aclCreateResponse struct { + ID string +} + +func (s *HTTPServer) ACLDelete(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + args := structs.ACLRequest{ + Op: structs.ACLDelete, + } + s.parseDC(req, &args.Datacenter) + + // Pull out the session id + args.ACL.ID = strings.TrimPrefix(req.URL.Path, "/v1/acl/delete/") + if args.ACL.ID == "" { + resp.WriteHeader(400) + resp.Write([]byte("Missing ACL")) + return nil, nil + } + + var out string + if err := s.agent.RPC("ACL.Apply", &args, &out); err != nil { + return nil, err + } + return true, nil +} + +func (s *HTTPServer) ACLCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + return s.aclSet(resp, req, false) +} + +func (s *HTTPServer) ACLUpdate(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + return s.aclSet(resp, req, true) +} + +func (s *HTTPServer) aclSet(resp http.ResponseWriter, req *http.Request, update bool) (interface{}, error) { + // Mandate a PUT request + if req.Method != "PUT" { + resp.WriteHeader(405) + return nil, nil + } + + args := structs.ACLRequest{ + Op: structs.ACLSet, + ACL: structs.ACL{ + Type: structs.ACLTypeClient, + }, + } + s.parseDC(req, &args.Datacenter) + + // Handle optional request body + if req.ContentLength > 0 { + if err := decodeBody(req, &args.ACL, nil); err != nil { + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf("Request decode failed: %v", err))) + return nil, nil + } + } + + // Ensure there is no ID set for create + if !update && args.ACL.ID != "" { + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf("ACL ID cannot be set"))) + return nil, nil + } + + // Ensure there is an ID set for update + if update && args.ACL.ID == "" { + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf("ACL ID must be set"))) + return nil, nil + } + + // Create the acl, get the ID + var out string + if err := s.agent.RPC("ACL.Apply", &args, &out); err != nil { + return nil, err + } + + // Format the response as a JSON object + return aclCreateResponse{out}, nil +} + +func (s *HTTPServer) ACLClone(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + args := structs.ACLSpecificRequest{} + if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { + return nil, nil + } + + // Pull out the session id + args.ACL = strings.TrimPrefix(req.URL.Path, "/v1/acl/clone/") + if args.ACL == "" { + resp.WriteHeader(400) + resp.Write([]byte("Missing ACL")) + return nil, nil + } + + var out structs.IndexedACLs + defer setMeta(resp, &out.QueryMeta) + if err := s.agent.RPC("ACL.Get", &args, &out); err != nil { + return nil, err + } + + // Bail if the ACL is not found + if len(out.ACLs) == 0 { + resp.WriteHeader(404) + resp.Write([]byte(fmt.Sprintf("Target ACL not found"))) + return nil, nil + } + + // Create a new ACL + createArgs := structs.ACLRequest{ + Datacenter: args.Datacenter, + Op: structs.ACLSet, + ACL: *out.ACLs[0], + } + createArgs.ACL.ID = "" + + // Create the acl, get the ID + var outID string + if err := s.agent.RPC("ACL.Apply", &createArgs, &outID); err != nil { + return nil, err + } + + // Format the response as a JSON object + return aclCreateResponse{outID}, nil +} + +func (s *HTTPServer) ACLGet(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + args := structs.ACLSpecificRequest{} + if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { + return nil, nil + } + + // Pull out the session id + args.ACL = strings.TrimPrefix(req.URL.Path, "/v1/acl/info/") + if args.ACL == "" { + resp.WriteHeader(400) + resp.Write([]byte("Missing ACL")) + return nil, nil + } + + var out structs.IndexedACLs + defer setMeta(resp, &out.QueryMeta) + if err := s.agent.RPC("ACL.Get", &args, &out); err != nil { + return nil, err + } + return out.ACLs, nil +} + +func (s *HTTPServer) ACLList(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + args := structs.DCSpecificRequest{} + if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { + return nil, nil + } + + var out structs.IndexedACLs + defer setMeta(resp, &out.QueryMeta) + if err := s.agent.RPC("ACL.List", &args, &out); err != nil { + return nil, err + } + return out.ACLs, nil +} diff --git a/command/agent/http.go b/command/agent/http.go index a254ecf19..e40a883f8 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -99,6 +99,13 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) { s.mux.HandleFunc("/v1/session/node/", s.wrap(s.SessionsForNode)) s.mux.HandleFunc("/v1/session/list", s.wrap(s.SessionList)) + s.mux.HandleFunc("/v1/acl/create", s.wrap(s.ACLCreate)) + s.mux.HandleFunc("/v1/acl/delete/", s.wrap(s.ACLDelete)) + s.mux.HandleFunc("/v1/acl/info/", s.wrap(s.ACLGet)) + s.mux.HandleFunc("/v1/acl/update/", s.wrap(s.ACLUpdate)) + s.mux.HandleFunc("/v1/acl/clone/", s.wrap(s.ACLClone)) + s.mux.HandleFunc("/v1/acl/list", s.wrap(s.ACLList)) + if enableDebug { s.mux.HandleFunc("/debug/pprof/", pprof.Index) s.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)