def on_record_changed(self, event): bucket_id = event.payload['bucket_id'] collection_id = event.payload['collection_id'] timestamp = event.payload['timestamp'] if not is_monitoring_collection(self.registry, bucket_id, collection_id): return for change in event.impacted_records: # This might be the first we've seen of this collection. old_timestamp = self.collection_timestamps.get((bucket_id, collection_id), None) # Synthesize event for /buckets/monitor/collections/changes record. new = changes_record(event.request, bucket_id, collection_id, timestamp) old = None action = ACTIONS.CREATE if old_timestamp: action = ACTIONS.UPDATE old = changes_record(event.request, bucket_id, collection_id, old_timestamp) resource_data = {'bucket_id': 'monitor', 'collection_id': 'changes', 'id': new['id']} notify_resource_event( event.request, '/buckets/monitor/collections/changes', timestamp, new, action, old=old, resource_name='record', resource_data=resource_data ) self.collection_timestamps[(bucket_id, collection_id)] = timestamp
def listener(cls, event): cls.events.append(event) # In these resource tests, the resource parent id is the user id. parent_id = event.request.prefixed_userid # Let's create a cascade of event. When a CREATE event is received, # we send an UPDATE one. if event.payload["action"] == "create": notify_resource_event(event.request, parent_id, event.payload["timestamp"], {"foo": "42"}, ACTIONS.UPDATE)
def listener(cls, event): """Have the first "de Paris" event trigger a follow-up "de New York" event.""" cls.events.append(event) if not isinstance(event, ResourceChanged): return if len(event.impacted_records) > 1: raise ValueError("Too many events {}".format(event.impacted_records)) # An event without records is impossible. assert len(event.impacted_records) == 1 if event.impacted_records[0]['new']['name'] == 'de Paris': new_record = {'name': 'de New York'} # Trying to match the Mushroom (i.e. UserResource) parent # ID to test event grouping parent_id = event.request.prefixed_userid notify_resource_event(event.request, parent_id, event.payload['timestamp'], new_record, ACTIONS.CREATE)
def listener(cls, event): """Have the first "de Paris" event trigger a follow-up "de New York" event.""" cls.events.append(event) if not isinstance(event, ResourceChanged): return if len(event.impacted_objects) > 1: raise ValueError("Too many events {}".format( event.impacted_objects)) # An event without objects is impossible. assert len(event.impacted_objects) == 1 if event.impacted_objects[0]["new"]["name"] == "de Paris": new_object = {"name": "de New York"} # Trying to match the Mushroom parent ID to test event grouping parent_id = event.request.prefixed_userid notify_resource_event(event.request, parent_id, event.payload["timestamp"], new_object, ACTIONS.CREATE)
def listener(cls, event): """Have the first "de Paris" event trigger a follow-up "de New York" event.""" cls.events.append(event) if not isinstance(event, ResourceChanged): return if len(event.impacted_records) > 1: raise ValueError("Too many events {}".format( event.impacted_records)) # An event without records is impossible. assert len(event.impacted_records) == 1 if event.impacted_records[0]['new']['name'] == 'de Paris': new_record = {'name': 'de New York'} # Trying to match the Mushroom (i.e. UserResource) parent # ID to test event grouping parent_id = event.request.prefixed_userid notify_resource_event(event.request, parent_id, event.payload['timestamp'], new_record, ACTIONS.CREATE)
def delete(self): principal = self.request.matchdict["principal"] storage = self.request.registry.storage permission = self.request.registry.permission object_uris_and_permissions = permission.get_accessible_objects( [principal]) object_uris = list(object_uris_and_permissions.keys()) write_perm_principals = permission.get_objects_permissions( object_uris, ["write"]) to_delete = set() for object_uri, principals in zip(object_uris, write_perm_principals): principals = principals["write"] # "Ownership" isn't a real concept in Kinto, so instead we # define ownership as meaning "this user is the only one # who can write to this object". if principals == set([principal]): to_delete.add(object_uri) # Any accessible objects that won't be deleted, need to have # the user's permission removed. for object_uri, permissions in object_uris_and_permissions.items(): if object_uri in to_delete: continue for perm in permissions: permission.remove_principal_from_ace(object_uri, perm, principal) to_delete = condense_under_parents(self.request, to_delete) # Group by (parent_uri, resource of child) to make fewer # requests to storage backend. # Store the parsed object IDs, since those are what we # actually give to the storage backend. object_ids_by_parent_uri = collections.defaultdict(list) # Store also the object URIs, which we give to the permission backend. objects_by_parent_uri = collections.defaultdict(list) # We have to get the matchdict of the child here anyhow, so # keep that to generate events later. matchdicts_by_parent_uri = {} for object_uri in to_delete: parent_uri = get_parent_uri(object_uri) resource_name, matchdict = core_utils.view_lookup( self.request, object_uri) objects_by_parent_uri[(parent_uri, resource_name)].append(object_uri) object_ids_by_parent_uri[(parent_uri, resource_name)].append(matchdict["id"]) # This overwrites previous matchdicts for the parent, but # we'll only use the fields that are relevant to the # parent, which will be the same for each child. matchdicts_by_parent_uri[parent_uri] = matchdict for (parent_uri, resource_name), object_ids in object_ids_by_parent_uri.items(): # Generate the parent matchdict from an arbitrary child's matchdict. matchdict = {**matchdicts_by_parent_uri[parent_uri]} matchdict.pop("id", None) # Deletes are paginated too, so take the page size from settings. batch_size = self.request.registry.settings[ "storage_max_fetch_size"] for batch in slice_into_batches(object_ids, batch_size): batch = list(batch) filters = [Filter("id", batch, core_utils.COMPARISON.IN)] timestamp = storage.resource_timestamp(resource_name, parent_uri) records, _ = storage.get_all(resource_name=resource_name, parent_id=parent_uri, filters=filters) tombstones = storage.delete_all(resource_name=resource_name, parent_id=parent_uri, filters=filters) notify_resource_event( self.request, parent_uri, timestamp, tombstones, ACTIONS.DELETE, old=records, resource_name=resource_name, resource_data=matchdict, ) # FIXME: need to purge the above tombstones, but no # way to purge just some tombstones for just this # principal # Clear permissions from the deleted objects, for # example those of other users. permission.delete_object_permissions( *objects_by_parent_uri[(parent_uri, resource_name)]) # Remove this principal from existing users. permission.remove_principal(principal) # Remove this principal from all groups that contain it. associated_principals = permission.get_user_principals(principal) for associated_principal in associated_principals: permission.remove_user_principal(principal, associated_principal) return {"data": {"principal": principal}}