def attachSession(self, session: requests.Session) -> None: """Gives a Session superpowers This will decorate the get(), post() and request() methods of the session, in order to transparently authenticate with WebAuth when presented with one. For example, you can use s.get("https://checkmate.ics.uci.edu") and you will get the Checkmate page. WebAuthBot will deal with the login redirect transparently, without you noticing. If you try something like s.get("https://login.uci.edu/ucinetid/webauth?return_url=http%3A%2F%2Fcheckmate.ics.uci.edu"), you will also get the Checkmate page as the response, magically. To disable the WebAuthBot augmentations, set the `eee` attribute to False. Args: session: A Session object """ session.eee = True eeeobj = self def session_request(self, method, url, **kwargs): if not self.eee: # WebAuthBot augs disabled, pass through return requests.Session.request(self, method, url, **kwargs) else: response = requests.Session.request(self, method, url, **kwargs) parsed = urllib.parse.urlparse(response.url) if response.url.startswith(WEBAUTH_ENDPOINT): # Needs authentication needsAuth = response.url elif response.url.startswith(SHIBIDP_SAML_REDIRECT): # Already authed via Shibboleth needsAuth = False url = eeeobj._handleSamlRedirect(response, self) response = requests.Session.request( self, method, url, **kwargs) elif parsed.netloc in AUTH_MARKERS and response.headers[ 'content-type'].startswith('text/html'): # Check logged-in markers soup = BeautifulSoup(response.text, 'html.parser') if soup.find(*AUTH_MARKERS[parsed.netloc]['auth']): needsAuth = False else: needsAuth = url else: # We are clear :) needsAuth = False if needsAuth: target = eeeobj.authenticate(needsAuth, self) targetres = requests.Session.request( self, method, target, **kwargs) if targetres.url.startswith(WEBAUTH_ENDPOINT): raise WebAuthLoopError return targetres else: return response session.request = types.MethodType(session_request, session)
def authenticate(self, returnUrl: str, session: requests.Session = None) -> str: """Attempts to authenticate to WebAuth Note: Although optional, it is strongly recommended to pass a Session object so that the auth cookies can be preserved. This is required for EEE, for example. Args: returnUrl: The URL of a protected resource session (optional): A Session object. If not specified, one will be created just for this call. Returns: The return URL, possibly with a auth token included in the query string. A cookie will be added to the session. """ if session is None: session = requests.Session() if returnUrl.startswith(WEBAUTH_ENDPOINT): # Looks like a WebAuth URL qs = urllib.parse.parse_qs(urllib.parse.urlparse(returnUrl).query) if 'return_url' in qs: # Get the real return URL endpoint = returnUrl returnUrl = qs['return_url'].pop() else: # Not a parsable WebAuth URL endpoint = WEBAUTH_ENDPOINT else: endpoint = WEBAUTH_ENDPOINT data = { 'referer': '', 'return_url': returnUrl, 'info_text': '', 'info_url': '', 'submit_type': '', 'ucinetid': self.__ucinetid, 'password': self.__password, 'login_button': 'Login' # Surprisingly, this is required } params = {'return_url': returnUrl} session.eee = False r = session.post(endpoint, params=params, data=data) session.eee = True if r.url.startswith(WEBAUTH_ENDPOINT): soup = BeautifulSoup(r.text, 'html.parser') redirect = soup.find('meta', {'http-equiv': 'refresh'}) if not redirect: # The attempt was unsuccessful raise WebAuthFailureError if not redirect.has_attr('content'): raise WebAuthUnknownError('Malformed <meta> tag') matches = re.match(r'^[0-9]\;url\=(.+)', redirect['content']) if not matches: raise WebAuthUnknownError( 'Malformed content attribute: {}'.format( redirect['content'])) finalUrl = matches.group(1) if finalUrl.startswith(SHIBIDP_REMOTE_ENDPOINT): # Shibboleth IDP session.eee = False idp = session.get(finalUrl) session.eee = True if not idp.url.startswith(SHIBIDP_SAML_REDIRECT): raise WebAuthUnknownError( 'Unexpected Shibboleth redirect: {}'.format(idp.url)) return self._handleSamlRedirect(idp, session) return finalUrl else: # WebAuth is not supposed to do that # Don't know what to do - Let's bail raise WebAuthUnknownError