From 851184bf7577341246a15a49da0d792d181b40ab Mon Sep 17 00:00:00 2001 From: Davide Casale Date: Wed, 14 Dec 2022 18:56:03 +0100 Subject: [PATCH] Add authentication logic to _Requests class in BfxRestInterface.py. Add _RestAuthenticatedEndpoints class. Add InvalidAuthenticationCredentials in bfxapi/rest/exceptions.py. --- bfxapi/rest/BfxRestInterface.py | 54 +++++++++++++++++++++++++++++---- bfxapi/rest/exceptions.py | 10 +++++- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/bfxapi/rest/BfxRestInterface.py b/bfxapi/rest/BfxRestInterface.py index 30109c0..b7a8893 100644 --- a/bfxapi/rest/BfxRestInterface.py +++ b/bfxapi/rest/BfxRestInterface.py @@ -1,4 +1,4 @@ -import requests +import time, hmac, hashlib, json, requests from http import HTTPStatus @@ -8,15 +8,32 @@ from . import serializers from .typings import * from .enums import Config, Precision, Sort -from .exceptions import RequestParametersError, ResourceNotFound +from .exceptions import RequestParametersError, ResourceNotFound, InvalidAuthenticationCredentials class BfxRestInterface(object): - def __init__(self, host): + def __init__(self, host, API_KEY = None, API_SECRET = None): self.public = _RestPublicEndpoints(host=host) + self.auth = _RestAuthenticatedEndpoints(host=host, API_KEY=API_KEY, API_SECRET=API_SECRET) + class _Requests(object): - def __init__(self, host: str): - self.host = host + def __init__(self, host, API_KEY = None, API_SECRET = None): + self.host, self.API_KEY, self.API_SECRET = host, API_KEY, API_SECRET + + def __build_authentication_headers(self, endpoint, data): + nonce = str(int(time.time()) * 1000) + + signature = hmac.new( + self.API_SECRET.encode("utf8"), + f"/api/v2/{endpoint}{nonce}{json.dumps(data)}".encode("utf8"), + hashlib.sha384 + ).hexdigest() + + return { + "bfx-nonce": nonce, + "bfx-signature": signature, + "bfx-apikey": self.API_KEY + } def _GET(self, endpoint, params = None): response = requests.get(f"{self.host}/{endpoint}", params=params) @@ -32,6 +49,28 @@ class _Requests(object): return data + def _POST(self, endpoint, params = None, data = None, _append_authentication_headers = True): + headers = { "Content-Type": "application/json" } + + if _append_authentication_headers: + headers = { **headers, **self.__build_authentication_headers(f"{endpoint}", data) } + + response = requests.post(f"{self.host}/{endpoint}", params=params, data=json.dumps(data), headers=headers) + + if response.status_code == HTTPStatus.NOT_FOUND: + raise ResourceNotFound(f"No resources found at endpoint <{endpoint}>.") + + data = response.json() + + if len(data) and data[0] == "error": + if data[1] == 10020: + raise RequestParametersError(f"The request was rejected with the following parameter error: <{data[2]}>") + + if data[1] == 10100: + raise InvalidAuthenticationCredentials("Cannot authenticate with given API-KEY and API-SECRET.") + + return data + class _RestPublicEndpoints(_Requests): def platform_status(self) -> PlatformStatus: return serializers.PlatformStatus.parse(*self._GET("platform/status")) @@ -185,4 +224,7 @@ class _RestPublicEndpoints(_Requests): return [ serializers.FundingStat.parse(*subdata) for subdata in data ] def conf(self, config: Config) -> Any: - return self._GET(f"conf/{config}")[0] \ No newline at end of file + return self._GET(f"conf/{config}")[0] + +class _RestAuthenticatedEndpoints(_Requests): + __PREFIX = "auth/" \ No newline at end of file diff --git a/bfxapi/rest/exceptions.py b/bfxapi/rest/exceptions.py index 8afc74e..973fb5a 100644 --- a/bfxapi/rest/exceptions.py +++ b/bfxapi/rest/exceptions.py @@ -1,6 +1,7 @@ __all__ = [ "RequestParametersError", - "ResourceNotFound" + "ResourceNotFound", + "InvalidAuthenticationCredentials" ] class BfxRestException(Exception): @@ -22,4 +23,11 @@ class ResourceNotFound(BfxRestException): This error indicates a failed HTTP request to a non-existent resource. """ + pass + +class InvalidAuthenticationCredentials(BfxRestException): + """ + This error indicates that the user has provided incorrect credentials (API-KEY and API-SECRET) for authentication. + """ + pass \ No newline at end of file