Example #1
0
def test_encode_non_utf8_payload():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    branca._nonce = unhexlify(
        "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef")
    token = branca.encode(b"", timestamp=0)

    assert token == "4sfD0vPFhIif8cy4nB3BQkHeJqkOkDvinI4zIhMjYX4YXZU5WIq9ycCVjGzB5"
Example #2
0
def other_impl_decode(string: bytes):
    with mock.branca_impl():
        with mock.with_config() as config:
            key = config["manabi"]["key"]
        f = Branca(from_string(key))
        ct = f.encode(string)
        proc = run(["cargo", "run", "decode", key, ct],
                   stdout=PIPE,
                   check=True)
        assert from_string(proc.stdout.decode("UTF-8")) == string
Example #3
0
def test_encode_eight_nul_bytes_with_zero_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    branca._nonce = unhexlify(
        "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef")
    token = branca.encode(b"\x00\x00\x00\x00\x00\x00\x00\x00", timestamp=0)

    assert token == "1jIBheHbDdkCDFQmtgw4RUZeQoOJgGwTFJSpwOAk3XYpJJr52DEpILLmmwYl4tjdSbbNqcF1"
Example #4
0
def test_encode_hello_world_with_november_27_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

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

    assert token == "875GH23U0Dr6nHFA63DhOyd9LkYudBkX8RsCTOMz5xoYAMw9sMd5QwcEqLDRnTDHPenOX7nP2trlT"
Example #5
0
def test_encode_hello_world_with_max_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    branca._nonce = unhexlify(
        "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef")
    token = branca.encode("Hello world!", timestamp=4294967295)

    assert token == "89i7YCwu5tWAJNHUDdmIqhzOi5hVHOd4afjZcGMcVmM4enl4yeLiDyYv41eMkNmTX6IwYEFErCSqr"
Example #6
0
def test_encode_hello_world_with_zero_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    branca._nonce = unhexlify(
        "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef")
    token = branca.encode("Hello world!", timestamp=0)

    assert token == "870S4BYxgHw0KnP3W9fgVUHEhT5g86vJ17etaC5Kh5uIraWHCI1psNQGv298ZmjPwoYbjDQ9chy2z"
Example #7
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)
Example #8
0
def test_encode_eight_nul_bytes_with_november_27_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    branca._nonce = unhexlify(
        "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef")
    token = branca.encode(b"\x00\x00\x00\x00\x00\x00\x00\x00",
                          timestamp=123206400)

    assert token == "1jJDJOEjuwVb9Csz1Ypw1KBWSkr0YDpeBeJN6NzJWx1VgPLmcBhu2SbkpQ9JjZ3nfUf7Aytp"
Example #9
0
def test_encode_eight_nul_bytes_with_zero_timestamp():
    key = unhexlify(
        "73757065727365637265746b6579796f7573686f756c646e6f74636f6d6d6974")
    branca = Branca(key)

    branca._nonce = unhexlify(
        "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef")
    token = branca.encode(b"\x00\x00\x00\x00\x00\x00\x00\x00",
                          timestamp=4294967295)

    assert token == "1jrx6DUu5q06oxykef2e2ZMyTcDRTQot9ZnwgifUtzAphGtjsxfbxXNhQyBEOGtpbkBgvIQx"
Example #10
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
Example #11
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
Example #12
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
Example #13
0
def _encode(key: bytes, path: str, now: Optional[int] = None) -> str:
    f = Branca(key)
    p = path.encode("UTF-8")
    ciphertext = f.encode(p, now)
    return ciphertext