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)

Reply via email to