~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/gopkg.in/macaroon-bakery.v1/httpbakery/agent/discharge_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package agent_test
 
2
 
 
3
import (
 
4
        "encoding/base64"
 
5
        "encoding/json"
 
6
        "fmt"
 
7
        "net/http"
 
8
        "net/http/httptest"
 
9
        "strconv"
 
10
        "sync"
 
11
 
 
12
        "gopkg.in/errgo.v1"
 
13
        "gopkg.in/macaroon.v1"
 
14
 
 
15
        "gopkg.in/macaroon-bakery.v1/bakery"
 
16
        "gopkg.in/macaroon-bakery.v1/bakery/checkers"
 
17
        "gopkg.in/macaroon-bakery.v1/httpbakery"
 
18
        "gopkg.in/macaroon-bakery.v1/httpbakery/agent"
 
19
)
 
20
 
 
21
type discharge struct {
 
22
        cavId string
 
23
        c     chan error
 
24
}
 
25
 
 
26
type Discharger struct {
 
27
        Bakery       *bakery.Service
 
28
        URL          string
 
29
        LoginHandler func(*Discharger, http.ResponseWriter, *http.Request)
 
30
 
 
31
        mu      sync.Mutex
 
32
        waiting []discharge
 
33
}
 
34
 
 
35
func (d *Discharger) ServeMux() *http.ServeMux {
 
36
        mux := http.NewServeMux()
 
37
        httpbakery.AddDischargeHandler(mux, "/", d.Bakery, d.checker)
 
38
        mux.Handle("/login", http.HandlerFunc(d.login))
 
39
        mux.Handle("/wait", http.HandlerFunc(d.wait))
 
40
        mux.Handle("/", http.HandlerFunc(d.notfound))
 
41
        return mux
 
42
}
 
43
 
 
44
func (d *Discharger) Serve() *httptest.Server {
 
45
        s := httptest.NewServer(d.ServeMux())
 
46
        d.URL = s.URL
 
47
        return s
 
48
}
 
49
 
 
50
func (d *Discharger) WriteJSON(w http.ResponseWriter, status int, v interface{}) error {
 
51
        body, err := json.Marshal(v)
 
52
        if err != nil {
 
53
                return errgo.Notef(err, "cannot marshal v")
 
54
        }
 
55
        w.Header().Set("Content-Type", "application/json")
 
56
        w.WriteHeader(status)
 
57
        if _, err := w.Write(body); err != nil {
 
58
                return errgo.Notef(err, "cannot write response")
 
59
        }
 
60
        return nil
 
61
}
 
62
 
 
63
func (d *Discharger) GetAgentLogin(r *http.Request) (*agent.AgentLogin, error) {
 
64
        c, err := r.Cookie("agent-login")
 
65
        if err != nil {
 
66
                return nil, errgo.Notef(err, "cannot find cookie")
 
67
        }
 
68
        b, err := base64.StdEncoding.DecodeString(c.Value)
 
69
        if err != nil {
 
70
                return nil, errgo.Notef(err, "cannot decode cookie")
 
71
        }
 
72
        var al agent.AgentLogin
 
73
        if err := json.Unmarshal(b, &al); err != nil {
 
74
                return nil, errgo.Notef(err, "cannot unmarshal cookie")
 
75
        }
 
76
        return &al, nil
 
77
}
 
78
 
 
79
func (d *Discharger) FinishWait(w http.ResponseWriter, r *http.Request, err error) {
 
80
        r.ParseForm()
 
81
        id, err := strconv.Atoi(r.Form.Get("waitid"))
 
82
        if err != nil {
 
83
                d.WriteJSON(w, http.StatusBadRequest, httpbakery.Error{
 
84
                        Message: fmt.Sprintf("cannot read waitid: %s", err),
 
85
                })
 
86
                return
 
87
        }
 
88
        d.waiting[id].c <- err
 
89
        return
 
90
}
 
91
 
 
92
func (d *Discharger) checker(req *http.Request, cavId, cav string) ([]checkers.Caveat, error) {
 
93
        d.mu.Lock()
 
94
        id := len(d.waiting)
 
95
        d.waiting = append(d.waiting, discharge{cavId, make(chan error, 1)})
 
96
        d.mu.Unlock()
 
97
        return nil, &httpbakery.Error{
 
98
                Code:    httpbakery.ErrInteractionRequired,
 
99
                Message: "test interaction",
 
100
                Info: &httpbakery.ErrorInfo{
 
101
                        VisitURL: fmt.Sprintf("%s/login?waitid=%d", d.URL, id),
 
102
                        WaitURL:  fmt.Sprintf("%s/wait?waitid=%d", d.URL, id),
 
103
                },
 
104
        }
 
105
}
 
106
 
 
107
func (d *Discharger) login(w http.ResponseWriter, r *http.Request) {
 
108
        r.ParseForm()
 
109
        if d.LoginHandler != nil {
 
110
                d.LoginHandler(d, w, r)
 
111
                return
 
112
        }
 
113
        al, err := d.GetAgentLogin(r)
 
114
        if err != nil {
 
115
                d.WriteJSON(w, http.StatusBadRequest, httpbakery.Error{
 
116
                        Message: fmt.Sprintf("cannot read agent login: %s", err),
 
117
                })
 
118
                return
 
119
        }
 
120
        _, err = httpbakery.CheckRequest(d.Bakery, r, nil, nil)
 
121
        if err == nil {
 
122
                d.FinishWait(w, r, nil)
 
123
                d.WriteJSON(w, http.StatusOK, agent.AgentResponse{
 
124
                        AgentLogin: true,
 
125
                })
 
126
                return
 
127
        }
 
128
        m, err := d.Bakery.NewMacaroon("", nil, []checkers.Caveat{
 
129
                bakery.LocalThirdPartyCaveat(al.PublicKey),
 
130
        })
 
131
        if err != nil {
 
132
                d.WriteJSON(w, http.StatusInternalServerError, httpbakery.Error{
 
133
                        Message: fmt.Sprintf("cannot create macaroon: %s", err),
 
134
                })
 
135
                return
 
136
        }
 
137
        httpbakery.WriteDischargeRequiredError(w, m, "", nil)
 
138
}
 
139
 
 
140
func (d *Discharger) wait(w http.ResponseWriter, r *http.Request) {
 
141
        r.ParseForm()
 
142
        id, err := strconv.Atoi(r.Form.Get("waitid"))
 
143
        if err != nil {
 
144
                d.WriteJSON(w, http.StatusBadRequest, httpbakery.Error{
 
145
                        Message: fmt.Sprintf("cannot read waitid: %s", err),
 
146
                })
 
147
                return
 
148
        }
 
149
        err = <-d.waiting[id].c
 
150
        if err != nil {
 
151
                d.WriteJSON(w, http.StatusForbidden, err)
 
152
                return
 
153
        }
 
154
        m, err := d.Bakery.Discharge(
 
155
                bakery.ThirdPartyCheckerFunc(
 
156
                        func(cavId, caveat string) ([]checkers.Caveat, error) {
 
157
                                return nil, nil
 
158
                        },
 
159
                ),
 
160
                d.waiting[id].cavId,
 
161
        )
 
162
        if err != nil {
 
163
                d.WriteJSON(w, http.StatusForbidden, err)
 
164
                return
 
165
        }
 
166
        d.WriteJSON(
 
167
                w,
 
168
                http.StatusOK,
 
169
                struct {
 
170
                        Macaroon *macaroon.Macaroon
 
171
                }{
 
172
                        Macaroon: m,
 
173
                },
 
174
        )
 
175
}
 
176
 
 
177
func (d *Discharger) notfound(w http.ResponseWriter, r *http.Request) {
 
178
        d.WriteJSON(w, http.StatusNotFound, httpbakery.Error{
 
179
                Message: fmt.Sprintf("cannot find %s", r.URL.String()),
 
180
        })
 
181
}