def test_otp_attempt_tracker(self): """Check that OTP login attempts are tracked. """ authenticate_for_2factor(self.user) tmp_id = frappe.local.response['tmp_id'] otp = 'wrongotp' with self.assertRaises(frappe.AuthenticationError): confirm_otp_token(self.login_manager, otp=otp, tmp_id=tmp_id) with self.assertRaises(frappe.AuthenticationError): confirm_otp_token(self.login_manager, otp=otp, tmp_id=tmp_id) # REMOVE ME: current logic allows allow_consecutive_login_attempts+1 attempts # before raising security exception, remove below line when that is fixed. with self.assertRaises(frappe.AuthenticationError): confirm_otp_token(self.login_manager, otp=otp, tmp_id=tmp_id) with self.assertRaises(frappe.SecurityException): confirm_otp_token(self.login_manager, otp=otp, tmp_id=tmp_id) # Remove tracking cache so that user can try loging in again tracker = get_login_attempt_tracker(self.user, raise_locked_exception=False) tracker.add_success_attempt() otp = get_otp(self.user) self.assertTrue( confirm_otp_token(self.login_manager, otp=otp, tmp_id=tmp_id))
def test_authenticate_for_2factor(self): """Verification obj and tmp_id should be set in frappe.local.""" authenticate_for_2factor(self.user) verification_obj = frappe.local.response["verification"] tmp_id = frappe.local.response["tmp_id"] self.assertTrue(verification_obj) self.assertTrue(tmp_id) for k in ["_usr", "_pwd", "_otp_secret"]: self.assertTrue(frappe.cache().get("{0}{1}".format(tmp_id, k)), "{} not available".format(k))
def login(self): # clear cache frappe.clear_cache(user = frappe.form_dict.get('usr')) user, pwd = get_cached_user_pass() self.authenticate(user=user, pwd=pwd) if should_run_2fa(self.user): authenticate_for_2factor(self.user) if not confirm_otp_token(self): return False self.post_login()
def test_authenticate_for_2factor(self): '''Verification obj and tmp_id should be set in frappe.local.''' authenticate_for_2factor(self.user) verification_obj = frappe.local.response['verification'] tmp_id = frappe.local.response['tmp_id'] self.assertTrue(verification_obj) self.assertTrue(tmp_id) for k in ['_usr', '_pwd', '_otp_secret']: self.assertTrue(frappe.cache().get('{0}{1}'.format(tmp_id, k)), '{} not available'.format(k))
def login(self): # clear cache frappe.clear_cache(user = frappe.form_dict.get('usr')) user, pwd = get_cached_user_pass() self.authenticate(user=user, pwd=pwd) if should_run_2fa(self.user): authenticate_for_2factor(self.user) if not confirm_otp_token(self): return False self.post_login()
def test_authenticate_for_2factor(self): '''Verification obj and tmp_id should be set in frappe.local.''' authenticate_for_2factor(self.user) verification_obj = frappe.local.response['verification'] tmp_id = frappe.local.response['tmp_id'] self.assertTrue(verification_obj) self.assertTrue(tmp_id) for k in ['_usr','_pwd','_otp_secret']: self.assertTrue(frappe.cache().get('{0}{1}'.format(tmp_id,k)), '{} not available'.format(k))
def login_2fa(*args, **kwargs): if kwargs.get("data").get("login_by") == "mobile_no": raise NotImplemented("mobile_no not yet supported.") email = kwargs.get("data").get("email") authenticate_for_2factor(email) return { "message": "Verification code has been sent to your email.", "data": { "tmp_id": frappe.local.response['tmp_id'] } }
def test_confirm_otp_token(self): '''Ensure otp is confirmed''' authenticate_for_2factor(self.user) tmp_id = frappe.local.response['tmp_id'] otp = 'wrongotp' with self.assertRaises(frappe.AuthenticationError): confirm_otp_token(self.login_manager,otp=otp,tmp_id=tmp_id) otp = get_otp(self.user) self.assertTrue(confirm_otp_token(self.login_manager,otp=otp,tmp_id=tmp_id)) if frappe.flags.tests_verbose: print('Sleeping for 30secs to confirm token expires..') time.sleep(30) with self.assertRaises(frappe.AuthenticationError): confirm_otp_token(self.login_manager,otp=otp,tmp_id=tmp_id)
def test_confirm_otp_token(self): '''Ensure otp is confirmed''' authenticate_for_2factor(self.user) tmp_id = frappe.local.response['tmp_id'] otp = 'wrongotp' with self.assertRaises(frappe.AuthenticationError): confirm_otp_token(self.login_manager,otp=otp,tmp_id=tmp_id) otp = get_otp(self.user) self.assertTrue(confirm_otp_token(self.login_manager,otp=otp,tmp_id=tmp_id)) if frappe.flags.tests_verbose: print('Sleeping for 30secs to confirm token expires..') time.sleep(30) with self.assertRaises(frappe.AuthenticationError): confirm_otp_token(self.login_manager,otp=otp,tmp_id=tmp_id)
def login(self): # clear cache frappe.clear_cache(user = frappe.form_dict.get('usr')) user, pwd = get_cached_user_pass() self.authenticate(user=user, pwd=pwd) if self.force_user_to_reset_password(): doc = frappe.get_doc("User", self.user) frappe.local.response["redirect_to"] = doc.reset_password(send_email=False, password_expired=True) frappe.local.response["message"] = "Password Reset" return False if should_run_2fa(self.user): authenticate_for_2factor(self.user) if not confirm_otp_token(self): return False self.post_login()
def test_confirm_otp_token(self): """Ensure otp is confirmed""" frappe.flags.otp_expiry = 2 authenticate_for_2factor(self.user) tmp_id = frappe.local.response["tmp_id"] otp = "wrongotp" with self.assertRaises(frappe.AuthenticationError): confirm_otp_token(self.login_manager, otp=otp, tmp_id=tmp_id) otp = get_otp(self.user) self.assertTrue(confirm_otp_token(self.login_manager, otp=otp, tmp_id=tmp_id)) frappe.flags.otp_expiry = None if frappe.flags.tests_verbose: print("Sleeping for 2 secs to confirm token expires..") time.sleep(2) with self.assertRaises(ExpiredLoginException): confirm_otp_token(self.login_manager, otp=otp, tmp_id=tmp_id)
def login(): # LDAP LOGIN LOGIC args = frappe.form_dict ldap = frappe.get_doc("LDAP Settings") user = ldap.authenticate(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd)) frappe.local.login_manager.user = user.name if should_run_2fa(user.name): authenticate_for_2factor(user.name) if not confirm_otp_token(frappe.local.login_manager): return False frappe.local.login_manager.post_login() # because of a GET request! frappe.db.commit()
def login(self): # clear cache frappe.clear_cache(user = frappe.form_dict.get('usr')) user, pwd = get_cached_user_pass() self.authenticate(user=user, pwd=pwd) if should_run_2fa(self.user): authenticate_for_2factor(self.user) if not confirm_otp_token(self): return False self.post_login() def generate_key(length=40, get_bytes=os.urandom): raw_bytes = get_bytes((length + 1) // 2) hex_bytes = binascii.b2a_hex(raw_bytes)[:length] if not isinstance(hex_bytes, str): hex_bytes = hex_bytes.decode('ascii') print(hex_bytes) return hex_bytes
def initiate_pwd_reset(): email = frappe.form_dict.get('email') if email and frappe.db.exists('User', email): authenticate_for_2factor(email) phone = get_phone_no(email) if phone: tmp_id = frappe.local.response['tmp_id'] otp_secret = frappe.cache().get(tmp_id + '_otp_secret') token = frappe.cache().get(tmp_id + '_token') # Surprisingly following 2FA method is not working # status = send_token_via_sms(otp_secret, token=token, phone_no=phone) from frappe.core.doctype.sms_settings.sms_settings import send_sms hotp = pyotp.HOTP(otp_secret) msg = 'Your verification code is {}'.format(hotp.at(int(token))) send_sms([cstr(phone)], msg) frappe.db.commit() else: raise frappe.PermissionError("User does not exist")