printedparts stage 6: RBAC - declared permissions gate every mutation
get_permissions declares view/create/edit/delete/restock (seeded on install/enable and by flask seed permissions); every write route adds require_permission on top of jwt_required. New test proves authentication alone is not authorization: a role-less member gets 403 where an admin succeeds.
This commit is contained in:
@@ -17,6 +17,7 @@ from shopdb.api import (
|
||||
ErrorCodes,
|
||||
get_pagination_params,
|
||||
paginate_query,
|
||||
require_permission,
|
||||
)
|
||||
|
||||
from ..models import PrintedItem
|
||||
@@ -95,6 +96,7 @@ def _mint_itemcode(item):
|
||||
|
||||
@printedparts_bp.route('/items', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_permission('printedparts.create')
|
||||
def create_item():
|
||||
"""Create a printed item; the itemcode is minted from the row id."""
|
||||
data = request.get_json() or {}
|
||||
@@ -124,6 +126,7 @@ def create_item():
|
||||
|
||||
@printedparts_bp.route('/items/<int:item_id>', methods=['PUT'])
|
||||
@jwt_required()
|
||||
@require_permission('printedparts.edit')
|
||||
def update_item(item_id: int):
|
||||
"""Update catalog fields. Quantity moves ONLY through the ledger."""
|
||||
item = db.session.get(PrintedItem, item_id)
|
||||
@@ -144,6 +147,7 @@ def update_item(item_id: int):
|
||||
|
||||
@printedparts_bp.route('/items/<int:item_id>', methods=['DELETE'])
|
||||
@jwt_required()
|
||||
@require_permission('printedparts.delete')
|
||||
def delete_item(item_id: int):
|
||||
"""Soft-retire an item; its ledger history stays."""
|
||||
item = db.session.get(PrintedItem, item_id)
|
||||
@@ -159,6 +163,7 @@ def delete_item(item_id: int):
|
||||
|
||||
@printedparts_bp.route('/items/<int:item_id>/image', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_permission('printedparts.edit')
|
||||
def upload_item_image(item_id: int):
|
||||
"""Upload (or replace) the photo for an item (multipart file=<image>)."""
|
||||
item = db.session.get(PrintedItem, item_id)
|
||||
@@ -195,6 +200,7 @@ def serve_item_image(filename):
|
||||
|
||||
@printedparts_bp.route('/items/<int:item_id>/image', methods=['DELETE'])
|
||||
@jwt_required()
|
||||
@require_permission('printedparts.delete')
|
||||
def delete_item_image(item_id: int):
|
||||
"""Clear an item image; delete the file only if this plugin owns it."""
|
||||
item = db.session.get(PrintedItem, item_id)
|
||||
@@ -238,6 +244,7 @@ def _ledger_write(item, transactiontype, quantitychange, sso, name, reason=None)
|
||||
|
||||
@printedparts_bp.route('/items/<int:item_id>/restock', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_permission('printedparts.restock')
|
||||
def restock_item(item_id: int):
|
||||
"""Add freshly printed stock. Body: {quantity, badge}."""
|
||||
item = db.session.get(PrintedItem, item_id)
|
||||
@@ -260,6 +267,7 @@ def restock_item(item_id: int):
|
||||
|
||||
@printedparts_bp.route('/items/<int:item_id>/adjust', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_permission('printedparts.restock')
|
||||
def adjust_item(item_id: int):
|
||||
"""Correct the count (damage, recount). Body: {quantitychange, reason, badge}."""
|
||||
item = db.session.get(PrintedItem, item_id)
|
||||
|
||||
Reference in New Issue
Block a user