Skip to content

Commit

Permalink
Don't load filtered-out parents eagerly on every Object.access (#997)
Browse files Browse the repository at this point in the history
* Don't load filtered-out parents eagerly on every Object.access

* Load only accessible parents in ObjectItemResponseSchema
  • Loading branch information
psrok1 authored Dec 11, 2024
1 parent 38a4f65 commit 12d25f3
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 29 deletions.
43 changes: 16 additions & 27 deletions mwdb/model/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from sqlalchemy import and_, cast, distinct, exists, func, select
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import aliased, column_property, contains_eager
from sqlalchemy.orm import column_property
from sqlalchemy.sql.expression import true

from mwdb.core.capabilities import Capabilities
Expand Down Expand Up @@ -150,6 +150,19 @@ def latest_config(self):
def favorite(self):
return g.auth_user in self.followers

@property
def accessible_parents(self):
"""
Parent objects that are accessible for current user
"""
return (
db.session.query(Object)
.join(relation, relation.c.parent_id == Object.id)
.filter(relation.c.child_id == self.id)
.order_by(relation.c.creation_time.desc())
.filter(g.auth_user.has_access_to_object(Object.id))
)

def add_parent(self, parent, commit=True):
"""
Adding parent with permission inheritance
Expand Down Expand Up @@ -467,35 +480,11 @@ def access(cls, identifier, requestor=None):
if requestor is None:
requestor = g.auth_user

obj = cls.get(identifier)
obj = cls.get(identifier).first()
# If object doesn't exist - it doesn't exist
if obj.first() is None:
if obj is None:
return None

# In that case we want only those parents to which requestor has access.
stmtp = (
db.session.query(Object)
.join(relation, relation.c.parent_id == Object.id)
.filter(
Object.id.in_(
db.session.query(relation.c.parent_id).filter(
relation.c.child_id == obj.first().id
)
)
)
.order_by(relation.c.creation_time.desc())
.filter(requestor.has_access_to_object(Object.id))
)
stmtp = stmtp.subquery()

parent = aliased(Object, stmtp)

obj = (
obj.outerjoin(parent, Object.parents)
.options(contains_eager(Object.parents, alias=parent))
.all()[0]
)

# Ok, now let's check whether requestor has explicit access
if obj.has_explicit_access(requestor):
return obj
Expand Down
6 changes: 5 additions & 1 deletion mwdb/schema/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ class ObjectItemResponseSchema(Schema):
favorite = fields.Boolean(required=True, allow_none=False)

parents = fields.Nested(
ObjectListItemResponseSchema, many=True, required=True, allow_none=False
ObjectListItemResponseSchema,
many=True,
required=True,
allow_none=False,
attribute="accessible_parents",
)
children = fields.Nested(
ObjectListItemResponseSchema, many=True, required=True, allow_none=False
Expand Down
6 changes: 5 additions & 1 deletion mwdb/schema/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

class RelationsResponseSchema(Schema):
parents = fields.Nested(
ObjectListItemResponseSchema, many=True, required=True, allow_none=False
ObjectListItemResponseSchema,
many=True,
required=True,
allow_none=False,
attribute="accessible_parents",
)
children = fields.Nested(
ObjectListItemResponseSchema, many=True, required=True, allow_none=False
Expand Down

0 comments on commit 12d25f3

Please sign in to comment.