Example #1
0
func (d *InteractiveDischarger) wait(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	d.mu.Lock()
	discharge, ok := d.waiting[r.Form.Get("waitid")]
	d.mu.Unlock()
	if !ok {
		code, body := httpbakery.ErrorToResponse(errgo.Newf("invalid waitid %q", r.Form.Get("waitid")))
		httprequest.WriteJSON(w, code, body)
		return
	}
	defer func() {
		d.mu.Lock()
		delete(d.waiting, r.Form.Get("waitid"))
		d.mu.Unlock()
	}()
	var err error
	var cavs []checkers.Caveat
	select {
	case res := <-discharge.c:
		err = res.err
		cavs = res.cavs
	case <-time.After(5 * time.Minute):
		code, body := httpbakery.ErrorToResponse(errgo.New("timeout waiting for interaction to complete"))
		httprequest.WriteJSON(w, code, body)
		return
	}
	if err != nil {
		code, body := httpbakery.ErrorToResponse(err)
		httprequest.WriteJSON(w, code, body)
		return
	}
	m, err := d.Service.Discharge(
		bakery.ThirdPartyCheckerFunc(
			func(cavId, caveat string) ([]checkers.Caveat, error) {
				return cavs, nil
			},
		),
		discharge.cavId,
	)
	if err != nil {
		code, body := httpbakery.ErrorToResponse(err)
		httprequest.WriteJSON(w, code, body)
		return
	}
	httprequest.WriteJSON(
		w,
		http.StatusOK,
		httpbakery.WaitResponse{
			Macaroon: m,
		},
	)
}
Example #2
0
func (s *ErrorSuite) TestNewInteractionRequiredError(c *gc.C) {
	// With a request with no version header, the response
	// should be 407.
	req, err := http.NewRequest("GET", "/", nil)
	c.Assert(err, gc.IsNil)

	err = httpbakery.NewInteractionRequiredError("/visit", "/wait", nil, req)
	code, resp := httpbakery.ErrorToResponse(err)
	c.Assert(code, gc.Equals, http.StatusProxyAuthRequired)

	data, err := json.Marshal(resp)
	c.Assert(err, gc.IsNil)

	c.Assert(string(data), jc.JSONEquals, &httpbakery.Error{
		Code:    httpbakery.ErrInteractionRequired,
		Message: httpbakery.ErrInteractionRequired.Error(),
		Info: &httpbakery.ErrorInfo{
			VisitURL: "/visit",
			WaitURL:  "/wait",
		},
	})

	// With a request with a version 1 header, the response
	// should be 401.
	req.Header.Set("Bakery-Protocol-Version", "1")

	err = httpbakery.NewInteractionRequiredError("/visit", "/wait", nil, req)
	code, resp = httpbakery.ErrorToResponse(err)
	c.Assert(code, gc.Equals, http.StatusUnauthorized)

	h := make(http.Header)
	resp.(httprequest.HeaderSetter).SetHeader(h)
	c.Assert(h.Get("WWW-Authenticate"), gc.Equals, "Macaroon")

	data, err = json.Marshal(resp)
	c.Assert(err, gc.IsNil)

	c.Assert(string(data), jc.JSONEquals, &httpbakery.Error{
		Code:    httpbakery.ErrInteractionRequired,
		Message: httpbakery.ErrInteractionRequired.Error(),
		Info: &httpbakery.ErrorInfo{
			VisitURL: "/visit",
			WaitURL:  "/wait",
		},
	})

}
Example #3
0
// FinishInteraction signals to the InteractiveDischarger that a
// particular interaction is complete. It causes any waiting requests to
// return. If err is not nil then it will be returned by the
// corresponding /wait request.
func (d *InteractiveDischarger) FinishInteraction(w http.ResponseWriter, r *http.Request, cavs []checkers.Caveat, err error) {
	r.ParseForm()
	d.mu.Lock()
	discharge, ok := d.waiting[r.Form.Get("waitid")]
	d.mu.Unlock()
	if !ok {
		code, body := httpbakery.ErrorToResponse(errgo.Newf("invalid waitid %q", r.Form.Get("waitid")))
		httprequest.WriteJSON(w, code, body)
		return
	}
	select {
	case discharge.c <- dischargeResult{err: err, cavs: cavs}:
	default:
		panic("cannot finish interaction " + r.Form.Get("waitid"))
	}
	return
}