コード例 #1
0
 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")
コード例 #2
0
ファイル: test_journalist.py プロジェクト: tmc/securedrop
 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")
コード例 #3
0
    def edit_user(user_id):
        user = Journalist.query.get(user_id)

        if request.method == 'POST':
            if request.form.get('username', None):
                new_username = request.form['username']

                try:
                    Journalist.check_username_acceptable(new_username)
                except InvalidUsernameException as e:
                    flash('Invalid username: '******'error')
                    return redirect(url_for("admin.edit_user",
                                            user_id=user_id))

                if new_username == user.username:
                    pass
                elif Journalist.query.filter_by(
                        username=new_username).one_or_none():
                    flash(gettext(
                        'Username "{user}" already taken.').format(
                            user=new_username),
                        "error")
                    return redirect(url_for("admin.edit_user",
                                            user_id=user_id))
                else:
                    user.username = new_username

            user.is_admin = bool(request.form.get('is_admin'))

            commit_account_changes(user)

        password = make_password(config)
        return render_template("edit_account.html", user=user,
                               password=password)
コード例 #4
0
ファイル: test_2fa.py プロジェクト: tmc/securedrop
 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)
コード例 #5
0
ファイル: utils.py プロジェクト: zyphlar/securedrop
def make_password(config):
    while True:
        password = crypto_util.genrandomid(7, i18n.get_language(config))
        try:
            Journalist.check_password_acceptable(password)
            return password
        except PasswordError:
            continue
コード例 #6
0
ファイル: manage.py プロジェクト: singuliere/securedrop
def _make_password():
    while True:
        password = crypto_util.genrandomid(7)
        try:
            Journalist.check_password_acceptable(password)
            return password
        except PasswordError:
            continue
コード例 #7
0
ファイル: test_2fa.py プロジェクト: freedomofpress/securedrop
 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)
コード例 #8
0
ファイル: manage.py プロジェクト: freedomofpress/securedrop
def _make_password():
    while True:
        password = crypto_util.genrandomid(7)
        try:
            Journalist.check_password_acceptable(password)
            return password
        except PasswordError:
            continue
コード例 #9
0
ファイル: utils.py プロジェクト: freedomofpress/securedrop
def make_password(config):
    while True:
        password = crypto_util.genrandomid(7, i18n.get_language(config))
        try:
            Journalist.check_password_acceptable(password)
            return password
        except PasswordError:
            continue
コード例 #10
0
ファイル: manage.py プロジェクト: singuliere/securedrop
def _get_username():
    while True:
        username = raw_input('Username: '******'Invalid username: ' + str(e))
        else:
            return username
コード例 #11
0
ファイル: manage.py プロジェクト: freedomofpress/securedrop
def _get_username():
    while True:
        username = raw_input('Username: '******'Invalid username: ' + str(e))
        else:
            return username
コード例 #12
0
ファイル: journalist.py プロジェクト: fowlslegs/securedrop
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")
コード例 #13
0
ファイル: journalist.py プロジェクト: pep8er/securedrop
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")
コード例 #14
0
ファイル: test_journalist.py プロジェクト: tmc/securedrop
 def test_max_password_length(self):
     """Creating a Journalist with a password that is greater than the
     maximum password length should raise an exception"""
     overly_long_password = '******' * (Journalist.MAX_PASSWORD_LEN + 1)
     with self.assertRaises(InvalidPasswordLength):
         temp_journalist = Journalist(username="******",
                                      password=overly_long_password)
コード例 #15
0
 def test_min_password_length(self):
     """Creating a Journalist with a password that is smaller than the
     minimum password length should raise an exception"""
     with self.assertRaises(InvalidPasswordLength):
         temp_journalist = Journalist(
                 username="******",
                 password='******')
コード例 #16
0
 def test_min_password_length(self):
     """Creating a Journalist with a password that is smaller than the
        minimum password length should raise an exception. This uses the
        magic number 7 below to get around the "diceware-like" requirement
        that may cause a failure before the length check.
     """
     password = ('a ' * 7)[0:(Journalist.MIN_PASSWORD_LEN - 1)]
     with self.assertRaises(InvalidPasswordLength):
         Journalist(username="******", password=password)
コード例 #17
0
    def setUp(self):
        common.shared_setup()

        # Patch the two-factor verification to avoid intermittent errors
        patcher = mock.patch('db.Journalist.verify_token')
        self.addCleanup(patcher.stop)
        self.mock_journalist_verify_token = patcher.start()
        self.mock_journalist_verify_token.return_value = True

        # Set up test users
        self.user_pw = "bar"
        self.user = Journalist(username="******", password=self.user_pw)
        self.admin_user_pw = "admin"
        self.admin_user = Journalist(username="******",
                                     password=self.admin_user_pw,
                                     is_admin=True)
        db_session.add(self.user)
        db_session.add(self.admin_user)
        db_session.commit()
コード例 #18
0
    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, "")
コード例 #19
0
ファイル: manage.py プロジェクト: utopalex/securedrop
def _add_user(is_admin=False):
    username = _get_username()

    print("Note: Journalist passwords are now autogenerated.")
    password = _make_password()
    print("This journalist's password is: {}".format(password))

    is_hotp = _get_yubikey_usage()
    otp_secret = None
    if is_hotp:
        while True:
            otp_secret = raw_input(
                'Please configure your YubiKey and enter the secret: ')
            if otp_secret:
                break

    try:
        user = Journalist(username=username,
                          password=password,
                          is_admin=is_admin,
                          otp_secret=otp_secret)
        db_session.add(user)
        db_session.commit()
    except Exception as exc:
        db_session.rollback()
        if "UNIQUE constraint failed: journalists.username" in str(exc):
            print('ERROR: That username is already taken!')
        else:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            print(
                repr(
                    traceback.format_exception(exc_type, exc_value,
                                               exc_traceback)))
        return 1
    else:
        print('User "{}" successfully added'.format(username))
        if not otp_secret:
            # Print the QR code for FreeOTP/ Google Authenticator
            print(
                '\nScan the QR code below with FreeOTP or Google '
                'Authenticator:\n')
            uri = user.totp.provisioning_uri(username,
                                             issuer_name='SecureDrop')
            qr = qrcode.QRCode()
            qr.add_data(uri)
            qr.print_ascii(tty=sys.stdout.isatty())
            print(
                '\nIf the barcode does not render correctly, try changing '
                "your terminal's font (Monospace for Linux, Menlo for OS "
                'X). If you are using iTerm on Mac OS X, you will need to '
                'change the "Non-ASCII Font", which is your profile\'s Text '
                "settings.\n\nCan't scan the barcode? Enter following "
                'shared secret '
                'manually:\n{}\n'.format(user.formatted_otp_secret))
        return 0
コード例 #20
0
    def _journalist_logs_in(self):
        # Create a test user for logging in
        test_user_info = dict(username='******', password='******')
        test_user = Journalist(**test_user_info)
        db_session.add(test_user)
        db_session.commit()

        self._login_user(test_user_info['username'],
                         test_user_info['password'], test_user.totp.now())

        headline = self.driver.find_element_by_css_selector('span.headline')
        self.assertIn('Sources', headline.text)
コード例 #21
0
def admin_add_user():
    if request.method == 'POST':
        form_valid = True

        username = request.form['username']
        if len(username) == 0:
            form_valid = False
            flash("Missing username", "error")

        password = request.form['password']
        password_again = request.form['password_again']
        if password != password_again:
            form_valid = False
            flash("Passwords didn't match", "error")

        is_admin = bool(request.form.get('is_admin'))

        if form_valid:
            try:
                otp_secret = None
                if request.form.get('is_hotp', False):
                    otp_secret = request.form.get('otp_secret', '')
                new_user = Journalist(username=username,
                                      password=password,
                                      is_admin=is_admin,
                                      otp_secret=otp_secret)
                db_session.add(new_user)
                db_session.commit()
            except InvalidPasswordLength:
                form_valid = False
                flash(
                    "Your password must be between {} and {} characters.".
                    format(Journalist.MIN_PASSWORD_LEN,
                           Journalist.MAX_PASSWORD_LEN), "error")
            except IntegrityError as e:
                db_session.rollback()
                form_valid = False
                if "UNIQUE constraint failed: journalists.username" in str(e):
                    flash("That username is already in use", "error")
                else:
                    flash(
                        "An error occurred saving this user to the database."
                        " Please check the application logs.", "error")
                    app.logger.error("Adding user '{}' failed: {}".format(
                        username, e))

        if form_valid:
            return redirect(
                url_for('admin_new_user_two_factor', uid=new_user.id))

    return render_template("admin_add_user.html")
コード例 #22
0
    def add_user():
        form = NewUserForm()
        if form.validate_on_submit():
            form_valid = True
            username = request.form['username']
            password = request.form['password']
            is_admin = bool(request.form.get('is_admin'))

            try:
                otp_secret = None
                if request.form.get('is_hotp', False):
                    otp_secret = request.form.get('otp_secret', '')
                new_user = Journalist(username=username,
                                      password=password,
                                      is_admin=is_admin,
                                      otp_secret=otp_secret)
                db_session.add(new_user)
                db_session.commit()
            except PasswordError:
                flash(gettext(
                      'There was an error with the autogenerated password. '
                      'User not created. Please try again.'), 'error')
                form_valid = False
            except InvalidUsernameException as e:
                form_valid = False
                flash('Invalid username: '******'{}' failed: {}".format(
                                                 username, e))

            if form_valid:
                return redirect(url_for('admin.new_user_two_factor',
                                        uid=new_user.id))

        return render_template("admin_add_user.html",
                               password=make_password(config),
                               form=form)
コード例 #23
0
def admin_add_user():
    if request.method == 'POST':
        form_valid = True

        username = request.form['username']
        if len(username) == 0:
            form_valid = False
            flash("Missing username", "error")

        password = request.form['password']
        password_again = request.form['password_again']
        if password != password_again:
            form_valid = False
            flash("Passwords didn't match", "error")

        is_admin = bool(request.form.get('is_admin'))

        if form_valid:
            try:
                otp_secret = None
                if request.form.get('is_hotp', False):
                    otp_secret = request.form.get('otp_secret', '')
                new_user = Journalist(username=username,
                                      password=password,
                                      is_admin=is_admin,
                                      otp_secret=otp_secret)
                db_session.add(new_user)
                db_session.commit()
            except InvalidPasswordLength:
                form_valid = False
                flash(
                    "Your password is too long (maximum length {} characters)".
                    format(Journalist.MAX_PASSWORD_LEN), "error")
            except IntegrityError as e:
                form_valid = False
                if "username is not unique" in str(e):
                    flash("That username is already in use", "error")
                else:
                    flash("An error occurred saving this user to the database",
                          "error")

        if form_valid:
            return redirect(
                url_for('admin_new_user_two_factor', uid=new_user.id))

    return render_template("admin_add_user.html")
コード例 #24
0
ファイル: utils.py プロジェクト: freedomofpress/securedrop
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
コード例 #25
0
def add_admin():
    while True:
        username = raw_input("Username: "******"Sorry, that username is already in use."
        else:
            break

    while True:
        password = getpass("Password: "******"Confirm Password: "******"Your password is too long (maximum length {} characters). "
                "Please pick a shorter password.".format(
                    Journalist.MAX_PASSWORD_LEN))
            continue

        if password == password_again:
            break
        print "Passwords didn't match!"

    hotp_input = raw_input("Is this admin using a YubiKey [HOTP]? (y/N): ")
    otp_secret = None
    if hotp_input.lower() == "y" or hotp_input.lower() == "yes":
        while True:
            otp_secret = raw_input(
                "Please configure your YubiKey and enter the secret: ")
            if otp_secret:
                break

    try:
        admin = Journalist(username=username,
                           password=password,
                           is_admin=True,
                           otp_secret=otp_secret)
        db_session.add(admin)
        db_session.commit()
    except Exception, e:
        if "username is not unique" in str(e):
            print "ERROR: That username is already taken!"
        else:
            print "ERROR: An unexpected error occurred, traceback: \n{}".format(
                e)
コード例 #26
0
ファイル: utils.py プロジェクト: zyphlar/securedrop
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
コード例 #27
0
    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, "")
コード例 #28
0
    def setUp(self):
        utils.env.setup()

        self.source_app = source.app.test_client()
        self.journalist_app = journalist.app.test_client()

        self.gpg = gnupg.GPG(homedir=config.GPG_KEY_DIR)

        # Patch the two-factor verification to avoid intermittent errors
        patcher = mock.patch('db.Journalist.verify_token')
        self.addCleanup(patcher.stop)
        self.mock_journalist_verify_token = patcher.start()
        self.mock_journalist_verify_token.return_value = True

        # Add a test user to the journalist interface and log them in
        # print Journalist.query.all()
        self.user_pw = "longpassword"
        self.user = Journalist(username="******", password=self.user_pw)
        db_session.add(self.user)
        db_session.commit()
        self._login_user()
コード例 #29
0
def add_admin():
    while True:
        username = raw_input("Username: "******"Sorry, that username is already in use."
        else:
            break

    while True:
        password = getpass("Password: "******"Confirm Password: "******"Passwords didn't match!"

    hotp_input = raw_input("Is this admin using a YubiKey [HOTP]? (y/N): ")
    otp_secret = None
    if hotp_input.lower() == "y" or hotp_input.lower() == "yes":
        while True:
            otp_secret = raw_input(
                "Please configure your YubiKey and enter the secret: ")
            if otp_secret:
                break

    admin = Journalist(username=username,
                       password=password,
                       is_admin=True,
                       otp_secret=otp_secret)
    try:
        db_session.add(admin)
        db_session.commit()
    except Exception, e:
        if "username is not unique" in str(e):
            print "ERROR: That username is already taken!"
        else:
            print "ERROR: An unknown error occurred, traceback:"
            print e
コード例 #30
0
    def _admin_logs_in(self):
        # Create a test admin user for logging in
        admin_user_info = dict(username='******',
                               password='******',
                               is_admin=True)
        admin_user = Journalist(**admin_user_info)
        db_session.add(admin_user)
        db_session.commit()

        # Stash the admin user on self so we can use it in later tests
        self.admin_user = admin_user_info
        self.admin_user['orm_obj'] = admin_user

        self._login_user(admin_user_info['username'],
                         admin_user_info['password'], admin_user.totp.now())

        # Admin user should log in to the same interface as a normal user,
        # since there may be users who wish to be both journalists and admins.
        headline = self.driver.find_element_by_css_selector('span.headline')
        self.assertIn('Sources', headline.text)

        # Admin user should have a link that take them to the admin page
        links = self.driver.find_elements_by_tag_name('a')
        self.assertIn('Admin', [el.text for el in links])
コード例 #31
0
def _add_user(is_admin=False):  # pragma: no cover
    while True:
        username = raw_input('Username: '******'Password: '******'Confirm Password: '******'Your password is too long (maximum length {} characters). '
                  'Please pick a shorter '
                  'password.'.format(Journalist.MAX_PASSWORD_LEN))
            continue

        if len(password) < Journalist.MIN_PASSWORD_LEN:
            print('Error: Password needs to be at least {} characters.'.format(
                Journalist.MIN_PASSWORD_LEN
            ))
            continue

        if password == password_again:
            break
        print("Passwords didn't match!")

    hotp_input = raw_input('Will this user be using a YubiKey [HOTP]? (y/N): ')
    otp_secret = None
    if hotp_input.lower() in ('y', 'yes'):
        while True:
            otp_secret = raw_input(
                'Please configure your YubiKey and enter the secret: ')
            if otp_secret:
                break

    try:
        user = Journalist(username=username,
                          password=password,
                          is_admin=is_admin,
                          otp_secret=otp_secret)
        db_session.add(user)
        db_session.commit()
    except Exception as exc:
        db_session.rollback()
        if "UNIQUE constraint failed: journalists.username" in str(exc):
            print('ERROR: That username is already taken!')
        else:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            print(repr(traceback.format_exception(exc_type, exc_value,
                                                  exc_traceback)))
        return 1
    else:
        print('User "{}" successfully added'.format(username))
        if not otp_secret:
            # Print the QR code for FreeOTP/ Google Authenticator
            print('\nScan the QR code below with FreeOTP or Google '
                  'Authenticator:\n')
            uri = user.totp.provisioning_uri(username,
                                             issuer_name='SecureDrop')
            qr = qrcode.QRCode()
            qr.add_data(uri)
            qr.print_ascii(tty=sys.stdout.isatty())
            print('\nIf the barcode does not render correctly, try changing '
                  "your terminal's font (Monospace for Linux, Menlo for OS "
                  'X). If you are using iTerm on Mac OS X, you will need to '
                  'change the "Non-ASCII Font", which is your profile\'s Text '
                  "settings.\n\nCan't scan the barcode? Enter following "
                  'shared secret '
                  'manually:\n{}\n'.format(user.formatted_otp_secret))
        return 0
コード例 #32
0
ファイル: test_db.py プロジェクト: freedomofpress/securedrop
 def test_throttle_login(self):
     journalist, _ = db_helper.init_journalist()
     for _ in range(Journalist._MAX_LOGIN_ATTEMPTS_PER_PERIOD):
         Journalist.throttle_login(journalist)
     with self.assertRaises(LoginThrottledException):
         Journalist.throttle_login(journalist)
コード例 #33
0
 def test_throttle_login(self):
     journalist, _ = db_helper.init_journalist()
     for _ in range(Journalist._MAX_LOGIN_ATTEMPTS_PER_PERIOD):
         Journalist.throttle_login(journalist)
     with self.assertRaises(LoginThrottledException):
         Journalist.throttle_login(journalist)