Source code for sagace.auth.infrastructure.authentication_api

"""
--------------------------------------------------------------------------------------------------------------------

Descrição:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Este módulo implementa a autenticação de usuários no sistema SAGACE utilizando uma API externa.
A classe `AuthenticationAPI` herda de `AuthenticationRepository`, garantindo a conformidade com o princípio de **Dependency Inversion** (D do SOLID),
permitindo a substituição da implementação sem afetar os consumidores da interface.

Principais funcionalidades:

- Realiza autenticação via API externa.
- Armazena o token JWT utilizando `TokenStorage`.
- Lança exceções apropriadas em caso de falha.

Classes:

- ``AuthenticationAPI``: Implementação do repositório de autenticação via API.

Exemplo de uso:

.. code-block:: python

    from sagace.infrastructure import AuthenticationAPI
    from sagace.core import TokenStorage

    auth_api = AuthenticationAPI()
    token_storage = TokenStorage()
    token = auth_api.authenticate("https://api.sagace.online", "usuario", "senha", "app_token", token_storage)
    print(token)


Autor: Diego Yosiura
Última Atualização: 27/02/2025 15:31
Criado em: 27/02/2025 15:31
Copyright: (c) Ampere Consultoria Ltda
Projeto Original: sagace-v2-package
IDE: PyCharm
"""

import requests
from ..domain import AuthenticationRepository
from ...core import TokenStorage, Token
from ...exceptions.domain import APIRequestError
from ...exceptions.infrastructure import AuthenticationFailedError


[docs] class AuthenticationAPI(AuthenticationRepository): """ Implementação da autenticação via API externa. Esta classe implementa `AuthenticationRepository`, garantindo que qualquer mudança na forma de autenticação possa ser feita sem impactar os consumidores desta interface. Princípios utilizados: - **Dependency Inversion (D - SOLID)**: Utiliza uma abstração (`AuthenticationRepository`) para evitar dependências diretas. - **Single Responsibility (S - SOLID)**: Responsável exclusivamente por autenticação via API. - **Clean Architecture**: Implementação no nível de infraestrutura, garantindo separação entre domínio e serviços externos. """ BASE_URL = "https://demo.sagace.online/" AUTH_URL = "auth/base/login/"
[docs] def authenticate(self, base_url: str, username: str, password: str, token: str, token_storage: TokenStorage) -> Token: """ Autentica um usuário via API e retorna um token JWT. :param base_url: URL base da API de autenticação. :type base_url: str :param username: Nome de usuário para autenticação. :type username: str :param password: Senha do usuário. :type password: str :param token: Token da aplicação para autenticação. :type token: str :param token_storage: Instância responsável por armazenar o token JWT. :type token_storage: TokenStorage :return: Token JWT retornado pela API. :rtype: Token :raises APIRequestError: Se a API retornar um erro na requisição. :raises AuthenticationFailedError: Se os dados esperados não estiverem presentes na resposta. """ # Constrói a URL completa garantindo que a barra não seja duplicada ou omitida full_url = f"{base_url.rstrip('/')}/{self.AUTH_URL.lstrip('/')}" # Envia uma requisição HTTP POST para a API de autenticação response = requests.post( url=full_url, json={"username": username, "password": password}, headers={"Authorization": token} ) try: # Garante que a resposta HTTP não contenha erros (4xx ou 5xx) response.raise_for_status() except Exception: # Lança uma exceção caso a requisição falhe raise APIRequestError(response.status_code, response.text) data = response.json() # Verifica se a chave 'data' está presente na resposta if 'data' not in data: raise AuthenticationFailedError() # Garante que os campos essenciais existam antes de criar o token required_fields = ['ds_application_name', 'ds_description', 'authorization_token'] if not all(field in data['data'] for field in required_fields): raise AuthenticationFailedError() # Cria um objeto Token com os dados retornados pela API token = Token( base_url=base_url, application_name=data['data']["ds_application_name"], description=data['data']["ds_description"], access_token=data['data']["authorization_token"] ) # Salva o token no armazenamento definido token_storage.save_token(token) return token