Promote the 2026-08-30 review security fixes and supplies ordering to main

This commit is contained in:
2026-08-31 09:24:13 -04:00
parent 6680c3c5de
commit 0e3d5a6226
30 changed files with 991 additions and 98 deletions

View File

@@ -18,7 +18,7 @@ from shopdb.core.models import (
from shopdb.core.api.settings import get_cached_settings
from shopdb.core.services.spellfix import Vocabulary
from shopdb.utils.responses import success_response
from shopdb.utils.authz import require_permission
from shopdb.utils.authz import require_permission, caller_holds
logger = logging.getLogger(__name__)
@@ -1233,22 +1233,42 @@ def global_search():
results = []
search_term = f'%{query}%'
# Run all search domains
results.extend(_search_applications(query, search_term))
results.extend(_search_knowledgebase(query, search_term))
results.extend(_search_guides(query, search_term))
results.extend(_search_employees(query, search_term))
results.extend(_search_assets(query, search_term))
results.extend(_search_measuringtools(query, search_term))
results.extend(_search_usbdevices(query, search_term))
results.extend(_search_printeditems(query, search_term))
results.extend(_search_customfields(query, search_term))
results.extend(_search_notifications(query, search_term))
results.extend(_search_hostnames(query, search_term))
results.extend(_search_vendor_model_type(query, search_term))
# Every domain answers only to a caller who holds ITS permission.
#
# The route is gated on assets.view alone, and that used to be the whole
# check: one permission then queried twelve domains, so it read out of the
# staff directory, the USB checkout history, the notification log and the
# network inventory to anybody who could look up an asset number. The
# SEARCH_DOMAINS settings are feature toggles a site sets, not authorization,
# and were never doing this job.
#
# caller_holds rather than require_permission: a search that refuses the
# whole request because one domain is off-limits is useless. Each domain
# simply contributes nothing.
domains = [
('assets.view', _search_assets),
# Emit asset results routed to an asset page, so they belong to the
# same grant as the assets themselves.
('assets.view', _search_customfields),
('assets.view', _search_vendor_model_type),
('applications.view', _search_applications),
('kb.view', _search_knowledgebase),
('guides.view', _search_guides),
('employees.view', _search_employees),
('measuringtools.view', _search_measuringtools),
('usb.view', _search_usbdevices),
('printedparts.view', _search_printeditems),
('notifications.view', _search_notifications),
('computers.view', _search_hostnames),
]
for permission, search in domains:
if caller_holds(permission):
results.extend(search(query, search_term))
# IP-specific searches
if classification['is_ip']:
# IP-specific searches. An address and the subnet it sits in are network
# inventory, and knowing what answers on 10.20.x is a different question
# from looking up an asset number.
if classification['is_ip'] and caller_holds('network.view'):
results.extend(_search_by_ip(query, search_term))
results.extend(_search_subnets(query))