From 2afd27b4a404b3808b2a8057c80918c12c9360aa Mon Sep 17 00:00:00 2001 From: Romain Bignon Date: Sun, 8 Mar 2015 11:02:23 +0100 Subject: [PATCH] use StateBrowser, s/BrowserToBeContinued/BrowserQuestion/ and coding style fixes --- modules/boursorama/browser.py | 39 ++++++------- modules/boursorama/module.py | 17 +----- .../boursorama/pages/two_authentication.py | 58 ++++++++++--------- weboob/exceptions.py | 13 +++-- weboob/tools/application/console.py | 14 ++--- 5 files changed, 64 insertions(+), 77 deletions(-) diff --git a/modules/boursorama/browser.py b/modules/boursorama/browser.py index 2b25128f98..bf0ac5b3e1 100644 --- a/modules/boursorama/browser.py +++ b/modules/boursorama/browser.py @@ -23,7 +23,7 @@ import re from collections import defaultdict -from weboob.deprecated.browser import Browser, BrowserIncorrectPassword +from weboob.deprecated.browser import StateBrowser, BrowserIncorrectPassword from weboob.capabilities.bank import Account from .pages import (LoginPage, AccountsList, AccountHistory, CardHistory, UpdateInfoPage, @@ -37,33 +37,31 @@ class BrowserIncorrectAuthenticationCode(BrowserIncorrectPassword): pass -class Boursorama(Browser): +class Boursorama(StateBrowser): DOMAIN = 'www.boursorama.com' PROTOCOL = 'https' CERTHASH = ['6bdf8b6dd177bd417ddcb1cfb818ede153288e44115eb269f2ddd458c8461039', 'b290ef629c88f0508e9cc6305421c173bd4291175e3ddedbee05ee666b34c20e'] ENCODING = None # refer to the HTML encoding - PAGES = { - '.*/connexion/securisation.*': AuthenticationPage, - '.*connexion.phtml.*': LoginPage, - '.*/comptes/synthese.phtml': AccountsList, - '.*/comptes/banque/detail/mouvements.phtml.*': AccountHistory, - '.*/comptes/banque/cartes/mouvements.phtml.*': CardHistory, - '.*/comptes/epargne/mouvements.phtml.*': AccountHistory, - '.*/date_anniversaire.phtml.*': UpdateInfoPage, - '.*/detail.phtml.*': AccountInvestment, - '.*/opcvm.phtml.*': InvestmentDetail + PAGES = {r'.*/connexion/securisation.*': AuthenticationPage, + r'.*connexion.phtml.*': LoginPage, + r'.*/comptes/synthese.phtml': AccountsList, + r'.*/comptes/banque/detail/mouvements.phtml.*': AccountHistory, + r'.*/comptes/banque/cartes/mouvements.phtml.*': CardHistory, + r'.*/comptes/epargne/mouvements.phtml.*': AccountHistory, + r'.*/date_anniversaire.phtml.*': UpdateInfoPage, + r'.*/detail.phtml.*': AccountInvestment, + r'.*/opcvm.phtml.*': InvestmentDetail } - __states__ = [] + __states__ = ('auth_token',) def __init__(self, config=None, *args, **kwargs): self.config = config self.auth_token = None - kwargs['get_home'] = False kwargs['username'] = self.config['login'].get() kwargs['password'] = self.config['password'].get() - Browser.__init__(self, *args, **kwargs) + StateBrowser.__init__(self, *args, **kwargs) def home(self): if not self.is_logged(): @@ -77,10 +75,7 @@ def is_logged(self): def handle_authentication(self): if self.is_on_page(AuthenticationPage): if self.config['enable_twofactors'].get(): - if not self.config['pin_code'].get() or not self.auth_token: - self.page.send_sms() - else: - self.page.authenticate() + self.page.send_sms() else: raise BrowserIncorrectAuthenticationCode( """Boursorama - activate the two factor authentication in boursorama config.""" @@ -92,8 +87,8 @@ def login(self): assert isinstance(self.config['enable_twofactors'].get(), bool) assert self.password.isdigit() - if self.is_on_page(AuthenticationPage): - self.handle_authentication() + if self.auth_token and self.config['pin_code'].get(): + AuthenticationPage.authenticate(self) else: if not self.is_on_page(LoginPage): self.location('https://' + self.DOMAIN + '/connexion.phtml', no_login=True) @@ -112,7 +107,7 @@ def login(self): #if the login was correct but authentication code failed, #we need to verify if bourso redirect us to login page or authentication page if self.is_on_page(LoginPage): - raise BrowserIncorrectAuthenticationCode() + raise BrowserIncorrectAuthenticationCode('Invalid PIN code') def get_accounts_list(self): if self.is_on_page(AuthenticationPage): diff --git a/modules/boursorama/module.py b/modules/boursorama/module.py index f3364cf546..f04e5ef0c6 100644 --- a/modules/boursorama/module.py +++ b/modules/boursorama/module.py @@ -49,8 +49,7 @@ def create_default_browser(self): return self.create_browser(self.config) def iter_accounts(self): - for account in self.browser.get_accounts_list(): - yield account + return self.browser.get_accounts_list() def get_account(self, _id): with self.browser: @@ -61,17 +60,7 @@ def get_account(self, _id): raise AccountNotFound() def iter_history(self, account): - with self.browser: - for history in self.browser.get_history(account): - yield history + return self.browser.get_history(account) def iter_investment(self, account): - with self.browser: - for investment in self.browser.get_investment(account): - yield investment - - # TODO - #def iter_coming(self, account): - # with self.browser: - # for coming in self.browser.get_coming_operations(account): - # yield coming + return self.browser.get_investment(account) diff --git a/modules/boursorama/pages/two_authentication.py b/modules/boursorama/pages/two_authentication.py index 69a1b5ee16..9616331a4a 100644 --- a/modules/boursorama/pages/two_authentication.py +++ b/modules/boursorama/pages/two_authentication.py @@ -20,8 +20,10 @@ import re import urllib2 -from weboob.exceptions import BrowserToBeContinued +from weboob.exceptions import BrowserQuestion from weboob.deprecated.browser import Page, BrowserIncorrectPassword +from weboob.tools.value import Value + class BrowserAuthenticationCodeMaxLimit(BrowserIncorrectPassword): pass @@ -34,36 +36,38 @@ class AuthenticationPage(Page): REFERER = SECURE_PAGE headers = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows " - "NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8" - " GTB7.1 (.NET CLR 3.5.30729)", - "Referer": REFERER, - } + "NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8" + " GTB7.1 (.NET CLR 3.5.30729)", + "Referer": REFERER, + } headers_ajax = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows " - "NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8" - " GTB7.1 (.NET CLR 3.5.30729)", - "Accept": "application/json", - "X-Requested-With": "XMLHttpRequest", - "X-Request": "JSON", - "X-Brs-Xhr-Request": "true", - "X-Brs-Xhr-Schema": "DATA+OUT", - "Referer": REFERER, - } + "NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8" + " GTB7.1 (.NET CLR 3.5.30729)", + "Accept": "application/json", + "X-Requested-With": "XMLHttpRequest", + "X-Request": "JSON", + "X-Brs-Xhr-Request": "true", + "X-Brs-Xhr-Schema": "DATA+OUT", + "Referer": REFERER, + } def on_loaded(self): pass - def authenticate(self): - url = "https://" + self.browser.DOMAIN + "/ajax/banque/otp.phtml" - data = "authentificationforteToken=%s&authentificationforteStep=otp&alertType=10100&org=%s&otp=%s&validate=" % (self.browser.auth_token, self.REFERER, self.browser.config['pin_code'].get()) - req = urllib2.Request(url, data, self.headers_ajax) - response = self.browser.open(req) + @classmethod + def authenticate(cls, browser): + browser.logger.info('Using the PIN Code %s to login', browser.auth_token) + url = "https://" + browser.DOMAIN + "/ajax/banque/otp.phtml" + data = "authentificationforteToken=%s&authentificationforteStep=otp&alertType=10100&org=%s&otp=%s&validate=" % (browser.auth_token, cls.REFERER, browser.config['pin_code'].get()) + req = urllib2.Request(url, data, cls.headers_ajax) + browser.open(req) - url = "%s?" % (self.SECURE_PAGE) - data = "org=/&device=%s" % (self.browser.config['device'].get()) - req = urllib2.Request(url, data, headers=self.headers) - response = self.browser.open(req) - self.browser.auth_token = None + url = "%s?" % (cls.SECURE_PAGE) + data = "org=/&device=%s" % (browser.config['device'].get()) + req = urllib2.Request(url, data, headers=cls.headers) + browser.open(req) + browser.auth_token = None def send_sms(self): """This function simulates the registration of a device on @@ -71,7 +75,6 @@ def send_sms(self): I @param device device name to register @exception BrowserAuthenticationCodeMaxLimit when daily limit is consumed - @exception BrowserIncorrectAuthenticationCode when code is not correct """ url = "https://%s/ajax/banque/otp.phtml?org=%s&alertType=10100" % (self.browser.DOMAIN, self.REFERER) req = urllib2.Request(url, headers=self.headers_ajax) @@ -82,8 +85,7 @@ def send_sms(self): regex = re.compile(self.MAX_LIMIT) r = regex.search(info) if r: - self.logger.info("Boursorama - Vous avez atteint le nombre maximum d'utilisation de l'authentification forte") - raise BrowserAuthenticationCodeMaxLimit() + raise BrowserAuthenticationCodeMaxLimit("Vous avez atteint le nombre maximum d'utilisation de l'authentification forte") regex = re.compile(r"name=\\\"authentificationforteToken\\\" " r"value=\\\"(?P\w*?)\\\"") @@ -95,4 +97,4 @@ def send_sms(self): data = "authentificationforteToken=%s&authentificationforteStep=start&alertType=10100&org=%s&validate=" % (self.browser.auth_token, self.REFERER) req = urllib2.Request(url, data, self.headers_ajax) response = self.browser.open(req) - raise BrowserToBeContinued('pin_code') + raise BrowserQuestion(Value('pin_code', label='Enter the PIN Code')) diff --git a/weboob/exceptions.py b/weboob/exceptions.py index bca509cf18..12e90904e0 100644 --- a/weboob/exceptions.py +++ b/weboob/exceptions.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with weboob. If not, see . -from weboob.tools.value import Value + class BrowserIncorrectPassword(Exception): pass @@ -39,11 +39,12 @@ class BrowserUnavailable(Exception): pass -class BrowserToBeContinued(BrowserUnavailable): - def __init__(self, *args): - self.fields = [] - for arg in args: - self.fields.append(Value(label=arg)) +class BrowserQuestion(BrowserIncorrectPassword): + """ + When raised by a browser, + """ + def __init__(self, *fields): + self.fields = fields class BrowserHTTPNotFound(BrowserUnavailable): diff --git a/weboob/tools/application/console.py b/weboob/tools/application/console.py index ef9292b052..14dcdc8ff3 100644 --- a/weboob/tools/application/console.py +++ b/weboob/tools/application/console.py @@ -32,7 +32,7 @@ from weboob.core.backendscfg import BackendAlreadyExists from weboob.core.modules import ModuleLoadError from weboob.core.repositories import ModuleInstallError, IProgress -from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword, BrowserForbidden, BrowserSSLError, BrowserToBeContinued +from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword, BrowserForbidden, BrowserSSLError, BrowserQuestion from weboob.tools.value import Value, ValueBool, ValueFloat, ValueInt, ValueBackendPassword from weboob.tools.misc import to_unicode from weboob.tools.ordereddict import OrderedDict @@ -547,7 +547,12 @@ def bcall_error_handler(self, backend, error, backtrace): This method can be overrided to support more exceptions types. """ - if isinstance(error, BrowserIncorrectPassword): + if isinstance(error, BrowserQuestion): + for field in error.fields: + v = self.ask(field) + if v: + backend.config[field.id].set(v) + elif isinstance(error, BrowserIncorrectPassword): msg = unicode(error) if not msg: msg = 'invalid login/password.' @@ -560,11 +565,6 @@ def bcall_error_handler(self, backend, error, backtrace): print(u'FATAL(%s): ' % backend.name + self.BOLD + '/!\ SERVER CERTIFICATE IS INVALID /!\\' + self.NC, file=self.stderr) elif isinstance(error, BrowserForbidden): print(u'Error(%s): %s' % (backend.name, msg or 'Forbidden'), file=self.stderr) - elif isinstance(error, BrowserToBeContinued): - for field in error.fields: - v = self.ask(field) - if v: - backend.config[field.label].set(v) elif isinstance(error, BrowserUnavailable): msg = unicode(error) if not msg: -- GitLab