示例#1
0
def test_should_throw_with_wrong_key():
    key = unhexlify(
        "77726f6e677365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)
    token = "870S4BYxgHw0KnP3W9fgVUHEhT5g86vJ17etaC5Kh5uIraWHCI1psNQGv298ZmjPwoYbjDQ9chy2z"

    with pytest.raises(RuntimeError):
        branca.decode(token)
示例#2
0
def test_should_throw_with_modified_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "870g1RCk4lW1YInhaU3TP8u2hGtfol16ettLcTOSoA0JIpjCaQRW7tQeP6dQmTvFIB2s6wL5deMXr"

    with pytest.raises(RuntimeError):
        branca.decode(token)
示例#3
0
def test_should_throw_with_modified_nonce():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "875GH233SUysT7fQ711EWd9BXpwOjB72ng3ZLnjWFrmOqVy49Bv93b78JU5331LbcY0EEzhLfpmSx"

    with pytest.raises(RuntimeError):
        branca.decode(token)
示例#4
0
def test_should_throw_with_modified_version():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "89mvl3S0BE0UCMIY94xxIux4eg1w5oXrhvCEXrDAjusSbO0Yk7AU6FjjTnbTWTqogLfNPJLzecHVb"

    with pytest.raises(RuntimeError):
        branca.decode(token)
示例#5
0
def test_should_throw_with_wrong_version():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "89mvl3RkwXjpEj5WMxK7GUDEHEeeeZtwjMIOogTthvr44qBfYtQSIZH5MHOTC0GzoutDIeoPVZk3w"

    with pytest.raises(RuntimeError):
        branca.decode(token)
示例#6
0
def test_should_throw_with_modified_tag():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5QwcEqLDRnTDHPenOX7nP2trk0"

    with pytest.raises(RuntimeError):
        branca.decode(token)
示例#7
0
def test_should_throw_with_modified_ciphertext():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5Qw6Jpo96myliI3hHD7VbKZBYh"

    with pytest.raises(RuntimeError):
        branca.decode(token)
示例#8
0
def test_should_throw_when_expired():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    branca._nonce = unhexlify(
        "0102030405060708090a0b0c0102030405060708090a0b0c")
    token = branca.encode(b"Hello world!", timestamp=123206400)

    with pytest.raises(RuntimeError):
        branca.decode(token, 3600)
示例#9
0
def test_decode_hello_world_with_zero_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)
    token = "870S4BYxgHw0KnP3W9fgVUHEhT5g86vJ17etaC5Kh5uIraWHCI1psNQGv298ZmjPwoYbjDQ9chy2z"

    assert branca.decode(token) == b"Hello world!"
    assert branca.timestamp(token) == 0
示例#10
0
def test_decode_hello_world_with_max_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)
    token = "89i7YCwu5tWAJNHUDdmIqhzOi5hVHOd4afjZcGMcVmM4enl4yeLiDyYv41eMkNmTX6IwYEFErCSqr"

    assert branca.decode(token) == b"Hello world!"
    assert branca.timestamp(token) == 4294967295
示例#11
0
def test_should_allow_hex_string_key():
    branca = Branca(
        key="73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")

    token = "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5QwcEqLDRnTDHPenOX7nP2trlT"

    assert branca.decode(token) == b"Hello world!"
    assert branca.timestamp(token) == 123206400
示例#12
0
def test_decode_eight_nul_bytes_with_nov27_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "1jJDJOEjuwVb9Csz1Ypw1KBWSkr0YDpeBeJN6NzJWx1VgPLmcBhu2SbkpQ9JjZ3nfUf7Aytp"

    assert branca.decode(token) == b"\x00\x00\x00\x00\x00\x00\x00\x00"
    assert branca.timestamp(token) == 123206400
示例#13
0
def test_decode_eight_nul_bytes_with_max_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "1jrx6DUu5q06oxykef2e2ZMyTcDRTQot9ZnwgifUtzAphGtjsxfbxXNhQyBEOGtpbkBgvIQx"

    assert branca.decode(token) == b"\x00\x00\x00\x00\x00\x00\x00\x00"
    assert branca.timestamp(token) == 4294967295
示例#14
0
def test_decode_eight_nul_bytes_with_zero_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "1jIBheHbDdkCDFQmtgw4RUZeQoOJgGwTFJSpwOAk3XYpJJr52DEpILLmmwYl4tjdSbbNqcF1"

    assert branca.decode(token) == b"\x00\x00\x00\x00\x00\x00\x00\x00"
    assert branca.timestamp(token) == 0
示例#15
0
def test_decode_hello_world_with_nov27_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5QwcEqLDRnTDHPenOX7nP2trlT"

    assert branca.decode(token) == b"Hello world!"
    assert branca.timestamp(token) == 123206400
示例#16
0
def test_decode_empty_payload():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "4sfD0vPFhIif8cy4nB3BQkHeJqkOkDvinI4zIhMjYX4YXZU5WIq9ycCVjGzB5"

    assert branca.decode(token) == b""
    assert branca.timestamp(token) == 0
示例#17
0
def test_decode_non_utf8_payload():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    token = "K9u6d0zjXp8RXNUGDyXAsB9AtPo60CD3xxQ2ulL8aQoTzXbvockRff0y1eXoHm"

    assert branca.decode(token) == b"\x80"
    assert branca.timestamp(token) == 123206400
示例#18
0
 def from_ciphertext(cls, key: Key, ciphertext: str) -> "Token":
     assert ciphertext
     branca = Branca(key.data)
     try:
         timestamp = branca.timestamp(ciphertext)
     except (struct.error, ValueError):
         return cls(key, None, None)
     try:
         token_path = Path(branca.decode(ciphertext).decode("UTF-8"))
     except RuntimeError:
         return cls(key, None, timestamp)
     return cls(key, token_path, timestamp, ciphertext)
示例#19
0
def test_branca_roundtrip(string: bytes):
    with mock.with_config() as config:
        key = config["manabi"]["key"]
    f = Branca(from_string(key))
    res = f.decode(f.encode(string))
    assert res == string
示例#20
0
class ApiTokens:
    """A class for managing branca-based API tokens."""
    def __init__(self, app, secret):
        """Initialize the Api Token manager.

        Parameters
        ----------
        app : aiohttp.web.Application
            The aiohttp application instance
        secret : str
            The secret key used to sign the API tokens (after hashing)
        """
        self.app = app
        self.branca = Branca(key=sha3_256(secret.encode("utf-8")).digest())
        logger.debug("created Branca instance")

    async def generate_token(self,
                             username,
                             token_name=str(uuid4()),
                             permissions="*"):
        """Generate a token for a user with given permissions.

        Parameters
        ----------
        username : str
            The username of the user for whom the token is being generated
        token_name : str, default=str(uuid4())
            The name of the token to be generated - will represent it on the frontend
        permissions : str, default="*"
            The permissions that the token will have acces to. "*" means all permissions user has
        Returns
        -------
        (token, token_name, permissions) : tuple(str, str, list(str))
            The generated token, its name and permissions it has access to
        """
        if not token_name:
            token_name = str(uuid4())
        logger.debug(
            "generating a token for %s with these permissions: %s",
            username,
            permissions,
        )
        user = await find_user_by_username(self.app, username,
                                           ["permissions", "password", "_id"])
        if permissions == "*":
            permissions = list(user.get("permissions", []))
        else:
            permissions = list(
                filter(
                    lambda permission: permission in user.get(
                        "permissions", []),
                    permissions,
                ))
        packed = msgpack.dumps({
            "username": username,
            "permisisons": permissions,
            "token_name": token_name
        })
        token = self.branca.encode(packed)
        logger.debug(
            "successfuly generated a token for %s: %s. Adding it to the database",
            username,
            token,
        )
        await add_token_to_user(self.app, user.get("_id", ""), token_name,
                                token)
        return (
            token,
            token_name,
            permissions,
        )

    async def validate_token(self, token, permissions=None, raise_error=False):
        """Validate if token is valid and has the requested permissions.

        Parameters
        ----------
        token : str
            The token to be validated
        permissions : list(str), default=None
            The permissions that the token must have access to.
        raise_error : bool, default=False
            If True, raise an error if the token is invalid, otherwise just return False
        Returns
        -------
        bool
            True if the token is valid and has the requested permissions, False otherwise
        """
        permissions = permissions if permissions else []
        logger.debug(
            "validating a token%s. Token: %s",
            " for permissions" + permissions if permissions != [] else "",
            token,
        )
        user = await find_user_by_token(self.app, token,
                                        ["permissions", "username"])
        packed = self.branca.decode(token)
        payload = msgpack.loads(packed, raw=False)
        logger.debug("decoded token: %s", payload)
        if user is None:
            if raise_error:
                raise HTTPUnauthorized()
            return False
        user_permission_set = set(user.get("permissions", []))
        if not set(payload.get("permissions",
                               [])).issubset(user_permission_set):
            payload["permissions"] = filter(
                lambda permission: permission in user_permission_set,
                payload["permissions"],
            )
        if (
                # check if token has all requested permissions
                not set(permissions).issubset(
                    set(payload.get("permissions", [])))
                # check if user has all requested permissions
                or not set(permissions).issubset(user_permission_set)):
            if raise_error:
                raise HTTPForbidden()
            return False
        logger.debug("Token is valid")
        return True

    async def get_token_info(self, token):
        """Get information about a given token. Includes permissions, username, token name, etc.

        Parameters
        ----------
        token : str
            The token to be checked
        Returns
        -------
        (payload, user) : tuple(dict, dict)
            A dictionary containing information about the token and a dict with user information
            (username, user permissions)

        """
        user = await find_user_by_token(self.app, token,
                                        ["permissions", "username"])
        packed = self.branca.decode(token)
        payload = msgpack.loads(packed, raw=False)
        if user is None:
            return payload, None
        user_permission_set = set(user.get("permissions", []))
        if not set(payload.get("permissions",
                               [])).issubset(user_permission_set):
            payload["permissions"] = filter(
                lambda permission: permission in user_permission_set,
                payload["permissions"],
            )
        return payload, user
示例#21
0
class ApiTokens:
    def __init__(self, app, secret):
        self.app = app
        self.branca = Branca(key=sha3_256(secret.encode("utf-8")).digest())
        logger.debug("created Branca instance")

    async def generate_token(self, username, token_name=str(uuid4()), permissions="*"):
        if not token_name:
            token_name = str(uuid4())
        logger.debug(
            f"generating a token for {username} with these permissions: {permissions}"
        )
        user = await find_user_by_username(
            self.app, username, ["permissions", "password", "_id"]
        )
        if permissions == "*":
            permissions = list(user.get("permissions", []))
        else:
            permissions = list(
                filter(
                    lambda permission: permission in user.get("permissions", []),
                    permissions,
                )
            )
        packed = msgpack.dumps(
            {"username": username, "permisisons": permissions, "token_name": token_name}
        )
        token = self.branca.encode(packed)
        logger.debug(
            f"successfuly generated a token for {username}: {token}. Adding it to the database"
        )
        await add_token_to_user(self.app, user.get("_id", ""), token_name, token)
        return (
            token,
            token_name,
            permissions,
        )

    async def validate_token(self, token, permissions=[], raise_error=False):
        logger.debug(
            f"validating a token{' for permissions' + permissions if permissions != [] else ''}. Token: {token}"
        )
        user = await find_user_by_token(self.app, token, ["permissions", "username"])
        packed = self.branca.decode(token)
        payload = msgpack.loads(packed, raw=False)
        logger.debug(f"decoded token: {payload}")
        if user == None:
            if raise_error:
                raise HTTPUnauthorized()
            return False
        user_permission_set = set(user.get("permissions", []))
        if not set(payload.get("permissions", [])).issubset(user_permission_set):
            payload["permissions"] = filter(
                lambda permission: permission in user_permission_set,
                payload["permissions"],
            )
        if (
            # check if token has all requested permissions
            not set(permissions).issubset(set(payload.get("permissions", [])))
            # check if user has all requested permissions
            or not set(permissions).issubset(user_permission_set)
        ):
            if raise_error:
                raise HTTPForbidden()
            return False
        logger.debug("Token is valid")
        return True

    async def get_token_info(self, token):
        user = await find_user_by_token(self.app, token, ["permissions", "username"])
        packed = self.branca.decode(token)
        payload = msgpack.loads(packed, raw=False)
        if user == None:
            return payload, None
        user_permission_set = set(user.get("permissions", []))
        if not set(payload.get("permissions", [])).issubset(user_permission_set):
            payload["permissions"] = filter(
                lambda permission: permission in user_permission_set,
                payload["permissions"],
            )
        return payload, user
示例#22
0
def _decode(key: bytes, ciphertext: str, ttl=None) -> str:
    f = Branca(key)
    return f.decode(ciphertext, ttl).decode("UTF-8")