summaryrefslogtreecommitdiff
path: root/macaroonbakery/httpbakery/error.py
blob: e138c66780371201e8b97368a8191b7840e48a13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# Copyright 2017 Canonical Ltd.
# Licensed under the LGPLv3, see LICENCE file for details.
import json

import macaroonbakery


def discharged_required_response(macaroon, path, cookie_suffix_name):
    ''' Get response content and headers from a discharge macaroons error.

    @param macaroon may hold a macaroon that, when discharged, may
    allow access to a service.
    @param path holds the URL path to be associated with the macaroon.
    The macaroon is potentially valid for all URLs under the given path.
    @param cookie_suffix_name 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)
    @return content(bytes) and the headers to set on the response(dict).
    '''
    content = json.dumps(
        {
            'Code': 'macaroon discharge required',
            'Message': 'discharge required',
            'Info': {
                'Macaroon': macaroon.to_dict(),
                'MacaroonPath': path,
                'CookieNameSuffix': cookie_suffix_name
            },
        }
    )
    return content, {
        'WWW-Authenticate': 'Macaroon',
        'Content-Type': 'application/json'
    }

# BAKERY_PROTOCOL_HEADER 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 greater than 0, the response will have status 401 with the
# WWW-Authenticate header set to "Macaroon".
BAKERY_PROTOCOL_HEADER = 'Bakery-Protocol-Version'


def request_version(req_headers):
    ''' Determines the bakery protocol version from a client request.
    If the protocol cannot be determined, or is invalid, the original version
    of the protocol is used. If a later version is found, the latest known
    version is used, which is OK because versions are backwardly compatible.

    @param req_headers: the request headers as a dict.
    @return: bakery protocol version (for example macaroonbakery.BAKERY_V1)
    '''
    vs = req_headers.get(BAKERY_PROTOCOL_HEADER)
    if vs is None:
        # No header - use backward compatibility mode.
        return macaroonbakery.BAKERY_V1
    try:
        x = int(vs)
    except ValueError:
        # Badly formed header - use backward compatibility mode.
        return macaroonbakery.BAKERY_V1
    if x > macaroonbakery.LATEST_BAKERY_VERSION:
        # Later version than we know about - use the
        # latest version that we can.
        return macaroonbakery.LATEST_BAKERY_VERSION
    return x