Skip to content

Commit d5fb3b1

Browse files
vincbeckromsharon98
authored andcommitted
Add tests to airflow/auth/managers/fab/decorators/auth.py (apache#35358)
1 parent c33a1fe commit d5fb3b1

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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 __future__ import annotations
18+
19+
from unittest.mock import Mock, patch
20+
21+
import pytest
22+
23+
from airflow.api_connexion.exceptions import PermissionDenied
24+
from airflow.auth.managers.fab.decorators.auth import _has_access_fab, _requires_access_fab
25+
from airflow.security.permissions import ACTION_CAN_READ, RESOURCE_DAG
26+
from airflow.www import app as application
27+
28+
29+
@pytest.fixture(scope="module")
30+
def app():
31+
return application.create_app(testing=True)
32+
33+
34+
@pytest.fixture
35+
def mock_sm():
36+
return Mock()
37+
38+
39+
@pytest.fixture
40+
def mock_appbuilder(mock_sm):
41+
appbuilder = Mock()
42+
appbuilder.sm = mock_sm
43+
return appbuilder
44+
45+
46+
@pytest.fixture
47+
def mock_app(mock_appbuilder):
48+
app = Mock()
49+
app.appbuilder = mock_appbuilder
50+
return app
51+
52+
53+
mock_call = Mock()
54+
55+
permissions = [(ACTION_CAN_READ, RESOURCE_DAG)]
56+
57+
58+
@_has_access_fab(permissions)
59+
def decorated_has_access_fab():
60+
mock_call()
61+
62+
63+
class TestFabAuthManagerDecorators:
64+
def setup_method(self) -> None:
65+
mock_call.reset_mock()
66+
67+
@patch("airflow.auth.managers.fab.decorators.auth.get_airflow_app")
68+
def test_requires_access_fab_sync_resource_permissions(
69+
self, mock_get_airflow_app, mock_sm, mock_appbuilder, mock_app
70+
):
71+
mock_appbuilder.update_perms = True
72+
mock_get_airflow_app.return_value = mock_app
73+
74+
@_requires_access_fab()
75+
def decorated_requires_access_fab():
76+
pass
77+
78+
mock_sm.sync_resource_permissions.assert_called_once()
79+
80+
@patch("airflow.auth.managers.fab.decorators.auth.get_airflow_app")
81+
@patch("airflow.auth.managers.fab.decorators.auth.check_authentication")
82+
def test_requires_access_fab_access_denied(
83+
self, mock_check_authentication, mock_get_airflow_app, mock_sm, mock_app
84+
):
85+
mock_get_airflow_app.return_value = mock_app
86+
mock_sm.check_authorization.return_value = False
87+
88+
@_requires_access_fab(permissions)
89+
def decorated_requires_access_fab():
90+
pass
91+
92+
with pytest.raises(PermissionDenied):
93+
decorated_requires_access_fab()
94+
95+
mock_check_authentication.assert_called_once()
96+
mock_sm.check_authorization.assert_called_once()
97+
mock_call.assert_not_called()
98+
99+
@patch("airflow.auth.managers.fab.decorators.auth.get_airflow_app")
100+
@patch("airflow.auth.managers.fab.decorators.auth.check_authentication")
101+
def test_requires_access_fab_access_granted(
102+
self, mock_check_authentication, mock_get_airflow_app, mock_sm, mock_app
103+
):
104+
mock_get_airflow_app.return_value = mock_app
105+
mock_sm.check_authorization.return_value = True
106+
107+
@_requires_access_fab(permissions)
108+
def decorated_requires_access_fab():
109+
mock_call()
110+
111+
decorated_requires_access_fab()
112+
113+
mock_check_authentication.assert_called_once()
114+
mock_sm.check_authorization.assert_called_once()
115+
mock_call.assert_called_once()
116+
117+
@pytest.mark.db_test
118+
@patch("airflow.auth.managers.fab.decorators.auth._has_access")
119+
def test_has_access_fab_with_no_dags(self, mock_has_access, mock_sm, mock_appbuilder, app):
120+
app.appbuilder = mock_appbuilder
121+
with app.test_request_context():
122+
decorated_has_access_fab()
123+
124+
mock_sm.check_authorization.assert_called_once_with(permissions, None)
125+
mock_has_access.assert_called_once()
126+
127+
@pytest.mark.db_test
128+
@patch("airflow.auth.managers.fab.decorators.auth.render_template")
129+
@patch("airflow.auth.managers.fab.decorators.auth._has_access")
130+
def test_has_access_fab_with_multiple_dags_render_error(
131+
self, mock_has_access, mock_render_template, mock_sm, mock_appbuilder, app
132+
):
133+
app.appbuilder = mock_appbuilder
134+
with app.test_request_context() as mock_context:
135+
mock_context.request.args = {"dag_id": "dag1"}
136+
mock_context.request.form = {"dag_id": "dag2"}
137+
decorated_has_access_fab()
138+
139+
mock_sm.check_authorization.assert_not_called()
140+
mock_has_access.assert_not_called()
141+
mock_render_template.assert_called_once()

0 commit comments

Comments
 (0)