Пример #1
0
def test_login_with_missing_reply_files(source_app, app_storage):
    """
    Test that source can log in when replies are present in database but missing
    from storage.
    """
    source, codename = utils.db_helper.init_source(app_storage)
    journalist, _ = utils.db_helper.init_journalist()
    replies = utils.db_helper.reply(app_storage, journalist, source, 1)
    assert len(replies) > 0
    # Delete the reply file
    reply_file_path = Path(
        app_storage.path(source.filesystem_id, replies[0].filename))
    reply_file_path.unlink()
    assert not reply_file_path.exists()

    with source_app.test_client() as app:
        resp = app.get(url_for("main.login"))
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Enter Codename" in text

        resp = app.post(url_for("main.login"),
                        data=dict(codename=codename),
                        follow_redirects=True)
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Submit Files" in text
        assert SessionManager.is_user_logged_in(db_session=db.session)
Пример #2
0
    def generate() -> Union[str, werkzeug.Response]:
        if request.method == "POST":
            # Try to detect Tor2Web usage by looking to see if tor2web_check got mangled
            tor2web_check = request.form.get("tor2web_check")
            if tor2web_check is None:
                # Missing form field
                abort(403)
            elif tor2web_check != 'href="fake.onion"':
                return redirect(url_for("info.tor2web_warning"))

        if SessionManager.is_user_logged_in(db_session=db.session):
            flash_msg(
                "notification",
                None,
                gettext(
                    "You were redirected because you are already logged in. "
                    "If you want to create a new account, you should log out first."
                ),
            )
            return redirect(url_for(".lookup"))
        codename = PassphraseGenerator.get_default().generate_passphrase(
            preferred_language=g.localeinfo.language
        )

        # Generate a unique id for each browser tab and associate the codename with this id.
        # This will allow retrieval of the codename displayed in the tab from which the source has
        # clicked to proceed to /generate (ref. issue #4458)
        tab_id = urlsafe_b64encode(os.urandom(64)).decode()
        codenames = session.get("codenames", {})
        codenames[tab_id] = codename
        session["codenames"] = fit_codenames_into_cookie(codenames)
        session["codenames_expire"] = datetime.now(timezone.utc) + timedelta(
            minutes=config.SESSION_EXPIRATION_MINUTES
        )
        return render_template("generate.html", codename=codename, tab_id=tab_id)
Пример #3
0
def test_create_duplicate_codename_logged_in_not_in_session(source_app):
    with patch.object(source_app.logger, "error") as logger:
        with source_app.test_client() as app:
            resp = app.post(url_for("main.generate"), data=GENERATE_DATA)
            assert resp.status_code == 200
            tab_id, codename = next(iter(session["codenames"].items()))

            # Create a source the first time
            resp = app.post(url_for("main.create"),
                            data={"tab_id": tab_id},
                            follow_redirects=True)
            assert resp.status_code == 200

        with source_app.test_client() as app:
            # Attempt to add the same source
            with app.session_transaction() as sess:
                sess["codenames"] = {tab_id: codename}
                sess["codenames_expire"] = datetime.utcnow() + timedelta(
                    hours=1)
            resp = app.post(url_for("main.create"),
                            data={"tab_id": tab_id},
                            follow_redirects=True)
            logger.assert_called_once()
            assert "Could not create a source" in logger.call_args[0][0]
            assert resp.status_code == 200
            assert not SessionManager.is_user_logged_in(db_session=db.session)
Пример #4
0
def test_login_and_logout(source_app):
    with source_app.test_client() as app:
        resp = app.get(url_for("main.login"))
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Enter Codename" in text

        codename = new_codename(app, session)
        resp = app.post(url_for("main.login"),
                        data=dict(codename=codename),
                        follow_redirects=True)
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Submit Files" in text
        assert SessionManager.is_user_logged_in(db_session=db.session)

    with source_app.test_client() as app:
        resp = app.post(url_for("main.login"),
                        data=dict(codename="invalid"),
                        follow_redirects=True)
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Sorry, that is not a recognized codename." in text
        assert not SessionManager.is_user_logged_in(db_session=db.session)

    with source_app.test_client() as app:
        resp = app.post(url_for("main.login"),
                        data=dict(codename=codename),
                        follow_redirects=True)
        assert resp.status_code == 200
        assert SessionManager.is_user_logged_in(db_session=db.session)

        resp = app.post(url_for("main.login"),
                        data=dict(codename=codename),
                        follow_redirects=True)
        assert resp.status_code == 200
        assert SessionManager.is_user_logged_in(db_session=db.session)

        resp = app.get(url_for("main.logout"), follow_redirects=True)
        assert not SessionManager.is_user_logged_in(db_session=db.session)
        text = resp.data.decode("utf-8")

        # This is part of the logout page message instructing users
        # to click the 'New Identity' icon
        assert "This will clear your Tor Browser activity data" in text
Пример #5
0
def test_source_is_deleted_while_logged_in(source_app):
    """If a source is deleted by a journalist when they are logged in,
    a NoResultFound will occur. The source should be redirected to the
    index when this happens, and a warning logged."""
    with source_app.test_client() as app:
        codename = new_codename(app, session)
        app.post("login", data=dict(codename=codename), follow_redirects=True)

        # Now that the source is logged in, the journalist deletes the source
        source_user = SessionManager.get_logged_in_user(db_session=db.session)
        delete_collection(source_user.filesystem_id)

        # Source attempts to continue to navigate
        resp = app.get(url_for("main.lookup"), follow_redirects=True)
        assert resp.status_code == 200
        assert not SessionManager.is_user_logged_in(db_session=db.session)
        text = resp.data.decode("utf-8")
        assert "First submission" in text
        assert not SessionManager.is_user_logged_in(db_session=db.session)
Пример #6
0
    def login_test(app, codename):
        resp = app.get(url_for("main.login"))
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Enter Codename" in text

        resp = app.post(url_for("main.login"),
                        data=dict(codename=codename),
                        follow_redirects=True)
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Submit Files" in text
        assert SessionManager.is_user_logged_in(db_session=db.session)
Пример #7
0
def test_create_new_source(source_app):
    with source_app.test_client() as app:
        resp = app.post(url_for("main.generate"), data=GENERATE_DATA)
        assert resp.status_code == 200
        tab_id = next(iter(session["codenames"].keys()))
        resp = app.post(url_for("main.create"),
                        data={"tab_id": tab_id},
                        follow_redirects=True)
        assert SessionManager.is_user_logged_in(db_session=db.session)
        # should be redirected to /lookup
        text = resp.data.decode("utf-8")
        assert "Submit Files" in text
        assert "codenames" not in session
Пример #8
0
def test_submit_codename_second_login(source_app):
    """
    Test codename submissions *not* prevented on second session
    """
    with source_app.test_client() as app:
        InstanceConfig.get_default().update_submission_prefs(
            allow_uploads=True, min_length=0, reject_codenames=True)
        codename = new_codename(app, session)
        resp = app.post(
            url_for("main.submit"),
            data=dict(msg=codename, fh=(StringIO(""), "")),
            follow_redirects=True,
        )
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Please do not submit your codename!" in text

        resp = app.get(url_for("main.logout"), follow_redirects=True)
        assert not SessionManager.is_user_logged_in(db_session=db.session)
        text = resp.data.decode("utf-8")
        assert "This will clear your Tor Browser activity data" in text

        resp = app.post(url_for("main.login"),
                        data=dict(codename=codename),
                        follow_redirects=True)
        assert resp.status_code == 200
        assert SessionManager.is_user_logged_in(db_session=db.session)

        resp = app.post(
            url_for("main.submit"),
            data=dict(msg=codename, fh=(StringIO(""), "")),
            follow_redirects=True,
        )
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Thank you for sending this information" in text
    def test_log_user_in(self, source_app, app_storage):
        # Given a source user
        passphrase = PassphraseGenerator.get_default().generate_passphrase()
        source_user = create_source_user(
            db_session=db.session,
            source_passphrase=passphrase,
            source_app_storage=app_storage,
        )

        with source_app.test_request_context():
            # When they log in, it succeeds
            SessionManager.log_user_in(db_session=db.session, supplied_passphrase=passphrase)

            # And the SessionManager returns them as the current user
            assert SessionManager.is_user_logged_in(db_session=db.session)
            logged_in_user = SessionManager.get_logged_in_user(db_session=db.session)
            assert logged_in_user.db_record_id == source_user.db_record_id
Пример #10
0
    def create() -> werkzeug.Response:
        if SessionManager.is_user_logged_in(db_session=db.session):
            flash_msg(
                "notification",
                None,
                gettext(
                    "You are already logged in. Please verify your codename as it "
                    "may differ from the one displayed on the previous page."
                ),
            )
        else:
            # Ensure the codenames have not expired
            date_codenames_expire = session.get("codenames_expire")
            if not date_codenames_expire or datetime.now(timezone.utc) >= date_codenames_expire:
                return clear_session_and_redirect_to_logged_out_page(flask_session=session)

            tab_id = request.form["tab_id"]
            codename = session["codenames"][tab_id]
            del session["codenames"]

            try:
                current_app.logger.info("Creating new source user...")
                create_source_user(
                    db_session=db.session,
                    source_passphrase=codename,
                    source_app_storage=Storage.get_default(),
                )
            except (SourcePassphraseCollisionError, SourceDesignationCollisionError) as e:
                current_app.logger.error("Could not create a source: {}".format(e))
                flash_msg(
                    "error",
                    None,
                    gettext(
                        "There was a temporary problem creating your account. Please try again."
                    ),
                )
                return redirect(url_for(".index"))

            # All done - source user was successfully created
            current_app.logger.info("New source user created")
            session["new_user_codename"] = codename
            SessionManager.log_user_in(
                db_session=db.session, supplied_passphrase=DicewarePassphrase(codename)
            )

        return redirect(url_for(".lookup"))
Пример #11
0
    def logout() -> Union[str, werkzeug.Response]:
        """
        If a user is logged in, show them a logout page that prompts them to
        click the New Identity button in Tor Browser to complete their session.
        Otherwise redirect to the main Source Interface page.
        """
        if SessionManager.is_user_logged_in(db_session=db.session):
            SessionManager.log_user_out()

            # Clear the session after we render the message so it's localized
            # If a user specified a locale, save it and restore it
            session.clear()
            session["locale"] = g.localeinfo.id

            return render_template("logout.html")
        else:
            return redirect(url_for(".index"))
    def test_log_user_out(self, source_app, app_storage):
        # Given a source user
        passphrase = PassphraseGenerator.get_default().generate_passphrase()
        create_source_user(
            db_session=db.session,
            source_passphrase=passphrase,
            source_app_storage=app_storage,
        )

        with source_app.test_request_context():
            # Who previously logged in
            SessionManager.log_user_in(db_session=db.session, supplied_passphrase=passphrase)

            # When they log out, it succeeds
            SessionManager.log_user_out()

            # And the SessionManager no longer returns a current user
            assert not SessionManager.is_user_logged_in(db_session=db.session)
            with pytest.raises(UserNotLoggedIn):
                SessionManager.get_logged_in_user(db_session=db.session)
Пример #13
0
def test_normalize_timestamps(source_app, app_storage):
    """
    Check function of source_app.utils.normalize_timestamps.

    All submissions for a source should have the same timestamp. Any
    existing submissions' files that did not exist at the time of a
    new submission should not be created by normalize_timestamps.
    """
    with source_app.test_client() as app:
        # create a source
        source, codename = utils.db_helper.init_source(app_storage)

        # create one submission
        first_submission = submit(app_storage, source, 1)[0]

        # delete the submission's file from the store
        first_submission_path = Path(
            app_storage.path(source.filesystem_id, first_submission.filename))
        first_submission_path.unlink()
        assert not first_submission_path.exists()

        # log in as the source
        resp = app.post(url_for("main.login"),
                        data=dict(codename=codename),
                        follow_redirects=True)
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Submit Files" in text
        assert SessionManager.is_user_logged_in(db_session=db.session)

        # submit another message
        resp = _dummy_submission(app)
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Thanks! We received your message" in text

        # sleep to ensure timestamps would differ
        time.sleep(1)

        # submit another message
        resp = _dummy_submission(app)
        assert resp.status_code == 200
        text = resp.data.decode("utf-8")
        assert "Thanks! We received your message" in text

        # only two of the source's three submissions should have files in the store
        assert 3 == len(source.submissions)
        submission_paths = [
            Path(app_storage.path(source.filesystem_id, s.filename))
            for s in source.submissions
        ]
        extant_paths = [p for p in submission_paths if p.exists()]
        assert 2 == len(extant_paths)

        # verify that the deleted file has not been recreated
        assert not first_submission_path.exists()
        assert first_submission_path not in extant_paths

        # and the timestamps of all existing files should match exactly
        assert extant_paths[0].stat().st_atime_ns == extant_paths[1].stat(
        ).st_atime_ns
        assert extant_paths[0].stat().st_ctime_ns == extant_paths[1].stat(
        ).st_ctime_ns
        assert extant_paths[0].stat().st_mtime_ns == extant_paths[1].stat(
        ).st_mtime_ns