Пример #1
0
func (d *Discharger) login(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	if d.LoginHandler != nil {
		d.LoginHandler(d, w, r)
		return
	}
	al, err := d.GetAgentLogin(r)
	if err != nil {
		d.WriteJSON(w, http.StatusBadRequest, httpbakery.Error{
			Message: fmt.Sprintf("cannot read agent login: %s", err),
		})
		return
	}
	_, err = httpbakery.CheckRequest(d.Bakery, r, nil, nil)
	if err == nil {
		d.FinishWait(w, r, nil)
		d.WriteJSON(w, http.StatusOK, agent.AgentResponse{
			AgentLogin: true,
		})
		return
	}
	m, err := d.Bakery.NewMacaroon("", nil, []checkers.Caveat{
		bakery.LocalThirdPartyCaveat(al.PublicKey),
	})
	if err != nil {
		d.WriteJSON(w, http.StatusInternalServerError, httpbakery.Error{
			Message: fmt.Sprintf("cannot create macaroon: %s", err),
		})
		return
	}
	httpbakery.WriteDischargeRequiredError(w, m, "", nil)
}
Пример #2
0
// userHandler handles requests to add new users, change user details, etc.
// It is only accessible to users that are members of the admin group.
func (h *handler) userHandler(p httprequest.Params) (interface{}, error) {
	ctxt := h.newContext(p.Request, "change-user")
	if _, err := httpbakery.CheckRequest(h.svc, p.Request, nil, ctxt); err != nil {
		// TODO do this only if the error cause is *bakery.VerificationError
		// We issue a macaroon with a third-party caveat targetting
		// the id service itself. This means that the flow for self-created
		// macaroons is just the same as for any other service.
		// Theoretically, we could just redirect the user to the
		// login page, but that would p.Requestuire a different flow
		// and it's not clear that it would be an advantage.
		m, err := h.svc.NewMacaroon("", nil, []checkers.Caveat{{
			Location:  h.svc.Location(),
			Condition: "member-of-group admin",
		}, {
			Condition: "operation change-user",
		}})
		if err != nil {
			return nil, errgo.Notef(err, "cannot mint new macaroon")
		}
		return nil, &httpbakery.Error{
			Message: err.Error(),
			Code:    httpbakery.ErrDischargeRequired,
			Info: &httpbakery.ErrorInfo{
				Macaroon: m,
			},
		}
	}
	// PUT /user/$user - create new user
	// PUT /user/$user/group-membership - change group membership of user
	return nil, errgo.New("not implemented yet")
}
Пример #3
0
// serverHandler returns an HTTP handler that checks macaroon authorization
// and, if that succeeds, writes the string "done" and echos anything in the
// request body.
// It recognises the single first party caveat "is something".
func serverHandler(hp serverHandlerParams) http.Handler {
	if hp.checker == nil {
		hp.checker = isChecker("something")
	}
	h := handleErrors(func(p httprequest.Params) error {
		if hp.alwaysReadBody {
			defer ioutil.ReadAll(p.Request.Body)
		}
		if _, checkErr := httpbakery.CheckRequest(hp.service, p.Request, nil, hp.checker); checkErr != nil {
			return newDischargeRequiredError(hp, checkErr, p.Request)
		}
		fmt.Fprintf(p.Response, "done")
		// Special case: the no-body path doesn't return the body.
		if p.Request.URL.Path == "/no-body" {
			return nil
		}
		data, err := ioutil.ReadAll(p.Request.Body)
		if err != nil {
			panic(fmt.Errorf("cannot read body: %v", err))
		}
		if len(data) > 0 {
			fmt.Fprintf(p.Response, " %s", data)
		}
		return nil
	})
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		h(w, req, nil)
	})
}
Пример #4
0
func (s *ClientSuite) TestDoWithBodyAndCustomError(c *gc.C) {
	d := bakerytest.NewDischarger(nil, noCaveatChecker)
	defer d.Close()

	// Create a target service.
	svc := newService("loc", d)

	type customError struct {
		CustomError *httpbakery.Error
	}
	callCount := 0
	handler := func(w http.ResponseWriter, req *http.Request) {
		callCount++
		if _, checkErr := httpbakery.CheckRequest(svc, req, nil, checkers.New()); checkErr != nil {
			httprequest.WriteJSON(w, http.StatusTeapot, customError{
				CustomError: newDischargeRequiredError(svc, d.Location(), nil, checkErr, req).(*httpbakery.Error),
			})
			return
		}
		fmt.Fprintf(w, "hello there")
	}
	srv := httptest.NewServer(http.HandlerFunc(handler))
	defer srv.Close()

	req, err := http.NewRequest("GET", srv.URL, nil)
	c.Assert(err, gc.IsNil)

	// First check that a normal request fails.
	resp, err := httpbakery.NewClient().Do(req)
	c.Assert(err, gc.IsNil)
	defer resp.Body.Close()
	c.Assert(resp.StatusCode, gc.Equals, http.StatusTeapot)
	c.Assert(callCount, gc.Equals, 1)
	callCount = 0

	// Then check that a request with a custom error getter succeeds.
	errorGetter := func(resp *http.Response) error {
		if resp.StatusCode != http.StatusTeapot {
			return nil
		}
		data, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			panic(err)
		}
		var respErr customError
		if err := json.Unmarshal(data, &respErr); err != nil {
			panic(err)
		}
		return respErr.CustomError
	}

	resp, err = httpbakery.NewClient().DoWithBodyAndCustomError(req, nil, errorGetter)
	c.Assert(err, gc.IsNil)

	data, err := ioutil.ReadAll(resp.Body)
	c.Assert(err, gc.IsNil)
	c.Assert(string(data), gc.Equals, "hello there")
	c.Assert(callCount, gc.Equals, 2)
}
Пример #5
0
func (srv *targetServiceHandler) serveSilver(w http.ResponseWriter, req *http.Request) {
	checker := srv.checkers(req, "silver")
	if _, err := httpbakery.CheckRequest(srv.svc, req, nil, checker); err != nil {
		srv.writeError(w, req, "silver", err)
		return
	}
	fmt.Fprintf(w, "every cloud has a silver lining")
}
Пример #6
0
func (srv *targetServiceHandler) serveGold(w http.ResponseWriter, req *http.Request) {
	checker := srv.checkers(req, "gold")
	if _, err := httpbakery.CheckRequest(srv.svc, req, nil, checker); err != nil {
		srv.writeError(w, req, "gold", err)
		return
	}
	fmt.Fprintf(w, "all is golden")
}
Пример #7
0
func (*suite) TestMacaraq(c *gc.C) {
	checked := false
	d := bakerytest.NewDischarger(nil, func(_ *http.Request, cond, arg string) ([]checkers.Caveat, error) {
		if cond != "something" {
			return nil, fmt.Errorf("unexpected 3rd party cond")
		}
		checked = true
		return nil, nil
	})

	bsvc, err := bakery.NewService(bakery.NewServiceParams{
		Location: "here",
		Locator:  d,
	})
	c.Assert(err, gc.IsNil)
	svc := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		req.ParseForm()
		_, checkErr := httpbakery.CheckRequest(bsvc, req, nil, checkers.New())
		if checkErr == nil {
			w.Header().Set("Content-Type", "application/json")
			data, err := json.Marshal(req.Form)
			c.Check(err, gc.IsNil)
			w.Write(data)
			return
		}
		m, err := bsvc.NewMacaroon("", nil, []checkers.Caveat{{
			Location:  d.Service.Location(),
			Condition: "something",
		}})
		c.Check(err, gc.IsNil)
		httpbakery.WriteDischargeRequiredError(w, m, "/", checkErr)
	}))

	fset := flag.NewFlagSet("http", flag.ContinueOnError)
	ctxt, params, err := newContext(fset, []string{
		svc.URL,
		"x=y",
	})
	c.Assert(err, gc.IsNil)
	client := httpbakery.NewClient()
	resp, err := ctxt.doRequest(client, nil)
	c.Assert(err, gc.IsNil)
	defer resp.Body.Close()
	c.Assert(resp.StatusCode, gc.Equals, http.StatusOK)
	c.Assert(checked, jc.IsTrue)

	var stdout bytes.Buffer
	err = showResponse(params, resp, &stdout)
	c.Assert(err, gc.IsNil)
	c.Assert(stdout.String(), gc.Equals, `{
	x: [
		"y"
	]
}
`)
}
Пример #8
0
func (s *ClientSuite) TestDischargeServerWithMacaraqOnDischarge(c *gc.C) {
	locator := bakery.NewPublicKeyRing()

	var called [3]int

	// create the services from leaf discharger to primary
	// service so that each one can know the location
	// to discharge at.
	key2, h2 := newHTTPDischarger(locator, func(svc *bakery.Service, req *http.Request, cavId, cav string) ([]checkers.Caveat, error) {
		called[2]++
		if cav != "is-ok" {
			return nil, fmt.Errorf("unrecognized caveat at srv2")
		}
		return nil, nil
	})
	srv2 := httptest.NewServer(h2)
	locator.AddPublicKeyForLocation(srv2.URL, true, key2)

	key1, h1 := newHTTPDischarger(locator, func(svc *bakery.Service, req *http.Request, cavId, cav string) ([]checkers.Caveat, error) {
		called[1]++
		if _, err := httpbakery.CheckRequest(svc, req, nil, checkers.New()); err != nil {
			return nil, newDischargeRequiredError(serverHandlerParams{
				service:      svc,
				authLocation: srv2.URL,
			}, err, req)
		}
		if cav != "is-ok" {
			return nil, fmt.Errorf("unrecognized caveat at srv1")
		}
		return nil, nil
	})
	srv1 := httptest.NewServer(h1)
	locator.AddPublicKeyForLocation(srv1.URL, true, key1)

	svc0 := newService("loc", locator)
	srv0 := httptest.NewServer(serverHandler(serverHandlerParams{
		service:      svc0,
		authLocation: srv1.URL,
	}))

	// Make a client request.
	client := httpbakery.NewClient()
	req, err := http.NewRequest("GET", srv0.URL, nil)
	c.Assert(err, gc.IsNil)
	resp, err := client.Do(req)
	c.Assert(err, gc.IsNil)
	defer resp.Body.Close()
	assertResponse(c, resp, "done")

	c.Assert(called, gc.DeepEquals, [3]int{0, 2, 1})
}
Пример #9
0
// canSpeakFor checks whether the client sending
// the given request can speak for the given user.
// We do that by declaring that user and checking
// whether the supplied macaroons in the request
// verify OK.
func (ctxt *context) canSpeakFor(user string) error {
	if user == ctxt.declaredUser && ctxt.verifiedUser {
		// The context is a direct result of logging in.
		// No need to check macaroons.
		return nil
	}
	ctxt1 := *ctxt
	ctxt1.declaredUser = user
	_, err := httpbakery.CheckRequest(ctxt.svc, ctxt.req, nil, &ctxt1)
	if err != nil {
		log.Printf("client cannot speak for %q: %v", user, err)
	} else {
		log.Printf("client can speak for %q", user)
	}
	return err
}
Пример #10
0
Файл: user.go Проект: bac/juju
// CheckLocalLoginRequest checks that the given HTTP request contains at least
// one valid local login macaroon minted by the given service using
// CreateLocalLoginMacaroon. It returns an error with a
// *bakery.VerificationError cause if the macaroon verification failed. If the
// macaroon is valid, CheckLocalLoginRequest returns a list of caveats to add
// to the discharge macaroon.
func CheckLocalLoginRequest(
	service *bakery.Service,
	req *http.Request,
	tag names.UserTag,
	clock clock.Clock,
) ([]checkers.Caveat, error) {
	_, err := httpbakery.CheckRequest(service, req, nil, checkers.CheckerFunc{
		// Having a macaroon with an is-authenticated-user
		// caveat is proof that the user is "logged in".
		"is-authenticated-user",
		func(cond, arg string) error { return nil },
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	firstPartyCaveats := []checkers.Caveat{
		checkers.DeclaredCaveat("username", tag.Id()),
		checkers.TimeBeforeCaveat(clock.Now().Add(localLoginExpiryTime)),
	}
	return firstPartyCaveats, nil
}
Пример #11
0
// serverHandler returns an HTTP handler that checks macaroon authorization
// and, if that succeeds, writes the string "done" and echos anything in the
// request body.
// It recognises the single first party caveat "is something".
func serverHandler(service *bakery.Service, authLocation string, cookiePath func() string) http.Handler {
	h := handleErrors(func(p httprequest.Params) error {
		if _, checkErr := httpbakery.CheckRequest(service, p.Request, nil, isChecker("something")); checkErr != nil {
			return newDischargeRequiredError(service, authLocation, cookiePath, checkErr, p.Request)
		}
		fmt.Fprintf(p.Response, "done")
		// Special case: the no-body path doesn't return the body.
		if p.Request.URL.Path == "/no-body" {
			return nil
		}
		data, err := ioutil.ReadAll(p.Request.Body)
		if err != nil {
			panic(fmt.Errorf("cannot read body: %v", err))
		}
		if len(data) > 0 {
			fmt.Fprintf(p.Response, " %s", data)
		}
		return nil
	})
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		h(w, req, nil)
	})
}