We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
POST /api/v1/users fails with exception
### Apache Airflow version

main (development)

### Operating System

From Astronomer’s QA team

### Versions of Apache Airflow Providers

_No response_

### Deployment

Astronomer

### Deployment details

_No response_

### What happened

When adding a new user, The following exception is emitted:

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.9/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/decorator.py", line 48, in wrapper
    response = function(request)
  File "/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/uri_parsing.py", line 144, in wrapper
    response = function(request)
  File "/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/validation.py", line 184, in wrapper
    response = function(request)
  File "/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/response.py", line 103, in wrapper
    response = function(request)
  File "/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/parameter.py", line 121, in wrapper
    return function(**kwargs)
  File "/usr/local/lib/python3.9/site-packages/airflow/api_connexion/security.py", line 47, in decorated
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/airflow/api_connexion/endpoints/user_endpoint.py", line 105, in post_user
    user.roles.extend(roles_to_add)
AttributeError: 'bool' object has no attribute 'roles'
```

The immediate cause to this exception is F.A.B. returns `False` when it fails to add a new user. The problem, however, is _why_ excactly it failed. This is the payload used:

```json
{
    "username": "username6",
    "password": "password1",
    "email": "username5@example.com",
    "first_name": "user2",
    "last_name": "test1",
    "roles":[{"name":"Admin"},{"name":"Viewer"}]
}
```

This went through validation, therefore we know

1. The POST-ing user has permission to create a new user.
2. The format is correct (including the nested roles).
3. There is not already an existing `username6` in the database.
4. All listed roles exist.

(All these are already covered by unit tests.)

Further complicating the issue is F.A.B.’s security manager swallows an exception when this happens, and only logs the exception to the server. And we’re having trouble locating that line of log. It’s quite difficult to diagnose further, so I’m posting this hoping someone has better luck reproducing this.

I will submit a fix to correct the immediate issue, making the API emit 500 with something like “Failed to create user for unknown reason” to make the failure _slightly_ less confusing.

### What you expected to happen

_No response_

### How to reproduce

_No response_

### Anything else

_No response_

### Are you willing to submit PR?

- [X] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `airflow/api_connexion/endpoints/user_endpoint.py`
Content:
```
1 # Licensed to the Apache Software Foundation (ASF) under one
2 # or more contributor license agreements.  See the NOTICE file
3 # distributed with this work for additional information
4 # regarding copyright ownership.  The ASF licenses this file
5 # to you under the Apache License, Version 2.0 (the
6 # "License"); you may not use this file except in compliance
7 # with the License.  You may obtain a copy of the License at
8 #
9 #   http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing,
12 # software distributed under the License is distributed on an
13 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 # KIND, either express or implied.  See the License for the
15 # specific language governing permissions and limitations
16 # under the License.
17 from flask import current_app, request
18 from flask_appbuilder.security.sqla.models import User
19 from marshmallow import ValidationError
20 from sqlalchemy import func
21 from werkzeug.security import generate_password_hash
22 
23 from airflow.api_connexion import security
24 from airflow.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound
25 from airflow.api_connexion.parameters import apply_sorting, check_limit, format_parameters
26 from airflow.api_connexion.schemas.user_schema import (
27     UserCollection,
28     user_collection_item_schema,
29     user_collection_schema,
30     user_schema,
31 )
32 from airflow.security import permissions
33 
34 
35 @security.requires_access([(permissions.ACTION_CAN_READ, permissions.RESOURCE_USER)])
36 def get_user(username):
37     """Get a user"""
38     ab_security_manager = current_app.appbuilder.sm
39     user = ab_security_manager.find_user(username=username)
40     if not user:
41         raise NotFound(title="User not found", detail=f"The User with username `{username}` was not found")
42     return user_collection_item_schema.dump(user)
43 
44 
45 @security.requires_access([(permissions.ACTION_CAN_READ, permissions.RESOURCE_USER)])
46 @format_parameters({'limit': check_limit})
47 def get_users(limit, order_by='id', offset=None):
48     """Get users"""
49     appbuilder = current_app.appbuilder
50     session = appbuilder.get_session
51     total_entries = session.query(func.count(User.id)).scalar()
52     to_replace = {"user_id": "id"}
53     allowed_filter_attrs = [
54         "user_id",
55         'id',
56         "first_name",
57         "last_name",
58         "user_name",
59         "email",
60         "is_active",
61         "role",
62     ]
63     query = session.query(User)
64     query = apply_sorting(query, order_by, to_replace, allowed_filter_attrs)
65     users = query.offset(offset).limit(limit).all()
66 
67     return user_collection_schema.dump(UserCollection(users=users, total_entries=total_entries))
68 
69 
70 @security.requires_access([(permissions.ACTION_CAN_CREATE, permissions.RESOURCE_USER)])
71 def post_user():
72     """Create a new user"""
73     try:
74         data = user_schema.load(request.json)
75     except ValidationError as e:
76         raise BadRequest(detail=str(e.messages))
77 
78     security_manager = current_app.appbuilder.sm
79 
80     user = security_manager.find_user(username=data["username"])
81     if user is not None:
82         detail = f"Username `{user.username}` already exists. Use PATCH to update."
83         raise AlreadyExists(detail=detail)
84 
85     roles_to_add = []
86     missing_role_names = []
87     for role_data in data.pop("roles", ()):
88         role_name = role_data["name"]
89         role = security_manager.find_role(role_name)
90         if role is None:
91             missing_role_names.append(role_name)
92         else:
93             roles_to_add.append(role)
94     if missing_role_names:
95         detail = f"Unknown roles: {', '.join(repr(n) for n in missing_role_names)}"
96         raise BadRequest(detail=detail)
97 
98     if roles_to_add:
99         default_role = roles_to_add.pop()
100     else:  # No roles provided, use the F.A.B's default registered user role.
101         default_role = security_manager.find_role(security_manager.auth_user_registration_role)
102 
103     user = security_manager.add_user(role=default_role, **data)
104     if roles_to_add:
105         user.roles.extend(roles_to_add)
106         security_manager.update_user(user)
107     return user_schema.dump(user)
108 
109 
110 @security.requires_access([(permissions.ACTION_CAN_EDIT, permissions.RESOURCE_USER)])
111 def patch_user(username, update_mask=None):
112     """Update a role"""
113     try:
114         data = user_schema.load(request.json)
115     except ValidationError as e:
116         raise BadRequest(detail=str(e.messages))
117 
118     security_manager = current_app.appbuilder.sm
119 
120     user = security_manager.find_user(username=username)
121     if user is None:
122         detail = f"The User with username `{username}` was not found"
123         raise NotFound(title="User not found", detail=detail)
124 
125     # Get fields to update. 'username' is always excluded (and it's an error to
126     # include it in update_maek).
127     if update_mask is not None:
128         masked_data = {}
129         missing_mask_names = []
130         for field in update_mask:
131             field = field.strip()
132             try:
133                 masked_data[field] = data[field]
134             except KeyError:
135                 missing_mask_names.append(field)
136         if missing_mask_names:
137             detail = f"Unknown update masks: {', '.join(repr(n) for n in missing_mask_names)}"
138             raise BadRequest(detail=detail)
139         if "username" in masked_data:
140             raise BadRequest("Cannot update fields: 'username'")
141         data = masked_data
142     else:
143         data.pop("username", None)
144 
145     if "roles" in data:
146         roles_to_update = []
147         missing_role_names = []
148         for role_data in data.pop("roles", ()):
149             role_name = role_data["name"]
150             role = security_manager.find_role(role_name)
151             if role is None:
152                 missing_role_names.append(role_name)
153             else:
154                 roles_to_update.append(role)
155         if missing_role_names:
156             detail = f"Unknown roles: {', '.join(repr(n) for n in missing_role_names)}"
157             raise BadRequest(detail=detail)
158     else:
159         roles_to_update = None  # Don't change existing value.
160 
161     if "password" in data:
162         user.password = generate_password_hash(data.pop("password"))
163     if roles_to_update is not None:
164         user.roles = roles_to_update
165     for key, value in data.items():
166         setattr(user, key, value)
167     security_manager.update_user(user)
168 
169     return user_schema.dump(user)
170 
171 
172 @security.requires_access([(permissions.ACTION_CAN_DELETE, permissions.RESOURCE_USER)])
173 def delete_user(username):
174     """Delete a user"""
175     security_manager = current_app.appbuilder.sm
176 
177     user = security_manager.find_user(username=username)
178     if user is None:
179         detail = f"The User with username `{username}` was not found"
180         raise NotFound(title="User not found", detail=detail)
181 
182     user.roles = []  # Clear foreign keys on this user first.
183     security_manager.get_session.delete(user)
184     security_manager.get_session.commit()
185
```
--- 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/airflow/api_connexion/endpoints/user_endpoint.py b/airflow/api_connexion/endpoints/user_endpoint.py
--- a/airflow/api_connexion/endpoints/user_endpoint.py
+++ b/airflow/api_connexion/endpoints/user_endpoint.py
@@ -21,7 +21,7 @@
 from werkzeug.security import generate_password_hash
 
 from airflow.api_connexion import security
-from airflow.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound
+from airflow.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound, Unknown
 from airflow.api_connexion.parameters import apply_sorting, check_limit, format_parameters
 from airflow.api_connexion.schemas.user_schema import (
     UserCollection,
@@ -76,10 +76,14 @@
         raise BadRequest(detail=str(e.messages))
 
     security_manager = current_app.appbuilder.sm
+    username = data["username"]
+    email = data["email"]
 
-    user = security_manager.find_user(username=data["username"])
-    if user is not None:
-        detail = f"Username `{user.username}` already exists. Use PATCH to update."
+    if security_manager.find_user(username=username):
+        detail = f"Username `{username}` already exists. Use PATCH to update."
+        raise AlreadyExists(detail=detail)
+    if security_manager.find_user(email=email):
+        detail = f"The email `{email}` is already taken."
         raise AlreadyExists(detail=detail)
 
     roles_to_add = []
@@ -101,6 +105,10 @@
         default_role = security_manager.find_role(security_manager.auth_user_registration_role)
 
     user = security_manager.add_user(role=default_role, **data)
+    if not user:
+        detail = f"Failed to add user `{username}`."
+        return Unknown(detail=detail)
+
     if roles_to_add:
         user.roles.extend(roles_to_add)
         security_manager.update_user(user)

{"golden_diff": "diff --git a/airflow/api_connexion/endpoints/user_endpoint.py b/airflow/api_connexion/endpoints/user_endpoint.py\n--- a/airflow/api_connexion/endpoints/user_endpoint.py\n+++ b/airflow/api_connexion/endpoints/user_endpoint.py\n@@ -21,7 +21,7 @@\n from werkzeug.security import generate_password_hash\n \n from airflow.api_connexion import security\n-from airflow.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound\n+from airflow.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound, Unknown\n from airflow.api_connexion.parameters import apply_sorting, check_limit, format_parameters\n from airflow.api_connexion.schemas.user_schema import (\n     UserCollection,\n@@ -76,10 +76,14 @@\n         raise BadRequest(detail=str(e.messages))\n \n     security_manager = current_app.appbuilder.sm\n+    username = data[\"username\"]\n+    email = data[\"email\"]\n \n-    user = security_manager.find_user(username=data[\"username\"])\n-    if user is not None:\n-        detail = f\"Username `{user.username}` already exists. Use PATCH to update.\"\n+    if security_manager.find_user(username=username):\n+        detail = f\"Username `{username}` already exists. Use PATCH to update.\"\n+        raise AlreadyExists(detail=detail)\n+    if security_manager.find_user(email=email):\n+        detail = f\"The email `{email}` is already taken.\"\n         raise AlreadyExists(detail=detail)\n \n     roles_to_add = []\n@@ -101,6 +105,10 @@\n         default_role = security_manager.find_role(security_manager.auth_user_registration_role)\n \n     user = security_manager.add_user(role=default_role, **data)\n+    if not user:\n+        detail = f\"Failed to add user `{username}`.\"\n+        return Unknown(detail=detail)\n+\n     if roles_to_add:\n         user.roles.extend(roles_to_add)\n         security_manager.update_user(user)\n", "issue": "POST /api/v1/users fails with exception\n### Apache Airflow version\n\nmain (development)\n\n### Operating System\n\nFrom Astronomer\u2019s QA team\n\n### Versions of Apache Airflow Providers\n\n_No response_\n\n### Deployment\n\nAstronomer\n\n### Deployment details\n\n_No response_\n\n### What happened\n\nWhen adding a new user, The following exception is emitted:\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.9/site-packages/flask/app.py\", line 2447, in wsgi_app\r\n    response = self.full_dispatch_request()\r\n  File \"/usr/local/lib/python3.9/site-packages/flask/app.py\", line 1952, in full_dispatch_request\r\n    rv = self.handle_user_exception(e)\r\n  File \"/usr/local/lib/python3.9/site-packages/flask/app.py\", line 1821, in handle_user_exception\r\n    reraise(exc_type, exc_value, tb)\r\n  File \"/usr/local/lib/python3.9/site-packages/flask/_compat.py\", line 39, in reraise\r\n    raise value\r\n  File \"/usr/local/lib/python3.9/site-packages/flask/app.py\", line 1950, in full_dispatch_request\r\n    rv = self.dispatch_request()\r\n  File \"/usr/local/lib/python3.9/site-packages/flask/app.py\", line 1936, in dispatch_request\r\n    return self.view_functions[rule.endpoint](**req.view_args)\r\n  File \"/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/decorator.py\", line 48, in wrapper\r\n    response = function(request)\r\n  File \"/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/uri_parsing.py\", line 144, in wrapper\r\n    response = function(request)\r\n  File \"/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/validation.py\", line 184, in wrapper\r\n    response = function(request)\r\n  File \"/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/response.py\", line 103, in wrapper\r\n    response = function(request)\r\n  File \"/usr/local/lib/python3.9/site-packages/airflow/_vendor/connexion/decorators/parameter.py\", line 121, in wrapper\r\n    return function(**kwargs)\r\n  File \"/usr/local/lib/python3.9/site-packages/airflow/api_connexion/security.py\", line 47, in decorated\r\n    return func(*args, **kwargs)\r\n  File \"/usr/local/lib/python3.9/site-packages/airflow/api_connexion/endpoints/user_endpoint.py\", line 105, in post_user\r\n    user.roles.extend(roles_to_add)\r\nAttributeError: 'bool' object has no attribute 'roles'\r\n```\r\n\r\nThe immediate cause to this exception is F.A.B. returns `False` when it fails to add a new user. The problem, however, is _why_ excactly it failed. This is the payload used:\r\n\r\n```json\r\n{\r\n    \"username\": \"username6\",\r\n    \"password\": \"password1\",\r\n    \"email\": \"username5@example.com\",\r\n    \"first_name\": \"user2\",\r\n    \"last_name\": \"test1\",\r\n    \"roles\":[{\"name\":\"Admin\"},{\"name\":\"Viewer\"}]\r\n}\r\n```\r\n\r\nThis went through validation, therefore we know\r\n\r\n1. The POST-ing user has permission to create a new user.\r\n2. The format is correct (including the nested roles).\r\n3. There is not already an existing `username6` in the database.\r\n4. All listed roles exist.\r\n\r\n(All these are already covered by unit tests.)\r\n\r\nFurther complicating the issue is F.A.B.\u2019s security manager swallows an exception when this happens, and only logs the exception to the server. And we\u2019re having trouble locating that line of log. It\u2019s quite difficult to diagnose further, so I\u2019m posting this hoping someone has better luck reproducing this.\r\n\r\nI will submit a fix to correct the immediate issue, making the API emit 500 with something like \u201cFailed to create user for unknown reason\u201d to make the failure _slightly_ less confusing.\n\n### What you expected to happen\n\n_No response_\n\n### How to reproduce\n\n_No response_\n\n### Anything else\n\n_No response_\n\n### Are you willing to submit PR?\n\n- [X] Yes I am willing to submit a PR!\n\n### Code of Conduct\n\n- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)\n\n", "before_files": [{"content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\nfrom flask import current_app, request\nfrom flask_appbuilder.security.sqla.models import User\nfrom marshmallow import ValidationError\nfrom sqlalchemy import func\nfrom werkzeug.security import generate_password_hash\n\nfrom airflow.api_connexion import security\nfrom airflow.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound\nfrom airflow.api_connexion.parameters import apply_sorting, check_limit, format_parameters\nfrom airflow.api_connexion.schemas.user_schema import (\n    UserCollection,\n    user_collection_item_schema,\n    user_collection_schema,\n    user_schema,\n)\nfrom airflow.security import permissions\n\n\n@security.requires_access([(permissions.ACTION_CAN_READ, permissions.RESOURCE_USER)])\ndef get_user(username):\n    \"\"\"Get a user\"\"\"\n    ab_security_manager = current_app.appbuilder.sm\n    user = ab_security_manager.find_user(username=username)\n    if not user:\n        raise NotFound(title=\"User not found\", detail=f\"The User with username `{username}` was not found\")\n    return user_collection_item_schema.dump(user)\n\n\n@security.requires_access([(permissions.ACTION_CAN_READ, permissions.RESOURCE_USER)])\n@format_parameters({'limit': check_limit})\ndef get_users(limit, order_by='id', offset=None):\n    \"\"\"Get users\"\"\"\n    appbuilder = current_app.appbuilder\n    session = appbuilder.get_session\n    total_entries = session.query(func.count(User.id)).scalar()\n    to_replace = {\"user_id\": \"id\"}\n    allowed_filter_attrs = [\n        \"user_id\",\n        'id',\n        \"first_name\",\n        \"last_name\",\n        \"user_name\",\n        \"email\",\n        \"is_active\",\n        \"role\",\n    ]\n    query = session.query(User)\n    query = apply_sorting(query, order_by, to_replace, allowed_filter_attrs)\n    users = query.offset(offset).limit(limit).all()\n\n    return user_collection_schema.dump(UserCollection(users=users, total_entries=total_entries))\n\n\n@security.requires_access([(permissions.ACTION_CAN_CREATE, permissions.RESOURCE_USER)])\ndef post_user():\n    \"\"\"Create a new user\"\"\"\n    try:\n        data = user_schema.load(request.json)\n    except ValidationError as e:\n        raise BadRequest(detail=str(e.messages))\n\n    security_manager = current_app.appbuilder.sm\n\n    user = security_manager.find_user(username=data[\"username\"])\n    if user is not None:\n        detail = f\"Username `{user.username}` already exists. Use PATCH to update.\"\n        raise AlreadyExists(detail=detail)\n\n    roles_to_add = []\n    missing_role_names = []\n    for role_data in data.pop(\"roles\", ()):\n        role_name = role_data[\"name\"]\n        role = security_manager.find_role(role_name)\n        if role is None:\n            missing_role_names.append(role_name)\n        else:\n            roles_to_add.append(role)\n    if missing_role_names:\n        detail = f\"Unknown roles: {', '.join(repr(n) for n in missing_role_names)}\"\n        raise BadRequest(detail=detail)\n\n    if roles_to_add:\n        default_role = roles_to_add.pop()\n    else:  # No roles provided, use the F.A.B's default registered user role.\n        default_role = security_manager.find_role(security_manager.auth_user_registration_role)\n\n    user = security_manager.add_user(role=default_role, **data)\n    if roles_to_add:\n        user.roles.extend(roles_to_add)\n        security_manager.update_user(user)\n    return user_schema.dump(user)\n\n\n@security.requires_access([(permissions.ACTION_CAN_EDIT, permissions.RESOURCE_USER)])\ndef patch_user(username, update_mask=None):\n    \"\"\"Update a role\"\"\"\n    try:\n        data = user_schema.load(request.json)\n    except ValidationError as e:\n        raise BadRequest(detail=str(e.messages))\n\n    security_manager = current_app.appbuilder.sm\n\n    user = security_manager.find_user(username=username)\n    if user is None:\n        detail = f\"The User with username `{username}` was not found\"\n        raise NotFound(title=\"User not found\", detail=detail)\n\n    # Get fields to update. 'username' is always excluded (and it's an error to\n    # include it in update_maek).\n    if update_mask is not None:\n        masked_data = {}\n        missing_mask_names = []\n        for field in update_mask:\n            field = field.strip()\n            try:\n                masked_data[field] = data[field]\n            except KeyError:\n                missing_mask_names.append(field)\n        if missing_mask_names:\n            detail = f\"Unknown update masks: {', '.join(repr(n) for n in missing_mask_names)}\"\n            raise BadRequest(detail=detail)\n        if \"username\" in masked_data:\n            raise BadRequest(\"Cannot update fields: 'username'\")\n        data = masked_data\n    else:\n        data.pop(\"username\", None)\n\n    if \"roles\" in data:\n        roles_to_update = []\n        missing_role_names = []\n        for role_data in data.pop(\"roles\", ()):\n            role_name = role_data[\"name\"]\n            role = security_manager.find_role(role_name)\n            if role is None:\n                missing_role_names.append(role_name)\n            else:\n                roles_to_update.append(role)\n        if missing_role_names:\n            detail = f\"Unknown roles: {', '.join(repr(n) for n in missing_role_names)}\"\n            raise BadRequest(detail=detail)\n    else:\n        roles_to_update = None  # Don't change existing value.\n\n    if \"password\" in data:\n        user.password = generate_password_hash(data.pop(\"password\"))\n    if roles_to_update is not None:\n        user.roles = roles_to_update\n    for key, value in data.items():\n        setattr(user, key, value)\n    security_manager.update_user(user)\n\n    return user_schema.dump(user)\n\n\n@security.requires_access([(permissions.ACTION_CAN_DELETE, permissions.RESOURCE_USER)])\ndef delete_user(username):\n    \"\"\"Delete a user\"\"\"\n    security_manager = current_app.appbuilder.sm\n\n    user = security_manager.find_user(username=username)\n    if user is None:\n        detail = f\"The User with username `{username}` was not found\"\n        raise NotFound(title=\"User not found\", detail=detail)\n\n    user.roles = []  # Clear foreign keys on this user first.\n    security_manager.get_session.delete(user)\n    security_manager.get_session.commit()\n", "path": "airflow/api_connexion/endpoints/user_endpoint.py"}], "after_files": [{"content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\nfrom flask import current_app, request\nfrom flask_appbuilder.security.sqla.models import User\nfrom marshmallow import ValidationError\nfrom sqlalchemy import func\nfrom werkzeug.security import generate_password_hash\n\nfrom airflow.api_connexion import security\nfrom airflow.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound, Unknown\nfrom airflow.api_connexion.parameters import apply_sorting, check_limit, format_parameters\nfrom airflow.api_connexion.schemas.user_schema import (\n    UserCollection,\n    user_collection_item_schema,\n    user_collection_schema,\n    user_schema,\n)\nfrom airflow.security import permissions\n\n\n@security.requires_access([(permissions.ACTION_CAN_READ, permissions.RESOURCE_USER)])\ndef get_user(username):\n    \"\"\"Get a user\"\"\"\n    ab_security_manager = current_app.appbuilder.sm\n    user = ab_security_manager.find_user(username=username)\n    if not user:\n        raise NotFound(title=\"User not found\", detail=f\"The User with username `{username}` was not found\")\n    return user_collection_item_schema.dump(user)\n\n\n@security.requires_access([(permissions.ACTION_CAN_READ, permissions.RESOURCE_USER)])\n@format_parameters({'limit': check_limit})\ndef get_users(limit, order_by='id', offset=None):\n    \"\"\"Get users\"\"\"\n    appbuilder = current_app.appbuilder\n    session = appbuilder.get_session\n    total_entries = session.query(func.count(User.id)).scalar()\n    to_replace = {\"user_id\": \"id\"}\n    allowed_filter_attrs = [\n        \"user_id\",\n        'id',\n        \"first_name\",\n        \"last_name\",\n        \"user_name\",\n        \"email\",\n        \"is_active\",\n        \"role\",\n    ]\n    query = session.query(User)\n    query = apply_sorting(query, order_by, to_replace, allowed_filter_attrs)\n    users = query.offset(offset).limit(limit).all()\n\n    return user_collection_schema.dump(UserCollection(users=users, total_entries=total_entries))\n\n\n@security.requires_access([(permissions.ACTION_CAN_CREATE, permissions.RESOURCE_USER)])\ndef post_user():\n    \"\"\"Create a new user\"\"\"\n    try:\n        data = user_schema.load(request.json)\n    except ValidationError as e:\n        raise BadRequest(detail=str(e.messages))\n\n    security_manager = current_app.appbuilder.sm\n    username = data[\"username\"]\n    email = data[\"email\"]\n\n    if security_manager.find_user(username=username):\n        detail = f\"Username `{username}` already exists. Use PATCH to update.\"\n        raise AlreadyExists(detail=detail)\n    if security_manager.find_user(email=email):\n        detail = f\"The email `{email}` is already taken.\"\n        raise AlreadyExists(detail=detail)\n\n    roles_to_add = []\n    missing_role_names = []\n    for role_data in data.pop(\"roles\", ()):\n        role_name = role_data[\"name\"]\n        role = security_manager.find_role(role_name)\n        if role is None:\n            missing_role_names.append(role_name)\n        else:\n            roles_to_add.append(role)\n    if missing_role_names:\n        detail = f\"Unknown roles: {', '.join(repr(n) for n in missing_role_names)}\"\n        raise BadRequest(detail=detail)\n\n    if roles_to_add:\n        default_role = roles_to_add.pop()\n    else:  # No roles provided, use the F.A.B's default registered user role.\n        default_role = security_manager.find_role(security_manager.auth_user_registration_role)\n\n    user = security_manager.add_user(role=default_role, **data)\n    if not user:\n        detail = f\"Failed to add user `{username}`.\"\n        return Unknown(detail=detail)\n\n    if roles_to_add:\n        user.roles.extend(roles_to_add)\n        security_manager.update_user(user)\n    return user_schema.dump(user)\n\n\n@security.requires_access([(permissions.ACTION_CAN_EDIT, permissions.RESOURCE_USER)])\ndef patch_user(username, update_mask=None):\n    \"\"\"Update a role\"\"\"\n    try:\n        data = user_schema.load(request.json)\n    except ValidationError as e:\n        raise BadRequest(detail=str(e.messages))\n\n    security_manager = current_app.appbuilder.sm\n\n    user = security_manager.find_user(username=username)\n    if user is None:\n        detail = f\"The User with username `{username}` was not found\"\n        raise NotFound(title=\"User not found\", detail=detail)\n\n    # Get fields to update. 'username' is always excluded (and it's an error to\n    # include it in update_maek).\n    if update_mask is not None:\n        masked_data = {}\n        missing_mask_names = []\n        for field in update_mask:\n            field = field.strip()\n            try:\n                masked_data[field] = data[field]\n            except KeyError:\n                missing_mask_names.append(field)\n        if missing_mask_names:\n            detail = f\"Unknown update masks: {', '.join(repr(n) for n in missing_mask_names)}\"\n            raise BadRequest(detail=detail)\n        if \"username\" in masked_data:\n            raise BadRequest(\"Cannot update fields: 'username'\")\n        data = masked_data\n    else:\n        data.pop(\"username\", None)\n\n    if \"roles\" in data:\n        roles_to_update = []\n        missing_role_names = []\n        for role_data in data.pop(\"roles\", ()):\n            role_name = role_data[\"name\"]\n            role = security_manager.find_role(role_name)\n            if role is None:\n                missing_role_names.append(role_name)\n            else:\n                roles_to_update.append(role)\n        if missing_role_names:\n            detail = f\"Unknown roles: {', '.join(repr(n) for n in missing_role_names)}\"\n            raise BadRequest(detail=detail)\n    else:\n        roles_to_update = None  # Don't change existing value.\n\n    if \"password\" in data:\n        user.password = generate_password_hash(data.pop(\"password\"))\n    if roles_to_update is not None:\n        user.roles = roles_to_update\n    for key, value in data.items():\n        setattr(user, key, value)\n    security_manager.update_user(user)\n\n    return user_schema.dump(user)\n\n\n@security.requires_access([(permissions.ACTION_CAN_DELETE, permissions.RESOURCE_USER)])\ndef delete_user(username):\n    \"\"\"Delete a user\"\"\"\n    security_manager = current_app.appbuilder.sm\n\n    user = security_manager.find_user(username=username)\n    if user is None:\n        detail = f\"The User with username `{username}` was not found\"\n        raise NotFound(title=\"User not found\", detail=detail)\n\n    user.roles = []  # Clear foreign keys on this user first.\n    security_manager.get_session.delete(user)\n    security_manager.get_session.commit()\n", "path": "airflow/api_connexion/endpoints/user_endpoint.py"}]}