Implement activity forwarding

This commit is contained in:
Jeremy Dormitzer 2019-04-27 16:03:41 -04:00
parent e4a8e84dcc
commit cb3abbdc3c
3 changed files with 67 additions and 3 deletions

View File

@ -45,6 +45,9 @@ class ActivityPersister implements EventSubscriberInterface
public function persistActivityToInbox( InboxActivityEvent $event ) public function persistActivityToInbox( InboxActivityEvent $event )
{ {
$activity = $event->getActivity(); $activity = $event->getActivity();
if ( ! $this->objectsService->getObject( $activity['id'] ) ) {
$event->getRequest()->attributes->set( 'firstTimeSeen', true );
}
$receivingActor = $event->getReceivingActor(); $receivingActor = $event->getReceivingActor();
if ( $receivingActor->hasField( 'inbox' ) ) { if ( $receivingActor->hasField( 'inbox' ) ) {
$this->collectionsService->addItem( $receivingActor['inbox'], $activity ); $this->collectionsService->addItem( $receivingActor['inbox'], $activity );

View File

@ -7,6 +7,7 @@ use ActivityPub\Entities\ActivityPubObject;
use ActivityPub\Objects\CollectionIterator; use ActivityPub\Objects\CollectionIterator;
use ActivityPub\Objects\ObjectsService; use ActivityPub\Objects\ObjectsService;
use ActivityPub\Utils\DateTimeProvider; use ActivityPub\Utils\DateTimeProvider;
use ActivityPub\Utils\Util;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Request;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -62,7 +63,52 @@ class DeliveryHandler implements EventSubscriberInterface
public function handleInboxForwarding( InboxActivityEvent $event ) public function handleInboxForwarding( InboxActivityEvent $event )
{ {
// Forward the activity if:
// - this is the first time we've seen the activity
// - AND the values of to, cc, or audience contain a Collection that we own
// - AND (according to Kaninii) if the 'object' of the activity is NOT an actor
// - AND the values of inReplyTo, object, target, or tag are objects that we own, recursing through
// the objects in these value chains up to some reasonable limit
$activity = $event->getActivity();
if ( ! $event->getRequest()->attributes->get( 'firstTimeSeen' ) ) {
$this->logger->debug(
'Not forwarding activity because we\'ve seen it before', array( 'activity' => $activity )
);
return;
}
if ( array_key_exists( 'object', $activity ) && $this->isActor( $activity['object'] ) ) {
$this->logger->debug(
'Not forwarding activity with an actor as its object', array( 'activity' => $activity )
);
return;
}
$forwardingTargets = array();
$recipients = array_intersect( $activity, array_flip( array( 'to', 'cc', 'audience' ) ) );
foreach ( $recipients as $recip ) {
$recipId = $recip;
if ( is_array( $recipId ) && array_key_exists( 'id', $recipId ) ) {
$recipId = $recipId['id'];
}
if ( is_string( $recipId ) ) {
$recipient = $this->objectsService->dereference( $recipId );
if (
$recipient->hasField( 'type' ) &&
in_array( $recipient['type'], array( 'Collection', 'OrderedCollection') ) &&
Util::isLocalUri( $recipient['id'] )
) {
$forwardingTargets = array_unique( array_merge(
$forwardingTargets, $this->resolveRecipient( $recipient )
) );
}
}
}
if ( count( $forwardingTargets ) === 0 ) {
$this->logger->debug(
'No collections we own in recipients, not forwarding', array( 'activity' => $activity )
);
return;
}
// TODO recurse through inReplyTo, object, target, and tags looking for object we own
} }
public function deliverActivity( OutboxActivityEvent $event ) public function deliverActivity( OutboxActivityEvent $event )
@ -169,4 +215,19 @@ class DeliveryHandler implements EventSubscriberInterface
} }
return array(); return array();
} }
private function isActor( $objectId )
{
if ( is_array( $objectId ) && array_key_exists( 'id', $objectId ) ) {
$objectId = $objectId['id'];
}
if ( ! is_string( $objectId ) ) {
return false;
}
$object = $this->objectsService->dereference( $objectId );
if ( ! $object ) {
return false;
}
return $object->hasField( 'inbox' ) && $object->hasField( 'outbox' );
}
} }

View File

@ -68,13 +68,13 @@ class ObjectsService
/** /**
* Gets an object from the DB by its ActivityPub id * Gets an object from the DB by its ActivityPub id
* *
* For internal use only - external callers should use dereference() * You probably want dereference() instead of this.
* *
* @param string $id The object's id * @param string $id The object's id
* @return ActivityPubObject|null The object or null * @return ActivityPubObject|null The object or null
* if no object exists with that id * if no object exists with that id
*/ */
protected function getObject( $id ) public function getObject( $id )
{ {
$results = $this->query( array( 'id' => $id ) ); $results = $this->query( array( 'id' => $id ) );
if ( !empty( $results ) ) { if ( !empty( $results ) ) {