diff options
Diffstat (limited to 'macaroonbakery/_utils/__init__.py')
-rw-r--r-- | macaroonbakery/_utils/__init__.py | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/macaroonbakery/_utils/__init__.py b/macaroonbakery/_utils/__init__.py new file mode 100644 index 0000000..f2779e0 --- /dev/null +++ b/macaroonbakery/_utils/__init__.py @@ -0,0 +1,162 @@ +# Copyright 2017 Canonical Ltd. +# Licensed under the LGPLv3, see LICENCE file for details. +import base64 +import binascii +import json +import webbrowser +from datetime import datetime + +import six +from pymacaroons import Macaroon +from pymacaroons.serializers import json_serializer + +import six.moves.http_cookiejar as http_cookiejar +from six.moves.urllib.parse import urlparse + + +def to_bytes(s): + '''Return s as a bytes type, using utf-8 encoding if necessary. + @param s string or bytes + @return bytes + ''' + if isinstance(s, six.binary_type): + return s + if isinstance(s, six.string_types): + return s.encode('utf-8') + raise TypeError('want string or bytes, got {}', type(s)) + + +def macaroon_from_dict(json_macaroon): + '''Return a pymacaroons.Macaroon object from the given + JSON-deserialized dict. + + @param JSON-encoded macaroon as dict + @return the deserialized macaroon object. + ''' + return Macaroon.deserialize(json.dumps(json_macaroon), + json_serializer.JsonSerializer()) + + +def macaroon_to_dict(macaroon): + '''Turn macaroon into JSON-serializable dict object + @param pymacaroons.Macaroon. + ''' + return json.loads(macaroon.serialize(json_serializer.JsonSerializer())) + + +def macaroon_to_json_string(macaroon): + '''Serialize macaroon object to a JSON-encoded string. + + @param macaroon object to be serialized. + @return a string serialization form of the macaroon. + ''' + return macaroon.serialize(json_serializer.JsonSerializer()) + + +def _add_base64_padding(b): + '''Add padding to base64 encoded bytes. + + pymacaroons does not give padded base64 bytes from serialization. + + @param bytes b to be padded. + @return a padded bytes. + ''' + return b + b'=' * (-len(b) % 4) + + +def _remove_base64_padding(b): + '''Remove padding from base64 encoded bytes. + + pymacaroons does not give padded base64 bytes from serialization. + + @param bytes b to be padded. + @return a padded bytes. + ''' + return b.rstrip(b'=') + + +def b64decode(s): + '''Base64 decodes a base64-encoded string in URL-safe + or normal format, with or without padding. + The argument may be string or bytes. + + @param s bytes decode + @return bytes decoded + @raises ValueError on failure + ''' + # add padding if necessary. + s = to_bytes(s) + if not s.endswith(b'='): + s = s + b'=' * (-len(s) % 4) + try: + if '_' or '-' in s: + return base64.urlsafe_b64decode(s) + else: + return base64.b64decode(s) + except (TypeError, binascii.Error) as e: + raise ValueError(str(e)) + + +def raw_urlsafe_b64encode(b): + '''Base64 encode using URL-safe encoding with padding removed. + + @param b bytes to decode + @return bytes decoded + ''' + b = to_bytes(b) + b = base64.urlsafe_b64encode(b) + b = b.rstrip(b'=') # strip padding + return b + + +def visit_page_with_browser(visit_url): + '''Open a browser so the user can validate its identity. + + @param visit_url: where to prove your identity. + ''' + webbrowser.open(visit_url, new=1) + print('Opening an authorization web page in your browser.') + print('If it does not open, please open this URL:\n', visit_url, '\n') + + +def cookie( + url, + name, + value, + expires=None): + '''Return a new Cookie using a slightly more + friendly API than that provided by six.moves.http_cookiejar + + @param name The cookie name {str} + @param value The cookie value {str} + @param url The URL path of the cookie {str} + @param expires The expiry time of the cookie {datetime}. If provided, + it must be a naive timestamp in UTC. + ''' + u = urlparse(url) + domain = u.hostname or u.netloc + port = str(u.port) if u.port is not None else None + secure = u.scheme == 'https' + if expires is not None: + if expires.tzinfo is not None: + raise ValueError('Cookie expiration must be a naive datetime') + expires = (expires - datetime(1970, 1, 1)).total_seconds() + return http_cookiejar.Cookie( + version=0, + name=name, + value=value, + port=port, + port_specified=port is not None, + domain=domain, + domain_specified=True, + domain_initial_dot=False, + path=u.path, + path_specified=True, + secure=secure, + expires=expires, + discard=False, + comment=None, + comment_url=None, + rest=None, + rfc2109=False, + ) |