Repository: edgedb/edgedb

Issue:
Access policies are ineffective for modifying abstract types, leading to the ability to directly modify entity types by altering abstract types.
<!-- Please search existing issues to avoid creating duplicates. -->

<!--
For the EdgeDB Version: run `edgedb query 'select sys::get_version_as_str()'` from your project directory (or run `select sys::get_version_as_str();` in the EdgeDB interactive shell).
For the EdgeDB CLI Version: Run `edgedb --version` from anywhere
-->

- EdgeDB Version:3.3+d5734dd
- EdgeDB CLI Version:EdgeDB CLI 3.5.0+e1ad387
- OS Version: macos 13.4.1 

Steps to Reproduce:
```

module default {
    global current_user_id: uuid;

    global current_user := (
        select User filter .id = global current_user_id
    );
   abstract type User{
        required role: str

        access policy allow_all
            allow all
            using (global current_user.role ?= 'admin'){
              errmessage := 'forbid'
            };

        access policy only_read
            allow select
            using (global current_user.role ?= 'readonly'){
              errmessage := 'forbid'
            };
    }

     type Customer extending User{
       required mobile: str
    }

}
```

1. Set the current user's role to 'readonly'.

I was surprised that I could successfully modify the abstract type 'User' directly, which resulted in unintentional changes to 'Customer.' However, attempting to directly modify the 'Customer' entity type was unsuccessful. This has left me puzzled.

![image](https://github.com/edgedb/edgedb/assets/33833181/7ea875d8-840e-44cf-9afe-713f22552b8e)
![image](https://github.com/edgedb/edgedb/assets/33833181/8e1cd426-cb0f-43e7-b3db-7a01c9fc1ac7)






Discussion:
I have no idea how I accidentally assigned Dave and Dijana to this issue; this one goes to me :(

Resolving patch:
diff --git a/edb/edgeql/compiler/policies.py b/edb/edgeql/compiler/policies.py
--- a/edb/edgeql/compiler/policies.py
+++ b/edb/edgeql/compiler/policies.py
@@ -70,6 +70,9 @@ def get_access_policies(
     stype: s_objtypes.ObjectType, *, ctx: context.ContextLevel,
 ) -> Tuple[s_policies.AccessPolicy, ...]:
     schema = ctx.env.schema
+    if not ctx.env.options.apply_query_rewrites:
+        return ()
+
     # The apply_access_policies config flag disables user-specified
     # access polices, but not stdlib ones
     if (
@@ -380,7 +383,8 @@ def compile_dml_write_policies(
     ctx: context.ContextLevel,
 ) -> Optional[irast.WritePolicies]:
     """Compile policy filters and wrap them into irast.WritePolicies"""
-    if not ctx.env.type_rewrites.get((stype, False)):
+    pols = get_access_policies(stype, ctx=ctx)
+    if not pols:
         return None
 
     with ctx.detached() as _, _.newscope(fenced=True) as subctx:
@@ -391,10 +395,6 @@ def compile_dml_write_policies(
         schema = subctx.env.schema
         subctx.anchors = subctx.anchors.copy()
 
-        pols = get_access_policies(stype, ctx=ctx)
-        if not pols:
-            return None
-
         policies = []
         for pol in pols:
             if mode not in pol.get_access_kinds(schema):
@@ -425,7 +425,7 @@ def compile_dml_read_policies(
     ctx: context.ContextLevel,
 ) -> Optional[irast.ReadPolicyExpr]:
     """Compile a policy filter for a DML statement at a particular type"""
-    if not ctx.env.type_rewrites.get((stype, False)):
+    if not get_access_policies(stype, ctx=ctx):
         return None
 
     with ctx.detached() as _, _.newscope(fenced=True) as subctx:
@@ -450,6 +450,8 @@ def _prepare_dml_policy_context(
     *,
     ctx: context.ContextLevel,
 ) -> None:
+    # It doesn't matter whether we skip subtypes here, so don't skip
+    # subtypes if it has already been compiled that way, otherwise do.
     skip_subtypes = (stype, False) not in ctx.env.type_rewrites
     result = setgen.class_set(
         stype, path_id=result.path_id, skip_subtypes=skip_subtypes, ctx=ctx


Test patch:
diff --git a/tests/test_edgeql_policies.py b/tests/test_edgeql_policies.py
--- a/tests/test_edgeql_policies.py
+++ b/tests/test_edgeql_policies.py
@@ -1247,3 +1247,43 @@ async def test_edgeql_policies_optional_leakage_01(self):
             r'''select Src''',
             [],
         )
+
+    async def test_edgeql_policies_parent_update_01(self):
+        await self.con.execute('''
+            CREATE ABSTRACT TYPE Base {
+                CREATE PROPERTY name: std::str;
+                CREATE ACCESS POLICY sel_ins
+                    ALLOW SELECT, INSERT USING (true);
+            };
+            CREATE TYPE Child EXTENDING Base;
+
+            INSERT Child;
+        ''')
+
+        await self.assert_query_result(
+            '''
+            update Base set { name := '!!!' }
+            ''',
+            [],
+        )
+
+        await self.assert_query_result(
+            '''
+            delete Base
+            ''',
+            [],
+        )
+
+        await self.con.execute('''
+            ALTER TYPE Base {
+                CREATE ACCESS POLICY upd_read
+                    ALLOW UPDATE READ USING (true);
+            };
+        ''')
+
+        async with self.assertRaisesRegexTx(
+                edgedb.InvalidValueError,
+                r"access policy violation on update"):
+            await self.con.query('''
+                update Base set { name := '!!!' }
+            ''')