13
"gopkg.in/macaroon.v1"
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"
21
type discharge struct {
26
type Discharger struct {
27
Bakery *bakery.Service
29
LoginHandler func(*Discharger, http.ResponseWriter, *http.Request)
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))
44
func (d *Discharger) Serve() *httptest.Server {
45
s := httptest.NewServer(d.ServeMux())
50
func (d *Discharger) WriteJSON(w http.ResponseWriter, status int, v interface{}) error {
51
body, err := json.Marshal(v)
53
return errgo.Notef(err, "cannot marshal v")
55
w.Header().Set("Content-Type", "application/json")
57
if _, err := w.Write(body); err != nil {
58
return errgo.Notef(err, "cannot write response")
63
func (d *Discharger) GetAgentLogin(r *http.Request) (*agent.AgentLogin, error) {
64
c, err := r.Cookie("agent-login")
66
return nil, errgo.Notef(err, "cannot find cookie")
68
b, err := base64.StdEncoding.DecodeString(c.Value)
70
return nil, errgo.Notef(err, "cannot decode cookie")
72
var al agent.AgentLogin
73
if err := json.Unmarshal(b, &al); err != nil {
74
return nil, errgo.Notef(err, "cannot unmarshal cookie")
79
func (d *Discharger) FinishWait(w http.ResponseWriter, r *http.Request, err error) {
81
id, err := strconv.Atoi(r.Form.Get("waitid"))
83
d.WriteJSON(w, http.StatusBadRequest, httpbakery.Error{
84
Message: fmt.Sprintf("cannot read waitid: %s", err),
88
d.waiting[id].c <- err
92
func (d *Discharger) checker(req *http.Request, cavId, cav string) ([]checkers.Caveat, error) {
95
d.waiting = append(d.waiting, discharge{cavId, make(chan error, 1)})
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),
107
func (d *Discharger) login(w http.ResponseWriter, r *http.Request) {
109
if d.LoginHandler != nil {
110
d.LoginHandler(d, w, r)
113
al, err := d.GetAgentLogin(r)
115
d.WriteJSON(w, http.StatusBadRequest, httpbakery.Error{
116
Message: fmt.Sprintf("cannot read agent login: %s", err),
120
_, err = httpbakery.CheckRequest(d.Bakery, r, nil, nil)
122
d.FinishWait(w, r, nil)
123
d.WriteJSON(w, http.StatusOK, agent.AgentResponse{
128
m, err := d.Bakery.NewMacaroon("", nil, []checkers.Caveat{
129
bakery.LocalThirdPartyCaveat(al.PublicKey),
132
d.WriteJSON(w, http.StatusInternalServerError, httpbakery.Error{
133
Message: fmt.Sprintf("cannot create macaroon: %s", err),
137
httpbakery.WriteDischargeRequiredError(w, m, "", nil)
140
func (d *Discharger) wait(w http.ResponseWriter, r *http.Request) {
142
id, err := strconv.Atoi(r.Form.Get("waitid"))
144
d.WriteJSON(w, http.StatusBadRequest, httpbakery.Error{
145
Message: fmt.Sprintf("cannot read waitid: %s", err),
149
err = <-d.waiting[id].c
151
d.WriteJSON(w, http.StatusForbidden, err)
154
m, err := d.Bakery.Discharge(
155
bakery.ThirdPartyCheckerFunc(
156
func(cavId, caveat string) ([]checkers.Caveat, error) {
163
d.WriteJSON(w, http.StatusForbidden, err)
170
Macaroon *macaroon.Macaroon
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()),