Implement and test AuthListener

This commit is contained in:
Jeremy Dormitzer 2019-01-18 09:45:34 -05:00
parent 887ac185ca
commit 88dd8a7b8f
5 changed files with 136 additions and 34 deletions

View File

@ -46,8 +46,8 @@ class ActivityPub
}
$dispatcher = new EventDispatcher();
$signatureListener = $this->module->get( 'signatureListener' );
$dispatcher->addSubscriber( $signatureListener );
$dispatcher->addSubscriber( $this->module->get( 'authListener' ) );
$dispatcher->addSubscriber( $this->module->get( 'signatureListener' ) );
$dispatcher->addSubscriber( new ExceptionListener() );
$controllerResolver = new ControllerResolver();

55
src/Auth/AuthListener.php Normal file
View File

@ -0,0 +1,55 @@
<?php
namespace ActivityPub\Auth;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
/**
* 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
* plug in their own authentication methods.
*/
class AuthListener implements EventSubscriberInterface
{
/**
* The Callable that is called to determine if a request is authorized for an Actor
*
* @var Callable
*
*/
private $authFunction;
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => 'checkAuth'
);
}
/**
* Constructs a new AuthenticationService
*
* @param Callable $authFunction A Callable that should accept
*
*/
public function __construct( Callable $authFunction )
{
$this->authFunction = $authFunction;
}
public function checkAuth( GetResponseEvent $event )
{
$request = $event->getRequest();
if ( $request->attributes->has( 'actor' ) ) {
return;
}
$actorId = call_user_func( $this->authFunction );
if ( $actorId && ! empty( $actorId ) ) {
$request->attributes->set( 'actor', $actorId );
}
}
}
?>

View File

@ -1,32 +0,0 @@
<?php
namespace ActivityPub\Auth;
/**
* The AuthenticationService class answers the question, "is this request authenticated
* to act on behalf of this Actor?"
*
* It delegates most of the work to a passed-in Callable to allow library clients to
* plug in their own authentication methods.
*/
class AuthenticationService
{
/**
* The Callable that is called to determine if a request is authorized for an Actor
*
* @var Callable
*
*/
private $authFunction;
/**
* Constructs a new AuthenticationService
*
* @param Callable $authFunction A Callable that should accept
*
*/
public function __construct( Callable $authFunction )
{
$this->authFunction = $authFunction;
}
}
?>

View File

@ -1,6 +1,8 @@
<?php
namespace ActivityPub\Config;
use ActivityPub\Auth\AuthListener;
use ActivityPub\Auth\SignatureListener;
use ActivityPub\Crypto\HttpSignatureService;
use ActivityPub\Database\PrefixNamingStrategy;
use ActivityPub\Objects\ObjectsService;
@ -26,6 +28,9 @@ class ActivityPubModule
$defaults = array(
'isDevMode' => false,
'dbPrefix' => '',
'authFunction' => function() {
return false;
},
);
$options = array_merge( $defaults, $options );
$this->validateOptions( $options );
@ -59,6 +64,9 @@ class ActivityPubModule
$this->injector->register( 'signatureListener', SignatureListener::class )
->addArgument( new Reference( 'httpSignatureService' ) )
->addArgument( new Reference( 'objectsService' ) );
$this->injector->register( 'authListener', AuthListener::class )
->addArgument( $options['authFunction'] );
}
/**

View File

@ -0,0 +1,71 @@
<?php
namespace ActivityPub\Test\Auth;
use ActivityPub\Auth\AuthListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use PHPUnit\Framework\TestCase;
class AuthListenerTest extends TestCase
{
public function getEvent()
{
$kernel = $this->createMock( 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() {
return 'https://example.com/actor/1';
},
'expectedAttributes' => array(
'actor' => 'https://example.com/actor/1',
),
),
array(
'id' => 'existingActorTest',
'authFunction' => function() {
return 'https://example.com/actor/1';
},
'requestAttributes' => array(
'actor' => 'https://example.com/actor/2',
),
'expectedAttributes' => array(
'actor' => 'https://example.com/actor/2',
),
),
array(
'id' => 'defaultAuthTest',
'authFunction' => function() {
return false;
},
'expectedAttributes' => array(),
),
);
foreach ( $testCases as $testCase ) {
$event = $this->getEvent();
if ( array_key_exists( 'requestAttributes', $testCase ) ) {
foreach ( $testCase['requestAttributes'] as $attribute => $value ) {
$event->getRequest()->attributes->set( $attribute, $value );
}
}
$authListener = new AuthListener( $testCase['authFunction'] );
$authListener->checkAuth( $event );
$this->assertEquals(
$testCase['expectedAttributes'],
$event->getRequest()->attributes->all(),
"Error on test $testCase[id]"
);
}
}
}
?>