printedparts: catalog access is printedparts.view-gated
Some checks failed
CI / backend (push) Successful in 1m43s
CI / naming (push) Successful in 2s
CI / frontend (push) Successful in 9s
CI / migrations-mysql (push) Failing after 8s

Browsing the catalog (item list, detail, file listings) now requires
authentication plus the view permission, and the /printedparts pages
and the label print page require login. Still deliberately open: the
kiosk endpoints per the decision record, the image serve and file
download (img tags and anchor downloads cannot carry a JWT), and the
reports (product-wide jwt-optional convention). Grant
printedparts.view to the roles that should see the catalog.
This commit is contained in:
cproudlock
2026-07-17 09:19:30 -04:00
parent 96e48e0f50
commit d1357defc4
5 changed files with 46 additions and 12 deletions

View File

@@ -1,8 +1,11 @@
"""Printedparts plugin API routes.
Reads are open (jwt optional) like every list surface; mutations arrive in
later stages with permission gates. The kiosk endpoints (unauthenticated by
explicit decision - see the proposal) also land later.
Access model: browsing the catalog (items, detail, file listings) requires
the printedparts.view permission; every mutation carries its own permission.
Deliberately open: the kiosk endpoints (decision record in the proposal),
the image serve and file download (fetched by <img> tags and anchor
downloads, which cannot carry a JWT header), and the reports (jwt-optional
like every other report in the product).
"""
from flask import Blueprint, request
@@ -26,7 +29,8 @@ printedparts_bp = Blueprint('printedparts', __name__)
@printedparts_bp.route('/items', methods=['GET'])
@jwt_required(optional=True)
@jwt_required()
@require_permission('printedparts.view')
def list_items():
"""List printed items, paginated; search + low-stock filter."""
page, per_page = get_pagination_params(request)
@@ -51,7 +55,8 @@ def list_items():
@printedparts_bp.route('/items/<int:item_id>', methods=['GET'])
@jwt_required(optional=True)
@jwt_required()
@require_permission('printedparts.view')
def get_item(item_id: int):
"""Get one printed item with its recent transactions."""
item = db.session.get(PrintedItem, item_id)
@@ -578,7 +583,8 @@ def _uploader_name():
@printedparts_bp.route('/items/<int:item_id>/files', methods=['GET'])
@jwt_required(optional=True)
@jwt_required()
@require_permission('printedparts.view')
def list_item_files(item_id: int):
"""Revision history, newest first."""
files = (PrintedItemFile.query.filter_by(printeditemid=item_id)