api.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // Package api implements an HTTP-based API and server for CFSSL.
  2. package api
  3. import (
  4. "encoding/json"
  5. "io/ioutil"
  6. "net/http"
  7. "github.com/cloudflare/cfssl/errors"
  8. "github.com/cloudflare/cfssl/log"
  9. )
  10. // Handler is an interface providing a generic mechanism for handling HTTP requests.
  11. type Handler interface {
  12. Handle(w http.ResponseWriter, r *http.Request) error
  13. }
  14. // HTTPHandler is a wrapper that encapsulates Handler interface as http.Handler.
  15. // HTTPHandler also enforces that the Handler only responds to requests with registered HTTP methods.
  16. type HTTPHandler struct {
  17. Handler // CFSSL handler
  18. Methods []string // The associated HTTP methods
  19. }
  20. // HandlerFunc is similar to the http.HandlerFunc type; it serves as
  21. // an adapter allowing the use of ordinary functions as Handlers. If
  22. // f is a function with the appropriate signature, HandlerFunc(f) is a
  23. // Handler object that calls f.
  24. type HandlerFunc func(http.ResponseWriter, *http.Request) error
  25. // Handle calls f(w, r)
  26. func (f HandlerFunc) Handle(w http.ResponseWriter, r *http.Request) error {
  27. w.Header().Set("Content-Type", "application/json")
  28. return f(w, r)
  29. }
  30. // HandleError is the centralised error handling and reporting.
  31. func HandleError(w http.ResponseWriter, err error) (code int) {
  32. if err == nil {
  33. return http.StatusOK
  34. }
  35. msg := err.Error()
  36. httpCode := http.StatusInternalServerError
  37. // If it is recognized as HttpError emitted from cfssl,
  38. // we rewrite the status code accordingly. If it is a
  39. // cfssl error, set the http status to StatusBadRequest
  40. switch err := err.(type) {
  41. case *errors.HTTPError:
  42. httpCode = err.StatusCode
  43. code = err.StatusCode
  44. case *errors.Error:
  45. httpCode = http.StatusBadRequest
  46. code = err.ErrorCode
  47. msg = err.Message
  48. }
  49. response := NewErrorResponse(msg, code)
  50. jsonMessage, err := json.Marshal(response)
  51. if err != nil {
  52. log.Errorf("Failed to marshal JSON: %v", err)
  53. } else {
  54. msg = string(jsonMessage)
  55. }
  56. http.Error(w, msg, httpCode)
  57. return code
  58. }
  59. // ServeHTTP encapsulates the call to underlying Handler to handle the request
  60. // and return the response with proper HTTP status code
  61. func (h HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  62. var err error
  63. var match bool
  64. // Throw 405 when requested with an unsupported verb.
  65. for _, m := range h.Methods {
  66. if m == r.Method {
  67. match = true
  68. }
  69. }
  70. if match {
  71. err = h.Handle(w, r)
  72. } else {
  73. err = errors.NewMethodNotAllowed(r.Method)
  74. }
  75. status := HandleError(w, err)
  76. log.Infof("%s - \"%s %s\" %d", r.RemoteAddr, r.Method, r.URL, status)
  77. }
  78. // readRequestBlob takes a JSON-blob-encoded response body in the form
  79. // map[string]string and returns it, the list of keywords presented,
  80. // and any error that occurred.
  81. func readRequestBlob(r *http.Request) (map[string]string, error) {
  82. var blob map[string]string
  83. body, err := ioutil.ReadAll(r.Body)
  84. if err != nil {
  85. return nil, err
  86. }
  87. r.Body.Close()
  88. err = json.Unmarshal(body, &blob)
  89. if err != nil {
  90. return nil, err
  91. }
  92. return blob, nil
  93. }
  94. // ProcessRequestOneOf reads a JSON blob for the request and makes
  95. // sure it contains one of a set of keywords. For example, a request
  96. // might have the ('foo' && 'bar') keys, OR it might have the 'baz'
  97. // key. In either case, we want to accept the request; however, if
  98. // none of these sets shows up, the request is a bad request, and it
  99. // should be returned.
  100. func ProcessRequestOneOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
  101. blob, err := readRequestBlob(r)
  102. if err != nil {
  103. return nil, nil, err
  104. }
  105. var matched []string
  106. for _, set := range keywordSets {
  107. if matchKeywords(blob, set) {
  108. if matched != nil {
  109. return nil, nil, errors.NewBadRequestString("mismatched parameters")
  110. }
  111. matched = set
  112. }
  113. }
  114. if matched == nil {
  115. return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
  116. }
  117. return blob, matched, nil
  118. }
  119. // ProcessRequestFirstMatchOf reads a JSON blob for the request and returns
  120. // the first match of a set of keywords. For example, a request
  121. // might have one of the following combinations: (foo=1, bar=2), (foo=1), and (bar=2)
  122. // By giving a specific ordering of those combinations, we could decide how to accept
  123. // the request.
  124. func ProcessRequestFirstMatchOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
  125. blob, err := readRequestBlob(r)
  126. if err != nil {
  127. return nil, nil, err
  128. }
  129. for _, set := range keywordSets {
  130. if matchKeywords(blob, set) {
  131. return blob, set, nil
  132. }
  133. }
  134. return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
  135. }
  136. func matchKeywords(blob map[string]string, keywords []string) bool {
  137. for _, keyword := range keywords {
  138. if _, ok := blob[keyword]; !ok {
  139. return false
  140. }
  141. }
  142. return true
  143. }
  144. // ResponseMessage implements the standard for response errors and
  145. // messages. A message has a code and a string message.
  146. type ResponseMessage struct {
  147. Code int `json:"code"`
  148. Message string `json:"message"`
  149. }
  150. // Response implements the CloudFlare standard for API
  151. // responses.
  152. type Response struct {
  153. Success bool `json:"success"`
  154. Result interface{} `json:"result"`
  155. Errors []ResponseMessage `json:"errors"`
  156. Messages []ResponseMessage `json:"messages"`
  157. }
  158. // NewSuccessResponse is a shortcut for creating new successul API
  159. // responses.
  160. func NewSuccessResponse(result interface{}) Response {
  161. return Response{
  162. Success: true,
  163. Result: result,
  164. Errors: []ResponseMessage{},
  165. Messages: []ResponseMessage{},
  166. }
  167. }
  168. // NewSuccessResponseWithMessage is a shortcut for creating new successul API
  169. // responses that includes a message.
  170. func NewSuccessResponseWithMessage(result interface{}, message string, code int) Response {
  171. return Response{
  172. Success: true,
  173. Result: result,
  174. Errors: []ResponseMessage{},
  175. Messages: []ResponseMessage{{code, message}},
  176. }
  177. }
  178. // NewErrorResponse is a shortcut for creating an error response for a
  179. // single error.
  180. func NewErrorResponse(message string, code int) Response {
  181. return Response{
  182. Success: false,
  183. Result: nil,
  184. Errors: []ResponseMessage{{code, message}},
  185. Messages: []ResponseMessage{},
  186. }
  187. }
  188. // SendResponse builds a response from the result, sets the JSON
  189. // header, and writes to the http.ResponseWriter.
  190. func SendResponse(w http.ResponseWriter, result interface{}) error {
  191. response := NewSuccessResponse(result)
  192. w.Header().Set("Content-Type", "application/json")
  193. enc := json.NewEncoder(w)
  194. err := enc.Encode(response)
  195. return err
  196. }
  197. // SendResponseWithMessage builds a response from the result and the
  198. // provided message, sets the JSON header, and writes to the
  199. // http.ResponseWriter.
  200. func SendResponseWithMessage(w http.ResponseWriter, result interface{}, message string, code int) error {
  201. response := NewSuccessResponseWithMessage(result, message, code)
  202. w.Header().Set("Content-Type", "application/json")
  203. enc := json.NewEncoder(w)
  204. err := enc.Encode(response)
  205. return err
  206. }