def process_entity_post(entity, profile, receiving_profile=None): """Process an entity of type Post.""" fid = safe_text(entity.id) if not validate_against_old_content(fid, entity, profile): return values = { "fid": fid, "text": safe_text_for_markdown(entity.raw_content), "author": profile, "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED, "remote_created": safe_make_aware(entity.created_at, "UTC"), "service_label": safe_text(entity.provider_display_name) or "", } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) if getattr(entity, "guid", None): values["guid"] = safe_text(entity.guid) content, created = Content.objects.fed_update_or_create(fid, values) _process_mentions(content, entity) if created: logger.info("Saved Content: %s", content) else: logger.info("Updated Content: %s", content) if content.visibility != Visibility.PUBLIC and receiving_profile: content.limited_visibilities.add(receiving_profile) logger.info("Added visibility to Post %s to %s", content.fid, receiving_profile.fid)
def process_entity_comment(entity, profile): """Process an entity of type Comment.""" guid = safe_text(entity.guid) if Content.objects.filter(guid=guid, author__user__isnull=False).exists(): logger.warning("Remote sent comment with guid %s is local! Skipping.", guid) return try: parent = Content.objects.get(guid=entity.target_guid) except Content.DoesNotExist: logger.warning("No target found for comment: %s", entity) return values = { "text": safe_text_for_markdown(entity.raw_content), "author": profile, "visibility": parent.visibility, "remote_created": safe_make_aware(entity.created_at, "UTC"), "parent": parent, } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) content, created = Content.objects.update_or_create(guid=guid, defaults=values) if created: logger.info("Saved Content from comment entity: %s", content) else: logger.info("Updated Content from comment entity: %s", content) if parent.is_local: # We should relay this to participants we know of from socialhome.federate.tasks import forward_relayable django_rq.enqueue(forward_relayable, entity, parent.id)
def process_entity_post(entity, profile, receiving_profile=None): """Process an entity of type Post.""" fid = safe_text(entity.id) if not validate_against_old_content(fid, entity, profile): return values = { "fid": fid, "text": safe_text_for_markdown(entity.raw_content), "author": profile, "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED, "remote_created": safe_make_aware(entity.created_at, "UTC"), "service_label": safe_text(entity.provider_display_name) or "", } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) if getattr(entity, "guid", None): values["guid"] = safe_text(entity.guid) content, created = Content.objects.fed_update_or_create(fid, values) _process_mentions(content, entity) if created: logger.info("Saved Content: %s", content) else: logger.info("Updated Content: %s", content) if content.visibility != Visibility.PUBLIC and receiving_profile: content.limited_visibilities.add(receiving_profile) logger.info("Added visibility to Post %s to %s", content.fid, receiving_profile.fid)
def process_entity_share(entity, profile): """Process an entity of type Share.""" if not entity.entity_type == "Post": # TODO: enable shares of replies too logger.warning("Ignoring share entity type that is not of type Post") return try: target_content = Content.objects.fed(entity.target_id, share_of__isnull=True).get() except Content.DoesNotExist: # Try fetching. If found, process and then try again remote_target = retrieve_remote_content( entity.target_id, guid=entity.target_guid, handle=entity.target_handle, entity_type=entity.entity_type, sender_key_fetcher=sender_key_fetcher, ) if remote_target: process_entities([remote_target]) try: target_content = Content.objects.fed( entity.target_id, share_of__isnull=True).get() except Content.DoesNotExist: logger.warning( "Share target was fetched from remote, but it is still missing locally! Share: %s", entity) return else: logger.warning( "No target found for share even after fetching from remote: %s", entity) return values = { "text": safe_text_for_markdown(entity.raw_content), "author": profile, # TODO: ensure visibility constraints depending on shared content? "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED, "remote_created": safe_make_aware(entity.created_at, "UTC"), "service_label": safe_text(entity.provider_display_name) or "", } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) fid = safe_text(entity.id) if getattr(entity, "guid", None): values["guid"] = safe_text(entity.guid) content, created = Content.objects.fed_update_or_create( fid, values, extra_lookups={'share_of': target_content}) _process_mentions(content, entity) if created: logger.info("Saved share: %s", content) else: logger.info("Updated share: %s", content) # TODO: send participation to the share from the author, if local # We probably want that to happen even though our shares are not separate in the stream? if target_content.local: # We should relay this share entity to participants we know of from socialhome.federate.tasks import forward_entity django_rq.enqueue(forward_entity, entity, target_content.id)
def process_entity_comment(entity, profile, receiving_profile=None): """Process an entity of type Comment.""" fid = safe_text(entity.id) if not validate_against_old_content(fid, entity, profile): return try: parent = Content.objects.fed(entity.target_id).get() except Content.DoesNotExist: logger.warning("No target found for comment: %s", entity) return values = { "text": safe_text_for_markdown(entity.raw_content), "author": profile, "visibility": parent.visibility, "remote_created": safe_make_aware(entity.created_at, "UTC"), "parent": parent, } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) if getattr(entity, "guid", None): values["guid"] = safe_text(entity.guid) content, created = Content.objects.fed_update_or_create(fid, values) _process_mentions(content, entity) if created: logger.info("Saved Content from comment entity: %s", content) else: logger.info("Updated Content from comment entity: %s", content) if parent.visibility != Visibility.PUBLIC and receiving_profile: content.limited_visibilities.add(receiving_profile) logger.info("Added visibility to Comment %s to %s", content.uuid, receiving_profile.uuid) if parent.local: # We should relay this to participants we know of from socialhome.federate.tasks import forward_entity django_rq.enqueue(forward_entity, entity, parent.id)
def process_entity_comment(entity, profile, receiving_profile=None): """Process an entity of type Comment.""" fid = safe_text(entity.id) if not validate_against_old_content(fid, entity, profile): return try: parent = Content.objects.fed(entity.target_id).get() except Content.DoesNotExist: logger.warning("No target found for comment: %s", entity) return values = { "text": safe_text_for_markdown(entity.raw_content), "author": profile, "visibility": parent.visibility, "remote_created": safe_make_aware(entity.created_at, "UTC"), "parent": parent, } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) if getattr(entity, "guid", None): values["guid"] = safe_text(entity.guid) content, created = Content.objects.fed_update_or_create(fid, values) _process_mentions(content, entity) if created: logger.info("Saved Content from comment entity: %s", content) else: logger.info("Updated Content from comment entity: %s", content) if parent.visibility != Visibility.PUBLIC and receiving_profile: content.limited_visibilities.add(receiving_profile) logger.info("Added visibility to Comment %s to %s", content.uuid, receiving_profile.uuid) if parent.local: # We should relay this to participants we know of from socialhome.federate.tasks import forward_entity django_rq.enqueue(forward_entity, entity, parent.id)
def process_entity_comment(entity: Any, profile: Profile): """Process an entity of type Comment.""" fid = safe_text(entity.id) if not validate_against_old_content(fid, entity, profile): return try: parent = Content.objects.fed(entity.target_id).get() except Content.DoesNotExist: logger.warning("No target found for comment: %s", entity) return root_parent = parent if entity.root_target_id: try: root_parent = Content.objects.fed(entity.root_target_id).get() except Content.DoesNotExist: pass values = { "text": _embed_entity_images_to_post( entity._children, safe_text_for_markdown(entity.raw_content)), "author": profile, "visibility": parent.visibility, "remote_created": safe_make_aware(entity.created_at, "UTC"), "parent": parent, "root_parent": root_parent, } if getattr(entity, "guid", None): values["guid"] = safe_text(entity.guid) content, created = Content.objects.fed_update_or_create(fid, values) _process_mentions(content, entity) if created: logger.info("Saved Content from comment entity: %s", content) else: logger.info("Updated Content from comment entity: %s", content) # TODO we should respect the visibility of the comment instead here if parent.visibility == Visibility.LIMITED: if entity._receivers: receivers = get_profiles_from_receivers(entity._receivers) if len(receivers): content.limited_visibilities.set(receivers) logger.info("Added visibility to Comment %s to %s", content.fid, receivers) else: logger.warning( "No local receivers found for limited Comment %s", content.fid) else: logger.warning("No receivers for limited Comment %s", content.fid) if parent.local: # We should relay this to participants we know of from socialhome.federate.tasks import forward_entity django_rq.enqueue(forward_entity, entity, root_parent.id)
def process_entity_share(entity, profile): """Process an entity of type Share.""" if not entity.entity_type == "Post": # TODO: enable shares of replies too logger.warning("Ignoring share entity type that is not of type Post") return try: target_content = Content.objects.fed(entity.target_id, share_of__isnull=True).get() except Content.DoesNotExist: # Try fetching. If found, process and then try again remote_target = retrieve_remote_content( entity.target_id, guid=entity.target_guid, handle=entity.target_handle, entity_type=entity.entity_type, sender_key_fetcher=sender_key_fetcher, ) if remote_target: process_entities([remote_target]) try: target_content = Content.objects.fed(entity.target_id, share_of__isnull=True).get() except Content.DoesNotExist: logger.warning("Share target was fetched from remote, but it is still missing locally! Share: %s", entity) return else: logger.warning("No target found for share even after fetching from remote: %s", entity) return values = { "text": safe_text_for_markdown(entity.raw_content), "author": profile, # TODO: ensure visibility constraints depending on shared content? "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED, "remote_created": safe_make_aware(entity.created_at, "UTC"), "service_label": safe_text(entity.provider_display_name) or "", } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) fid = safe_text(entity.id) if getattr(entity, "guid", None): values["guid"] = safe_text(entity.guid) content, created = Content.objects.fed_update_or_create(fid, values, extra_lookups={'share_of': target_content}) _process_mentions(content, entity) if created: logger.info("Saved share: %s", content) else: logger.info("Updated share: %s", content) # TODO: send participation to the share from the author, if local # We probably want that to happen even though our shares are not separate in the stream? if target_content.local: # We should relay this share entity to participants we know of from socialhome.federate.tasks import forward_entity django_rq.enqueue(forward_entity, entity, target_content.id)
def process_entity_share(entity, profile): """Process an entity of type Share.""" if not entity.entity_type == "Post": # TODO: enable shares of replies too logger.warning("Ignoring share entity type that is not of type Post") return try: target_content = Content.objects.get(guid=entity.target_guid, share_of__isnull=True) except Content.DoesNotExist: # Try fetching. If found, process and then try again remote_target = retrieve_remote_content( entity.target_id, sender_key_fetcher=sender_key_fetcher) if remote_target: process_entities([remote_target]) try: target_content = Content.objects.get(guid=entity.target_guid, share_of__isnull=True) except Content.DoesNotExist: logger.warning( "Share target was fetched from remote, but it is still missing locally! Share: %s", entity) return else: logger.warning( "No target found for share even after fetching from remote: %s", entity) return if not target_content.author.handle == entity.target_handle: logger.warning( "Share target handle is different from the author of locally known shared content!" ) return values = { "text": safe_text_for_markdown(entity.raw_content), "author": profile, # TODO: ensure visibility constraints depending on shared content? "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED, "remote_created": safe_make_aware(entity.created_at, "UTC"), "service_label": safe_text(entity.provider_display_name) or "", } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) guid = safe_text(entity.guid) content, created = Content.objects.update_or_create( guid=guid, share_of=target_content, defaults=values) if created: logger.info("Saved share: %s", content) else: logger.info("Updated share: %s", content)
def process_entity_post(entity, profile): """Process an entity of type Post.""" values = { "text": safe_text_for_markdown(entity.raw_content), "author": profile, "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED, "remote_created": safe_make_aware(entity.created_at, "UTC"), "service_label": safe_text(entity.provider_display_name) or "", } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) content, created = Content.objects.update_or_create(guid=safe_text( entity.guid), defaults=values) if created: logger.info("Saved Content: %s", content) else: logger.info("Updated Content: %s", content)
def process_entity_post(entity: Any, profile: Profile): """Process an entity of type Post.""" fid = safe_text(entity.id) if not validate_against_old_content(fid, entity, profile): return values = { "fid": fid, "text": _embed_entity_images_to_post( entity._children, safe_text_for_markdown(entity.raw_content)), "author": profile, "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED, "remote_created": safe_make_aware(entity.created_at, "UTC"), "service_label": safe_text(entity.provider_display_name) or "", } extra_lookups = {} if getattr(entity, "guid", None): values["guid"] = safe_text(entity.guid) extra_lookups["guid"] = values["guid"] content, created = Content.objects.fed_update_or_create( fid, values, extra_lookups=extra_lookups) _process_mentions(content, entity) if created: logger.info("Saved Content: %s", content) else: logger.info("Updated Content: %s", content) if content.visibility == Visibility.LIMITED: if entity._receivers: receivers = get_profiles_from_receivers(entity._receivers) if len(receivers): content.limited_visibilities.set(receivers) logger.info("Added visibility to Post %s to %s", content.fid, receivers) else: logger.warning("No local receivers found for limited Post %s", content.fid) else: logger.warning("No receivers for limited Post %s", content.fid)
def process_entity_post(entity, profile): """Process an entity of type Post.""" guid = safe_text(entity.guid) if Content.objects.filter(guid=guid, author__user__isnull=False).exists(): logger.warning("Remote sent content with guid %s is local! Skipping.", guid) return values = { "text": safe_text_for_markdown(entity.raw_content), "author": profile, "visibility": Visibility.PUBLIC if entity.public else Visibility.LIMITED, "remote_created": safe_make_aware(entity.created_at, "UTC"), "service_label": safe_text(entity.provider_display_name) or "", } values["text"] = _embed_entity_images_to_post(entity._children, values["text"]) content, created = Content.objects.update_or_create(guid=guid, defaults=values) if created: logger.info("Saved Content: %s", content) else: logger.info("Updated Content: %s", content)
def validate_text(self, value): """Sanitize text if user is untrusted.""" user = self.context.get("request").user if user.trusted_editor: return value return safe_text_for_markdown(value)
def test_text_with_quotes_survives(self): assert safe_text_for_markdown(MARKDOWN_QUOTES_TEXT) == "> foo\n> bar > foo"
def test_text_with_html_is_cleaned(self): assert safe_text_for_markdown(HTML_TEXT) == '<a href="foo">bar</a><b>cee</b><em>daaa<div>faa<' \ ';/div></em>'
def test_text_with_script_is_cleaned(self): assert safe_text_for_markdown(SCRIPT_TEXT) == "<script>console.log</script>"
def clean_text(self): """Sanitize text if user is untrusted.""" if self.user.trusted_editor: return self.cleaned_data["text"] return safe_text_for_markdown(self.cleaned_data["text"])
def test_text_with_quotes_survives(self): assert safe_text_for_markdown( MARKDOWN_QUOTES_TEXT) == "> foo\n> bar > foo"
def test_text_with_script_is_cleaned(self): assert safe_text_for_markdown( SCRIPT_TEXT) == "<script>console.log</script>"
def test_text_with_markdown_survives(self): assert safe_text_for_markdown(MARKDOWN_TEXT) == MARKDOWN_TEXT
def test_plain_text_survives(self): assert safe_text_for_markdown(PLAIN_TEXT) == PLAIN_TEXT
def clean_text(self): """Sanitize text if user is untrusted.""" if self.user.trusted_editor: return self.cleaned_data["text"] return safe_text_for_markdown(self.cleaned_data["text"])
def test_text_with_markdown_code_survives(self): assert safe_text_for_markdown(MARKDOWN_CODE_TEXT) == \ "`\n<script>alert('yup');</script>\n`\n\n`<script>alert('yup');</script>`\n\n```\n" \ "<script>alert('yap');</script>\n```"
def test_plain_text_survives(self): assert safe_text_for_markdown(PLAIN_TEXT) == PLAIN_TEXT
def test_text_with_html_is_cleaned(self): assert safe_text_for_markdown(HTML_TEXT) == '<a href="foo">bar</a><b>cee</b><em>daaa<div><span ' \ 'class="jee">faa</span></div></em>'
def test_text_with_markdown_survives(self): assert safe_text_for_markdown(MARKDOWN_TEXT) == MARKDOWN_TEXT
def test_text_with_markdown_code_survives(self): assert safe_text_for_markdown(MARKDOWN_CODE_TEXT) == \ "`\n<script>alert('yup');</script>\n`\n\n`<script>alert('yup');</script>`\n\n```\n" \ "<script>alert('yap');</script>\n```"
def test_text_with_html_is_cleaned__mention_link_classes_preserved(self): assert safe_text_for_markdown(HTML_TEXT_WITH_MENTION_LINK) == \ '<p><span class="h-card"><a class="u-url mention" href="https://dev.jasonrobinson.me/u/jaywink/"' \ '>@<span>jaywink</span></a></span> boom</p>'