auth
auth/request.go
Clean up sessions and tokens after Profile is deleted. Add a terminateSessionsByProfile method to our sessionStore to mark Sessions associated with a Profile as inactive. Implement memstore and postgres implementations of the terminateSessionsByProfile method. Add a TerminateSessionsByProfile wrapper method to Context. Add a revokeTokensByProfileID method to our tokenStore to mark Tokens associated with a Profile as revoked. Implement memstore and postgres implementation of the revokeTokensByProfileID method. Add a RevokeTokensByProfileID wrapper method to Context. Call our RevokeTokensByProfileID and TerminateSessionsByProfile methods after a Profile is deleted, to clean up the Tokens and Sessions associated with it.
1 package auth
3 import (
4 "encoding/json"
5 "log"
6 "net/http"
8 "bitbucket.org/ww/goautoneg"
9 )
11 const (
12 requestErrAccessDenied = "access_denied"
13 requestErrInsufficient = "insufficient"
14 requestErrOverflow = "overflow"
15 requestErrInvalidValue = "invalid_value"
16 requestErrInvalidFormat = "invalid_format"
17 requestErrMissing = "missing"
18 requestErrNotFound = "not_found"
19 requestErrConflict = "conflict"
20 requestErrActOfGod = "act_of_god"
21 )
23 var (
24 actOfGodResponse = response{Errors: []requestError{requestError{Slug: requestErrActOfGod}}}
25 invalidFormatResponse = response{Errors: []requestError{requestError{Slug: requestErrInvalidFormat, Field: "/"}}}
27 encoders = []string{"application/json"}
28 )
30 type response struct {
31 Errors []requestError `json:"errors,omitempty"`
32 Logins []Login `json:"logins,omitempty"`
33 Profiles []Profile `json:"profiles,omitempty"`
34 Clients []Client `json:"clients,omitempty"`
35 Endpoints []Endpoint `json:"endpoints,omitempty"`
36 Sessions []Session `json:"sessions,omitempty"`
37 }
39 type requestError struct {
40 Slug string `json:"error,omitempty"`
41 Field string `json:"field,omitempty"`
42 Param string `json:"param,omitempty"`
43 Header string `json:"header,omitempty"`
44 }
46 func negotiate(h http.Handler) http.Handler {
47 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
48 if r.Header.Get("Accept") != "" {
49 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders)
50 if contentType == "" {
51 w.WriteHeader(http.StatusNotAcceptable)
52 w.Write([]byte("Unsupported content type requested: " + r.Header.Get("Accept")))
53 return
54 }
55 }
56 h.ServeHTTP(w, r)
57 })
58 }
60 func encode(w http.ResponseWriter, r *http.Request, status int, resp response) {
61 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders)
62 w.Header().Set("content-type", contentType)
63 w.WriteHeader(status)
64 var err error
65 switch contentType {
66 case "application/json":
67 enc := json.NewEncoder(w)
68 err = enc.Encode(resp)
69 default:
70 enc := json.NewEncoder(w)
71 err = enc.Encode(resp)
72 }
73 if err != nil {
74 log.Println(err)
75 }
76 }
78 func decode(r *http.Request, target interface{}) error {
79 defer r.Body.Close()
80 switch r.Header.Get("Content-Type") {
81 case "application/json":
82 dec := json.NewDecoder(r.Body)
83 return dec.Decode(target)
84 default:
85 dec := json.NewDecoder(r.Body)
86 return dec.Decode(target)
87 }
88 }
90 func wrap(context Context, f func(w http.ResponseWriter, r *http.Request, context Context)) http.Handler {
91 return negotiate(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
92 f(w, r, context)
93 }))
94 }