Source code for bitex.auth
"""Basic auth class for :mod:`bitex-framework`."""
# Built-in
import json
import logging
import time
from urllib.parse import parse_qs
# Third-party
import requests
# Home-brew
from bitex.request import BitexPreparedRequest
from bitex.types import DecodedParams
# Init Logging Facilities
log = logging.getLogger(__name__)
[docs]class BitexAuth(requests.auth.AuthBase):
"""Authentication Meta Class for API authentication.
Takes care of generating a signature and preparing data to be sent, headers and
URLs as required by the exchange this class is subclassed for.
:param str key: API Key.
:param str secret: API Secret.
"""
def __init__(self, key: str, secret: str) -> None:
self.key = key
self.secret = secret
@property
def key_as_bytes(self) -> bytes:
"""Return the key encoded as bytes."""
return self.key.encode("utf-8")
@property
def secret_as_bytes(self) -> bytes:
"""Return the secret encoded as bytes."""
return self.secret.encode("utf-8")
def __call__(self, request: BitexPreparedRequest) -> BitexPreparedRequest:
"""Sign the given request.
This must be extended in subclasses as it merely returns the request and
does not do any signing / authenticating.
:param requests.PreparedRequest request: The prepared request to sign.
:rtype: requests.PreparedRequest
"""
return request
[docs] @staticmethod
def decode_body(request: BitexPreparedRequest) -> DecodedParams:
"""Decode the urlencoded body of the given request and return it.
Some signature algorithms require us to use parameters supplied via the
request body. Since the body is already urlencoded using
:meth:`requests.PreparedRequest.prepare`, we need to undo its work
before returning the request body's contents.
We must accommodate for the case that in some cases the body may be a
JSON encoded string. We expect the parsed JSON to be a dictionary of
objects.
:param BitexPreparedRequest request:
The request whose body we should decode.
"""
if request.headers["Content-Type"] == "application/json":
# The body is required to be bytes, so we decode to string first
body = request.body.decode("UTF-8")
body_as_dict = json.loads(body, parse_int=str, parse_float=str)
body_as_dict = {k: [v] for k, v in body_as_dict.items()}
else:
body_as_dict = parse_qs(request.body)
print(body_as_dict)
items = body_as_dict.items()
return tuple((key, value) for key, value in sorted(items, key=lambda x: x[0]))
[docs] @staticmethod
def nonce() -> str:
"""Create a Nonce value for signature generation.
By default, this is a unix timestamp with millisecond resolution.
converted to a str.
:rtype: str
"""
return str(int(round(1000 * time.time())))