Source code for sqrl.utils

# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals
from base64 import urlsafe_b64decode, urlsafe_b64encode
from collections import OrderedDict

import six
from django.core.files.base import ContentFile
from qrcode import ERROR_CORRECT_L, QRCode

[docs]class Base64(object): """ Helper class for base64 encoding/decoding """ @classmethod
[docs] def encode(cls, s): """ Encode binary string as base64. Remaining "=" characters are removed. Parameters ---------- s: bytes Bytes string to be encoded as base64 """ assert isinstance(s, (six.binary_type, bytearray)) return urlsafe_b64encode(s).decode('ascii').rstrip('=')
[docs] def decode(cls, s): """ Decode unicode string from base64 where remaining "=" characters were stripped. Parameters ---------- s: str Unicode string to be decoded from base64 """ assert isinstance(s, six.text_type) return urlsafe_b64decode((s + '=' * (4 - len(s) % 4)).encode('ascii'))
[docs]class Encoder(object): """ Helper class for encoding/decoding SQRL response data. """ @classmethod
[docs] def base64_dumps(cls, data): """ Dumps given data into a single Base64 string. Practically this is the same as :meth:`dumps` except :meth:`dumps` can return multiline string for ``dict``. This method normalizes that further by converting that multiline string to a single base64 encoded value. Returns ------- binary Base64 encoded binary data of input ``data`` """ if data and isinstance(data, dict): return Base64.encode(cls.dumps(data).encode('ascii')) return cls.dumps(data)
[docs] def dumps(cls, data): """ Recursively dumps given data to SQRL response format. Before data is dumped out, it is normalized by using :meth:`.normalize`. This dumps each data type as follows: :``dict``: returns an ``\\r\\n`` multiline string. Each line is for a single key-pair of format ``<key>=<dumped value>``. :``list``: tilde (``~``) joined dumped list of values :other: no operation """ data = cls.normalize(data) if isinstance(data, dict): return '\r\n'.join( '{}={}'.format(k, cls.dumps(v)) for k, v in data.items() ) + '\r\n' elif isinstance(data, (list, tuple)): return '~'.join(cls.dumps(i) for i in data) else: return data
[docs] def normalize(cls, data): """ Recursively normalize data for encoding. This encodes each data type as follows: :``dict``: returns an ``OrderedDict`` where all values are recursively normalized. Empty dict is normalized to empty string :``list``: each value is recursively normalized :``binary``: Base64 encode data :``str``: no operation :other: data is casted to string using ``__str__`` (or ``__unicode__``) """ if isinstance(data, dict): if data: return OrderedDict(( (k, cls.normalize(v)) for k, v in data.items() )) else: return '' elif isinstance(data, (list, tuple)): return [cls.dumps(i) for i in data] elif isinstance(data, six.binary_type): return Base64.encode(data) elif isinstance(data, six.text_type): return data else: return six.text_type(data)
[docs]class QRGenerator(object): """ Helper class for generating a QR image for the given SQRL url. Parameters ---------- url : str URL for which to generate QR image """ def __init__(self, url): self.url = url def _generate_image(self): qr = QRCode(error_correction=ERROR_CORRECT_L) qr.add_data(self.url) return qr.make_image()
[docs] def generate_image(self): """ Generate QR image and get its binary data. Returns ------- bytes Binary data of the png image file which can directly be returned to the user """ img = self._generate_image() f = ContentFile(b'', name='qr.png'), 'png') return
[docs]def get_user_ip(request): """ Utility function for getting user's IP from request address. This either returns the IP address from the ``request.REMOTE_ADDR`` or ``request.META'HTTP_X_REAL_IP']`` when request might of been reverse proxied. """ return ( request.META.get('HTTP_X_REAL_IP') or request.META['REMOTE_ADDR'] )