auth

Paddy 2015-07-18 Parent:b0d1b3e39fc8

180:4b68bac597b7 Go to Latest

auth/client/client.go

Update client to detect errors. The client doesn't treat non-200 responses as errors automatically, so we need to detect when the response.Errors property is set, and use that to return an error. To avoid the boilerplate and an extensive error system, I just wrapped them in an httpErrors type that implements the error interface. That way the errors can be returned, and callers can type-cast and interrogate them. I also updated the GetLogin function to return an auth.ErrLoginNotFound error when the httpErrors response indicates that's the reason the request failed.

History
paddy@172 1 package client
paddy@172 2
paddy@172 3 import (
paddy@172 4 "bytes"
paddy@172 5 "encoding/json"
paddy@172 6 "errors"
paddy@180 7 "fmt"
paddy@172 8 "io"
paddy@172 9 "net/http"
paddy@172 10 "strings"
paddy@173 11 "time"
paddy@172 12
paddy@172 13 "code.secondbit.org/auth.hg"
paddy@173 14 "code.secondbit.org/uuid.hg"
paddy@172 15 )
paddy@172 16
paddy@172 17 var (
paddy@172 18 ErrNilClient = errors.New("nil client wrapper")
paddy@172 19 ErrNilHTTPClient = errors.New("nil client")
paddy@172 20 )
paddy@172 21
paddy@172 22 type Client struct {
paddy@172 23 client *http.Client
paddy@172 24 address string
paddy@173 25 ID uuid.ID
paddy@172 26 }
paddy@172 27
paddy@173 28 func New(address string, id uuid.ID) *Client {
paddy@172 29 address = strings.TrimRight(address, "/")
paddy@172 30 return &Client{
paddy@172 31 address: address,
paddy@172 32 client: &http.Client{},
paddy@173 33 ID: id,
paddy@172 34 }
paddy@172 35 }
paddy@172 36
paddy@173 37 func (c *Client) do(method, url string, request interface{}, scopes []string, subject uuid.ID) (auth.Response, error) {
paddy@172 38 if c == nil {
paddy@172 39 return auth.Response{}, ErrNilClient
paddy@172 40 }
paddy@172 41 if c.client == nil {
paddy@172 42 return auth.Response{}, ErrNilHTTPClient
paddy@172 43 }
paddy@172 44 var response auth.Response
paddy@172 45 if !strings.HasPrefix(url, "http") {
paddy@172 46 url = strings.TrimLeft(url, "/")
paddy@172 47 url = "/" + url
paddy@172 48 url = c.address + url
paddy@172 49 }
paddy@172 50 var body io.Reader
paddy@172 51 if request != nil {
paddy@172 52 data, err := json.Marshal(request)
paddy@172 53 if err != nil {
paddy@172 54 return response, err
paddy@172 55 }
paddy@172 56 body = bytes.NewBuffer(data)
paddy@172 57 }
paddy@172 58 req, err := http.NewRequest(method, url, body)
paddy@172 59 if err != nil {
paddy@172 60 return response, err
paddy@172 61 }
paddy@173 62 req.Header.Set("Ducky-Scope", strings.Join(scopes, " "))
paddy@173 63 req.Header.Set("Ducky-Issuer", c.ID.String())
paddy@173 64 if subject != nil {
paddy@173 65 req.Header.Set("Ducky-Subject", subject.String())
paddy@173 66 }
paddy@173 67 req.Header.Set("Ducky-Expires", time.Now().Add(time.Hour).String())
paddy@173 68 req.Header.Set("Ducky-Issued-At", time.Now().String())
paddy@173 69 req.Header.Set("Ducky-Not-Before", time.Now().Add(-5*time.Minute).String())
paddy@172 70 resp, err := c.client.Do(req)
paddy@172 71 if err != nil {
paddy@172 72 return response, err
paddy@172 73 }
paddy@172 74 defer resp.Body.Close()
paddy@172 75 switch resp.Header.Get("Content-Type") {
paddy@172 76 case "application/json":
paddy@172 77 dec := json.NewDecoder(resp.Body)
paddy@172 78 err = dec.Decode(&response)
paddy@172 79 if err != nil {
paddy@172 80 return response, err
paddy@172 81 }
paddy@172 82 default:
paddy@172 83 dec := json.NewDecoder(resp.Body)
paddy@172 84 err = dec.Decode(&response)
paddy@172 85 if err != nil {
paddy@172 86 return response, err
paddy@172 87 }
paddy@172 88 }
paddy@180 89 if len(response.Errors) > 0 {
paddy@180 90 return response, httpErrors(response.Errors)
paddy@180 91 }
paddy@172 92 return response, nil
paddy@172 93 }
paddy@172 94
paddy@180 95 type httpErrors []auth.RequestError
paddy@180 96
paddy@180 97 func (h httpErrors) Error() string {
paddy@180 98 return fmt.Sprintf("%+#v", h)
paddy@180 99 }
paddy@180 100
paddy@173 101 func (c *Client) Get(url string, scopes []string, subject uuid.ID) (auth.Response, error) {
paddy@173 102 return c.do("GET", url, nil, scopes, subject)
paddy@172 103 }