Exemplo n.º 1
0
    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))
Exemplo n.º 2
0
def confirm_otp_token(login_manager, otp=None, tmp_id=None):
    """Confirm otp matches."""
    from frappe.auth import get_login_attempt_tracker

    if not otp:
        otp = frappe.form_dict.get("otp")
    if not otp:
        if two_factor_is_enabled_for_(login_manager.user):
            return False
        return True
    if not tmp_id:
        tmp_id = frappe.form_dict.get("tmp_id")
    hotp_token = frappe.cache().get(tmp_id + "_token")
    otp_secret = frappe.cache().get(tmp_id + "_otp_secret")
    if not otp_secret:
        raise ExpiredLoginException(
            _("Login session expired, refresh page to retry"))

    tracker = get_login_attempt_tracker(login_manager.user)

    hotp = pyotp.HOTP(otp_secret)
    if hotp_token:
        if hotp.verify(otp, int(hotp_token)):
            frappe.cache().delete(tmp_id + "_token")
            tracker.add_success_attempt()
            return True
        else:
            tracker.add_failure_attempt()
            login_manager.fail(_("Incorrect Verification code"),
                               login_manager.user)

    totp = pyotp.TOTP(otp_secret)
    if totp.verify(otp):
        # show qr code only once
        if not frappe.db.get_default(login_manager.user + "_otplogin"):
            frappe.db.set_default(login_manager.user + "_otplogin", 1)
            delete_qrimage(login_manager.user)
        tracker.add_success_attempt()
        return True
    else:
        tracker.add_failure_attempt()
        login_manager.fail(_("Incorrect Verification code"),
                           login_manager.user)