diff --git a/backend/app/api/files.py b/backend/app/api/files.py index da64e07..8bd3416 100644 --- a/backend/app/api/files.py +++ b/backend/app/api/files.py @@ -41,16 +41,22 @@ def _user_upload_dir(user_id): def _check_file_access(file_obj, user, permission='read'): - """Check if user has access to file. Owner always has full access.""" + """Check if user has access to file. Owner always has full access. + A permission on an ancestor folder also grants access to all descendants.""" if file_obj.owner_id == user.id: return True - perm = FilePermission.query.filter_by( - file_id=file_obj.id, user_id=user.id - ).first() - if not perm: - return False perm_levels = {'read': 0, 'write': 1, 'admin': 2} - return perm_levels.get(perm.permission, -1) >= perm_levels.get(permission, 0) + needed = perm_levels.get(permission, 0) + # Walk up the tree looking for a permission on this file or any ancestor + cur = file_obj + while cur is not None: + perm = FilePermission.query.filter_by( + file_id=cur.id, user_id=user.id + ).first() + if perm and perm_levels.get(perm.permission, -1) >= needed: + return True + cur = cur.parent + return False def _get_file_or_403(file_id, user, permission='read'): @@ -78,9 +84,25 @@ def list_files(): user = request.current_user parent_id = request.args.get('parent_id', None, type=int) - # Own files in this folder (exclude trashed) - query = File.query.filter_by(owner_id=user.id, parent_id=parent_id, is_trashed=False) - files = query.order_by(File.is_folder.desc(), File.name).all() + # When browsing into a folder, verify access first. If the folder is + # shared with us (directly or via an ancestor), list ALL its children + # - not just ones owned by us. + if parent_id is not None: + parent_folder, perr = _get_file_or_403(parent_id, user, 'read') + if perr: + return perr + if parent_folder.owner_id == user.id: + files = File.query.filter_by( + owner_id=user.id, parent_id=parent_id, is_trashed=False + ).order_by(File.is_folder.desc(), File.name).all() + else: + files = File.query.filter_by( + parent_id=parent_id, is_trashed=False + ).order_by(File.is_folder.desc(), File.name).all() + else: + files = File.query.filter_by( + owner_id=user.id, parent_id=None, is_trashed=False + ).order_by(File.is_folder.desc(), File.name).all() # Shared files at root level shared = [] @@ -90,7 +112,7 @@ def list_files(): if shared_file_ids: shared = File.query.filter( File.id.in_(shared_file_ids), - File.parent_id.is_(None) + File.is_trashed == False # noqa: E712 ).order_by(File.is_folder.desc(), File.name).all() result = []