"""OAuth Clients""" from typing import Any, Dict, Optional from urllib.parse import urlencode from django.http import HttpRequest from requests import Session from requests.exceptions import RequestException from requests.models import Response from structlog import get_logger from passbook import __version__ from passbook.sources.oauth.models import OAuthSource LOGGER = get_logger() class BaseOAuthClient: """Base OAuth Client""" session: Session source: OAuthSource request: HttpRequest callback: Optional[str] def __init__( self, source: OAuthSource, request: HttpRequest, callback: Optional[str] = None ): self.source = source self.session = Session() self.request = request self.callback = callback self.session.headers.update({"User-Agent": f"passbook {__version__}"}) def get_access_token(self, **request_kwargs) -> Optional[Dict[str, Any]]: "Fetch access token from callback request." raise NotImplementedError("Defined in a sub-class") # pragma: no cover def get_profile_info(self, token: Dict[str, str]) -> Optional[Dict[str, Any]]: "Fetch user profile information." try: response = self.do_request("get", self.source.profile_url, token=token) response.raise_for_status() except RequestException as exc: LOGGER.warning("Unable to fetch user profile", exc=exc) return None else: return response.json() def get_redirect_args(self) -> Dict[str, str]: "Get request parameters for redirect url." raise NotImplementedError("Defined in a sub-class") # pragma: no cover def get_redirect_url(self, parameters=None): "Build authentication redirect url." args = self.get_redirect_args() additional = parameters or {} args.update(additional) params = urlencode(args) LOGGER.info("redirect args", **args) return f"{self.source.authorization_url}?{params}" def parse_raw_token(self, raw_token: str) -> Dict[str, Any]: "Parse token and secret from raw token response." raise NotImplementedError("Defined in a sub-class") # pragma: no cover def do_request(self, method: str, url: str, **kwargs) -> Response: """Wrapper around self.session.request, which can add special headers""" return self.session.request(method, url, **kwargs) @property def session_key(self) -> str: """Return Session Key""" raise NotImplementedError("Defined in a sub-class") # pragma: no cover