Skip to content

Commit a6b15cd

Browse files
jmcarpwayne.morris
authored and
wayne.morris
committed
[AIRFLOW-3103][AIRFLOW-3147] Update flask-appbuilder (apache#3937)
1 parent afb0008 commit a6b15cd

15 files changed

+55
-21
lines changed

UPDATING.md

+16
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ To delete a user:
5252
airflow users --delete --username jondoe
5353
```
5454

55+
### Custom auth backends interface change
56+
57+
We have updated the version of flask-login we depend upon, and as a result any
58+
custom auth backends might need a small change: `is_active`,
59+
`is_authenticated`, and `is_anonymous` should now be properties. What this means is if
60+
previously you had this in your user class
61+
62+
def is_active(self):
63+
return self.active
64+
65+
then you need to change it like this
66+
67+
@property
68+
def is_active(self):
69+
return self.active
70+
5571
## Airflow 1.10
5672

5773
Installation and upgrading requires setting `SLUGIFY_USES_TEXT_UNIDECODE=yes` in your environment or

airflow/contrib/auth/backends/github_enterprise_auth.py

+3
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,17 @@ class GHEUser(models.User):
4141
def __init__(self, user):
4242
self.user = user
4343

44+
@property
4445
def is_active(self):
4546
"""Required by flask_login"""
4647
return True
4748

49+
@property
4850
def is_authenticated(self):
4951
"""Required by flask_login"""
5052
return True
5153

54+
@property
5255
def is_anonymous(self):
5356
"""Required by flask_login"""
5457
return False

airflow/contrib/auth/backends/google_auth.py

+3
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,17 @@ class GoogleUser(models.User):
4242
def __init__(self, user):
4343
self.user = user
4444

45+
@property
4546
def is_active(self):
4647
"""Required by flask_login"""
4748
return True
4849

50+
@property
4951
def is_authenticated(self):
5052
"""Required by flask_login"""
5153
return True
5254

55+
@property
5356
def is_anonymous(self):
5457
"""Required by flask_login"""
5558
return False

airflow/contrib/auth/backends/kerberos_auth.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,17 @@ def authenticate(username, password):
7171

7272
return
7373

74+
@property
7475
def is_active(self):
7576
"""Required by flask_login"""
7677
return True
7778

79+
@property
7880
def is_authenticated(self):
7981
"""Required by flask_login"""
8082
return True
8183

84+
@property
8285
def is_anonymous(self):
8386
"""Required by flask_login"""
8487
return False
@@ -108,7 +111,7 @@ def load_user(userid, session=None):
108111

109112
@provide_session
110113
def login(self, request, session=None):
111-
if current_user.is_authenticated():
114+
if current_user.is_authenticated:
112115
flash("You are already logged in")
113116
return redirect(url_for('index'))
114117

airflow/contrib/auth/backends/ldap_auth.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,17 @@ def try_login(username, password):
235235
log.info("Password incorrect for user %s", username)
236236
raise AuthenticationError("Invalid username or password")
237237

238+
@property
238239
def is_active(self):
239240
"""Required by flask_login"""
240241
return True
241242

243+
@property
242244
def is_authenticated(self):
243245
"""Required by flask_login"""
244246
return True
245247

248+
@property
246249
def is_anonymous(self):
247250
"""Required by flask_login"""
248251
return False
@@ -273,7 +276,7 @@ def load_user(userid, session=None):
273276

274277
@provide_session
275278
def login(self, request, session=None):
276-
if current_user.is_authenticated():
279+
if current_user.is_authenticated:
277280
flash("You are already logged in")
278281
return redirect(url_for('admin.index'))
279282

airflow/contrib/auth/backends/password_auth.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,17 @@ def password(self, plaintext):
7171
def authenticate(self, plaintext):
7272
return check_password_hash(self._password, plaintext)
7373

74+
@property
7475
def is_active(self):
7576
"""Required by flask_login"""
7677
return True
7778

79+
@property
7880
def is_authenticated(self):
7981
"""Required by flask_login"""
8082
return True
8183

84+
@property
8285
def is_anonymous(self):
8386
"""Required by flask_login"""
8487
return False
@@ -137,7 +140,7 @@ def authenticate(session, username, password):
137140

138141
@provide_session
139142
def login(self, request, session=None):
140-
if current_user.is_authenticated():
143+
if current_user.is_authenticated:
141144
flash("You are already logged in")
142145
return redirect(url_for('admin.index'))
143146

airflow/default_login.py

+3
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,17 @@ class DefaultUser(object):
4444
def __init__(self, user):
4545
self.user = user
4646

47+
@property
4748
def is_active(self):
4849
"""Required by flask_login"""
4950
return True
5051

52+
@property
5153
def is_authenticated(self):
5254
"""Required by flask_login"""
5355
return True
5456

57+
@property
5558
def is_anonymous(self):
5659
"""Required by flask_login"""
5760
return False

airflow/www/utils.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ class LoginMixin(object):
7373
def is_accessible(self):
7474
return (
7575
not AUTHENTICATE or (
76-
not current_user.is_anonymous() and
77-
current_user.is_authenticated()
76+
not current_user.is_anonymous and
77+
current_user.is_authenticated
7878
)
7979
)
8080

@@ -83,15 +83,15 @@ class SuperUserMixin(object):
8383
def is_accessible(self):
8484
return (
8585
not AUTHENTICATE or
86-
(not current_user.is_anonymous() and current_user.is_superuser())
86+
(not current_user.is_anonymous and current_user.is_superuser())
8787
)
8888

8989

9090
class DataProfilingMixin(object):
9191
def is_accessible(self):
9292
return (
9393
not AUTHENTICATE or
94-
(not current_user.is_anonymous() and current_user.data_profiling())
94+
(not current_user.is_anonymous and current_user.data_profiling())
9595
)
9696

9797

airflow/www/views.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def data_profiling_required(f):
260260
def decorated_function(*args, **kwargs):
261261
if (
262262
current_app.config['LOGIN_DISABLED'] or
263-
(not current_user.is_anonymous() and current_user.data_profiling())
263+
(not current_user.is_anonymous and current_user.data_profiling())
264264
):
265265
return f(*args, **kwargs)
266266
else:

airflow/www_rbac/decorators.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def action_logging(f):
3232
@functools.wraps(f)
3333
def wrapper(*args, **kwargs):
3434
session = settings.Session()
35-
if g.user.is_anonymous():
35+
if g.user.is_anonymous:
3636
user = 'anonymous'
3737
else:
3838
user = g.user.username

airflow/www_rbac/security.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def get_user_roles(self, user=None):
195195
"""
196196
if user is None:
197197
user = g.user
198-
if user.is_anonymous():
198+
if user.is_anonymous:
199199
public_role = appbuilder.config.get('AUTH_ROLE_PUBLIC')
200200
return [appbuilder.security_manager.find_role(public_role)] \
201201
if public_role else []
@@ -221,7 +221,7 @@ def get_accessible_dag_ids(self, username=None):
221221
if not username:
222222
username = g.user
223223

224-
if username.is_anonymous() or 'Public' in username.roles:
224+
if username.is_anonymous or 'Public' in username.roles:
225225
# return an empty list if the role is public
226226
return set()
227227

@@ -245,7 +245,7 @@ def has_access(self, permission, view_name, user=None):
245245
"""
246246
if not user:
247247
user = g.user
248-
if user.is_anonymous():
248+
if user.is_anonymous:
249249
return self.is_item_public(permission, view_name)
250250
return self._has_view_access(user, permission, view_name)
251251

airflow/www_rbac/templates/appbuilder/navbar_right.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
<!-- clock -->
4848
<li><a id="clock"></a></li>
4949

50-
{% if not current_user.is_anonymous() %}
50+
{% if not current_user.is_anonymous %}
5151
<li class="dropdown">
5252
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
5353
<span class="fa fa-user"></span> {{g.user.get_full_name()}}<b class="caret"></b>

setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,10 @@ def do_setup():
298298
'croniter>=0.3.17, <0.4',
299299
'dill>=0.2.2, <0.3',
300300
'flask>=0.12.4, <0.13',
301-
'flask-appbuilder>=1.11.1, <2.0.0',
301+
'flask-appbuilder>=1.12, <2.0.0',
302302
'flask-admin==1.4.1',
303303
'flask-caching>=1.3.3, <1.4.0',
304-
'flask-login==0.2.11',
304+
'flask-login>=0.3, <0.5',
305305
'flask-swagger==0.2.13',
306306
'flask-wtf>=0.14.2, <0.15',
307307
'funcsigs==1.0.0',

tests/www/test_utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ def test_params_all(self):
117117
self.assertEqual('page=3&search=bash_&showPaused=False',
118118
utils.get_params(showPaused=False, page=3, search='bash_'))
119119

120-
# flask_login is loaded by calling flask_login._get_user.
121-
@mock.patch("flask_login._get_user")
120+
# flask_login is loaded by calling flask_login.utils._get_user.
121+
@mock.patch("flask_login.utils._get_user")
122122
@mock.patch("airflow.settings.Session")
123123
def test_action_logging_with_login_user(self, mocked_session, mocked_get_user):
124124
fake_username = 'someone'
@@ -143,7 +143,7 @@ def some_func():
143143
self.assertEqual(fake_username, kwargs['owner'])
144144
mocked_session_instance.add.assert_called_once()
145145

146-
@mock.patch("flask_login._get_user")
146+
@mock.patch("flask_login.utils._get_user")
147147
@mock.patch("airflow.settings.Session")
148148
def test_action_logging_with_invalid_user(self, mocked_session, mocked_get_user):
149149
anonymous_username = 'anonymous'

tests/www_rbac/test_security.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def test_init_role_modelview(self):
109109

110110
def test_get_user_roles(self):
111111
user = mock.MagicMock()
112-
user.is_anonymous.return_value = False
112+
user.is_anonymous = False
113113
roles = self.appbuilder.sm.find_role('Admin')
114114
user.roles = roles
115115
self.assertEqual(self.security_manager.get_user_roles(user), roles)
@@ -144,7 +144,7 @@ def test_get_accessible_dag_ids(self, mock_get_user_roles,
144144
self.security_manager.init_role(role_name, role_vms, role_perms)
145145
role = self.security_manager.find_role(role_name)
146146
user.roles = [role]
147-
user.is_anonymous.return_value = False
147+
user.is_anonymous = False
148148
mock_get_all_permissions_views.return_value = {('can_dag_read', 'dag_id')}
149149

150150
mock_get_user_roles.return_value = [role]
@@ -154,7 +154,7 @@ def test_get_accessible_dag_ids(self, mock_get_user_roles,
154154
@mock.patch('airflow.www_rbac.security.AirflowSecurityManager._has_view_access')
155155
def test_has_access(self, mock_has_view_access):
156156
user = mock.MagicMock()
157-
user.is_anonymous.return_value = False
157+
user.is_anonymous = False
158158
mock_has_view_access.return_value = True
159159
self.assertTrue(self.security_manager.has_access('perm', 'view', user))
160160

0 commit comments

Comments
 (0)