auth

Paddy 2015-12-14 Parent:4b68bac597b7

181:b7e685839a1b Go to Latest

auth/client/client.go

Break out scopes and events. This repo has gotten unwieldy, and there are portions of it that need to be imported by a large number of other packages. For example, scopes will be used in almost every API we write. Rather than importing the entirety of this codebase into every API we write, I've opted to move the scope logic out into a scopes package, with a subpackage for the defined types, which is all most projects actually want to import. We also define some event type constants, and importing those shouldn't require a project to import all our dependencies, either. So I made an events subpackage that just holds those constants. This package has become a little bit of a red-headed stepchild and is do for a refactor, but I'm trying to put that off as long as I can. The refactoring of our scopes stuff has left a bug wherein a token can be granted for scopes that don't exist. I'm going to need to revisit that, and also how to limit scopes to only be granted to the users that should be able to request them. But that's a battle for another day.

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 }