auth
auth/request.go
Introduced scopes. Created a Scope type and a scopeStore interface, along with the memstore methods for the scopeStore. This will allow applications to define access with granularity, so users can grant access to some data, not _all_ data. We're operating on the assumption that there won't be an unreasonable number of scopes defined, so there is no paging operation included for the ListScopes method. This is a decision that may have to be revisited in the future, depending on usecases.
| 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 } |