We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Password Auth enabling itself when using LDAP
### Issue Summary

When using LDAP for auth, the checkbox for "Password Login Enabled" in settings becomes greyed out. However, when changing any other setting on that page and clicking save, the "Password Login Enabled" gets enabled. I can't find any way to them disable it other than doing so manually in the Postgres 'organizations' table.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redash/handlers/authentication.py`
Content:
```
1 import logging
2 
3 from flask import abort, flash, redirect, render_template, request, url_for
4 
5 from flask_login import current_user, login_required, login_user, logout_user
6 from redash import __version__, limiter, models, settings
7 from redash.authentication import current_org, get_login_url, get_next_path
8 from redash.authentication.account import (
9     BadSignature,
10     SignatureExpired,
11     send_password_reset_email,
12     send_user_disabled_email,
13     send_verify_email,
14     validate_token,
15 )
16 from redash.handlers import routes
17 from redash.handlers.base import json_response, org_scoped_rule
18 from redash.version_check import get_latest_version
19 from sqlalchemy.orm.exc import NoResultFound
20 
21 logger = logging.getLogger(__name__)
22 
23 
24 def get_google_auth_url(next_path):
25     if settings.MULTI_ORG:
26         google_auth_url = url_for(
27             "google_oauth.authorize_org", next=next_path, org_slug=current_org.slug
28         )
29     else:
30         google_auth_url = url_for("google_oauth.authorize", next=next_path)
31     return google_auth_url
32 
33 
34 def render_token_login_page(template, org_slug, token, invite):
35     try:
36         user_id = validate_token(token)
37         org = current_org._get_current_object()
38         user = models.User.get_by_id_and_org(user_id, org)
39     except NoResultFound:
40         logger.exception(
41             "Bad user id in token. Token= , User id= %s, Org=%s",
42             user_id,
43             token,
44             org_slug,
45         )
46         return (
47             render_template(
48                 "error.html",
49                 error_message="Invalid invite link. Please ask for a new one.",
50             ),
51             400,
52         )
53     except (SignatureExpired, BadSignature):
54         logger.exception("Failed to verify invite token: %s, org=%s", token, org_slug)
55         return (
56             render_template(
57                 "error.html",
58                 error_message="Your invite link has expired. Please ask for a new one.",
59             ),
60             400,
61         )
62 
63     if invite and user.details.get("is_invitation_pending") is False:
64         return (
65             render_template(
66                 "error.html",
67                 error_message=(
68                     "This invitation has already been accepted. "
69                     "Please try resetting your password instead."
70                 ),
71             ),
72             400,
73         )
74 
75     status_code = 200
76     if request.method == "POST":
77         if "password" not in request.form:
78             flash("Bad Request")
79             status_code = 400
80         elif not request.form["password"]:
81             flash("Cannot use empty password.")
82             status_code = 400
83         elif len(request.form["password"]) < 6:
84             flash("Password length is too short (<6).")
85             status_code = 400
86         else:
87             if invite:
88                 user.is_invitation_pending = False
89             user.hash_password(request.form["password"])
90             models.db.session.add(user)
91             login_user(user)
92             models.db.session.commit()
93             return redirect(url_for("redash.index", org_slug=org_slug))
94 
95     google_auth_url = get_google_auth_url(url_for("redash.index", org_slug=org_slug))
96 
97     return (
98         render_template(
99             template,
100             show_google_openid=settings.GOOGLE_OAUTH_ENABLED,
101             google_auth_url=google_auth_url,
102             show_saml_login=current_org.get_setting("auth_saml_enabled"),
103             show_remote_user_login=settings.REMOTE_USER_LOGIN_ENABLED,
104             show_ldap_login=settings.LDAP_LOGIN_ENABLED,
105             org_slug=org_slug,
106             user=user,
107         ),
108         status_code,
109     )
110 
111 
112 @routes.route(org_scoped_rule("/invite/<token>"), methods=["GET", "POST"])
113 def invite(token, org_slug=None):
114     return render_token_login_page("invite.html", org_slug, token, True)
115 
116 
117 @routes.route(org_scoped_rule("/reset/<token>"), methods=["GET", "POST"])
118 def reset(token, org_slug=None):
119     return render_token_login_page("reset.html", org_slug, token, False)
120 
121 
122 @routes.route(org_scoped_rule("/verify/<token>"), methods=["GET"])
123 def verify(token, org_slug=None):
124     try:
125         user_id = validate_token(token)
126         org = current_org._get_current_object()
127         user = models.User.get_by_id_and_org(user_id, org)
128     except (BadSignature, NoResultFound):
129         logger.exception(
130             "Failed to verify email verification token: %s, org=%s", token, org_slug
131         )
132         return (
133             render_template(
134                 "error.html",
135                 error_message="Your verification link is invalid. Please ask for a new one.",
136             ),
137             400,
138         )
139 
140     user.is_email_verified = True
141     models.db.session.add(user)
142     models.db.session.commit()
143 
144     template_context = {"org_slug": org_slug} if settings.MULTI_ORG else {}
145     next_url = url_for("redash.index", **template_context)
146 
147     return render_template("verify.html", next_url=next_url)
148 
149 
150 @routes.route(org_scoped_rule("/forgot"), methods=["GET", "POST"])
151 def forgot_password(org_slug=None):
152     if not current_org.get_setting("auth_password_login_enabled"):
153         abort(404)
154 
155     submitted = False
156     if request.method == "POST" and request.form["email"]:
157         submitted = True
158         email = request.form["email"]
159         try:
160             org = current_org._get_current_object()
161             user = models.User.get_by_email_and_org(email, org)
162             if user.is_disabled:
163                 send_user_disabled_email(user)
164             else:
165                 send_password_reset_email(user)
166         except NoResultFound:
167             logging.error("No user found for forgot password: %s", email)
168 
169     return render_template("forgot.html", submitted=submitted)
170 
171 
172 @routes.route(org_scoped_rule("/verification_email/"), methods=["POST"])
173 def verification_email(org_slug=None):
174     if not current_user.is_email_verified:
175         send_verify_email(current_user, current_org)
176 
177     return json_response(
178         {
179             "message": "Please check your email inbox in order to verify your email address."
180         }
181     )
182 
183 
184 @routes.route(org_scoped_rule("/login"), methods=["GET", "POST"])
185 @limiter.limit(settings.THROTTLE_LOGIN_PATTERN)
186 def login(org_slug=None):
187     # We intentionally use == as otherwise it won't actually use the proxy. So weird :O
188     # noinspection PyComparisonWithNone
189     if current_org == None and not settings.MULTI_ORG:
190         return redirect("/setup")
191     elif current_org == None:
192         return redirect("/")
193 
194     index_url = url_for("redash.index", org_slug=org_slug)
195     unsafe_next_path = request.args.get("next", index_url)
196     next_path = get_next_path(unsafe_next_path)
197     if current_user.is_authenticated:
198         return redirect(next_path)
199 
200     if request.method == "POST":
201         try:
202             org = current_org._get_current_object()
203             user = models.User.get_by_email_and_org(request.form["email"], org)
204             if (
205                 user
206                 and not user.is_disabled
207                 and user.verify_password(request.form["password"])
208             ):
209                 remember = "remember" in request.form
210                 login_user(user, remember=remember)
211                 return redirect(next_path)
212             else:
213                 flash("Wrong email or password.")
214         except NoResultFound:
215             flash("Wrong email or password.")
216 
217     google_auth_url = get_google_auth_url(next_path)
218 
219     return render_template(
220         "login.html",
221         org_slug=org_slug,
222         next=next_path,
223         email=request.form.get("email", ""),
224         show_google_openid=settings.GOOGLE_OAUTH_ENABLED,
225         google_auth_url=google_auth_url,
226         show_password_login=current_org.get_setting("auth_password_login_enabled"),
227         show_saml_login=current_org.get_setting("auth_saml_enabled"),
228         show_remote_user_login=settings.REMOTE_USER_LOGIN_ENABLED,
229         show_ldap_login=settings.LDAP_LOGIN_ENABLED,
230     )
231 
232 
233 @routes.route(org_scoped_rule("/logout"))
234 def logout(org_slug=None):
235     logout_user()
236     return redirect(get_login_url(next=None))
237 
238 
239 def base_href():
240     if settings.MULTI_ORG:
241         base_href = url_for("redash.index", _external=True, org_slug=current_org.slug)
242     else:
243         base_href = url_for("redash.index", _external=True)
244 
245     return base_href
246 
247 
248 def date_time_format_config():
249     date_format = current_org.get_setting("date_format")
250     date_format_list = set(["DD/MM/YY", "MM/DD/YY", "YYYY-MM-DD", settings.DATE_FORMAT])
251     time_format = current_org.get_setting("time_format")
252     time_format_list = set(["HH:mm", "HH:mm:ss", "HH:mm:ss.SSS", settings.TIME_FORMAT])
253     return {
254         "dateFormat": date_format,
255         "dateFormatList": list(date_format_list),
256         "timeFormatList": list(time_format_list),
257         "dateTimeFormat": "{0} {1}".format(date_format, time_format),
258     }
259 
260 
261 def number_format_config():
262     return {
263         "integerFormat": current_org.get_setting("integer_format"),
264         "floatFormat": current_org.get_setting("float_format"),
265     }
266 
267 
268 def client_config():
269     if not current_user.is_api_user() and current_user.is_authenticated:
270         client_config = {
271             "newVersionAvailable": bool(get_latest_version()),
272             "version": __version__,
273         }
274     else:
275         client_config = {}
276 
277     if (
278         current_user.has_permission("admin")
279         and current_org.get_setting("beacon_consent") is None
280     ):
281         client_config["showBeaconConsentMessage"] = True
282 
283     defaults = {
284         "allowScriptsInUserInput": settings.ALLOW_SCRIPTS_IN_USER_INPUT,
285         "showPermissionsControl": current_org.get_setting(
286             "feature_show_permissions_control"
287         ),
288         "allowCustomJSVisualizations": settings.FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS,
289         "autoPublishNamedQueries": settings.FEATURE_AUTO_PUBLISH_NAMED_QUERIES,
290         "extendedAlertOptions": settings.FEATURE_EXTENDED_ALERT_OPTIONS,
291         "mailSettingsMissing": not settings.email_server_is_configured(),
292         "dashboardRefreshIntervals": settings.DASHBOARD_REFRESH_INTERVALS,
293         "queryRefreshIntervals": settings.QUERY_REFRESH_INTERVALS,
294         "googleLoginEnabled": settings.GOOGLE_OAUTH_ENABLED,
295         "pageSize": settings.PAGE_SIZE,
296         "pageSizeOptions": settings.PAGE_SIZE_OPTIONS,
297         "tableCellMaxJSONSize": settings.TABLE_CELL_MAX_JSON_SIZE,
298     }
299 
300     client_config.update(defaults)
301     client_config.update({"basePath": base_href()})
302     client_config.update(date_time_format_config())
303     client_config.update(number_format_config())
304 
305     return client_config
306 
307 
308 def messages():
309     messages = []
310 
311     if not current_user.is_email_verified:
312         messages.append("email-not-verified")
313 
314     if settings.ALLOW_PARAMETERS_IN_EMBEDS:
315         messages.append("using-deprecated-embed-feature")
316 
317     return messages
318 
319 
320 @routes.route("/api/config", methods=["GET"])
321 def config(org_slug=None):
322     return json_response(
323         {"org_slug": current_org.slug, "client_config": client_config()}
324     )
325 
326 
327 @routes.route(org_scoped_rule("/api/session"), methods=["GET"])
328 @login_required
329 def session(org_slug=None):
330     if current_user.is_api_user():
331         user = {"permissions": [], "apiKey": current_user.id}
332     else:
333         user = {
334             "profile_image_url": current_user.profile_image_url,
335             "id": current_user.id,
336             "name": current_user.name,
337             "email": current_user.email,
338             "groups": current_user.group_ids,
339             "permissions": current_user.permissions,
340         }
341 
342     return json_response(
343         {
344             "user": user,
345             "messages": messages(),
346             "org_slug": current_org.slug,
347             "client_config": client_config(),
348         }
349     )
350
```
--- 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/redash/handlers/authentication.py b/redash/handlers/authentication.py
--- a/redash/handlers/authentication.py
+++ b/redash/handlers/authentication.py
@@ -292,6 +292,7 @@
         "dashboardRefreshIntervals": settings.DASHBOARD_REFRESH_INTERVALS,
         "queryRefreshIntervals": settings.QUERY_REFRESH_INTERVALS,
         "googleLoginEnabled": settings.GOOGLE_OAUTH_ENABLED,
+        "ldapLoginEnabled": settings.LDAP_LOGIN_ENABLED,
         "pageSize": settings.PAGE_SIZE,
         "pageSizeOptions": settings.PAGE_SIZE_OPTIONS,
         "tableCellMaxJSONSize": settings.TABLE_CELL_MAX_JSON_SIZE,

{"golden_diff": "diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py\n--- a/redash/handlers/authentication.py\n+++ b/redash/handlers/authentication.py\n@@ -292,6 +292,7 @@\n         \"dashboardRefreshIntervals\": settings.DASHBOARD_REFRESH_INTERVALS,\n         \"queryRefreshIntervals\": settings.QUERY_REFRESH_INTERVALS,\n         \"googleLoginEnabled\": settings.GOOGLE_OAUTH_ENABLED,\n+        \"ldapLoginEnabled\": settings.LDAP_LOGIN_ENABLED,\n         \"pageSize\": settings.PAGE_SIZE,\n         \"pageSizeOptions\": settings.PAGE_SIZE_OPTIONS,\n         \"tableCellMaxJSONSize\": settings.TABLE_CELL_MAX_JSON_SIZE,\n", "issue": "Password Auth enabling itself when using LDAP\n### Issue Summary\r\n\r\nWhen using LDAP for auth, the checkbox for \"Password Login Enabled\" in settings becomes greyed out. However, when changing any other setting on that page and clicking save, the \"Password Login Enabled\" gets enabled. I can't find any way to them disable it other than doing so manually in the Postgres 'organizations' table.\r\n\r\n\n", "before_files": [{"content": "import logging\n\nfrom flask import abort, flash, redirect, render_template, request, url_for\n\nfrom flask_login import current_user, login_required, login_user, logout_user\nfrom redash import __version__, limiter, models, settings\nfrom redash.authentication import current_org, get_login_url, get_next_path\nfrom redash.authentication.account import (\n    BadSignature,\n    SignatureExpired,\n    send_password_reset_email,\n    send_user_disabled_email,\n    send_verify_email,\n    validate_token,\n)\nfrom redash.handlers import routes\nfrom redash.handlers.base import json_response, org_scoped_rule\nfrom redash.version_check import get_latest_version\nfrom sqlalchemy.orm.exc import NoResultFound\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_google_auth_url(next_path):\n    if settings.MULTI_ORG:\n        google_auth_url = url_for(\n            \"google_oauth.authorize_org\", next=next_path, org_slug=current_org.slug\n        )\n    else:\n        google_auth_url = url_for(\"google_oauth.authorize\", next=next_path)\n    return google_auth_url\n\n\ndef render_token_login_page(template, org_slug, token, invite):\n    try:\n        user_id = validate_token(token)\n        org = current_org._get_current_object()\n        user = models.User.get_by_id_and_org(user_id, org)\n    except NoResultFound:\n        logger.exception(\n            \"Bad user id in token. Token= , User id= %s, Org=%s\",\n            user_id,\n            token,\n            org_slug,\n        )\n        return (\n            render_template(\n                \"error.html\",\n                error_message=\"Invalid invite link. Please ask for a new one.\",\n            ),\n            400,\n        )\n    except (SignatureExpired, BadSignature):\n        logger.exception(\"Failed to verify invite token: %s, org=%s\", token, org_slug)\n        return (\n            render_template(\n                \"error.html\",\n                error_message=\"Your invite link has expired. Please ask for a new one.\",\n            ),\n            400,\n        )\n\n    if invite and user.details.get(\"is_invitation_pending\") is False:\n        return (\n            render_template(\n                \"error.html\",\n                error_message=(\n                    \"This invitation has already been accepted. \"\n                    \"Please try resetting your password instead.\"\n                ),\n            ),\n            400,\n        )\n\n    status_code = 200\n    if request.method == \"POST\":\n        if \"password\" not in request.form:\n            flash(\"Bad Request\")\n            status_code = 400\n        elif not request.form[\"password\"]:\n            flash(\"Cannot use empty password.\")\n            status_code = 400\n        elif len(request.form[\"password\"]) < 6:\n            flash(\"Password length is too short (<6).\")\n            status_code = 400\n        else:\n            if invite:\n                user.is_invitation_pending = False\n            user.hash_password(request.form[\"password\"])\n            models.db.session.add(user)\n            login_user(user)\n            models.db.session.commit()\n            return redirect(url_for(\"redash.index\", org_slug=org_slug))\n\n    google_auth_url = get_google_auth_url(url_for(\"redash.index\", org_slug=org_slug))\n\n    return (\n        render_template(\n            template,\n            show_google_openid=settings.GOOGLE_OAUTH_ENABLED,\n            google_auth_url=google_auth_url,\n            show_saml_login=current_org.get_setting(\"auth_saml_enabled\"),\n            show_remote_user_login=settings.REMOTE_USER_LOGIN_ENABLED,\n            show_ldap_login=settings.LDAP_LOGIN_ENABLED,\n            org_slug=org_slug,\n            user=user,\n        ),\n        status_code,\n    )\n\n\n@routes.route(org_scoped_rule(\"/invite/<token>\"), methods=[\"GET\", \"POST\"])\ndef invite(token, org_slug=None):\n    return render_token_login_page(\"invite.html\", org_slug, token, True)\n\n\n@routes.route(org_scoped_rule(\"/reset/<token>\"), methods=[\"GET\", \"POST\"])\ndef reset(token, org_slug=None):\n    return render_token_login_page(\"reset.html\", org_slug, token, False)\n\n\n@routes.route(org_scoped_rule(\"/verify/<token>\"), methods=[\"GET\"])\ndef verify(token, org_slug=None):\n    try:\n        user_id = validate_token(token)\n        org = current_org._get_current_object()\n        user = models.User.get_by_id_and_org(user_id, org)\n    except (BadSignature, NoResultFound):\n        logger.exception(\n            \"Failed to verify email verification token: %s, org=%s\", token, org_slug\n        )\n        return (\n            render_template(\n                \"error.html\",\n                error_message=\"Your verification link is invalid. Please ask for a new one.\",\n            ),\n            400,\n        )\n\n    user.is_email_verified = True\n    models.db.session.add(user)\n    models.db.session.commit()\n\n    template_context = {\"org_slug\": org_slug} if settings.MULTI_ORG else {}\n    next_url = url_for(\"redash.index\", **template_context)\n\n    return render_template(\"verify.html\", next_url=next_url)\n\n\n@routes.route(org_scoped_rule(\"/forgot\"), methods=[\"GET\", \"POST\"])\ndef forgot_password(org_slug=None):\n    if not current_org.get_setting(\"auth_password_login_enabled\"):\n        abort(404)\n\n    submitted = False\n    if request.method == \"POST\" and request.form[\"email\"]:\n        submitted = True\n        email = request.form[\"email\"]\n        try:\n            org = current_org._get_current_object()\n            user = models.User.get_by_email_and_org(email, org)\n            if user.is_disabled:\n                send_user_disabled_email(user)\n            else:\n                send_password_reset_email(user)\n        except NoResultFound:\n            logging.error(\"No user found for forgot password: %s\", email)\n\n    return render_template(\"forgot.html\", submitted=submitted)\n\n\n@routes.route(org_scoped_rule(\"/verification_email/\"), methods=[\"POST\"])\ndef verification_email(org_slug=None):\n    if not current_user.is_email_verified:\n        send_verify_email(current_user, current_org)\n\n    return json_response(\n        {\n            \"message\": \"Please check your email inbox in order to verify your email address.\"\n        }\n    )\n\n\n@routes.route(org_scoped_rule(\"/login\"), methods=[\"GET\", \"POST\"])\n@limiter.limit(settings.THROTTLE_LOGIN_PATTERN)\ndef login(org_slug=None):\n    # We intentionally use == as otherwise it won't actually use the proxy. So weird :O\n    # noinspection PyComparisonWithNone\n    if current_org == None and not settings.MULTI_ORG:\n        return redirect(\"/setup\")\n    elif current_org == None:\n        return redirect(\"/\")\n\n    index_url = url_for(\"redash.index\", org_slug=org_slug)\n    unsafe_next_path = request.args.get(\"next\", index_url)\n    next_path = get_next_path(unsafe_next_path)\n    if current_user.is_authenticated:\n        return redirect(next_path)\n\n    if request.method == \"POST\":\n        try:\n            org = current_org._get_current_object()\n            user = models.User.get_by_email_and_org(request.form[\"email\"], org)\n            if (\n                user\n                and not user.is_disabled\n                and user.verify_password(request.form[\"password\"])\n            ):\n                remember = \"remember\" in request.form\n                login_user(user, remember=remember)\n                return redirect(next_path)\n            else:\n                flash(\"Wrong email or password.\")\n        except NoResultFound:\n            flash(\"Wrong email or password.\")\n\n    google_auth_url = get_google_auth_url(next_path)\n\n    return render_template(\n        \"login.html\",\n        org_slug=org_slug,\n        next=next_path,\n        email=request.form.get(\"email\", \"\"),\n        show_google_openid=settings.GOOGLE_OAUTH_ENABLED,\n        google_auth_url=google_auth_url,\n        show_password_login=current_org.get_setting(\"auth_password_login_enabled\"),\n        show_saml_login=current_org.get_setting(\"auth_saml_enabled\"),\n        show_remote_user_login=settings.REMOTE_USER_LOGIN_ENABLED,\n        show_ldap_login=settings.LDAP_LOGIN_ENABLED,\n    )\n\n\n@routes.route(org_scoped_rule(\"/logout\"))\ndef logout(org_slug=None):\n    logout_user()\n    return redirect(get_login_url(next=None))\n\n\ndef base_href():\n    if settings.MULTI_ORG:\n        base_href = url_for(\"redash.index\", _external=True, org_slug=current_org.slug)\n    else:\n        base_href = url_for(\"redash.index\", _external=True)\n\n    return base_href\n\n\ndef date_time_format_config():\n    date_format = current_org.get_setting(\"date_format\")\n    date_format_list = set([\"DD/MM/YY\", \"MM/DD/YY\", \"YYYY-MM-DD\", settings.DATE_FORMAT])\n    time_format = current_org.get_setting(\"time_format\")\n    time_format_list = set([\"HH:mm\", \"HH:mm:ss\", \"HH:mm:ss.SSS\", settings.TIME_FORMAT])\n    return {\n        \"dateFormat\": date_format,\n        \"dateFormatList\": list(date_format_list),\n        \"timeFormatList\": list(time_format_list),\n        \"dateTimeFormat\": \"{0} {1}\".format(date_format, time_format),\n    }\n\n\ndef number_format_config():\n    return {\n        \"integerFormat\": current_org.get_setting(\"integer_format\"),\n        \"floatFormat\": current_org.get_setting(\"float_format\"),\n    }\n\n\ndef client_config():\n    if not current_user.is_api_user() and current_user.is_authenticated:\n        client_config = {\n            \"newVersionAvailable\": bool(get_latest_version()),\n            \"version\": __version__,\n        }\n    else:\n        client_config = {}\n\n    if (\n        current_user.has_permission(\"admin\")\n        and current_org.get_setting(\"beacon_consent\") is None\n    ):\n        client_config[\"showBeaconConsentMessage\"] = True\n\n    defaults = {\n        \"allowScriptsInUserInput\": settings.ALLOW_SCRIPTS_IN_USER_INPUT,\n        \"showPermissionsControl\": current_org.get_setting(\n            \"feature_show_permissions_control\"\n        ),\n        \"allowCustomJSVisualizations\": settings.FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS,\n        \"autoPublishNamedQueries\": settings.FEATURE_AUTO_PUBLISH_NAMED_QUERIES,\n        \"extendedAlertOptions\": settings.FEATURE_EXTENDED_ALERT_OPTIONS,\n        \"mailSettingsMissing\": not settings.email_server_is_configured(),\n        \"dashboardRefreshIntervals\": settings.DASHBOARD_REFRESH_INTERVALS,\n        \"queryRefreshIntervals\": settings.QUERY_REFRESH_INTERVALS,\n        \"googleLoginEnabled\": settings.GOOGLE_OAUTH_ENABLED,\n        \"pageSize\": settings.PAGE_SIZE,\n        \"pageSizeOptions\": settings.PAGE_SIZE_OPTIONS,\n        \"tableCellMaxJSONSize\": settings.TABLE_CELL_MAX_JSON_SIZE,\n    }\n\n    client_config.update(defaults)\n    client_config.update({\"basePath\": base_href()})\n    client_config.update(date_time_format_config())\n    client_config.update(number_format_config())\n\n    return client_config\n\n\ndef messages():\n    messages = []\n\n    if not current_user.is_email_verified:\n        messages.append(\"email-not-verified\")\n\n    if settings.ALLOW_PARAMETERS_IN_EMBEDS:\n        messages.append(\"using-deprecated-embed-feature\")\n\n    return messages\n\n\n@routes.route(\"/api/config\", methods=[\"GET\"])\ndef config(org_slug=None):\n    return json_response(\n        {\"org_slug\": current_org.slug, \"client_config\": client_config()}\n    )\n\n\n@routes.route(org_scoped_rule(\"/api/session\"), methods=[\"GET\"])\n@login_required\ndef session(org_slug=None):\n    if current_user.is_api_user():\n        user = {\"permissions\": [], \"apiKey\": current_user.id}\n    else:\n        user = {\n            \"profile_image_url\": current_user.profile_image_url,\n            \"id\": current_user.id,\n            \"name\": current_user.name,\n            \"email\": current_user.email,\n            \"groups\": current_user.group_ids,\n            \"permissions\": current_user.permissions,\n        }\n\n    return json_response(\n        {\n            \"user\": user,\n            \"messages\": messages(),\n            \"org_slug\": current_org.slug,\n            \"client_config\": client_config(),\n        }\n    )\n", "path": "redash/handlers/authentication.py"}], "after_files": [{"content": "import logging\n\nfrom flask import abort, flash, redirect, render_template, request, url_for\n\nfrom flask_login import current_user, login_required, login_user, logout_user\nfrom redash import __version__, limiter, models, settings\nfrom redash.authentication import current_org, get_login_url, get_next_path\nfrom redash.authentication.account import (\n    BadSignature,\n    SignatureExpired,\n    send_password_reset_email,\n    send_user_disabled_email,\n    send_verify_email,\n    validate_token,\n)\nfrom redash.handlers import routes\nfrom redash.handlers.base import json_response, org_scoped_rule\nfrom redash.version_check import get_latest_version\nfrom sqlalchemy.orm.exc import NoResultFound\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_google_auth_url(next_path):\n    if settings.MULTI_ORG:\n        google_auth_url = url_for(\n            \"google_oauth.authorize_org\", next=next_path, org_slug=current_org.slug\n        )\n    else:\n        google_auth_url = url_for(\"google_oauth.authorize\", next=next_path)\n    return google_auth_url\n\n\ndef render_token_login_page(template, org_slug, token, invite):\n    try:\n        user_id = validate_token(token)\n        org = current_org._get_current_object()\n        user = models.User.get_by_id_and_org(user_id, org)\n    except NoResultFound:\n        logger.exception(\n            \"Bad user id in token. Token= , User id= %s, Org=%s\",\n            user_id,\n            token,\n            org_slug,\n        )\n        return (\n            render_template(\n                \"error.html\",\n                error_message=\"Invalid invite link. Please ask for a new one.\",\n            ),\n            400,\n        )\n    except (SignatureExpired, BadSignature):\n        logger.exception(\"Failed to verify invite token: %s, org=%s\", token, org_slug)\n        return (\n            render_template(\n                \"error.html\",\n                error_message=\"Your invite link has expired. Please ask for a new one.\",\n            ),\n            400,\n        )\n\n    if invite and user.details.get(\"is_invitation_pending\") is False:\n        return (\n            render_template(\n                \"error.html\",\n                error_message=(\n                    \"This invitation has already been accepted. \"\n                    \"Please try resetting your password instead.\"\n                ),\n            ),\n            400,\n        )\n\n    status_code = 200\n    if request.method == \"POST\":\n        if \"password\" not in request.form:\n            flash(\"Bad Request\")\n            status_code = 400\n        elif not request.form[\"password\"]:\n            flash(\"Cannot use empty password.\")\n            status_code = 400\n        elif len(request.form[\"password\"]) < 6:\n            flash(\"Password length is too short (<6).\")\n            status_code = 400\n        else:\n            if invite:\n                user.is_invitation_pending = False\n            user.hash_password(request.form[\"password\"])\n            models.db.session.add(user)\n            login_user(user)\n            models.db.session.commit()\n            return redirect(url_for(\"redash.index\", org_slug=org_slug))\n\n    google_auth_url = get_google_auth_url(url_for(\"redash.index\", org_slug=org_slug))\n\n    return (\n        render_template(\n            template,\n            show_google_openid=settings.GOOGLE_OAUTH_ENABLED,\n            google_auth_url=google_auth_url,\n            show_saml_login=current_org.get_setting(\"auth_saml_enabled\"),\n            show_remote_user_login=settings.REMOTE_USER_LOGIN_ENABLED,\n            show_ldap_login=settings.LDAP_LOGIN_ENABLED,\n            org_slug=org_slug,\n            user=user,\n        ),\n        status_code,\n    )\n\n\n@routes.route(org_scoped_rule(\"/invite/<token>\"), methods=[\"GET\", \"POST\"])\ndef invite(token, org_slug=None):\n    return render_token_login_page(\"invite.html\", org_slug, token, True)\n\n\n@routes.route(org_scoped_rule(\"/reset/<token>\"), methods=[\"GET\", \"POST\"])\ndef reset(token, org_slug=None):\n    return render_token_login_page(\"reset.html\", org_slug, token, False)\n\n\n@routes.route(org_scoped_rule(\"/verify/<token>\"), methods=[\"GET\"])\ndef verify(token, org_slug=None):\n    try:\n        user_id = validate_token(token)\n        org = current_org._get_current_object()\n        user = models.User.get_by_id_and_org(user_id, org)\n    except (BadSignature, NoResultFound):\n        logger.exception(\n            \"Failed to verify email verification token: %s, org=%s\", token, org_slug\n        )\n        return (\n            render_template(\n                \"error.html\",\n                error_message=\"Your verification link is invalid. Please ask for a new one.\",\n            ),\n            400,\n        )\n\n    user.is_email_verified = True\n    models.db.session.add(user)\n    models.db.session.commit()\n\n    template_context = {\"org_slug\": org_slug} if settings.MULTI_ORG else {}\n    next_url = url_for(\"redash.index\", **template_context)\n\n    return render_template(\"verify.html\", next_url=next_url)\n\n\n@routes.route(org_scoped_rule(\"/forgot\"), methods=[\"GET\", \"POST\"])\ndef forgot_password(org_slug=None):\n    if not current_org.get_setting(\"auth_password_login_enabled\"):\n        abort(404)\n\n    submitted = False\n    if request.method == \"POST\" and request.form[\"email\"]:\n        submitted = True\n        email = request.form[\"email\"]\n        try:\n            org = current_org._get_current_object()\n            user = models.User.get_by_email_and_org(email, org)\n            if user.is_disabled:\n                send_user_disabled_email(user)\n            else:\n                send_password_reset_email(user)\n        except NoResultFound:\n            logging.error(\"No user found for forgot password: %s\", email)\n\n    return render_template(\"forgot.html\", submitted=submitted)\n\n\n@routes.route(org_scoped_rule(\"/verification_email/\"), methods=[\"POST\"])\ndef verification_email(org_slug=None):\n    if not current_user.is_email_verified:\n        send_verify_email(current_user, current_org)\n\n    return json_response(\n        {\n            \"message\": \"Please check your email inbox in order to verify your email address.\"\n        }\n    )\n\n\n@routes.route(org_scoped_rule(\"/login\"), methods=[\"GET\", \"POST\"])\n@limiter.limit(settings.THROTTLE_LOGIN_PATTERN)\ndef login(org_slug=None):\n    # We intentionally use == as otherwise it won't actually use the proxy. So weird :O\n    # noinspection PyComparisonWithNone\n    if current_org == None and not settings.MULTI_ORG:\n        return redirect(\"/setup\")\n    elif current_org == None:\n        return redirect(\"/\")\n\n    index_url = url_for(\"redash.index\", org_slug=org_slug)\n    unsafe_next_path = request.args.get(\"next\", index_url)\n    next_path = get_next_path(unsafe_next_path)\n    if current_user.is_authenticated:\n        return redirect(next_path)\n\n    if request.method == \"POST\":\n        try:\n            org = current_org._get_current_object()\n            user = models.User.get_by_email_and_org(request.form[\"email\"], org)\n            if (\n                user\n                and not user.is_disabled\n                and user.verify_password(request.form[\"password\"])\n            ):\n                remember = \"remember\" in request.form\n                login_user(user, remember=remember)\n                return redirect(next_path)\n            else:\n                flash(\"Wrong email or password.\")\n        except NoResultFound:\n            flash(\"Wrong email or password.\")\n\n    google_auth_url = get_google_auth_url(next_path)\n\n    return render_template(\n        \"login.html\",\n        org_slug=org_slug,\n        next=next_path,\n        email=request.form.get(\"email\", \"\"),\n        show_google_openid=settings.GOOGLE_OAUTH_ENABLED,\n        google_auth_url=google_auth_url,\n        show_password_login=current_org.get_setting(\"auth_password_login_enabled\"),\n        show_saml_login=current_org.get_setting(\"auth_saml_enabled\"),\n        show_remote_user_login=settings.REMOTE_USER_LOGIN_ENABLED,\n        show_ldap_login=settings.LDAP_LOGIN_ENABLED,\n    )\n\n\n@routes.route(org_scoped_rule(\"/logout\"))\ndef logout(org_slug=None):\n    logout_user()\n    return redirect(get_login_url(next=None))\n\n\ndef base_href():\n    if settings.MULTI_ORG:\n        base_href = url_for(\"redash.index\", _external=True, org_slug=current_org.slug)\n    else:\n        base_href = url_for(\"redash.index\", _external=True)\n\n    return base_href\n\n\ndef date_time_format_config():\n    date_format = current_org.get_setting(\"date_format\")\n    date_format_list = set([\"DD/MM/YY\", \"MM/DD/YY\", \"YYYY-MM-DD\", settings.DATE_FORMAT])\n    time_format = current_org.get_setting(\"time_format\")\n    time_format_list = set([\"HH:mm\", \"HH:mm:ss\", \"HH:mm:ss.SSS\", settings.TIME_FORMAT])\n    return {\n        \"dateFormat\": date_format,\n        \"dateFormatList\": list(date_format_list),\n        \"timeFormatList\": list(time_format_list),\n        \"dateTimeFormat\": \"{0} {1}\".format(date_format, time_format),\n    }\n\n\ndef number_format_config():\n    return {\n        \"integerFormat\": current_org.get_setting(\"integer_format\"),\n        \"floatFormat\": current_org.get_setting(\"float_format\"),\n    }\n\n\ndef client_config():\n    if not current_user.is_api_user() and current_user.is_authenticated:\n        client_config = {\n            \"newVersionAvailable\": bool(get_latest_version()),\n            \"version\": __version__,\n        }\n    else:\n        client_config = {}\n\n    if (\n        current_user.has_permission(\"admin\")\n        and current_org.get_setting(\"beacon_consent\") is None\n    ):\n        client_config[\"showBeaconConsentMessage\"] = True\n\n    defaults = {\n        \"allowScriptsInUserInput\": settings.ALLOW_SCRIPTS_IN_USER_INPUT,\n        \"showPermissionsControl\": current_org.get_setting(\n            \"feature_show_permissions_control\"\n        ),\n        \"allowCustomJSVisualizations\": settings.FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS,\n        \"autoPublishNamedQueries\": settings.FEATURE_AUTO_PUBLISH_NAMED_QUERIES,\n        \"extendedAlertOptions\": settings.FEATURE_EXTENDED_ALERT_OPTIONS,\n        \"mailSettingsMissing\": not settings.email_server_is_configured(),\n        \"dashboardRefreshIntervals\": settings.DASHBOARD_REFRESH_INTERVALS,\n        \"queryRefreshIntervals\": settings.QUERY_REFRESH_INTERVALS,\n        \"googleLoginEnabled\": settings.GOOGLE_OAUTH_ENABLED,\n        \"ldapLoginEnabled\": settings.LDAP_LOGIN_ENABLED,\n        \"pageSize\": settings.PAGE_SIZE,\n        \"pageSizeOptions\": settings.PAGE_SIZE_OPTIONS,\n        \"tableCellMaxJSONSize\": settings.TABLE_CELL_MAX_JSON_SIZE,\n    }\n\n    client_config.update(defaults)\n    client_config.update({\"basePath\": base_href()})\n    client_config.update(date_time_format_config())\n    client_config.update(number_format_config())\n\n    return client_config\n\n\ndef messages():\n    messages = []\n\n    if not current_user.is_email_verified:\n        messages.append(\"email-not-verified\")\n\n    if settings.ALLOW_PARAMETERS_IN_EMBEDS:\n        messages.append(\"using-deprecated-embed-feature\")\n\n    return messages\n\n\n@routes.route(\"/api/config\", methods=[\"GET\"])\ndef config(org_slug=None):\n    return json_response(\n        {\"org_slug\": current_org.slug, \"client_config\": client_config()}\n    )\n\n\n@routes.route(org_scoped_rule(\"/api/session\"), methods=[\"GET\"])\n@login_required\ndef session(org_slug=None):\n    if current_user.is_api_user():\n        user = {\"permissions\": [], \"apiKey\": current_user.id}\n    else:\n        user = {\n            \"profile_image_url\": current_user.profile_image_url,\n            \"id\": current_user.id,\n            \"name\": current_user.name,\n            \"email\": current_user.email,\n            \"groups\": current_user.group_ids,\n            \"permissions\": current_user.permissions,\n        }\n\n    return json_response(\n        {\n            \"user\": user,\n            \"messages\": messages(),\n            \"org_slug\": current_org.slug,\n            \"client_config\": client_config(),\n        }\n    )\n", "path": "redash/handlers/authentication.py"}]}