From b704fff5c1a86c2fd1a9d942b1fbef9e2c3bd606 Mon Sep 17 00:00:00 2001 From: Sylvie Ye Date: Tue, 29 Jan 2019 14:22:24 +0100 Subject: [PATCH] [societegenerale] update and reorganize new recipient code Put navigation on browser.py and page scraping on transfer.py Handle result of add new recipient with security pass --- modules/societegenerale/browser.py | 74 +++++++++++++++++------ modules/societegenerale/module.py | 2 +- modules/societegenerale/pages/transfer.py | 68 +++++++++++---------- 3 files changed, 95 insertions(+), 49 deletions(-) diff --git a/modules/societegenerale/browser.py b/modules/societegenerale/browser.py index 208cb4a2b7..505f5f8c30 100644 --- a/modules/societegenerale/browser.py +++ b/modules/societegenerale/browser.py @@ -25,10 +25,11 @@ from weboob.browser import LoginBrowser, URL, need_login, StatesMixin from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded, BrowserUnavailable -from weboob.capabilities.bank import Account, TransferBankError +from weboob.capabilities.bank import Account, TransferBankError, AddRecipientStep from weboob.capabilities.base import find_object, NotAvailable from weboob.browser.exceptions import BrowserHTTPNotFound, ClientError from weboob.capabilities.profile import ProfileMissing +from weboob.tools.value import Value, ValueBool from .pages.accounts_list import ( AccountsMainPage, AccountDetailsPage, AccountsPage, LoansPage, HistoryPage, @@ -37,7 +38,7 @@ MarketPage, LifeInsurance, LifeInsuranceHistory, LifeInsuranceInvest, LifeInsuranceInvest2, UnavailableServicePage, ) -from .pages.transfer import AddRecipientPage, RecipientJson, TransferJson, SignTransferPage +from .pages.transfer import AddRecipientPage, SignRecipientPage, TransferJson, SignTransferPage from .pages.login import MainPage, LoginPage, BadLoginPage, ReinitPasswordPage, ActionNeededPage, ErrorPage from .pages.subscription import BankStatementPage @@ -67,7 +68,7 @@ class SocieteGenerale(LoginBrowser, StatesMixin): json_recipient = URL(r'/sec/getsigninfo.json', r'/sec/csa/send.json', r'/sec/oob_sendoob.json', - r'/sec/oob_polling.json', RecipientJson) + r'/sec/oob_polling.json', SignRecipientPage) # Transfer json_transfer = URL(r'/icd/vupri/data/vupri-liste-comptes.json\?an200_isBack=false', r'/icd/vupri/data/vupri-check.json', @@ -114,6 +115,9 @@ class SocieteGenerale(LoginBrowser, StatesMixin): __states__ = ('context', 'dup', 'id_transaction') + def locate_browser(self, state): + self.location('/com/icd-web/cbo/index.html') + def load_state(self, state): if state.get('dup') is not None and state.get('context') is not None: super(SocieteGenerale, self).load_state(state) @@ -327,17 +331,26 @@ def execute_transfer(self, transfer): return transfer def end_sms_recipient(self, recipient, **params): - data = [('context', self.context), ('context', self.context), ('dup', self.dup), ('code', params['code']), ('csa_op', 'sign')] - self.add_recipient.go(data=data, headers={'Referer': self.absurl('/lgn/url.html')}) + """End adding recipient with OTP SMS authentication""" + data = [ + ('context', [self.context, self.context]), + ('dup', self.dup), + ('code', params['code']), + ('csa_op', 'sign') + ] + # needed to confirm recipient validation + add_recipient_url = self.absurl('/lgn/url.html', base=True) + self.location(add_recipient_url, data=data, headers={'Referer': add_recipient_url}) return self.page.get_recipient_object(recipient) def end_oob_recipient(self, recipient, **params): - r = self.open(self.absurl('/sec/oob_polling.json'), data={'n10_id_transaction': self.id_transaction}) - assert r.page.doc['donnees']['transaction_status'] in ('available', 'in_progress'), \ - 'transaction_status is %s' % r.page.doc['donnees']['transaction_status'] - - if r.page.doc['donnees']['transaction_status'] == 'in_progress': - raise ActionNeeded('Veuillez valider le bénéficiaire sur votre application bancaire.') + """End adding recipient with 'pass sécurité' authentication""" + r = self.open( + self.absurl('/sec/oob_polling.json'), + data={'n10_id_transaction': self.id_transaction} + ) + assert self.id_transaction, "Transaction id is missing, can't sign new recipient." + r.page.check_recipient_status() data = [ ('context', self.context), @@ -346,24 +359,51 @@ def end_oob_recipient(self, recipient, **params): ('n10_id_transaction', self.id_transaction), ('oob_op', 'sign') ] + # needed to confirm recipient validation add_recipient_url = self.absurl('/lgn/url.html', base=True) - self.location( - add_recipient_url, - data=data, - headers={'Referer': add_recipient_url} - ) + self.location(add_recipient_url, data=data, headers={'Referer': add_recipient_url}) return self.page.get_recipient_object(recipient) + def send_sms_to_user(self, recipient): + """Add recipient with OTP SMS authentication""" + data = {} + data['csa_op'] = 'sign' + data['context'] = self.context + self.open(self.absurl('/sec/csa/send.json'), data=data) + raise AddRecipientStep(recipient, Value('code', label='Cette opération doit être validée par un Code Sécurité.')) + + def send_notif_to_user(self, recipient): + """Add recipient with 'pass sécurité' authentication""" + data = {} + data['b64_jeton_transaction'] = self.context + r = self.open(self.absurl('/sec/oob_sendoob.json'), data=data) + self.id_transaction = r.page.get_transaction_id() + raise AddRecipientStep(recipient, ValueBool('pass', label='Valider cette opération sur votre applicaton société générale')) + @need_login def new_recipient(self, recipient, **params): if 'code' in params: return self.end_sms_recipient(recipient, **params) if 'pass' in params: return self.end_oob_recipient(recipient, **params) + self.add_recipient.go() self.page.post_iban(recipient) self.page.post_label(recipient) - self.page.double_auth(recipient) + + recipient = self.page.get_recipient_object(recipient, get_info=True) + self.page.update_browser_recipient_state() + data = self.page.get_signinfo_data() + + r = self.open(self.absurl('/sec/getsigninfo.json'), data=data) + sign_method = r.page.get_sign_method() + + # WARNING: this send validation request to user + if sign_method == 'CSA': + return self.send_sms_to_user(recipient) + elif sign_method == 'OOB': + return self.send_notif_to_user(recipient) + assert False, 'Sign process unknown: %s' % sign_method @need_login def get_advisor(self): diff --git a/modules/societegenerale/module.py b/modules/societegenerale/module.py index 55781dac07..14cbc60a45 100644 --- a/modules/societegenerale/module.py +++ b/modules/societegenerale/module.py @@ -100,7 +100,7 @@ def iter_transfer_recipients(self, origin_account): return self.browser.iter_recipients(origin_account) def new_recipient(self, recipient, **params): - if self.config['website'].get() not in ('pro', ): + if self.config['website'].get() not in ('par', 'pro'): raise NotImplementedError() recipient.label = ' '.join(w for w in re.sub('[^0-9a-zA-Z:\/\-\?\(\)\.,\'\+ ]+', '', recipient.label).split()) return self.browser.new_recipient(recipient, **params) diff --git a/modules/societegenerale/pages/transfer.py b/modules/societegenerale/pages/transfer.py index 8bf358d788..a202a9cb1f 100644 --- a/modules/societegenerale/pages/transfer.py +++ b/modules/societegenerale/pages/transfer.py @@ -23,7 +23,7 @@ from weboob.browser.pages import LoggedPage, JsonPage, FormNotFound from weboob.browser.elements import method, ItemElement, DictElement from weboob.capabilities.bank import ( - Recipient, Transfer, TransferBankError, AddRecipientBankError, AddRecipientStep, + Recipient, Transfer, TransferBankError, AddRecipientBankError, AddRecipientTimeout, ) from weboob.tools.capabilities.bank.iban import is_iban_valid from weboob.capabilities.base import NotAvailable @@ -32,9 +32,8 @@ ) from weboob.browser.filters.html import Link from weboob.browser.filters.json import Dict -from weboob.tools.value import Value, ValueBool from weboob.tools.json import json -from weboob.exceptions import BrowserUnavailable +from weboob.exceptions import BrowserUnavailable, ActionNeeded from .base import BasePage from .login import MainPage @@ -167,8 +166,29 @@ def get_confirm_transfer_data(self, password): } -class RecipientJson(LoggedPage, JsonPage): - pass +class SignRecipientPage(LoggedPage, JsonPage): + def on_load(self): + assert Dict('commun/statut')(self.doc).upper() == 'OK', \ + 'Something went wrong on sign recipient page: %s' % Dict('commun/raison')(self.doc) + + def get_sign_method(self): + return Dict('donnees/sign_proc')(self.doc).upper() + + def check_recipient_status(self): + transaction_status = Dict('donnees/transaction_status')(self.doc) + + # check add new recipient status + assert transaction_status in ('available', 'in_progress', 'aborted', 'rejected'), \ + 'transaction_status is %s' % transaction_status + if transaction_status == 'aborted': + raise AddRecipientTimeout() + elif transaction_status == 'rejected': + raise ActionNeeded("La demande d'ajout de bénéficiaire a été annulée.") + elif transaction_status == 'in_progress': + raise ActionNeeded('Veuillez valider le bénéficiaire sur votre application bancaire.') + + def get_transaction_id(self): + return Dict('donnees/id-transaction')(self.doc) class AddRecipientPage(LoggedPage, BasePage): @@ -201,40 +221,26 @@ def get_action_level(self): if 'actionLevel' in CleanText('.')(script): return re.search("'actionLevel': (\d{3}),", script.text).group(1) - def double_auth(self, recipient): + def get_signinfo_data_form(self): try: form = self.get_form(id='formCache') except FormNotFound: - assert False, 'Double auth form not found' + assert False, 'Transfer auth form not found' + return form + def update_browser_recipient_state(self): + form = self.get_signinfo_data_form() + # set browser variable used to continue new recipient self.browser.context = form['context'] self.browser.dup = form['dup'] self.browser.logged = 1 - getsigninfo_data = {} - getsigninfo_data['b64_jeton_transaction'] = form['context'] - getsigninfo_data['action_level'] = self.get_action_level() - r = self.browser.open('https://particuliers.secure.societegenerale.fr/sec/getsigninfo.json', data=getsigninfo_data) - assert r.page.doc['commun']['statut'] == 'ok' - - recipient = self.get_recipient_object(recipient, get_info=True) - self.browser.page = None - if r.page.doc['donnees']['sign_proc'] == 'csa': - send_data = {} - send_data['csa_op'] = 'sign' - send_data['context'] = form['context'] - r = self.browser.open('https://particuliers.secure.societegenerale.fr/sec/csa/send.json', data=send_data) - assert r.page.doc['commun']['statut'] == 'ok' - raise AddRecipientStep(recipient, Value('code', label=u'Cette opération doit être validée par un Code Sécurité.')) - elif r.page.doc['donnees']['sign_proc'] == 'OOB': - oob_data = {} - oob_data['b64_jeton_transaction'] = form['context'] - r = self.browser.open('https://particuliers.secure.societegenerale.fr/sec/oob_sendoob.json', data=oob_data) - assert r.page.doc['commun']['statut'] == 'ok' - self.browser.id_transaction = r.page.doc['donnees']['id-transaction'] - raise AddRecipientStep(recipient, ValueBool('pass', label=u'Valider cette opération sur votre applicaton société générale')) - else: - assert False, 'Sign process unknown: %s' % r.page.doc['donnees']['sign_proc'] + def get_signinfo_data(self): + form = self.get_signinfo_data_form() + signinfo_data = {} + signinfo_data['b64_jeton_transaction'] = form['context'] + signinfo_data['action_level'] = self.get_action_level() + return signinfo_data def get_recipient_object(self, recipient, get_info=False): r = Recipient() -- GitLab