We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ADMIN: Autentication is locked if error is thrown
## Environment & Versions
### Environment
 - [x] docker-compose
 - [ ] kubernetes
 - [ ] docker swarm

### Versions
To find your version, get the image name of a mailu container and read  the version from the tag (example for version 1.7).
```
1.9.23
```

## Description
The when the basic auth api throw an error make nginx enter an "invalid" state where it think the it has successfully auteticated.
a thorw-catch block would solve this issue.

## Replication Steps
- Access `https://mail.example.com/webdav/` and login with username only (no `@example.com`)
- Now you cannot "log out". The page is completly stuck. Clearing cache/cookie does nothing.

## Expected behaviour
I would expect an auth rejection. So I can properly login with the right user.

## Logs
```
[2022-03-26 15:48:38,308] ERROR in app: Exception on /internal/auth/basic [GET]
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1699, in _execute_context
    context = constructor(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py", line 1076, in _init_compiled
    param = {
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py", line 1077, in <dictcomp>
    key: processors[key](compiled_params[key])
  File "/usr/lib/python3.9/site-packages/sqlalchemy/sql/type_api.py", line 1379, in process
    return process_param(value, dialect)
  File "/app/mailu/models.py", line 63, in process_bind_param
    raise ValueError('invalid email address (no "@")')
ValueError: invalid email address (no "@")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/flask/app.py", line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/lib/python3.9/site-packages/flask/app.py", line 1518, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/lib/python3.9/site-packages/flask/app.py", line 1516, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/lib/python3.9/site-packages/flask/app.py", line 1502, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "/app/mailu/internal/views/auth.py", line 99, in basic_authentication
    user = models.User.query.get(user_email)
  File "<string>", line 2, in get
  File "/usr/lib/python3.9/site-packages/sqlalchemy/util/deprecations.py", line 390, in warned
    return fn(*args, **kwargs)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/query.py", line 943, in get
    return self._get_impl(ident, loading.load_on_pk_identity)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/query.py", line 947, in _get_impl
    return self.session._get_impl(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/session.py", line 2869, in _get_impl
    return db_load_fn(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/loading.py", line 524, in load_on_pk_identity
    session.execute(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/session.py", line 1689, in execute
    result = conn._execute_20(statement, params or {}, execution_options)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1611, in _execute_20
    return meth(self, args_10style, kwargs_10style, execution_options)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/sql/elements.py", line 325, in _execute_on_connection
    return connection._execute_clauseelement(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1478, in _execute_clauseelement
    ret = self._execute_context(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1705, in _execute_context
    self._handle_dbapi_exception(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 2023, in _handle_dbapi_exception
    util.raise_(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
    raise exception
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1699, in _execute_context
    context = constructor(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py", line 1076, in _init_compiled
    param = {
  File "/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py", line 1077, in <dictcomp>
    key: processors[key](compiled_params[key])
  File "/usr/lib/python3.9/site-packages/sqlalchemy/sql/type_api.py", line 1379, in process
    return process_param(value, dialect)
  File "/app/mailu/models.py", line 63, in process_bind_param
    raise ValueError('invalid email address (no "@")')
sqlalchemy.exc.StatementError: (builtins.ValueError) invalid email address (no "@")

[SQL: SELECT "user".email AS user_email, "user".created_at AS user_created_at, "user".updated_at AS user_updated_at, "user".comment AS user_comment, "user".localpart AS user_localpart, "user".password AS user_password, "user".quota_bytes AS user_quota_bytes, "user".quota_bytes_used AS user_quota_bytes_used, "user".global_admin AS user_global_admin, "user".enabled AS user_enabled, "user".enable_imap AS user_enable_imap, "user".enable_pop AS user_enable_pop, "user".forward_enabled AS user_forward_enabled, "user".forward_destination AS user_forward_destination, "user".forward_keep AS user_forward_keep, "user".reply_enabled AS user_reply_enabled, "user".reply_subject AS user_reply_subject, "user".reply_body AS user_reply_body, "user".reply_startdate AS user_reply_startdate, "user".reply_enddate AS user_reply_enddate, "user".displayed_name AS user_displayed_name, "user".spam_enabled AS user_spam_enabled, "user".spam_threshold AS user_spam_threshold, "user".domain_name AS user_domain_name 
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `core/admin/mailu/internal/views/auth.py`
Content:
```
1 from mailu import models, utils
2 from mailu.internal import internal, nginx
3 from flask import current_app as app
4 
5 import flask
6 import flask_login
7 import base64
8 
9 @internal.route("/auth/email")
10 def nginx_authentication():
11     """ Main authentication endpoint for Nginx email server
12     """
13     client_ip = flask.request.headers["Client-Ip"]
14     headers = flask.request.headers
15     if headers["Auth-Port"] == '25' and headers['Auth-Method'] != 'none':
16         response = flask.Response()
17         response.headers['Auth-Status'] = 'AUTH not supported'
18         response.headers['Auth-Error-Code'] = '502 5.5.1'
19         utils.limiter.rate_limit_ip(client_ip)
20         return response
21     is_from_webmail = headers['Auth-Port'] in ['10143', '10025']
22     if not is_from_webmail and utils.limiter.should_rate_limit_ip(client_ip):
23         status, code = nginx.get_status(flask.request.headers['Auth-Protocol'], 'ratelimit')
24         response = flask.Response()
25         response.headers['Auth-Status'] = status
26         response.headers['Auth-Error-Code'] = code
27         if int(flask.request.headers['Auth-Login-Attempt']) < 10:
28             response.headers['Auth-Wait'] = '3'
29         return response
30     headers = nginx.handle_authentication(flask.request.headers)
31     response = flask.Response()
32     for key, value in headers.items():
33         response.headers[key] = str(value)
34     is_valid_user = False
35     if response.headers.get("Auth-User-Exists") == "True":
36         username = response.headers["Auth-User"]
37         if utils.limiter.should_rate_limit_user(username, client_ip):
38             # FIXME could be done before handle_authentication()
39             status, code = nginx.get_status(flask.request.headers['Auth-Protocol'], 'ratelimit')
40             response = flask.Response()
41             response.headers['Auth-Status'] = status
42             response.headers['Auth-Error-Code'] = code
43             if int(flask.request.headers['Auth-Login-Attempt']) < 10:
44                 response.headers['Auth-Wait'] = '3'
45             return response
46         is_valid_user = True
47     if headers.get("Auth-Status") == "OK":
48         utils.limiter.exempt_ip_from_ratelimits(client_ip)
49     elif is_valid_user:
50         utils.limiter.rate_limit_user(username, client_ip)
51     elif not is_from_webmail:
52         utils.limiter.rate_limit_ip(client_ip)
53     return response
54 
55 @internal.route("/auth/admin")
56 def admin_authentication():
57     """ Fails if the user is not an authenticated admin.
58     """
59     if (not flask_login.current_user.is_anonymous
60         and flask_login.current_user.global_admin
61         and flask_login.current_user.enabled):
62         return ""
63     return flask.abort(403)
64 
65 @internal.route("/auth/user")
66 def user_authentication():
67     """ Fails if the user is not authenticated.
68     """
69     if (not flask_login.current_user.is_anonymous
70         and flask_login.current_user.enabled):
71         response = flask.Response()
72         email = flask_login.current_user.get_id()
73         response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, email, "")
74         response.headers["X-User-Token"] = utils.gen_temp_token(email, flask.session)
75         return response
76     return flask.abort(403)
77 
78 
79 @internal.route("/auth/basic")
80 def basic_authentication():
81     """ Tries to authenticate using the Authorization header.
82     """
83     client_ip = flask.request.headers.get('X-Real-IP', flask.request.remote_addr)
84     if utils.limiter.should_rate_limit_ip(client_ip):
85         response = flask.Response(status=401)
86         response.headers["WWW-Authenticate"] = 'Basic realm="Authentication rate limit from one source exceeded"'
87         response.headers['Retry-After'] = '60'
88         return response
89     authorization = flask.request.headers.get("Authorization")
90     if authorization and authorization.startswith("Basic "):
91         encoded = authorization.replace("Basic ", "")
92         user_email, password = base64.b64decode(encoded).split(b":", 1)
93         user_email = user_email.decode("utf8")
94         if utils.limiter.should_rate_limit_user(user_email, client_ip):
95             response = flask.Response(status=401)
96             response.headers["WWW-Authenticate"] = 'Basic realm="Authentication rate limit for this username exceeded"'
97             response.headers['Retry-After'] = '60'
98             return response
99         user = models.User.query.get(user_email)
100         if user and nginx.check_credentials(user, password.decode('utf-8'), client_ip, "web"):
101             response = flask.Response()
102             response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, user.email, "")
103             utils.limiter.exempt_ip_from_ratelimits(client_ip)
104             return response
105         utils.limiter.rate_limit_user(user_email, client_ip) if user else utils.limiter.rate_limit_ip(client_ip)
106     response = flask.Response(status=401)
107     response.headers["WWW-Authenticate"] = 'Basic realm="Login Required"'
108     return response
109
```
--- 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/core/admin/mailu/internal/views/auth.py b/core/admin/mailu/internal/views/auth.py
--- a/core/admin/mailu/internal/views/auth.py
+++ b/core/admin/mailu/internal/views/auth.py
@@ -5,6 +5,7 @@
 import flask
 import flask_login
 import base64
+import sqlalchemy.exc
 
 @internal.route("/auth/email")
 def nginx_authentication():
@@ -96,13 +97,19 @@
             response.headers["WWW-Authenticate"] = 'Basic realm="Authentication rate limit for this username exceeded"'
             response.headers['Retry-After'] = '60'
             return response
-        user = models.User.query.get(user_email)
-        if user and nginx.check_credentials(user, password.decode('utf-8'), client_ip, "web"):
-            response = flask.Response()
-            response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, user.email, "")
-            utils.limiter.exempt_ip_from_ratelimits(client_ip)
-            return response
-        utils.limiter.rate_limit_user(user_email, client_ip) if user else utils.limiter.rate_limit_ip(client_ip)
+        try:
+            user = models.User.query.get(user_email) if '@' in user_email else None
+        except sqlalchemy.exc.StatementError as exc:
+            exc = str(exc).split('\n', 1)[0]
+            app.logger.warn(f'Invalid user {user_email!r}: {exc}')
+        else:
+            if user is not None and nginx.check_credentials(user, password.decode('utf-8'), client_ip, "web"):
+                response = flask.Response()
+                response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, user.email, "")
+                utils.limiter.exempt_ip_from_ratelimits(client_ip)
+                return response
+            # We failed check_credentials
+            utils.limiter.rate_limit_user(user_email, client_ip) if user else utils.limiter.rate_limit_ip(client_ip)
     response = flask.Response(status=401)
     response.headers["WWW-Authenticate"] = 'Basic realm="Login Required"'
     return response

{"golden_diff": "diff --git a/core/admin/mailu/internal/views/auth.py b/core/admin/mailu/internal/views/auth.py\n--- a/core/admin/mailu/internal/views/auth.py\n+++ b/core/admin/mailu/internal/views/auth.py\n@@ -5,6 +5,7 @@\n import flask\n import flask_login\n import base64\n+import sqlalchemy.exc\n \n @internal.route(\"/auth/email\")\n def nginx_authentication():\n@@ -96,13 +97,19 @@\n             response.headers[\"WWW-Authenticate\"] = 'Basic realm=\"Authentication rate limit for this username exceeded\"'\n             response.headers['Retry-After'] = '60'\n             return response\n-        user = models.User.query.get(user_email)\n-        if user and nginx.check_credentials(user, password.decode('utf-8'), client_ip, \"web\"):\n-            response = flask.Response()\n-            response.headers[\"X-User\"] = models.IdnaEmail.process_bind_param(flask_login, user.email, \"\")\n-            utils.limiter.exempt_ip_from_ratelimits(client_ip)\n-            return response\n-        utils.limiter.rate_limit_user(user_email, client_ip) if user else utils.limiter.rate_limit_ip(client_ip)\n+        try:\n+            user = models.User.query.get(user_email) if '@' in user_email else None\n+        except sqlalchemy.exc.StatementError as exc:\n+            exc = str(exc).split('\\n', 1)[0]\n+            app.logger.warn(f'Invalid user {user_email!r}: {exc}')\n+        else:\n+            if user is not None and nginx.check_credentials(user, password.decode('utf-8'), client_ip, \"web\"):\n+                response = flask.Response()\n+                response.headers[\"X-User\"] = models.IdnaEmail.process_bind_param(flask_login, user.email, \"\")\n+                utils.limiter.exempt_ip_from_ratelimits(client_ip)\n+                return response\n+            # We failed check_credentials\n+            utils.limiter.rate_limit_user(user_email, client_ip) if user else utils.limiter.rate_limit_ip(client_ip)\n     response = flask.Response(status=401)\n     response.headers[\"WWW-Authenticate\"] = 'Basic realm=\"Login Required\"'\n     return response\n", "issue": "ADMIN: Autentication is locked if error is thrown\n## Environment & Versions\r\n### Environment\r\n - [x] docker-compose\r\n - [ ] kubernetes\r\n - [ ] docker swarm\r\n\r\n### Versions\r\nTo find your version, get the image name of a mailu container and read  the version from the tag (example for version 1.7).\r\n```\r\n1.9.23\r\n```\r\n\r\n## Description\r\nThe when the basic auth api throw an error make nginx enter an \"invalid\" state where it think the it has successfully auteticated.\r\na thorw-catch block would solve this issue.\r\n\r\n## Replication Steps\r\n- Access `https://mail.example.com/webdav/` and login with username only (no `@example.com`)\r\n- Now you cannot \"log out\". The page is completly stuck. Clearing cache/cookie does nothing.\r\n\r\n## Expected behaviour\r\nI would expect an auth rejection. So I can properly login with the right user.\r\n\r\n## Logs\r\n```\r\n[2022-03-26 15:48:38,308] ERROR in app: Exception on /internal/auth/basic [GET]\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py\", line 1699, in _execute_context\r\n    context = constructor(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py\", line 1076, in _init_compiled\r\n    param = {\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py\", line 1077, in <dictcomp>\r\n    key: processors[key](compiled_params[key])\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/sql/type_api.py\", line 1379, in process\r\n    return process_param(value, dialect)\r\n  File \"/app/mailu/models.py\", line 63, in process_bind_param\r\n    raise ValueError('invalid email address (no \"@\")')\r\nValueError: invalid email address (no \"@\")\r\nThe above exception was the direct cause of the following exception:\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.9/site-packages/flask/app.py\", line 2073, in wsgi_app\r\n    response = self.full_dispatch_request()\r\n  File \"/usr/lib/python3.9/site-packages/flask/app.py\", line 1518, in full_dispatch_request\r\n    rv = self.handle_user_exception(e)\r\n  File \"/usr/lib/python3.9/site-packages/flask/app.py\", line 1516, in full_dispatch_request\r\n    rv = self.dispatch_request()\r\n  File \"/usr/lib/python3.9/site-packages/flask/app.py\", line 1502, in dispatch_request\r\n    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)\r\n  File \"/app/mailu/internal/views/auth.py\", line 99, in basic_authentication\r\n    user = models.User.query.get(user_email)\r\n  File \"<string>\", line 2, in get\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/util/deprecations.py\", line 390, in warned\r\n    return fn(*args, **kwargs)\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/orm/query.py\", line 943, in get\r\n    return self._get_impl(ident, loading.load_on_pk_identity)\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/orm/query.py\", line 947, in _get_impl\r\n    return self.session._get_impl(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/orm/session.py\", line 2869, in _get_impl\r\n    return db_load_fn(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/orm/loading.py\", line 524, in load_on_pk_identity\r\n    session.execute(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/orm/session.py\", line 1689, in execute\r\n    result = conn._execute_20(statement, params or {}, execution_options)\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py\", line 1611, in _execute_20\r\n    return meth(self, args_10style, kwargs_10style, execution_options)\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/sql/elements.py\", line 325, in _execute_on_connection\r\n    return connection._execute_clauseelement(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py\", line 1478, in _execute_clauseelement\r\n    ret = self._execute_context(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py\", line 1705, in _execute_context\r\n    self._handle_dbapi_exception(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py\", line 2023, in _handle_dbapi_exception\r\n    util.raise_(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/util/compat.py\", line 207, in raise_\r\n    raise exception\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/base.py\", line 1699, in _execute_context\r\n    context = constructor(\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py\", line 1076, in _init_compiled\r\n    param = {\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/engine/default.py\", line 1077, in <dictcomp>\r\n    key: processors[key](compiled_params[key])\r\n  File \"/usr/lib/python3.9/site-packages/sqlalchemy/sql/type_api.py\", line 1379, in process\r\n    return process_param(value, dialect)\r\n  File \"/app/mailu/models.py\", line 63, in process_bind_param\r\n    raise ValueError('invalid email address (no \"@\")')\r\nsqlalchemy.exc.StatementError: (builtins.ValueError) invalid email address (no \"@\")\r\n\r\n[SQL: SELECT \"user\".email AS user_email, \"user\".created_at AS user_created_at, \"user\".updated_at AS user_updated_at, \"user\".comment AS user_comment, \"user\".localpart AS user_localpart, \"user\".password AS user_password, \"user\".quota_bytes AS user_quota_bytes, \"user\".quota_bytes_used AS user_quota_bytes_used, \"user\".global_admin AS user_global_admin, \"user\".enabled AS user_enabled, \"user\".enable_imap AS user_enable_imap, \"user\".enable_pop AS user_enable_pop, \"user\".forward_enabled AS user_forward_enabled, \"user\".forward_destination AS user_forward_destination, \"user\".forward_keep AS user_forward_keep, \"user\".reply_enabled AS user_reply_enabled, \"user\".reply_subject AS user_reply_subject, \"user\".reply_body AS user_reply_body, \"user\".reply_startdate AS user_reply_startdate, \"user\".reply_enddate AS user_reply_enddate, \"user\".displayed_name AS user_displayed_name, \"user\".spam_enabled AS user_spam_enabled, \"user\".spam_threshold AS user_spam_threshold, \"user\".domain_name AS user_domain_name \r\n```\r\n\n", "before_files": [{"content": "from mailu import models, utils\nfrom mailu.internal import internal, nginx\nfrom flask import current_app as app\n\nimport flask\nimport flask_login\nimport base64\n\n@internal.route(\"/auth/email\")\ndef nginx_authentication():\n    \"\"\" Main authentication endpoint for Nginx email server\n    \"\"\"\n    client_ip = flask.request.headers[\"Client-Ip\"]\n    headers = flask.request.headers\n    if headers[\"Auth-Port\"] == '25' and headers['Auth-Method'] != 'none':\n        response = flask.Response()\n        response.headers['Auth-Status'] = 'AUTH not supported'\n        response.headers['Auth-Error-Code'] = '502 5.5.1'\n        utils.limiter.rate_limit_ip(client_ip)\n        return response\n    is_from_webmail = headers['Auth-Port'] in ['10143', '10025']\n    if not is_from_webmail and utils.limiter.should_rate_limit_ip(client_ip):\n        status, code = nginx.get_status(flask.request.headers['Auth-Protocol'], 'ratelimit')\n        response = flask.Response()\n        response.headers['Auth-Status'] = status\n        response.headers['Auth-Error-Code'] = code\n        if int(flask.request.headers['Auth-Login-Attempt']) < 10:\n            response.headers['Auth-Wait'] = '3'\n        return response\n    headers = nginx.handle_authentication(flask.request.headers)\n    response = flask.Response()\n    for key, value in headers.items():\n        response.headers[key] = str(value)\n    is_valid_user = False\n    if response.headers.get(\"Auth-User-Exists\") == \"True\":\n        username = response.headers[\"Auth-User\"]\n        if utils.limiter.should_rate_limit_user(username, client_ip):\n            # FIXME could be done before handle_authentication()\n            status, code = nginx.get_status(flask.request.headers['Auth-Protocol'], 'ratelimit')\n            response = flask.Response()\n            response.headers['Auth-Status'] = status\n            response.headers['Auth-Error-Code'] = code\n            if int(flask.request.headers['Auth-Login-Attempt']) < 10:\n                response.headers['Auth-Wait'] = '3'\n            return response\n        is_valid_user = True\n    if headers.get(\"Auth-Status\") == \"OK\":\n        utils.limiter.exempt_ip_from_ratelimits(client_ip)\n    elif is_valid_user:\n        utils.limiter.rate_limit_user(username, client_ip)\n    elif not is_from_webmail:\n        utils.limiter.rate_limit_ip(client_ip)\n    return response\n\n@internal.route(\"/auth/admin\")\ndef admin_authentication():\n    \"\"\" Fails if the user is not an authenticated admin.\n    \"\"\"\n    if (not flask_login.current_user.is_anonymous\n        and flask_login.current_user.global_admin\n        and flask_login.current_user.enabled):\n        return \"\"\n    return flask.abort(403)\n\n@internal.route(\"/auth/user\")\ndef user_authentication():\n    \"\"\" Fails if the user is not authenticated.\n    \"\"\"\n    if (not flask_login.current_user.is_anonymous\n        and flask_login.current_user.enabled):\n        response = flask.Response()\n        email = flask_login.current_user.get_id()\n        response.headers[\"X-User\"] = models.IdnaEmail.process_bind_param(flask_login, email, \"\")\n        response.headers[\"X-User-Token\"] = utils.gen_temp_token(email, flask.session)\n        return response\n    return flask.abort(403)\n\n\n@internal.route(\"/auth/basic\")\ndef basic_authentication():\n    \"\"\" Tries to authenticate using the Authorization header.\n    \"\"\"\n    client_ip = flask.request.headers.get('X-Real-IP', flask.request.remote_addr)\n    if utils.limiter.should_rate_limit_ip(client_ip):\n        response = flask.Response(status=401)\n        response.headers[\"WWW-Authenticate\"] = 'Basic realm=\"Authentication rate limit from one source exceeded\"'\n        response.headers['Retry-After'] = '60'\n        return response\n    authorization = flask.request.headers.get(\"Authorization\")\n    if authorization and authorization.startswith(\"Basic \"):\n        encoded = authorization.replace(\"Basic \", \"\")\n        user_email, password = base64.b64decode(encoded).split(b\":\", 1)\n        user_email = user_email.decode(\"utf8\")\n        if utils.limiter.should_rate_limit_user(user_email, client_ip):\n            response = flask.Response(status=401)\n            response.headers[\"WWW-Authenticate\"] = 'Basic realm=\"Authentication rate limit for this username exceeded\"'\n            response.headers['Retry-After'] = '60'\n            return response\n        user = models.User.query.get(user_email)\n        if user and nginx.check_credentials(user, password.decode('utf-8'), client_ip, \"web\"):\n            response = flask.Response()\n            response.headers[\"X-User\"] = models.IdnaEmail.process_bind_param(flask_login, user.email, \"\")\n            utils.limiter.exempt_ip_from_ratelimits(client_ip)\n            return response\n        utils.limiter.rate_limit_user(user_email, client_ip) if user else utils.limiter.rate_limit_ip(client_ip)\n    response = flask.Response(status=401)\n    response.headers[\"WWW-Authenticate\"] = 'Basic realm=\"Login Required\"'\n    return response\n", "path": "core/admin/mailu/internal/views/auth.py"}], "after_files": [{"content": "from mailu import models, utils\nfrom mailu.internal import internal, nginx\nfrom flask import current_app as app\n\nimport flask\nimport flask_login\nimport base64\nimport sqlalchemy.exc\n\n@internal.route(\"/auth/email\")\ndef nginx_authentication():\n    \"\"\" Main authentication endpoint for Nginx email server\n    \"\"\"\n    client_ip = flask.request.headers[\"Client-Ip\"]\n    headers = flask.request.headers\n    if headers[\"Auth-Port\"] == '25' and headers['Auth-Method'] != 'none':\n        response = flask.Response()\n        response.headers['Auth-Status'] = 'AUTH not supported'\n        response.headers['Auth-Error-Code'] = '502 5.5.1'\n        utils.limiter.rate_limit_ip(client_ip)\n        return response\n    is_from_webmail = headers['Auth-Port'] in ['10143', '10025']\n    if not is_from_webmail and utils.limiter.should_rate_limit_ip(client_ip):\n        status, code = nginx.get_status(flask.request.headers['Auth-Protocol'], 'ratelimit')\n        response = flask.Response()\n        response.headers['Auth-Status'] = status\n        response.headers['Auth-Error-Code'] = code\n        if int(flask.request.headers['Auth-Login-Attempt']) < 10:\n            response.headers['Auth-Wait'] = '3'\n        return response\n    headers = nginx.handle_authentication(flask.request.headers)\n    response = flask.Response()\n    for key, value in headers.items():\n        response.headers[key] = str(value)\n    is_valid_user = False\n    if response.headers.get(\"Auth-User-Exists\") == \"True\":\n        username = response.headers[\"Auth-User\"]\n        if utils.limiter.should_rate_limit_user(username, client_ip):\n            # FIXME could be done before handle_authentication()\n            status, code = nginx.get_status(flask.request.headers['Auth-Protocol'], 'ratelimit')\n            response = flask.Response()\n            response.headers['Auth-Status'] = status\n            response.headers['Auth-Error-Code'] = code\n            if int(flask.request.headers['Auth-Login-Attempt']) < 10:\n                response.headers['Auth-Wait'] = '3'\n            return response\n        is_valid_user = True\n    if headers.get(\"Auth-Status\") == \"OK\":\n        utils.limiter.exempt_ip_from_ratelimits(client_ip)\n    elif is_valid_user:\n        utils.limiter.rate_limit_user(username, client_ip)\n    elif not is_from_webmail:\n        utils.limiter.rate_limit_ip(client_ip)\n    return response\n\n@internal.route(\"/auth/admin\")\ndef admin_authentication():\n    \"\"\" Fails if the user is not an authenticated admin.\n    \"\"\"\n    if (not flask_login.current_user.is_anonymous\n        and flask_login.current_user.global_admin\n        and flask_login.current_user.enabled):\n        return \"\"\n    return flask.abort(403)\n\n@internal.route(\"/auth/user\")\ndef user_authentication():\n    \"\"\" Fails if the user is not authenticated.\n    \"\"\"\n    if (not flask_login.current_user.is_anonymous\n        and flask_login.current_user.enabled):\n        response = flask.Response()\n        email = flask_login.current_user.get_id()\n        response.headers[\"X-User\"] = models.IdnaEmail.process_bind_param(flask_login, email, \"\")\n        response.headers[\"X-User-Token\"] = utils.gen_temp_token(email, flask.session)\n        return response\n    return flask.abort(403)\n\n\n@internal.route(\"/auth/basic\")\ndef basic_authentication():\n    \"\"\" Tries to authenticate using the Authorization header.\n    \"\"\"\n    client_ip = flask.request.headers.get('X-Real-IP', flask.request.remote_addr)\n    if utils.limiter.should_rate_limit_ip(client_ip):\n        response = flask.Response(status=401)\n        response.headers[\"WWW-Authenticate\"] = 'Basic realm=\"Authentication rate limit from one source exceeded\"'\n        response.headers['Retry-After'] = '60'\n        return response\n    authorization = flask.request.headers.get(\"Authorization\")\n    if authorization and authorization.startswith(\"Basic \"):\n        encoded = authorization.replace(\"Basic \", \"\")\n        user_email, password = base64.b64decode(encoded).split(b\":\", 1)\n        user_email = user_email.decode(\"utf8\")\n        if utils.limiter.should_rate_limit_user(user_email, client_ip):\n            response = flask.Response(status=401)\n            response.headers[\"WWW-Authenticate\"] = 'Basic realm=\"Authentication rate limit for this username exceeded\"'\n            response.headers['Retry-After'] = '60'\n            return response\n        try:\n            user = models.User.query.get(user_email) if '@' in user_email else None\n        except sqlalchemy.exc.StatementError as exc:\n            exc = str(exc).split('\\n', 1)[0]\n            app.logger.warn(f'Invalid user {user_email!r}: {exc}')\n        else:\n            if user is not None and nginx.check_credentials(user, password.decode('utf-8'), client_ip, \"web\"):\n                response = flask.Response()\n                response.headers[\"X-User\"] = models.IdnaEmail.process_bind_param(flask_login, user.email, \"\")\n                utils.limiter.exempt_ip_from_ratelimits(client_ip)\n                return response\n            # We failed check_credentials\n            utils.limiter.rate_limit_user(user_email, client_ip) if user else utils.limiter.rate_limit_ip(client_ip)\n    response = flask.Response(status=401)\n    response.headers[\"WWW-Authenticate\"] = 'Basic realm=\"Login Required\"'\n    return response\n", "path": "core/admin/mailu/internal/views/auth.py"}]}