예제 #1
0
def discharge(ctx, content, key, locator, checker):
    '''Handles a discharge request as received by the /discharge
    endpoint.
    @param ctx The context passed to the checker {checkers.AuthContext}
    @param content URL and form parameters {dict}
    @param locator Locator used to add third party caveats returned by
    the checker {macaroonbakery.ThirdPartyLocator}
    @param checker {macaroonbakery.ThirdPartyCaveatChecker} Used to check third
    party caveats.
    @return The discharge macaroon {macaroonbakery.Macaroon}
    '''
    id = content.get('id')
    if id is None:
        id = content.get('id64')
        if id is not None:
            id = utils.b64decode(id)
    caveat = content.get('caveat64')
    if caveat is not None:
        caveat = utils.b64decode(caveat)

    return bakery.discharge(
        ctx,
        id=id,
        caveat=caveat,
        key=key,
        checker=checker,
        locator=locator,
    )
예제 #2
0
def discharge(ctx, content, key, locator, checker):
    '''Handles a discharge request as received by the /discharge
    endpoint.
    @param ctx The context passed to the checker {checkers.AuthContext}
    @param content URL and form parameters {dict}
    @param locator Locator used to add third party caveats returned by
    the checker {macaroonbakery.ThirdPartyLocator}
    @param checker Used to check third party caveats {macaroonbakery.ThirdPartyCaveatChecker}
    @return The discharge macaroon {macaroonbakery.Macaroon}
    '''
    id = content.get('id')
    if id is None:
        id = content.get('id64')
        if id is not None:
            id = utils.b64decode(id)
    caveat = content.get('caveat64')
    if caveat is not None:
        caveat = utils.b64decode(caveat)

    return bakery.discharge(
        ctx,
        id=id,
        caveat=caveat,
        key=key,
        checker=checker,
        locator=locator,
    )
예제 #3
0
 def interact(self, client, location, interaction_required_err):
     '''Implement Interactor.interact by obtaining obtaining
     a macaroon from the discharger, discharging it with the
     local private key using the discharged macaroon as
     a discharge token'''
     p = interaction_required_err.interaction_method('agent',
                                                     InteractionInfo)
     if p.login_url is None or p.login_url == '':
         raise httpbakery.InteractionError(
             'no login-url field found in agent interaction method')
     agent = self._find_agent(location)
     if not location.endswith('/'):
         location += '/'
     login_url = urljoin(location, p.login_url)
     resp = requests.get(login_url, json={
         'Username': agent.username,
         'PublicKey': self._auth_info.key.encode().decode('utf-8'),
     })
     if resp.status_code != 200:
         raise httpbakery.InteractionError(
             'cannot acquire agent macaroon: {}'.format(resp.status_code)
         )
     m = resp.json().get('macaroon')
     if m is None:
         raise httpbakery.InteractionError('no macaroon in response')
     m = bakery.Macaroon.from_dict(m)
     ms = bakery.discharge_all(m, None, self._auth_info.key)
     b = bytearray()
     for m in ms:
         b.extend(utils.b64decode(m.serialize()))
     return httpbakery.DischargeToken(kind='agent', value=bytes(b))
예제 #4
0
 def interact(self, client, location, interaction_required_err):
     '''Implement Interactor.interact by obtaining obtaining
     a macaroon from the discharger, discharging it with the
     local private key using the discharged macaroon as
     a discharge token'''
     p = interaction_required_err.interaction_method(
         'agent', InteractionInfo)
     if p.login_url is None or p.login_url == '':
         raise httpbakery.InteractionError(
             'no login-url field found in agent interaction method')
     agent = self._find_agent(location)
     if not location.endswith('/'):
         location += '/'
     login_url = urljoin(location, p.login_url)
     resp = requests.get(login_url,
                         json={
                             'Username':
                             agent.username,
                             'PublicKey':
                             self._auth_info.key.encode().decode('utf-8'),
                         })
     if resp.status_code != 200:
         raise httpbakery.InteractionError(
             'cannot acquire agent macaroon: {}'.format(resp.status_code))
     m = resp.json().get('macaroon')
     if m is None:
         raise httpbakery.InteractionError('no macaroon in response')
     m = bakery.Macaroon.from_dict(m)
     ms = bakery.discharge_all(m, None, self._auth_info.key)
     b = bytearray()
     for m in ms:
         b.extend(utils.b64decode(m.serialize()))
     return httpbakery.DischargeToken(kind='agent', value=bytes(b))
예제 #5
0
    def from_dict(cls, json_dict):
        '''Return a macaroon obtained from the given dictionary as
        deserialized from JSON.
        @param json_dict The deserialized JSON object.
        '''
        json_macaroon = json_dict.get('m')
        if json_macaroon is None:
            # Try the v1 format if we don't have a macaroon field.
            m = pymacaroons.Macaroon.deserialize(
                json.dumps(json_dict), json_serializer.JsonSerializer())
            macaroon = Macaroon(root_key=None,
                                id=None,
                                namespace=bakery.legacy_namespace(),
                                version=_bakery_version(m.version))
            macaroon._macaroon = m
            return macaroon

        version = json_dict.get('v', None)
        if version is None:
            raise ValueError('no version specified')
        if (version < bakery.VERSION_3 or version > bakery.LATEST_VERSION):
            raise ValueError('unknown bakery version {}'.format(version))
        m = pymacaroons.Macaroon.deserialize(json.dumps(json_macaroon),
                                             json_serializer.JsonSerializer())
        if m.version != macaroon_version(version):
            raise ValueError('underlying macaroon has inconsistent version; '
                             'got {} want {}'.format(
                                 m.version, macaroon_version(version)))
        namespace = checkers.deserialize_namespace(json_dict.get('ns'))
        cdata = json_dict.get('cdata', {})
        caveat_data = {}
        for id64 in cdata:
            id = utils.b64decode(id64)
            data = utils.b64decode(cdata[id64])
            caveat_data[id] = data
        macaroon = Macaroon(root_key=None,
                            id=None,
                            namespace=namespace,
                            version=version)
        macaroon._caveat_data = caveat_data
        macaroon._macaroon = m
        return macaroon
예제 #6
0
    def from_dict(cls, json_dict):
        '''Return a macaroon obtained from the given dictionary as
        deserialized from JSON.
        @param json_dict The deserialized JSON object.
        '''
        json_macaroon = json_dict.get('m')
        if json_macaroon is None:
            # Try the v1 format if we don't have a macaroon field.
            m = pymacaroons.Macaroon.deserialize(
                json.dumps(json_dict), json_serializer.JsonSerializer())
            macaroon = Macaroon(root_key=None, id=None,
                                namespace=bakery.legacy_namespace(),
                                version=_bakery_version(m.version))
            macaroon._macaroon = m
            return macaroon

        version = json_dict.get('v', None)
        if version is None:
            raise ValueError('no version specified')
        if (version < bakery.VERSION_3 or
                version > bakery.LATEST_VERSION):
            raise ValueError('unknown bakery version {}'.format(version))
        m = pymacaroons.Macaroon.deserialize(json.dumps(json_macaroon),
                                             json_serializer.JsonSerializer())
        if m.version != macaroon_version(version):
            raise ValueError(
                'underlying macaroon has inconsistent version; '
                'got {} want {}'.format(m.version, macaroon_version(version)))
        namespace = checkers.deserialize_namespace(json_dict.get('ns'))
        cdata = json_dict.get('cdata', {})
        caveat_data = {}
        for id64 in cdata:
            id = utils.b64decode(id64)
            data = utils.b64decode(cdata[id64])
            caveat_data[id] = data
        macaroon = Macaroon(root_key=None, id=None,
                            namespace=namespace,
                            version=version)
        macaroon._caveat_data = caveat_data
        macaroon._macaroon = m
        return macaroon
예제 #7
0
def _decode_macaroon_id(id):
    storage_id = b''
    base64_decoded = False
    first = id[:1]
    if first == b'A':
        # The first byte is not a version number and it's 'A', which is the
        # base64 encoding of the top 6 bits (all zero) of the version number 2
        # or 3, so we assume that it's the base64 encoding of a new-style
        # macaroon id, so we base64 decode it.
        #
        # Note that old-style ids always start with an ASCII character >= 4
        # (> 32 in fact) so this logic won't be triggered for those.
        try:
            dec = utils.b64decode(id.decode('utf-8'))
            # Set the id only on success.
            id = dec
            base64_decoded = True
        except:
            # if it's a bad encoding, we'll get an error which is fine
            pass

    # Trim any extraneous information from the id before retrieving
    # it from storage, including the UUID that's added when
    # creating macaroons to make all macaroons unique even if
    # they're using the same root key.
    first = six.byte2int(id[:1])
    if first == bakery.VERSION_2:
        # Skip the UUID at the start of the id.
        storage_id = id[1 + 16:]
    if first == bakery.VERSION_3:
        try:
            id1 = id_pb2.MacaroonId.FromString(id[1:])
        except google.protobuf.message.DecodeError:
            raise bakery.VerificationError(
                'no operations found in macaroon')
        if len(id1.ops) == 0 or len(id1.ops[0].actions) == 0:
            raise bakery.VerificationError(
                'no operations found in macaroon')

        ops = []
        for op in id1.ops:
            for action in op.actions:
                ops.append(bakery.Op(op.entity, action))
        return id1.storageId, ops

    if not base64_decoded and _is_lower_case_hex_char(first):
        # It's an old-style id, probably with a hyphenated UUID.
        # so trim that off.
        last = id.rfind(b'-')
        if last >= 0:
            storage_id = id[0:last]
    return storage_id, [bakery.LOGIN_OP]
예제 #8
0
def _decode_macaroon_id(id):
    storage_id = b''
    base64_decoded = False
    first = id[:1]
    if first == b'A':
        # The first byte is not a version number and it's 'A', which is the
        # base64 encoding of the top 6 bits (all zero) of the version number 2
        # or 3, so we assume that it's the base64 encoding of a new-style
        # macaroon id, so we base64 decode it.
        #
        # Note that old-style ids always start with an ASCII character >= 4
        # (> 32 in fact) so this logic won't be triggered for those.
        try:
            dec = utils.b64decode(id.decode('utf-8'))
            # Set the id only on success.
            id = dec
            base64_decoded = True
        except:
            # if it's a bad encoding, we'll get an error which is fine
            pass

    # Trim any extraneous information from the id before retrieving
    # it from storage, including the UUID that's added when
    # creating macaroons to make all macaroons unique even if
    # they're using the same root key.
    first = six.byte2int(id[:1])
    if first == bakery.VERSION_2:
        # Skip the UUID at the start of the id.
        storage_id = id[1 + 16:]
    if first == bakery.VERSION_3:
        try:
            id1 = id_pb2.MacaroonId.FromString(id[1:])
        except google.protobuf.message.DecodeError:
            raise bakery.VerificationError('no operations found in macaroon')
        if len(id1.ops) == 0 or len(id1.ops[0].actions) == 0:
            raise bakery.VerificationError('no operations found in macaroon')

        ops = []
        for op in id1.ops:
            for action in op.actions:
                ops.append(bakery.Op(op.entity, action))
        return id1.storageId, ops

    if not base64_decoded and _is_lower_case_hex_char(first):
        # It's an old-style id, probably with a hyphenated UUID.
        # so trim that off.
        last = id.rfind(b'-')
        if last >= 0:
            storage_id = id[0:last]
    return storage_id, [bakery.LOGIN_OP]
예제 #9
0
 def add_macaroon(data):
     data = utils.b64decode(data)
     data_as_objs = json.loads(data.decode('utf-8'))
     ms = [utils.macaroon_from_dict(x) for x in data_as_objs]
     mss.append(ms)