diff --git a/modules/creditmutuel/browser.py b/modules/creditmutuel/browser.py index e3f199da58d60f64de7d2c6cb6078e6d65159248..8e77bdaa75143efa44c1e31f9628dc09ebf53e41 100644 --- a/modules/creditmutuel/browser.py +++ b/modules/creditmutuel/browser.py @@ -59,7 +59,7 @@ ConditionsPage, MobileConfirmationPage, UselessPage, DecoupledStatePage, CancelDecoupled, OtpValidationPage, OtpBlockedErrorPage, TwoFAUnabledPage, LoansOperationsPage, OutagePage, PorInvestmentsPage, PorHistoryPage, PorHistoryDetailsPage, - PorMarketOrdersPage, PorMarketOrderDetailsPage, + PorMarketOrdersPage, PorMarketOrderDetailsPage, SafeTransPage, ) @@ -86,6 +86,7 @@ class CreditMutuelBrowser(TwoFactorBrowser): outage_page = URL(r'/fr/outage.html', OutagePage) twofa_unabled_page = URL(r'/(?P.*)fr/banque/validation.aspx', TwoFAUnabledPage) mobile_confirmation = URL(r'/(?P.*)fr/banque/validation.aspx', MobileConfirmationPage) + safetrans_page = URL(r'/(?P.*)fr/banque/validation.aspx', SafeTransPage) decoupled_state = URL(r'/fr/banque/async/otp/SOSD_OTP_GetTransactionState.htm', DecoupledStatePage) cancel_decoupled = URL(r'/fr/banque/async/otp/SOSD_OTP_CancelTransaction.htm', CancelDecoupled) otp_validation_page = URL(r'/(?P.*)fr/banque/validation.aspx', OtpValidationPage) @@ -374,6 +375,10 @@ def check_auth_methods(self): assert self.polling_data, "Can't proceed to polling if no polling_data" raise AppValidation(self.page.get_validation_msg()) + if self.safetrans_page.is_here(): + msg = self.page.get_safetrans_message() + raise AuthMethodNotImplemented(msg) + if self.otp_validation_page.is_here(): self.otp_data = self.page.get_otp_data() assert self.otp_data, "Can't proceed to SMS handling if no otp_data" diff --git a/modules/creditmutuel/pages.py b/modules/creditmutuel/pages.py index f07250ba2af96fe85fb1b244d55ec08bc67d46e5..0e84be78091421b762fa90fc2d6455b8133288bf 100644 --- a/modules/creditmutuel/pages.py +++ b/modules/creditmutuel/pages.py @@ -193,6 +193,23 @@ def check_bypass(self): self.logger.warning('This connexion cannot bypass mobile confirmation') +# PartialHTMLPage: this page shares URL with other pages, +# that might be empty of text while used in a redirection +class SafeTransPage(PartialHTMLPage, AppValidationPage): + # only 'class' and cryptic 'id' tags on this page + # so we scrape based on text, not tags + def is_here(self): + return ( + 'Authentification forte' in CleanText('//p[contains(@id, "title")]')(self.doc) + and CleanText('//*[contains(text(), "confirmer votre connexion avec Safetrans")]')(self.doc) + ) + + def get_safetrans_message(self): + return CleanText( + '//*[contains(text(), "Confirmation Mobile") or contains(text(), "confirmer votre connexion avec Safetrans")]' + )(self.doc) + + class TwoFAUnabledPage(PartialHTMLPage): def is_here(self): return self.doc.xpath('//*[contains(text(), "aucun moyen pour confirmer")]')