def handleResendSMS(self, action): """ Handle resend SMS action. """ if not action.title == _(u'Resend SMS'): return logger.debug('resend sms') data, errors = self.extractData() #if errors: # return False token = data.get('token', '') user = None username = self.request.get('auth_user', '') if username: user = api.user.get(username=username) # Validating the signed request data. If invalid (likely throttled with or expired), generate an # appropriate error message. user_data_validation_result = validate_user_data( request=self.request, user=user) if not user_data_validation_result.result: if 'Signature timestamp expired!' in user_data_validation_result.reason: # Remove used authentication code user.setMemberProperties( mapping={ 'mobile_number_authentication_code': '', }) IStatusMessage(self.request).addStatusMessage( _("Invalid data. Details: {0}").format(' '.join( user_data_validation_result.reason)), 'error') return mobile_number_authentication_code = generate_code(user) mobile_number = user.getProperty('mobile_number') # Send the SMS sms_sent = send_login_code_sms(mobile_number=mobile_number, code=mobile_number_authentication_code) if sms_sent: # Save the `signature` value to the `mobile_number_reset_token`. user.setMemberProperties( mapping={ 'mobile_number_authentication_code': mobile_number_authentication_code, }) IStatusMessage(self.request).addStatusMessage( _("Your SMS has just been resent."), 'info') self.request.response.redirect("{0}?{1}".format( self.request.ACTUAL_URL, self.request.QUERY_STRING))
def handleSubmit(self, action): if bool(api.user.is_anonymous()) is True: self.request.response.setStatus(401, _('Forbidden for anonymous'), True) return False data, errors = self.extractData() if errors: return False mobile_number = data.get('mobile_number', '') reason = None if mobile_number: try: # Set the ``enable_two_step_verification`` to True user = api.user.get_current() # Send a code to confirm the mobile number mobile_number_confirmation_code = generate_code(user) # Send the SMS send_mobile_number_setup_confirmation_code_sms( mobile_number=mobile_number, code=mobile_number_confirmation_code) user.setMemberProperties( mapping={ 'mobile_number': mobile_number, 'mobile_number_reset_code': mobile_number_confirmation_code, }) IStatusMessage(self.request).addStatusMessage( _("A confirmation SMS message has been sent to the mobile number specified." ), 'info') redirect_url = "{0}/@@setup-two-step-verification".format( self.context.absolute_url()) except Exception as e: reason = _(str(e)) else: reason = _("Invalid mobile number.") if reason is not None: IStatusMessage(self.request).addStatusMessage( _("Setup failed! {0}".format(reason)), 'error') redirect_url = "{0}/@@setup-mobile-number".format( self.context.absolute_url()) self.request.response.redirect(redirect_url)
def handleSubmit(self, action): if bool(api.user.is_anonymous()) is True: self.request.response.setStatus(401, _('Forbidden for anonymous'), True) return False data, errors = self.extractData() if errors: return False mobile_number = data.get('mobile_number', '') reason = None if mobile_number: try: # Set the ``enable_two_step_verification`` to True user = api.user.get_current() # Send a code to confirm the mobile number mobile_number_confirmation_code = generate_code(user) # Send the SMS send_mobile_number_setup_confirmation_code_sms( mobile_number = mobile_number, code = mobile_number_confirmation_code ) user.setMemberProperties( mapping = { 'mobile_number': mobile_number, 'mobile_number_reset_code': mobile_number_confirmation_code, } ) IStatusMessage(self.request).addStatusMessage( _("A confirmation SMS message has been sent to the mobile number specified."), 'info' ) redirect_url = "{0}/@@setup-two-step-verification".format(self.context.absolute_url()) except Exception as e: reason = _(str(e)) else: reason = _("Invalid mobile number.") if reason is not None: IStatusMessage(self.request).addStatusMessage(_("Setup failed! {0}".format(reason)), 'error') redirect_url = "{0}/@@setup-mobile-number".format(self.context.absolute_url()) self.request.response.redirect(redirect_url)
def authenticateCredentials(self, credentials): """ Place to actually validate the user credentials specified and return a tuple (login, login) on success or (None, None) on failure. If we find one and two-step verification is not enabled for the account, we consider the authentication passed and log the user in. If two-step verification has been enabled for the account, the first step of authentication is considered to be passed and we go to the next page (having the user and pass remembered), where we check for the token generated by the token generator (SMS Authenticator). If the token is valid too, we log the user in. """ login = credentials['login'] password = credentials['password'] if not login: return None user = api.user.get(username=login) two_step_verification_enabled = user.getProperty( 'enable_two_step_verification') if two_step_verification_enabled: # First see, if the password is correct. # We fetch the user manager plugin to chekc that. auth_plugins = self._getPAS().plugins.listPlugins( IAuthenticationPlugin) user_manager = authorized = None for plugid, authplugin in auth_plugins: if 'user' in plugid: user_manager = authplugin break if user_manager: authorized = user_manager.authenticateCredentials(credentials) # Not logged in, we want the user to be authorized before # we do our part if authorized is None: return None if is_whitelisted_client(request=self.REQUEST, user=user): return None # Setting the data in the session doesn't seem to work. That's # why we use the `ska` package. # The secret key would be then a combination of username, # secret stored in users' profile and the browser version. request = self.REQUEST response = request['RESPONSE'] response.setCookie('__ac', '', path='/') # Redirect to token thing... signed_url = sign_user_data(request=request, user=user, url='@@sms-authenticator-token') came_from_adapter = ICameFrom(request) # Appending possible `came_from`, but give it another name. came_from = came_from_adapter.getCameFrom() if came_from: signed_url = '{0}&next_url={1}'.format(signed_url, came_from) # **************************************** # Generate the login code and send the SMS mobile_number = user.getProperty('mobile_number') mobile_number_authentication_code = user.getProperty( 'mobile_number_authentication_code') # If user doesn't have yet a mobile numer specified, redirect # him to the reset mobile number view. if not mobile_number: IStatusMessage(request).addStatusMessage( _("Two-step verification is enabled for your account, but " "you haven't specified a mobile number yet! In order to" " be able to log in, you have to go through mobile " "number recovery procedure first."), 'warning') response.redirect( '@@request-mobile-number-reset/?username={0}'.format( login), lock=1) return None # Generate a code mobile_number_authentication_code = generate_code(user) # Send the SMS sms_sent = send_login_code_sms( mobile_number=mobile_number, code=mobile_number_authentication_code) if sms_sent: # Save the `signature` value to the `mobile_number_reset_token` user.setMemberProperties( mapping={ 'mobile_number_authentication_code': mobile_number_authentication_code, }) # Redirecting user to authentication code validation page. response.redirect(signed_url, lock=1) return None if credentials.get('extractor') != self.getId(): return None return None
def handleResendSMS(self, action): """ Handle resend SMS action. """ if not action.title == _(u'Resend SMS'): return logger.debug('resend sms') data, errors = self.extractData() #if errors: # return False token = data.get('token', '') user = None username = self.request.get('auth_user', '') if username: user = api.user.get(username=username) # Validating the signed request data. If invalid (likely throttled with or expired), generate an # appropriate error message. user_data_validation_result = validate_user_data(request=self.request, user=user) if not user_data_validation_result.result: if 'Signature timestamp expired!' in user_data_validation_result.reason: # Remove used authentication code user.setMemberProperties( mapping = { 'mobile_number_authentication_code': '', } ) IStatusMessage(self.request).addStatusMessage( _("Invalid data. Details: {0}").format( ' '.join(user_data_validation_result.reason) ), 'error' ) return mobile_number_authentication_code = generate_code(user) mobile_number = user.getProperty('mobile_number') # Send the SMS sms_sent = send_login_code_sms( mobile_number = mobile_number, code = mobile_number_authentication_code ) if sms_sent: # Save the `signature` value to the `mobile_number_reset_token`. user.setMemberProperties( mapping = { 'mobile_number_authentication_code': mobile_number_authentication_code, } ) IStatusMessage(self.request).addStatusMessage( _("Your SMS has just been resent."), 'info' ) self.request.response.redirect("{0}?{1}".format(self.request.ACTUAL_URL, self.request.QUERY_STRING))
def authenticateCredentials(self, credentials): """ Place to actually validate the user credentials specified and return a tuple (login, login) on success or (None, None) on failure. If we find one and two-step verification is not enabled for the account, we consider the authentication passed and log the user in. If two-step verification has been enabled for the account, the first step of authentication is considered to be passed and we go to the next page (having the user and pass remembered), where we check for the token generated by the token generator (SMS Authenticator). If the token is valid too, we log the user in. """ login = credentials['login'] password = credentials['password'] if not login: return None user = api.user.get(username=login) two_step_verification_enabled = user.getProperty( 'enable_two_step_verification') if two_step_verification_enabled: # First see, if the password is correct. # We fetch the user manager plugin to chekc that. auth_plugins = self._getPAS().plugins.listPlugins( IAuthenticationPlugin) user_manager = authorized = None for plugid, authplugin in auth_plugins: if 'user' in plugid: user_manager = authplugin break if user_manager: authorized = user_manager.authenticateCredentials(credentials) # Not logged in, we want the user to be authorized before # we do our part if authorized is None: return None if is_whitelisted_client(request=self.REQUEST, user=user): return None # Setting the data in the session doesn't seem to work. That's # why we use the `ska` package. # The secret key would be then a combination of username, # secret stored in users' profile and the browser version. request = self.REQUEST response = request['RESPONSE'] response.setCookie('__ac', '', path='/') # Redirect to token thing... signed_url = sign_user_data( request=request, user=user, url='@@sms-authenticator-token') came_from_adapter = ICameFrom(request) # Appending possible `came_from`, but give it another name. came_from = came_from_adapter.getCameFrom() if came_from: signed_url = '{0}&next_url={1}'.format(signed_url, came_from) # **************************************** # Generate the login code and send the SMS mobile_number = user.getProperty('mobile_number') mobile_number_authentication_code = user.getProperty( 'mobile_number_authentication_code') # If user doesn't have yet a mobile numer specified, redirect # him to the reset mobile number view. if not mobile_number: IStatusMessage(request).addStatusMessage( _("Two-step verification is enabled for your account, but " "you haven't specified a mobile number yet! In order to" " be able to log in, you have to go through mobile " "number recovery procedure first."), 'warning' ) response.redirect( '@@request-mobile-number-reset/?username={0}'.format( login), lock=1) return None # Generate a code mobile_number_authentication_code = generate_code(user) # Send the SMS sms_sent = send_login_code_sms( mobile_number=mobile_number, code=mobile_number_authentication_code ) if sms_sent: # Save the `signature` value to the `mobile_number_reset_token` user.setMemberProperties( mapping={ 'mobile_number_authentication_code': mobile_number_authentication_code, } ) # Redirecting user to authentication code validation page. response.redirect(signed_url, lock=1) return None if credentials.get('extractor') != self.getId(): return None return None
def handleSubmit(self, action): data, errors = self.extractData() if errors: return False username = data.get('username', '') mobile_number = data.get('mobile_number', '') user = api.user.get(username=username) reason = None if user: try: # Here we need to generate a token which is valid for let's say, 2 hours # using which it should be possible to reset the mobile number. The `signature` # generated should be saved in the user profile `mobile_number_reset_token`. ska_secret_key = get_ska_secret_key(request=self.request, user=user) # We also need to generate another token (no security, just a random string) # to sent to users' mobile number. mobile_number_reset_code = generate_code(user) token_lifetime = get_ska_token_lifetime() signature = Signature.generate_signature( auth_user = username, secret_key = ska_secret_key, lifetime = token_lifetime, extra = {'mobile_number': mobile_number} ) request_helper = RequestHelper( signature_param = 'signature', auth_user_param = 'auth_user', valid_until_param = 'valid_until' ) signed_url = request_helper.signature_to_url( signature = signature, endpoint_url = '{0}/{1}'.format(self.context.absolute_url(), '@@reset-mobile-number') ) # Send the SMS sms_sent = send_mobile_number_reset_confirmation_code_sms( mobile_number = mobile_number, code = mobile_number_reset_code ) if not sms_sent: IStatusMessage(self.request).addStatusMessage( _("An error occured while sending the SMS to the number specified."), 'info' ) redirect_url = "{0}".format(self.context.absolute_url()) self.request.response.redirect(redirect_url) return # Save the `signature` value to the `mobile_number_reset_token`. user.setMemberProperties( mapping = { 'mobile_number_reset_token': str(signature), 'mobile_number_reset_code': mobile_number_reset_code, 'mobile_number_authentication_code': '', } ) # Now we need to send an email to user with URL in and a small explanations. try: host = getToolByName(self, 'MailHost') mail_text_template = self.context.restrictedTraverse('request_mobile_number_reset_email') mail_text = mail_text_template( member = user, mobile_number_reset_url = signed_url, charset = 'utf-8' ) mail_text = mail_text.format(mobile_number_reset_url=signed_url) host.send( mail_text.encode('UTF-8'), immediate = True, msg_type = 'text/html' ) except SMTPRecipientsRefused as e: raise SMTPRecipientsRefused('Recipient address rejected by server') IStatusMessage(self.request).addStatusMessage( _("An email with further instructions for (re)setting your mobile number has been sent."), 'info' ) redirect_url = "{0}/@@reset-email-sent".format(self.context.absolute_url()) self.request.response.redirect(redirect_url) except ValueError as e: reason = _(str(e)) else: reason = _("Invalid username.") if reason is not None: IStatusMessage(self.request).addStatusMessage( _("Request for mobile number reset is failed! {0}").format(reason), 'error' )
def handleSubmit(self, action): data, errors = self.extractData() if errors: return False username = data.get('username', '') mobile_number = data.get('mobile_number', '') user = api.user.get(username=username) reason = None if user: try: # Here we need to generate a token which is valid for let's say, 2 hours # using which it should be possible to reset the mobile number. The `signature` # generated should be saved in the user profile `mobile_number_reset_token`. ska_secret_key = get_ska_secret_key(request=self.request, user=user) # We also need to generate another token (no security, just a random string) # to sent to users' mobile number. mobile_number_reset_code = generate_code(user) token_lifetime = get_ska_token_lifetime() signature = Signature.generate_signature( auth_user=username, secret_key=ska_secret_key, lifetime=token_lifetime, extra={'mobile_number': mobile_number}) request_helper = RequestHelper(signature_param='signature', auth_user_param='auth_user', valid_until_param='valid_until') signed_url = request_helper.signature_to_url( signature=signature, endpoint_url='{0}/{1}'.format(self.context.absolute_url(), '@@reset-mobile-number')) # Send the SMS sms_sent = send_mobile_number_reset_confirmation_code_sms( mobile_number=mobile_number, code=mobile_number_reset_code) if not sms_sent: IStatusMessage(self.request).addStatusMessage( _("An error occured while sending the SMS to the number specified." ), 'info') redirect_url = "{0}".format(self.context.absolute_url()) self.request.response.redirect(redirect_url) return # Save the `signature` value to the `mobile_number_reset_token`. user.setMemberProperties( mapping={ 'mobile_number_reset_token': str(signature), 'mobile_number_reset_code': mobile_number_reset_code, 'mobile_number_authentication_code': '', }) # Now we need to send an email to user with URL in and a small explanations. try: host = getToolByName(self, 'MailHost') mail_text_template = self.context.restrictedTraverse( 'request_mobile_number_reset_email') mail_text = mail_text_template( member=user, mobile_number_reset_url=signed_url, charset='utf-8') mail_text = mail_text.format( mobile_number_reset_url=signed_url) host.send(mail_text.encode('UTF-8'), immediate=True, msg_type='text/html') except SMTPRecipientsRefused as e: raise SMTPRecipientsRefused( 'Recipient address rejected by server') IStatusMessage(self.request).addStatusMessage( _("An email with further instructions for (re)setting your mobile number has been sent." ), 'info') redirect_url = "{0}/@@reset-email-sent".format( self.context.absolute_url()) self.request.response.redirect(redirect_url) except ValueError as e: reason = _(str(e)) else: reason = _("Invalid username.") if reason is not None: IStatusMessage(self.request).addStatusMessage( _("Request for mobile number reset is failed! {0}").format( reason), 'error')