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