Helpers to authenticate and programmatically use external websites.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

122 lines
4.1 KiB

import logging
import random
import re
from http.cookiejar import Cookie
from urllib.parse import urljoin, urlparse, parse_qs
import requests
from .utils.webclient import WebClient
from .utils.exceptions import *
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class PasteurSSO:
HEADERS = {
'User-Agent': ("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0)"
" Gecko/20100101 Firefox/104.0)")
}
SP_START = "https://connect.pasteur.fr"
IDP_ENTRY_POINT = "https://idp.pasteur.fr/idp/profile/SAML2/POST/SSO"
def __init__(self, username, password):
self.username = username
self.password = password
self.client = WebClient(headers=self.HEADERS)
def authenticate(self):
# Go to SP authentication start page and look for SAML form.
logger.info(f"SP: Starting authentication from {self.SP_START}")
self.client.get(self.SP_START)
if not self.client.find_forms(action=self.IDP_ENTRY_POINT):
logger.info("SP: SAML form not found: already logged in?")
self.verify_sp_auth()
return
# Send SAML form to the IDP entry point
logger.info(f"SP: SAML form found: submitting to IDP")
self.client.select_form(action=self.IDP_ENTRY_POINT)
self.client.submit_form()
if self.client.status_code == 400:
raise Exception("IDP: Error 400: SAML request probably expired.")
# Check if already authenticated to IDP
self.client.select_form()
if 'SAMLResponse' in self.client.form:
logger.info("IDP: Already authenticated")
else:
# Perform authentication with IDP
logger.info("IDP: Authentication required.")
self.client.select_form()
self.client.submit_form()
self.client.select_form()
self.client.form['j_username'] = self.username
self.client.form['j_password'] = self.password
self.client.form['_eventId_proceed'] = ""
# Make authentication persistent
self.client.form['donotcache'] = "0"
logger.info(f"IDP: Authenticating '{self.username}'.")
self.client.submit_form()
self.client.select_form()
if 'SAMLResponse' not in self.client.form:
raise Exception("IDP: Authentication failed.")
# Send IDP SAMLResponse back to SP
logger.info("IDP: Got SAMLResponse, sending to SP")
self.client.submit_form()
self.sp_after_saml()
def verify_sp_auth(self):
m = re.match(
r"https://connect.pasteur.fr/f5-w-[0-9a-f]+\$\$/connect/$",
sso.client.url
)
if not m:
raise Exception("SP: unlogged and not redirecting to IDP!")
def sp_after_saml(self):
pass
class PasteurEmail(PasteurSSO):
SP_START = "https://email.pasteur.fr"
def verify_sp_auth(self):
if self.client.url != 'https://email.pasteur.fr/owa/':
raise Exception("SP: unlogged and not redirecting to IDP!")
class PasteurSAP(PasteurSSO):
SP_START = "https://portailha.pasteur.fr"
def sp_after_saml(self):
self.client.select_form(action="/sap/bc/ui2/nwbc")
self.client.submit_form()
def verify_sp_auth(self):
pass
class PasteurEurofins(PasteurSAP):
def authenticate(self):
self.client.get("https://b2b.eurofinsgenomics.eu")
if self.client.url == "https://b2b.eurofinsgenomics.eu/":
return True
super().authenticate()
self.client.get(
f"https://portailha.pasteur.fr/sap/opu/odata/srmnxp"
f"/CATALOG_LAUNCH_DETAILS/PollDetails(LAUNCH_FROM='PUNCH_OUT',"
f"SERVICE_ID='PEUROFINS2',OBJECT_ID='442078',PRODUCTID='')/?="
f"&random={random.random()}&random={random.random()}",
headers={'Accept': "application/json"}
)
launch_data = self.client.res.json()['d']
self.client.post(
launch_data['SERVICE_URL'],
data=launch_data['FORM_DATA']
)