Commit 39fcaf86 authored by Stéphane Sobucki's avatar Stéphane Sobucki Committed by hydrargyrum
Browse files

[hsbc] Sturdy handle_otp by adding otp_form to states

Because relogin or ActionNeeded can happen during the handle_otp, solely
relying on the presence of the otp config value may cause some issue. We
might already have validated the otp once but we will run handle_otp
once again.

Also using locate_browser() when handling otp can lead to weird
behaviour. In our case it leads to unavailable page.

To fix this we don't use locate_browser() anymore, instead we store the
otp_form_data and otp_validation_url to use them in handle_otp. We also
use their presence in states to be sure that it's the first time we call
handle_otp.

Closes: 337@sibi
parent 7dece3e3
......@@ -161,12 +161,15 @@ def __init__(self, config, username, password, secret, *args, **kwargs):
self.AUTHENTICATION_METHODS = {
'otp': self.handle_otp,
}
self.otp_form_data = None
self.otp_validation_url = None
self.__states__ += ('otp_form_data', 'otp_validation_url',)
def load_state(self, state):
# when the otp is being handled, we want to keep the same session
if self.config['otp'].get():
return super(HSBC, self).load_state(state)
return
state.pop('url', None)
super(HSBC, self).load_state(state)
def handle_otp(self):
otp = self.config['otp'].get()
......@@ -176,7 +179,28 @@ def handle_otp(self):
# the otp once again even though we might not be on the right page anymore.
self.config['otp'].set(self.config['otp'].default)
self.page.login_with_secure_key(self.secret, otp)
if not self.otp_form_data or not self.otp_validation_url:
# An ActionNeeded can happen during handle_otp(),
# but self.otp_form_data and self.otp_form_url would have been
# set to None and the OTP would already been submitted and accepted by the server.
#
# To avoid running handle_otp a second time, we check
# if self.otp_form_data and self.otp_validation_url are present.
# If they're not, we call init_login() where the SCA won't be triggered.
self.logger.info(
"We have an OTP but we don't have the OTP form and/or the OTP validation url."
+ " Restarting the login process..."
)
return self.init_login()
self.otp_form_data['memorableAnswer'] = self.secret
self.otp_form_data['idv_OtpCredential'] = otp
self.location(self.otp_validation_url, data=self.otp_form_data) # validate the otp
# This is to make sure that we won't run handle_otp() a second time
# if an ActionNeeded occurs during handle_otp().
self.otp_form_data = self.otp_form_url = None
self.end_login()
def check_login_error(self):
......@@ -218,6 +242,10 @@ def init_login(self):
else:
self.check_login_error()
self.check_interactive()
otp_form = self.page.get_form(nr=0)
self.otp_form_data = dict(otp_form)
self.otp_validation_url = 'https://www.hsbc.fr' + otp_form.url
raise BrowserQuestion(
Value(
'otp',
......
......@@ -619,12 +619,6 @@ def login_w_secure(self, password, secret):
form['password'] = split_pass
form.submit()
def login_with_secure_key(self, secret, otp):
form = self.get_form(nr=0)
form['memorableAnswer'] = secret
form['idv_OtpCredential'] = otp
form.submit()
def useless_form(self):
form = self.get_form(nr=0)
# There is space added at the end of the url
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment