We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`new-password` route is defined twice
It looks to me like this route in the admin UI is defined redundantly:
https://github.com/freedomofpress/securedrop/blob/develop/securedrop/journalist_app/admin.py#L219
https://github.com/freedomofpress/securedrop/blob/develop/securedrop/journalist_app/admin.py#L259

If there is no reason for this, we should remove one of the redundant definitions.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `securedrop/journalist_app/admin.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 import os
4 
5 from flask import (Blueprint, render_template, request, url_for, redirect, g,
6                    current_app, flash, abort)
7 from flask_babel import gettext
8 from sqlalchemy.exc import IntegrityError
9 from sqlalchemy.orm.exc import NoResultFound
10 
11 from db import db
12 from models import (InstanceConfig, Journalist, InvalidUsernameException,
13                     FirstOrLastNameError, PasswordError)
14 from journalist_app.decorators import admin_required
15 from journalist_app.utils import (make_password, commit_account_changes, set_diceware_password,
16                                   validate_hotp_secret, revoke_token)
17 from journalist_app.forms import LogoForm, NewUserForm, SubmissionPreferencesForm
18 
19 
20 def make_blueprint(config):
21     view = Blueprint('admin', __name__)
22 
23     @view.route('/', methods=('GET', 'POST'))
24     @admin_required
25     def index():
26         users = Journalist.query.all()
27         return render_template("admin.html", users=users)
28 
29     @view.route('/config', methods=('GET', 'POST'))
30     @admin_required
31     def manage_config():
32         # The UI prompt ("prevent") is the opposite of the setting ("allow"):
33         submission_preferences_form = SubmissionPreferencesForm(
34             prevent_document_uploads=not current_app.instance_config.allow_document_uploads)
35         logo_form = LogoForm()
36         if logo_form.validate_on_submit():
37             f = logo_form.logo.data
38             custom_logo_filepath = os.path.join(current_app.static_folder, 'i',
39                                                 'custom_logo.png')
40             try:
41                 f.save(custom_logo_filepath)
42                 flash(gettext("Image updated."), "logo-success")
43             except Exception:
44                 flash("Unable to process the image file."
45                       " Try another one.", "logo-error")
46             finally:
47                 return redirect(url_for("admin.manage_config"))
48         else:
49             for field, errors in list(logo_form.errors.items()):
50                 for error in errors:
51                     flash(error, "logo-error")
52             return render_template("config.html",
53                                    submission_preferences_form=submission_preferences_form,
54                                    logo_form=logo_form)
55 
56     @view.route('/update-submission-preferences', methods=['POST'])
57     @admin_required
58     def update_submission_preferences():
59         form = SubmissionPreferencesForm()
60         if form.validate_on_submit():
61             # The UI prompt ("prevent") is the opposite of the setting ("allow"):
62             flash(gettext("Preferences saved."), "submission-preferences-success")
63             value = not bool(request.form.get('prevent_document_uploads'))
64             InstanceConfig.set('allow_document_uploads', value)
65             return redirect(url_for('admin.manage_config'))
66 
67     @view.route('/add', methods=('GET', 'POST'))
68     @admin_required
69     def add_user():
70         form = NewUserForm()
71         if form.validate_on_submit():
72             form_valid = True
73             username = request.form['username']
74             first_name = request.form['first_name']
75             last_name = request.form['last_name']
76             password = request.form['password']
77             is_admin = bool(request.form.get('is_admin'))
78 
79             try:
80                 otp_secret = None
81                 if request.form.get('is_hotp', False):
82                     otp_secret = request.form.get('otp_secret', '')
83                 new_user = Journalist(username=username,
84                                       password=password,
85                                       first_name=first_name,
86                                       last_name=last_name,
87                                       is_admin=is_admin,
88                                       otp_secret=otp_secret)
89                 db.session.add(new_user)
90                 db.session.commit()
91             except PasswordError:
92                 flash(gettext(
93                       'There was an error with the autogenerated password. '
94                       'User not created. Please try again.'), 'error')
95                 form_valid = False
96             except InvalidUsernameException as e:
97                 form_valid = False
98                 flash('Invalid username: ' + str(e), "error")
99             except IntegrityError as e:
100                 db.session.rollback()
101                 form_valid = False
102                 if "UNIQUE constraint failed: journalists.username" in str(e):
103                     flash(gettext('Username "{user}" already taken.'.format(
104                         user=username)), "error")
105                 else:
106                     flash(gettext("An error occurred saving this user"
107                                   " to the database."
108                                   " Please inform your admin."),
109                           "error")
110                     current_app.logger.error("Adding user "
111                                              "'{}' failed: {}".format(
112                                                  username, e))
113 
114             if form_valid:
115                 return redirect(url_for('admin.new_user_two_factor',
116                                         uid=new_user.id))
117 
118         return render_template("admin_add_user.html",
119                                password=make_password(config),
120                                form=form)
121 
122     @view.route('/2fa', methods=('GET', 'POST'))
123     @admin_required
124     def new_user_two_factor():
125         user = Journalist.query.get(request.args['uid'])
126 
127         if request.method == 'POST':
128             token = request.form['token']
129             if user.verify_token(token):
130                 flash(gettext(
131                     "The two-factor code for user \"{user}\" was verified "
132                     "successfully.").format(user=user.username),
133                     "notification")
134                 return redirect(url_for("admin.index"))
135             else:
136                 flash(gettext(
137                     "There was a problem verifying the two-factor code. Please try again."),
138                       "error")
139 
140         return render_template("admin_new_user_two_factor.html", user=user)
141 
142     @view.route('/reset-2fa-totp', methods=['POST'])
143     @admin_required
144     def reset_two_factor_totp():
145         uid = request.form['uid']
146         user = Journalist.query.get(uid)
147         user.is_totp = True
148         user.regenerate_totp_shared_secret()
149         db.session.commit()
150         return redirect(url_for('admin.new_user_two_factor', uid=uid))
151 
152     @view.route('/reset-2fa-hotp', methods=['POST'])
153     @admin_required
154     def reset_two_factor_hotp():
155         uid = request.form['uid']
156         otp_secret = request.form.get('otp_secret', None)
157         if otp_secret:
158             user = Journalist.query.get(uid)
159             if not validate_hotp_secret(user, otp_secret):
160                 return render_template('admin_edit_hotp_secret.html', uid=uid)
161             db.session.commit()
162             return redirect(url_for('admin.new_user_two_factor', uid=uid))
163         else:
164             return render_template('admin_edit_hotp_secret.html', uid=uid)
165 
166     @view.route('/edit/<int:user_id>', methods=('GET', 'POST'))
167     @admin_required
168     def edit_user(user_id):
169         user = Journalist.query.get(user_id)
170 
171         if request.method == 'POST':
172             if request.form.get('username', None):
173                 new_username = request.form['username']
174 
175                 try:
176                     Journalist.check_username_acceptable(new_username)
177                 except InvalidUsernameException as e:
178                     flash('Invalid username: ' + str(e), 'error')
179                     return redirect(url_for("admin.edit_user",
180                                             user_id=user_id))
181 
182                 if new_username == user.username:
183                     pass
184                 elif Journalist.query.filter_by(
185                         username=new_username).one_or_none():
186                     flash(gettext(
187                         'Username "{user}" already taken.').format(
188                             user=new_username),
189                         "error")
190                     return redirect(url_for("admin.edit_user",
191                                             user_id=user_id))
192                 else:
193                     user.username = new_username
194 
195             try:
196                 first_name = request.form['first_name']
197                 Journalist.check_name_acceptable(first_name)
198                 user.first_name = first_name
199             except FirstOrLastNameError as e:
200                 flash(gettext('Name not updated: {}'.format(e)), "error")
201                 return redirect(url_for("admin.edit_user", user_id=user_id))
202 
203             try:
204                 last_name = request.form['last_name']
205                 Journalist.check_name_acceptable(last_name)
206                 user.last_name = last_name
207             except FirstOrLastNameError as e:
208                 flash(gettext('Name not updated: {}'.format(e)), "error")
209                 return redirect(url_for("admin.edit_user", user_id=user_id))
210 
211             user.is_admin = bool(request.form.get('is_admin'))
212 
213             commit_account_changes(user)
214 
215         password = make_password(config)
216         return render_template("edit_account.html", user=user,
217                                password=password)
218 
219     @view.route('/edit/<int:user_id>/new-password', methods=('POST',))
220     @admin_required
221     def set_password(user_id):
222         try:
223             user = Journalist.query.get(user_id)
224         except NoResultFound:
225             abort(404)
226 
227         password = request.form.get('password')
228         if set_diceware_password(user, password) is not False:
229             if user.last_token is not None:
230                 revoke_token(user, user.last_token)
231             user.session_nonce += 1
232             db.session.commit()
233 
234         return redirect(url_for('admin.edit_user', user_id=user_id))
235 
236     @view.route('/delete/<int:user_id>', methods=('POST',))
237     @admin_required
238     def delete_user(user_id):
239         user = Journalist.query.get(user_id)
240         if user_id == g.user.id:
241             # Do not flash because the interface already has safe guards.
242             # It can only happen by manually crafting a POST request
243             current_app.logger.error(
244                 "Admin {} tried to delete itself".format(g.user.username))
245             abort(403)
246         elif user:
247             db.session.delete(user)
248             db.session.commit()
249             flash(gettext("Deleted user '{user}'.").format(
250                 user=user.username), "notification")
251         else:
252             current_app.logger.error(
253                 "Admin {} tried to delete nonexistent user with pk={}".format(
254                     g.user.username, user_id))
255             abort(404)
256 
257         return redirect(url_for('admin.index'))
258 
259     @view.route('/edit/<int:user_id>/new-password', methods=('POST',))
260     @admin_required
261     def new_password(user_id):
262         try:
263             user = Journalist.query.get(user_id)
264         except NoResultFound:
265             abort(404)
266 
267         password = request.form.get('password')
268         if set_diceware_password(user, password) is not False:
269             if user.last_token is not None:
270                 revoke_token(user, user.last_token)
271             user.session_nonce += 1
272             db.session.commit()
273         return redirect(url_for('admin.edit_user', user_id=user_id))
274 
275     @view.route('/ossec-test')
276     @admin_required
277     def ossec_test():
278         current_app.logger.error('This is a test OSSEC alert')
279         flash(gettext('Test alert sent. Please check your email.'),
280               'notification')
281         return redirect(url_for('admin.manage_config'))
282 
283     return view
284
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/securedrop/journalist_app/admin.py b/securedrop/journalist_app/admin.py
--- a/securedrop/journalist_app/admin.py
+++ b/securedrop/journalist_app/admin.py
@@ -216,23 +216,6 @@
         return render_template("edit_account.html", user=user,
                                password=password)
 
-    @view.route('/edit/<int:user_id>/new-password', methods=('POST',))
-    @admin_required
-    def set_password(user_id):
-        try:
-            user = Journalist.query.get(user_id)
-        except NoResultFound:
-            abort(404)
-
-        password = request.form.get('password')
-        if set_diceware_password(user, password) is not False:
-            if user.last_token is not None:
-                revoke_token(user, user.last_token)
-            user.session_nonce += 1
-            db.session.commit()
-
-        return redirect(url_for('admin.edit_user', user_id=user_id))
-
     @view.route('/delete/<int:user_id>', methods=('POST',))
     @admin_required
     def delete_user(user_id):

{"golden_diff": "diff --git a/securedrop/journalist_app/admin.py b/securedrop/journalist_app/admin.py\n--- a/securedrop/journalist_app/admin.py\n+++ b/securedrop/journalist_app/admin.py\n@@ -216,23 +216,6 @@\n         return render_template(\"edit_account.html\", user=user,\n                                password=password)\n \n-    @view.route('/edit/<int:user_id>/new-password', methods=('POST',))\n-    @admin_required\n-    def set_password(user_id):\n-        try:\n-            user = Journalist.query.get(user_id)\n-        except NoResultFound:\n-            abort(404)\n-\n-        password = request.form.get('password')\n-        if set_diceware_password(user, password) is not False:\n-            if user.last_token is not None:\n-                revoke_token(user, user.last_token)\n-            user.session_nonce += 1\n-            db.session.commit()\n-\n-        return redirect(url_for('admin.edit_user', user_id=user_id))\n-\n     @view.route('/delete/<int:user_id>', methods=('POST',))\n     @admin_required\n     def delete_user(user_id):\n", "issue": "`new-password` route is defined twice\nIt looks to me like this route in the admin UI is defined redundantly:\r\nhttps://github.com/freedomofpress/securedrop/blob/develop/securedrop/journalist_app/admin.py#L219\r\nhttps://github.com/freedomofpress/securedrop/blob/develop/securedrop/journalist_app/admin.py#L259\r\n\r\nIf there is no reason for this, we should remove one of the redundant definitions.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\nimport os\n\nfrom flask import (Blueprint, render_template, request, url_for, redirect, g,\n                   current_app, flash, abort)\nfrom flask_babel import gettext\nfrom sqlalchemy.exc import IntegrityError\nfrom sqlalchemy.orm.exc import NoResultFound\n\nfrom db import db\nfrom models import (InstanceConfig, Journalist, InvalidUsernameException,\n                    FirstOrLastNameError, PasswordError)\nfrom journalist_app.decorators import admin_required\nfrom journalist_app.utils import (make_password, commit_account_changes, set_diceware_password,\n                                  validate_hotp_secret, revoke_token)\nfrom journalist_app.forms import LogoForm, NewUserForm, SubmissionPreferencesForm\n\n\ndef make_blueprint(config):\n    view = Blueprint('admin', __name__)\n\n    @view.route('/', methods=('GET', 'POST'))\n    @admin_required\n    def index():\n        users = Journalist.query.all()\n        return render_template(\"admin.html\", users=users)\n\n    @view.route('/config', methods=('GET', 'POST'))\n    @admin_required\n    def manage_config():\n        # The UI prompt (\"prevent\") is the opposite of the setting (\"allow\"):\n        submission_preferences_form = SubmissionPreferencesForm(\n            prevent_document_uploads=not current_app.instance_config.allow_document_uploads)\n        logo_form = LogoForm()\n        if logo_form.validate_on_submit():\n            f = logo_form.logo.data\n            custom_logo_filepath = os.path.join(current_app.static_folder, 'i',\n                                                'custom_logo.png')\n            try:\n                f.save(custom_logo_filepath)\n                flash(gettext(\"Image updated.\"), \"logo-success\")\n            except Exception:\n                flash(\"Unable to process the image file.\"\n                      \" Try another one.\", \"logo-error\")\n            finally:\n                return redirect(url_for(\"admin.manage_config\"))\n        else:\n            for field, errors in list(logo_form.errors.items()):\n                for error in errors:\n                    flash(error, \"logo-error\")\n            return render_template(\"config.html\",\n                                   submission_preferences_form=submission_preferences_form,\n                                   logo_form=logo_form)\n\n    @view.route('/update-submission-preferences', methods=['POST'])\n    @admin_required\n    def update_submission_preferences():\n        form = SubmissionPreferencesForm()\n        if form.validate_on_submit():\n            # The UI prompt (\"prevent\") is the opposite of the setting (\"allow\"):\n            flash(gettext(\"Preferences saved.\"), \"submission-preferences-success\")\n            value = not bool(request.form.get('prevent_document_uploads'))\n            InstanceConfig.set('allow_document_uploads', value)\n            return redirect(url_for('admin.manage_config'))\n\n    @view.route('/add', methods=('GET', 'POST'))\n    @admin_required\n    def add_user():\n        form = NewUserForm()\n        if form.validate_on_submit():\n            form_valid = True\n            username = request.form['username']\n            first_name = request.form['first_name']\n            last_name = request.form['last_name']\n            password = request.form['password']\n            is_admin = bool(request.form.get('is_admin'))\n\n            try:\n                otp_secret = None\n                if request.form.get('is_hotp', False):\n                    otp_secret = request.form.get('otp_secret', '')\n                new_user = Journalist(username=username,\n                                      password=password,\n                                      first_name=first_name,\n                                      last_name=last_name,\n                                      is_admin=is_admin,\n                                      otp_secret=otp_secret)\n                db.session.add(new_user)\n                db.session.commit()\n            except PasswordError:\n                flash(gettext(\n                      'There was an error with the autogenerated password. '\n                      'User not created. Please try again.'), 'error')\n                form_valid = False\n            except InvalidUsernameException as e:\n                form_valid = False\n                flash('Invalid username: ' + str(e), \"error\")\n            except IntegrityError as e:\n                db.session.rollback()\n                form_valid = False\n                if \"UNIQUE constraint failed: journalists.username\" in str(e):\n                    flash(gettext('Username \"{user}\" already taken.'.format(\n                        user=username)), \"error\")\n                else:\n                    flash(gettext(\"An error occurred saving this user\"\n                                  \" to the database.\"\n                                  \" Please inform your admin.\"),\n                          \"error\")\n                    current_app.logger.error(\"Adding user \"\n                                             \"'{}' failed: {}\".format(\n                                                 username, e))\n\n            if form_valid:\n                return redirect(url_for('admin.new_user_two_factor',\n                                        uid=new_user.id))\n\n        return render_template(\"admin_add_user.html\",\n                               password=make_password(config),\n                               form=form)\n\n    @view.route('/2fa', methods=('GET', 'POST'))\n    @admin_required\n    def new_user_two_factor():\n        user = Journalist.query.get(request.args['uid'])\n\n        if request.method == 'POST':\n            token = request.form['token']\n            if user.verify_token(token):\n                flash(gettext(\n                    \"The two-factor code for user \\\"{user}\\\" was verified \"\n                    \"successfully.\").format(user=user.username),\n                    \"notification\")\n                return redirect(url_for(\"admin.index\"))\n            else:\n                flash(gettext(\n                    \"There was a problem verifying the two-factor code. Please try again.\"),\n                      \"error\")\n\n        return render_template(\"admin_new_user_two_factor.html\", user=user)\n\n    @view.route('/reset-2fa-totp', methods=['POST'])\n    @admin_required\n    def reset_two_factor_totp():\n        uid = request.form['uid']\n        user = Journalist.query.get(uid)\n        user.is_totp = True\n        user.regenerate_totp_shared_secret()\n        db.session.commit()\n        return redirect(url_for('admin.new_user_two_factor', uid=uid))\n\n    @view.route('/reset-2fa-hotp', methods=['POST'])\n    @admin_required\n    def reset_two_factor_hotp():\n        uid = request.form['uid']\n        otp_secret = request.form.get('otp_secret', None)\n        if otp_secret:\n            user = Journalist.query.get(uid)\n            if not validate_hotp_secret(user, otp_secret):\n                return render_template('admin_edit_hotp_secret.html', uid=uid)\n            db.session.commit()\n            return redirect(url_for('admin.new_user_two_factor', uid=uid))\n        else:\n            return render_template('admin_edit_hotp_secret.html', uid=uid)\n\n    @view.route('/edit/<int:user_id>', methods=('GET', 'POST'))\n    @admin_required\n    def edit_user(user_id):\n        user = Journalist.query.get(user_id)\n\n        if request.method == 'POST':\n            if request.form.get('username', None):\n                new_username = request.form['username']\n\n                try:\n                    Journalist.check_username_acceptable(new_username)\n                except InvalidUsernameException as e:\n                    flash('Invalid username: ' + str(e), 'error')\n                    return redirect(url_for(\"admin.edit_user\",\n                                            user_id=user_id))\n\n                if new_username == user.username:\n                    pass\n                elif Journalist.query.filter_by(\n                        username=new_username).one_or_none():\n                    flash(gettext(\n                        'Username \"{user}\" already taken.').format(\n                            user=new_username),\n                        \"error\")\n                    return redirect(url_for(\"admin.edit_user\",\n                                            user_id=user_id))\n                else:\n                    user.username = new_username\n\n            try:\n                first_name = request.form['first_name']\n                Journalist.check_name_acceptable(first_name)\n                user.first_name = first_name\n            except FirstOrLastNameError as e:\n                flash(gettext('Name not updated: {}'.format(e)), \"error\")\n                return redirect(url_for(\"admin.edit_user\", user_id=user_id))\n\n            try:\n                last_name = request.form['last_name']\n                Journalist.check_name_acceptable(last_name)\n                user.last_name = last_name\n            except FirstOrLastNameError as e:\n                flash(gettext('Name not updated: {}'.format(e)), \"error\")\n                return redirect(url_for(\"admin.edit_user\", user_id=user_id))\n\n            user.is_admin = bool(request.form.get('is_admin'))\n\n            commit_account_changes(user)\n\n        password = make_password(config)\n        return render_template(\"edit_account.html\", user=user,\n                               password=password)\n\n    @view.route('/edit/<int:user_id>/new-password', methods=('POST',))\n    @admin_required\n    def set_password(user_id):\n        try:\n            user = Journalist.query.get(user_id)\n        except NoResultFound:\n            abort(404)\n\n        password = request.form.get('password')\n        if set_diceware_password(user, password) is not False:\n            if user.last_token is not None:\n                revoke_token(user, user.last_token)\n            user.session_nonce += 1\n            db.session.commit()\n\n        return redirect(url_for('admin.edit_user', user_id=user_id))\n\n    @view.route('/delete/<int:user_id>', methods=('POST',))\n    @admin_required\n    def delete_user(user_id):\n        user = Journalist.query.get(user_id)\n        if user_id == g.user.id:\n            # Do not flash because the interface already has safe guards.\n            # It can only happen by manually crafting a POST request\n            current_app.logger.error(\n                \"Admin {} tried to delete itself\".format(g.user.username))\n            abort(403)\n        elif user:\n            db.session.delete(user)\n            db.session.commit()\n            flash(gettext(\"Deleted user '{user}'.\").format(\n                user=user.username), \"notification\")\n        else:\n            current_app.logger.error(\n                \"Admin {} tried to delete nonexistent user with pk={}\".format(\n                    g.user.username, user_id))\n            abort(404)\n\n        return redirect(url_for('admin.index'))\n\n    @view.route('/edit/<int:user_id>/new-password', methods=('POST',))\n    @admin_required\n    def new_password(user_id):\n        try:\n            user = Journalist.query.get(user_id)\n        except NoResultFound:\n            abort(404)\n\n        password = request.form.get('password')\n        if set_diceware_password(user, password) is not False:\n            if user.last_token is not None:\n                revoke_token(user, user.last_token)\n            user.session_nonce += 1\n            db.session.commit()\n        return redirect(url_for('admin.edit_user', user_id=user_id))\n\n    @view.route('/ossec-test')\n    @admin_required\n    def ossec_test():\n        current_app.logger.error('This is a test OSSEC alert')\n        flash(gettext('Test alert sent. Please check your email.'),\n              'notification')\n        return redirect(url_for('admin.manage_config'))\n\n    return view\n", "path": "securedrop/journalist_app/admin.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\nimport os\n\nfrom flask import (Blueprint, render_template, request, url_for, redirect, g,\n                   current_app, flash, abort)\nfrom flask_babel import gettext\nfrom sqlalchemy.exc import IntegrityError\nfrom sqlalchemy.orm.exc import NoResultFound\n\nfrom db import db\nfrom models import (InstanceConfig, Journalist, InvalidUsernameException,\n                    FirstOrLastNameError, PasswordError)\nfrom journalist_app.decorators import admin_required\nfrom journalist_app.utils import (make_password, commit_account_changes, set_diceware_password,\n                                  validate_hotp_secret, revoke_token)\nfrom journalist_app.forms import LogoForm, NewUserForm, SubmissionPreferencesForm\n\n\ndef make_blueprint(config):\n    view = Blueprint('admin', __name__)\n\n    @view.route('/', methods=('GET', 'POST'))\n    @admin_required\n    def index():\n        users = Journalist.query.all()\n        return render_template(\"admin.html\", users=users)\n\n    @view.route('/config', methods=('GET', 'POST'))\n    @admin_required\n    def manage_config():\n        # The UI prompt (\"prevent\") is the opposite of the setting (\"allow\"):\n        submission_preferences_form = SubmissionPreferencesForm(\n            prevent_document_uploads=not current_app.instance_config.allow_document_uploads)\n        logo_form = LogoForm()\n        if logo_form.validate_on_submit():\n            f = logo_form.logo.data\n            custom_logo_filepath = os.path.join(current_app.static_folder, 'i',\n                                                'custom_logo.png')\n            try:\n                f.save(custom_logo_filepath)\n                flash(gettext(\"Image updated.\"), \"logo-success\")\n            except Exception:\n                flash(\"Unable to process the image file.\"\n                      \" Try another one.\", \"logo-error\")\n            finally:\n                return redirect(url_for(\"admin.manage_config\"))\n        else:\n            for field, errors in list(logo_form.errors.items()):\n                for error in errors:\n                    flash(error, \"logo-error\")\n            return render_template(\"config.html\",\n                                   submission_preferences_form=submission_preferences_form,\n                                   logo_form=logo_form)\n\n    @view.route('/update-submission-preferences', methods=['POST'])\n    @admin_required\n    def update_submission_preferences():\n        form = SubmissionPreferencesForm()\n        if form.validate_on_submit():\n            # The UI prompt (\"prevent\") is the opposite of the setting (\"allow\"):\n            flash(gettext(\"Preferences saved.\"), \"submission-preferences-success\")\n            value = not bool(request.form.get('prevent_document_uploads'))\n            InstanceConfig.set('allow_document_uploads', value)\n            return redirect(url_for('admin.manage_config'))\n\n    @view.route('/add', methods=('GET', 'POST'))\n    @admin_required\n    def add_user():\n        form = NewUserForm()\n        if form.validate_on_submit():\n            form_valid = True\n            username = request.form['username']\n            first_name = request.form['first_name']\n            last_name = request.form['last_name']\n            password = request.form['password']\n            is_admin = bool(request.form.get('is_admin'))\n\n            try:\n                otp_secret = None\n                if request.form.get('is_hotp', False):\n                    otp_secret = request.form.get('otp_secret', '')\n                new_user = Journalist(username=username,\n                                      password=password,\n                                      first_name=first_name,\n                                      last_name=last_name,\n                                      is_admin=is_admin,\n                                      otp_secret=otp_secret)\n                db.session.add(new_user)\n                db.session.commit()\n            except PasswordError:\n                flash(gettext(\n                      'There was an error with the autogenerated password. '\n                      'User not created. Please try again.'), 'error')\n                form_valid = False\n            except InvalidUsernameException as e:\n                form_valid = False\n                flash('Invalid username: ' + str(e), \"error\")\n            except IntegrityError as e:\n                db.session.rollback()\n                form_valid = False\n                if \"UNIQUE constraint failed: journalists.username\" in str(e):\n                    flash(gettext('Username \"{user}\" already taken.'.format(\n                        user=username)), \"error\")\n                else:\n                    flash(gettext(\"An error occurred saving this user\"\n                                  \" to the database.\"\n                                  \" Please inform your admin.\"),\n                          \"error\")\n                    current_app.logger.error(\"Adding user \"\n                                             \"'{}' failed: {}\".format(\n                                                 username, e))\n\n            if form_valid:\n                return redirect(url_for('admin.new_user_two_factor',\n                                        uid=new_user.id))\n\n        return render_template(\"admin_add_user.html\",\n                               password=make_password(config),\n                               form=form)\n\n    @view.route('/2fa', methods=('GET', 'POST'))\n    @admin_required\n    def new_user_two_factor():\n        user = Journalist.query.get(request.args['uid'])\n\n        if request.method == 'POST':\n            token = request.form['token']\n            if user.verify_token(token):\n                flash(gettext(\n                    \"The two-factor code for user \\\"{user}\\\" was verified \"\n                    \"successfully.\").format(user=user.username),\n                    \"notification\")\n                return redirect(url_for(\"admin.index\"))\n            else:\n                flash(gettext(\n                    \"There was a problem verifying the two-factor code. Please try again.\"),\n                      \"error\")\n\n        return render_template(\"admin_new_user_two_factor.html\", user=user)\n\n    @view.route('/reset-2fa-totp', methods=['POST'])\n    @admin_required\n    def reset_two_factor_totp():\n        uid = request.form['uid']\n        user = Journalist.query.get(uid)\n        user.is_totp = True\n        user.regenerate_totp_shared_secret()\n        db.session.commit()\n        return redirect(url_for('admin.new_user_two_factor', uid=uid))\n\n    @view.route('/reset-2fa-hotp', methods=['POST'])\n    @admin_required\n    def reset_two_factor_hotp():\n        uid = request.form['uid']\n        otp_secret = request.form.get('otp_secret', None)\n        if otp_secret:\n            user = Journalist.query.get(uid)\n            if not validate_hotp_secret(user, otp_secret):\n                return render_template('admin_edit_hotp_secret.html', uid=uid)\n            db.session.commit()\n            return redirect(url_for('admin.new_user_two_factor', uid=uid))\n        else:\n            return render_template('admin_edit_hotp_secret.html', uid=uid)\n\n    @view.route('/edit/<int:user_id>', methods=('GET', 'POST'))\n    @admin_required\n    def edit_user(user_id):\n        user = Journalist.query.get(user_id)\n\n        if request.method == 'POST':\n            if request.form.get('username', None):\n                new_username = request.form['username']\n\n                try:\n                    Journalist.check_username_acceptable(new_username)\n                except InvalidUsernameException as e:\n                    flash('Invalid username: ' + str(e), 'error')\n                    return redirect(url_for(\"admin.edit_user\",\n                                            user_id=user_id))\n\n                if new_username == user.username:\n                    pass\n                elif Journalist.query.filter_by(\n                        username=new_username).one_or_none():\n                    flash(gettext(\n                        'Username \"{user}\" already taken.').format(\n                            user=new_username),\n                        \"error\")\n                    return redirect(url_for(\"admin.edit_user\",\n                                            user_id=user_id))\n                else:\n                    user.username = new_username\n\n            try:\n                first_name = request.form['first_name']\n                Journalist.check_name_acceptable(first_name)\n                user.first_name = first_name\n            except FirstOrLastNameError as e:\n                flash(gettext('Name not updated: {}'.format(e)), \"error\")\n                return redirect(url_for(\"admin.edit_user\", user_id=user_id))\n\n            try:\n                last_name = request.form['last_name']\n                Journalist.check_name_acceptable(last_name)\n                user.last_name = last_name\n            except FirstOrLastNameError as e:\n                flash(gettext('Name not updated: {}'.format(e)), \"error\")\n                return redirect(url_for(\"admin.edit_user\", user_id=user_id))\n\n            user.is_admin = bool(request.form.get('is_admin'))\n\n            commit_account_changes(user)\n\n        password = make_password(config)\n        return render_template(\"edit_account.html\", user=user,\n                               password=password)\n\n    @view.route('/delete/<int:user_id>', methods=('POST',))\n    @admin_required\n    def delete_user(user_id):\n        user = Journalist.query.get(user_id)\n        if user_id == g.user.id:\n            # Do not flash because the interface already has safe guards.\n            # It can only happen by manually crafting a POST request\n            current_app.logger.error(\n                \"Admin {} tried to delete itself\".format(g.user.username))\n            abort(403)\n        elif user:\n            db.session.delete(user)\n            db.session.commit()\n            flash(gettext(\"Deleted user '{user}'.\").format(\n                user=user.username), \"notification\")\n        else:\n            current_app.logger.error(\n                \"Admin {} tried to delete nonexistent user with pk={}\".format(\n                    g.user.username, user_id))\n            abort(404)\n\n        return redirect(url_for('admin.index'))\n\n    @view.route('/edit/<int:user_id>/new-password', methods=('POST',))\n    @admin_required\n    def new_password(user_id):\n        try:\n            user = Journalist.query.get(user_id)\n        except NoResultFound:\n            abort(404)\n\n        password = request.form.get('password')\n        if set_diceware_password(user, password) is not False:\n            if user.last_token is not None:\n                revoke_token(user, user.last_token)\n            user.session_nonce += 1\n            db.session.commit()\n        return redirect(url_for('admin.edit_user', user_id=user_id))\n\n    @view.route('/ossec-test')\n    @admin_required\n    def ossec_test():\n        current_app.logger.error('This is a test OSSEC alert')\n        flash(gettext('Test alert sent. Please check your email.'),\n              'notification')\n        return redirect(url_for('admin.manage_config'))\n\n    return view\n", "path": "securedrop/journalist_app/admin.py"}]}