예제 #1
0
def test_duplicate_tag():
    class TagDict(JSONTag):
        key = ' d'

    s = TaggedJSONSerializer()
    pytest.raises(KeyError, s.register, TagDict)
    s.register(TagDict, force=True, index=0)
    assert isinstance(s.tags[' d'], TagDict)
    assert isinstance(s.order[0], TagDict)
예제 #2
0
def test_duplicate_tag():
    class TagDict(JSONTag):
        key = " d"

    s = TaggedJSONSerializer()
    pytest.raises(KeyError, s.register, TagDict)
    s.register(TagDict, force=True, index=0)
    assert isinstance(s.tags[" d"], TagDict)
    assert isinstance(s.order[0], TagDict)
예제 #3
0
def test_tag_order():
    class Tag1(JSONTag):
        key = " 1"

    class Tag2(JSONTag):
        key = " 2"

    s = TaggedJSONSerializer()

    s.register(Tag1, index=-1)
    assert isinstance(s.order[-2], Tag1)

    s.register(Tag2, index=None)
    assert isinstance(s.order[-1], Tag2)
예제 #4
0
def test_dump_load_unchanged(data):
    # logger.info(data)
    print(data)
    # logger.setLevel(logging.DEBUG)
    s = TaggedJSONSerializer()
    s.loads(s.dumps(data))
    # print(s.tags)
    print(s.dumps(data))
    assert s.loads(s.dumps(data)) == data
예제 #5
0
def test_custom_tag():
    class Foo(object):
        def __init__(self, data):
            self.data = data

    class TagFoo(JSONTag):
        __slots__ = ()
        key = ' f'

        def check(self, value):
            return isinstance(value, Foo)

        def to_json(self, value):
            return self.serializer.tag(value.data)

        def to_python(self, value):
            return Foo(value)

    s = TaggedJSONSerializer()
    s.register(TagFoo)
    assert s.loads(s.dumps(Foo('bar'))).data == 'bar'
예제 #6
0
def setup_session(app):
    global sessionOptions
    setupSession(app.config)
    app.config['SESSION_COOKIE_SECURE'] = sessionOptions[
        'session_cookie_secure']
    digest_method = hashlib.sha1
    key_derivation = "hmac"

    # Seems flask cookies are not natively handled well, we borrowed some codes from: http://pythonexample.com/code/flask-cookie-httponly/
    serializer = URLSafeTimedSerializer(
        sessionOptions['session_secret_key'],
        salt=sessionOptions['session_secret_key'],
        serializer=TaggedJSONSerializer(),
        signer_kwargs=dict(key_derivation=key_derivation,
                           digest_method=digest_method))

    def encodeCookie(_sessionId):
        scookie = SecureCookie(
            {sessionOptions['session_cookie_id']: _sessionId},
            sessionOptions['session_secret_key'])
        return serializer.dumps(scookie)

    def decodeCookie(_sessionId):
        try:
            return serializer.loads(_sessionId).get(
                sessionOptions['session_cookie_id'])
        except:
            pass

    @app.before_request
    def _attach():
        request.session = None
        sessionId = decodeCookie(
            request.cookies.get(sessionOptions['session_cookie_id']))
        if sessionId is None or sessionId == '':
            sessionId = newSessionId()

            @after_this_request
            def post_set_cookie(response):
                # TODO: Add http only switch here after some testing
                response.set_cookie(
                    sessionOptions['session_cookie_id'],
                    encodeCookie(sessionId),
                    secure=sessionOptions['session_cookie_secure'],
                    domain=sessionOptions['session_cookie_domain'],
                    path=sessionOptions['session_cookie_path'])
                return response

        request.session = Session(sessionId, **sessionOptions)
def decrypt_api_key(api_key):
    secret_key = os.getenv('FLASK_SECRET_KEY', None)
    assert secret_key is not None

    signer_kwargs = dict(
        key_derivation="hmac", digest_method=hashlib.sha1
    )

    serializer = URLSafeTimedSerializer(
        secret_key,
        salt="cookie-session",
        serializer=TaggedJSONSerializer(),
        signer_kwargs=signer_kwargs,
    )
    return serializer.loads(api_key)
예제 #8
0
def get_session(response):
    """ Return session cookie contents.
    This a base64 encoded json.
    Returns a dict
    """

    # Alas seems like if there are multiple set-cookie headers - we are on our own
    for index, h in enumerate(response.headers):
        if h[0] == "Set-Cookie":
            cookie = parse_cookie(response.headers[index][1])
            encoded_cookie = cookie.get("session", None)
            if encoded_cookie:
                serializer = URLSafeTimedSerializer(
                    "secret", serializer=TaggedJSONSerializer())
                val = serializer.loads_unsafe(encoded_cookie)
                return val[1]
예제 #9
0
 def __init__(
     self,
     orm_session: Session,
     sql_session_model: Type,
     make_id: Callable[[], str],
     make_session_id: Callable[[], str] = default_mint_session_id,
     permanent: Optional[bool] = None,
     serializer: Optional[SerializerProtocol] = None,
 ):
     self.permanent = permanent
     self.make_id = make_id
     self.make_session_id = make_session_id
     if serializer is None:
         serializer = TaggedJSONSerializer()
     self.serializer = serializer
     self.orm_session = orm_session
     self.sql_session_model = sql_session_model
예제 #10
0
def test_tag_order():
    class Tag1(JSONTag):
        key = " 1"

    class Tag2(JSONTag):
        key = " 2"

    s = TaggedJSONSerializer()

    s.register(Tag1, index=-1)
    assert isinstance(s.order[-2], Tag1)

    s.register(Tag2, index=None)
    assert isinstance(s.order[-1], Tag2)
예제 #11
0
def get_serializer(secret: str, legacy: bool, salt: str) -> URLSafeTimedSerializer:
    """
    Get a (cached) serializer instance
    :param secret: Secret key
    :param salt: Salt
    :param legacy: Should the legacy timestamp generator be used?
    :return: Flask session serializer
    """
    if legacy:
        signer = LegacyTimestampSigner
    else:
        signer = TimestampSigner

    return URLSafeTimedSerializer(
        secret_key=secret,
        salt=salt,
        serializer=TaggedJSONSerializer(),
        signer=signer,
        signer_kwargs={
            'key_derivation': 'hmac',
            'digest_method': hashlib.sha1})
예제 #12
0
def test_custom_tag():
    class Foo(object):
        def __init__(self, data):
            self.data = data

    class TagFoo(JSONTag):
        __slots__ = ()
        key = ' f'

        def check(self, value):
            return isinstance(value, Foo)

        def to_json(self, value):
            return self.serializer.tag(value.data)

        def to_python(self, value):
            return Foo(value)

    s = TaggedJSONSerializer()
    s.register(TagFoo)
    assert s.loads(s.dumps(Foo('bar'))).data == 'bar'
예제 #13
0
def test_custom_tag():
    class Foo:  # noqa: B903, for Python2 compatibility
        def __init__(self, data):
            self.data = data

    class TagFoo(JSONTag):
        __slots__ = ()
        key = " f"

        def check(self, value):
            return isinstance(value, Foo)

        def to_json(self, value):
            return self.serializer.tag(value.data)

        def to_python(self, value):
            return Foo(value)

    s = TaggedJSONSerializer()
    s.register(TagFoo)
    assert s.loads(s.dumps(Foo("bar"))).data == "bar"
예제 #14
0
def test_dump_load_unchanged(data):
    s = TaggedJSONSerializer()
    assert s.loads(s.dumps(data)) == data
예제 #15
0
class FirestoreSessionInterface(SessionInterface):
    serializer = TaggedJSONSerializer()
    session_class = FirestoreSession

    def __init__(self, db):
        self.db = db

    def _get_doc(self, sid):
        return self.db.collection("sessions").document(sid)

    def _get_signer(self, app):
        return Signer(app.secret_key, salt="session-id", key_derivation="hmac")

    def _get_crypt(self, app):
        return AES.new(app.secret_key[:16], mode=AES.MODE_ECB)

    def _decrypt(self, app, encrypted: str, verify_salt: str) -> dict:
        crypt = self._get_crypt(app)
        salted = unpad(crypt.decrypt(bytes.fromhex(encrypted)),
                       AES.block_size).decode("utf-8")
        salt, serialised = salted.split(":", 1)
        if not hmac.compare_digest(salt, verify_salt):
            raise ValueError()
        return self.serializer.loads(serialised)

    def _encrypt(self, app, data: dict, salt: str):
        serialised = self.serializer.dumps(data)
        salted = f"{salt}:{serialised}".encode("utf-8")
        crypt = self._get_crypt(app)
        return crypt.encrypt(pad(salted, AES.block_size)).hex()

    def save_session(self, app, session: FirestoreSession, response):
        session_id = self._get_signer(app).sign(session.sid.encode("utf-8"))
        if session.modified:
            self._get_doc(session.sid).set(
                dict(encrypted=self._encrypt(app, dict(session), session.salt),
                     salt=session.salt))
        if session.accessed:
            response.set_cookie(
                app.session_cookie_name,
                session_id,
                expires=self.get_expiration_time(app, session),
                httponly=self.get_cookie_httponly(app),
                domain=self.get_cookie_domain(app),
                path=self.get_cookie_path(app),
                secure=self.get_cookie_secure(app),
            )

    def open_session(self, app, request):
        sid = request.cookies.get(app.session_cookie_name)
        if not sid:
            return self.session_class(sid=uuid.uuid4().hex)
        try:
            sid = self._get_signer(app).unsign(sid).decode("utf-8")
        except BadSignature:
            return self.session_class(sid=uuid.uuid4().hex)
        doc = self._get_doc(sid).get()
        if not doc.exists:
            return self.session_class(sid=uuid.uuid4().hex)
        data = doc.to_dict()
        salt = data["salt"]
        return self.session_class(data=self._decrypt(app, data["encrypted"],
                                                     salt),
                                  sid=sid,
                                  salt=salt)
예제 #16
0
        in case the loading failed because of a configuration error or an
        instance of a session object which implements a dictionary like
        interface + the methods and attributes on :class:`SessionMixin`.
        """
        raise NotImplementedError()

    def save_session(self, app, session, response):
        """This is called for actual sessions returned by :meth:`open_session`
        at the end of the request.  This is still called during a request
        context so if you absolutely need access to the request you can do
        that.
        """
        raise NotImplementedError()


session_json_serializer = TaggedJSONSerializer()  # Session JSON序列化器


class SecureCookieSessionInterface(SessionInterface):
    """The default session interface that stores sessions in signed cookies
    through the :mod:`itsdangerous` module.
    """
    #: the salt that should be applied on top of the secret key for the
    #: signing of cookie based sessions.
    salt = 'cookie-session'
    #: the hash function to use for the signature.  The default is sha1
    digest_method = staticmethod(hashlib.sha1)
    #: the name of the itsdangerous supported key derivation.  The default
    #: is hmac.
    key_derivation = 'hmac'
    #: A python serializer for the payload.  The default is a compact
예제 #17
0
def test_dump_load_unchanged(data):
    s = TaggedJSONSerializer()
    assert s.loads(s.dumps(data)) == data
class StorageAccount(object):
    json_serializer = TaggedJSONSerializer()

    def __init__(self, connection_str: str, table_name: str, partition_key: str, create_table_if_not_exists: bool):
        self.table_name = table_name
        self.partition_key = partition_key
        self.create_table_if_not_exists = create_table_if_not_exists
        self.table_service = TableClient.from_connection_string(conn_str=connection_str, table_name=self.table_name)

    def write(self, key: str, data: dict, encryption_key: bytes) -> None:
        """
        serializes and encrypts the passed dict object object and writes it to the storage
        """

        data = self.json_serializer.dumps(data)
        encoded_data, tag, nonce = self.encrypt(data, encryption_key)
        entity = {
            "PartitionKey": self.partition_key,
            "RowKey": key,
            "Data": encoded_data,
            "Tag": tag,
            "Nonce": nonce
        }
        try:
            self.table_service.upsert_entity(entity=entity)
        except AzureMissingResourceHttpError:
            if not self.create_table_if_not_exists:
                raise
            self.table_service.create_table()
            self.table_service.upsert_entity(entity=entity)

    def read(self, key: str, app_key: bytes) -> Union[List[Dict], None]:
        """
        reads encrypted data from storage and decrypts and deserializes it.
        Returns None if no data was found or decryption failed.
        """
        try:
            data = self.table_service.get_entity(self.partition_key, key)
            decoded = self.decrypt(data["Data"].value, data["Tag"].value, data["Nonce"].value, app_key)
            if decoded is not None:
                return self.json_serializer.loads(decoded)
            return None
        except AzureMissingResourceHttpError:
            return None

    def delete(self, key: str) -> None:
        """
        Removes an element from storage if it exists
        """
        try:
            self.table_service.delete_entity(self.partition_key, key)
        except AzureMissingResourceHttpError:
            pass

    @staticmethod
    def encrypt(data: str, secret_text: bytes) -> Tuple[str, str, str]:
        """
        encrypts the passed data with the secret text.
        :return: a tuple of three elements: encrypted data, verification_tag and nonce element.
        All elements are base64 encoded strings
        """
        cipher = AES.new(secret_text, AES.MODE_EAX)
        ciphertext, tag = cipher.encrypt_and_digest((data.encode("utf-8")))
        return (base64.b64encode(ciphertext).decode("ascii"),
                base64.b64encode(tag).decode("ascii"),
                base64.b64encode(cipher.nonce).decode("ascii"))

    @staticmethod
    def decrypt(encrypted_data: str, verification_tag: str, nonce: str, secret_text: bytes) -> Union[str, None]:
        """
        Decrypts encoded data using the passed secret_text
        :param encrypted_data:  as base64 encoded string or byte array
        :param verification_tag: as base64 encoded string or byte array
        :param nonce: as base64 encoded string or byte array
        :param secret_text: the same secret text with wich the element was encoded
        :return: the plaintext on success, None if the data could not be decoded or verified
        """
        nonce = base64.b64decode(nonce)
        cipher = AES.new(secret_text, AES.MODE_EAX, nonce=nonce)
        data = base64.b64decode(encrypted_data)
        plaintext = cipher.decrypt(data)
        tag = base64.b64decode(verification_tag)
        try:
            cipher.verify(tag)
            return plaintext.decode("utf-8")
        except ValueError:
            return None
예제 #19
0
        in case the loading failed because of a configuration error or an
        instance of a session object which implements a dictionary like
        interface + the methods and attributes on :class:`SessionMixin`.
        """
        raise NotImplementedError()

    def save_session(self, app, session, response):
        """This is called for actual sessions returned by :meth:`open_session`
        at the end of the request.  This is still called during a request
        context so if you absolutely need access to the request you can do
        that.
        """
        raise NotImplementedError()


session_json_serializer = TaggedJSONSerializer()


class SecureCookieSessionInterface(SessionInterface):
    """The default session interface that stores sessions in signed cookies
    through the :mod:`itsdangerous` module.
    """
    #: the salt that should be applied on top of the secret key for the
    #: signing of cookie based sessions.
    salt = 'cookie-session'
    #: the hash function to use for the signature.  The default is sha1
    digest_method = staticmethod(hashlib.sha1)
    #: the name of the itsdangerous supported key derivation.  The default
    #: is hmac.
    key_derivation = 'hmac'
    #: A python serializer for the payload.  The default is a compact
예제 #20
0
class CachingSessionInterface(SessionInterface):
    """
    This code is partially based off of the RedisSessionInterface from Flask-Session with updates to properly
    interoperate with Flask-Caching and be more inline with modern Flask (i.e. doesn't use pickle).

    https://github.com/fengsp/flask-session/blob/master/flask_session/sessions.py#L90
    """

    serializer = TaggedJSONSerializer()
    session_class = CachedSession

    def _generate_sid(self):
        sid = str(uuid4())
        v = cache.get(key=self.key_prefix + sid)
        while v:
            sid = str(uuid4())
            v = cache.get(key=self.key_prefix + sid)
        return sid

    def __init__(self, key_prefix, use_signer=True, permanent=False):
        self.key_prefix = key_prefix
        self.use_signer = use_signer
        self.permanent = permanent

    def open_session(self, app, request):
        sid = request.cookies.get(app.session_cookie_name)
        if not sid:
            sid = self._generate_sid()
            return self.session_class(sid=sid, permanent=self.permanent)

        if self.use_signer:
            try:
                sid_as_bytes = unsign(sid)
                sid = sid_as_bytes.decode()
            except BadSignature:
                sid = self._generate_sid()
                return self.session_class(sid=sid, permanent=self.permanent)

        if isinstance(sid, text_type) is False:
            sid = sid.decode("utf-8", "strict")
        val = cache.get(self.key_prefix + sid)
        if val is not None:
            try:
                data = self.serializer.loads(val)
                return self.session_class(data, sid=sid)
            except Exception:
                return self.session_class(sid=sid, permanent=self.permanent)
        return self.session_class(sid=sid, permanent=self.permanent)

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)

        if not session:
            if session.modified:
                cache.delete(self.key_prefix + session.sid)
                response.delete_cookie(app.session_cookie_name,
                                       domain=domain,
                                       path=path)
            return

        if session.modified:
            httponly = self.get_cookie_httponly(app)
            secure = self.get_cookie_secure(app)
            expires = self.get_expiration_time(app, session)
            samesite = self.get_cookie_samesite(app)
            val = self.serializer.dumps(dict(session))

            if session.sid is None:
                session.sid = self._generate_sid()

            cache.set(
                key=self.key_prefix + session.sid,
                value=val,
                timeout=total_seconds(app.permanent_session_lifetime),
            )

            if self.use_signer:
                session_id = sign(want_bytes(session.sid))
            else:
                session_id = session.sid

            response.set_cookie(
                app.session_cookie_name,
                session_id,
                expires=expires,
                httponly=httponly,
                domain=domain,
                path=path,
                secure=secure,
                samesite=samesite,
            )
예제 #21
0
    "pinwheel", "wafer", "macaroon", "fortune", "crinkle", "icebox",
    "gingerbread", "tassie", "lebkuchen", "macaron", "black and white",
    "white chocolate macadamia"
]

s = requests.Session()
s.get(URL)
old_session = s.cookies.get_dict()["session"]

for secret in cookie_flavors:
    try:
        signature = tsigner(secret_key=secret,
                            salt="cookie-session",
                            key_derivation="hmac",
                            digest_method=hashlib.sha1).unsign(old_session)
    except:
        continue
    break

new_session = serializer(secret_key=secret,
                         salt="cookie-session",
                         serializer=TaggedJSONSerializer(),
                         signer=tsigner,
                         signer_kwargs={
                             "key_derivation": "hmac",
                             "digest_method": hashlib.sha1
                         }).dumps(data)

response = requests.get(URL, cookies=dict(session=new_session))
print(response.text)