prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Memory permission backend implementation of remove_principal() is wrong
  According to the `PermissionBase` docstring, `remove_principal` is supposed to `Remove a principal from every user`. In other words, `remove_principal(principal)` is equivalent to `remove_user_principal(user_id, principal) for user_id in all_possible_user_ids`. However, the current implementation stores all permissions of all kinds in one hash table, and removes the principal from permissions of non-user things as well.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `kinto/core/permission/memory.py`
  Content:
  ```
  1 import re
  2 
  3 from kinto.core.decorators import synchronized
  4 from kinto.core.permission import PermissionBase
  5 
  6 
  7 class Permission(PermissionBase):
  8     """Permission backend implementation in local process memory.
  9 
  10     Enable in configuration::
  11 
  12         kinto.permission_backend = kinto.core.permission.memory
  13 
  14     :noindex:
  15     """
  16 
  17     def __init__(self, *args, **kwargs):
  18         super().__init__(*args, **kwargs)
  19         self.flush()
  20 
  21     def initialize_schema(self, dry_run=False):
  22         # Nothing to do.
  23         pass
  24 
  25     def flush(self):
  26         self._store = {}
  27 
  28     @synchronized
  29     def add_user_principal(self, user_id, principal):
  30         user_key = f"user:{user_id}"
  31         user_principals = self._store.get(user_key, set())
  32         user_principals.add(principal)
  33         self._store[user_key] = user_principals
  34 
  35     @synchronized
  36     def remove_user_principal(self, user_id, principal):
  37         user_key = f"user:{user_id}"
  38         user_principals = self._store.get(user_key, set())
  39         try:
  40             user_principals.remove(principal)
  41         except KeyError:
  42             pass
  43         if len(user_principals) == 0:
  44             if user_key in self._store:
  45                 del self._store[user_key]
  46         else:
  47             self._store[user_key] = user_principals
  48 
  49     @synchronized
  50     def remove_principal(self, principal):
  51         for user_principals in self._store.values():
  52             try:
  53                 user_principals.remove(principal)
  54             except KeyError:
  55                 pass
  56 
  57     @synchronized
  58     def get_user_principals(self, user_id):
  59         # Fetch the groups the user is in.
  60         user_key = f"user:{user_id}"
  61         members = self._store.get(user_key, set())
  62         # Fetch the groups system.Authenticated is in.
  63         group_authenticated = self._store.get("user:system.Authenticated", set())
  64         return members | group_authenticated
  65 
  66     @synchronized
  67     def add_principal_to_ace(self, object_id, permission, principal):
  68         permission_key = f"permission:{object_id}:{permission}"
  69         object_permission_principals = self._store.get(permission_key, set())
  70         object_permission_principals.add(principal)
  71         self._store[permission_key] = object_permission_principals
  72 
  73     @synchronized
  74     def remove_principal_from_ace(self, object_id, permission, principal):
  75         permission_key = f"permission:{object_id}:{permission}"
  76         object_permission_principals = self._store.get(permission_key, set())
  77         try:
  78             object_permission_principals.remove(principal)
  79         except KeyError:
  80             pass
  81         if len(object_permission_principals) == 0:
  82             if permission_key in self._store:
  83                 del self._store[permission_key]
  84         else:
  85             self._store[permission_key] = object_permission_principals
  86 
  87     @synchronized
  88     def get_object_permission_principals(self, object_id, permission):
  89         permission_key = f"permission:{object_id}:{permission}"
  90         members = self._store.get(permission_key, set())
  91         return members
  92 
  93     @synchronized
  94     def get_accessible_objects(self, principals, bound_permissions=None, with_children=True):
  95         principals = set(principals)
  96         candidates = []
  97         if bound_permissions is None:
  98             for key, value in self._store.items():
  99                 _, object_id, permission = key.split(":", 2)
  100                 candidates.append((object_id, permission, value))
  101         else:
  102             for pattern, perm in bound_permissions:
  103                 id_match = ".*" if with_children else "[^/]+"
  104                 regexp = re.compile(f"^{pattern.replace('*', id_match)}$")
  105                 for key, value in self._store.items():
  106                     if key.endswith(perm):
  107                         object_id = key.split(":")[1]
  108                         if regexp.match(object_id):
  109                             candidates.append((object_id, perm, value))
  110 
  111         perms_by_object_id = {}
  112         for (object_id, perm, value) in candidates:
  113             if len(principals & value) > 0:
  114                 perms_by_object_id.setdefault(object_id, set()).add(perm)
  115         return perms_by_object_id
  116 
  117     @synchronized
  118     def get_authorized_principals(self, bound_permissions):
  119         principals = set()
  120         for obj_id, perm in bound_permissions:
  121             principals |= self.get_object_permission_principals(obj_id, perm)
  122         return principals
  123 
  124     @synchronized
  125     def get_objects_permissions(self, objects_ids, permissions=None):
  126         result = []
  127         for object_id in objects_ids:
  128             if permissions is None:
  129                 aces = [k for k in self._store.keys() if k.startswith(f"permission:{object_id}:")]
  130             else:
  131                 aces = [f"permission:{object_id}:{permission}" for permission in permissions]
  132             perms = {}
  133             for ace in aces:
  134                 # Should work with 'permission:/url/id:object:create'.
  135                 permission = ace.split(":", 2)[2]
  136                 perms[permission] = set(self._store[ace])
  137             result.append(perms)
  138         return result
  139 
  140     @synchronized
  141     def replace_object_permissions(self, object_id, permissions):
  142         for permission, principals in permissions.items():
  143             permission_key = f"permission:{object_id}:{permission}"
  144             if permission_key in self._store and len(principals) == 0:
  145                 del self._store[permission_key]
  146             elif principals:
  147                 self._store[permission_key] = set(principals)
  148         return permissions
  149 
  150     @synchronized
  151     def delete_object_permissions(self, *object_id_list):
  152         to_delete = []
  153         for key in self._store.keys():
  154             object_id = key.split(":")[1]
  155             for pattern in object_id_list:
  156                 regexp = re.compile(f"^{pattern.replace('*', '.*')}$")
  157                 if regexp.match(object_id):
  158                     to_delete.append(key)
  159         for k in to_delete:
  160             del self._store[k]
  161 
  162 
  163 def load_from_config(config):
  164     return Permission()
  165
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/kinto/core/permission/memory.py b/kinto/core/permission/memory.py
  --- a/kinto/core/permission/memory.py
  +++ b/kinto/core/permission/memory.py
  @@ -48,7 +48,9 @@
   
       @synchronized
       def remove_principal(self, principal):
  -        for user_principals in self._store.values():
  +        for key, user_principals in self._store.items():
  +            if not key.startswith("user:"):
  +                continue
               try:
                   user_principals.remove(principal)
               except KeyError:
  
verification_info: {"golden_diff": "diff --git a/kinto/core/permission/memory.py b/kinto/core/permission/memory.py\n--- a/kinto/core/permission/memory.py\n+++ b/kinto/core/permission/memory.py\n@@ -48,7 +48,9 @@\n \n     @synchronized\n     def remove_principal(self, principal):\n-        for user_principals in self._store.values():\n+        for key, user_principals in self._store.items():\n+            if not key.startswith(\"user:\"):\n+                continue\n             try:\n                 user_principals.remove(principal)\n             except KeyError:\n", "issue": "Memory permission backend implementation of remove_principal() is wrong\nAccording to the `PermissionBase` docstring, `remove_principal` is supposed to `Remove a principal from every user`. In other words, `remove_principal(principal)` is equivalent to `remove_user_principal(user_id, principal) for user_id in all_possible_user_ids`. However, the current implementation stores all permissions of all kinds in one hash table, and removes the principal from permissions of non-user things as well.\n", "before_files": [{"content": "import re\n\nfrom kinto.core.decorators import synchronized\nfrom kinto.core.permission import PermissionBase\n\n\nclass Permission(PermissionBase):\n    \"\"\"Permission backend implementation in local process memory.\n\n    Enable in configuration::\n\n        kinto.permission_backend = kinto.core.permission.memory\n\n    :noindex:\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.flush()\n\n    def initialize_schema(self, dry_run=False):\n        # Nothing to do.\n        pass\n\n    def flush(self):\n        self._store = {}\n\n    @synchronized\n    def add_user_principal(self, user_id, principal):\n        user_key = f\"user:{user_id}\"\n        user_principals = self._store.get(user_key, set())\n        user_principals.add(principal)\n        self._store[user_key] = user_principals\n\n    @synchronized\n    def remove_user_principal(self, user_id, principal):\n        user_key = f\"user:{user_id}\"\n        user_principals = self._store.get(user_key, set())\n        try:\n            user_principals.remove(principal)\n        except KeyError:\n            pass\n        if len(user_principals) == 0:\n            if user_key in self._store:\n                del self._store[user_key]\n        else:\n            self._store[user_key] = user_principals\n\n    @synchronized\n    def remove_principal(self, principal):\n        for user_principals in self._store.values():\n            try:\n                user_principals.remove(principal)\n            except KeyError:\n                pass\n\n    @synchronized\n    def get_user_principals(self, user_id):\n        # Fetch the groups the user is in.\n        user_key = f\"user:{user_id}\"\n        members = self._store.get(user_key, set())\n        # Fetch the groups system.Authenticated is in.\n        group_authenticated = self._store.get(\"user:system.Authenticated\", set())\n        return members | group_authenticated\n\n    @synchronized\n    def add_principal_to_ace(self, object_id, permission, principal):\n        permission_key = f\"permission:{object_id}:{permission}\"\n        object_permission_principals = self._store.get(permission_key, set())\n        object_permission_principals.add(principal)\n        self._store[permission_key] = object_permission_principals\n\n    @synchronized\n    def remove_principal_from_ace(self, object_id, permission, principal):\n        permission_key = f\"permission:{object_id}:{permission}\"\n        object_permission_principals = self._store.get(permission_key, set())\n        try:\n            object_permission_principals.remove(principal)\n        except KeyError:\n            pass\n        if len(object_permission_principals) == 0:\n            if permission_key in self._store:\n                del self._store[permission_key]\n        else:\n            self._store[permission_key] = object_permission_principals\n\n    @synchronized\n    def get_object_permission_principals(self, object_id, permission):\n        permission_key = f\"permission:{object_id}:{permission}\"\n        members = self._store.get(permission_key, set())\n        return members\n\n    @synchronized\n    def get_accessible_objects(self, principals, bound_permissions=None, with_children=True):\n        principals = set(principals)\n        candidates = []\n        if bound_permissions is None:\n            for key, value in self._store.items():\n                _, object_id, permission = key.split(\":\", 2)\n                candidates.append((object_id, permission, value))\n        else:\n            for pattern, perm in bound_permissions:\n                id_match = \".*\" if with_children else \"[^/]+\"\n                regexp = re.compile(f\"^{pattern.replace('*', id_match)}$\")\n                for key, value in self._store.items():\n                    if key.endswith(perm):\n                        object_id = key.split(\":\")[1]\n                        if regexp.match(object_id):\n                            candidates.append((object_id, perm, value))\n\n        perms_by_object_id = {}\n        for (object_id, perm, value) in candidates:\n            if len(principals & value) > 0:\n                perms_by_object_id.setdefault(object_id, set()).add(perm)\n        return perms_by_object_id\n\n    @synchronized\n    def get_authorized_principals(self, bound_permissions):\n        principals = set()\n        for obj_id, perm in bound_permissions:\n            principals |= self.get_object_permission_principals(obj_id, perm)\n        return principals\n\n    @synchronized\n    def get_objects_permissions(self, objects_ids, permissions=None):\n        result = []\n        for object_id in objects_ids:\n            if permissions is None:\n                aces = [k for k in self._store.keys() if k.startswith(f\"permission:{object_id}:\")]\n            else:\n                aces = [f\"permission:{object_id}:{permission}\" for permission in permissions]\n            perms = {}\n            for ace in aces:\n                # Should work with 'permission:/url/id:object:create'.\n                permission = ace.split(\":\", 2)[2]\n                perms[permission] = set(self._store[ace])\n            result.append(perms)\n        return result\n\n    @synchronized\n    def replace_object_permissions(self, object_id, permissions):\n        for permission, principals in permissions.items():\n            permission_key = f\"permission:{object_id}:{permission}\"\n            if permission_key in self._store and len(principals) == 0:\n                del self._store[permission_key]\n            elif principals:\n                self._store[permission_key] = set(principals)\n        return permissions\n\n    @synchronized\n    def delete_object_permissions(self, *object_id_list):\n        to_delete = []\n        for key in self._store.keys():\n            object_id = key.split(\":\")[1]\n            for pattern in object_id_list:\n                regexp = re.compile(f\"^{pattern.replace('*', '.*')}$\")\n                if regexp.match(object_id):\n                    to_delete.append(key)\n        for k in to_delete:\n            del self._store[k]\n\n\ndef load_from_config(config):\n    return Permission()\n", "path": "kinto/core/permission/memory.py"}], "after_files": [{"content": "import re\n\nfrom kinto.core.decorators import synchronized\nfrom kinto.core.permission import PermissionBase\n\n\nclass Permission(PermissionBase):\n    \"\"\"Permission backend implementation in local process memory.\n\n    Enable in configuration::\n\n        kinto.permission_backend = kinto.core.permission.memory\n\n    :noindex:\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.flush()\n\n    def initialize_schema(self, dry_run=False):\n        # Nothing to do.\n        pass\n\n    def flush(self):\n        self._store = {}\n\n    @synchronized\n    def add_user_principal(self, user_id, principal):\n        user_key = f\"user:{user_id}\"\n        user_principals = self._store.get(user_key, set())\n        user_principals.add(principal)\n        self._store[user_key] = user_principals\n\n    @synchronized\n    def remove_user_principal(self, user_id, principal):\n        user_key = f\"user:{user_id}\"\n        user_principals = self._store.get(user_key, set())\n        try:\n            user_principals.remove(principal)\n        except KeyError:\n            pass\n        if len(user_principals) == 0:\n            if user_key in self._store:\n                del self._store[user_key]\n        else:\n            self._store[user_key] = user_principals\n\n    @synchronized\n    def remove_principal(self, principal):\n        for key, user_principals in self._store.items():\n            if not key.startswith(\"user:\"):\n                continue\n            try:\n                user_principals.remove(principal)\n            except KeyError:\n                pass\n\n    @synchronized\n    def get_user_principals(self, user_id):\n        # Fetch the groups the user is in.\n        user_key = f\"user:{user_id}\"\n        members = self._store.get(user_key, set())\n        # Fetch the groups system.Authenticated is in.\n        group_authenticated = self._store.get(\"user:system.Authenticated\", set())\n        return members | group_authenticated\n\n    @synchronized\n    def add_principal_to_ace(self, object_id, permission, principal):\n        permission_key = f\"permission:{object_id}:{permission}\"\n        object_permission_principals = self._store.get(permission_key, set())\n        object_permission_principals.add(principal)\n        self._store[permission_key] = object_permission_principals\n\n    @synchronized\n    def remove_principal_from_ace(self, object_id, permission, principal):\n        permission_key = f\"permission:{object_id}:{permission}\"\n        object_permission_principals = self._store.get(permission_key, set())\n        try:\n            object_permission_principals.remove(principal)\n        except KeyError:\n            pass\n        if len(object_permission_principals) == 0:\n            if permission_key in self._store:\n                del self._store[permission_key]\n        else:\n            self._store[permission_key] = object_permission_principals\n\n    @synchronized\n    def get_object_permission_principals(self, object_id, permission):\n        permission_key = f\"permission:{object_id}:{permission}\"\n        members = self._store.get(permission_key, set())\n        return members\n\n    @synchronized\n    def get_accessible_objects(self, principals, bound_permissions=None, with_children=True):\n        principals = set(principals)\n        candidates = []\n        if bound_permissions is None:\n            for key, value in self._store.items():\n                _, object_id, permission = key.split(\":\", 2)\n                candidates.append((object_id, permission, value))\n        else:\n            for pattern, perm in bound_permissions:\n                id_match = \".*\" if with_children else \"[^/]+\"\n                regexp = re.compile(f\"^{pattern.replace('*', id_match)}$\")\n                for key, value in self._store.items():\n                    if key.endswith(perm):\n                        object_id = key.split(\":\")[1]\n                        if regexp.match(object_id):\n                            candidates.append((object_id, perm, value))\n\n        perms_by_object_id = {}\n        for (object_id, perm, value) in candidates:\n            if len(principals & value) > 0:\n                perms_by_object_id.setdefault(object_id, set()).add(perm)\n        return perms_by_object_id\n\n    @synchronized\n    def get_authorized_principals(self, bound_permissions):\n        principals = set()\n        for obj_id, perm in bound_permissions:\n            principals |= self.get_object_permission_principals(obj_id, perm)\n        return principals\n\n    @synchronized\n    def get_objects_permissions(self, objects_ids, permissions=None):\n        result = []\n        for object_id in objects_ids:\n            if permissions is None:\n                aces = [k for k in self._store.keys() if k.startswith(f\"permission:{object_id}:\")]\n            else:\n                aces = [f\"permission:{object_id}:{permission}\" for permission in permissions]\n            perms = {}\n            for ace in aces:\n                # Should work with 'permission:/url/id:object:create'.\n                permission = ace.split(\":\", 2)[2]\n                perms[permission] = set(self._store[ace])\n            result.append(perms)\n        return result\n\n    @synchronized\n    def replace_object_permissions(self, object_id, permissions):\n        for permission, principals in permissions.items():\n            permission_key = f\"permission:{object_id}:{permission}\"\n            if permission_key in self._store and len(principals) == 0:\n                del self._store[permission_key]\n            elif principals:\n                self._store[permission_key] = set(principals)\n        return permissions\n\n    @synchronized\n    def delete_object_permissions(self, *object_id_list):\n        to_delete = []\n        for key in self._store.keys():\n            object_id = key.split(\":\")[1]\n            for pattern in object_id_list:\n                regexp = re.compile(f\"^{pattern.replace('*', '.*')}$\")\n                if regexp.match(object_id):\n                    to_delete.append(key)\n        for k in to_delete:\n            del self._store[k]\n\n\ndef load_from_config(config):\n    return Permission()\n", "path": "kinto/core/permission/memory.py"}]}