def _flush(self, parent_relationship): """Manually INSERT generated automappings.""" if not self.auto_mappings: return with benchmark("Automapping flush"): current_user_id = login.get_current_user_id() automapping_result = db.session.execute( Automapping.__table__.insert().values( relationship_id=parent_relationship.id, source_id=parent_relationship.source_id, source_type=parent_relationship.source_type, destination_id=parent_relationship.destination_id, destination_type=parent_relationship.destination_type, modified_by_id=current_user_id, ) ) automapping_id = automapping_result.inserted_primary_key[0] self.automapping_ids.add(automapping_id) now = datetime.utcnow() # We are doing an INSERT IGNORE INTO here to mitigate a race condition # that happens when multiple simultaneous requests create the same # automapping. If a relationship object fails our unique constraint # it means that the mapping was already created by another request # and we can safely ignore it. inserter = Relationship.__table__.insert().prefix_with("IGNORE") original = self.order(Stub.from_source(parent_relationship), Stub.from_destination(parent_relationship)) db.session.execute(inserter.values([{ "id": None, "modified_by_id": current_user_id, "created_at": now, "updated_at": now, "source_id": src.id, "source_type": src.type, "destination_id": dst.id, "destination_type": dst.type, "context_id": None, "status": None, "parent_id": parent_relationship.id, "automapping_id": automapping_id, "is_external": False} for src, dst in self.auto_mappings if (src, dst) != original])) # (src, dst) is sorted self._set_audit_id_for_issues(automapping_id) cache = Cache.get_cache(create=True) if cache: # Add inserted relationships into new objects collection of the cache, # so that they will be logged within event and appropriate revisions # will be created. cache.new.update( (relationship, relationship.log_json()) for relationship in Relationship.query.filter_by( automapping_id=automapping_id, ) )
def _flush(self, parent_relationship): """Manually INSERT generated automappings.""" if not self.auto_mappings: return with benchmark("Automapping flush"): current_user_id = login.get_current_user_id() automapping_result = db.session.execute( Automapping.__table__.insert().values( relationship_id=parent_relationship.id, source_id=parent_relationship.source_id, source_type=parent_relationship.source_type, destination_id=parent_relationship.destination_id, destination_type=parent_relationship.destination_type, ) ) automapping_id = automapping_result.inserted_primary_key[0] self.automapping_ids.add(automapping_id) now = datetime.utcnow() # We are doing an INSERT IGNORE INTO here to mitigate a race condition # that happens when multiple simultaneous requests create the same # automapping. If a relationship object fails our unique constraint # it means that the mapping was already created by another request # and we can safely ignore it. inserter = Relationship.__table__.insert().prefix_with("IGNORE") original = self.order(Stub.from_source(parent_relationship), Stub.from_destination(parent_relationship)) db.session.execute(inserter.values([{ "id": None, "modified_by_id": current_user_id, "created_at": now, "updated_at": now, "source_id": src.id, "source_type": src.type, "destination_id": dst.id, "destination_type": dst.type, "context_id": None, "status": None, "parent_id": parent_relationship.id, "automapping_id": automapping_id, "is_external": False} for src, dst in self.auto_mappings if (src, dst) != original])) # (src, dst) is sorted self._set_audit_id_for_issues(automapping_id) cache = Cache.get_cache(create=True) if cache: # Add inserted relationships into new objects collection of the cache, # so that they will be logged within event and appropriate revisions # will be created. cache.new.update( (relationship, relationship.log_json()) for relationship in Relationship.query.filter_by( automapping_id=automapping_id, ) )
def generate_automappings(self, relationship): """Generate Automappings for a given relationship""" # pylint: disable=protected-access self.auto_mappings = set() with benchmark("Automapping generate_automappings"): # initial relationship is special since it is already created and # processing it would abort the loop so we manually enqueue the # neighborhood src = Stub.from_source(relationship) dst = Stub.from_destination(relationship) self._step(src, dst) self._step(dst, src) while self.queue: if len(self.auto_mappings) > self.COUNT_LIMIT: break src, dst = entry = self.queue.pop() if {src.type, dst.type} != {"Audit", "Issue"}: # Auditor doesn't have edit (+map) permission on the Audit, # but the Auditor should be allowed to Raise an Issue. # Since Issue-Assessment-Audit is the only rule that # triggers Issue to Audit mapping, we should skip the # permission check for it if not (permissions.is_allowed_update( src.type, src.id, None) and permissions.is_allowed_update( dst.type, dst.id, None)): continue created = self._ensure_relationship(src, dst) self.processed.add(entry) if not created: # If the edge already exists it means that auto mappings for it have # already been processed and it is safe to cut here. continue self._step(src, dst) self._step(dst, src) if len(self.auto_mappings) <= self.COUNT_LIMIT: self._flush(relationship) else: relationship._json_extras = { # pylint: disable=protected-access 'automapping_limit_exceeded': True }
def generate_automappings(self, relationship): """Generate Automappings for a given relationship""" self.auto_mappings = set() # initial relationship is special since it is already created and # processing it would abort the loop so we manually enqueue the # neighborhood src = Stub.from_source(relationship) dst = Stub.from_destination(relationship) self._step(src, dst) self._step(dst, src) while self.queue: if len(self.auto_mappings) > self.COUNT_LIMIT: break src, dst = entry = self.queue.pop() if {src.type, dst.type} not in self._AUTOMAP_WITHOUT_PERMISSION: # Mapping between some objects should be created even if there is no # permission to edit (+map) this objects. Thus permissions check for # them should be skipped. if not (permissions.is_allowed_update(src.type, src.id, None) and permissions.is_allowed_update( dst.type, dst.id, None)): continue created = self._ensure_relationship(src, dst) self.processed.add(entry) if not created: # If the edge already exists it means that auto mappings for it have # already been processed and it is safe to cut here. continue self._step(src, dst) self._step(dst, src) if len(self.auto_mappings) <= self.COUNT_LIMIT: if self.auto_mappings: logger.info("Automapping count: count=%s", len(self.auto_mappings)) self._flush(relationship) else: logger.error("Automapping limit exceeded: limit=%s, count=%s", self.COUNT_LIMIT, len(self.auto_mappings))
def generate_automappings(self, relationship): """Generate Automappings for a given relationship""" # pylint: disable=protected-access self.auto_mappings = set() with benchmark("Automapping generate_automappings"): # initial relationship is special since it is already created and # processing it would abort the loop so we manually enqueue the # neighborhood src = Stub.from_source(relationship) dst = Stub.from_destination(relationship) self._step(src, dst) self._step(dst, src) while self.queue: if len(self.auto_mappings) > self.COUNT_LIMIT: break src, dst = entry = self.queue.pop() if {src.type, dst.type} != {"Audit", "Issue"}: # Auditor doesn't have edit (+map) permission on the Audit, # but the Auditor should be allowed to Raise an Issue. # Since Issue-Assessment-Audit is the only rule that # triggers Issue to Audit mapping, we should skip the # permission check for it if not (permissions.is_allowed_update(src.type, src.id, None) and permissions.is_allowed_update(dst.type, dst.id, None)): continue created = self._ensure_relationship(src, dst) self.processed.add(entry) if not created: # If the edge already exists it means that auto mappings for it have # already been processed and it is safe to cut here. continue self._step(src, dst) self._step(dst, src) if len(self.auto_mappings) <= self.COUNT_LIMIT: self._flush(relationship) else: logger.error("Automapping limit exceeded: limit=%s, count=%s", self.COUNT_LIMIT, len(self.auto_mappings))