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

Simplify fab has access lookup #19294

Merged
merged 63 commits into from
Feb 4, 2022

Conversation

jhtimmins
Copy link
Contributor

@jhtimmins jhtimmins commented Oct 28, 2021

Simplifies the FAB security manager code by doing the following.

  1. Modifies permission lookup by accessing User.perms property method and checking if a permission pair is included in the user's list of perms.
  2. Extends AnonymousUserMixin to create AnonymousUser, which has both perms and roles property methods. Removes the need for custom auth logic for anonymous users.
  3. Removes helper methods that provide little value, including:
  • .get_readable_dags()
  • .current_user_has_permissions()
  • ._has_resource_access()
  • .user_has_perms()
  • .user_has_roles()
  1. Removes unused methods:
  • SecurityManager.is_item_public()
  1. Moves the code to create a root dag resource name to Permissions.resource_name_for_dag()

This is the next piece of the permissions refactor.

@boring-cyborg boring-cyborg bot added area:API Airflow's REST/HTTP API area:webserver Webserver related Issues labels Oct 28, 2021
@jhtimmins jhtimmins force-pushed the simplify-fab-has-access-lookup branch from e7e11e7 to 8f2dbef Compare October 30, 2021 00:17
@jhtimmins jhtimmins marked this pull request as ready for review November 1, 2021 22:50
@jhtimmins jhtimmins force-pushed the simplify-fab-has-access-lookup branch from 45decd0 to da380c4 Compare November 2, 2021 16:10
def perms(self):
perms = set()
for role in self.roles:
perms.update((perm.action.name, perm.resource.name) for perm in role.permissions)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you may need to do this as a single query for efficiency reasons?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added lazy=joined to several model fields, which causes prefetching. It now runs 11 queries instead of 49.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@andrewgodwin that said, this PR does add some overall time to the tests. pytest tests/views --with-db-init runs in 1:53 on main and 2:00 on my branch. How significant is that?

@jhtimmins jhtimmins force-pushed the simplify-fab-has-access-lookup branch from da380c4 to a682403 Compare November 3, 2021 03:22
@jhtimmins jhtimmins force-pushed the simplify-fab-has-access-lookup branch 4 times, most recently from ecb8ad1 to bf6f20c Compare November 9, 2021 06:40
@@ -35,7 +35,7 @@ def decorated(*args, **kwargs):
__tracebackhide__ = True # Hide from pytest traceback.

appbuilder = current_app.appbuilder
if not g.user.is_anonymous and not appbuilder.sm.current_user_has_permissions():
if not (g.user.is_anonymous or g.user.perms):
Copy link
Member

Choose a reason for hiding this comment

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

I feel the previous version is a bit easier to understand (i.e. “not anonymous but does not have any permissions”)

Suggested change
if not (g.user.is_anonymous or g.user.perms):
if not g.user.is_anonymous and not g.user.perms:

Copy link
Member

Choose a reason for hiding this comment

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

See this one @jhtimmins

@jhtimmins jhtimmins force-pushed the simplify-fab-has-access-lookup branch from bf6f20c to 50f4180 Compare December 7, 2021 02:23
Copy link
Member

@ashb ashb left a comment

Choose a reason for hiding this comment

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

LGTM -- a few small comments to address or say "naaah" to

tests/www/test_security.py Show resolved Hide resolved
@@ -339,7 +333,7 @@ def filter_roles_by_perm_with_action(self, action_name: str, role_ids: List[int]
)
).all()

def get_role_permissions_from_db(self, role_id: int) -> List[Permission]:
def get_permissions_from_roles(self, role_id: int) -> List[Permission]:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def get_permissions_from_roles(self, role_id: int) -> List[Permission]:
def get_permissions_from_role(self, role_id: int) -> List[Permission]:

Should be singular given it takes a single role_id. (Possibly also called _from_role_id, but don't mind on that one)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Its actually not used anymore so should be removed entirely

Comment on lines +106 to +113
@property
def perms(self):
if not self._perms:
self._perms = set()
for role in self.roles:
self._perms.update({(perm.action.name, perm.resource.name) for perm in role.permissions})
return self._perms
Copy link
Member

Choose a reason for hiding this comment

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

from airflow.compat.functools import cached_property and then:

Suggested change
@property
def perms(self):
if not self._perms:
self._perms = set()
for role in self.roles:
self._perms.update({(perm.action.name, perm.resource.name) for perm in role.permissions})
return self._perms
@cached_property
def perms(self):
_perms = set()
for role in self.roles:
_perms.update({(perm.action.name, perm.resource.name) for perm in role.permissions})
return _perms

And then del self.perms in the @roles.setter to clear the cache.

(This is all functionally the same, I just find the delcarative @cached_property clearer to read.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll make this change in a follow up PR

@@ -35,7 +35,7 @@ def decorated(*args, **kwargs):
__tracebackhide__ = True # Hide from pytest traceback.

appbuilder = current_app.appbuilder
if not g.user.is_anonymous and not appbuilder.sm.current_user_has_permissions():
if not (g.user.is_anonymous or g.user.perms):
Copy link
Member

Choose a reason for hiding this comment

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

See this one @jhtimmins

@@ -100,7 +100,7 @@ def apply_sorting(query, order_by, to_replace=None, allowed_attrs=None):
if to_replace:
lstriped_orderby = to_replace.get(lstriped_orderby, lstriped_orderby)
if order_by[0] == "-":
order_by = f"{lstriped_orderby} desc"
order_by = desc(text(lstriped_orderby))
Copy link
Member

Choose a reason for hiding this comment

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

Unrelated change in this file?

@jhtimmins jhtimmins force-pushed the simplify-fab-has-access-lookup branch 3 times, most recently from 5975e47 to 7b0487b Compare January 19, 2022 04:40
@jhtimmins jhtimmins force-pushed the simplify-fab-has-access-lookup branch 2 times, most recently from d3fac5c to ff89d19 Compare January 29, 2022 01:44
@jhtimmins
Copy link
Contributor Author

@ashb The most recent changes address a bug that only arises with MSSQL. Bc my changes to the permission models include prefetching of data, the parameters.apply_sorting() method doesn't work anymore https://github.com/apache/airflow/blob/main/airflow/api_connexion/parameters.py#L99. It's a utility method for allowing users to pass sort parameters to the API, and it then adds the order_by string to the query. This is problematic for joined queries, since sqlalchemy uses aliases to reference the joins and subqueries. MSSQL has a different alias naming scheme than the other DBs.

My change addresses the two API functions that query on the permission models, and replaces the calls to apply_sorting() with localized code to handle the different sort arguments.

I've duplicated some code, which I could remove by modifying the original .apply_sorting() method, but to prevent scope creep I'd rather do that in a future PR.

@jhtimmins jhtimmins requested review from ashb and uranusjr January 29, 2022 01:54
@jhtimmins jhtimmins requested a review from potiuk as a code owner February 3, 2022 21:05
@jhtimmins jhtimmins merged commit 3e98280 into apache:main Feb 4, 2022
@jhtimmins jhtimmins deleted the simplify-fab-has-access-lookup branch February 4, 2022 17:09
potiuk added a commit to potiuk/airflow that referenced this pull request Feb 9, 2022
@ephraimbuddy ephraimbuddy added the type:improvement Changelog: Improvements label Apr 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:API Airflow's REST/HTTP API area:webserver Webserver related Issues full tests needed We need to run full set of tests for this PR to merge type:improvement Changelog: Improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants