Repository: freedomofpress/securedrop

Issue:
Changing language on `/generate` results in "Method Not Allowed" error
# Steps to Reproduce

(On `develop` / `release/2.3.0` branch as of this writing. Bug is visible in both staging and dev environments.)

1. Visit the `/generate` page in the Source Interface
2. Change the language using the dropdown selector under the logo

# Expected behavior

`/generate` page is-rendered in the selected language.

# Actual behavior

HTTP 405 error page:

> Method Not Allowed
> 
> The method is not allowed for the requested URL.

The language change still takes effect for subsequent page views.


Discussion:

Resolving patch:
diff --git a/securedrop/source_app/main.py b/securedrop/source_app/main.py
--- a/securedrop/source_app/main.py
+++ b/securedrop/source_app/main.py
@@ -37,15 +37,17 @@ def make_blueprint(config: SDConfig) -> Blueprint:
     def index() -> str:
         return render_template('index.html')
 
-    @view.route('/generate', methods=('POST',))
+    @view.route('/generate', methods=('POST', 'GET'))
     def generate() -> Union[str, werkzeug.Response]:
-        # 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 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(gettext(
                 "You were redirected because you are already logged in. "


Test patch:
diff --git a/securedrop/tests/test_source.py b/securedrop/tests/test_source.py
--- a/securedrop/tests/test_source.py
+++ b/securedrop/tests/test_source.py
@@ -133,7 +133,7 @@ def test_create_new_source(source_app):
         assert 'codenames' not in session
 
 
-def test_generate(source_app):
+def test_generate_as_post(source_app):
     with source_app.test_client() as app:
         resp = app.post(url_for('main.generate'), data=GENERATE_DATA)
         assert resp.status_code == 200
@@ -147,6 +147,21 @@ def test_generate(source_app):
     # codename displayed to the source
     assert codename == escape(session_codename)
 
+def test_generate_as_get(source_app):
+    with source_app.test_client() as app:
+        resp = app.get(url_for('main.generate'))
+        assert resp.status_code == 200
+        session_codename = next(iter(session['codenames'].values()))
+
+    text = resp.data.decode('utf-8')
+    assert "functions as both your username and your password" in text
+
+    codename = _find_codename(resp.data.decode('utf-8'))
+    # codename is also stored in the session - make sure it matches the
+    # codename displayed to the source
+    assert codename == escape(session_codename)
+
+
 
 def test_create_duplicate_codename_logged_in_not_in_session(source_app):
     with patch.object(source_app.logger, 'error') as logger: