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='******')
Exemple #2
0
 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)
Exemple #3
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)
    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()
Exemple #5
0
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
    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)
Exemple #7
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")
Exemple #8
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)
Exemple #9
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")
Exemple #10
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)
    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, "")
Exemple #12
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()
Exemple #13
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
    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])
Exemple #15
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