kanban-app/backend/app/decorators/owned.py

181 lines
4.4 KiB
Python
Raw Normal View History

from functools import wraps
from flask import abort
from app.decorators.decorators import get_current_user_id
from app.models import (Board, Card, CheckItem, Checklist, Comment,
FileAttachment, List)
def load_board_owned(f):
"""
Loads a Board and ensures it belongs to the current user.
Injects 'board' into the route kwargs.
Aborts with 404 if not found or not owned.
"""
@wraps(f)
def decorated_function(*args, **kwargs):
user_id = get_current_user_id()
board_id = kwargs.get("board_id")
# SECURE QUERY: Filter by ID *and* User ID in the DB
board = Board.query.filter_by(id=board_id, user_id=user_id).first()
if not board:
abort(404)
kwargs["board"] = board
return f(*args, **kwargs)
return decorated_function
def load_card_owned(f):
"""
Loads a Card and ensures its Parent Board belongs to the current user.
Injects 'card' into the route kwargs.
2026-05-01 15:03:00 +00:00
Aborts with 404 if not found, not owned, or soft-deleted.
"""
@wraps(f)
def decorated_function(*args, **kwargs):
user_id = get_current_user_id()
card_id = kwargs.get("card_id")
2026-05-01 15:03:00 +00:00
# Join Board to check ownership and filter soft-deleted cards
card = (
Card.query.join(Board)
2026-05-01 15:03:00 +00:00
.filter(
Card.id == card_id, Board.user_id == user_id, Card.deleted_at.is_(None)
)
.first()
)
if not card:
abort(404)
kwargs["card"] = card
return f(*args, **kwargs)
return decorated_function
def load_list_owned(f):
"""Loads a List ensuring Parent Board ownership."""
@wraps(f)
def decorated_function(*args, **kwargs):
user_id = get_current_user_id()
list_id = kwargs.get("list_id")
lst = (
List.query.join(Board)
.filter(List.id == list_id, Board.user_id == user_id)
.first()
)
if not lst:
abort(404)
kwargs["lst"] = lst
return f(*args, **kwargs)
return decorated_function
def load_checklist_owned(f):
"""Loads a Checklist ensuring Parent Board ownership."""
@wraps(f)
def decorated_function(*args, **kwargs):
user_id = get_current_user_id()
checklist_id = kwargs.get("checklist_id")
checklist = (
Checklist.query.join(Card)
.join(Board)
.filter(Checklist.id == checklist_id, Board.user_id == user_id)
.first()
)
if not checklist:
abort(404)
kwargs["checklist"] = checklist
return f(*args, **kwargs)
return decorated_function
def load_check_item_owned(f):
"""Loads a CheckItem ensuring Parent Board ownership."""
@wraps(f)
def decorated_function(*args, **kwargs):
user_id = get_current_user_id()
item_id = kwargs.get("item_id")
check_item = (
CheckItem.query.join(Checklist)
.join(Card)
.join(Board)
.filter(CheckItem.id == item_id, Board.user_id == user_id)
.first()
)
if not check_item:
abort(404)
kwargs["check_item"] = check_item
return f(*args, **kwargs)
return decorated_function
def load_comment_owned(f):
"""
Loads a Comment ensuring the Comment itself belongs to the user.
(Based on schema where Comment has user_id)
"""
@wraps(f)
def decorated_function(*args, **kwargs):
user_id = get_current_user_id()
comment_id = kwargs.get("comment_id")
comment = Comment.query.filter_by(id=comment_id, user_id=user_id).first()
if not comment:
abort(404)
kwargs["comment"] = comment
return f(*args, **kwargs)
return decorated_function
def load_file_owned(f):
"""
Loads a FileAttachment ensuring it belongs to the user.
Aborts with 404 if not found or not owned.
"""
@wraps(f)
def decorated_function(*args, **kwargs):
user_id = get_current_user_id()
file_id = kwargs.get("file_id")
# Filter by ID and user ID
attachment = FileAttachment.query.filter_by(
id=file_id, uploaded_by=user_id
).first()
if not attachment:
abort(404)
kwargs["file"] = attachment
return f(*args, **kwargs)
return decorated_function