diff --git a/modules/boursorama/browser.py b/modules/boursorama/browser.py index 787f8481..2b25128f 100644 --- a/modules/boursorama/browser.py +++ b/modules/boursorama/browser.py @@ -44,7 +44,7 @@ class Boursorama(Browser): 'b290ef629c88f0508e9cc6305421c173bd4291175e3ddedbee05ee666b34c20e'] ENCODING = None # refer to the HTML encoding PAGES = { - '.*/connexion/securisation/index.phtml': AuthenticationPage, + '.*/connexion/securisation.*': AuthenticationPage, '.*connexion.phtml.*': LoginPage, '.*/comptes/synthese.phtml': AccountsList, '.*/comptes/banque/detail/mouvements.phtml.*': AccountHistory, @@ -55,10 +55,14 @@ class Boursorama(Browser): '.*/opcvm.phtml.*': InvestmentDetail } - def __init__(self, device="weboob", enable_twofactors=False, - *args, **kwargs): - self.device = device - self.enable_twofactors = enable_twofactors + __states__ = [] + + 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) def home(self): @@ -72,32 +76,36 @@ class Boursorama(Browser): def handle_authentication(self): if self.is_on_page(AuthenticationPage): - if self.enable_twofactors: - self.page.authenticate(self.device) + 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() else: raise BrowserIncorrectAuthenticationCode( """Boursorama - activate the two factor authentication in boursorama config.""" """ You will receive SMS code but are limited in request per day (around 15)""" ) - def login(self): - assert isinstance(self.device, basestring) - assert isinstance(self.enable_twofactors, bool) + assert isinstance(self.config['device'].get(), basestring) + assert isinstance(self.config['enable_twofactors'].get(), bool) assert self.password.isdigit() - if not self.is_on_page(LoginPage): - self.location('https://' + self.DOMAIN + '/connexion.phtml', no_login=True) + if self.is_on_page(AuthenticationPage): + self.handle_authentication() + else: + if not self.is_on_page(LoginPage): + self.location('https://' + self.DOMAIN + '/connexion.phtml', no_login=True) - self.page.login(self.username, self.password) + self.page.login(self.username, self.password) - if self.is_on_page(LoginPage): - raise BrowserIncorrectPassword() + if self.is_on_page(LoginPage): + raise BrowserIncorrectPassword() - #after login, we might be redirected to the two factor - #authentication page - #print "handle authentication" - self.handle_authentication() + #after login, we might be redirected to the two factor + #authentication page + self.handle_authentication() self.location('/comptes/synthese.phtml', no_login=True) @@ -107,6 +115,8 @@ class Boursorama(Browser): raise BrowserIncorrectAuthenticationCode() def get_accounts_list(self): + if self.is_on_page(AuthenticationPage): + self.login() if not self.is_on_page(AccountsList): self.location('/comptes/synthese.phtml') diff --git a/modules/boursorama/module.py b/modules/boursorama/module.py index 3c5b6678..f3364cf5 100644 --- a/modules/boursorama/module.py +++ b/modules/boursorama/module.py @@ -41,15 +41,12 @@ class BoursoramaModule(Module, CapBank): ValueBackendPassword('password', label='Mot de passe'), ValueBool('enable_twofactors', label='Send validation sms', default=False), Value('device', label='Device name', regexp='\w*', default=''), + Value('pin_code', label='Sms code', required=False), ) BROWSER = Boursorama def create_default_browser(self): - return self.create_browser( - self.config["device"].get() - , self.config["enable_twofactors"].get() - , self.config['login'].get() - , self.config['password'].get()) + return self.create_browser(self.config) def iter_accounts(self): for account in self.browser.get_accounts_list(): diff --git a/modules/boursorama/pages/two_authentication.py b/modules/boursorama/pages/two_authentication.py index 3549a5d0..69a1b5ee 100644 --- a/modules/boursorama/pages/two_authentication.py +++ b/modules/boursorama/pages/two_authentication.py @@ -17,23 +17,55 @@ # You should have received a copy of the GNU Affero General Public License # along with weboob. If not, see . -from weboob.deprecated.browser import Page, BrowserIncorrectPassword -import urllib2 import re +import urllib2 +from weboob.exceptions import BrowserToBeContinued +from weboob.deprecated.browser import Page, BrowserIncorrectPassword class BrowserAuthenticationCodeMaxLimit(BrowserIncorrectPassword): pass class AuthenticationPage(Page): - MAX_LIMIT = "vous avez atteint le nombre maximum "\ + MAX_LIMIT = r"vous avez atteint le nombre maximum "\ "d'utilisation de l'authentification forte." + SECURE_PAGE = "https://www.boursorama.com/comptes/connexion/securisation/index.phtml" + 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, + } + + 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, + } def on_loaded(self): pass - def authenticate(self, device): + 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) + + 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 + + def send_sms(self): """This function simulates the registration of a device on boursorama two factor authentification web page. I @@ -41,73 +73,26 @@ class AuthenticationPage(Page): @exception BrowserAuthenticationCodeMaxLimit when daily limit is consumed @exception BrowserIncorrectAuthenticationCode when code is not correct """ - DOMAIN = self.browser.DOMAIN - SECURE_PAGE = "https://www.boursorama.com/comptes/connexion/securisation/index.phtml" - REFERER = SECURE_PAGE - - #print "Need to authenticate for device", device - #print "Domain information", DOMAIN - - url = "https://%s/ajax/banque/otp.phtml?org=%s&alertType=10100" % (DOMAIN, REFERER) - #print url - 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, - } - - 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, - } - - req = urllib2.Request(url, headers=headers_ajax) + url = "https://%s/ajax/banque/otp.phtml?org=%s&alertType=10100" % (self.browser.DOMAIN, self.REFERER) + req = urllib2.Request(url, headers=self.headers_ajax) response = self.browser.open(req) #extrat authentication token from response (in form) info = response.read() - regex = re.compile(r"vous avez atteint le nombre maximum d'utilisation de l'authentification forte.") + 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() - #print "Response from initial request,", len(info), response.info() regex = re.compile(r"name=\\\"authentificationforteToken\\\" " r"value=\\\"(?P\w*?)\\\"") r = regex.search(info) - token = r.group('value') - #print "Extracted token", token + self.browser.auth_token = r.group('value') #step2 - url = "https://" + DOMAIN + "/ajax/banque/otp.phtml" - data = "authentificationforteToken=%s&authentificationforteStep=start&alertType=10100&org=%s&validate=" % (token, REFERER) - req = urllib2.Request(url, data, headers_ajax) + url = "https://" + self.browser.DOMAIN + "/ajax/banque/otp.phtml" + 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) - #info = response.read() - #print "after asking to send token authentification" \ - # ,len(info), response.info() - - - pin = raw_input('Enter the "Boursorama Banque" access code:') - #print "Pin access code: ''%s''" % (pin) - url = "https://" + DOMAIN + "/ajax/banque/otp.phtml" - data = "authentificationforteToken=%s&authentificationforteStep=otp&alertType=10100&org=%s&otp=%s&validate=" % (token, REFERER, pin) - req = urllib2.Request(url, data, headers_ajax) - response = self.browser.open(req) - #info = response.read() - #print "after pin authentification", len(info), response.info() - - url = "%s?" % (SECURE_PAGE) - data = "org=/&device=%s" % (device) - req = urllib2.Request(url, data, headers=headers) - response = self.browser.open(req) - - #result = response.read() - #print response, "\n", response.info() + raise BrowserToBeContinued('pin_code')