if password if expired, switch with the value of the new 'rotating_password' backend setting

This commit is contained in:
Romain Bignon 2010-10-01 21:24:22 +02:00
commit a4dbaf01c5
6 changed files with 122 additions and 6 deletions

View file

@ -22,6 +22,9 @@ from weboob.tools.backend import BaseBackend
from .browser import BNPorc from .browser import BNPorc
__all__ = ['BNPorcBackend']
class BNPorcBackend(BaseBackend, ICapBank): class BNPorcBackend(BaseBackend, ICapBank):
NAME = 'bnporc' NAME = 'bnporc'
MAINTAINER = 'Romain Bignon' MAINTAINER = 'Romain Bignon'
@ -30,12 +33,29 @@ class BNPorcBackend(BaseBackend, ICapBank):
LICENSE = 'GPLv3' LICENSE = 'GPLv3'
DESCRIPTION = 'BNP Paribas french bank\' website' DESCRIPTION = 'BNP Paribas french bank\' website'
CONFIG = {'login': BaseBackend.ConfigField(description='Account ID'), CONFIG = {'login': BaseBackend.ConfigField(description='Account ID'),
'password': BaseBackend.ConfigField(description='Password of account', is_masked=True) 'password': BaseBackend.ConfigField(description='Password of account', is_masked=True),
'rotating_password': BaseBackend.ConfigField(
description='Password to set when the allowed uses are exhausted (6 digits)',
default='',
regexp='^(\d{6}|)$'),
} }
BROWSER = BNPorc BROWSER = BNPorc
def create_default_browser(self): def create_default_browser(self):
return self.create_browser(self.config['login'], self.config['password']) if self.config['rotating_password'].isdigit() and len(self.config['rotating_password']) == 6:
rotating_password = self.config['rotating_password']
else:
rotating_password = None
return self.create_browser(self.config['login'],
self.config['password'],
password_changed_cb=self._password_changed_cb,
rotating_password=rotating_password)
def _password_changed_cb(self, old, new):
new_settings = {'password': new,
'rotating_password': old,
}
self.weboob.backends_config.edit_backend(self.name, self.NAME, new_settings)
def iter_accounts(self): def iter_accounts(self):
for account in self.browser.get_accounts_list(): for account in self.browser.get_accounts_list():

View file

@ -18,23 +18,34 @@
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
from weboob.backends.bnporc import pages from weboob.backends.bnporc import pages
from .errors import PasswordExpired
__all__ = ['BNPorc']
# Browser
class BNPorc(BaseBrowser): class BNPorc(BaseBrowser):
DOMAIN = 'www.secure.bnpparibas.net' DOMAIN = 'www.secure.bnpparibas.net'
PROTOCOL = 'https' PROTOCOL = 'https'
ENCODING = None # refer to the HTML encoding ENCODING = None # refer to the HTML encoding
PAGES = {'.*identifiant=DOSSIER_Releves_D_Operation.*': pages.AccountsList, PAGES = {'.*identifiant=DOSSIER_Releves_D_Operation.*': pages.AccountsList,
'.*SAF_ROP.*': pages.AccountHistory, '.*SAF_ROP.*': pages.AccountHistory,
'.*Action=SAF_CHM.*': pages.ChangePasswordPage,
'.*NS_AVEET.*': pages.AccountComing, '.*NS_AVEET.*': pages.AccountComing,
'.*NS_AVEDP.*': pages.AccountPrelevement, '.*NS_AVEDP.*': pages.AccountPrelevement,
'.*Action=DSP_VGLOBALE.*': pages.LoginPage, '.*Action=DSP_VGLOBALE.*': pages.LoginPage,
'.*type=homeconnex.*': pages.LoginPage, '.*type=homeconnex.*': pages.LoginPage,
'.*layout=HomeConnexion.*': pages.ConfirmPage, '.*layout=HomeConnexion.*': pages.ConfirmPage,
'.*SAF_CHM_VALID.*': pages.ConfirmPage,
} }
is_logging = False is_logging = False
def __init__(self, *args, **kwargs):
self.rotating_password = kwargs.pop('rotating_password', None)
self.password_changed_cb = kwargs.pop('password_changed_cb', None)
BaseBrowser.__init__(self, *args, **kwargs)
def home(self): def home(self):
self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/HomeConnexion?type=homeconnex') self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/HomeConnexion?type=homeconnex')
@ -57,9 +68,35 @@ class BNPorc(BaseBrowser):
raise BrowserIncorrectPassword() raise BrowserIncorrectPassword()
self.is_logging = False self.is_logging = False
def change_password(self, new_password):
assert new_password.isdigit() and len(new_password) == 6
self.location('https://www.secure.bnpparibas.net/SAF_CHM?Action=SAF_CHM')
assert self.is_on_page(pages.ChangePasswordPage)
self.page.change_password(self.password, new_password)
self.password, self.rotating_password = (new_password, self.password)
if self.password_changed_cb:
self.password_changed_cb(self.rotating_password, self.password)
def check_expired_password(func):
def inner(self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except PasswordExpired:
if self.rotating_password is not None:
self.change_password(self.rotating_password)
return func(self, *args, **kwargs)
else:
raise
return inner
@check_expired_password
def get_accounts_list(self): def get_accounts_list(self):
if not self.is_on_page(pages.AccountsList): if not self.is_on_page(pages.AccountsList):
self.location('/NSFR?Action=DSP_VGLOBALE') self.location('/NSFR?Action=DSP_VGLOBALE')
return self.page.get_list() return self.page.get_list()
def get_account(self, id): def get_account(self, id):

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2009-2010 Romain Bignon
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
__all__ = ['PasswordExpired']
class PasswordExpired(Exception):
pass

View file

@ -19,9 +19,9 @@
from .accounts_list import AccountsList from .accounts_list import AccountsList
from .account_coming import AccountComing from .account_coming import AccountComing
from .account_history import AccountHistory from .account_history import AccountHistory
from .login import LoginPage, ConfirmPage from .login import LoginPage, ConfirmPage, ChangePasswordPage
class AccountPrelevement(AccountsList): pass class AccountPrelevement(AccountsList): pass
__all__ = ['AccountsList', 'AccountComing', 'AccountHistory', 'LoginPage', __all__ = ['AccountsList', 'AccountComing', 'AccountHistory', 'LoginPage',
'ConfirmPage', 'AccountPrelevement'] 'ConfirmPage', 'AccountPrelevement', 'ChangePasswordPage']

View file

@ -21,6 +21,8 @@ import re
from weboob.capabilities.bank import Account from weboob.capabilities.bank import Account
from weboob.tools.browser import BasePage from weboob.tools.browser import BasePage
from ..errors import PasswordExpired
class AccountsList(BasePage): class AccountsList(BasePage):
LINKID_REGEXP = re.compile(".*ch4=(\w+).*") LINKID_REGEXP = re.compile(".*ch4=(\w+).*")
@ -59,4 +61,11 @@ class AccountsList(BasePage):
account.coming = float(coming) account.coming = float(coming)
l.append(account) l.append(account)
if len(l) == 0:
# oops, no accounts? check if we have not exhausted the allowed use
# of this password
for div in self.document.getroot().cssselect('div.Style_texte_gras'):
if div.text.strip() == 'Vous avez atteint la date de fin de vie de votre code secret.':
raise PasswordExpired(div.text.strip())
return l return l

View file

@ -18,10 +18,16 @@
from weboob.tools.mech import ClientForm from weboob.tools.mech import ClientForm
import sys import sys
import urllib
from logging import error
from weboob.tools.browser import BasePage from weboob.tools.browser import BasePage
from weboob.backends.bnporc.captcha import Captcha, TileError from weboob.backends.bnporc.captcha import Captcha, TileError
__all__ = ['LoginPage', 'ConfirmPage', 'ChangePasswordPage']
class LoginPage(BasePage): class LoginPage(BasePage):
def on_loaded(self): def on_loaded(self):
pass pass
@ -32,7 +38,7 @@ class LoginPage(BasePage):
try: try:
img.build_tiles() img.build_tiles()
except TileError, err: except TileError, err:
print >>sys.stderr, "Error: %s" % err error("Error: %s" % err)
if err.tile: if err.tile:
err.tile.display() err.tile.display()
@ -47,3 +53,24 @@ class LoginPage(BasePage):
class ConfirmPage(BasePage): class ConfirmPage(BasePage):
pass pass
class ChangePasswordPage(BasePage):
def change_password(self, current, new):
img = Captcha(self.browser.openurl('/NSImgGrille'))
try:
img.build_tiles()
except TileError, err:
error('Error: %s' % err)
if err.tile:
err.tile.display()
code_current = img.get_codes(current)
code_new = img.get_codes(new)
data = {'ch1': code_current,
'ch2': code_new,
'ch3': code_new
}
self.browser.location('/SAF_CHM_VALID', urllib.urlencode(data))