Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AIRFLOW-3103][AIRFLOW-3147] Update flask-appbuilder #3937

Merged
merged 1 commit into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ To delete a user:
airflow users --delete --username jondoe
```

### Custom auth backends interface change

We have updated the version of flask-login we depend upon, and as a result any
custom auth backends might need a small change: `is_active`,
`is_authenticated`, and `is_anonymous` should now be properties. What this means is if
previously you had this in your user class

def is_active(self):
return self.active

then you need to change it like this

@property
def is_active(self):
return self.active

## Airflow 1.10

Installation and upgrading requires setting `SLUGIFY_USES_TEXT_UNIDECODE=yes` in your environment or
Expand Down
3 changes: 3 additions & 0 deletions airflow/contrib/auth/backends/github_enterprise_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,17 @@ class GHEUser(models.User):
def __init__(self, user):
self.user = user

@property
def is_active(self):
"""Required by flask_login"""
return True

@property
def is_authenticated(self):
"""Required by flask_login"""
return True

@property
def is_anonymous(self):
"""Required by flask_login"""
return False
Expand Down
3 changes: 3 additions & 0 deletions airflow/contrib/auth/backends/google_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,17 @@ class GoogleUser(models.User):
def __init__(self, user):
self.user = user

@property
def is_active(self):
"""Required by flask_login"""
return True

@property
def is_authenticated(self):
"""Required by flask_login"""
return True

@property
def is_anonymous(self):
"""Required by flask_login"""
return False
Expand Down
5 changes: 4 additions & 1 deletion airflow/contrib/auth/backends/kerberos_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,17 @@ def authenticate(username, password):

return

@property
def is_active(self):
"""Required by flask_login"""
return True

@property
def is_authenticated(self):
"""Required by flask_login"""
return True

@property
def is_anonymous(self):
"""Required by flask_login"""
return False
Expand Down Expand Up @@ -108,7 +111,7 @@ def load_user(userid, session=None):

@provide_session
def login(self, request, session=None):
if current_user.is_authenticated():
if current_user.is_authenticated:
flash("You are already logged in")
return redirect(url_for('index'))

Expand Down
5 changes: 4 additions & 1 deletion airflow/contrib/auth/backends/ldap_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,17 @@ def try_login(username, password):
log.info("Password incorrect for user %s", username)
raise AuthenticationError("Invalid username or password")

@property
def is_active(self):
"""Required by flask_login"""
return True

@property
def is_authenticated(self):
"""Required by flask_login"""
return True

@property
def is_anonymous(self):
"""Required by flask_login"""
return False
Expand Down Expand Up @@ -273,7 +276,7 @@ def load_user(userid, session=None):

@provide_session
def login(self, request, session=None):
if current_user.is_authenticated():
if current_user.is_authenticated:
flash("You are already logged in")
return redirect(url_for('admin.index'))

Expand Down
5 changes: 4 additions & 1 deletion airflow/contrib/auth/backends/password_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,17 @@ def password(self, plaintext):
def authenticate(self, plaintext):
return check_password_hash(self._password, plaintext)

@property
def is_active(self):
"""Required by flask_login"""
return True

@property
def is_authenticated(self):
"""Required by flask_login"""
return True

@property
def is_anonymous(self):
"""Required by flask_login"""
return False
Expand Down Expand Up @@ -137,7 +140,7 @@ def authenticate(session, username, password):

@provide_session
def login(self, request, session=None):
if current_user.is_authenticated():
if current_user.is_authenticated:
flash("You are already logged in")
return redirect(url_for('admin.index'))

Expand Down
3 changes: 3 additions & 0 deletions airflow/default_login.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,17 @@ class DefaultUser(object):
def __init__(self, user):
self.user = user

@property
def is_active(self):
"""Required by flask_login"""
return True

@property
def is_authenticated(self):
"""Required by flask_login"""
return True

@property
def is_anonymous(self):
"""Required by flask_login"""
return False
Expand Down
8 changes: 4 additions & 4 deletions airflow/www/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ class LoginMixin(object):
def is_accessible(self):
return (
not AUTHENTICATE or (
not current_user.is_anonymous() and
current_user.is_authenticated()
not current_user.is_anonymous and
current_user.is_authenticated
)
)

Expand All @@ -83,15 +83,15 @@ class SuperUserMixin(object):
def is_accessible(self):
return (
not AUTHENTICATE or
(not current_user.is_anonymous() and current_user.is_superuser())
(not current_user.is_anonymous and current_user.is_superuser())
)


class DataProfilingMixin(object):
def is_accessible(self):
return (
not AUTHENTICATE or
(not current_user.is_anonymous() and current_user.data_profiling())
(not current_user.is_anonymous and current_user.data_profiling())
)


Expand Down
2 changes: 1 addition & 1 deletion airflow/www/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def data_profiling_required(f):
def decorated_function(*args, **kwargs):
if (
current_app.config['LOGIN_DISABLED'] or
(not current_user.is_anonymous() and current_user.data_profiling())
(not current_user.is_anonymous and current_user.data_profiling())
):
return f(*args, **kwargs)
else:
Expand Down
2 changes: 1 addition & 1 deletion airflow/www_rbac/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def action_logging(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
session = settings.Session()
if g.user.is_anonymous():
if g.user.is_anonymous:
user = 'anonymous'
else:
user = g.user.username
Expand Down
6 changes: 3 additions & 3 deletions airflow/www_rbac/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def get_user_roles(self, user=None):
"""
if user is None:
user = g.user
if user.is_anonymous():
if user.is_anonymous:
public_role = appbuilder.config.get('AUTH_ROLE_PUBLIC')
return [appbuilder.security_manager.find_role(public_role)] \
if public_role else []
Expand All @@ -221,7 +221,7 @@ def get_accessible_dag_ids(self, username=None):
if not username:
username = g.user

if username.is_anonymous() or 'Public' in username.roles:
if username.is_anonymous or 'Public' in username.roles:
# return an empty list if the role is public
return set()

Expand All @@ -245,7 +245,7 @@ def has_access(self, permission, view_name, user=None):
"""
if not user:
user = g.user
if user.is_anonymous():
if user.is_anonymous:
return self.is_item_public(permission, view_name)
return self._has_view_access(user, permission, view_name)

Expand Down
2 changes: 1 addition & 1 deletion airflow/www_rbac/templates/appbuilder/navbar_right.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<!-- clock -->
<li><a id="clock"></a></li>

{% if not current_user.is_anonymous() %}
{% if not current_user.is_anonymous %}
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<span class="fa fa-user"></span> {{g.user.get_full_name()}}<b class="caret"></b>
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,10 @@ def do_setup():
'croniter>=0.3.17, <0.4',
'dill>=0.2.2, <0.3',
'flask>=0.12.4, <0.13',
'flask-appbuilder>=1.11.1, <2.0.0',
'flask-appbuilder>=1.12, <2.0.0',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add [AIRFLOW-3147] in to your commit message too please?

'flask-admin==1.4.1',
'flask-caching>=1.3.3, <1.4.0',
'flask-login==0.2.11',
'flask-login>=0.3, <0.5',
'flask-swagger==0.2.13',
'flask-wtf>=0.14.2, <0.15',
'funcsigs==1.0.0',
Expand Down
6 changes: 3 additions & 3 deletions tests/www/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ def test_params_all(self):
self.assertEqual('page=3&search=bash_&showPaused=False',
utils.get_params(showPaused=False, page=3, search='bash_'))

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

@mock.patch("flask_login._get_user")
@mock.patch("flask_login.utils._get_user")
@mock.patch("airflow.settings.Session")
def test_action_logging_with_invalid_user(self, mocked_session, mocked_get_user):
anonymous_username = 'anonymous'
Expand Down
6 changes: 3 additions & 3 deletions tests/www_rbac/test_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def test_init_role_modelview(self):

def test_get_user_roles(self):
user = mock.MagicMock()
user.is_anonymous.return_value = False
user.is_anonymous = False
roles = self.appbuilder.sm.find_role('Admin')
user.roles = roles
self.assertEqual(self.security_manager.get_user_roles(user), roles)
Expand Down Expand Up @@ -144,7 +144,7 @@ def test_get_accessible_dag_ids(self, mock_get_user_roles,
self.security_manager.init_role(role_name, role_vms, role_perms)
role = self.security_manager.find_role(role_name)
user.roles = [role]
user.is_anonymous.return_value = False
user.is_anonymous = False
mock_get_all_permissions_views.return_value = {('can_dag_read', 'dag_id')}

mock_get_user_roles.return_value = [role]
Expand All @@ -154,7 +154,7 @@ def test_get_accessible_dag_ids(self, mock_get_user_roles,
@mock.patch('airflow.www_rbac.security.AirflowSecurityManager._has_view_access')
def test_has_access(self, mock_has_view_access):
user = mock.MagicMock()
user.is_anonymous.return_value = False
user.is_anonymous = False
mock_has_view_access.return_value = True
self.assertTrue(self.security_manager.has_access('perm', 'view', user))

Expand Down