This is an automated email from the ASF dual-hosted git repository.
jscheffl pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 83afc02de12 Fix user creation in FAB fails when no role specified
(#60015)
83afc02de12 is described below
commit 83afc02de1295d7ef8661eec92605d7303d19653
Author: M Junaid Shaukat <[email protected]>
AuthorDate: Thu Jan 1 16:15:04 2026 +0500
Fix user creation in FAB fails when no role specified (#60015)
Fixes #59963
When creating a user in FAB without selecting a role, the system was
throwing HTTP 500 error due to KeyError: 'groups'. This fix adds
DataRequired validator to the roles field in CustomUserDBModelView
to show a proper validation error instead.
---
.../providers/fab/auth_manager/views/user.py | 3 ++
.../fab/www/views/test_views_custom_user_views.py | 36 ++++++++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/views/user.py
b/providers/fab/src/airflow/providers/fab/auth_manager/views/user.py
index 2fc0f83209c..20564f18d23 100644
--- a/providers/fab/src/airflow/providers/fab/auth_manager/views/user.py
+++ b/providers/fab/src/airflow/providers/fab/auth_manager/views/user.py
@@ -26,6 +26,7 @@ from flask_appbuilder.security.views import (
UserOAuthModelView,
UserRemoteUserModelView,
)
+from wtforms.validators import DataRequired
from airflow.providers.fab.www.security import permissions
@@ -186,6 +187,8 @@ class CustomUserDBModelView(MultiResourceUserMixin,
UserDBModelView):
"conf_password",
]
+ validators_columns = {"roles": [DataRequired()]}
+
base_permissions = [
permissions.ACTION_CAN_CREATE,
permissions.ACTION_CAN_READ,
diff --git
a/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
b/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
index 1b12575d12a..2cfcb3f35fb 100644
--- a/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
+++ b/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
@@ -95,6 +95,7 @@ class TestSecurity:
yield
delete_user(app, "no_access")
delete_user(app, "has_access")
+ delete_user(app, "test_new_user")
@pytest.mark.parametrize(("url", "_", "expected_text"),
PERMISSIONS_TESTS_PARAMS)
def test_user_model_view_without_access(self, url, expected_text, _, app,
client):
@@ -185,6 +186,41 @@ class TestSecurity:
client.post(f"/users/delete/{user_to_delete.id}",
follow_redirects=False)
assert
bool(get_auth_manager().security_manager.get_user_by_id(user_to_delete.id)) is
False
+ def test_user_creation_without_role_shows_validation_error(self, app,
client):
+ """Regression test for
https://github.com/apache/airflow/issues/59963"""
+ create_user(
+ app,
+ username="has_access",
+ role_name="role_has_access",
+ permissions=[
+ (permissions.ACTION_CAN_READ, permissions.RESOURCE_WEBSITE),
+ (permissions.ACTION_CAN_CREATE, permissions.RESOURCE_USER),
+ ],
+ )
+
+ client = client_with_login(
+ app,
+ username="has_access",
+ password="has_access",
+ )
+
+ response = client.post(
+ "/users/add",
+ data={
+ "first_name": "Test",
+ "last_name": "User",
+ "username": "test_new_user",
+ "email": "[email protected]",
+ "password": "test_password",
+ "conf_password": "test_password",
+ "active": "y",
+ },
+ follow_redirects=True,
+ )
+
+ assert response.status_code == 200
+ check_content_in_response("This field is required", response)
+
class TestResetUserSessions:
@pytest.fixture(autouse=True)