auth
auth/client.go
Support email verification. The bulk of this commit is auto-modifying files to export variables (mostly our request error types and our response type) so that they can be reused in a Go client for that API. We also implement the beginnings of a Go client for that API, implementing the bare minimum we need for our immediate purposes: the ability to retrieve information about a Login. This, of course, means we need an API endpoint that will return information about a Login, which in turn required us to implement a GetLogin method in our profileStore. Which got in-memory and postgres implementations. That done, we could add the Verification field and Verified field to the Login type, to keep track of whether we've verified the user's ownership of those communication methods (if the Login is, in fact, a communication method). This required us to update sql/postgres_init.sql to account for the new fields we're tracking. It also means that when creating a Login, we had to generate a UUID to use as the Verification field. To make things complete, we needed a verifyLogin method on the profileStore to mark a Login as verified. That, in turn, required an endpoint to control this through the API. While doing so, I lumped things together in an UpdateLogin handler just so we could reuse the endpoint and logic when resending a verification email that may have never reached the user, for whatever reason (the quintessential "send again" button). Finally, we implemented an email_verification listener that will pull email_verification events off NSQ, check for the requisite data integrity, and use mailgun to email out a verification/welcome email.
1.1 --- a/client.go Sun May 17 02:18:07 2015 -0400 1.2 +++ b/client.go Sun May 17 02:27:36 2015 -0400 1.3 @@ -455,22 +455,22 @@ 1.4 } 1.5 1.6 func CreateClientHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.7 - errors := []requestError{} 1.8 + errors := []RequestError{} 1.9 username, password, ok := r.BasicAuth() 1.10 if !ok { 1.11 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.12 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.13 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.14 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.15 return 1.16 } 1.17 profile, err := authenticate(username, password, c) 1.18 if err != nil { 1.19 if isAuthError(err) { 1.20 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.21 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.22 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.23 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.24 } else { 1.25 log.Printf("Error authenticating: %#+v\n", err) 1.26 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.27 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.28 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.29 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.30 } 1.31 return 1.32 } 1.33 @@ -482,19 +482,19 @@ 1.34 return 1.35 } 1.36 if req.Type == "" { 1.37 - errors = append(errors, requestError{Slug: requestErrMissing, Field: "/type"}) 1.38 + errors = append(errors, RequestError{Slug: RequestErrMissing, Field: "/type"}) 1.39 } else if req.Type != clientTypePublic && req.Type != clientTypeConfidential { 1.40 - errors = append(errors, requestError{Slug: requestErrInvalidValue, Field: "/type"}) 1.41 + errors = append(errors, RequestError{Slug: RequestErrInvalidValue, Field: "/type"}) 1.42 } 1.43 if req.Name == "" { 1.44 - errors = append(errors, requestError{Slug: requestErrMissing, Field: "/name"}) 1.45 + errors = append(errors, RequestError{Slug: RequestErrMissing, Field: "/name"}) 1.46 } else if len(req.Name) < minClientNameLen { 1.47 - errors = append(errors, requestError{Slug: requestErrInsufficient, Field: "/name"}) 1.48 + errors = append(errors, RequestError{Slug: RequestErrInsufficient, Field: "/name"}) 1.49 } else if len(req.Name) > maxClientNameLen { 1.50 - errors = append(errors, requestError{Slug: requestErrOverflow, Field: "/name"}) 1.51 + errors = append(errors, RequestError{Slug: RequestErrOverflow, Field: "/name"}) 1.52 } 1.53 if len(errors) > 0 { 1.54 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.55 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.56 return 1.57 } 1.58 client := Client{ 1.59 @@ -518,8 +518,8 @@ 1.60 err = c.SaveClient(client) 1.61 if err != nil { 1.62 if err == ErrClientAlreadyExists { 1.63 - errors = append(errors, requestError{Slug: requestErrConflict, Field: "/id"}) 1.64 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.65 + errors = append(errors, RequestError{Slug: RequestErrConflict, Field: "/id"}) 1.66 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.67 return 1.68 } 1.69 log.Printf("Error saving client: %#+v\n", err) 1.70 @@ -530,11 +530,11 @@ 1.71 for pos, u := range req.Endpoints { 1.72 uri, err := url.Parse(u) 1.73 if err != nil { 1.74 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Field: "/endpoints/" + strconv.Itoa(pos)}) 1.75 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Field: "/endpoints/" + strconv.Itoa(pos)}) 1.76 continue 1.77 } 1.78 if !uri.IsAbs() { 1.79 - errors = append(errors, requestError{Slug: requestErrInvalidValue, Field: "/endpoints/" + strconv.Itoa(pos)}) 1.80 + errors = append(errors, RequestError{Slug: RequestErrInvalidValue, Field: "/endpoints/" + strconv.Itoa(pos)}) 1.81 continue 1.82 } 1.83 endpoint := Endpoint{ 1.84 @@ -548,11 +548,11 @@ 1.85 err = c.AddEndpoints(endpoints) 1.86 if err != nil { 1.87 log.Printf("Error adding endpoints: %#+v\n", err) 1.88 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.89 - encode(w, r, http.StatusInternalServerError, response{Errors: errors, Clients: []Client{client}}) 1.90 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.91 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors, Clients: []Client{client}}) 1.92 return 1.93 } 1.94 - resp := response{ 1.95 + resp := Response{ 1.96 Clients: []Client{client}, 1.97 Endpoints: endpoints, 1.98 Errors: errors, 1.99 @@ -561,28 +561,28 @@ 1.100 } 1.101 1.102 func GetClientHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.103 - errors := []requestError{} 1.104 + errors := []RequestError{} 1.105 vars := mux.Vars(r) 1.106 if vars["id"] == "" { 1.107 - errors = append(errors, requestError{Slug: requestErrMissing, Param: "id"}) 1.108 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.109 + errors = append(errors, RequestError{Slug: RequestErrMissing, Param: "id"}) 1.110 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.111 return 1.112 } 1.113 id, err := uuid.Parse(vars["id"]) 1.114 if err != nil { 1.115 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "id"}) 1.116 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.117 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "id"}) 1.118 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.119 return 1.120 } 1.121 client, err := c.GetClient(id) 1.122 if err != nil { 1.123 if err == ErrClientNotFound { 1.124 - errors = append(errors, requestError{Slug: requestErrNotFound, Param: "id"}) 1.125 - encode(w, r, http.StatusNotFound, response{Errors: errors}) 1.126 + errors = append(errors, RequestError{Slug: RequestErrNotFound, Param: "id"}) 1.127 + encode(w, r, http.StatusNotFound, Response{Errors: errors}) 1.128 return 1.129 } 1.130 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.131 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.132 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.133 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.134 return 1.135 } 1.136 username, password, ok := r.BasicAuth() 1.137 @@ -592,11 +592,11 @@ 1.138 profile, err := authenticate(username, password, c) 1.139 if err != nil { 1.140 if isAuthError(err) { 1.141 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.142 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.143 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.144 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.145 } else { 1.146 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.147 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.148 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.149 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.150 } 1.151 return 1.152 } 1.153 @@ -604,7 +604,7 @@ 1.154 client.Secret = "" 1.155 } 1.156 } 1.157 - resp := response{ 1.158 + resp := Response{ 1.159 Clients: []Client{client}, 1.160 Errors: errors, 1.161 } 1.162 @@ -612,7 +612,7 @@ 1.163 } 1.164 1.165 func ListClientsHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.166 - errors := []requestError{} 1.167 + errors := []RequestError{} 1.168 var err error 1.169 // BUG(paddy): If ids are provided in query params, retrieve only those clients 1.170 num := defaultClientResponseSize 1.171 @@ -623,38 +623,38 @@ 1.172 if numStr != "" { 1.173 num, err = strconv.Atoi(numStr) 1.174 if err != nil { 1.175 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "num"}) 1.176 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "num"}) 1.177 } 1.178 if num > maxClientResponseSize { 1.179 - errors = append(errors, requestError{Slug: requestErrOverflow, Param: "num"}) 1.180 + errors = append(errors, RequestError{Slug: RequestErrOverflow, Param: "num"}) 1.181 } 1.182 if num < 1 { 1.183 - errors = append(errors, requestError{Slug: requestErrInsufficient, Param: "num"}) 1.184 + errors = append(errors, RequestError{Slug: RequestErrInsufficient, Param: "num"}) 1.185 } 1.186 } 1.187 if offsetStr != "" { 1.188 offset, err = strconv.Atoi(offsetStr) 1.189 if err != nil { 1.190 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "offset"}) 1.191 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "offset"}) 1.192 } 1.193 } 1.194 if ownerIDStr == "" { 1.195 - errors = append(errors, requestError{Slug: requestErrMissing, Param: "owner_id"}) 1.196 + errors = append(errors, RequestError{Slug: RequestErrMissing, Param: "owner_id"}) 1.197 } 1.198 if len(errors) > 0 { 1.199 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.200 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.201 return 1.202 } 1.203 ownerID, err := uuid.Parse(ownerIDStr) 1.204 if err != nil { 1.205 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "owner_id"}) 1.206 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.207 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "owner_id"}) 1.208 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.209 return 1.210 } 1.211 clients, err := c.ListClientsByOwner(ownerID, num, offset) 1.212 if err != nil { 1.213 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.214 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.215 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.216 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.217 return 1.218 } 1.219 username, password, ok := r.BasicAuth() 1.220 @@ -667,11 +667,11 @@ 1.221 profile, err := authenticate(username, password, c) 1.222 if err != nil { 1.223 if isAuthError(err) { 1.224 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.225 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.226 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.227 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.228 } else { 1.229 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.230 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.231 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.232 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.233 } 1.234 return 1.235 } 1.236 @@ -682,7 +682,7 @@ 1.237 } 1.238 } 1.239 } 1.240 - resp := response{ 1.241 + resp := Response{ 1.242 Clients: clients, 1.243 Errors: errors, 1.244 } 1.245 @@ -690,80 +690,80 @@ 1.246 } 1.247 1.248 func UpdateClientHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.249 - errors := []requestError{} 1.250 + errors := []RequestError{} 1.251 vars := mux.Vars(r) 1.252 if _, ok := vars["id"]; !ok { 1.253 - errors = append(errors, requestError{Slug: requestErrMissing, Param: "id"}) 1.254 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.255 + errors = append(errors, RequestError{Slug: RequestErrMissing, Param: "id"}) 1.256 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.257 return 1.258 } 1.259 id, err := uuid.Parse(vars["id"]) 1.260 if err != nil { 1.261 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "id"}) 1.262 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "id"}) 1.263 } 1.264 username, password, ok := r.BasicAuth() 1.265 if !ok { 1.266 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.267 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.268 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.269 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.270 return 1.271 } 1.272 profile, err := authenticate(username, password, c) 1.273 if err != nil { 1.274 if isAuthError(err) { 1.275 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.276 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.277 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.278 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.279 } else { 1.280 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.281 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.282 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.283 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.284 } 1.285 return 1.286 } 1.287 var change ClientChange 1.288 err = decode(r, &change) 1.289 if err != nil { 1.290 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Field: "/"}) 1.291 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.292 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Field: "/"}) 1.293 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.294 return 1.295 } 1.296 errs := change.Validate() 1.297 for _, err := range errs { 1.298 switch err { 1.299 case ErrEmptyChange: 1.300 - errors = append(errors, requestError{Slug: requestErrMissing, Field: "/"}) 1.301 + errors = append(errors, RequestError{Slug: RequestErrMissing, Field: "/"}) 1.302 case ErrClientNameTooShort: 1.303 - errors = append(errors, requestError{Slug: requestErrInsufficient, Field: "/name"}) 1.304 + errors = append(errors, RequestError{Slug: RequestErrInsufficient, Field: "/name"}) 1.305 case ErrClientNameTooLong: 1.306 - errors = append(errors, requestError{Slug: requestErrOverflow, Field: "/name"}) 1.307 + errors = append(errors, RequestError{Slug: RequestErrOverflow, Field: "/name"}) 1.308 case ErrClientLogoTooLong: 1.309 - errors = append(errors, requestError{Slug: requestErrOverflow, Field: "/logo"}) 1.310 + errors = append(errors, RequestError{Slug: RequestErrOverflow, Field: "/logo"}) 1.311 case ErrClientLogoNotURL: 1.312 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Field: "/logo"}) 1.313 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Field: "/logo"}) 1.314 case ErrClientWebsiteTooLong: 1.315 - errors = append(errors, requestError{Slug: requestErrOverflow, Field: "/website"}) 1.316 + errors = append(errors, RequestError{Slug: RequestErrOverflow, Field: "/website"}) 1.317 case ErrClientWebsiteNotURL: 1.318 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Field: "/website"}) 1.319 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Field: "/website"}) 1.320 default: 1.321 log.Println("Unrecognised error from client change validation:", err) 1.322 } 1.323 } 1.324 if len(errors) > 0 { 1.325 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.326 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.327 return 1.328 } 1.329 client, err := c.GetClient(id) 1.330 if err == ErrClientNotFound { 1.331 - errors = append(errors, requestError{Slug: requestErrNotFound, Param: "id"}) 1.332 - encode(w, r, http.StatusNotFound, response{Errors: errors}) 1.333 + errors = append(errors, RequestError{Slug: RequestErrNotFound, Param: "id"}) 1.334 + encode(w, r, http.StatusNotFound, Response{Errors: errors}) 1.335 return 1.336 } else if err != nil { 1.337 log.Println("Error retrieving client:", err) 1.338 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.339 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.340 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.341 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.342 return 1.343 } 1.344 if !client.OwnerID.Equal(profile.ID) { 1.345 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.346 - encode(w, r, http.StatusForbidden, response{Errors: errors}) 1.347 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.348 + encode(w, r, http.StatusForbidden, Response{Errors: errors}) 1.349 return 1.350 } 1.351 if change.Secret != nil && client.Type == clientTypeConfidential { 1.352 @@ -779,59 +779,59 @@ 1.353 err = c.UpdateClient(id, change) 1.354 if err != nil { 1.355 log.Println("Error updating client:", err) 1.356 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.357 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.358 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.359 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.360 return 1.361 } 1.362 client.ApplyChange(change) 1.363 - encode(w, r, http.StatusOK, response{Clients: []Client{client}, Errors: errors}) 1.364 + encode(w, r, http.StatusOK, Response{Clients: []Client{client}, Errors: errors}) 1.365 return 1.366 } 1.367 1.368 func RemoveClientHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.369 - errors := []requestError{} 1.370 + errors := []RequestError{} 1.371 vars := mux.Vars(r) 1.372 if _, ok := vars["id"]; !ok { 1.373 - errors = append(errors, requestError{Slug: requestErrMissing, Param: "id"}) 1.374 - encode(w, r, http.StatusNotFound, response{Errors: errors}) 1.375 + errors = append(errors, RequestError{Slug: RequestErrMissing, Param: "id"}) 1.376 + encode(w, r, http.StatusNotFound, Response{Errors: errors}) 1.377 return 1.378 } 1.379 id, err := uuid.Parse(vars["id"]) 1.380 if err != nil { 1.381 - errors = append(errors, requestError{Slug: requestErrNotFound, Param: "id"}) 1.382 + errors = append(errors, RequestError{Slug: RequestErrNotFound, Param: "id"}) 1.383 } 1.384 username, password, ok := r.BasicAuth() 1.385 if !ok { 1.386 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.387 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.388 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.389 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.390 return 1.391 } 1.392 profile, err := authenticate(username, password, c) 1.393 if err != nil { 1.394 if isAuthError(err) { 1.395 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.396 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.397 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.398 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.399 } else { 1.400 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.401 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.402 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.403 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.404 } 1.405 return 1.406 } 1.407 client, err := c.GetClient(id) 1.408 if err != nil { 1.409 if err == ErrClientNotFound { 1.410 - errors = append(errors, requestError{Slug: requestErrNotFound}) 1.411 - encode(w, r, http.StatusNotFound, response{Errors: errors}) 1.412 + errors = append(errors, RequestError{Slug: RequestErrNotFound}) 1.413 + encode(w, r, http.StatusNotFound, Response{Errors: errors}) 1.414 return 1.415 } 1.416 log.Println("Error retrieving client:", err) 1.417 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.418 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.419 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.420 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.421 return 1.422 } 1.423 if !client.OwnerID.Equal(profile.ID) { 1.424 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.425 - encode(w, r, http.StatusForbidden, response{Errors: errors}) 1.426 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.427 + encode(w, r, http.StatusForbidden, Response{Errors: errors}) 1.428 return 1.429 } 1.430 deleted := true 1.431 @@ -839,16 +839,16 @@ 1.432 err = c.UpdateClient(id, change) 1.433 if err != nil { 1.434 if err == ErrClientNotFound { 1.435 - errors = append(errors, requestError{Slug: requestErrNotFound}) 1.436 - encode(w, r, http.StatusNotFound, response{Errors: errors}) 1.437 + errors = append(errors, RequestError{Slug: RequestErrNotFound}) 1.438 + encode(w, r, http.StatusNotFound, Response{Errors: errors}) 1.439 return 1.440 } 1.441 log.Println("Error deleting client:", err) 1.442 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.443 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.444 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.445 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.446 return 1.447 } 1.448 - encode(w, r, http.StatusOK, response{Errors: errors}) 1.449 + encode(w, r, http.StatusOK, Response{Errors: errors}) 1.450 go cleanUpAfterClientDeletion(id, c) 1.451 return 1.452 } 1.453 @@ -857,50 +857,50 @@ 1.454 type addEndpointReq struct { 1.455 Endpoints []string `json:"endpoints"` 1.456 } 1.457 - errors := []requestError{} 1.458 + errors := []RequestError{} 1.459 vars := mux.Vars(r) 1.460 if vars["id"] == "" { 1.461 - errors = append(errors, requestError{Slug: requestErrMissing, Param: "id"}) 1.462 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.463 + errors = append(errors, RequestError{Slug: RequestErrMissing, Param: "id"}) 1.464 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.465 return 1.466 } 1.467 id, err := uuid.Parse(vars["id"]) 1.468 if err != nil { 1.469 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "id"}) 1.470 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.471 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "id"}) 1.472 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.473 return 1.474 } 1.475 username, password, ok := r.BasicAuth() 1.476 if !ok { 1.477 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.478 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.479 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.480 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.481 return 1.482 } 1.483 profile, err := authenticate(username, password, c) 1.484 if err != nil { 1.485 if isAuthError(err) { 1.486 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.487 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.488 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.489 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.490 } else { 1.491 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.492 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.493 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.494 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.495 } 1.496 return 1.497 } 1.498 client, err := c.GetClient(id) 1.499 if err != nil { 1.500 if err == ErrClientNotFound { 1.501 - errors = append(errors, requestError{Slug: requestErrNotFound, Param: "id"}) 1.502 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.503 + errors = append(errors, RequestError{Slug: RequestErrNotFound, Param: "id"}) 1.504 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.505 return 1.506 } 1.507 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.508 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.509 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.510 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.511 return 1.512 } 1.513 if !client.OwnerID.Equal(profile.ID) { 1.514 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.515 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.516 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.517 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.518 return 1.519 } 1.520 var req addEndpointReq 1.521 @@ -911,17 +911,17 @@ 1.522 return 1.523 } 1.524 if len(req.Endpoints) < 1 { 1.525 - errors = append(errors, requestError{Slug: requestErrMissing, Field: "/endpoints"}) 1.526 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.527 + errors = append(errors, RequestError{Slug: RequestErrMissing, Field: "/endpoints"}) 1.528 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.529 return 1.530 } 1.531 endpoints := []Endpoint{} 1.532 for pos, u := range req.Endpoints { 1.533 if parsed, err := url.Parse(u); err != nil { 1.534 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Field: "/endpoints/" + strconv.Itoa(pos)}) 1.535 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Field: "/endpoints/" + strconv.Itoa(pos)}) 1.536 continue 1.537 } else if !parsed.IsAbs() { 1.538 - errors = append(errors, requestError{Slug: requestErrInvalidValue, Field: "/endpoints" + strconv.Itoa(pos)}) 1.539 + errors = append(errors, RequestError{Slug: RequestErrInvalidValue, Field: "/endpoints" + strconv.Itoa(pos)}) 1.540 continue 1.541 } 1.542 e := Endpoint{ 1.543 @@ -933,7 +933,7 @@ 1.544 endpoints = append(endpoints, e) 1.545 } 1.546 if len(errors) > 0 { 1.547 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.548 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.549 return 1.550 } 1.551 err = c.AddEndpoints(endpoints) 1.552 @@ -941,7 +941,7 @@ 1.553 encode(w, r, http.StatusInternalServerError, actOfGodResponse) 1.554 return 1.555 } 1.556 - resp := response{ 1.557 + resp := Response{ 1.558 Errors: errors, 1.559 Endpoints: endpoints, 1.560 } 1.561 @@ -949,12 +949,12 @@ 1.562 } 1.563 1.564 func ListEndpointsHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.565 - errors := []requestError{} 1.566 + errors := []RequestError{} 1.567 vars := mux.Vars(r) 1.568 clientID, err := uuid.Parse(vars["id"]) 1.569 if err != nil { 1.570 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "client_id"}) 1.571 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.572 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "client_id"}) 1.573 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.574 return 1.575 } 1.576 num := defaultEndpointResponseSize 1.577 @@ -964,29 +964,29 @@ 1.578 if numStr != "" { 1.579 num, err = strconv.Atoi(numStr) 1.580 if err != nil { 1.581 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "num"}) 1.582 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "num"}) 1.583 } 1.584 if num > maxEndpointResponseSize { 1.585 - errors = append(errors, requestError{Slug: requestErrOverflow, Param: "num"}) 1.586 + errors = append(errors, RequestError{Slug: RequestErrOverflow, Param: "num"}) 1.587 } 1.588 } 1.589 if offsetStr != "" { 1.590 offset, err = strconv.Atoi(offsetStr) 1.591 if err != nil { 1.592 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "offset"}) 1.593 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "offset"}) 1.594 } 1.595 } 1.596 if len(errors) > 0 { 1.597 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.598 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.599 return 1.600 } 1.601 endpoints, err := c.ListEndpoints(clientID, num, offset) 1.602 if err != nil { 1.603 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.604 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.605 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.606 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.607 return 1.608 } 1.609 - resp := response{ 1.610 + resp := Response{ 1.611 Endpoints: endpoints, 1.612 Errors: errors, 1.613 } 1.614 @@ -994,72 +994,72 @@ 1.615 } 1.616 1.617 func RemoveEndpointHandler(w http.ResponseWriter, r *http.Request, c Context) { 1.618 - errors := []requestError{} 1.619 + errors := []RequestError{} 1.620 vars := mux.Vars(r) 1.621 if vars["client_id"] == "" { 1.622 - errors = append(errors, requestError{Slug: requestErrMissing, Param: "client_id"}) 1.623 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.624 + errors = append(errors, RequestError{Slug: RequestErrMissing, Param: "client_id"}) 1.625 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.626 return 1.627 } 1.628 clientID, err := uuid.Parse(vars["client_id"]) 1.629 if err != nil { 1.630 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "client_id"}) 1.631 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.632 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "client_id"}) 1.633 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.634 return 1.635 } 1.636 if vars["id"] == "" { 1.637 - errors = append(errors, requestError{Slug: requestErrMissing, Param: "id"}) 1.638 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.639 + errors = append(errors, RequestError{Slug: RequestErrMissing, Param: "id"}) 1.640 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.641 return 1.642 } 1.643 id, err := uuid.Parse(vars["id"]) 1.644 if err != nil { 1.645 - errors = append(errors, requestError{Slug: requestErrInvalidFormat, Param: "id"}) 1.646 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.647 + errors = append(errors, RequestError{Slug: RequestErrInvalidFormat, Param: "id"}) 1.648 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.649 return 1.650 } 1.651 username, password, ok := r.BasicAuth() 1.652 if !ok { 1.653 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.654 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.655 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.656 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.657 return 1.658 } 1.659 profile, err := authenticate(username, password, c) 1.660 if err != nil { 1.661 if isAuthError(err) { 1.662 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.663 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.664 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.665 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.666 } else { 1.667 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.668 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.669 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.670 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.671 } 1.672 return 1.673 } 1.674 client, err := c.GetClient(clientID) 1.675 if err != nil { 1.676 if err == ErrClientNotFound { 1.677 - errors = append(errors, requestError{Slug: requestErrNotFound, Param: "client_id"}) 1.678 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.679 + errors = append(errors, RequestError{Slug: RequestErrNotFound, Param: "client_id"}) 1.680 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.681 return 1.682 } 1.683 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.684 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.685 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.686 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.687 return 1.688 } 1.689 if !client.OwnerID.Equal(profile.ID) { 1.690 - errors = append(errors, requestError{Slug: requestErrAccessDenied}) 1.691 - encode(w, r, http.StatusUnauthorized, response{Errors: errors}) 1.692 + errors = append(errors, RequestError{Slug: RequestErrAccessDenied}) 1.693 + encode(w, r, http.StatusUnauthorized, Response{Errors: errors}) 1.694 return 1.695 } 1.696 endpoint, err := c.GetEndpoint(clientID, id) 1.697 if err != nil { 1.698 if err == ErrEndpointNotFound { 1.699 - errors = append(errors, requestError{Slug: requestErrNotFound, Param: "id"}) 1.700 - encode(w, r, http.StatusBadRequest, response{Errors: errors}) 1.701 + errors = append(errors, RequestError{Slug: RequestErrNotFound, Param: "id"}) 1.702 + encode(w, r, http.StatusBadRequest, Response{Errors: errors}) 1.703 return 1.704 } 1.705 - errors = append(errors, requestError{Slug: requestErrActOfGod}) 1.706 - encode(w, r, http.StatusInternalServerError, response{Errors: errors}) 1.707 + errors = append(errors, RequestError{Slug: RequestErrActOfGod}) 1.708 + encode(w, r, http.StatusInternalServerError, Response{Errors: errors}) 1.709 return 1.710 } 1.711 err = c.RemoveEndpoint(clientID, id) 1.712 @@ -1067,7 +1067,7 @@ 1.713 encode(w, r, http.StatusInternalServerError, actOfGodResponse) 1.714 return 1.715 } 1.716 - resp := response{ 1.717 + resp := Response{ 1.718 Errors: errors, 1.719 Endpoints: []Endpoint{endpoint}, 1.720 }