def test_login_with_invalid_password_doesnt_call_scrypt( self, mock_scrypt_hash): invalid_pw = 'a' * (Journalist.MAX_PASSWORD_LEN + 1) with self.assertRaises(InvalidPasswordLength): Journalist.login(self.user.username, invalid_pw, 'mocked') self.assertFalse(mock_scrypt_hash.called, "Called _scrypt_hash for password w/ invalid length")
def test_totp_reuse_protections2(self): """More granular than the preceeding test, we want to make sure the right exception is being raised in the right place.""" valid_token = self.user.totp.now() Journalist.login(self.user.username, self.user_pw, valid_token) with self.assertRaises(BadTokenException): Journalist.login(self.user.username, self.user_pw, valid_token)
def test_valid_login_calls_scrypt(self, mock_scrypt_hash, mock_valid_password): Journalist.login(self.user.username, self.user_pw, 'mocked') self.assertTrue( mock_scrypt_hash.called, "Failed to call _scrypt_hash for password w/ valid length")
def test_totp_reuse_protections2(self): """More granular than the preceeding test, we want to make sure the right exception is being raised in the right place. """ valid_token = self.user.totp.now() Journalist.login(self.user.username, self.user_pw, valid_token) with self.assertRaises(BadTokenException): Journalist.login(self.user.username, self.user_pw, valid_token)
def login(): if request.method == "POST": try: user = Journalist.login(request.form["username"], request.form["password"], request.form["token"]) except Exception as e: app.logger.error("Login for '{}' failed: {}".format(request.form["username"], e)) login_flashed_msg = "Login failed." if isinstance(e, LoginThrottledException): login_flashed_msg += " Please wait at least 60 seconds before logging in again." else: try: user = Journalist.query.filter_by(username=request.form["username"]).one() if user.is_totp: login_flashed_msg += " Please wait for a new two-factor token before logging in again." except: pass flash(login_flashed_msg, "error") else: app.logger.info( "Successful login for '{}' with token {}".format(request.form["username"], request.form["token"]) ) # Update access metadata user.last_access = datetime.utcnow() db_session.add(user) db_session.commit() session["uid"] = user.id return redirect(url_for("index")) return render_template("login.html")
def login(): if request.method == 'POST': try: user = Journalist.login(request.form['username'], request.form['password'], request.form['token']) except Exception as e: app.logger.error("Login for '{}' failed: {}".format( request.form['username'], e)) login_flashed_msg = "Login failed." if isinstance(e, LoginThrottledException): login_flashed_msg += " Please wait at least 60 seconds before logging in again." else: try: user = Journalist.query.filter_by(username=request.form['username']).one() if user.is_totp: login_flashed_msg += " Please wait for a new two-factor token before logging in again." except: pass flash(login_flashed_msg, "error") else: app.logger.info("Successful login for '{}' with token {}".format( request.form['username'], request.form['token'])) # Update access metadata user.last_access = datetime.utcnow() db_session.add(user) db_session.commit() session['uid'] = user.id return redirect(url_for('index')) return render_template("login.html")
def validate_user(username, password, token, error_message=None): """ Validates the user by calling the login and handling exceptions :param username: Username :param password: Password :param token: Two-factor authentication token :param error_message: Localized error message string to use on failure :return: Journalist user object if successful, None otherwise. """ try: return Journalist.login(username, password, token) except (InvalidUsernameException, BadTokenException, WrongPasswordException, LoginThrottledException) as e: current_app.logger.error("Login for '{}' failed: {}".format( username, e)) if not error_message: error_message = gettext('Login failed.') login_flashed_msg = error_message if isinstance(e, LoginThrottledException): login_flashed_msg += " " period = Journalist._LOGIN_ATTEMPT_PERIOD # ngettext is needed although we always have period > 1 # see https://github.com/freedomofpress/securedrop/issues/2422 login_flashed_msg += ngettext( "Please wait at least {seconds} second " "before logging in again.", "Please wait at least {seconds} seconds " "before logging in again.", period).format(seconds=period) else: try: user = Journalist.query.filter_by( username=username).one() if user.is_totp: login_flashed_msg += " " login_flashed_msg += gettext( "Please wait for a new two-factor token" " before trying again.") except: pass flash(login_flashed_msg, "error") return None
def setUp(self): common.shared_setup() # Patch the two-factor verification so it always succeeds patcher = patch('db.Journalist.verify_token') self.addCleanup(patcher.stop) self.mock_journalist_verify_token = patcher.start() self.mock_journalist_verify_token.return_value = True self.username = "******" self.password = "******" self.user = Journalist(username=self.username, password=self.password) db_session.add(self.user) db_session.commit() # Use a patched login function to avoid dealing with two-factor tokens # (which are being ignored here anyway) self.login = lambda username, password: \ Journalist.login(username, password, "")
def validate_user(username, password, token, error_message=None): """ Validates the user by calling the login and handling exceptions :param username: Username :param password: Password :param token: Two-factor authentication token :param error_message: Localized error message string to use on failure :return: Journalist user object if successful, None otherwise. """ try: return Journalist.login(username, password, token) except (InvalidUsernameException, BadTokenException, WrongPasswordException, LoginThrottledException) as e: current_app.logger.error("Login for '{}' failed: {}".format( username, e)) if not error_message: error_message = gettext('Login failed.') login_flashed_msg = error_message if isinstance(e, LoginThrottledException): login_flashed_msg += " " period = Journalist._LOGIN_ATTEMPT_PERIOD # ngettext is needed although we always have period > 1 # see https://github.com/freedomofpress/securedrop/issues/2422 login_flashed_msg += ngettext( "Please wait at least {seconds} second " "before logging in again.", "Please wait at least {seconds} seconds " "before logging in again.", period).format(seconds=period) else: try: user = Journalist.query.filter_by(username=username).one() if user.is_totp: login_flashed_msg += " " login_flashed_msg += gettext( "Please wait for a new two-factor token" " before trying again.") except: pass flash(login_flashed_msg, "error") return None
def setUp(self): common.shared_setup() # Patch the two-factor verification so it always succeeds patcher = patch('db.Journalist.verify_token') self.addCleanup(patcher.stop) self.mock_journalist_verify_token = patcher.start() self.mock_journalist_verify_token.return_value = True self.username = "******" self.password = "******" self.user = Journalist( username=self.username, password=self.password) db_session.add(self.user) db_session.commit() # Use a patched login function to avoid dealing with two-factor tokens # (which are being ignored here anyway) self.login = lambda username, password: \ Journalist.login(username, password, "")