From ce363e2646f7968ac22002b4b403cd77213333b3 Mon Sep 17 00:00:00 2001 From: bain Date: Wed, 27 Mar 2024 19:54:45 +0100 Subject: [PATCH] fix tying issues I had to make some asserts in the _sso_redirect function. The other options would be to add explicit error handling, or ignoring the type. In all cases the function would throw an exception with which we can't do anything, so I'd say this is the easiest solution. --- pronotepy/clients.py | 19 ++++++++++++-- pronotepy/ent/generic_func.py | 47 +++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/pronotepy/clients.py b/pronotepy/clients.py index 8dcc884..056d6c2 100644 --- a/pronotepy/clients.py +++ b/pronotepy/clients.py @@ -1,7 +1,16 @@ import datetime import logging from time import time -from typing import List, Callable, Optional, Union, TypeVar, Type, TYPE_CHECKING, Tuple +from typing import ( + List, + Callable, + Optional, + Union, + TypeVar, + Type, + TYPE_CHECKING, + Tuple, +) from Crypto.Hash import SHA256 from uuid import uuid4 @@ -21,6 +30,12 @@ if TYPE_CHECKING: from requests.cookies import RequestsCookieJar + from typing_extensions import Protocol + + class ENTFunction(Protocol): + def __call__(self, u: str, p: str, **kwargs: str) -> RequestsCookieJar: + ... + __all__ = ("ClientBase", "Client", "ParentClient", "VieScolaireClient") @@ -53,7 +68,7 @@ def __init__( pronote_url: str, username: str = "", password: str = "", - ent: Optional[Callable[[str, str], "RequestsCookieJar"]] = None, + ent: Optional["ENTFunction"] = None, mode: str = "normal", uuid: str = "", ) -> None: diff --git a/pronotepy/ent/generic_func.py b/pronotepy/ent/generic_func.py index 99d7317..57c594a 100644 --- a/pronotepy/ent/generic_func.py +++ b/pronotepy/ent/generic_func.py @@ -2,7 +2,7 @@ import typing import requests -from bs4 import BeautifulSoup +from bs4 import BeautifulSoup, Tag from urllib.parse import urljoin, urlparse, urlunparse from ..exceptions import * @@ -21,21 +21,30 @@ def _sso_redirect( saml_type: str, # SAMLRequest or SAMLResponse request_url: str = "", request_payload: dict = {}, -) -> requests.Response: +) -> typing.Optional[requests.Response]: soup = BeautifulSoup(response.text, "html.parser") + saml = soup.find("input", {"name": saml_type}) if not saml and response.status_code == 200 and request_url != response.url: # manual redirect response = session.post(response.url, headers=HEADERS, data=request_payload) soup = BeautifulSoup(response.text, "html.parser") saml = soup.find("input", {"name": saml_type}) + + assert isinstance(saml, Tag) + if not saml: return None - payload = {saml_type: saml["value"]} + + payload = {saml_type: saml.get("value")} + relay_state = soup.find("input", {"name": "RelayState"}) + assert isinstance(relay_state, Tag) if relay_state: payload["RelayState"] = relay_state["value"] - url = soup.find("form")["action"] + + url: str = soup.find("form")["action"] # type: ignore + return session.post(url, headers=HEADERS, data=payload) @@ -46,7 +55,7 @@ def _educonnect( password: str, url: str, exceptions: bool = True, - **opts, + **opts: str, ) -> typing.Optional[requests.Response]: """ Generic function for EduConnect @@ -85,7 +94,11 @@ def _educonnect( @typing.no_type_check def _cas_edu( - username: str, password: str, url: str = "", redirect_form: bool = True, **opts + username: str, + password: str, + url: str = "", + redirect_form: bool = True, + **opts: str, ) -> requests.cookies.RequestsCookieJar: """ Generic function for CAS with Educonnect @@ -127,7 +140,7 @@ def _cas_edu( @typing.no_type_check def _cas( - username: str, password: str, url: str = "", **opts + username: str, password: str, url: str = "", **opts: str ) -> requests.cookies.RequestsCookieJar: """ Generic function for CAS @@ -175,7 +188,7 @@ def _cas( def _open_ent_ng( - username: str, password: str, url: str = "", **opts + username: str, password: str, url: str = "", **opts: str ) -> requests.cookies.RequestsCookieJar: """ ENT which has an authentication like https://ent.iledefrance.fr/auth/login @@ -213,7 +226,11 @@ def _open_ent_ng( def _open_ent_ng_edu( - username: str, password: str, domain: str = "", providerId: str = "", **opts + username: str, + password: str, + domain: str = "", + providerId: str = "", + **opts: str, ) -> requests.cookies.RequestsCookieJar: """ ENT which has an authentication like https://connexion.l-educdenormandie.fr/ @@ -271,7 +288,7 @@ def _wayf( entityID: str = "", returnX: str = "", redirect_form: bool = True, - **opts, + **opts: str, ) -> requests.cookies.RequestsCookieJar: """ Generic function for WAYF @@ -331,7 +348,7 @@ def _wayf( @typing.no_type_check def _oze_ent( - username: str, password: str, url: str = "", **opts + username: str, password: str, url: str = "", **opts: str ) -> requests.cookies.RequestsCookieJar: """ Generic function for Oze ENT @@ -423,7 +440,11 @@ def _oze_ent( @typing.no_type_check def _simple_auth( - username: str, password: str, url: str = "", form_attr: dict = {}, **opts + username: str, + password: str, + url: str = "", + form_attr: dict = {}, + **opts: str, ) -> requests.cookies.RequestsCookieJar: """ Generic function for ENT with simple login form @@ -474,7 +495,7 @@ def _simple_auth( @typing.no_type_check def _hubeduconnect( - username: str, password: str, pronote_url: str = "", **opts + username: str, password: str, pronote_url: str = "", **opts: str ) -> requests.cookies.RequestsCookieJar: """ Pronote EduConnect connection (with HubEduConnect.index-education.net)