Remediate the 2026-09-03 feature-test findings
This commit is contained in:
@@ -36,10 +36,27 @@ def _blueprint_dir():
|
||||
|
||||
# =============================================================================
|
||||
# Read - public
|
||||
#
|
||||
# ADR-017 rule 6: the level list is readable without authentication, because the
|
||||
# printer installer map runs optional-auth before anyone logs in and needs a
|
||||
# blueprint. It follows the precedent of printer install-list and the slide
|
||||
# feed, both still public.
|
||||
#
|
||||
# afce2437 ("Reading anything needs a permission") swept 165 open GET routes
|
||||
# and caught these three in the net. Fail-closed, so nothing leaked - but the
|
||||
# anonymous installer map has drawn no floor plan since, while this same file
|
||||
# kept the docstring saying the reads are public, the API inventory kept
|
||||
# declaring auth "none", and PrinterInstallerMap.vue kept its comment
|
||||
# explaining why. Four places agreed and the code did not.
|
||||
#
|
||||
# Restored rather than amending the ADR: blueprint paths and level names are
|
||||
# not secrets, the asset positions on them already render on public kiosk
|
||||
# pages, and the feature the rule exists for is broken without it. These three
|
||||
# are registered in tests/test_core/test_public_read_surface.py.
|
||||
# =============================================================================
|
||||
|
||||
@maplevels_bp.route('', methods=['GET'])
|
||||
@require_permission('assets.view')
|
||||
@jwt_required(optional=True)
|
||||
def list_levels():
|
||||
"""Every active level, grouped by building, in display order.
|
||||
|
||||
@@ -74,7 +91,7 @@ def list_levels():
|
||||
|
||||
|
||||
@maplevels_bp.route('/<int:levelid>', methods=['GET'])
|
||||
@require_permission('assets.view')
|
||||
@jwt_required(optional=True)
|
||||
def get_level(levelid):
|
||||
level = db.session.get(MapLevel, levelid)
|
||||
if not level or not level.isactive:
|
||||
@@ -84,7 +101,7 @@ def get_level(levelid):
|
||||
|
||||
|
||||
@maplevels_bp.route('/<int:levelid>/blueprint/<path:filename>', methods=['GET'])
|
||||
@require_permission('assets.view')
|
||||
@jwt_required(optional=True)
|
||||
def serve_blueprint(filename, levelid=None):
|
||||
"""Serve a level's blueprint image. Public, for the same reason as above."""
|
||||
return send_upload(_blueprint_dir(), filename)
|
||||
@@ -160,9 +177,17 @@ def create_level():
|
||||
data = request.get_json() or {}
|
||||
name = (data.get('levelname') or '').strip()
|
||||
buildingid = data.get('buildingid')
|
||||
# Names the fields this route actually reads. It said 'buildingname',
|
||||
# which this handler never looks at, so somebody who omitted the level name
|
||||
# was told to supply something else entirely.
|
||||
if not name or not buildingid:
|
||||
return error_response(ErrorCodes.VALIDATION_ERROR,
|
||||
'buildingname and buildingid are required')
|
||||
missing = [field for field, value in
|
||||
(('levelname', name), ('buildingid', buildingid))
|
||||
if not value]
|
||||
return error_response(
|
||||
ErrorCodes.VALIDATION_ERROR,
|
||||
'%s %s required' % (' and '.join(missing),
|
||||
'is' if len(missing) == 1 else 'are'))
|
||||
if not db.session.get(Building, buildingid):
|
||||
return error_response(ErrorCodes.NOT_FOUND, 'No such building',
|
||||
http_code=404)
|
||||
|
||||
Reference in New Issue
Block a user