Get rid of deterministic collection id generation (#4463)

It is a weird mismatch with the rest of the system, and introduces
scope for weird collision attacks if untrusted users to a system can
control explicitly inserted ids.

Co-authored-by: Elvis Pranskevichus <elvis@edgedb.com>
This commit is contained in:
Michael J. Sullivan 2022-10-13 10:40:13 -07:00 committed by GitHub
parent b711207bc4
commit b0a9f230dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 36 additions and 138 deletions

View file

@ -44,7 +44,7 @@ from edb.common import verutils
# Increment this whenever the database layout or stdlib changes.
EDGEDB_CATALOG_VERSION = 2022_10_07_00_00
EDGEDB_CATALOG_VERSION = 2022_10_11_00_00
EDGEDB_MAJOR_VERSION = 3

View file

@ -589,7 +589,9 @@ def compile_TypeCast(
)
if ex_param := ctx.env.script_params.get(param_name):
param_first_type = ex_param.schema_type
# N.B. Accessing the schema_type from the param is unreliable
ctx.env.schema, param_first_type = irtyputils.ir_typeref_to_type(
ctx.env.schema, ex_param.ir_type)
if param_first_type != pt:
raise errors.QueryError(
f'parameter type '

View file

@ -3981,17 +3981,7 @@ class GetBaseScalarTypeMap(dbops.Function):
else ql(f'pg_catalog.{v[0]}')
}
)"""
for k, v in types.base_type_name_map.items())},
{", ".join(
f"""(
{ql(str(k))}::uuid,
{
ql(f'{v[0]}.{v[1]}') if len(v) == 2
else ql(f'pg_catalog.{v[0]}')
}
)"""
for k, v in types.base_range_name_map.items())}
for k, v in types.base_type_name_map.items())}
'''
def __init__(self) -> None:
@ -4044,7 +4034,27 @@ class GetPgTypeForEdgeDBTypeFunction(dbops.Function):
edgedb._get_base_scalar_type_map()
AS m(tid uuid, tn text)
WHERE
tid = elemid
tid = "elemid"
)
),
(
SELECT
rng.rngtypid
FROM
pg_catalog.pg_range rng
INNER JOIN pg_catalog.pg_type typ
ON (rng.rngsubtype = typ.oid)
WHERE
typ.typname = "elemid"::text || '_domain'
OR typ.typname = "elemid"::text || '_t'
OR typ.oid = (
SELECT
tn::regtype::oid
FROM
edgedb._get_base_scalar_type_map()
AS m(tid uuid, tn text)
WHERE
tid = "elemid"
)
),
edgedb.raise(
@ -4053,7 +4063,7 @@ class GetPgTypeForEdgeDBTypeFunction(dbops.Function):
msg => (
format(
'cannot determine OID of EdgeDB type %L',
typeid::text
"typeid"::text
)
)
)

View file

@ -33,7 +33,6 @@ from edb.schema import objtypes as s_objtypes
from edb.schema import name as sn
from edb.schema import objects as s_obj
from edb.schema import schema as s_schema
from edb.schema import types as s_types
from . import common
@ -122,17 +121,6 @@ base_type_name_map_r = {
'memory_t': sn.QualName('cfg', 'memory'),
}
# Use the known type IDs to generate corresponding range type IDs
base_range_name_map = {
s_types.generate_type_id(
f'''range-{
s_obj.get_known_type_id(
base_type_name_map_r[".".join(pg_type)])
}'''
): pg_range
for pg_type, pg_range in type_to_range_name_map.items()
}
def is_builtin_scalar(schema, scalar):
return scalar.id in base_type_name_map

View file

@ -424,7 +424,7 @@ def _build_object_mutation_shape(
and not issubclass(mcls, s_types.CollectionExprAlias)
and not cmd.get_attribute_value('abstract')
):
if issubclass(mcls, s_types.Array):
if issubclass(mcls, (s_types.Array, s_types.Range)):
assignments.append(
f'backend_id := sys::_get_pg_type_for_edgedb_type('
f'<uuid>$__{var_prefix}id, '

View file

@ -1093,9 +1093,6 @@ class CollectionTypeShell(TypeShell[CollectionTypeT_co]):
) -> typing.Tuple[TypeShell[Type], ...]:
raise NotImplementedError
def get_id(self, schema: s_schema.Schema) -> uuid.UUID:
raise NotImplementedError
def is_polymorphic(self, schema: s_schema.Schema) -> bool:
return any(
st.is_polymorphic(schema) for st in self.get_subtypes(schema)
@ -1157,24 +1154,6 @@ class Array(
compcoef=0,
)
@classmethod
def generate_id(
cls,
schema: s_schema.Schema,
data: Dict[str, Any],
) -> uuid.UUID:
if (
data.get('alias_is_persistent')
or isinstance(data.get('name'), s_name.QualName)
):
return super().generate_id(schema, data)
else:
return generate_array_type_id(
schema,
data['element_type'],
data['dimensions'],
)
@classmethod
def generate_name(
cls,
@ -1219,6 +1198,8 @@ class Array(
dimensions=dimensions,
**kwargs,
)
# Compute material type so that we can retrieve it safely later
schema, _ = result.material_type(schema)
return schema, result
@ -1471,14 +1452,11 @@ class ArrayTypeShell(CollectionTypeShell[Array_T_co]):
def get_displayname(self, schema: s_schema.Schema) -> str:
return f'array<{self.subtype.get_displayname(schema)}>'
def get_id(self, schema: s_schema.Schema) -> uuid.UUID:
return generate_array_type_id(schema, self.subtype, self.typemods[0])
def resolve(self, schema: s_schema.Schema) -> Array_T_co:
if isinstance(self.name, s_name.QualName):
arr = schema.get(self.name, type=Array)
else:
arr = schema.get_by_id(self.get_id(schema), type=Array)
arr = schema.get_global(Array, self.get_name(schema))
return arr # type: ignore
def as_create_delta(
@ -1495,7 +1473,6 @@ class ArrayTypeShell(CollectionTypeShell[Array_T_co]):
classname=self.get_name(schema),
if_not_exists=True,
)
ca.set_attribute_value('id', self.get_id(schema))
else:
ca = CreateArrayExprAlias(
classname=view_name,
@ -1561,21 +1538,6 @@ class Tuple(
reflection_proxy=('schema::TupleElement', 'type'),
)
@classmethod
def generate_id(
cls,
schema: s_schema.Schema,
data: Dict[str, Any],
) -> uuid.UUID:
if isinstance(data['name'], s_name.QualName):
return super().generate_id(schema, data)
else:
return generate_tuple_type_id(
schema,
dict(data['element_types'].items(schema)),
data.get('named', False),
)
@classmethod
def generate_name(
cls,
@ -1623,6 +1585,8 @@ class Tuple(
element_types=el_types,
**kwargs,
)
# Compute material type so that we can retrieve it safely later
schema, _ = result.material_type(schema)
return schema, result
@ -2053,14 +2017,11 @@ class TupleTypeShell(CollectionTypeShell[Tuple_T_co]):
def is_named(self) -> bool:
return self.typemods is not None and self.typemods.get('named', False)
def get_id(self, schema: s_schema.Schema) -> uuid.UUID:
return generate_tuple_type_id(schema, self.subtypes, self.is_named())
def resolve(self, schema: s_schema.Schema) -> Tuple_T_co:
if isinstance(self.name, s_name.QualName):
tup = schema.get(self.name, type=Tuple)
else:
tup = schema.get_by_id(self.get_id(schema), type=Tuple)
tup = schema.get_global(Tuple, self.get_name(schema))
return tup # type: ignore
def as_create_delta(
@ -2097,7 +2058,6 @@ class TupleTypeShell(CollectionTypeShell[Tuple_T_co]):
self.is_named(),
)
ct = CreateTuple(classname=name, if_not_exists=True)
ct.set_attribute_value('id', self.get_id(schema))
self._populate_create_delta(schema, ct)
return ct
@ -2150,23 +2110,6 @@ class Range(
compcoef=0,
)
@classmethod
def generate_id(
cls,
schema: s_schema.Schema,
data: Dict[str, Any],
) -> uuid.UUID:
if (
data.get('alias_is_persistent')
or isinstance(data.get('name'), s_name.QualName)
):
return super().generate_id(schema, data)
else:
return generate_range_type_id(
schema,
data['element_type'],
)
@classmethod
def generate_name(
cls,
@ -2436,14 +2379,11 @@ class RangeTypeShell(CollectionTypeShell[Range_T_co]):
def get_displayname(self, schema: s_schema.Schema) -> str:
return f'range<{self.subtype.get_displayname(schema)}>'
def get_id(self, schema: s_schema.Schema) -> uuid.UUID:
return generate_range_type_id(schema, self.subtype)
def resolve(self, schema: s_schema.Schema) -> Range_T_co:
if isinstance(self.name, s_name.QualName):
rng = schema.get(self.name, type=Range)
else:
rng = schema.get_by_id(self.get_id(schema), type=Range)
rng = schema.get_global(Range, self.get_name(schema))
return rng # type: ignore
def as_create_delta(
@ -2460,7 +2400,6 @@ class RangeTypeShell(CollectionTypeShell[Range_T_co]):
classname=self.get_name(schema),
if_not_exists=True,
)
ca.set_attribute_value('id', self.get_id(schema))
else:
ca = CreateRangeExprAlias(
classname=view_name,
@ -2497,47 +2436,6 @@ class RangeExprAlias(
return Range
def generate_type_id(id_str: str) -> uuid.UUID:
return uuidgen.uuid5(TYPE_ID_NAMESPACE, id_str)
def generate_tuple_type_id(
schema: s_schema.Schema,
element_types: Mapping[str, Union[Type, TypeShell[Type]]],
named: bool = False,
*quals: str,
) -> uuid.UUID:
id_str = ','.join(
f'{n}:{st.get_id(schema)}' for n, st in element_types.items())
id_str = f'{"named" if named else ""}tuple-{id_str}'
if quals:
id_str = f'{id_str}_{"-".join(quals)}'
return generate_type_id(id_str)
def generate_array_type_id(
schema: s_schema.Schema,
element_type: Union[Type, TypeShell[Type]],
dimensions: Sequence[int] = (),
*quals: str,
) -> uuid.UUID:
id_basis = f'array-{element_type.get_id(schema)}-{dimensions}'
if quals:
id_basis = f'{id_basis}-{"-".join(quals)}'
return generate_type_id(id_basis)
def generate_range_type_id(
schema: s_schema.Schema,
element_type: Union[Type, TypeShell[Type]],
*quals: str,
) -> uuid.UUID:
id_basis = f'range-{element_type.get_id(schema)}'
if quals:
id_basis = f'{id_basis}-{"-".join(quals)}'
return generate_type_id(id_basis)
def get_union_type_name(
component_names: typing.Iterable[s_name.Name],
*,

View file

@ -1050,7 +1050,7 @@ async def _init_stdlib(
compiler,
stdlib.reflschema,
'''
UPDATE schema::Array
UPDATE (schema::Array UNION schema::Range)
FILTER
.builtin
AND NOT (.abstract ?? False)