auth
auth/request.go
Randomly generate codes. We've been using our IDs for auth codes. But our IDs may at some point be non-random, for the purpose of optimising database performance, or some other perfectly valid reason. Auth codes we always want to be random, and have no relation to IDs, so why conflate them? Instead, we pull 16 random bytes out of crypto/rand.Reader and hex encode them.
| 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@104 | 36 } |
| paddy@104 | 37 |
| paddy@99 | 38 type requestError struct { |
| paddy@99 | 39 Slug string `json:"error,omitempty"` |
| paddy@99 | 40 Field string `json:"field,omitempty"` |
| paddy@99 | 41 Param string `json:"param,omitempty"` |
| paddy@99 | 42 Header string `json:"header,omitempty"` |
| paddy@99 | 43 } |
| paddy@104 | 44 |
| paddy@104 | 45 func negotiate(h http.Handler) http.Handler { |
| paddy@104 | 46 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| paddy@110 | 47 if r.Header.Get("Accept") != "" { |
| paddy@110 | 48 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders) |
| paddy@110 | 49 if contentType == "" { |
| paddy@110 | 50 w.WriteHeader(http.StatusNotAcceptable) |
| paddy@110 | 51 w.Write([]byte("Unsupported content type requested: " + r.Header.Get("Accept"))) |
| paddy@110 | 52 return |
| paddy@110 | 53 } |
| paddy@104 | 54 } |
| paddy@104 | 55 h.ServeHTTP(w, r) |
| paddy@104 | 56 }) |
| paddy@104 | 57 } |
| paddy@104 | 58 |
| paddy@104 | 59 func encode(w http.ResponseWriter, r *http.Request, status int, resp response) { |
| paddy@104 | 60 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders) |
| paddy@104 | 61 w.Header().Set("content-type", contentType) |
| paddy@104 | 62 w.WriteHeader(status) |
| paddy@104 | 63 var err error |
| paddy@104 | 64 switch contentType { |
| paddy@104 | 65 case "application/json": |
| paddy@104 | 66 enc := json.NewEncoder(w) |
| paddy@104 | 67 err = enc.Encode(resp) |
| paddy@110 | 68 default: |
| paddy@110 | 69 enc := json.NewEncoder(w) |
| paddy@110 | 70 err = enc.Encode(resp) |
| paddy@104 | 71 } |
| paddy@104 | 72 if err != nil { |
| paddy@104 | 73 log.Println(err) |
| paddy@104 | 74 } |
| paddy@104 | 75 } |
| paddy@104 | 76 |
| paddy@133 | 77 func decode(r *http.Request, target interface{}) error { |
| paddy@133 | 78 defer r.Body.Close() |
| paddy@133 | 79 switch r.Header.Get("Content-Type") { |
| paddy@133 | 80 case "application/json": |
| paddy@133 | 81 dec := json.NewDecoder(r.Body) |
| paddy@133 | 82 return dec.Decode(target) |
| paddy@133 | 83 default: |
| paddy@133 | 84 dec := json.NewDecoder(r.Body) |
| paddy@133 | 85 return dec.Decode(target) |
| paddy@133 | 86 } |
| paddy@133 | 87 } |
| paddy@133 | 88 |
| paddy@104 | 89 func wrap(context Context, f func(w http.ResponseWriter, r *http.Request, context Context)) http.Handler { |
| paddy@104 | 90 return negotiate(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| paddy@104 | 91 f(w, r, context) |
| paddy@104 | 92 })) |
| paddy@104 | 93 } |