Skip to content

Commit

Permalink
feat: add api client based on basic authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
andrey-canon committed Aug 4, 2023
1 parent ebcdb0b commit 3efc731
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 121 deletions.
142 changes: 103 additions & 39 deletions eox_nelp/api_clients/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
from abc import ABC, abstractmethod

import requests
from django.conf import settings
from django.core.cache import cache
from oauthlib.oauth2 import BackendApplicationClient
from requests.auth import HTTPBasicAuth
from requests_oauthlib import OAuth2Session
from rest_framework import status

Expand All @@ -24,49 +26,16 @@ def base_url(self):
"""Abstract base_url property method."""
raise NotImplementedError

def __init__(self, client_id, client_secret):
def __init__(self):
"""
Abstract ApiClient creator, this will set the session based on the authenticate result.
Args:
client_id<str>: Public application identifier.
client_secret<str>: Confidential identifier.
"""
key = f"{client_id}-{client_secret}"
headers = cache.get(key)

if not headers:
response = self._authenticate(client_id, client_secret)
headers = {
"Authorization": f"{response.get('token_type')} {response.get('access_token')}"
}
self.session = self._authenticate()

cache.set(key, headers, response.get("expires_in", 300))

self.session = requests.Session()
self.session.headers.update(headers)

def _authenticate(self, client_id, client_secret):
"""This method uses the session attribute to perform a POS request based on the
base_url attribute and the given path, if the response has a status code 200
this will return the json from that response otherwise this will return an empty dictionary.
Args:
client_id<str>: Public application identifier.
client_secret<str>: Confidential identifier.
Return:
Dictionary: Response from authentication request.
"""
client = BackendApplicationClient(client_id=client_id)
oauth = OAuth2Session(client_id=client_id, client=client)
authenticate_url = f"{self.base_url}/oauth/token"

return oauth.fetch_token(
token_url=authenticate_url,
client_secret=client_secret,
include_client_id=True,
)
@abstractmethod
def _authenticate(self):
"""Abstract method that should return a requests Session instance in its implementation."""
raise NotImplementedError

def make_post(self, path, data):
"""This method uses the session attribute to perform a POST request based on the
Expand Down Expand Up @@ -127,3 +96,98 @@ def make_get(self, path, payload):
"error": True,
"message": f"Invalid response with status {response.status_code}"
}


class AbstractOauth2ApiClient(AbstractApiClient):
"""Abstract class for an OAuth 2.0 authentication API client.
This class provides basic functionality for an API client that requires
OAuth 2.0 authentication using the client ID and client secret.
Attributes:
client_id (str): Client ID for OAuth 2.0 authentication.
client_secret (str): Client secret for OAuth 2.0 authentication.
"""

def __init__(self):
"""Initialize an instance of AbstractOauth2ApiClient.
Gets the client ID and client secret from Django settings and then
calls the constructor of the parent class (AbstractApiClient).
"""
self.client_id = getattr(settings, "FUTUREX_API_CLIENT_ID")
self.client_secret = getattr(settings, "FUTUREX_API_CLIENT_SECRET")

super().__init__()

def _authenticate(self):
"""Authenticate the session with OAuth 2.0 credentials.
This method uses OAuth 2.0 client credentials (client ID and client secret)
to obtain an access token from the OAuth token endpoint. The access token
is then used to create and configure a requests session.
The access token is cached to minimize token requests to the OAuth server.
Returns:
requests.Session: Session authenticated with OAuth 2.0 credentials.
"""
key = f"{self.client_id}-{self.client_secret}"
headers = cache.get(key)

if not headers:
client = BackendApplicationClient(client_id=self.client_id)
oauth = OAuth2Session(client_id=self.client_id, client=client)
authenticate_url = f"{self.base_url}/oauth/token"
response = oauth.fetch_token(
token_url=authenticate_url,
client_secret=self.client_secret,
include_client_id=True,
)
headers = {
"Authorization": f"{response.get('token_type')} {response.get('access_token')}"
}

cache.set(key, headers, response.get("expires_in", 300))

session = requests.Session()
session.headers.update(headers)

return session


class AbstractBasicAuthApiClient(AbstractApiClient):
"""Abstract class for a basic authentication API client.
This class provides basic functionality for an API client that requires
basic authentication using a usern and password.
Attributes:
user (str): Username for basic authentication.
password (str): Password for basic authentication.
"""

def __init__(self):
"""Initialize an instance of AbstractBasicAuthApiClient.
Gets the user and password from Django settings and then calls
the constructor of the parent class (AbstractApiClient).
"""
self.user = getattr(settings, "EXTERNAL_CERTIFICATES_USER")
self.password = getattr(settings, "EXTERNAL_CERTIFICATES_PASSWORD")

super().__init__()

def _authenticate(self):
"""Authenticate the session with the user and password.
Creates and configures a requests session with basic authentication
provided by the user and password.
Returns:
requests.Session: Session authenticated.
"""
session = requests.Session()
session.auth = HTTPBasicAuth(self.user, self.password)

return session
10 changes: 5 additions & 5 deletions eox_nelp/api_clients/certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
"""
from django.conf import settings

from eox_nelp.api_clients import AbstractApiClient
from eox_nelp.api_clients import AbstractBasicAuthApiClient


class ExternalCertificatesApiClient(AbstractApiClient):
class ExternalCertificatesApiClient(AbstractBasicAuthApiClient):
"""Allow to perform multiple external certificates operations."""

def __init__(self):
client_id = getattr(settings, "EXTERNAL_CERTIFICATES_API_CLIENT_ID")
client_secret = getattr(settings, "EXTERNAL_CERTIFICATES_API_CLIENT_SECRET")
self.user = getattr(settings, "EXTERNAL_CERTIFICATES_USER")
self.password = getattr(settings, "EXTERNAL_CERTIFICATES_PASSWORD")

super().__init__(client_id, client_secret)
super().__init__()

@property
def base_url(self):
Expand Down
11 changes: 2 additions & 9 deletions eox_nelp/api_clients/futurex.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,16 @@
"""
from django.conf import settings

from eox_nelp.api_clients import AbstractApiClient
from eox_nelp.api_clients import AbstractOauth2ApiClient


class FuturexApiClient(AbstractApiClient):
class FuturexApiClient(AbstractOauth2ApiClient):
"""Allow to perform multiple Futurex API operations based on an authenticated session.
Attributes:
base_url: Futurex domain.
session: persist certain parameters across requests.
"""

def __init__(self):
client_id = getattr(settings, "FUTUREX_API_CLIENT_ID")
client_secret = getattr(settings, "FUTUREX_API_CLIENT_SECRET")

super().__init__(client_id, client_secret)

@property
def base_url(self):
return getattr(settings, "FUTUREX_API_URL")
Expand Down
Loading

0 comments on commit 3efc731

Please sign in to comment.