auth
auth/http.go
Fix go vet issue. Was passing a Grant to fmt.Printf when I wanted a string. Changed to pass the code I was using to retrieve the Grant, as that turns out to be more relevant for the test, and go vet is now satisfied.
| paddy@51 | 1 package auth |
| paddy@51 | 2 |
| paddy@51 | 3 import ( |
| paddy@61 | 4 "html/template" |
| paddy@51 | 5 "net/http" |
| paddy@60 | 6 "net/url" |
| paddy@60 | 7 "time" |
| paddy@56 | 8 |
| paddy@56 | 9 "code.secondbit.org/uuid" |
| paddy@51 | 10 ) |
| paddy@51 | 11 |
| paddy@60 | 12 const ( |
| paddy@60 | 13 getGrantTemplateName = "get_grant" |
| paddy@60 | 14 defaultGrantExpiration = 600 // default to ten minute grant expirations |
| paddy@60 | 15 ) |
| paddy@51 | 16 |
| paddy@57 | 17 // GetGrantHandler presents and processes the page for asking a user to grant access |
| paddy@57 | 18 // to their data. See RFC 6749, Section 4.1. |
| paddy@51 | 19 func GetGrantHandler(w http.ResponseWriter, r *http.Request, context Context) { |
| paddy@56 | 20 if r.URL.Query().Get("client_id") == "" { |
| paddy@56 | 21 w.WriteHeader(http.StatusBadRequest) |
| paddy@56 | 22 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@61 | 23 "error": template.HTML("Client ID must be specified in the request."), |
| paddy@56 | 24 }) |
| paddy@56 | 25 return |
| paddy@56 | 26 } |
| paddy@56 | 27 clientID, err := uuid.Parse(r.URL.Query().Get("client_id")) |
| paddy@56 | 28 if err != nil { |
| paddy@56 | 29 w.WriteHeader(http.StatusBadRequest) |
| paddy@56 | 30 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@61 | 31 "error": template.HTML("client_id is not a valid Client ID."), |
| paddy@56 | 32 }) |
| paddy@56 | 33 return |
| paddy@56 | 34 } |
| paddy@64 | 35 redirectURI := r.URL.Query().Get("redirect_uri") |
| paddy@64 | 36 redirectURL, err := url.Parse(redirectURI) |
| paddy@64 | 37 if err != nil { |
| paddy@64 | 38 w.WriteHeader(http.StatusBadRequest) |
| paddy@64 | 39 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@64 | 40 "error": template.HTML("The redirect_uri specified is not valid."), |
| paddy@64 | 41 }) |
| paddy@64 | 42 return |
| paddy@64 | 43 } |
| paddy@56 | 44 client, err := context.GetClient(clientID) |
| paddy@56 | 45 if err != nil { |
| paddy@59 | 46 if err == ErrClientNotFound { |
| paddy@59 | 47 w.WriteHeader(http.StatusBadRequest) |
| paddy@59 | 48 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@61 | 49 "error": template.HTML("The specified Client couldn’t be found."), |
| paddy@59 | 50 }) |
| paddy@59 | 51 } else { |
| paddy@59 | 52 w.WriteHeader(http.StatusInternalServerError) |
| paddy@59 | 53 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@61 | 54 "internal_error": template.HTML(err.Error()), |
| paddy@59 | 55 }) |
| paddy@59 | 56 } |
| paddy@56 | 57 return |
| paddy@56 | 58 } |
| paddy@56 | 59 // whether a redirect URI is valid or not depends on the number of endpoints |
| paddy@56 | 60 // the client has registered |
| paddy@56 | 61 numEndpoints, err := context.CountEndpoints(clientID) |
| paddy@56 | 62 if err != nil { |
| paddy@56 | 63 w.WriteHeader(http.StatusInternalServerError) |
| paddy@56 | 64 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@61 | 65 "internal_error": template.HTML(err.Error()), |
| paddy@56 | 66 }) |
| paddy@56 | 67 return |
| paddy@56 | 68 } |
| paddy@56 | 69 var validURI bool |
| paddy@58 | 70 if redirectURI != "" { |
| paddy@58 | 71 // BUG(paddy): We really should normalize URIs before trying to compare them. |
| paddy@58 | 72 validURI, err = context.CheckEndpoint(clientID, redirectURI) |
| paddy@56 | 73 if err != nil { |
| paddy@56 | 74 w.WriteHeader(http.StatusInternalServerError) |
| paddy@56 | 75 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@61 | 76 "internal_error": template.HTML(err.Error()), |
| paddy@56 | 77 }) |
| paddy@56 | 78 return |
| paddy@56 | 79 } |
| paddy@56 | 80 } else if redirectURI == "" && numEndpoints == 1 { |
| paddy@56 | 81 // if we don't specify the endpoint and there's only one endpoint, the |
| paddy@56 | 82 // request is valid, and we're redirecting to that one endpoint |
| paddy@56 | 83 validURI = true |
| paddy@56 | 84 endpoints, err := context.ListEndpoints(clientID, 1, 0) |
| paddy@56 | 85 if err != nil { |
| paddy@56 | 86 w.WriteHeader(http.StatusInternalServerError) |
| paddy@56 | 87 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@61 | 88 "internal_error": template.HTML(err.Error()), |
| paddy@56 | 89 }) |
| paddy@56 | 90 return |
| paddy@56 | 91 } |
| paddy@56 | 92 if len(endpoints) != 1 { |
| paddy@56 | 93 validURI = false |
| paddy@56 | 94 } else { |
| paddy@66 | 95 u := endpoints[0].URI // Copy it here to avoid grabbing a pointer to the memstore |
| paddy@66 | 96 redirectURI = u.String() |
| paddy@66 | 97 redirectURL = &u |
| paddy@56 | 98 } |
| paddy@56 | 99 } else { |
| paddy@56 | 100 validURI = false |
| paddy@56 | 101 } |
| paddy@56 | 102 if !validURI { |
| paddy@56 | 103 w.WriteHeader(http.StatusBadRequest) |
| paddy@56 | 104 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@61 | 105 "error": template.HTML("The redirect_uri specified is not valid."), |
| paddy@56 | 106 }) |
| paddy@56 | 107 return |
| paddy@56 | 108 } |
| paddy@60 | 109 scope := r.URL.Query().Get("scope") |
| paddy@60 | 110 state := r.URL.Query().Get("state") |
| paddy@56 | 111 if r.URL.Query().Get("response_type") != "code" { |
| paddy@65 | 112 q := redirectURL.Query() |
| paddy@65 | 113 q.Add("error", "invalid_request") |
| paddy@65 | 114 q.Add("state", state) |
| paddy@65 | 115 redirectURL.RawQuery = q.Encode() |
| paddy@60 | 116 http.Redirect(w, r, redirectURL.String(), http.StatusFound) |
| paddy@60 | 117 return |
| paddy@56 | 118 } |
| paddy@56 | 119 if r.Method == "POST" { |
| paddy@63 | 120 // BUG(paddy): We need to implement CSRF protection when obtaining a grant code. |
| paddy@56 | 121 if r.PostFormValue("grant") == "approved" { |
| paddy@60 | 122 code := uuid.NewID().String() |
| paddy@60 | 123 grant := Grant{ |
| paddy@60 | 124 Code: code, |
| paddy@60 | 125 Created: time.Now(), |
| paddy@60 | 126 ExpiresIn: defaultGrantExpiration, |
| paddy@60 | 127 ClientID: clientID, |
| paddy@60 | 128 Scope: scope, |
| paddy@60 | 129 RedirectURI: redirectURI, |
| paddy@60 | 130 State: state, |
| paddy@60 | 131 } |
| paddy@60 | 132 err := context.SaveGrant(grant) |
| paddy@60 | 133 if err != nil { |
| paddy@66 | 134 q := redirectURL.Query() |
| paddy@66 | 135 q.Add("error", "server_error") |
| paddy@66 | 136 q.Add("state", state) |
| paddy@66 | 137 redirectURL.RawQuery = q.Encode() |
| paddy@60 | 138 http.Redirect(w, r, redirectURL.String(), http.StatusFound) |
| paddy@60 | 139 return |
| paddy@60 | 140 } |
| paddy@66 | 141 q := redirectURL.Query() |
| paddy@66 | 142 q.Add("code", code) |
| paddy@66 | 143 q.Add("state", state) |
| paddy@66 | 144 redirectURL.RawQuery = q.Encode() |
| paddy@60 | 145 http.Redirect(w, r, redirectURL.String(), http.StatusFound) |
| paddy@60 | 146 return |
| paddy@56 | 147 } |
| paddy@66 | 148 q := redirectURL.Query() |
| paddy@66 | 149 q.Add("error", "access_denied") |
| paddy@66 | 150 q.Add("state", state) |
| paddy@66 | 151 redirectURL.RawQuery = q.Encode() |
| paddy@60 | 152 http.Redirect(w, r, redirectURL.String(), http.StatusFound) |
| paddy@60 | 153 return |
| paddy@56 | 154 } |
| paddy@51 | 155 w.WriteHeader(http.StatusOK) |
| paddy@56 | 156 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 157 "client": client, |
| paddy@56 | 158 }) |
| paddy@51 | 159 } |