Reformat all code
This commit is contained in:
parent
9aab13701c
commit
510df450e9
6114
composer.lock
generated
6114
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
use ActivityPub\Objects\CollectionsService;
|
||||
@ -16,14 +17,6 @@ class AcceptHandler implements EventSubscriberInterface
|
||||
* @var CollectionsService
|
||||
*/
|
||||
private $collectionsService;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
InboxActivityEvent::NAME => 'handleInbox',
|
||||
OutboxActivityEvent::NAME => 'handleOutbox',
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct( ObjectsService $objectsService,
|
||||
CollectionsService $collectionsService )
|
||||
@ -32,6 +25,14 @@ class AcceptHandler implements EventSubscriberInterface
|
||||
$this->collectionsService = $collectionsService;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
InboxActivityEvent::NAME => 'handleInbox',
|
||||
OutboxActivityEvent::NAME => 'handleOutbox',
|
||||
);
|
||||
}
|
||||
|
||||
public function handleInbox( InboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
@ -54,17 +55,17 @@ class AcceptHandler implements EventSubscriberInterface
|
||||
// or there isn't, in which case this is an ordinary Accept
|
||||
// sent by a client and the Follow is in the database
|
||||
$follow = $request->attributes->get( 'follow' );
|
||||
if ( ! $follow ) {
|
||||
if ( !$follow ) {
|
||||
$followId = $activity['object'];
|
||||
if ( is_array( $followId ) && array_key_exists( 'id', $followId ) ) {
|
||||
$followId = $followId['id'];
|
||||
}
|
||||
if ( ! is_string( $followId ) ) {
|
||||
if ( !is_string( $followId ) ) {
|
||||
return;
|
||||
}
|
||||
$follow = $this->objectsService->dereference( $followId )->asArray( -1 );
|
||||
}
|
||||
if ( ! $follow || ! array_key_exists( 'object', $follow ) ) {
|
||||
if ( !$follow || !array_key_exists( 'object', $follow ) ) {
|
||||
return;
|
||||
}
|
||||
$followObjectId = $follow['object'];
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
@ -37,7 +38,7 @@ class ActivityEvent extends Event
|
||||
protected $response;
|
||||
|
||||
public function __construct( array $activity, ActivityPubObject $actor,
|
||||
Request $request )
|
||||
Request $request )
|
||||
{
|
||||
$this->activity = $activity;
|
||||
$this->actor = $actor;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
use ActivityPub\Objects\CollectionsService;
|
||||
@ -23,14 +24,6 @@ class CreateHandler implements EventSubscriberInterface
|
||||
*/
|
||||
private $collectionsService;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
InboxActivityEvent::NAME => 'handleInbox',
|
||||
OutboxActivityEvent::NAME => 'handleOutbox',
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct( ObjectsService $objectsService,
|
||||
IdProvider $idProvider,
|
||||
CollectionsService $collectionsService )
|
||||
@ -39,6 +32,15 @@ class CreateHandler implements EventSubscriberInterface
|
||||
$this->idProvider = $idProvider;
|
||||
$this->collectionsService = $collectionsService;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
InboxActivityEvent::NAME => 'handleInbox',
|
||||
OutboxActivityEvent::NAME => 'handleOutbox',
|
||||
);
|
||||
}
|
||||
|
||||
public function handleInbox( InboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
@ -61,7 +63,7 @@ class CreateHandler implements EventSubscriberInterface
|
||||
return;
|
||||
}
|
||||
$object = $activity['object'];
|
||||
if ( ! array_key_exists( 'id', $object ) ) {
|
||||
if ( !array_key_exists( 'id', $object ) ) {
|
||||
$object['id'] = $this->idProvider->getId(
|
||||
$event->getRequest(),
|
||||
strtolower( $object['type'] )
|
||||
@ -94,31 +96,31 @@ class CreateHandler implements EventSubscriberInterface
|
||||
|
||||
private function copyFields( array $fields, array $sourceObj, array $targetObj )
|
||||
{
|
||||
foreach( $fields as $field ) {
|
||||
if ( ! array_key_exists( $field, $sourceObj ) ) {
|
||||
foreach ( $fields as $field ) {
|
||||
if ( !array_key_exists( $field, $sourceObj ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( array_key_exists( $field, $targetObj ) &&
|
||||
$sourceObj[$field] === $targetObj[$field] ) {
|
||||
$sourceObj[$field] === $targetObj[$field] ) {
|
||||
continue;
|
||||
} else if ( ! array_key_exists( $field, $targetObj ) ) {
|
||||
} else if ( !array_key_exists( $field, $targetObj ) ) {
|
||||
$targetObj[$field] = $sourceObj[$field];
|
||||
} else if ( is_array( $sourceObj[$field] ) &&
|
||||
is_array( $targetObj[$field] ) ) {
|
||||
is_array( $targetObj[$field] ) ) {
|
||||
$targetObj[$field] = array_unique(
|
||||
array_merge( $sourceObj[$field], $targetObj[$field] )
|
||||
);
|
||||
} else if ( is_array( $sourceObj[$field] ) &&
|
||||
! is_array( $targetObj[$field] ) ) {
|
||||
!is_array( $targetObj[$field] ) ) {
|
||||
$targetObj[$field] = array( $targetObj[$field] );
|
||||
$targetObj[$field] = array_unique(
|
||||
array_merge( $sourceObj[$field], $targetObj[$field] )
|
||||
);
|
||||
} else if ( ! is_array( $sourceObj[$field] ) &&
|
||||
is_array( $targetObj[$field] ) ) {
|
||||
} else if ( !is_array( $sourceObj[$field] ) &&
|
||||
is_array( $targetObj[$field] ) ) {
|
||||
$targetObj[$field][] = $sourceObj[$field];
|
||||
} else if ( ! is_array( $sourceObj[$field] ) &&
|
||||
! is_array( $targetObj[$field] ) ) {
|
||||
} else if ( !is_array( $sourceObj[$field] ) &&
|
||||
!is_array( $targetObj[$field] ) ) {
|
||||
$targetObj[$field] = array( $targetObj[$field] );
|
||||
$targetObj[$field][] = $sourceObj[$field];
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
@ -21,6 +22,13 @@ class DeleteHandler implements EventSubscriberInterface
|
||||
*/
|
||||
private $objectsService;
|
||||
|
||||
public function __construct( DateTimeProvider $dateTimeProvider,
|
||||
ObjectsService $objectsService )
|
||||
{
|
||||
$this->dateTimeProvider = $dateTimeProvider;
|
||||
$this->objectsService = $objectsService;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
@ -29,13 +37,6 @@ class DeleteHandler implements EventSubscriberInterface
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct( DateTimeProvider $dateTimeProvider,
|
||||
ObjectsService $objectsService )
|
||||
{
|
||||
$this->dateTimeProvider = $dateTimeProvider;
|
||||
$this->objectsService = $objectsService;
|
||||
}
|
||||
|
||||
public function handleDelete( ActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
@ -43,14 +44,14 @@ class DeleteHandler implements EventSubscriberInterface
|
||||
return;
|
||||
}
|
||||
$objectId = $activity['object'];
|
||||
if ( ! is_string( $objectId ) ) {
|
||||
if ( !is_string( $objectId ) ) {
|
||||
if ( is_array( $objectId ) && array_key_exists( 'id', $objectId ) ) {
|
||||
$objectId = $objectId['id'];
|
||||
} else {
|
||||
throw new BadRequestHttpException( 'Object must have an "id" field' );
|
||||
}
|
||||
}
|
||||
if ( ! $this->authorized( $event->getRequest(), $objectId ) ) {
|
||||
if ( !$this->authorized( $event->getRequest(), $objectId ) ) {
|
||||
throw new UnauthorizedHttpException(
|
||||
'Signature realm="ActivityPub",headers="(request-target) host date"'
|
||||
);
|
||||
@ -68,27 +69,27 @@ class DeleteHandler implements EventSubscriberInterface
|
||||
$this->objectsService->replace( $objectId, $tombstone );
|
||||
}
|
||||
|
||||
public function authorized( Request $request, $objectId )
|
||||
{
|
||||
if ( !$request->attributes->has( 'actor' ) ) {
|
||||
return false;
|
||||
}
|
||||
$requestActor = $request->attributes->get( 'actor' );
|
||||
$object = $this->objectsService->dereference( $objectId );
|
||||
if ( !$object || !$object->hasField( 'attributedTo' ) ) {
|
||||
return false;
|
||||
}
|
||||
$attributedActorId = $object['attributedTo'];
|
||||
if ( !is_string( $attributedActorId ) ) {
|
||||
$attributedActorId = $attributedActorId['id'];
|
||||
}
|
||||
return $requestActor['id'] === $attributedActorId;
|
||||
}
|
||||
|
||||
private function getNowTimestamp()
|
||||
{
|
||||
return $this->dateTimeProvider->getTime( 'activities.delete' )
|
||||
->format( DateTime::ISO8601 );
|
||||
}
|
||||
|
||||
public function authorized( Request $request, $objectId )
|
||||
{
|
||||
if ( ! $request->attributes->has( 'actor' ) ) {
|
||||
return false;
|
||||
}
|
||||
$requestActor = $request->attributes->get( 'actor' );
|
||||
$object = $this->objectsService->dereference( $objectId );
|
||||
if ( ! $object || ! $object->hasField( 'attributedTo' ) ) {
|
||||
return false;
|
||||
}
|
||||
$attributedActorId = $object['attributedTo'];
|
||||
if ( ! is_string( $attributedActorId ) ) {
|
||||
$attributedActorId = $attributedActorId['id'];
|
||||
}
|
||||
return $requestActor['id'] === $attributedActorId;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
@ -18,13 +19,6 @@ class FollowHandler implements EventSubscriberInterface
|
||||
*/
|
||||
private $contextProvider;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
InboxActivityEvent::NAME => 'handleInbox',
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct( $autoAccepts,
|
||||
ContextProvider $contextProvider )
|
||||
{
|
||||
@ -32,14 +26,20 @@ class FollowHandler implements EventSubscriberInterface
|
||||
$this->contextProvider = $contextProvider;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
InboxActivityEvent::NAME => 'handleInbox',
|
||||
);
|
||||
}
|
||||
|
||||
public function handleInbox(InboxActivityEvent $event,
|
||||
/** @noinspection PhpUnusedParameterInspection */
|
||||
$eventName,
|
||||
EventDispatcher $eventDispatcher )
|
||||
public function handleInbox( InboxActivityEvent $event,
|
||||
/** @noinspection PhpUnusedParameterInspection */
|
||||
$eventName,
|
||||
EventDispatcher $eventDispatcher )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
if ( ! $activity['type'] === 'Follow' ) {
|
||||
if ( !$activity['type'] === 'Follow' ) {
|
||||
return;
|
||||
}
|
||||
if ( $this->autoAccepts ) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
class InboxActivityEvent extends ActivityEvent
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
@ -16,19 +17,11 @@ class NonActivityHandler implements EventSubscriberInterface
|
||||
*/
|
||||
private $contextProvider;
|
||||
|
||||
public static function activityTypes()
|
||||
public function __construct( ContextProvider $contextProvider )
|
||||
{
|
||||
return array(
|
||||
'Accept', 'Add', 'Announce', 'Arrive',
|
||||
'Block', 'Create', 'Delete', 'Dislike',
|
||||
'Flag', 'Follow', 'Ignore', 'Invite',
|
||||
'Join', 'Leave', 'Like', 'Listen',
|
||||
'Move', 'Offer', 'Question', 'Reject',
|
||||
'Read', 'Remove', 'TentativeReject', 'TentativeAccept',
|
||||
'Travel', 'Undo', 'Update', 'View',
|
||||
);
|
||||
$this->contextProvider = $contextProvider;
|
||||
}
|
||||
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
@ -36,11 +29,6 @@ class NonActivityHandler implements EventSubscriberInterface
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct( ContextProvider $contextProvider )
|
||||
{
|
||||
$this->contextProvider = $contextProvider;
|
||||
}
|
||||
|
||||
public function handle( OutboxActivityEvent $event )
|
||||
{
|
||||
$object = $event->getActivity();
|
||||
@ -52,6 +40,19 @@ class NonActivityHandler implements EventSubscriberInterface
|
||||
$event->setActivity( $create );
|
||||
}
|
||||
|
||||
public static function activityTypes()
|
||||
{
|
||||
return array(
|
||||
'Accept', 'Add', 'Announce', 'Arrive',
|
||||
'Block', 'Create', 'Delete', 'Dislike',
|
||||
'Flag', 'Follow', 'Ignore', 'Invite',
|
||||
'Join', 'Leave', 'Like', 'Listen',
|
||||
'Move', 'Offer', 'Question', 'Reject',
|
||||
'Read', 'Remove', 'TentativeReject', 'TentativeAccept',
|
||||
'Travel', 'Undo', 'Update', 'View',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a new Create activity with $object as the object
|
||||
*
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
class OutboxActivityEvent extends ActivityEvent
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
@ -14,6 +15,11 @@ class UpdateHandler implements EventSubscriberInterface
|
||||
*/
|
||||
private $objectsService;
|
||||
|
||||
public function __construct( ObjectsService $objectsService )
|
||||
{
|
||||
$this->objectsService = $objectsService;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
@ -22,11 +28,6 @@ class UpdateHandler implements EventSubscriberInterface
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct( ObjectsService $objectsService )
|
||||
{
|
||||
$this->objectsService = $objectsService;
|
||||
}
|
||||
|
||||
public function handleInbox( InboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
@ -34,10 +35,10 @@ class UpdateHandler implements EventSubscriberInterface
|
||||
return;
|
||||
}
|
||||
$object = $activity['object'];
|
||||
if ( ! array_key_exists( 'id', $object ) ) {
|
||||
if ( !array_key_exists( 'id', $object ) ) {
|
||||
throw new BadRequestHttpException( 'Update object has no "id" field' );
|
||||
}
|
||||
if ( ! $this->authorized( $event->getRequest(), $object ) ) {
|
||||
if ( !$this->authorized( $event->getRequest(), $object ) ) {
|
||||
throw new UnauthorizedHttpException(
|
||||
'Signature realm="ActivityPub",headers="(request-target) host date"'
|
||||
);
|
||||
@ -45,26 +46,6 @@ class UpdateHandler implements EventSubscriberInterface
|
||||
$this->objectsService->replace( $object['id'], $object );
|
||||
}
|
||||
|
||||
public function handleOutbox( OutboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
if ( $activity['type'] !== 'Update' ) {
|
||||
return;
|
||||
}
|
||||
$updateFields = $activity['object'];
|
||||
if ( ! array_key_exists( 'id', $updateFields ) ) {
|
||||
throw new BadRequestHttpException( 'Update object has no "id" field' );
|
||||
}
|
||||
if ( ! $this->authorized( $event->getRequest(), $updateFields ) ) {
|
||||
throw new UnauthorizedHttpException(
|
||||
'Signature realm="ActivityPub",headers="(request-target) host date"'
|
||||
);
|
||||
}
|
||||
$updated = $this->objectsService->update( $updateFields['id'], $updateFields );
|
||||
$activity['object'] = $updated->asArray();
|
||||
$event->setActivity( $activity );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if $request is authorized to update $object
|
||||
*
|
||||
@ -74,25 +55,45 @@ class UpdateHandler implements EventSubscriberInterface
|
||||
*/
|
||||
private function authorized( Request $request, array $object )
|
||||
{
|
||||
if ( ! $request->attributes->has( 'actor' ) ) {
|
||||
if ( !$request->attributes->has( 'actor' ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( ! array_key_exists( 'id', $object ) ) {
|
||||
if ( !array_key_exists( 'id', $object ) ) {
|
||||
return false;
|
||||
}
|
||||
$object = $this->objectsService->dereference( $object['id'] );
|
||||
if ( ! $object->hasField( 'attributedTo' ) ) {
|
||||
if ( !$object->hasField( 'attributedTo' ) ) {
|
||||
return false;
|
||||
}
|
||||
$attributedActorId = $object['attributedTo'];
|
||||
if ( is_array( $attributedActorId ) &&
|
||||
array_key_exists( 'id', $attributedActorId ) ) {
|
||||
array_key_exists( 'id', $attributedActorId ) ) {
|
||||
$attributedActorId = $attributedActorId['id'];
|
||||
}
|
||||
if ( ! is_string( $attributedActorId ) ) {
|
||||
if ( !is_string( $attributedActorId ) ) {
|
||||
return false;
|
||||
}
|
||||
$requestActor = $request->attributes->get( 'actor' );
|
||||
return $requestActor['id'] === $attributedActorId;
|
||||
}
|
||||
|
||||
public function handleOutbox( OutboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
if ( $activity['type'] !== 'Update' ) {
|
||||
return;
|
||||
}
|
||||
$updateFields = $activity['object'];
|
||||
if ( !array_key_exists( 'id', $updateFields ) ) {
|
||||
throw new BadRequestHttpException( 'Update object has no "id" field' );
|
||||
}
|
||||
if ( !$this->authorized( $event->getRequest(), $updateFields ) ) {
|
||||
throw new UnauthorizedHttpException(
|
||||
'Signature realm="ActivityPub",headers="(request-target) host date"'
|
||||
);
|
||||
}
|
||||
$updated = $this->objectsService->update( $updateFields['id'], $updateFields );
|
||||
$activity['object'] = $updated->asArray();
|
||||
$event->setActivity( $activity );
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Activities;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
@ -6,6 +7,29 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
class ValidationHandler implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
InboxActivityEvent::NAME => 'verifyInboxActivity',
|
||||
OutboxActivityEvent::NAME => 'verifyOutboxActivity',
|
||||
);
|
||||
}
|
||||
|
||||
public function verifyInboxActivity( InboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
$requiredFields = array( 'type', 'id', 'actor' );
|
||||
if ( array_key_exists( 'type', $activity ) &&
|
||||
in_array( $activity['type'], self::getObjectRequiredTypes() ) ) {
|
||||
$requiredFields[] = 'object';
|
||||
}
|
||||
if ( array_key_exists( 'type', $activity ) &&
|
||||
in_array( $activity['type'], self::getTargetRequiredTypes() ) ) {
|
||||
$requiredFields[] = 'target';
|
||||
}
|
||||
$this->requireFields( $activity, $requiredFields );
|
||||
}
|
||||
|
||||
public static function getObjectRequiredTypes()
|
||||
{
|
||||
return array(
|
||||
@ -21,49 +45,11 @@ class ValidationHandler implements EventSubscriberInterface
|
||||
);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
InboxActivityEvent::NAME => 'verifyInboxActivity',
|
||||
OutboxActivityEvent::NAME => 'verifyOutboxActivity',
|
||||
);
|
||||
}
|
||||
|
||||
public function verifyInboxActivity( InboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
$requiredFields = array( 'type', 'id', 'actor' );
|
||||
if ( array_key_exists( 'type', $activity ) &&
|
||||
in_array( $activity['type'], self::getObjectRequiredTypes() ) ) {
|
||||
$requiredFields[] = 'object';
|
||||
}
|
||||
if ( array_key_exists( 'type', $activity ) &&
|
||||
in_array( $activity['type'], self::getTargetRequiredTypes() ) ) {
|
||||
$requiredFields[] = 'target';
|
||||
}
|
||||
$this->requireFields( $activity, $requiredFields );
|
||||
}
|
||||
|
||||
public function verifyOutboxActivity( OutboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
$requiredFields = array( 'type', 'actor' );
|
||||
if ( array_key_exists( 'type', $activity ) &&
|
||||
in_array( $activity['type'], self::getObjectRequiredTypes() ) ) {
|
||||
$requiredFields[] = 'object';
|
||||
}
|
||||
if ( array_key_exists( 'type', $activity ) &&
|
||||
in_array( $activity['type'], self::getTargetRequiredTypes() ) ) {
|
||||
$requiredFields[] = 'target';
|
||||
}
|
||||
$this->requireFields( $activity, $requiredFields );
|
||||
}
|
||||
|
||||
private function requireFields( array $activity, array $fields )
|
||||
{
|
||||
$missing = array();
|
||||
foreach ( $fields as $field ) {
|
||||
if ( ! array_key_exists( $field, $activity ) ) {
|
||||
if ( !array_key_exists( $field, $activity ) ) {
|
||||
$missing[] = $field;
|
||||
}
|
||||
}
|
||||
@ -73,5 +59,20 @@ class ValidationHandler implements EventSubscriberInterface
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function verifyOutboxActivity( OutboxActivityEvent $event )
|
||||
{
|
||||
$activity = $event->getActivity();
|
||||
$requiredFields = array( 'type', 'actor' );
|
||||
if ( array_key_exists( 'type', $activity ) &&
|
||||
in_array( $activity['type'], self::getObjectRequiredTypes() ) ) {
|
||||
$requiredFields[] = 'object';
|
||||
}
|
||||
if ( array_key_exists( 'type', $activity ) &&
|
||||
in_array( $activity['type'], self::getTargetRequiredTypes() ) ) {
|
||||
$requiredFields[] = 'target';
|
||||
}
|
||||
$this->requireFields( $activity, $requiredFields );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ class ActivityPub
|
||||
*/
|
||||
public function __construct( ActivityPubConfig $config )
|
||||
{
|
||||
$this->module = new ActivityPubModule( $config);
|
||||
$this->module = new ActivityPubModule( $config );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +52,7 @@ class ActivityPub
|
||||
*/
|
||||
public function handle( $request = null )
|
||||
{
|
||||
if ( ! $request ) {
|
||||
if ( !$request ) {
|
||||
$request = Request::createFromGlobals();
|
||||
}
|
||||
|
||||
@ -73,21 +73,6 @@ class ActivityPub
|
||||
return $kernel->handle( $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database tables necessary for the library to function,
|
||||
* if they have not already been created.
|
||||
*
|
||||
* For best performance, this should only get called once in an application
|
||||
* (for example, when other database migrations get run).
|
||||
*/
|
||||
public function updateSchema()
|
||||
{
|
||||
$entityManager = $this->module->get( EntityManager::class );
|
||||
$schemaTool = new SchemaTool( $entityManager );
|
||||
$classes = $entityManager->getMetadataFactory()->getAllMetadata();
|
||||
$schemaTool->updateSchema( $classes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the activity handling pipeline
|
||||
*
|
||||
@ -102,5 +87,20 @@ class ActivityPub
|
||||
$dispatcher->addSubscriber( $this->module->get( UpdateHandler::class ) );
|
||||
$dispatcher->addSubscriber( $this->module->get( DeleteHandler::class ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database tables necessary for the library to function,
|
||||
* if they have not already been created.
|
||||
*
|
||||
* For best performance, this should only get called once in an application
|
||||
* (for example, when other database migrations get run).
|
||||
*/
|
||||
public function updateSchema()
|
||||
{
|
||||
$entityManager = $this->module->get( EntityManager::class );
|
||||
$schemaTool = new SchemaTool( $entityManager );
|
||||
$classes = $entityManager->getMetadataFactory()->getAllMetadata();
|
||||
$schemaTool->updateSchema( $classes );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
namespace ActivityPub\Auth;
|
||||
|
||||
use Exception;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use Exception;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* The AuthListener class answers the question, "is this request authorized
|
||||
* The AuthListener class answers the question, "is this request authorized
|
||||
* to act on behalf of this Actor?"
|
||||
*
|
||||
* It delegates most of the work to a passed-in Callable to allow library clients to
|
||||
@ -30,13 +30,6 @@ class AuthListener implements EventSubscriberInterface
|
||||
*/
|
||||
private $objectsService;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::REQUEST => 'checkAuth'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new AuthenticationService
|
||||
*
|
||||
@ -49,6 +42,13 @@ class AuthListener implements EventSubscriberInterface
|
||||
$this->objectsService = $objectsService;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::REQUEST => 'checkAuth'
|
||||
);
|
||||
}
|
||||
|
||||
public function checkAuth( GetResponseEvent $event )
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
@ -56,9 +56,9 @@ class AuthListener implements EventSubscriberInterface
|
||||
return;
|
||||
}
|
||||
$actorId = call_user_func( $this->authFunction );
|
||||
if ( $actorId && ! empty( $actorId ) ) {
|
||||
if ( $actorId && !empty( $actorId ) ) {
|
||||
$actor = $this->objectsService->dereference( $actorId );
|
||||
if ( ! $actor ) {
|
||||
if ( !$actor ) {
|
||||
throw new Exception( "Actor $actorId does not exist" );
|
||||
}
|
||||
$request->attributes->set( 'actor', $actor );
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Auth;
|
||||
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
@ -9,7 +10,7 @@ class AuthService
|
||||
public function isAuthorized( Request $request,
|
||||
ActivityPubObject $object )
|
||||
{
|
||||
if ( ! $this->hasAudience( $object ) ) {
|
||||
if ( !$this->hasAudience( $object ) ) {
|
||||
return true;
|
||||
}
|
||||
$audience = $this->getAudience( $object );
|
||||
@ -43,8 +44,8 @@ class AuthService
|
||||
// TODO do I need to traverse the inReplyTo chain here?
|
||||
$objectArr = $object->asArray( 0 );
|
||||
$audience = array();
|
||||
foreach( array( 'to', 'bto', 'cc', 'bcc', 'audience', 'attributedTo', 'actor' )
|
||||
as $attribute ) {
|
||||
foreach ( array( 'to', 'bto', 'cc', 'bcc', 'audience', 'attributedTo', 'actor' )
|
||||
as $attribute ) {
|
||||
$audience = $this->checkAudienceAttribute( $audience, $attribute, $objectArr );
|
||||
}
|
||||
return $audience;
|
||||
@ -54,7 +55,7 @@ class AuthService
|
||||
{
|
||||
if ( array_key_exists( $attribute, $objectArr ) ) {
|
||||
$audienceValue = $objectArr[$attribute];
|
||||
if ( ! is_array( $audienceValue ) ) {
|
||||
if ( !is_array( $audienceValue ) ) {
|
||||
$audienceValue = array( $audienceValue );
|
||||
}
|
||||
return array_merge( $audience, $audienceValue );
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Auth;
|
||||
|
||||
use ActivityPub\Crypto\HttpSignatureService;
|
||||
@ -24,13 +25,6 @@ class SignatureListener implements EventSubscriberInterface
|
||||
*/
|
||||
private $objectsService;
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::REQUEST => 'validateHttpSignature'
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct( HttpSignatureService $httpSignatureService,
|
||||
ObjectsService $objectsService )
|
||||
{
|
||||
@ -38,6 +32,13 @@ class SignatureListener implements EventSubscriberInterface
|
||||
$this->objectsService = $objectsService;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::REQUEST => 'validateHttpSignature'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a valid HTTP signature on the request. If the request has a valid
|
||||
* signature, set the 'signed' and 'signedBy' keys on the request ('signedBy' is
|
||||
@ -52,33 +53,33 @@ class SignatureListener implements EventSubscriberInterface
|
||||
if ( $headers->has( 'signature' ) ) {
|
||||
$signatureHeader = $headers->get( 'signature' );
|
||||
} else if ( $headers->has( 'authorization' ) &&
|
||||
substr( $headers->get( 'authorization' ), 0, 9 ) === 'Signature' ) {
|
||||
substr( $headers->get( 'authorization' ), 0, 9 ) === 'Signature' ) {
|
||||
$signatureHeader = substr( $headers->get( 'authorization' ), 10 );
|
||||
}
|
||||
if ( ! $signatureHeader ) {
|
||||
if ( !$signatureHeader ) {
|
||||
return;
|
||||
}
|
||||
$matches = array();
|
||||
if ( ! preg_match( '/keyId="([^"]*)"/', $signatureHeader, $matches) ) {
|
||||
if ( !preg_match( '/keyId="([^"]*)"/', $signatureHeader, $matches ) ) {
|
||||
return;
|
||||
}
|
||||
$keyId = $matches[1];
|
||||
$key = $this->objectsService->dereference( $keyId );
|
||||
if ( ! $key || ! $key->hasField( 'owner' ) || ! $key->hasField( 'publicKeyPem' ) ) {
|
||||
if ( !$key || !$key->hasField( 'owner' ) || !$key->hasField( 'publicKeyPem' ) ) {
|
||||
return;
|
||||
}
|
||||
$owner = $key['owner'];
|
||||
if ( is_string( $owner ) ) {
|
||||
$owner = $this->objectsService->dereference( $owner );
|
||||
}
|
||||
if ( ! $owner ) {
|
||||
if ( !$owner ) {
|
||||
return;
|
||||
}
|
||||
if ( ! $this->httpSignatureService->verify( $request, $key['publicKeyPem'] ) ) {
|
||||
if ( !$this->httpSignatureService->verify( $request, $key['publicKeyPem'] ) ) {
|
||||
return;
|
||||
}
|
||||
$request->attributes->set( 'signed', true );
|
||||
if ( ! $request->attributes->has( 'actor' ) ) {
|
||||
if ( !$request->attributes->has( 'actor' ) ) {
|
||||
$request->attributes->set( 'actor', $owner );
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Config;
|
||||
|
||||
/**
|
||||
@ -37,7 +38,7 @@ class ActivityPubConfig
|
||||
private $idPathPrefix;
|
||||
|
||||
/**
|
||||
* Don't call this directly - instead, use
|
||||
* Don't call this directly - instead, use
|
||||
* ActivityPubConfig->createBuilder()->build()
|
||||
*
|
||||
* @param ActivityPubConfigBuilder $builder
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Config;
|
||||
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
@ -61,7 +62,7 @@ class ActivityPubConfigBuilder
|
||||
{
|
||||
$this->isDevMode = false;
|
||||
$this->dbPrefix = '';
|
||||
$this->authFunction = function() {
|
||||
$this->authFunction = function () {
|
||||
return false;
|
||||
};
|
||||
$this->jsonLdContext = ContextProvider::getDefaultContext();
|
||||
@ -85,11 +86,21 @@ class ActivityPubConfigBuilder
|
||||
*/
|
||||
private function validate()
|
||||
{
|
||||
if ( ! $this->dbConnectionParams ) {
|
||||
if ( !$this->dbConnectionParams ) {
|
||||
throw new Exception( "Missing required option 'dbConnectionParams'" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
*
|
||||
*/
|
||||
public function getDbConnectionParams()
|
||||
{
|
||||
return $this->dbConnectionParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `dbConnectionParams` are the Doctrine connection parameters,
|
||||
* passed directly through to EntityManager::create(). See
|
||||
@ -106,13 +117,13 @@ class ActivityPubConfigBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return bool
|
||||
*
|
||||
*
|
||||
*/
|
||||
public function getDbConnectionParams()
|
||||
public function getIsDevMode()
|
||||
{
|
||||
return $this->dbConnectionParams;
|
||||
return $this->isDevMode;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,13 +141,11 @@ class ActivityPubConfigBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIsDevMode()
|
||||
public function getDbPrefix()
|
||||
{
|
||||
return $this->isDevMode;
|
||||
return $this->dbPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,11 +165,11 @@ class ActivityPubConfigBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return Callable
|
||||
*/
|
||||
public function getDbPrefix()
|
||||
public function getAuthFunction()
|
||||
{
|
||||
return $this->dbPrefix;
|
||||
return $this->authFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,11 +191,11 @@ class ActivityPubConfigBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Callable
|
||||
* @return array
|
||||
*/
|
||||
public function getAuthFunction()
|
||||
public function getJsonLdContext()
|
||||
{
|
||||
return $this->authFunction;
|
||||
return $this->jsonLdContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,11 +214,11 @@ class ActivityPubConfigBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return string
|
||||
*/
|
||||
public function getJsonLdContext()
|
||||
public function getIdPathPrefix()
|
||||
{
|
||||
return $this->jsonLdContext;
|
||||
return $this->idPathPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,13 +235,5 @@ class ActivityPubConfigBuilder
|
||||
$this->idPathPrefix = $idPathPrefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIdPathPrefix()
|
||||
{
|
||||
return $this->idPathPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
namespace ActivityPub\Config;
|
||||
|
||||
use ActivityPub\Activities\CreateHandler;
|
||||
use ActivityPub\Activities\NonActivityHandler;
|
||||
use ActivityPub\Activities\DeleteHandler;
|
||||
use ActivityPub\Activities\NonActivityHandler;
|
||||
use ActivityPub\Activities\UpdateHandler;
|
||||
use ActivityPub\Activities\ValidationHandler;
|
||||
use ActivityPub\Auth\AuthListener;
|
||||
@ -17,8 +17,8 @@ use ActivityPub\Controllers\PostController;
|
||||
use ActivityPub\Crypto\HttpSignatureService;
|
||||
use ActivityPub\Database\PrefixNamingStrategy;
|
||||
use ActivityPub\Http\Router;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Objects\CollectionsService;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Objects\IdProvider;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Utils\RandomProvider;
|
||||
@ -94,7 +94,7 @@ class ActivityPubModule
|
||||
->addArgument( new Reference( AuthService::class ) )
|
||||
->addArgument( new Reference( ContextProvider::class ) )
|
||||
->addArgument( new Reference( Client::class ) )
|
||||
->addArgument( new Reference( SimpleDateTimeProvider::class ));
|
||||
->addArgument( new Reference( SimpleDateTimeProvider::class ) );
|
||||
|
||||
$this->injector->register( RandomProvider::class, RandomProvider::class );
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Controllers;
|
||||
|
||||
use ActivityPub\Auth\AuthService;
|
||||
@ -15,7 +16,7 @@ use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
*/
|
||||
class GetController
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @var ObjectsService
|
||||
*/
|
||||
@ -54,23 +55,23 @@ class GetController
|
||||
$uri = substr( $uri, 0, $queryPos );
|
||||
}
|
||||
$object = $this->objectsService->dereference( $uri );
|
||||
if ( ! $object ) {
|
||||
if ( !$object ) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
if ( ! $this->authService->isAuthorized( $request, $object ) ) {
|
||||
if ( !$this->authService->isAuthorized( $request, $object ) ) {
|
||||
throw new UnauthorizedHttpException(
|
||||
'Signature realm="ActivityPub",headers="(request-target) host date"'
|
||||
);
|
||||
}
|
||||
if ( $object->hasField( 'type' ) &&
|
||||
( $object['type'] === 'Collection' ||
|
||||
$object['type'] === 'OrderedCollection' ) ) {
|
||||
( $object['type'] === 'Collection' ||
|
||||
$object['type'] === 'OrderedCollection' ) ) {
|
||||
$pagedCollection = $this->collectionsService->pageAndFilterCollection( $request, $object );
|
||||
return new JsonResponse( $pagedCollection );
|
||||
}
|
||||
$response = new JsonResponse( $object->asArray() );
|
||||
if ( $object->hasField( 'type' ) &&
|
||||
$object['type'] === 'Tombstone' ) {
|
||||
$object['type'] === 'Tombstone' ) {
|
||||
$response->setStatusCode( 410 );
|
||||
}
|
||||
return $response;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Controllers;
|
||||
|
||||
use ActivityPub\Activities\InboxActivityEvent;
|
||||
@ -54,15 +55,15 @@ class PostController
|
||||
$inboxField = $object->getReferencingField( 'inbox' );
|
||||
if ( $inboxField ) {
|
||||
$activity = json_decode( $request->getContent(), true );
|
||||
if ( ! $activity || ! array_key_exists( 'actor', $activity ) ) {
|
||||
if ( !$activity || !array_key_exists( 'actor', $activity ) ) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
$activityActor = $this->getActivityActor( $activity );
|
||||
if ( ! $activityActor) {
|
||||
if ( !$activityActor ) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
if ( ! $request->attributes->has( 'signed' ) ||
|
||||
! $this->authorized( $request, $activityActor ) ) {
|
||||
if ( !$request->attributes->has( 'signed' ) ||
|
||||
!$this->authorized( $request, $activityActor ) ) {
|
||||
throw new UnauthorizedHttpException(
|
||||
'Signature realm="ActivityPub",headers="(request-target) host date"'
|
||||
);
|
||||
@ -75,22 +76,32 @@ class PostController
|
||||
$outboxField = $object->getReferencingField( 'outbox' );
|
||||
if ( $outboxField ) {
|
||||
$actorWithOutbox = $outboxField->getObject();
|
||||
if ( ! $this->authorized( $request, $actorWithOutbox ) ) {
|
||||
if ( !$this->authorized( $request, $actorWithOutbox ) ) {
|
||||
throw new UnauthorizedHttpException(
|
||||
'Signature realm="ActivityPub",headers="(request-target) host date"'
|
||||
);
|
||||
}
|
||||
$activity = json_decode( $request->getContent(), true );
|
||||
if ( ! $activity ) {
|
||||
if ( !$activity ) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
$event = new OutboxActivityEvent( $activity, $actorWithOutbox, $request );
|
||||
$this->eventDispatcher->dispatch( OutboxActivityEvent::NAME, $event );
|
||||
return $event->getResponse();
|
||||
}
|
||||
}
|
||||
throw new MethodNotAllowedHttpException( array( Request::METHOD_GET ) );
|
||||
}
|
||||
|
||||
private function getUriWithoutQuery( Request $request )
|
||||
{
|
||||
$uri = $request->getUri();
|
||||
$queryPos = strpos( $uri, '?' );
|
||||
if ( $queryPos !== false ) {
|
||||
$uri = substr( $uri, 0, $queryPos );
|
||||
}
|
||||
return $uri;
|
||||
}
|
||||
|
||||
private function getActivityActor( array $activity )
|
||||
{
|
||||
$actor = $activity['actor'];
|
||||
@ -104,7 +115,7 @@ class PostController
|
||||
|
||||
private function authorized( Request $request, ActivityPubObject $activityActor )
|
||||
{
|
||||
if ( ! $request->attributes->has( 'actor' ) ) {
|
||||
if ( !$request->attributes->has( 'actor' ) ) {
|
||||
return false;
|
||||
}
|
||||
$requestActor = $request->attributes->get( 'actor' );
|
||||
@ -113,15 +124,5 @@ class PostController
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getUriWithoutQuery( Request $request )
|
||||
{
|
||||
$uri = $request->getUri();
|
||||
$queryPos = strpos( $uri, '?' );
|
||||
if ( $queryPos !== false ) {
|
||||
$uri = substr( $uri, 0, $queryPos );
|
||||
}
|
||||
return $uri;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Crypto;
|
||||
|
||||
use ActivityPub\Utils\HeaderUtils;
|
||||
use DateTime;
|
||||
use ActivityPub\Utils\DateTimeProvider;
|
||||
use ActivityPub\Utils\HeaderUtils;
|
||||
use ActivityPub\Utils\SimpleDateTimeProvider;
|
||||
use DateTime;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@ -30,41 +31,32 @@ class HttpSignatureService
|
||||
/**
|
||||
* Constructs a new HttpSignatureService
|
||||
*
|
||||
* @param DateTimeProvider $dateTimeProvider The DateTimeProvider,
|
||||
* @param DateTimeProvider $dateTimeProvider The DateTimeProvider,
|
||||
* defaults to SimpleDateTimeProvider
|
||||
*/
|
||||
public function __construct( DateTimeProvider $dateTimeProvider = null )
|
||||
{
|
||||
if ( ! $dateTimeProvider ) {
|
||||
if ( !$dateTimeProvider ) {
|
||||
$dateTimeProvider = new SimpleDateTimeProvider();
|
||||
}
|
||||
$this->dateTimeProvider = $dateTimeProvider;
|
||||
$this->psr7Factory = new DiactorosFactory();
|
||||
}
|
||||
|
||||
public static function getDefaultHeaders()
|
||||
{
|
||||
return array(
|
||||
'(request-target)',
|
||||
'host',
|
||||
'date',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a signature given the request and private key
|
||||
*
|
||||
* @param RequestInterface $request The request to be signed
|
||||
* @param string $privateKey The private key to use to sign the request
|
||||
* @param string $keyId The id of the signing key
|
||||
* @param array $headers|null The headers to use in the signature
|
||||
* @param array $headers |null The headers to use in the signature
|
||||
* (default ['(request-target)', 'host', 'date'])
|
||||
* @return string The Signature header value
|
||||
*/
|
||||
public function sign( RequestInterface $request, $privateKey,
|
||||
public function sign( RequestInterface $request, $privateKey,
|
||||
$keyId, $headers = null )
|
||||
{
|
||||
if ( ! $headers ) {
|
||||
if ( !$headers ) {
|
||||
$headers = self::getDefaultHeaders();
|
||||
}
|
||||
$headers = array_map( 'strtolower', $headers );
|
||||
@ -78,6 +70,45 @@ class HttpSignatureService
|
||||
"signature=\"$signature\"";
|
||||
}
|
||||
|
||||
public static function getDefaultHeaders()
|
||||
{
|
||||
return array(
|
||||
'(request-target)',
|
||||
'host',
|
||||
'date',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signing string from the request
|
||||
*
|
||||
* @param RequestInterface $request The request
|
||||
* @param array $headers The headers to use to generate the signing string
|
||||
* @return string The signing string
|
||||
*/
|
||||
private function getSigningString( RequestInterface $request, $headers )
|
||||
{
|
||||
$signingComponents = array();
|
||||
foreach ( $headers as $header ) {
|
||||
$component = "${header}: ";
|
||||
if ( $header == '(request-target)' ) {
|
||||
$method = strtolower( $request->getMethod() );
|
||||
$path = $request->getUri()->getPath();
|
||||
$query = $request->getUri()->getQuery();
|
||||
if ( !empty( $query ) ) {
|
||||
$path = "$path?$query";
|
||||
}
|
||||
$component = $component . $method . ' ' . $path;
|
||||
} else {
|
||||
// TODO handle 'digest' specially here too
|
||||
$values = $request->getHeader( $header );
|
||||
$component = $component . implode( ', ', $values );
|
||||
}
|
||||
$signingComponents[] = $component;
|
||||
}
|
||||
return implode( "\n", $signingComponents );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the HTTP signature of $request
|
||||
*
|
||||
@ -85,12 +116,12 @@ class HttpSignatureService
|
||||
* @param string $publicKey The public key to use to verify the request
|
||||
* @return bool True if the signature is valid, false if it is missing or invalid
|
||||
*/
|
||||
public function verify( Request $request, $publicKey )
|
||||
public function verify( Request $request, $publicKey )
|
||||
{
|
||||
$params = array();
|
||||
$headers = $request->headers;
|
||||
|
||||
if ( ! $headers->has( 'date' ) ) {
|
||||
if ( !$headers->has( 'date' ) ) {
|
||||
return false;
|
||||
}
|
||||
$now = $this->dateTimeProvider->getTime( 'http-signature.verify' );
|
||||
@ -102,7 +133,7 @@ class HttpSignatureService
|
||||
if ( $headers->has( 'signature' ) ) {
|
||||
$params = $this->parseSignatureParams( $headers->get( 'signature' ) );
|
||||
} else if ( $headers->has( 'authorization' ) &&
|
||||
substr($headers->get( 'authorization' ), 0, 9) === 'Signature' ) {
|
||||
substr( $headers->get( 'authorization' ), 0, 9 ) === 'Signature' ) {
|
||||
$paramsStr = substr( $headers->get( 'authorization' ), 10 );
|
||||
$params = $this->parseSignatureParams( $paramsStr );
|
||||
}
|
||||
@ -121,43 +152,13 @@ class HttpSignatureService
|
||||
$signature = base64_decode( $params['signature'] );
|
||||
// TODO handle different algorithms here, checking the 'algorithm' param and the key headers
|
||||
$keypair = RsaKeypair::fromPublicKey( $publicKey );
|
||||
return $keypair->verify($signingString, $signature, 'sha256');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signing string from the request
|
||||
*
|
||||
* @param RequestInterface $request The request
|
||||
* @param array $headers The headers to use to generate the signing string
|
||||
* @return string The signing string
|
||||
*/
|
||||
private function getSigningString( RequestInterface $request, $headers )
|
||||
{
|
||||
$signingComponents = array();
|
||||
foreach ( $headers as $header ) {
|
||||
$component = "${header}: ";
|
||||
if ( $header == '(request-target)' ) {
|
||||
$method = strtolower( $request->getMethod());
|
||||
$path = $request->getUri()->getPath();
|
||||
$query = $request->getUri()->getQuery();
|
||||
if ( ! empty( $query ) ) {
|
||||
$path = "$path?$query";
|
||||
}
|
||||
$component = $component . $method . ' ' . $path;
|
||||
} else {
|
||||
// TODO handle 'digest' specially here too
|
||||
$values = $request->getHeader( $header );
|
||||
$component = $component . implode( ', ', $values );
|
||||
}
|
||||
$signingComponents[] = $component;
|
||||
}
|
||||
return implode( "\n", $signingComponents );
|
||||
return $keypair->verify( $signingString, $signature, 'sha256' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the signature params from the provided params string
|
||||
*
|
||||
* @param string $paramsStr The params represented as a string,
|
||||
* @param string $paramsStr The params represented as a string,
|
||||
* e.g. 'keyId="theKey",algorithm="rsa-sha256"'
|
||||
* @return array The params as an associative array
|
||||
*/
|
||||
@ -169,7 +170,7 @@ class HttpSignatureService
|
||||
$paramName = $paramArr[0];
|
||||
$paramValue = $paramArr[1];
|
||||
if ( $paramName == 'headers' ) {
|
||||
$paramValue = explode(' ', $paramValue);
|
||||
$paramValue = explode( ' ', $paramValue );
|
||||
}
|
||||
$params[$paramName] = $paramValue;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Crypto;
|
||||
|
||||
use BadMethodCallException;
|
||||
@ -21,13 +22,53 @@ class RsaKeypair
|
||||
*
|
||||
*/
|
||||
private $privateKey;
|
||||
|
||||
public function __construct( $publicKey, $privateKey )
|
||||
|
||||
public function __construct( $publicKey, $privateKey )
|
||||
{
|
||||
$this->publicKey = $publicKey;
|
||||
$this->privateKey = $privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new keypair
|
||||
*
|
||||
* @return RsaKeypair
|
||||
*/
|
||||
public static function generate()
|
||||
{
|
||||
$rsa = new RSA();
|
||||
$key = $rsa->createKey( 2048 );
|
||||
return new RsaKeypair( $key['publickey'], $key['privatekey'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an RsaKeypair with the given public key
|
||||
*
|
||||
* The generated RsaKeypair will be able to verify signatures but
|
||||
* not sign data, since it won't have a private key.
|
||||
*
|
||||
* @param string $publicKey The public key
|
||||
* @return RsaKeypair
|
||||
*/
|
||||
public static function fromPublicKey( $publicKey )
|
||||
{
|
||||
return new RsaKeypair( $publicKey, '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an RsaKeypair with the given private key
|
||||
*
|
||||
* The generated RsaKeypair will be able to sign data but
|
||||
* not verify signatures, since it won't have a public key.
|
||||
*
|
||||
* @param string $privateKey The private key
|
||||
* @return RsaKeypair
|
||||
*/
|
||||
public static function fromPrivateKey( $privateKey )
|
||||
{
|
||||
return new RsaKeypair( '', $privateKey );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key as a string
|
||||
*
|
||||
@ -66,7 +107,7 @@ class RsaKeypair
|
||||
}
|
||||
$rsa = new RSA();
|
||||
$rsa->setHash( $hash );
|
||||
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
|
||||
$rsa->setSignatureMode( RSA::SIGNATURE_PKCS1 );
|
||||
$rsa->loadKey( $this->privateKey );
|
||||
return $rsa->sign( $data );
|
||||
}
|
||||
@ -86,49 +127,9 @@ class RsaKeypair
|
||||
// I have no idea what that means or how to fix it
|
||||
$rsa = new RSA();
|
||||
$rsa->setHash( $hash );
|
||||
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
|
||||
$rsa->setSignatureMode( RSA::SIGNATURE_PKCS1 );
|
||||
$rsa->loadKey( $this->publicKey );
|
||||
return $rsa->verify( $data, $signature );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new keypair
|
||||
*
|
||||
* @return RsaKeypair
|
||||
*/
|
||||
public static function generate()
|
||||
{
|
||||
$rsa = new RSA();
|
||||
$key = $rsa->createKey( 2048 );
|
||||
return new RsaKeypair( $key['publickey'], $key['privatekey'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an RsaKeypair with the given public key
|
||||
*
|
||||
* The generated RsaKeypair will be able to verify signatures but
|
||||
* not sign data, since it won't have a private key.
|
||||
*
|
||||
* @param string $publicKey The public key
|
||||
* @return RsaKeypair
|
||||
*/
|
||||
public static function fromPublicKey( $publicKey )
|
||||
{
|
||||
return new RsaKeypair( $publicKey, '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an RsaKeypair with the given private key
|
||||
*
|
||||
* The generated RsaKeypair will be able to sign data but
|
||||
* not verify signatures, since it won't have a public key.
|
||||
*
|
||||
* @param string $privateKey The private key
|
||||
* @return RsaKeypair
|
||||
*/
|
||||
public static function fromPrivateKey( $privateKey)
|
||||
{
|
||||
return new RsaKeypair( '', $privateKey );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Database;
|
||||
|
||||
use Doctrine\ORM\Mapping\NamingStrategy;
|
||||
@ -12,41 +13,41 @@ class PrefixNamingStrategy implements NamingStrategy
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
public function classToTableName($className)
|
||||
{
|
||||
return $this->prefix . substr($className, strrpos($className, '\\') + 1);
|
||||
}
|
||||
|
||||
public function propertyToColumnName($propertyName, $className = null)
|
||||
public function propertyToColumnName( $propertyName, $className = null )
|
||||
{
|
||||
return $propertyName;
|
||||
}
|
||||
|
||||
public function joinColumnName( $propertyName, $className = null )
|
||||
{
|
||||
return $propertyName . '_' . $this->referenceColumnName();
|
||||
}
|
||||
|
||||
public function referenceColumnName()
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
|
||||
public function joinColumnName($propertyName, $className = null)
|
||||
public function joinTableName( $sourceEntity, $targetEntity, $propertyName = null )
|
||||
{
|
||||
return $propertyName . '_' . $this->referenceColumnName();
|
||||
return strtolower( $this->classToTableName( $sourceEntity ) . '_' .
|
||||
$this->classToTableName( $targetEntity ) );
|
||||
}
|
||||
|
||||
public function joinTableName($sourceEntity, $targetEntity, $propertyName = null)
|
||||
public function classToTableName( $className )
|
||||
{
|
||||
return strtolower($this->classToTableName($sourceEntity) . '_' .
|
||||
$this->classToTableName($targetEntity));
|
||||
return $this->prefix . substr( $className, strrpos( $className, '\\' ) + 1 );
|
||||
}
|
||||
|
||||
public function joinKeyColumnName($entityName, $referencedColumnName = null)
|
||||
public function joinKeyColumnName( $entityName, $referencedColumnName = null )
|
||||
{
|
||||
return strtolower($this->classToTableName($entityName) . '_' .
|
||||
($referencedColumnName ?: $this->referenceColumnName()));
|
||||
return strtolower( $this->classToTableName( $entityName ) . '_' .
|
||||
( $referencedColumnName ?: $this->referenceColumnName() ) );
|
||||
}
|
||||
|
||||
public function embeddedFieldToColumnName($propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null)
|
||||
public function embeddedFieldToColumnName( $propertyName, $embeddedColumnName, $className = null, $embeddedClassName = null )
|
||||
{
|
||||
return $propertyName.'_'.$embeddedColumnName;
|
||||
return $propertyName . '_' . $embeddedColumnName;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,8 +58,9 @@ class ActivityPubObject implements ArrayAccess
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
public function __construct( DateTime $time = null ) {
|
||||
if ( ! $time ) {
|
||||
public function __construct( DateTime $time = null )
|
||||
{
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$this->fields = new ArrayCollection();
|
||||
@ -75,7 +76,8 @@ class ActivityPubObject implements ArrayAccess
|
||||
*
|
||||
* @return array|string Either the object or its id if $depth is < 0
|
||||
*/
|
||||
public function asArray( $depth = 1 ) {
|
||||
public function asArray( $depth = 1 )
|
||||
{
|
||||
if ( $depth < 0 && $this->hasField( 'id' ) ) {
|
||||
return $this->getFieldValue( 'id' );
|
||||
}
|
||||
@ -91,9 +93,20 @@ class ActivityPubObject implements ArrayAccess
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
/**
|
||||
* Returns true if the object contains a field with key $name
|
||||
*
|
||||
* @param mixed $name
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasField( $name )
|
||||
{
|
||||
return $this->id;
|
||||
foreach ( $this->getFields() as $field ) {
|
||||
if ( $field->getName() === $name ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,13 +120,28 @@ class ActivityPubObject implements ArrayAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields which reference this object
|
||||
* Returns the value of the field with key $name
|
||||
*
|
||||
* @return Field[]
|
||||
* The value is either a string, another ActivityPubObject, or null
|
||||
* if no such key exists.
|
||||
*
|
||||
* @param mixed $name
|
||||
* @return string|ActivityPubObject|null The field's value, or null if
|
||||
* the field is not found
|
||||
*/
|
||||
public function getReferencingFields()
|
||||
public function getFieldValue( $name )
|
||||
{
|
||||
return $this->referencingFields;
|
||||
foreach ( $this->getFields() as $field ) {
|
||||
if ( $field->getName() === $name ) {
|
||||
return $field->getValueOrTargetObject();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,55 +165,14 @@ class ActivityPubObject implements ArrayAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the object contains a field with key $name
|
||||
* Sets the last updated timestamp
|
||||
*
|
||||
* @param DateTime $lastUpdated The new last updated timestamp
|
||||
*
|
||||
* @param mixed $name
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasField( $name )
|
||||
public function setLastUpdated( $lastUpdated )
|
||||
{
|
||||
foreach( $this->getFields() as $field ) {
|
||||
if ( $field->getName() === $name ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields named $field, if it exists
|
||||
*
|
||||
* @param string $name The name of the field to get
|
||||
* @return Field|null
|
||||
*/
|
||||
public function getField( $name )
|
||||
{
|
||||
foreach( $this->getFields() as $field ) {
|
||||
if ( $field->getName() === $name ) {
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the field with key $name
|
||||
*
|
||||
* The value is either a string, another ActivityPubObject, or null
|
||||
* if no such key exists.
|
||||
*
|
||||
* @param mixed $name
|
||||
* @return string|ActivityPubObject|null The field's value, or null if
|
||||
* the field is not found
|
||||
*/
|
||||
public function getFieldValue( $name )
|
||||
{
|
||||
foreach( $this->getFields() as $field ) {
|
||||
if ( $field->getName() === $name ) {
|
||||
return $field->getValueOrTargetObject();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
$this->lastUpdated = $lastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,7 +187,7 @@ class ActivityPubObject implements ArrayAccess
|
||||
*/
|
||||
public function addField( Field $field, DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$this->fields[] = $field;
|
||||
@ -215,7 +202,7 @@ class ActivityPubObject implements ArrayAccess
|
||||
*/
|
||||
public function hasReferencingField( $name )
|
||||
{
|
||||
foreach( $this->getReferencingFields() as $field ) {
|
||||
foreach ( $this->getReferencingFields() as $field ) {
|
||||
if ( $field->getName() === $name ) {
|
||||
return true;
|
||||
}
|
||||
@ -223,6 +210,16 @@ class ActivityPubObject implements ArrayAccess
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields which reference this object
|
||||
*
|
||||
* @return Field[]
|
||||
*/
|
||||
public function getReferencingFields()
|
||||
{
|
||||
return $this->referencingFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the referencing field named $field, if it exists
|
||||
*
|
||||
@ -231,7 +228,7 @@ class ActivityPubObject implements ArrayAccess
|
||||
*/
|
||||
public function getReferencingField( $name )
|
||||
{
|
||||
foreach( $this->getReferencingFields() as $field ) {
|
||||
foreach ( $this->getReferencingFields() as $field ) {
|
||||
if ( $field->getName() === $name ) {
|
||||
return $field;
|
||||
}
|
||||
@ -274,34 +271,13 @@ class ActivityPubObject implements ArrayAccess
|
||||
*/
|
||||
public function removeField( Field $field, DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$this->fields->removeElement( $field );
|
||||
$this->lastUpdated = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last updated timestamp
|
||||
*
|
||||
* @param DateTime $lastUpdated The new last updated timestamp
|
||||
*
|
||||
*/
|
||||
public function setLastUpdated( $lastUpdated )
|
||||
{
|
||||
$this->lastUpdated = $lastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this object has an associated private key, false if otherwise
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrivateKey()
|
||||
{
|
||||
return $this->privateKey !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object's private key
|
||||
*
|
||||
@ -316,6 +292,16 @@ class ActivityPubObject implements ArrayAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this object has an associated private key, false if otherwise
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrivateKey()
|
||||
{
|
||||
return $this->privateKey !== null;
|
||||
}
|
||||
|
||||
public function offsetExists( $offset )
|
||||
{
|
||||
return $this->hasField( $offset );
|
||||
@ -344,21 +330,37 @@ class ActivityPubObject implements ArrayAccess
|
||||
* Returns true if $other has all the same fields as $this
|
||||
*
|
||||
* @param ActivityPubObject $other The other object to compare to
|
||||
* @return bool Whether or not this object has the same fields and values as
|
||||
* @return bool Whether or not this object has the same fields and values as
|
||||
* the other
|
||||
*/
|
||||
public function equals( ActivityPubObject $other )
|
||||
{
|
||||
foreach( $other->getFields() as $otherField ) {
|
||||
foreach ( $other->getFields() as $otherField ) {
|
||||
$thisField = $this->getField( $otherField->getName() );
|
||||
if ( ! $thisField ) {
|
||||
if ( !$thisField ) {
|
||||
return false;
|
||||
}
|
||||
if ( ! $thisField->equals( $otherField ) ) {
|
||||
if ( !$thisField->equals( $otherField ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields named $field, if it exists
|
||||
*
|
||||
* @param string $name The name of the field to get
|
||||
* @return Field|null
|
||||
*/
|
||||
public function getField( $name )
|
||||
{
|
||||
foreach ( $this->getFields() as $field ) {
|
||||
if ( $field->getName() === $name ) {
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use DateTime;
|
||||
|
||||
/**
|
||||
* The field table hold the JSON-LD object graph.
|
||||
*
|
||||
*
|
||||
* Its structure is based on https://changelog.com/posts/graph-databases-101:
|
||||
* Every row has a subject, which is a foreign key into the Objects table,
|
||||
* a predicate, which is a the JSON field that describes the graph edge relationship
|
||||
@ -51,7 +51,7 @@ class Field
|
||||
protected $value;
|
||||
/**
|
||||
* @ManyToOne(targetEntity="ActivityPubObject", inversedBy="referencingFields")
|
||||
* @var ActivityPubObject The value of the field if it holds another object;
|
||||
* @var ActivityPubObject The value of the field if it holds another object;
|
||||
* mutually exclusive with $value
|
||||
*/
|
||||
protected $targetObject;
|
||||
@ -72,7 +72,7 @@ class Field
|
||||
|
||||
protected function __construct( DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$this->created = $time;
|
||||
@ -89,9 +89,9 @@ class Field
|
||||
* @return Field The new field
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function withValue( ActivityPubObject $object, $name, $value, DateTime $time = null )
|
||||
public static function withValue( ActivityPubObject $object, $name, $value, DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$field = new Field( $time );
|
||||
@ -116,7 +116,7 @@ class Field
|
||||
ActivityPubObject $targetObject,
|
||||
DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$field = new Field( $time );
|
||||
@ -126,59 +126,6 @@ class Field
|
||||
return $field;
|
||||
}
|
||||
|
||||
protected function setObject( ActivityPubObject $object, DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$object->addField( $this, $time );
|
||||
$this->object= $object;
|
||||
}
|
||||
|
||||
public function setTargetObject( ActivityPubObject $targetObject, DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$this->value = null;
|
||||
$oldTargetObject = $this->getTargetObject();
|
||||
if ( $oldTargetObject ) {
|
||||
$oldTargetObject->removeReferencingField( $this );
|
||||
}
|
||||
$targetObject->addReferencingField( $this );
|
||||
$this->targetObject = $targetObject;
|
||||
$this->lastUpdated = $time;
|
||||
}
|
||||
|
||||
protected function setName( $name )
|
||||
{
|
||||
$this->name= $name;
|
||||
}
|
||||
|
||||
public function setValue( $value, DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$oldTargetObject = $this->getTargetObject();
|
||||
if ( $oldTargetObject ) {
|
||||
$oldTargetObject->removeReferencingField( $this );
|
||||
}
|
||||
$this->targetObject = null;
|
||||
$this->value = $value;
|
||||
$this->lastUpdated = $time;
|
||||
}
|
||||
|
||||
protected function setCreated( DateTime $timestamp )
|
||||
{
|
||||
$this->created = $timestamp;
|
||||
}
|
||||
|
||||
protected function setLastUpdated( DateTime $timestamp )
|
||||
{
|
||||
$this->lastUpdated = $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object to which this field belongs
|
||||
*
|
||||
@ -189,54 +136,13 @@ class Field
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
protected function setObject( ActivityPubObject $object, DateTime $time = null )
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target object of the field or null if there isn't one
|
||||
*
|
||||
* @return ActivityPubObject|null
|
||||
*/
|
||||
public function getTargetObject()
|
||||
{
|
||||
return $this->targetObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the field or null if there isn't one
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the field has a value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasValue()
|
||||
{
|
||||
return $this->value !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the field has a target object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTargetObject()
|
||||
{
|
||||
return $this->targetObject !== null;
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$object->addField( $this, $time );
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -246,7 +152,7 @@ class Field
|
||||
*/
|
||||
public function getValueOrTargetObject()
|
||||
{
|
||||
if ( ! is_null( $this->targetObject) ) {
|
||||
if ( !is_null( $this->targetObject ) ) {
|
||||
return $this->targetObject;
|
||||
} else {
|
||||
return $this->value;
|
||||
@ -263,6 +169,11 @@ class Field
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
protected function setCreated( DateTime $timestamp )
|
||||
{
|
||||
$this->created = $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field's last updated timestamp
|
||||
*
|
||||
@ -273,6 +184,11 @@ class Field
|
||||
return $this->lastUpdated;
|
||||
}
|
||||
|
||||
protected function setLastUpdated( DateTime $timestamp )
|
||||
{
|
||||
$this->lastUpdated = $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if $this is equal to $other
|
||||
*
|
||||
@ -291,5 +207,89 @@ class Field
|
||||
$this->getTargetObject()->equals( $other->getTargetObject() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
protected function setName( $name )
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the field has a value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasValue()
|
||||
{
|
||||
return $this->value !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the field or null if there isn't one
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue( $value, DateTime $time = null )
|
||||
{
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$oldTargetObject = $this->getTargetObject();
|
||||
if ( $oldTargetObject ) {
|
||||
$oldTargetObject->removeReferencingField( $this );
|
||||
}
|
||||
$this->targetObject = null;
|
||||
$this->value = $value;
|
||||
$this->lastUpdated = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the field has a target object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTargetObject()
|
||||
{
|
||||
return $this->targetObject !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target object of the field or null if there isn't one
|
||||
*
|
||||
* @return ActivityPubObject|null
|
||||
*/
|
||||
public function getTargetObject()
|
||||
{
|
||||
return $this->targetObject;
|
||||
}
|
||||
|
||||
public function setTargetObject( ActivityPubObject $targetObject, DateTime $time = null )
|
||||
{
|
||||
if ( !$time ) {
|
||||
$time = new DateTime( "now" );
|
||||
}
|
||||
$this->value = null;
|
||||
$oldTargetObject = $this->getTargetObject();
|
||||
if ( $oldTargetObject ) {
|
||||
$oldTargetObject->removeReferencingField( $this );
|
||||
}
|
||||
$targetObject->addReferencingField( $this );
|
||||
$this->targetObject = $targetObject;
|
||||
$this->lastUpdated = $time;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Entities;
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Http;
|
||||
|
||||
use ActivityPub\Controllers\GetController;
|
||||
@ -21,6 +22,13 @@ class Router implements EventSubscriberInterface
|
||||
*/
|
||||
private $postController;
|
||||
|
||||
public function __construct( GetController $getController,
|
||||
PostController $postController )
|
||||
{
|
||||
$this->getController = $getController;
|
||||
$this->postController = $postController;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
@ -28,12 +36,6 @@ class Router implements EventSubscriberInterface
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct( GetController $getController,
|
||||
PostController $postController )
|
||||
{
|
||||
$this->getController = $getController;
|
||||
$this->postController = $postController;
|
||||
}
|
||||
/**
|
||||
* Routes the request by setting the _controller attribute
|
||||
*
|
||||
|
@ -104,6 +104,89 @@ class CollectionsService
|
||||
return $colArr;
|
||||
}
|
||||
|
||||
private function getCollectionPage( ActivityPubObject $collection,
|
||||
Request $request,
|
||||
$offset,
|
||||
$pageSize )
|
||||
{
|
||||
$itemsKey = 'items';
|
||||
$pageType = 'CollectionPage';
|
||||
$isOrdered = $this->isOrdered( $collection );
|
||||
if ( $isOrdered ) {
|
||||
$itemsKey = 'orderedItems';
|
||||
$pageType = 'OrderedCollectionPage';
|
||||
}
|
||||
if ( !$collection->hasField( $itemsKey ) ) {
|
||||
throw new InvalidArgumentException(
|
||||
"Collection does not have an \"$itemsKey\" key"
|
||||
);
|
||||
}
|
||||
$collectionItems = $collection->getFieldValue( $itemsKey );
|
||||
$pageItems = array();
|
||||
$idx = $offset;
|
||||
$count = 0;
|
||||
while ( $count < $pageSize ) {
|
||||
$item = $collectionItems->getFieldValue( $idx );
|
||||
if ( !$item ) {
|
||||
break;
|
||||
}
|
||||
if ( is_string( $item ) ) {
|
||||
$pageItems[] = $item;
|
||||
$count++;
|
||||
} else if ( $this->authService->isAuthorized( $request, $item ) ) {
|
||||
$pageItems[] = $item->asArray( 1 );
|
||||
$count++;
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
if ( $count === 0 ) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$page = array(
|
||||
'@context' => $this->contextProvider->getContext(),
|
||||
'id' => $collection['id'] . "?offset=$offset",
|
||||
'type' => $pageType,
|
||||
$itemsKey => $pageItems,
|
||||
'partOf' => $collection['id'],
|
||||
);
|
||||
// TODO set 'first' and 'last' on the page
|
||||
$nextIdx = $this->hasNextItem( $request, $collectionItems, $idx );
|
||||
if ( $nextIdx ) {
|
||||
$page['next'] = $collection['id'] . "?offset=$nextIdx";
|
||||
}
|
||||
if ( $isOrdered ) {
|
||||
$page['startIndex'] = $offset;
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
|
||||
private function isOrdered( ActivityPubObject $collection )
|
||||
{
|
||||
if ( $collection->hasField( 'type' ) &&
|
||||
$collection['type'] === 'OrderedCollection' ) {
|
||||
return true;
|
||||
} else if ( $collection->hasField( 'type' ) &&
|
||||
$collection['type'] === 'Collection' ) {
|
||||
return false;
|
||||
} else {
|
||||
throw new InvalidArgumentException( 'Not a collection' );
|
||||
}
|
||||
}
|
||||
|
||||
private function hasNextItem( Request $request, ActivityPubObject $collectionItems, $idx )
|
||||
{
|
||||
$next = $collectionItems->getFieldValue( $idx );
|
||||
while ( $next ) {
|
||||
if ( is_string( $next ) ||
|
||||
$this->authService->isAuthorized( $request, $next ) ) {
|
||||
return $idx;
|
||||
}
|
||||
$idx++;
|
||||
$next = $collectionItems->getFieldValue( $idx );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a collection as an array, normalize the collection by collapsing
|
||||
* collection pages into a single `items` or `orderedItems` array
|
||||
@ -139,6 +222,38 @@ class CollectionsService
|
||||
return $collection;
|
||||
}
|
||||
|
||||
private function fetchPage( $pageId )
|
||||
{
|
||||
$request = new Psr7Request( 'GET', $pageId, array(
|
||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
||||
) );
|
||||
$response = $this->httpClient->send( $request );
|
||||
if ( $response->getStatusCode() !== 200 || empty( $response->getBody() ) ) {
|
||||
return null;
|
||||
}
|
||||
return json_decode( $response->getBody(), true );
|
||||
}
|
||||
|
||||
private function getPageItems( array $collectionPage )
|
||||
{
|
||||
$items = array();
|
||||
if ( array_key_exists( 'items', $collectionPage ) ) {
|
||||
$items = array_merge( $items, $collectionPage['items'] );
|
||||
} else if ( array_key_exists( 'orderedItems', $collectionPage ) ) {
|
||||
$items = array_merge( $items, $collectionPage['orderedItems'] );
|
||||
}
|
||||
if ( array_key_exists( 'next', $collectionPage ) ) {
|
||||
$nextPage = $collectionPage['next'];
|
||||
if ( is_string( $nextPage ) ) {
|
||||
$nextPage = $this->fetchPage( $nextPage );
|
||||
}
|
||||
if ( $nextPage ) {
|
||||
$items = array_merge( $items, $this->getPageItems( $nextPage ) );
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds $item to $collection
|
||||
*
|
||||
@ -195,120 +310,5 @@ class CollectionsService
|
||||
$this->entityManager->persist( $collection );
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
private function getPageItems( array $collectionPage )
|
||||
{
|
||||
$items = array();
|
||||
if ( array_key_exists( 'items', $collectionPage ) ) {
|
||||
$items = array_merge( $items, $collectionPage['items'] );
|
||||
} else if ( array_key_exists( 'orderedItems', $collectionPage ) ) {
|
||||
$items = array_merge( $items, $collectionPage['orderedItems'] );
|
||||
}
|
||||
if ( array_key_exists( 'next', $collectionPage ) ) {
|
||||
$nextPage = $collectionPage['next'];
|
||||
if ( is_string( $nextPage ) ) {
|
||||
$nextPage = $this->fetchPage( $nextPage );
|
||||
}
|
||||
if ( $nextPage ) {
|
||||
$items = array_merge( $items, $this->getPageItems( $nextPage ) );
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function fetchPage( $pageId )
|
||||
{
|
||||
$request = new Psr7Request( 'GET', $pageId, array(
|
||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
||||
) );
|
||||
$response = $this->httpClient->send( $request );
|
||||
if ( $response->getStatusCode() !== 200 || empty( $response->getBody() ) ) {
|
||||
return null;
|
||||
}
|
||||
return json_decode( $response->getBody(), true );
|
||||
}
|
||||
|
||||
private function getCollectionPage( ActivityPubObject $collection,
|
||||
Request $request,
|
||||
$offset,
|
||||
$pageSize )
|
||||
{
|
||||
$itemsKey = 'items';
|
||||
$pageType = 'CollectionPage';
|
||||
$isOrdered = $this->isOrdered( $collection );
|
||||
if ( $isOrdered ) {
|
||||
$itemsKey = 'orderedItems';
|
||||
$pageType = 'OrderedCollectionPage';
|
||||
}
|
||||
if ( !$collection->hasField( $itemsKey ) ) {
|
||||
throw new InvalidArgumentException(
|
||||
"Collection does not have an \"$itemsKey\" key"
|
||||
);
|
||||
}
|
||||
$collectionItems = $collection->getFieldValue( $itemsKey );
|
||||
$pageItems = array();
|
||||
$idx = $offset;
|
||||
$count = 0;
|
||||
while ( $count < $pageSize ) {
|
||||
$item = $collectionItems->getFieldValue( $idx );
|
||||
if ( !$item ) {
|
||||
break;
|
||||
}
|
||||
if ( is_string( $item ) ) {
|
||||
$pageItems[] = $item;
|
||||
$count++;
|
||||
} else if ( $this->authService->isAuthorized( $request, $item ) ) {
|
||||
$pageItems[] = $item->asArray( 1 );
|
||||
$count++;
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
if ( $count === 0 ) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$page = array(
|
||||
'@context' => $this->contextProvider->getContext(),
|
||||
'id' => $collection['id'] . "?offset=$offset",
|
||||
'type' => $pageType,
|
||||
$itemsKey => $pageItems,
|
||||
'partOf' => $collection['id'],
|
||||
);
|
||||
// TODO set 'first' and 'last' on the page
|
||||
$nextIdx = $this->hasNextItem( $request, $collectionItems, $idx );
|
||||
if ( $nextIdx ) {
|
||||
$page['next'] = $collection['id'] . "?offset=$nextIdx";
|
||||
}
|
||||
if ( $isOrdered ) {
|
||||
$page['startIndex'] = $offset;
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
|
||||
private function hasNextItem( Request $request, ActivityPubObject $collectionItems, $idx )
|
||||
{
|
||||
$next = $collectionItems->getFieldValue( $idx );
|
||||
while ( $next ) {
|
||||
if ( is_string( $next ) ||
|
||||
$this->authService->isAuthorized( $request, $next ) ) {
|
||||
return $idx;
|
||||
}
|
||||
$idx++;
|
||||
$next = $collectionItems->getFieldValue( $idx );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isOrdered( ActivityPubObject $collection )
|
||||
{
|
||||
if ( $collection->hasField( 'type' ) &&
|
||||
$collection['type'] === 'OrderedCollection' ) {
|
||||
return true;
|
||||
} else if ( $collection->hasField( 'type' ) &&
|
||||
$collection['type'] === 'Collection' ) {
|
||||
return false;
|
||||
} else {
|
||||
throw new InvalidArgumentException( 'Not a collection' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Objects;
|
||||
|
||||
class ContextProvider
|
||||
{
|
||||
private $ctx;
|
||||
|
||||
|
||||
public function __construct( $ctx = null )
|
||||
{
|
||||
if ( ! $ctx ) {
|
||||
if ( !$ctx ) {
|
||||
$ctx = self::getDefaultContext();
|
||||
}
|
||||
$this->ctx = $ctx;
|
||||
}
|
||||
|
||||
public function getContext()
|
||||
{
|
||||
return $this->ctx;
|
||||
}
|
||||
|
||||
public static function getDefaultContext()
|
||||
{
|
||||
return array(
|
||||
@ -25,5 +21,10 @@ class ContextProvider
|
||||
'https://w3id.org/security/v1',
|
||||
);
|
||||
}
|
||||
|
||||
public function getContext()
|
||||
{
|
||||
return $this->ctx;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Objects;
|
||||
|
||||
use ActivityPub\Utils\RandomProvider;
|
||||
@ -51,7 +52,7 @@ class IdProvider
|
||||
public function getId( Request $request, $path = "objects" )
|
||||
{
|
||||
$baseUri = $request->getSchemeAndHttpHost();
|
||||
if ( ! empty( $path ) ) {
|
||||
if ( !empty( $path ) ) {
|
||||
$baseUri = $baseUri . "/{$this->pathPrefix}/$path";
|
||||
}
|
||||
$rnd = $this->randomProvider->randomString( self::ID_LENGTH );
|
||||
|
@ -14,28 +14,207 @@ use GuzzleHttp\Psr7\Request;
|
||||
|
||||
class ObjectsService
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
/**
|
||||
* @var DateTimeProvider
|
||||
/**
|
||||
* @var DateTimeProvider
|
||||
*/
|
||||
protected $dateTimeProvider;
|
||||
/**
|
||||
* @var Client
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $httpClient;
|
||||
|
||||
public function __construct( EntityManager $entityManager,
|
||||
DateTimeProvider $dateTimeProvider,
|
||||
Client $client)
|
||||
Client $client )
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->dateTimeProvider = $dateTimeProvider;
|
||||
$this->httpClient = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully replaces the object referenced by $id by the new $fields
|
||||
*
|
||||
* @param string $id The id of the object to replace
|
||||
* @param array $replacement The new fields to replace the object with
|
||||
* @return ActivityPubObject|null The replaced object, or null
|
||||
* if no object with $id exists
|
||||
*/
|
||||
public function replace( $id, $replacement )
|
||||
{
|
||||
$existing = $this->getObject( $id );
|
||||
if ( !$existing ) {
|
||||
return null;
|
||||
}
|
||||
foreach ( $existing->getFields() as $field ) {
|
||||
if ( !array_key_exists( $field->getName(), $replacement ) ) {
|
||||
$replacement[$field->getName()] = null;
|
||||
}
|
||||
}
|
||||
return $this->update( $id, $replacement );
|
||||
}/** @noinspection PhpDocMissingThrowsInspection */
|
||||
|
||||
/**
|
||||
* Gets an object from the DB by its ActivityPub id
|
||||
*
|
||||
* For internal use only - external callers should use dereference()
|
||||
*
|
||||
* @param string $id The object's id
|
||||
* @return ActivityPubObject|null The object or null
|
||||
* if no object exists with that id
|
||||
*/
|
||||
protected function getObject( $id )
|
||||
{
|
||||
$results = $this->query( array( 'id' => $id ) );
|
||||
if ( !empty( $results ) ) {
|
||||
return $results[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries for an object with certain field values
|
||||
*
|
||||
* @param array $queryTerms An associative array where the keys are field
|
||||
* names and the values are the values to query for. The value for a key
|
||||
* can also be another associative array, which represents a field
|
||||
* containing a target object that matches the given nested query.
|
||||
* Finally, the value could be a sequential array, which represents a field
|
||||
* containing all of the specified values (the field could also contain more
|
||||
* values).
|
||||
*
|
||||
* @return ActivityPubObject[] The objects that match the query, if any,
|
||||
* ordered by created timestamp from newest to oldest
|
||||
*/
|
||||
public function query( $queryTerms )
|
||||
{
|
||||
$qb = $this->getObjectQuery( $queryTerms );
|
||||
$query = $qb->getQuery();
|
||||
return $query->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the Doctrine QueryBuilder that represents the query
|
||||
*
|
||||
* This function is recursive; it traverses the query tree to build up the
|
||||
* final expression
|
||||
*
|
||||
* @param array $queryTerms The query terms from which to generate the expressions
|
||||
* @param int $nonce A nonce value to differentiate field names
|
||||
* @return QueryBuilder The expression
|
||||
*/
|
||||
protected function getObjectQuery( $queryTerms, $nonce = 0 )
|
||||
{
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
$exprs = array();
|
||||
foreach ( $queryTerms as $fieldName => $fieldValue ) {
|
||||
if ( is_array( $fieldValue ) ) {
|
||||
$subQuery = $this->getObjectQuery( $fieldValue, $nonce + 1 );
|
||||
$exprs[] = $qb->expr()->andX(
|
||||
$qb->expr()->like(
|
||||
"field$nonce.name",
|
||||
$qb->expr()->literal( (string)$fieldName )
|
||||
),
|
||||
$qb->expr()->in( "field$nonce.targetObject", $subQuery->getDql() )
|
||||
);
|
||||
} else {
|
||||
$exprs[] = $qb->expr()->andX(
|
||||
$qb->expr()->like(
|
||||
"field$nonce.name",
|
||||
$qb->expr()->literal( (string)$fieldName )
|
||||
),
|
||||
$qb->expr()->like(
|
||||
"field$nonce.value",
|
||||
$qb->expr()->literal( $fieldValue )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return $qb->select( "object$nonce" )
|
||||
->from( 'ActivityPub\Entities\ActivityPubObject', "object$nonce" )
|
||||
->join( "object{$nonce}.fields", "field$nonce" )
|
||||
->where( call_user_func_array(
|
||||
array( $qb->expr(), 'orX' ),
|
||||
$exprs
|
||||
) )
|
||||
->groupBy( "object$nonce" )
|
||||
->having( $qb->expr()->eq(
|
||||
$qb->expr()->count( "field$nonce" ),
|
||||
count( $queryTerms )
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates $object
|
||||
*
|
||||
* @param string $id The ActivityPub id of the object to update
|
||||
* @param array $updatedFields An array where the key is a field name
|
||||
* to update and the value is the field's new value. If the value is
|
||||
* null, the field will be deleted.
|
||||
*
|
||||
* If the update results in an orphaned anonymous node (an ActivityPubObject
|
||||
* with no 'id' field that no longer has any references to it), then the
|
||||
* orphaned node will be deleted.
|
||||
*
|
||||
* @return ActivityPubObject|null The updated object,
|
||||
* or null if an object with that id isn't in the DB
|
||||
*/
|
||||
public function update( $id, $updatedFields )
|
||||
{
|
||||
$object = $this->getObject( $id );
|
||||
if ( !$object ) {
|
||||
return null;
|
||||
}
|
||||
foreach ( $updatedFields as $fieldName => $newValue ) {
|
||||
if ( $newValue === null && $object->hasField( $fieldName ) ) {
|
||||
$field = $object->getField( $fieldName );
|
||||
if ( $field->hasTargetObject() && !$field->getTargetObject()->hasField( 'id' ) ) {
|
||||
$targetObject = $field->getTargetObject();
|
||||
// Clear the target object by setting a dummy value
|
||||
$field->setValue( '' );
|
||||
$this->entityManager->remove( $targetObject );
|
||||
}
|
||||
$object->removeField( $field );
|
||||
$this->entityManager->persist( $object );
|
||||
$this->entityManager->remove( $field );
|
||||
} else if ( $object->hasField( $fieldName ) ) {
|
||||
$field = $object->getField( $fieldName );
|
||||
$oldTargetObject = $field->getTargetObject();
|
||||
if ( is_array( $newValue ) ) {
|
||||
$newTargetObject = $this->persist( $newValue, 'objects-service.update' );
|
||||
$field->setTargetObject(
|
||||
$newTargetObject,
|
||||
$this->dateTimeProvider->getTime( 'objects-service.update' )
|
||||
);
|
||||
} else {
|
||||
$field->setValue(
|
||||
$newValue, $this->dateTimeProvider->getTime( 'objects-service.update' )
|
||||
);
|
||||
}
|
||||
if ( $oldTargetObject && !$oldTargetObject->hasField( 'id' ) ) {
|
||||
$this->entityManager->remove( $oldTargetObject );
|
||||
}
|
||||
$this->entityManager->persist( $field );
|
||||
} else {
|
||||
if ( is_array( $newValue ) ) {
|
||||
$newTargetObject = $this->persist( $newValue );
|
||||
$field = Field::withObject( $object, $fieldName, $newTargetObject );
|
||||
} else {
|
||||
$field = Field::withValue( $object, $fieldName, $newValue );
|
||||
}
|
||||
$this->entityManager->persist( $field );
|
||||
}
|
||||
}
|
||||
$object->setLastUpdated( $this->dateTimeProvider->getTime( 'objects-service.update' ) );
|
||||
$this->entityManager->persist( $object );
|
||||
$this->entityManager->flush();
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists a new object to the database with fields defined by $fields
|
||||
*
|
||||
@ -44,7 +223,7 @@ class ObjectsService
|
||||
* The existing object will not have its fields modified.
|
||||
*
|
||||
* @param array $fields The fields that define the new object
|
||||
* @param string $context The context to retrieve the current time in.
|
||||
* @param string $context The context to retrieve the current time in.
|
||||
* Used for fixing the time in tests.
|
||||
*
|
||||
* @return ActivityPubObject The created object
|
||||
@ -67,7 +246,7 @@ class ObjectsService
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->entityManager->flush();
|
||||
return $object;
|
||||
}/** @noinspection PhpDocMissingThrowsInspection */
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists a field.
|
||||
@ -89,7 +268,7 @@ class ObjectsService
|
||||
$this->entityManager->persist( $fieldEntity );
|
||||
} else {
|
||||
if ( $fieldName !== 'id' &&
|
||||
filter_var( $fieldValue, FILTER_VALIDATE_URL ) !== false ) {
|
||||
filter_var( $fieldValue, FILTER_VALIDATE_URL ) !== false ) {
|
||||
$dereferenced = $this->dereference( $fieldValue );
|
||||
if ( $dereferenced ) {
|
||||
$fieldEntity = Field::withObject(
|
||||
@ -102,7 +281,7 @@ class ObjectsService
|
||||
$fieldEntity = Field::withValue(
|
||||
$object, $fieldName, $fieldValue, $this->dateTimeProvider->getTime( $context )
|
||||
);
|
||||
$this->entityManager->persist( $fieldEntity);
|
||||
$this->entityManager->persist( $fieldEntity );
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,189 +314,10 @@ class ObjectsService
|
||||
return null;
|
||||
}
|
||||
$object = json_decode( $response->getBody(), true );
|
||||
if ( ! $object ) {
|
||||
if ( !$object ) {
|
||||
return null;
|
||||
}
|
||||
return $this->persist( $object );
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries for an object with certain field values
|
||||
*
|
||||
* @param array $queryTerms An associative array where the keys are field
|
||||
* names and the values are the values to query for. The value for a key
|
||||
* can also be another associative array, which represents a field
|
||||
* containing a target object that matches the given nested query.
|
||||
* Finally, the value could be a sequential array, which represents a field
|
||||
* containing all of the specified values (the field could also contain more
|
||||
* values).
|
||||
*
|
||||
* @return ActivityPubObject[] The objects that match the query, if any,
|
||||
* ordered by created timestamp from newest to oldest
|
||||
*/
|
||||
public function query( $queryTerms )
|
||||
{
|
||||
$qb = $this->getObjectQuery( $queryTerms );
|
||||
$query = $qb->getQuery();
|
||||
return $query->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the Doctrine QueryBuilder that represents the query
|
||||
*
|
||||
* This function is recursive; it traverses the query tree to build up the
|
||||
* final expression
|
||||
*
|
||||
* @param array $queryTerms The query terms from which to generate the expressions
|
||||
* @param int $nonce A nonce value to differentiate field names
|
||||
* @return QueryBuilder The expression
|
||||
*/
|
||||
protected function getObjectQuery( $queryTerms, $nonce = 0 )
|
||||
{
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
$exprs = array();
|
||||
foreach( $queryTerms as $fieldName => $fieldValue ) {
|
||||
if ( is_array( $fieldValue ) ) {
|
||||
$subQuery = $this->getObjectQuery( $fieldValue, $nonce + 1 );
|
||||
$exprs[] = $qb->expr()->andX(
|
||||
$qb->expr()->like(
|
||||
"field$nonce.name",
|
||||
$qb->expr()->literal( (string) $fieldName )
|
||||
),
|
||||
$qb->expr()->in( "field$nonce.targetObject", $subQuery->getDql())
|
||||
);
|
||||
} else {
|
||||
$exprs[] = $qb->expr()->andX(
|
||||
$qb->expr()->like(
|
||||
"field$nonce.name",
|
||||
$qb->expr()->literal( (string) $fieldName )
|
||||
),
|
||||
$qb->expr()->like(
|
||||
"field$nonce.value",
|
||||
$qb->expr()->literal( $fieldValue )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return $qb->select( "object$nonce" )
|
||||
->from( 'ActivityPub\Entities\ActivityPubObject', "object$nonce" )
|
||||
->join( "object{$nonce}.fields", "field$nonce" )
|
||||
->where( call_user_func_array(
|
||||
array( $qb->expr(), 'orX' ),
|
||||
$exprs
|
||||
) )
|
||||
->groupBy( "object$nonce" )
|
||||
->having( $qb->expr()->eq(
|
||||
$qb->expr()->count( "field$nonce" ),
|
||||
count( $queryTerms )
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an object from the DB by its ActivityPub id
|
||||
*
|
||||
* For internal use only - external callers should use dereference()
|
||||
*
|
||||
* @param string $id The object's id
|
||||
* @return ActivityPubObject|null The object or null
|
||||
* if no object exists with that id
|
||||
*/
|
||||
protected function getObject( $id )
|
||||
{
|
||||
$results = $this->query( array( 'id' => $id ) );
|
||||
if ( ! empty( $results ) ) {
|
||||
return $results[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates $object
|
||||
*
|
||||
* @param string $id The ActivityPub id of the object to update
|
||||
* @param array $updatedFields An array where the key is a field name
|
||||
* to update and the value is the field's new value. If the value is
|
||||
* null, the field will be deleted.
|
||||
*
|
||||
* If the update results in an orphaned anonymous node (an ActivityPubObject
|
||||
* with no 'id' field that no longer has any references to it), then the
|
||||
* orphaned node will be deleted.
|
||||
*
|
||||
* @return ActivityPubObject|null The updated object,
|
||||
* or null if an object with that id isn't in the DB
|
||||
*/
|
||||
public function update( $id, $updatedFields )
|
||||
{
|
||||
$object = $this->getObject( $id );
|
||||
if ( ! $object ) {
|
||||
return null;
|
||||
}
|
||||
foreach( $updatedFields as $fieldName => $newValue ) {
|
||||
if ( $newValue === null && $object->hasField( $fieldName ) ) {
|
||||
$field = $object->getField( $fieldName );
|
||||
if ( $field->hasTargetObject() && ! $field->getTargetObject()->hasField( 'id' ) ) {
|
||||
$targetObject = $field->getTargetObject();
|
||||
// Clear the target object by setting a dummy value
|
||||
$field->setValue( '' );
|
||||
$this->entityManager->remove( $targetObject );
|
||||
}
|
||||
$object->removeField( $field );
|
||||
$this->entityManager->persist( $object );
|
||||
$this->entityManager->remove( $field );
|
||||
} else if ( $object->hasField( $fieldName ) ) {
|
||||
$field = $object->getField( $fieldName );
|
||||
$oldTargetObject = $field->getTargetObject();
|
||||
if ( is_array( $newValue ) ) {
|
||||
$newTargetObject = $this->persist( $newValue, 'objects-service.update' );
|
||||
$field->setTargetObject(
|
||||
$newTargetObject,
|
||||
$this->dateTimeProvider->getTime( 'objects-service.update' )
|
||||
);
|
||||
} else {
|
||||
$field->setValue(
|
||||
$newValue, $this->dateTimeProvider->getTime( 'objects-service.update' )
|
||||
);
|
||||
}
|
||||
if ( $oldTargetObject && ! $oldTargetObject->hasField( 'id' ) ) {
|
||||
$this->entityManager->remove( $oldTargetObject );
|
||||
}
|
||||
$this->entityManager->persist( $field );
|
||||
} else {
|
||||
if ( is_array( $newValue ) ) {
|
||||
$newTargetObject = $this->persist( $newValue );
|
||||
$field = Field::withObject( $object, $fieldName, $newTargetObject );
|
||||
} else {
|
||||
$field = Field::withValue( $object, $fieldName, $newValue );
|
||||
}
|
||||
$this->entityManager->persist( $field );
|
||||
}
|
||||
}
|
||||
$object->setLastUpdated( $this->dateTimeProvider->getTime( 'objects-service.update' ) );
|
||||
$this->entityManager->persist( $object );
|
||||
$this->entityManager->flush();
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully replaces the object referenced by $id by the new $fields
|
||||
*
|
||||
* @param string $id The id of the object to replace
|
||||
* @param array $replacement The new fields to replace the object with
|
||||
* @return ActivityPubObject|null The replaced object, or null
|
||||
* if no object with $id exists
|
||||
*/
|
||||
public function replace( $id, $replacement )
|
||||
{
|
||||
$existing = $this->getObject( $id );
|
||||
if ( ! $existing ) {
|
||||
return null;
|
||||
}
|
||||
foreach ( $existing->getFields() as $field ) {
|
||||
if ( ! array_key_exists( $field->getName(), $replacement ) ) {
|
||||
$replacement[$field->getName()] = null;
|
||||
}
|
||||
}
|
||||
return $this->update( $id, $replacement );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Utils;
|
||||
|
||||
use DateTime;
|
||||
|
@ -10,6 +10,7 @@
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace ActivityPub\Utils;
|
||||
/**
|
||||
* HTTP header utility functions.
|
||||
@ -20,12 +21,14 @@ class HeaderUtils
|
||||
{
|
||||
const DISPOSITION_ATTACHMENT = 'attachment';
|
||||
const DISPOSITION_INLINE = 'inline';
|
||||
|
||||
/**
|
||||
* This class should not be instantiated.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits an HTTP header by one or more separators.
|
||||
*
|
||||
@ -34,17 +37,17 @@ class HeaderUtils
|
||||
* HeaderUtils::split("da, en-gb;q=0.8", ",;")
|
||||
* // => ['da'], ['en-gb', 'q=0.8']]
|
||||
*
|
||||
* @param string $header HTTP header value
|
||||
* @param string $header HTTP header value
|
||||
* @param string $separators List of characters to split on, ordered by
|
||||
* precedence, e.g. ",", ";=", or ",;="
|
||||
*
|
||||
* @return array Nested array with as many levels as there are characters in
|
||||
* $separators
|
||||
*/
|
||||
public static function split($header, $separators)
|
||||
public static function split( $header, $separators )
|
||||
{
|
||||
$quotedSeparators = preg_quote($separators, '/');
|
||||
preg_match_all('
|
||||
$quotedSeparators = preg_quote( $separators, '/' );
|
||||
preg_match_all( '
|
||||
/
|
||||
(?!\s)
|
||||
(?:
|
||||
@ -52,16 +55,55 @@ class HeaderUtils
|
||||
"(?:[^"\\\\]|\\\\.)*(?:"|\\\\|$)
|
||||
|
|
||||
# token
|
||||
[^"'.$quotedSeparators.']+
|
||||
[^"' . $quotedSeparators . ']+
|
||||
)+
|
||||
(?<!\s)
|
||||
|
|
||||
# separator
|
||||
\s*
|
||||
(?<separator>['.$quotedSeparators.'])
|
||||
(?<separator>[' . $quotedSeparators . '])
|
||||
\s*
|
||||
/x', trim($header), $matches, PREG_SET_ORDER);
|
||||
return self::groupParts($matches, $separators);
|
||||
/x', trim( $header ), $matches, PREG_SET_ORDER );
|
||||
return self::groupParts( $matches, $separators );
|
||||
}
|
||||
|
||||
private static function groupParts( array $matches, $separators )
|
||||
{
|
||||
$separator = $separators[0];
|
||||
$partSeparators = substr( $separators, 1 );
|
||||
$i = 0;
|
||||
$partMatches = [];
|
||||
foreach ( $matches as $match ) {
|
||||
if ( isset( $match['separator'] ) && $match['separator'] === $separator ) {
|
||||
++$i;
|
||||
} else {
|
||||
$partMatches[$i][] = $match;
|
||||
}
|
||||
}
|
||||
$parts = [];
|
||||
if ( $partSeparators ) {
|
||||
foreach ( $partMatches as $matches ) {
|
||||
$parts[] = self::groupParts( $matches, $partSeparators );
|
||||
}
|
||||
} else {
|
||||
foreach ( $partMatches as $matches ) {
|
||||
$parts[] = self::unquote( $matches[0][0] );
|
||||
}
|
||||
}
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a quoted string.
|
||||
*
|
||||
* If passed an unquoted string that matches the "token" construct (as
|
||||
* defined in the HTTP specification), it is passed through verbatimly.
|
||||
* @param $s
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
public static function unquote( $s )
|
||||
{
|
||||
return preg_replace( '/\\\\(.)|"/', '$1', $s );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,12 +121,12 @@ class HeaderUtils
|
||||
* @param array $parts
|
||||
* @return array
|
||||
*/
|
||||
public static function combine(array $parts)
|
||||
public static function combine( array $parts )
|
||||
{
|
||||
$assoc = [];
|
||||
foreach ($parts as $part) {
|
||||
$name = strtolower($part[0]);
|
||||
if (isset($part[1])) {
|
||||
foreach ( $parts as $part ) {
|
||||
$name = strtolower( $part[0] );
|
||||
if ( isset( $part[1] ) ) {
|
||||
$value = $part[1];
|
||||
} else {
|
||||
$value = true;
|
||||
@ -94,6 +136,48 @@ class HeaderUtils
|
||||
return $assoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a HTTP Content-Disposition field-value.
|
||||
*
|
||||
* @param string $disposition One of "inline" or "attachment"
|
||||
* @param string $filename A unicode string
|
||||
* @param string $filenameFallback A string containing only ASCII characters that
|
||||
* is semantically equivalent to $filename. If the filename is already ASCII,
|
||||
* it can be omitted, or just copied from $filename
|
||||
*
|
||||
* @return string A string suitable for use as a Content-Disposition field-value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @see RFC 6266
|
||||
*/
|
||||
public static function makeDisposition( $disposition, $filename, $filenameFallback = '' )
|
||||
{
|
||||
if ( !\in_array( $disposition, [ self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE ] ) ) {
|
||||
throw new \InvalidArgumentException( sprintf( 'The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE ) );
|
||||
}
|
||||
if ( '' === $filenameFallback ) {
|
||||
$filenameFallback = $filename;
|
||||
}
|
||||
// filenameFallback is not ASCII.
|
||||
if ( !preg_match( '/^[\x20-\x7e]*$/', $filenameFallback ) ) {
|
||||
throw new \InvalidArgumentException( 'The filename fallback must only contain ASCII characters.' );
|
||||
}
|
||||
// percent characters aren't safe in fallback.
|
||||
if ( false !== strpos( $filenameFallback, '%' ) ) {
|
||||
throw new \InvalidArgumentException( 'The filename fallback cannot contain the "%" character.' );
|
||||
}
|
||||
// path separators aren't allowed in either.
|
||||
if ( false !== strpos( $filename, '/' ) || false !== strpos( $filename, '\\' ) || false !== strpos( $filenameFallback, '/' ) || false !== strpos( $filenameFallback, '\\' ) ) {
|
||||
throw new \InvalidArgumentException( 'The filename and the fallback cannot contain the "/" and "\\" characters.' );
|
||||
}
|
||||
$params = [ 'filename' => $filenameFallback ];
|
||||
if ( $filename !== $filenameFallback ) {
|
||||
$params['filename*'] = "utf-8''" . rawurlencode( $filename );
|
||||
}
|
||||
return $disposition . '; ' . self::toString( $params, ';' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins an associative array into a string for use in an HTTP header.
|
||||
*
|
||||
@ -109,17 +193,17 @@ class HeaderUtils
|
||||
* @param $separator
|
||||
* @return string
|
||||
*/
|
||||
public static function toString(array $assoc, $separator)
|
||||
public static function toString( array $assoc, $separator )
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($assoc as $name => $value) {
|
||||
if (true === $value) {
|
||||
foreach ( $assoc as $name => $value ) {
|
||||
if ( true === $value ) {
|
||||
$parts[] = $name;
|
||||
} else {
|
||||
$parts[] = $name.'='.self::quote($value);
|
||||
$parts[] = $name . '=' . self::quote( $value );
|
||||
}
|
||||
}
|
||||
return implode($separator.' ', $parts);
|
||||
return implode( $separator . ' ', $parts );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,90 +215,11 @@ class HeaderUtils
|
||||
* @param $s
|
||||
* @return string
|
||||
*/
|
||||
public static function quote($s)
|
||||
public static function quote( $s )
|
||||
{
|
||||
if (preg_match('/^[a-z0-9!#$%&\'*.^_`|~-]+$/i', $s)) {
|
||||
if ( preg_match( '/^[a-z0-9!#$%&\'*.^_`|~-]+$/i', $s ) ) {
|
||||
return $s;
|
||||
}
|
||||
return '"'.addcslashes($s, '"\\"').'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a quoted string.
|
||||
*
|
||||
* If passed an unquoted string that matches the "token" construct (as
|
||||
* defined in the HTTP specification), it is passed through verbatimly.
|
||||
* @param $s
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
public static function unquote($s)
|
||||
{
|
||||
return preg_replace('/\\\\(.)|"/', '$1', $s);
|
||||
}
|
||||
/**
|
||||
* Generates a HTTP Content-Disposition field-value.
|
||||
*
|
||||
* @param string $disposition One of "inline" or "attachment"
|
||||
* @param string $filename A unicode string
|
||||
* @param string $filenameFallback A string containing only ASCII characters that
|
||||
* is semantically equivalent to $filename. If the filename is already ASCII,
|
||||
* it can be omitted, or just copied from $filename
|
||||
*
|
||||
* @return string A string suitable for use as a Content-Disposition field-value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @see RFC 6266
|
||||
*/
|
||||
public static function makeDisposition($disposition, $filename, $filenameFallback = '')
|
||||
{
|
||||
if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE])) {
|
||||
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
|
||||
}
|
||||
if ('' === $filenameFallback) {
|
||||
$filenameFallback = $filename;
|
||||
}
|
||||
// filenameFallback is not ASCII.
|
||||
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
|
||||
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
|
||||
}
|
||||
// percent characters aren't safe in fallback.
|
||||
if (false !== strpos($filenameFallback, '%')) {
|
||||
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
|
||||
}
|
||||
// path separators aren't allowed in either.
|
||||
if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
|
||||
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
|
||||
}
|
||||
$params = ['filename' => $filenameFallback];
|
||||
if ($filename !== $filenameFallback) {
|
||||
$params['filename*'] = "utf-8''".rawurlencode($filename);
|
||||
}
|
||||
return $disposition.'; '.self::toString($params, ';');
|
||||
}
|
||||
private static function groupParts(array $matches, $separators)
|
||||
{
|
||||
$separator = $separators[0];
|
||||
$partSeparators = substr($separators, 1);
|
||||
$i = 0;
|
||||
$partMatches = [];
|
||||
foreach ($matches as $match) {
|
||||
if (isset($match['separator']) && $match['separator'] === $separator) {
|
||||
++$i;
|
||||
} else {
|
||||
$partMatches[$i][] = $match;
|
||||
}
|
||||
}
|
||||
$parts = [];
|
||||
if ($partSeparators) {
|
||||
foreach ($partMatches as $matches) {
|
||||
$parts[] = self::groupParts($matches, $partSeparators);
|
||||
}
|
||||
} else {
|
||||
foreach ($partMatches as $matches) {
|
||||
$parts[] = self::unquote($matches[0][0]);
|
||||
}
|
||||
}
|
||||
return $parts;
|
||||
return '"' . addcslashes( $s, '"\\"' ) . '"';
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Utils;
|
||||
|
||||
class RandomProvider
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Utils;
|
||||
|
||||
class Util
|
||||
@ -11,8 +12,8 @@ class Util
|
||||
*/
|
||||
public static function isAssoc( array $arr )
|
||||
{
|
||||
if (array() === $arr) return false;
|
||||
return array_keys($arr) !== range(0, count($arr) - 1);
|
||||
if ( array() === $arr ) return false;
|
||||
return array_keys( $arr ) !== range( 0, count( $arr ) - 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,7 +26,7 @@ class Util
|
||||
public static function arrayKeysExist( array $arr, array $keys )
|
||||
{
|
||||
foreach ( $keys as $key ) {
|
||||
if ( ! array_key_exists( $key, $arr ) ) {
|
||||
if ( !array_key_exists( $key, $arr ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Activities;
|
||||
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
|
@ -1,18 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Activities;
|
||||
|
||||
use ActivityPub\Auth\AuthService;
|
||||
use ActivityPub\Activities\CreateHandler;
|
||||
use ActivityPub\Activities\InboxActivityEvent;
|
||||
use ActivityPub\Activities\OutboxActivityEvent;
|
||||
use ActivityPub\Auth\AuthService;
|
||||
use ActivityPub\Objects\CollectionsService;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Objects\IdProvider;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Utils\SimpleDateTimeProvider;
|
||||
use GuzzleHttp\Client;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
@ -30,7 +31,7 @@ class CreateHandlerTest extends APTestCase
|
||||
$idProvider = $this->getMock( IdProvider::class );
|
||||
// TODO provision mocks
|
||||
$collectionsService = new CollectionsService(
|
||||
4,
|
||||
4,
|
||||
$this->getMock( AuthService::class ),
|
||||
new ContextProvider(),
|
||||
$this->getMock( Client::class ),
|
||||
|
@ -1,36 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Activities;
|
||||
|
||||
use ActivityPub\Activities\DeleteHandler;
|
||||
use ActivityPub\Activities\InboxActivityEvent;
|
||||
use ActivityPub\Activities\OutboxActivityEvent;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Test\TestUtils\TestDateTimeProvider;
|
||||
use DateTime;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
|
||||
class DeleteHandlerTest extends APTestCase
|
||||
{
|
||||
private static function getObjects()
|
||||
{
|
||||
return array(
|
||||
'https://elsewhere.com/objects/1' => array(
|
||||
'id' => 'https://elsewhere.com/objects/1',
|
||||
'type' => 'Note',
|
||||
'attributedTo' => 'https://elsewhere.com/actors/1',
|
||||
),
|
||||
'https://example.com/objects/1' => array(
|
||||
'id' => 'https://example.com/objects/1',
|
||||
'type' => 'Note',
|
||||
'attributedTo' => 'https://example.com/actors/1',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testDeleteHandler()
|
||||
{
|
||||
$testCases = array(
|
||||
@ -139,12 +124,12 @@ class DeleteHandlerTest extends APTestCase
|
||||
),
|
||||
) );
|
||||
$objectsService = $this->getMockBuilder( ObjectsService::class )
|
||||
->disableOriginalConstructor()
|
||||
->setMethods( array( 'dereference', 'replace' ) )
|
||||
->getMock();
|
||||
$objectsService->method( 'dereference' )->will( $this->returnCallback(
|
||||
function( $id ) {
|
||||
if ( array_key_exists( $id, self::getObjects()) ) {
|
||||
->disableOriginalConstructor()
|
||||
->setMethods( array( 'dereference', 'replace' ) )
|
||||
->getMock();
|
||||
$objectsService->method( 'dereference' )->will( $this->returnCallback(
|
||||
function ( $id ) {
|
||||
if ( array_key_exists( $id, self::getObjects() ) ) {
|
||||
$objects = self::getObjects();
|
||||
return TestActivityPubObject::fromArray( $objects[$id] );
|
||||
}
|
||||
@ -173,5 +158,21 @@ class DeleteHandlerTest extends APTestCase
|
||||
$request->attributes->add( $attributes );
|
||||
return $request;
|
||||
}
|
||||
|
||||
private static function getObjects()
|
||||
{
|
||||
return array(
|
||||
'https://elsewhere.com/objects/1' => array(
|
||||
'id' => 'https://elsewhere.com/objects/1',
|
||||
'type' => 'Note',
|
||||
'attributedTo' => 'https://elsewhere.com/actors/1',
|
||||
),
|
||||
'https://example.com/objects/1' => array(
|
||||
'id' => 'https://example.com/objects/1',
|
||||
'type' => 'Note',
|
||||
'attributedTo' => 'https://example.com/actors/1',
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Activities;
|
||||
|
||||
use ActivityPub\Activities\FollowHandler;
|
||||
use ActivityPub\Activities\InboxActivityEvent;
|
||||
use ActivityPub\Activities\OutboxActivityEvent;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
@ -28,9 +29,8 @@ class FollowHandlerTest extends APTestCase
|
||||
'type' => 'Follow',
|
||||
'object' => 'https://example.com/actor/1',
|
||||
);
|
||||
$eventDispatcher->addListener( OutboxActivityEvent::NAME, function( $event, $name )
|
||||
use ( &$outboxDispatched, $actor, $follow )
|
||||
{
|
||||
$eventDispatcher->addListener( OutboxActivityEvent::NAME, function ( $event, $name )
|
||||
use ( &$outboxDispatched, $actor, $follow ) {
|
||||
$this->assertEquals( OutboxActivityEvent::NAME, $name );
|
||||
$outboxDispatched = true;
|
||||
$accept = array(
|
||||
@ -81,11 +81,10 @@ class FollowHandlerTest extends APTestCase
|
||||
'type' => 'Follow',
|
||||
'object' => 'https://example.com/actor/2',
|
||||
);
|
||||
$eventDispatcher->addListener( OutboxActivityEvent::NAME, function()
|
||||
use ( &$outboxDispatched )
|
||||
{
|
||||
$outboxDispatched = true;
|
||||
} );
|
||||
$eventDispatcher->addListener( OutboxActivityEvent::NAME, function ()
|
||||
use ( &$outboxDispatched ) {
|
||||
$outboxDispatched = true;
|
||||
} );
|
||||
$eventDispatcher->dispatch( InboxActivityEvent::NAME, new InboxActivityEvent(
|
||||
$follow,
|
||||
$actor,
|
||||
|
@ -1,11 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Activities;
|
||||
|
||||
use ActivityPub\Activities\OutboxActivityEvent;
|
||||
use ActivityPub\Activities\NonActivityHandler;
|
||||
use ActivityPub\Activities\OutboxActivityEvent;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class NonActivityHandlerTest extends APTestCase
|
||||
|
@ -1,39 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Activities;
|
||||
|
||||
use ActivityPub\Activities\InboxActivityEvent;
|
||||
use ActivityPub\Activities\OutboxActivityEvent;
|
||||
use ActivityPub\Activities\UpdateHandler;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
|
||||
class UpdateHandlerTest extends APTestCase
|
||||
{
|
||||
private static function getObjects()
|
||||
{
|
||||
return array(
|
||||
'https://elsewhere.com/objects/1' => array(
|
||||
'id' => 'https://elsewhere.com/objects/1',
|
||||
'attributedTo' => 'https://elsewhere.com/actors/1',
|
||||
),
|
||||
'https://example.com/objects/1' => array(
|
||||
'id' => 'https://example.com/objects/1',
|
||||
'attributedTo' => 'https://example.com/actors/1',
|
||||
'type' => 'Note',
|
||||
'content' => 'This is a note',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -44,7 +28,7 @@ class UpdateHandlerTest extends APTestCase
|
||||
$this->objects = self::getObjects();
|
||||
$objectsService = $this->getMock( ObjectsService::class );
|
||||
$objectsService->method( 'dereference' )->will( $this->returnCallback(
|
||||
function( $id ) {
|
||||
function ( $id ) {
|
||||
if ( array_key_exists( $id, $this->objects ) ) {
|
||||
return TestActivityPubObject::fromArray( $this->objects[$id] );
|
||||
}
|
||||
@ -52,7 +36,7 @@ class UpdateHandlerTest extends APTestCase
|
||||
}
|
||||
) );
|
||||
$objectsService->method( 'update' )->will( $this->returnCallback(
|
||||
function( $id, $updateFields ) {
|
||||
function ( $id, $updateFields ) {
|
||||
if ( array_key_exists( $id, $this->objects ) ) {
|
||||
$existing = $this->objects[$id];
|
||||
foreach ( $updateFields as $field => $newValue ) {
|
||||
@ -72,6 +56,22 @@ class UpdateHandlerTest extends APTestCase
|
||||
$this->eventDispatcher->addSubscriber( $updateHandler );
|
||||
}
|
||||
|
||||
private static function getObjects()
|
||||
{
|
||||
return array(
|
||||
'https://elsewhere.com/objects/1' => array(
|
||||
'id' => 'https://elsewhere.com/objects/1',
|
||||
'attributedTo' => 'https://elsewhere.com/actors/1',
|
||||
),
|
||||
'https://example.com/objects/1' => array(
|
||||
'id' => 'https://example.com/objects/1',
|
||||
'attributedTo' => 'https://example.com/actors/1',
|
||||
'type' => 'Note',
|
||||
'content' => 'This is a note',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testUpdateHandler()
|
||||
{
|
||||
$testCases = array(
|
||||
|
@ -1,11 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Activities;
|
||||
|
||||
use ActivityPub\Activities\InboxActivityEvent;
|
||||
use ActivityPub\Activities\OutboxActivityEvent;
|
||||
use ActivityPub\Activities\ValidationHandler;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
@ -16,13 +17,14 @@ class ValidationHandlerTest extends APTestCase
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->eventDispatcher = new EventDispatcher();
|
||||
$validationHandler = new ValidationHandler();
|
||||
$this->eventDispatcher->addSubscriber( $validationHandler );
|
||||
}
|
||||
|
||||
public function testValidationHandler()
|
||||
{
|
||||
$testCases = array(
|
||||
@ -162,7 +164,7 @@ class ValidationHandlerTest extends APTestCase
|
||||
$event = $testCase['event'];
|
||||
if ( array_key_exists( 'expectedException', $testCase ) ) {
|
||||
$expectedExceptionMessage = '';
|
||||
if ( array_key_exists( 'expectedExceptionMessage', $testCase )) {
|
||||
if ( array_key_exists( 'expectedExceptionMessage', $testCase ) ) {
|
||||
$expectedExceptionMessage = $testCase['expectedExceptionMessage'];
|
||||
}
|
||||
$this->setExpectedException(
|
||||
|
@ -1,39 +1,43 @@
|
||||
<?php /** @noinspection PhpUnhandledExceptionInspection */
|
||||
|
||||
namespace ActivityPub\Test;
|
||||
|
||||
|
||||
use ActivityPub\ActivityPub;
|
||||
use ActivityPub\Config\ActivityPubConfig;
|
||||
use ActivityPub\Test\TestConfig\SQLiteTestCase;
|
||||
use ActivityPub\Test\TestConfig\ArrayDataSet;
|
||||
use ActivityPub\Test\TestConfig\SQLiteTestCase;
|
||||
|
||||
class ActivityPubTest extends SQLiteTestCase
|
||||
{
|
||||
public function getDataSet() {
|
||||
public function getDataSet()
|
||||
{
|
||||
return new ArrayDataSet( array() );
|
||||
}
|
||||
|
||||
public function testItCreatesSchema() {
|
||||
|
||||
public function testItCreatesSchema()
|
||||
{
|
||||
$this->assertTrue( file_exists( $this->getDbPath() ) );
|
||||
}
|
||||
|
||||
protected function getDbPath()
|
||||
{
|
||||
return dirname( __FILE__ ) . '/db.sqlite';
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testItCreatesSchema
|
||||
*/
|
||||
public function testItUpdatesSchema() {
|
||||
public function testItUpdatesSchema()
|
||||
{
|
||||
$config = ActivityPubConfig::createBuilder()
|
||||
->setDbConnectionParams( array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => $this->getDbPath(),
|
||||
) )
|
||||
->build();
|
||||
->setDbConnectionParams( array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => $this->getDbPath(),
|
||||
) )
|
||||
->build();
|
||||
$activityPub = new ActivityPub( $config );
|
||||
$activityPub->updateSchema();
|
||||
$this->assertTrue( file_exists( $this->getDbPath() ) );
|
||||
}
|
||||
|
||||
protected function getDbPath() {
|
||||
return dirname( __FILE__ ) . '/db.sqlite';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,13 @@
|
||||
namespace ActivityPub\Test\Auth;
|
||||
|
||||
use ActivityPub\Auth\AuthListener;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
class AuthListenerTest extends APTestCase
|
||||
{
|
||||
@ -28,21 +28,12 @@ class AuthListenerTest extends APTestCase
|
||||
) ) );
|
||||
}
|
||||
|
||||
public function getEvent()
|
||||
{
|
||||
$kernel = $this->getMock( HttpKernelInterface::class );
|
||||
$request = Request::create( 'https://example.com/foo', Request::METHOD_GET );
|
||||
return new GetResponseEvent(
|
||||
$kernel, $request, HttpKernelInterface::MASTER_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
public function testAuthListener()
|
||||
{
|
||||
$testCases = array(
|
||||
array(
|
||||
'id' => 'basicTest',
|
||||
'authFunction' => function() {
|
||||
'authFunction' => function () {
|
||||
return 'https://example.com/actor/1';
|
||||
},
|
||||
'expectedAttributes' => array(
|
||||
@ -53,7 +44,7 @@ class AuthListenerTest extends APTestCase
|
||||
),
|
||||
array(
|
||||
'id' => 'existingActorTest',
|
||||
'authFunction' => function() {
|
||||
'authFunction' => function () {
|
||||
return 'https://example.com/actor/1';
|
||||
},
|
||||
'requestAttributes' => array(
|
||||
@ -69,7 +60,7 @@ class AuthListenerTest extends APTestCase
|
||||
),
|
||||
array(
|
||||
'id' => 'defaultAuthTest',
|
||||
'authFunction' => function() {
|
||||
'authFunction' => function () {
|
||||
return false;
|
||||
},
|
||||
'expectedAttributes' => array(),
|
||||
@ -108,5 +99,14 @@ class AuthListenerTest extends APTestCase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getEvent()
|
||||
{
|
||||
$kernel = $this->getMock( HttpKernelInterface::class );
|
||||
$request = Request::create( 'https://example.com/foo', Request::METHOD_GET );
|
||||
return new GetResponseEvent(
|
||||
$kernel, $request, HttpKernelInterface::MASTER_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Auth;
|
||||
|
||||
use ActivityPub\Auth\AuthService;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class AuthServiceTest extends APTestCase
|
||||
|
@ -1,16 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Auth;
|
||||
|
||||
use DateTime;
|
||||
use ActivityPub\Auth\SignatureListener;
|
||||
use ActivityPub\Crypto\HttpSignatureService;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Test\TestUtils\TestDateTimeProvider;
|
||||
use DateTime;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
class SignatureListenerTest extends APTestCase
|
||||
{
|
||||
@ -28,20 +29,6 @@ oYi+1hqp1fIekaxsyQIDAQAB
|
||||
*/
|
||||
private $signatureListener;
|
||||
|
||||
private static function getActor()
|
||||
{
|
||||
return array( 'id' => self::ACTOR_ID );
|
||||
}
|
||||
|
||||
private static function getKey()
|
||||
{
|
||||
return array(
|
||||
'id' => self::KEY_ID,
|
||||
'owner' => 'https://example.com/actor/1',
|
||||
'publicKeyPem' => self::PUBLIC_KEY,
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$dateTimeProvider = new TestDateTimeProvider( array(
|
||||
@ -53,35 +40,26 @@ oYi+1hqp1fIekaxsyQIDAQAB
|
||||
$objectsService = $this->getMock( ObjectsService::class );
|
||||
$objectsService->method( 'dereference' )
|
||||
->will( $this->returnValueMap( array(
|
||||
array( self::KEY_ID, TestActivityPubObject::fromArray( self::getKey()) ),
|
||||
array( self::ACTOR_ID, TestActivityPubObject::fromArray( self::getActor()) ),
|
||||
array( self::KEY_ID, TestActivityPubObject::fromArray( self::getKey() ) ),
|
||||
array( self::ACTOR_ID, TestActivityPubObject::fromArray( self::getActor() ) ),
|
||||
) ) );
|
||||
$this->signatureListener = new SignatureListener(
|
||||
$httpSignatureService, $objectsService
|
||||
);
|
||||
}
|
||||
|
||||
private function getEvent()
|
||||
private static function getKey()
|
||||
{
|
||||
$kernel = $this->getMock( HttpKernelInterface::class );
|
||||
$request = Request::create(
|
||||
'https://example.com/foo?param=value&pet=dog',
|
||||
Request::METHOD_POST,
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
'{"hello": "world"}'
|
||||
return array(
|
||||
'id' => self::KEY_ID,
|
||||
'owner' => 'https://example.com/actor/1',
|
||||
'publicKeyPem' => self::PUBLIC_KEY,
|
||||
);
|
||||
$request->headers->set( 'host', 'example.com' );
|
||||
$request->headers->set( 'content-type', 'application/json' );
|
||||
$request->headers->set(
|
||||
'digest', 'SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
|
||||
);
|
||||
$request->headers->set( 'content-length', 18 );
|
||||
$request->headers->set( 'date', 'Sun, 05 Jan 2014 21:31:40 GMT' );
|
||||
$event = new GetResponseEvent( $kernel, $request, HttpKernelInterface::MASTER_REQUEST );
|
||||
return $event;
|
||||
}
|
||||
|
||||
private static function getActor()
|
||||
{
|
||||
return array( 'id' => self::ACTOR_ID );
|
||||
}
|
||||
|
||||
public function testSignatureListener()
|
||||
@ -136,12 +114,12 @@ oYi+1hqp1fIekaxsyQIDAQAB
|
||||
foreach ( $testCases as $testCase ) {
|
||||
$event = $this->getEvent();
|
||||
if ( array_key_exists( 'headers', $testCase ) ) {
|
||||
foreach( $testCase['headers'] as $header => $value ) {
|
||||
foreach ( $testCase['headers'] as $header => $value ) {
|
||||
$event->getRequest()->headers->set( $header, $value );
|
||||
}
|
||||
}
|
||||
if ( array_key_exists( 'requestAttributes', $testCase ) ) {
|
||||
foreach( $testCase['requestAttributes'] as $attribute => $value ) {
|
||||
foreach ( $testCase['requestAttributes'] as $attribute => $value ) {
|
||||
$event->getRequest()->attributes->set( $attribute, $value );
|
||||
}
|
||||
}
|
||||
@ -153,5 +131,28 @@ oYi+1hqp1fIekaxsyQIDAQAB
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function getEvent()
|
||||
{
|
||||
$kernel = $this->getMock( HttpKernelInterface::class );
|
||||
$request = Request::create(
|
||||
'https://example.com/foo?param=value&pet=dog',
|
||||
Request::METHOD_POST,
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
'{"hello": "world"}'
|
||||
);
|
||||
$request->headers->set( 'host', 'example.com' );
|
||||
$request->headers->set( 'content-type', 'application/json' );
|
||||
$request->headers->set(
|
||||
'digest', 'SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
|
||||
);
|
||||
$request->headers->set( 'content-length', 18 );
|
||||
$request->headers->set( 'date', 'Sun, 05 Jan 2014 21:31:40 GMT' );
|
||||
$event = new GetResponseEvent( $kernel, $request, HttpKernelInterface::MASTER_REQUEST );
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@ namespace ActivityPub\Test\Config;
|
||||
use ActivityPub\Config\ActivityPubConfig;
|
||||
use ActivityPub\Config\ActivityPubModule;
|
||||
use ActivityPub\Http\Router;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
class ActivityPubModuleTest extends APTestCase
|
||||
{
|
||||
@ -18,14 +18,14 @@ class ActivityPubModuleTest extends APTestCase
|
||||
public function setUp()
|
||||
{
|
||||
$config = ActivityPubConfig::createBuilder()
|
||||
->setDbConnectionParams( array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => ':memory:',
|
||||
) )
|
||||
->build();
|
||||
->setDbConnectionParams( array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => ':memory:',
|
||||
) )
|
||||
->build();
|
||||
$this->module = new ActivityPubModule( $config );
|
||||
}
|
||||
|
||||
|
||||
public function testItInjects()
|
||||
{
|
||||
$entityManager = $this->module->get( EntityManager::class );
|
||||
|
@ -1,21 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Controllers;
|
||||
|
||||
use ActivityPub\Auth\AuthService;
|
||||
use ActivityPub\Controllers\GetController;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Objects\CollectionsService;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Utils\SimpleDateTimeProvider;
|
||||
use GuzzleHttp\Client;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
|
||||
class GetControllerTest extends APTestCase
|
||||
{
|
||||
/**
|
||||
* @var GetController
|
||||
*/
|
||||
private $getController;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $objects;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->objects = self::getObjects();
|
||||
$objectsService = $this->getMock( ObjectsService::class );
|
||||
$objectsService->method( 'dereference' )->will(
|
||||
$this->returnCallback( function ( $uri ) {
|
||||
if ( array_key_exists( $uri, $this->objects ) ) {
|
||||
return TestActivityPubObject::fromArray( $this->objects[$uri] );
|
||||
}
|
||||
return null;
|
||||
} )
|
||||
);
|
||||
$authService = new AuthService();
|
||||
$contextProvider = new ContextProvider();
|
||||
$httpClient = $this->getMock( Client::class );
|
||||
$collectionsService = new CollectionsService(
|
||||
4, $authService, $contextProvider, $httpClient, new SimpleDateTimeProvider()
|
||||
);
|
||||
$this->getController = new GetController(
|
||||
$objectsService, $collectionsService, $authService
|
||||
);
|
||||
}
|
||||
|
||||
private static function getObjects()
|
||||
{
|
||||
return array(
|
||||
@ -58,39 +91,6 @@ class GetControllerTest extends APTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var GetController
|
||||
*/
|
||||
private $getController;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $objects;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->objects = self::getObjects();
|
||||
$objectsService = $this->getMock( ObjectsService::class );
|
||||
$objectsService->method( 'dereference' )->will(
|
||||
$this->returnCallback( function( $uri ) {
|
||||
if ( array_key_exists( $uri, $this->objects) ) {
|
||||
return TestActivityPubObject::fromArray( $this->objects[$uri] );
|
||||
}
|
||||
return null;
|
||||
})
|
||||
);
|
||||
$authService = new AuthService();
|
||||
$contextProvider = new ContextProvider();
|
||||
$httpClient = $this->getMock( Client::class );
|
||||
$collectionsService = new CollectionsService(
|
||||
4, $authService, $contextProvider, $httpClient, new SimpleDateTimeProvider()
|
||||
);
|
||||
$this->getController = new GetController(
|
||||
$objectsService, $collectionsService, $authService
|
||||
);
|
||||
}
|
||||
|
||||
public function testItRendersPersistedObject()
|
||||
{
|
||||
$request = Request::create( 'https://example.com/objects/1' );
|
||||
|
@ -1,12 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Controllers;
|
||||
|
||||
use ActivityPub\Activities\InboxActivityEvent;
|
||||
use ActivityPub\Activities\OutboxActivityEvent;
|
||||
use ActivityPub\Controllers\PostController;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
@ -15,48 +16,10 @@ use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
|
||||
class PostControllerTest extends APTestCase
|
||||
{
|
||||
private static function getObjects()
|
||||
{
|
||||
return array(
|
||||
'https://example.com/actor/1/inbox' => array(
|
||||
'id' => 'https://example.com/actor/1/inbox',
|
||||
),
|
||||
'https://example.com/actor/1/outbox' => array(
|
||||
'id' => 'https://example.com/actor/1/outbox',
|
||||
),
|
||||
'https://example.com/actor/1' => array(
|
||||
'id' => 'https://example.com/actor/1',
|
||||
'inbox' => array(
|
||||
'id' => 'https://example.com/actor/1/inbox',
|
||||
),
|
||||
'outbox' => array(
|
||||
'id' => 'https://example.com/actor/1/outbox',
|
||||
),
|
||||
),
|
||||
'https://elsewhere.com/actor/1' => array(
|
||||
'id' => 'https://elsewhere.com/actor/1',
|
||||
),
|
||||
);
|
||||
}
|
||||
private static function getRefs()
|
||||
{
|
||||
return array(
|
||||
'https://example.com/actor/1/inbox' => array(
|
||||
'field' => 'inbox',
|
||||
'referencingObject' => 'https://example.com/actor/1',
|
||||
),
|
||||
'https://example.com/actor/1/outbox' => array(
|
||||
'field' => 'outbox',
|
||||
'referencingObject' => 'https://example.com/actor/1',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $objects;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -68,9 +31,9 @@ class PostControllerTest extends APTestCase
|
||||
$this->refs = self::getRefs();
|
||||
$objectsService = $this->getMock( ObjectsService::class );
|
||||
$objectsService->method( 'query' )->will(
|
||||
$this->returnCallback( function( $query ) {
|
||||
$this->returnCallback( function ( $query ) {
|
||||
if ( array_key_exists( 'id', $query ) &&
|
||||
array_key_exists( $query['id'], $this->objects ) ) {
|
||||
array_key_exists( $query['id'], $this->objects ) ) {
|
||||
$object = TestActivityPubObject::fromArray(
|
||||
$this->objects[$query['id']]
|
||||
);
|
||||
@ -89,7 +52,7 @@ class PostControllerTest extends APTestCase
|
||||
} )
|
||||
);
|
||||
$objectsService->method( 'dereference' )->will(
|
||||
$this->returnCallback( function( $id ) {
|
||||
$this->returnCallback( function ( $id ) {
|
||||
if ( array_key_exists( $id, $this->objects ) ) {
|
||||
return TestActivityPubObject::fromArray( $this->objects[$id] );
|
||||
} else {
|
||||
@ -235,14 +198,14 @@ class PostControllerTest extends APTestCase
|
||||
);
|
||||
foreach ( $testCases as $testCase ) {
|
||||
$eventDispatcher = $this->getMockBuilder( EventDispatcher::class )
|
||||
->setMethods( array( 'dispatch' ) )
|
||||
->getMock();
|
||||
->setMethods( array( 'dispatch' ) )
|
||||
->getMock();
|
||||
if ( array_key_exists( 'expectedEvent', $testCase ) ) {
|
||||
$eventDispatcher->expects( $this->once() )
|
||||
->method( 'dispatch' )
|
||||
->with(
|
||||
$this->equalTo($testCase['expectedEventName']),
|
||||
$this->equalTo($testCase['expectedEvent'])
|
||||
$this->equalTo( $testCase['expectedEventName'] ),
|
||||
$this->equalTo( $testCase['expectedEvent'] )
|
||||
);
|
||||
}
|
||||
$postController = new PostController( $eventDispatcher, $objectsService );
|
||||
@ -254,6 +217,44 @@ class PostControllerTest extends APTestCase
|
||||
}
|
||||
}
|
||||
|
||||
private static function getObjects()
|
||||
{
|
||||
return array(
|
||||
'https://example.com/actor/1/inbox' => array(
|
||||
'id' => 'https://example.com/actor/1/inbox',
|
||||
),
|
||||
'https://example.com/actor/1/outbox' => array(
|
||||
'id' => 'https://example.com/actor/1/outbox',
|
||||
),
|
||||
'https://example.com/actor/1' => array(
|
||||
'id' => 'https://example.com/actor/1',
|
||||
'inbox' => array(
|
||||
'id' => 'https://example.com/actor/1/inbox',
|
||||
),
|
||||
'outbox' => array(
|
||||
'id' => 'https://example.com/actor/1/outbox',
|
||||
),
|
||||
),
|
||||
'https://elsewhere.com/actor/1' => array(
|
||||
'id' => 'https://elsewhere.com/actor/1',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private static function getRefs()
|
||||
{
|
||||
return array(
|
||||
'https://example.com/actor/1/inbox' => array(
|
||||
'field' => 'inbox',
|
||||
'referencingObject' => 'https://example.com/actor/1',
|
||||
),
|
||||
'https://example.com/actor/1/outbox' => array(
|
||||
'field' => 'outbox',
|
||||
'referencingObject' => 'https://example.com/actor/1',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private function makeRequest( $uri, $method, $body, $attributes )
|
||||
{
|
||||
$request = Request::create(
|
||||
|
@ -1,11 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Crypto;
|
||||
|
||||
use DateTime;
|
||||
use ActivityPub\Crypto\HttpSignatureService;
|
||||
use ActivityPub\Test\TestUtils\TestDateTimeProvider;
|
||||
use GuzzleHttp\Psr7\Request as PsrRequest;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestDateTimeProvider;
|
||||
use DateTime;
|
||||
use GuzzleHttp\Psr7\Request as PsrRequest;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class HttpSignatureServiceTest extends APTestCase
|
||||
@ -48,42 +49,6 @@ G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
|
||||
$this->httpSignatureService = new HttpSignatureService( $dateTimeProvider );
|
||||
}
|
||||
|
||||
private static function getSymfonyRequest()
|
||||
{
|
||||
$request = Request::create(
|
||||
'https://example.com/foo?param=value&pet=dog',
|
||||
Request::METHOD_POST,
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
'{"hello": "world"}'
|
||||
);
|
||||
$request->headers->set( 'host', 'example.com' );
|
||||
$request->headers->set( 'content-type', 'application/json' );
|
||||
$request->headers->set(
|
||||
'digest', 'SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
|
||||
);
|
||||
$request->headers->set( 'content-length', 18 );
|
||||
$request->headers->set( 'date', 'Sun, 05 Jan 2014 21:31:40 GMT' );
|
||||
return $request;
|
||||
}
|
||||
|
||||
private static function getPsrRequest()
|
||||
{
|
||||
$headers = array(
|
||||
'Host' => 'example.com',
|
||||
'Content-Type' => 'application/json',
|
||||
'Digest' => 'SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=',
|
||||
'Content-Length' => 18,
|
||||
'Date' => 'Sun, 05 Jan 2014 21:31:40 GMT'
|
||||
);
|
||||
$body = '{"hello": "world"}';
|
||||
return new PsrRequest(
|
||||
'POST', 'https://example.com/foo?param=value&pet=dog', $headers, $body
|
||||
);
|
||||
}
|
||||
|
||||
public function testItVerifies()
|
||||
{
|
||||
$testCases = array(
|
||||
@ -138,7 +103,7 @@ G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
|
||||
'id' => 'headerMissing',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length x-foo-header",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
|
||||
),
|
||||
),
|
||||
'expectedResult' => false,
|
||||
),
|
||||
array(
|
||||
@ -194,6 +159,27 @@ G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
|
||||
}
|
||||
}
|
||||
|
||||
private static function getSymfonyRequest()
|
||||
{
|
||||
$request = Request::create(
|
||||
'https://example.com/foo?param=value&pet=dog',
|
||||
Request::METHOD_POST,
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
'{"hello": "world"}'
|
||||
);
|
||||
$request->headers->set( 'host', 'example.com' );
|
||||
$request->headers->set( 'content-type', 'application/json' );
|
||||
$request->headers->set(
|
||||
'digest', 'SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
|
||||
);
|
||||
$request->headers->set( 'content-length', 18 );
|
||||
$request->headers->set( 'date', 'Sun, 05 Jan 2014 21:31:40 GMT' );
|
||||
return $request;
|
||||
}
|
||||
|
||||
public function testItSigns()
|
||||
{
|
||||
$testCases = array(
|
||||
@ -223,7 +209,7 @@ G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
|
||||
$request, self::PRIVATE_KEY, $testCase['keyId'], $testCase['headers']
|
||||
);
|
||||
} else {
|
||||
$actual= $this->httpSignatureService->sign(
|
||||
$actual = $this->httpSignatureService->sign(
|
||||
$request, self::PRIVATE_KEY, $testCase['keyId']
|
||||
);
|
||||
}
|
||||
@ -232,5 +218,20 @@ G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static function getPsrRequest()
|
||||
{
|
||||
$headers = array(
|
||||
'Host' => 'example.com',
|
||||
'Content-Type' => 'application/json',
|
||||
'Digest' => 'SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=',
|
||||
'Content-Length' => 18,
|
||||
'Date' => 'Sun, 05 Jan 2014 21:31:40 GMT'
|
||||
);
|
||||
$body = '{"hello": "world"}';
|
||||
return new PsrRequest(
|
||||
'POST', 'https://example.com/foo?param=value&pet=dog', $headers, $body
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Crypto;
|
||||
|
||||
use ActivityPub\Crypto\RsaKeypair;
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
namespace ActivityPub\Test\Entities;
|
||||
|
||||
use ActivityPub\Utils\DateTimeProvider;
|
||||
use DateTime;
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
use ActivityPub\Database\PrefixNamingStrategy;
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
use ActivityPub\Test\TestConfig\ArrayDataSet;
|
||||
use ActivityPub\Test\TestConfig\SQLiteTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestDateTimeProvider;
|
||||
use ActivityPub\Utils\DateTimeProvider;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Tools\Setup;
|
||||
|
||||
@ -23,40 +23,6 @@ class EntityTest extends SQLiteTestCase
|
||||
*/
|
||||
protected $dateTimeProvider;
|
||||
|
||||
protected function getDataSet()
|
||||
{
|
||||
return new ArrayDataSet( array(
|
||||
'objects' => array(),
|
||||
'fields' => array(),
|
||||
'keys' => array(),
|
||||
) );
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$dbConfig = Setup::createAnnotationMetadataConfiguration(
|
||||
array( __DIR__ . '/../../src/Entities' ), true
|
||||
);
|
||||
$namingStrategy = new PrefixNamingStrategy( '' );
|
||||
$dbConfig->setNamingStrategy( $namingStrategy );
|
||||
$dbParams = array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => $this->getDbPath(),
|
||||
);
|
||||
$this->entityManager = EntityManager::create( $dbParams, $dbConfig );
|
||||
$this->dateTimeProvider = new TestDateTimeProvider( array(
|
||||
'objects-service.create' => new DateTime( "12:00" ),
|
||||
'objects-service.update' => new DateTime( "12:01" ),
|
||||
) );
|
||||
}
|
||||
|
||||
private function getTime( $context ) {
|
||||
return $this->dateTimeProvider
|
||||
->getTime( $context )
|
||||
->format( "Y-m-d H:i:s" );
|
||||
}
|
||||
|
||||
public function testItCreatesAnObjectWithAPrivateKey()
|
||||
{
|
||||
$object = new ActivityPubObject( $this->dateTimeProvider->getTime( 'objects-service.create' ) );
|
||||
@ -85,6 +51,13 @@ class EntityTest extends SQLiteTestCase
|
||||
$this->assertTablesEqual( $expectedKeysTable, $keysQueryTable );
|
||||
}
|
||||
|
||||
private function getTime( $context )
|
||||
{
|
||||
return $this->dateTimeProvider
|
||||
->getTime( $context )
|
||||
->format( "Y-m-d H:i:s" );
|
||||
}
|
||||
|
||||
public function itUpdatesAPrivateKey()
|
||||
{
|
||||
$object = new ActivityPubObject( $this->dateTimeProvider->getTime( 'objects-service.create' ) );
|
||||
@ -116,5 +89,33 @@ class EntityTest extends SQLiteTestCase
|
||||
$this->assertTablesEqual( $expectedObjectsTable, $objectsQueryTable );
|
||||
$this->assertTablesEqual( $expectedKeysTable, $keysQueryTable );
|
||||
}
|
||||
|
||||
protected function getDataSet()
|
||||
{
|
||||
return new ArrayDataSet( array(
|
||||
'objects' => array(),
|
||||
'fields' => array(),
|
||||
'keys' => array(),
|
||||
) );
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$dbConfig = Setup::createAnnotationMetadataConfiguration(
|
||||
array( __DIR__ . '/../../src/Entities' ), true
|
||||
);
|
||||
$namingStrategy = new PrefixNamingStrategy( '' );
|
||||
$dbConfig->setNamingStrategy( $namingStrategy );
|
||||
$dbParams = array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => $this->getDbPath(),
|
||||
);
|
||||
$this->entityManager = EntityManager::create( $dbParams, $dbConfig );
|
||||
$this->dateTimeProvider = new TestDateTimeProvider( array(
|
||||
'objects-service.create' => new DateTime( "12:00" ),
|
||||
'objects-service.update' => new DateTime( "12:01" ),
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Http;
|
||||
|
||||
use ActivityPub\Controllers\GetController;
|
||||
@ -29,7 +30,7 @@ class RouterTest extends APTestCase
|
||||
$this->router = new Router( $this->getController, $this->postController );
|
||||
$this->kernel = $this->getMock( HttpKernel::class );
|
||||
}
|
||||
|
||||
|
||||
public function testRouter()
|
||||
{
|
||||
$testCases = array(
|
||||
@ -49,7 +50,7 @@ class RouterTest extends APTestCase
|
||||
'expectedException' => MethodNotAllowedHttpException::class,
|
||||
),
|
||||
);
|
||||
foreach( $testCases as $testCase ) {
|
||||
foreach ( $testCases as $testCase ) {
|
||||
$request = $testCase['request'];
|
||||
$event = new GetResponseEvent(
|
||||
$this->kernel, $request, HttpKernelInterface::MASTER_REQUEST
|
||||
|
@ -1,14 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Objects;
|
||||
|
||||
use ActivityPub\Utils\SimpleDateTimeProvider;
|
||||
use ActivityPub\Auth\AuthService;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Objects\CollectionsService;
|
||||
use ActivityPub\Objects\ContextProvider;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestActivityPubObject;
|
||||
use ActivityPub\Utils\SimpleDateTimeProvider;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\Response as Psr7Response;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Objects;
|
||||
|
||||
use ActivityPub\Objects\IdProvider;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Utils\RandomProvider;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Utils\RandomProvider;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class IdProviderTest extends APTestCase
|
||||
@ -17,7 +18,7 @@ class IdProviderTest extends APTestCase
|
||||
{
|
||||
$this->objectsService = $this->getMock( ObjectsService::class );
|
||||
$this->objectsService->method( 'query' )
|
||||
->will( $this->returnCallback( function( $query) {
|
||||
->will( $this->returnCallback( function ( $query ) {
|
||||
$existsId = sprintf(
|
||||
'https://example.com/ap/objects/%s', self::EXISTING_ID_STR
|
||||
);
|
||||
|
@ -2,16 +2,16 @@
|
||||
|
||||
namespace ActivityPub\Test\Objects;
|
||||
|
||||
use ActivityPub\Utils\DateTimeProvider;
|
||||
use DateTime;
|
||||
use BadMethodCallException;
|
||||
use ActivityPub\Test\TestConfig\SQLiteTestCase;
|
||||
use ActivityPub\Test\TestConfig\ArrayDataSet;
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Database\PrefixNamingStrategy;
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
use ActivityPub\Objects\ObjectsService;
|
||||
use ActivityPub\Test\TestConfig\ArrayDataSet;
|
||||
use ActivityPub\Test\TestConfig\SQLiteTestCase;
|
||||
use ActivityPub\Test\TestUtils\TestDateTimeProvider;
|
||||
use ActivityPub\Utils\DateTimeProvider;
|
||||
use BadMethodCallException;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Tools\Setup;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
@ -35,42 +35,6 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
*/
|
||||
protected $httpClient;
|
||||
|
||||
protected function getDataSet()
|
||||
{
|
||||
return new ArrayDataSet( array( 'objects' => array(), 'fields' => array() ) );
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$dbConfig = Setup::createAnnotationMetadataConfiguration(
|
||||
array( __DIR__ . '/../../src/Entities' ), true
|
||||
);
|
||||
$namingStrategy = new PrefixNamingStrategy( '' );
|
||||
$dbConfig->setNamingStrategy( $namingStrategy );
|
||||
$dbParams = array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => $this->getDbPath(),
|
||||
);
|
||||
$this->entityManager = EntityManager::create( $dbParams, $dbConfig );
|
||||
$this->dateTimeProvider = new TestDateTimeProvider( array(
|
||||
'objects-service.create' => new DateTime( "12:00" ),
|
||||
'objects-service.update' => new DateTime( "12:01" ),
|
||||
) );
|
||||
$this->httpClient = $this->getMock( Client::class );
|
||||
$this->httpClient->method( 'send' )
|
||||
->willReturn( new Response( 404 ) );
|
||||
$this->objectsService = new ObjectsService(
|
||||
$this->entityManager, $this->dateTimeProvider, $this->httpClient
|
||||
);
|
||||
}
|
||||
|
||||
private function getTime( $context ) {
|
||||
return $this->dateTimeProvider
|
||||
->getTime( $context )
|
||||
->format( "Y-m-d H:i:s" );
|
||||
}
|
||||
|
||||
public function testItCreatesObject()
|
||||
{
|
||||
$fields = array(
|
||||
@ -114,8 +78,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -126,6 +90,13 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
$this->assertTablesEqual( $expectedFieldsTable, $fieldsQueryTable );
|
||||
}
|
||||
|
||||
private function getTime( $context )
|
||||
{
|
||||
return $this->dateTimeProvider
|
||||
->getTime( $context )
|
||||
->format( "Y-m-d H:i:s" );
|
||||
}
|
||||
|
||||
public function testObjectFieldsSet()
|
||||
{
|
||||
$fields = array(
|
||||
@ -241,8 +212,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -333,8 +304,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -380,11 +351,11 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
$now = $this->getTime( 'objects-service.create' );
|
||||
$this->objectsService->persist( $fields );
|
||||
$expected = new ArrayDataSet( array(
|
||||
'objects' => array(
|
||||
array( 'id' => 1, 'created' => $now, 'lastUpdated' => $now ),
|
||||
array( 'id' => 2, 'created' => $now, 'lastUpdated' => $now ),
|
||||
array( 'id' => 3, 'created' => $now, 'lastUpdated' => $now ),
|
||||
array( 'id' => 4, 'created' => $now, 'lastUpdated' => $now ),
|
||||
'objects' => array(
|
||||
array( 'id' => 1, 'created' => $now, 'lastUpdated' => $now ),
|
||||
array( 'id' => 2, 'created' => $now, 'lastUpdated' => $now ),
|
||||
array( 'id' => 3, 'created' => $now, 'lastUpdated' => $now ),
|
||||
array( 'id' => 4, 'created' => $now, 'lastUpdated' => $now ),
|
||||
),
|
||||
'fields' => array(
|
||||
array(
|
||||
@ -488,8 +459,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -714,12 +685,12 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
|
||||
public function testItDoesNotStoreObjectsWithTheSameId()
|
||||
{
|
||||
$fieldsOne = array(
|
||||
$fieldsOne = array(
|
||||
'id' => 'https://example.com/notes/1',
|
||||
'type' => 'Note',
|
||||
'content' => 'This is a note',
|
||||
);
|
||||
$fieldsTwo = array(
|
||||
);
|
||||
$fieldsTwo = array(
|
||||
'id' => 'https://example.com/notes/1',
|
||||
'type' => 'Note',
|
||||
'content' => 'This is another note',
|
||||
@ -762,8 +733,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -849,8 +820,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -953,8 +924,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -1054,8 +1025,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -1107,8 +1078,8 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
),
|
||||
),
|
||||
) );
|
||||
$expectedObjectsTable = $expected->getTable('objects');
|
||||
$expectedFieldsTable = $expected->getTable('fields');
|
||||
$expectedObjectsTable = $expected->getTable( 'objects' );
|
||||
$expectedFieldsTable = $expected->getTable( 'fields' );
|
||||
$objectsQueryTable = $this->getConnection()->createQueryTable(
|
||||
'objects', 'SELECT * FROM objects'
|
||||
);
|
||||
@ -1279,14 +1250,46 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
if ( array_key_exists( 'expectedQueryResults', $testCase ) ) {
|
||||
foreach ( $testCase['expectedQueryResults'] as $expectedQueryResult ) {
|
||||
$result = array_map(
|
||||
function( ActivityPubObject $obj ) { return $obj->asArray(); },
|
||||
function ( ActivityPubObject $obj ) {
|
||||
return $obj->asArray();
|
||||
},
|
||||
$this->objectsService->query( $expectedQueryResult['query'] )
|
||||
);
|
||||
$this->assertEquals( $expectedQueryResult['expectedResult'], $result );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$dbConfig = Setup::createAnnotationMetadataConfiguration(
|
||||
array( __DIR__ . '/../../src/Entities' ), true
|
||||
);
|
||||
$namingStrategy = new PrefixNamingStrategy( '' );
|
||||
$dbConfig->setNamingStrategy( $namingStrategy );
|
||||
$dbParams = array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => $this->getDbPath(),
|
||||
);
|
||||
$this->entityManager = EntityManager::create( $dbParams, $dbConfig );
|
||||
$this->dateTimeProvider = new TestDateTimeProvider( array(
|
||||
'objects-service.create' => new DateTime( "12:00" ),
|
||||
'objects-service.update' => new DateTime( "12:01" ),
|
||||
) );
|
||||
$this->httpClient = $this->getMock( Client::class );
|
||||
$this->httpClient->method( 'send' )
|
||||
->willReturn( new Response( 404 ) );
|
||||
$this->objectsService = new ObjectsService(
|
||||
$this->entityManager, $this->dateTimeProvider, $this->httpClient
|
||||
);
|
||||
}
|
||||
|
||||
protected function getDataSet()
|
||||
{
|
||||
return new ArrayDataSet( array( 'objects' => array(), 'fields' => array() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\TestConfig;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
abstract class APTestCase extends TestCase
|
||||
{
|
||||
function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = false, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null)
|
||||
function getMock( $originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = false, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null )
|
||||
{
|
||||
return parent::getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods, $proxyTarget); // TODO: Change the autogenerated stub
|
||||
return parent::getMock( $originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods, $proxyTarget ); // TODO: Change the autogenerated stub
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\TestConfig;
|
||||
|
||||
use InvalidArgumentException;
|
||||
@ -13,35 +14,35 @@ class ArrayDataSet extends \PHPUnit_Extensions_Database_DataSet_AbstractDataSet
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data)
|
||||
public function __construct( array $data )
|
||||
{
|
||||
foreach ($data as $tableName => $rows) {
|
||||
foreach ( $data as $tableName => $rows ) {
|
||||
$columns = [];
|
||||
if (isset($rows[0])) {
|
||||
$columns = array_keys($rows[0]);
|
||||
if ( isset( $rows[0] ) ) {
|
||||
$columns = array_keys( $rows[0] );
|
||||
}
|
||||
|
||||
$metaData = new \PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns);
|
||||
$table = new \PHPUnit_Extensions_Database_DataSet_DefaultTable($metaData);
|
||||
$metaData = new \PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData( $tableName, $columns );
|
||||
$table = new \PHPUnit_Extensions_Database_DataSet_DefaultTable( $metaData );
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$table->addRow($row);
|
||||
foreach ( $rows as $row ) {
|
||||
$table->addRow( $row );
|
||||
}
|
||||
$this->tables[$tableName] = $table;
|
||||
}
|
||||
}
|
||||
|
||||
protected function createIterator($reverse = false)
|
||||
public function getTable( $tableName )
|
||||
{
|
||||
return new \PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables, $reverse);
|
||||
}
|
||||
|
||||
public function getTable($tableName)
|
||||
{
|
||||
if (!isset($this->tables[$tableName])) {
|
||||
throw new InvalidArgumentException("$tableName is not a table in the current database.");
|
||||
if ( !isset( $this->tables[$tableName] ) ) {
|
||||
throw new InvalidArgumentException( "$tableName is not a table in the current database." );
|
||||
}
|
||||
|
||||
return $this->tables[$tableName];
|
||||
}
|
||||
|
||||
protected function createIterator( $reverse = false )
|
||||
{
|
||||
return new \PHPUnit_Extensions_Database_DataSet_DefaultTableIterator( $this->tables, $reverse );
|
||||
}
|
||||
}
|
||||
|
@ -16,36 +16,6 @@ abstract class SQLiteTestCase extends APTestCase
|
||||
private $conn = null;
|
||||
private $dbPath = '';
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$dbPath = $this->getDbPath();
|
||||
if ( file_exists( $dbPath ) ) {
|
||||
unlink( $dbPath );
|
||||
}
|
||||
$config = ActivityPubConfig::createBuilder()
|
||||
->setDbConnectionParams( array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => $dbPath,
|
||||
) )
|
||||
->build();
|
||||
$activityPub = new ActivityPub( $config );
|
||||
$activityPub->updateSchema();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
unlink( $this->getDbPath() );
|
||||
unset( $this->conn );
|
||||
unset( $this->pdo );
|
||||
}
|
||||
|
||||
protected function getDbPath()
|
||||
{
|
||||
return dirname( __FILE__ ) . '/db.sqlite';
|
||||
}
|
||||
|
||||
final public function getConnection()
|
||||
{
|
||||
if ( $this->conn === null ) {
|
||||
@ -57,5 +27,35 @@ abstract class SQLiteTestCase extends APTestCase
|
||||
}
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
protected function getDbPath()
|
||||
{
|
||||
return dirname( __FILE__ ) . '/db.sqlite';
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$dbPath = $this->getDbPath();
|
||||
if ( file_exists( $dbPath ) ) {
|
||||
unlink( $dbPath );
|
||||
}
|
||||
$config = ActivityPubConfig::createBuilder()
|
||||
->setDbConnectionParams( array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => $dbPath,
|
||||
) )
|
||||
->build();
|
||||
$activityPub = new ActivityPub( $config );
|
||||
$activityPub->updateSchema();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
unlink( $this->getDbPath() );
|
||||
unset( $this->conn );
|
||||
unset( $this->pdo );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,21 +11,39 @@ use DateTime;
|
||||
*/
|
||||
class TestActivityPubObject extends ActivityPubObject
|
||||
{
|
||||
public static function getDefaultTime() {
|
||||
private $fixedTime;
|
||||
|
||||
public function __construct( DateTime $time = null )
|
||||
{
|
||||
if ( !$time ) {
|
||||
$time = self::getDefaultTime();
|
||||
}
|
||||
$this->fixedTime = $time;
|
||||
parent::__construct( $time );
|
||||
}
|
||||
|
||||
public static function getDefaultTime()
|
||||
{
|
||||
return DateTime::createFromFormat(
|
||||
DateTime::RFC2822, 'Sun, 05 Jan 2014 21:31:40 GMT'
|
||||
);
|
||||
}
|
||||
|
||||
private $fixedTime;
|
||||
|
||||
public function __construct( DateTime $time = null )
|
||||
public static function fromArray( array $arr, DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
if ( !$time ) {
|
||||
$time = self::getDefaultTime();
|
||||
}
|
||||
$this->fixedTime = $time;
|
||||
parent::__construct( $time );
|
||||
$object = new TestActivityPubObject( $time );
|
||||
foreach ( $arr as $name => $value ) {
|
||||
if ( is_array( $value ) ) {
|
||||
$child = self::fromArray( $value, $time );
|
||||
TestField::withObject( $object, $name, $child, $time );
|
||||
} else {
|
||||
TestField::withValue( $object, $name, $value, $time );
|
||||
}
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function addField( Field $field, DateTime $time = null )
|
||||
@ -44,22 +62,5 @@ class TestActivityPubObject extends ActivityPubObject
|
||||
{
|
||||
// do not change lastUpdated
|
||||
}
|
||||
|
||||
public static function fromArray( array $arr, DateTime $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
$time = self::getDefaultTime();
|
||||
}
|
||||
$object = new TestActivityPubObject( $time );
|
||||
foreach ( $arr as $name => $value ) {
|
||||
if ( is_array( $value ) ) {
|
||||
$child = self::fromArray( $value, $time );
|
||||
TestField::withObject( $object, $name, $child, $time );
|
||||
} else {
|
||||
TestField::withValue( $object, $name, $value, $time );
|
||||
}
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
namespace ActivityPub\Test\TestUtils;
|
||||
|
||||
use DateTime;
|
||||
use ActivityPub\Utils\DateTimeProvider;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* A DateTimeProvider that returns fixed values for create and update times
|
||||
@ -19,10 +19,10 @@ class TestDateTimeProvider implements DateTimeProvider
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
|
||||
public function getTime( $context = '' )
|
||||
{
|
||||
if ( array_key_exists( $context, $this->context )) {
|
||||
if ( array_key_exists( $context, $this->context ) ) {
|
||||
return $this->context[$context];
|
||||
} else {
|
||||
return new DateTime( 'now' );
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\TestUtils;
|
||||
|
||||
use ActivityPub\Entities\ActivityPubObject;
|
||||
@ -14,7 +15,7 @@ class TestField extends Field
|
||||
|
||||
protected function __construct( $time = null )
|
||||
{
|
||||
if ( ! $time ) {
|
||||
if ( !$time ) {
|
||||
$time = TestActivityPubObject::getDefaultTime();
|
||||
}
|
||||
parent::__construct( $time );
|
||||
|
@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace ActivityPub\Test\Utils;
|
||||
|
||||
use ActivityPub\Utils\Util;
|
||||
use ActivityPub\Test\TestConfig\APTestCase;
|
||||
use ActivityPub\Utils\Util;
|
||||
|
||||
class UtilTest extends APTestCase
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user