database: handle invalid reduce results

It is possible to reduce to a value that is not a dict in "insert_item()"
and "update_item()".  Raise a ServerError with a clear description of
what went wrong in these situations.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Eric Callahan 2022-02-16 12:44:08 -05:00
parent 89e78b9a00
commit bb39a3c1b2
1 changed files with 20 additions and 2 deletions

View File

@ -15,7 +15,7 @@ from io import BytesIO
from functools import reduce from functools import reduce
from threading import Lock as ThreadLock from threading import Lock as ThreadLock
import lmdb import lmdb
from utils import SentinelClass from utils import SentinelClass, ServerError
# Annotation imports # Annotation imports
from typing import ( from typing import (
@ -64,6 +64,9 @@ RECORD_DECODE_FUNCS = {
SENTINEL = SentinelClass.get_instance() SENTINEL = SentinelClass.get_instance()
def getitem_with_default(item: Dict, field: Any) -> Any: def getitem_with_default(item: Dict, field: Any) -> Any:
if not isinstance(item, Dict):
raise ServerError(
f"Cannot reduce a value of type {type(item)}")
if field not in item: if field not in item:
item[field] = {} item[field] = {}
return item[field] return item[field]
@ -210,6 +213,12 @@ class MoonrakerDatabase:
f"{prev_type}. Overwriting with an object.") f"{prev_type}. Overwriting with an object.")
item: Dict[str, Any] = reduce( item: Dict[str, Any] = reduce(
getitem_with_default, key_list[1:-1], record) getitem_with_default, key_list[1:-1], record)
if not isinstance(item, dict):
rpt_key = ".".join(key_list[:-1])
raise self.server.error(
f"Item at key '{rpt_key}' in namespace '{namespace}'is "
"not a dictionary object, cannot insert"
)
item[key_list[-1]] = value item[key_list[-1]] = value
if not self._insert_record(namespace, key_list[0], record): if not self._insert_record(namespace, key_list[0], record):
logging.info( logging.info(
@ -233,7 +242,10 @@ class MoonrakerDatabase:
if isinstance(record, dict) and isinstance(value, dict): if isinstance(record, dict) and isinstance(value, dict):
record.update(value) record.update(value)
else: else:
assert value is not None if value is None:
raise self.server.error(
f"Item at key '{key}', namespace '{namespace}': "
"Cannot assign a record level null value")
record = value record = value
else: else:
try: try:
@ -244,6 +256,12 @@ class MoonrakerDatabase:
raise self.server.error( raise self.server.error(
f"Key '{key}' in namespace '{namespace}' not found", f"Key '{key}' in namespace '{namespace}' not found",
404) 404)
if not isinstance(item, dict) or key_list[-1] not in item:
rpt_key = ".".join(key_list[:-1])
raise self.server.error(
f"Item at key '{rpt_key}' in namespace '{namespace}'is "
"not a dictionary object, cannot update"
)
if isinstance(item[key_list[-1]], dict) \ if isinstance(item[key_list[-1]], dict) \
and isinstance(value, dict): and isinstance(value, dict):
item[key_list[-1]].update(value) item[key_list[-1]].update(value)