v0.0.0-...-fed95e0 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2016 License: LGPL-3.0 Imports: 24 Imported by: 5




View Source
const (
	ErrBadRequest          = ErrorCode("bad request")
	ErrDischargeRequired   = ErrorCode("macaroon discharge required")
	ErrInteractionRequired = ErrorCode("interaction required")
View Source
const BakeryProtocolHeader = "Bakery-Protocol-Version"

BakeryProtocolHeader is the header that HTTP clients should set to determine the bakery protocol version. If it is 0 or missing, a discharge-required error response will be returned with HTTP status 407; if it is 1, the response will have status 401 with the WWW-Authenticate header set to "Macaroon".

View Source
const MacaroonsHeader = "Macaroons"

MacaroonsHeader is the key of the HTTP header that can be used to provide a macaroon for request authorization.

View Source
const (
	// UserInteractionMethod is the methodURLs key used for a URL
	// that should be visited in a user's web browser. This is also
	// the URL that can be used to fetch the available login methods
	// (with an appropriate Accept header).
	UserInteractionMethod = "interactive"


View Source
var ErrMethodNotSupported = errgo.New("interaction method not supported")

ErrMethodNotSupported is the error that a Visitor implementation should return if it does not support any of the interaction methods.


func AddDischargeHandler

func AddDischargeHandler(mux *http.ServeMux, rootPath string, svc *bakery.Service, checker func(req *http.Request, cavId, cav string) ([]checkers.Caveat, error))

AddDischargeHandler adds handlers to the given ServeMux to serve third party caveat discharges using the given service.

The handlers are added under the given rootPath, which must be non-empty.

The check function is used to check whether a client making the given request should be allowed a discharge for the given caveat. If it does not return an error, the caveat will be discharged, with any returned caveats also added to the discharge macaroon. If it returns an error with a *Error cause, the error will be marshaled and sent back to the client.

The name space served by DischargeHandler is as follows. All parameters can be provided either as URL attributes or form attributes. The result is always formatted as a JSON object.

On failure, all endpoints return an error described by the Error type.

POST /discharge

	id: id of macaroon to discharge
	location: location of original macaroon (optional (?))
	?? flow=redirect|newwindow
result on success (http.StatusOK):
		Macaroon *macaroon.Macaroon

GET /publickey

	public key of service
	expiry time of key

func CheckRequest

func CheckRequest(svc *bakery.Service, req *http.Request, assert map[string]string, checker checkers.Checker) (map[string]string, error)

CheckRequest checks that the given http request contains at least one valid macaroon minted by the given service, using checker to check any first party caveats. It returns an error with a *bakery.VerificationError cause if the macaroon verification failed.

The assert map holds any required attributes of "declared" attributes, overriding any inferences made from the macaroons themselves. It has a similar effect to adding a checkers.DeclaredCaveat for each key and value, but the error message will be more useful.

It adds all the standard caveat checkers to the given checker.

It returns any attributes declared in the successfully validated request.

func CheckRequestM

func CheckRequestM(svc *bakery.Service, req *http.Request, assert map[string]string, checker checkers.Checker) (map[string]string, macaroon.Slice, error)

CheckRequestM is like CheckRequest except that on success it also returns the set of macaroons that was successfully checked. The "M" suffix is for backward compatibility reasons - in a later bakery version, the signature of CheckRequest will be changed to return the macaroon slice and CheckRequestM will be removed.

func Checkers

func Checkers(req *http.Request) checkers.Checker

Checkers implements the standard HTTP-request checkers. It does not include the "declared" checker, as that must be added for each individual set of macaroons that are checked.

func DefaultGetError

func DefaultGetError(httpResp *http.Response) error

DefaultGetError is the default error unmarshaler used by Client.DoWithBody.

func ErrorToResponse

func ErrorToResponse(err error) (int, interface{})

ErrorToResponse returns the HTTP status and an error body to be marshaled as JSON for the given error. This allows a third party package to integrate bakery errors into their error responses when they encounter an error with a *bakery.Error cause.

func GetInteractionMethods

func GetInteractionMethods(client httprequest.Doer, u *url.URL) (map[string]*url.URL, error)

GetInteractionMethods queries a URL as found in an ErrInteractionRequired VisitURL field to find available interaction methods.

It does this by sending a GET request to the URL with the Accept header set to "application/json" and parsing the resulting response as a map[string]string.

It uses the given Doer to execute the HTTP GET request.

func IsDischargeError

func IsDischargeError(err error) bool

IsDischargeError reports whether err is a *DischargeError.

func IsInteractionError

func IsInteractionError(err error) bool

IsInteractionError reports whether err is an *InteractionError.

func MacaroonsForURL

func MacaroonsForURL(jar http.CookieJar, u *url.URL) []macaroon.Slice

MacaroonsForURL returns any macaroons associated with the given URL in the given cookie jar.

func NewCookie

func NewCookie(ms macaroon.Slice) (*http.Cookie, error)

NewCookie takes a slice of macaroons and returns them encoded as a cookie. The slice should contain a single primary macaroon in its first element, and any discharges after that.

func NewDischargeRequiredError

func NewDischargeRequiredError(m *macaroon.Macaroon, path string, originalErr error) error

NewDischargeRequiredError returns an error of type *Error that reports the given original error and includes the given macaroon.

The returned macaroon will be declared as valid for the given URL path and may be relative. When the client stores the discharged macaroon as a cookie this will be the path associated with the cookie. See ErrorInfo.MacaroonPath for more information.

func NewDischargeRequiredErrorForRequest

func NewDischargeRequiredErrorForRequest(m *macaroon.Macaroon, path string, originalErr error, req *http.Request) error

NewDischargeRequiredErrorForRequest is like NewDischargeRequiredError except that it determines the client's bakery protocol version from the request and returns an error response appropriate for that.

This function should always be used in preference to NewDischargeRequiredError, because it enables in-browser macaroon discharge.

To request a particular cookie name:

err := NewDischargeRequiredErrorForRequest(...)
err.(*httpbakery.Error).Info.CookieNameSuffix = cookieName

func NewHTTPClient

func NewHTTPClient() *http.Client

NewHTTPClient returns an http.Client that ensures that headers are sent to the server even when the server redirects a GET request. The returned client also contains an empty in-memory cookie jar.


func NewInteractionRequiredError

func NewInteractionRequiredError(visitURL, waitURL string, originalErr error, req *http.Request) error

NewInteractionRequiredError returns an error of type *Error that requests an interaction from the client in response to the given request. The originalErr value describes the original error - if it is nil, a default message will be provided.

See Error.ErrorInfo for more details of visitURL and waitURL.

This function should be used in preference to creating the Error value directly, as it sets the bakery protocol version correctly in the error.

func OpenWebBrowser

func OpenWebBrowser(url *url.URL) error

OpenWebBrowser opens a web browser at the given URL. If the OS is not recognised, the URL is just printed to standard output.

func PublicKeyForLocation

func PublicKeyForLocation(client *http.Client, url string) (*bakery.PublicKey, error)

PublicKeyForLocation returns the public key from a macaroon discharge server running at the given location URL. Note that this is insecure if an http: URL scheme is used. If client is nil, http.DefaultClient will be used.

func RequestMacaroons

func RequestMacaroons(req *http.Request) []macaroon.Slice

RequestMacaroons returns any collections of macaroons from the header and cookies found in the request. By convention, each slice will contain a primary macaroon followed by its discharges.

func SameClientIPAddrCaveat

func SameClientIPAddrCaveat(req *http.Request) checkers.Caveat

SameClientIPAddrCaveat returns a caveat that will check that the remote IP address is the same as that in the given HTTP request.

func SetCookie

func SetCookie(jar http.CookieJar, url *url.URL, ms macaroon.Slice) error

SetCookie sets a cookie for the given URL on the given cookie jar that will holds the given macaroon slice. The macaroon slice should contain a single primary macaroon in its first element, and any discharges after that.

func WriteDischargeRequiredError

func WriteDischargeRequiredError(w http.ResponseWriter, m *macaroon.Macaroon, path string, originalErr error)

WriteDischargeRequiredError creates an error using NewDischargeRequiredError and writes it to the given response writer, indicating that the client should discharge the macaroon to allow the original request to be accepted.

func WriteDischargeRequiredErrorForRequest

func WriteDischargeRequiredErrorForRequest(w http.ResponseWriter, m *macaroon.Macaroon, path string, originalErr error, req *http.Request)

WriteDischargeRequiredErrorForRequest is like NewDischargeRequiredError but uses the given request to determine the protocol version appropriate for the client.

This function should always be used in preference to WriteDischargeRequiredError, because it enables in-browser macaroon discharge.


type Client

type Client struct {
	// Client holds the HTTP client to use. It should have a cookie
	// jar configured, and when redirecting it should preserve the
	// headers (see NewHTTPClient).

	// WebPageVisitor holds a Visitor that is called when the
	// discharge process requires further interaction. If this
	// is nil, VisitWebPage will be called; if that is also nil, no
	// interaction will be allowed.
	// The VisitWebPage method will always be called with a map
	// containing a single entry with the key UserInteractionMethod,
	// holding the URL found in the InteractionRequired error's
	// VisitURL field.
	WebPageVisitor Visitor

	// VisitWebPage is called when WebPageVisitor is nil and
	// the discharge process requires further interaction.
	// Note that this field is now deprecated in favour of
	// WebPageVisitor, which will take priority if set.
	VisitWebPage func(*url.URL) error

	// Key holds the client's key. If set, the client will try to
	// discharge third party caveats with the special location
	// "local" by using this key. See bakery.DischargeAllWithKey and
	// bakery.LocalThirdPartyCaveat for more information
	Key *bakery.KeyPair

	// DischargeAcquirer holds the object that will be used to obtain
	// third-party discharges. If nil, the Client itself will be used.
	DischargeAcquirer DischargeAcquirer

Client holds the context for making HTTP requests that automatically acquire and discharge macaroons.

func NewClient

func NewClient() *Client

NewClient returns a new Client containing an HTTP client created with NewHTTPClient and leaves all other fields zero.

func (*Client) AcquireDischarge

func (c *Client) AcquireDischarge(originalLocation string, cav macaroon.Caveat) (*macaroon.Macaroon, error)

AcquireDischarge implements DischargeAcquirer by requesting a discharge macaroon from the caveat location as an HTTP URL.

func (*Client) DischargeAll

func (c *Client) DischargeAll(m *macaroon.Macaroon) (macaroon.Slice, error)

DischargeAll attempts to acquire discharge macaroons for all the third party caveats in m, and returns a slice containing all of them bound to m.

If the discharge fails because a third party refuses to discharge a caveat, the returned error will have a cause of type *DischargeError. If the discharge fails because visitWebPage returns an error, the returned error will have a cause of *InteractionError.

The returned macaroon slice will not be stored in the client cookie jar (see SetCookie if you need to do that).

func (*Client) Do

func (c *Client) Do(req *http.Request) (*http.Response, error)

Do sends the given HTTP request and returns its response. If the request fails with a discharge-required error, any required discharge macaroons will be acquired, and the request will be repeated with those attached. Do may add headers to req.Header.

If the required discharges were refused by a third party, an error with a *DischargeError cause will be returned.

Note that because the request may be retried, no body may be provided in the request, otherwise the contents will be lost when retrying. For requests with a body (for example PUT or POST methods), use DoWithBody instead.

If interaction is required by the user, the visitWebPage function is called with a URL to be opened in a web browser. If visitWebPage returns an error, an error with a *InteractionError cause will be returned. See OpenWebBrowser for a possible implementation of visitWebPage.

func (*Client) DoWithBody

func (c *Client) DoWithBody(req *http.Request, body io.ReadSeeker) (*http.Response, error)

DoWithBody is like Do except that the given body is used for the body of the HTTP request, and reset to its start by seeking if the request is retried. It is an error if req.Body is non-zero.

Note that, unlike the request body passed to http.NewRequest, the body will not be closed even if implements io.Closer.

Do may add headers to req.Header.

func (*Client) DoWithBodyAndCustomError

func (c *Client) DoWithBodyAndCustomError(req *http.Request, body io.ReadSeeker, getError func(resp *http.Response) error) (*http.Response, error)

DoWithBodyAndCustomError is like DoWithBody except it allows a client to specify a custom error function, getError, which is called on the HTTP response and may return a non-nil error if the response holds an error. If the cause of the returned error is a *Error value and its code is ErrDischargeRequired, the macaroon in its Info field will be discharged and the request will be repeated with the discharged macaroon. If getError returns nil, it should leave the response body unchanged.

If getError is nil, DefaultGetError will be used.

This method can be useful when dealing with APIs that return their errors in a format incompatible with Error, but the need for it should be avoided when creating new APIs, as it makes the endpoints less amenable to generic tools.

func (*Client) HandleError

func (c *Client) HandleError(reqURL *url.URL, err error) error

HandleError tries to resolve the given error, which should be a response to the given URL, by discharging any macaroon contained in it. That is, if the error cause is an *Error and its code is ErrDischargeRequired, then it will try to discharge err.Info.Macaroon. If the discharge succeeds, the discharged macaroon will be saved to the client's cookie jar and ResolveError will return nil.

For any other kind of error, the original error will be returned.

type DischargeAcquirer

type DischargeAcquirer interface {
	// AcquireDischarge should return a discharge macaroon for the given third
	// party caveat. The firstPartyLocation holds the location of the original
	// macaroon.
	AcquireDischarge(firstPartyLocation string, cav macaroon.Caveat) (*macaroon.Macaroon, error)

DischargeAcquirer can be implemented by clients that want to customize the discharge-acquisition process used by a Client.

type DischargeError

type DischargeError struct {
	// Reason holds the underlying remote error that caused the
	// discharge to fail.
	Reason *Error

DischargeError represents the error when a third party discharge is refused by a server.

func (*DischargeError) Error

func (e *DischargeError) Error() string

type Error

type Error struct {
	Code    ErrorCode  `json:",omitempty"`
	Message string     `json:",omitempty"`
	Info    *ErrorInfo `json:",omitempty"`
	// contains filtered or unexported fields

Error holds the type of a response from an httpbakery HTTP request, marshaled as JSON.

Note: Do not construct Error values with ErrDischargeRequired or ErrInteractionRequired codes directly - use the NewDischargeRequiredErrorForRequest or NewInteractionRequiredError functions instead.

func (*Error) Error

func (e *Error) Error() string

func (*Error) ErrorCode

func (e *Error) ErrorCode() ErrorCode

func (*Error) ErrorInfo

func (e *Error) ErrorInfo() *ErrorInfo

ErrorInfo returns additional information about the error. TODO return interface{} here?

type ErrorCode

type ErrorCode string

ErrorCode holds an error code that classifies an error returned from a bakery HTTP handler.

func (ErrorCode) Error

func (e ErrorCode) Error() string

func (ErrorCode) ErrorCode

func (e ErrorCode) ErrorCode() ErrorCode

type ErrorInfo

type ErrorInfo struct {
	// Macaroon may hold a macaroon that, when
	// discharged, may allow access to a service.
	// This field is associated with the ErrDischargeRequired
	// error code.
	Macaroon *macaroon.Macaroon `json:",omitempty"`

	// MacaroonPath holds the URL path to be associated
	// with the macaroon. The macaroon is potentially
	// valid for all URLs under the given path.
	// If it is empty, the macaroon will be associated with
	// the original URL from which the error was returned.
	MacaroonPath string `json:",omitempty"`

	// CookieNameSuffix holds the desired cookie name suffix to be
	// associated with the macaroon. The actual name used will be
	// ("macaroon-" + CookieName). Clients may ignore this field -
	// older clients will always use ("macaroon-" +
	// macaroon.Signature() in hex).
	CookieNameSuffix string `json:",omitempty"`

	// VisitURL holds a URL that the client should visit
	// in a web browser to authenticate themselves.
	VisitURL string `json:",omitempty"`

	// WaitURL holds a URL that the client should visit
	// to acquire the discharge macaroon. A GET on
	// this URL will block until the client has authenticated,
	// and then it will return the discharge macaroon.
	WaitURL string `json:",omitempty"`

ErrorInfo holds additional information provided by an error.

type InteractionError

type InteractionError struct {
	// Reason holds the actual error returned from visitWebPage.
	Reason error

InteractionError wraps an error returned by a call to visitWebPage.

func (*InteractionError) Error

func (e *InteractionError) Error() string

type PublicKeyRing

type PublicKeyRing struct {
	// contains filtered or unexported fields

PublicKeyRing represents a public keyring that can interrogate remote services for their public keys. By default it refuses to use insecure URLs.

func NewPublicKeyRing

func NewPublicKeyRing(client *http.Client, cache *bakery.PublicKeyRing) *PublicKeyRing

NewPublicKeyRing returns a new public keyring that uses the given client to find public keys and uses the given cache as a backing. If cache is nil, a new cache will be created. If client is nil, http.DefaultClient will be used.

func (*PublicKeyRing) AllowInsecure

func (kr *PublicKeyRing) AllowInsecure()

AllowInsecure allows insecure URLs. This can be useful for testing purposes.

func (*PublicKeyRing) PublicKeyForLocation

func (kr *PublicKeyRing) PublicKeyForLocation(loc string) (*bakery.PublicKey, error)

PublicKeyForLocation implements bakery.PublicKeyLocator by first looking in the backing cache and, if that fails, making an HTTP request to the public key associated with the given discharge location.

type Visitor

type Visitor interface {
	VisitWebPage(client *Client, methodURLs map[string]*url.URL) error

Visitor represents a handler that can handle ErrInteractionRequired errors from a client's discharge request. The methodURLs parameter to VisitWebPage holds a set of possible ways to complete the discharge request. When called directly from Client, this will contain only a single entry with the UserInteractionMethod key, specifying that the associated URL should be opened in a web browser for the user to interact with.

See FallbackVisitor for a way to gain access to alternative methods.

A Visitor implementation should return ErrMethodNotSupported if it cannot handle any of the supplied methods.

var WebBrowserVisitor Visitor = webBrowserVisitor{}

WebBrowserVisitor holds an interactor that supports the "Interactive" method by opening a web browser at the required location.

func NewMultiVisitor

func NewMultiVisitor(methods ...Visitor) Visitor

NewMultiVisitor returns a Visitor that queries the discharger for available methods and then tries each of the given visitors in turn until one succeeds or fails with an error cause other than ErrMethodNotSupported.

type WaitResponse

type WaitResponse struct {
	Macaroon *macaroon.Macaroon

WaitResponse holds the type that should be returned by an HTTP response made to a WaitURL (See the ErrorInfo type).


Path Synopsis
Package agent enables non-interactive (agent) login using macaroons.
Package agent enables non-interactive (agent) login using macaroons.
Package form enables interactive login without using a web browser.
Package form enables interactive login without using a web browser.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL