"""Authentication and Request Helper.""" import logging import backoff from aiohttp import ClientSession, ClientResponse from pyprosegur.exceptions import BackendError, NotFound LOGGER = logging.getLogger(__name__) SMART_SERVER_WS = "https://api-smart.prosegur.cloud/smart-server/ws" COUNTRY = { "AR": { "Origin": "https://smart.prosegur.com/smart-individuo", "Referer": "https://smart.prosegur.com/smart-individuo/login.html", "origin": "Web", }, "PY": { "Origin": "https://smart.prosegur.com/smart-individuo", "Referer": "https://smart.prosegur.com/smart-individuo/login.html", "origin": "Web", }, "PT": { "Origin": "https://smart.prosegur.com/smart-individuo", "Referer": "https://smart.prosegur.com/smart-individuo/login.html", "origin": "Web", }, "ES": { "Origin": "https://alarmas.movistarproseguralarmas.es", "Referer": "https://alarmas.movistarproseguralarmas.es/smart-mv/login.html", "origin": "WebM", }, "CO": { "Origin": "https://smart.prosegur.com/smart-individuo", "Referer": "https://smart.prosegur.com/smart-individuo/login.html", "origin": "Web", }, "UY": { "Origin": "https://smart.prosegur.com/smart-individuo", "Referer": "https://smart.prosegur.com/smart-individuo/login.html", "origin": "Web", }, } class Auth: """Class to make authenticated requests.""" def __init__( self, websession: ClientSession, user: str, password: str, country: str ): """Initialize the auth.""" self.websession = websession self.user = user self.password = password self.country = country if country not in COUNTRY: raise ValueError(f"{country} not in {COUNTRY.keys()}") self.smart_token = None self.headers = { "User-Agent": "Smart/1 CFNetwork/3826.400.120 Darwin/24.3.0", #not proud, as we are starting a cat and mouse game "Accept": "application/json, text/plain, */*", "Content-Type": "application/json;charset=UTF-8", "Origin": COUNTRY[self.country]["Origin"], "Referer": COUNTRY[self.country]["Referer"], } async def login(self): """Login, retrieving Smart Token used for all requests.""" data = { "user": self.user, "password": self.password, "language": "en_GB", "origin": COUNTRY[self.country]["origin"], "platform": "smart2", "provider": None, } response = await self.websession.post( f"{SMART_SERVER_WS}/access/login", json=data, headers=self.headers, ) if response.status != 200: LOGGER.error("%s LOGIN FAILED: %s", response.status, response.reason) raise ConnectionRefusedError( f"Could not login: {response.status} {response.reason}" ) login = await response.json() self.headers["X-Smart-Token"] = login["data"]["token"] @backoff.on_exception( backoff.expo, ConnectionRefusedError, max_tries=1, logger=LOGGER ) @backoff.on_exception( backoff.expo, ConnectionError, base=5, max_tries=3, logger=LOGGER ) async def request(self, method: str, path: str, **kwargs) -> ClientResponse: """Make a request.""" if self.websession.closed: raise ConnectionError("websession with smart.prosegur is closed") headers = kwargs.get("headers") if headers is None: headers = self.headers else: headers = {**self.headers, **headers} if "X-Smart-Token" not in headers: LOGGER.debug("No X-Smart-Token, attempting login") await self.login() resp = await self.websession.request( method, f"{SMART_SERVER_WS}{path}", **kwargs, headers=headers, ) LOGGER.debug("Requesting %s %s %s", f"{SMART_SERVER_WS}{path}", kwargs, headers) if 500 <= resp.status <= 600: LOGGER.warning(resp.text) raise BackendError("Prosegur backend is unresponsive") if 404 == resp.status: raise NotFound() if 400 <= resp.status < 500: del self.headers["X-Smart-Token"] raise ConnectionRefusedError() if resp.status != 200: LOGGER.error(resp.text) raise ConnectionError( f"{resp.status} couldn't {method} {path}: {resp.text}" ) return resp