auth
auth/request.go
Instantiate a login verification handler. When starting up the authd binary, get our login verification handler set up. We're going to use environment variables to detect if NSQ is configured. If it is, that'll be used. Otherwise, fall back on logging to stdout.
| paddy@99 | 1 package auth |
| paddy@99 | 2 |
| paddy@104 | 3 import ( |
| paddy@104 | 4 "encoding/json" |
| paddy@104 | 5 "log" |
| paddy@104 | 6 "net/http" |
| paddy@104 | 7 |
| paddy@104 | 8 "bitbucket.org/ww/goautoneg" |
| paddy@104 | 9 ) |
| paddy@104 | 10 |
| paddy@99 | 11 const ( |
| paddy@99 | 12 requestErrAccessDenied = "access_denied" |
| paddy@99 | 13 requestErrInsufficient = "insufficient" |
| paddy@99 | 14 requestErrOverflow = "overflow" |
| paddy@99 | 15 requestErrInvalidValue = "invalid_value" |
| paddy@99 | 16 requestErrInvalidFormat = "invalid_format" |
| paddy@99 | 17 requestErrMissing = "missing" |
| paddy@99 | 18 requestErrNotFound = "not_found" |
| paddy@104 | 19 requestErrConflict = "conflict" |
| paddy@99 | 20 requestErrActOfGod = "act_of_god" |
| paddy@99 | 21 ) |
| paddy@99 | 22 |
| paddy@104 | 23 var ( |
| paddy@104 | 24 actOfGodResponse = response{Errors: []requestError{requestError{Slug: requestErrActOfGod}}} |
| paddy@104 | 25 invalidFormatResponse = response{Errors: []requestError{requestError{Slug: requestErrInvalidFormat, Field: "/"}}} |
| paddy@104 | 26 |
| paddy@104 | 27 encoders = []string{"application/json"} |
| paddy@104 | 28 ) |
| paddy@104 | 29 |
| paddy@104 | 30 type response struct { |
| paddy@108 | 31 Errors []requestError `json:"errors,omitempty"` |
| paddy@108 | 32 Logins []Login `json:"logins,omitempty"` |
| paddy@108 | 33 Profiles []Profile `json:"profiles,omitempty"` |
| paddy@108 | 34 Clients []Client `json:"clients,omitempty"` |
| paddy@108 | 35 Endpoints []Endpoint `json:"endpoints,omitempty"` |
| paddy@159 | 36 Sessions []Session `json:"sessions,omitempty"` |
| paddy@104 | 37 } |
| paddy@104 | 38 |
| paddy@99 | 39 type requestError struct { |
| paddy@99 | 40 Slug string `json:"error,omitempty"` |
| paddy@99 | 41 Field string `json:"field,omitempty"` |
| paddy@99 | 42 Param string `json:"param,omitempty"` |
| paddy@99 | 43 Header string `json:"header,omitempty"` |
| paddy@99 | 44 } |
| paddy@104 | 45 |
| paddy@104 | 46 func negotiate(h http.Handler) http.Handler { |
| paddy@104 | 47 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| paddy@110 | 48 if r.Header.Get("Accept") != "" { |
| paddy@110 | 49 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders) |
| paddy@110 | 50 if contentType == "" { |
| paddy@110 | 51 w.WriteHeader(http.StatusNotAcceptable) |
| paddy@110 | 52 w.Write([]byte("Unsupported content type requested: " + r.Header.Get("Accept"))) |
| paddy@110 | 53 return |
| paddy@110 | 54 } |
| paddy@104 | 55 } |
| paddy@104 | 56 h.ServeHTTP(w, r) |
| paddy@104 | 57 }) |
| paddy@104 | 58 } |
| paddy@104 | 59 |
| paddy@104 | 60 func encode(w http.ResponseWriter, r *http.Request, status int, resp response) { |
| paddy@104 | 61 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders) |
| paddy@104 | 62 w.Header().Set("content-type", contentType) |
| paddy@104 | 63 w.WriteHeader(status) |
| paddy@104 | 64 var err error |
| paddy@104 | 65 switch contentType { |
| paddy@104 | 66 case "application/json": |
| paddy@104 | 67 enc := json.NewEncoder(w) |
| paddy@104 | 68 err = enc.Encode(resp) |
| paddy@110 | 69 default: |
| paddy@110 | 70 enc := json.NewEncoder(w) |
| paddy@110 | 71 err = enc.Encode(resp) |
| paddy@104 | 72 } |
| paddy@104 | 73 if err != nil { |
| paddy@104 | 74 log.Println(err) |
| paddy@104 | 75 } |
| paddy@104 | 76 } |
| paddy@104 | 77 |
| paddy@133 | 78 func decode(r *http.Request, target interface{}) error { |
| paddy@133 | 79 defer r.Body.Close() |
| paddy@133 | 80 switch r.Header.Get("Content-Type") { |
| paddy@133 | 81 case "application/json": |
| paddy@133 | 82 dec := json.NewDecoder(r.Body) |
| paddy@133 | 83 return dec.Decode(target) |
| paddy@133 | 84 default: |
| paddy@133 | 85 dec := json.NewDecoder(r.Body) |
| paddy@133 | 86 return dec.Decode(target) |
| paddy@133 | 87 } |
| paddy@133 | 88 } |
| paddy@133 | 89 |
| paddy@104 | 90 func wrap(context Context, f func(w http.ResponseWriter, r *http.Request, context Context)) http.Handler { |
| paddy@104 | 91 return negotiate(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| paddy@165 | 92 w.Header().Set("Access-Control-Allow-Origin", "*") |
| paddy@165 | 93 w.Header().Set("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers")) |
| paddy@165 | 94 w.Header().Set("Access-Control-Allow-Credentials", "true") |
| paddy@165 | 95 if r.Method == "OPTIONS" { |
| paddy@165 | 96 w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS") |
| paddy@165 | 97 w.Header().Set("Allow", "GET, POST, PUT, DELETE, PATCH, OPTIONS") |
| paddy@165 | 98 w.WriteHeader(http.StatusOK) |
| paddy@165 | 99 return |
| paddy@165 | 100 } |
| paddy@104 | 101 f(w, r, context) |
| paddy@104 | 102 })) |
| paddy@104 | 103 } |