Finish implementing and test HttpSignatureService

This commit is contained in:
Jeremy Dormitzer 2019-01-14 21:30:22 -05:00
parent 032113492c
commit 47d384332d
6 changed files with 318 additions and 104 deletions

View File

@ -1,6 +1,9 @@
<?php
namespace ActivityPub\Auth;
namespace ActivityPub\Crypto;
use DateTime;
use ActivityPub\Utils\DateTimeProvider;
use ActivityPub\Utils\SimpleDateTimeProvider;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\HeaderUtils;
@ -15,13 +18,50 @@ class HttpSignatureService
'host',
'date',
);
public function sign( Request $request, string $privateKey, $headers = self::DEFAULT_HEADERS )
const REPLAY_THRESHOLD = 300;
/**
* @var DateTimeProvider
*/
private $dateTimeProvider;
/**
* Constructs a new HttpSignatureService
*
* @param DateTimeProvider $dateTimeProvider The DateTimeProvider,
* defaults to SimpleDateTimeProvider
*/
public function __construct( DateTimeProvider $dateTimeProvider = null )
{
// To generate a signature for a request:
// 1. put together the signing string from the headers list
// 2. generate an RSA-sha256 signature of the signing string using the private key
// 3. return the signature base64-encoded
if ( ! $dateTimeProvider ) {
$dateTimeProvider = new SimpleDateTimeProvider();
}
$this->dateTimeProvider = $dateTimeProvider;
}
/**
* Generates a signature given the request and private key
*
* @param Request $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 The headers to use in the signature
* (default ['(request-target)', 'host', 'date'])
* @return string The Signature header value
*/
public function sign( Request $request, string $privateKey, string $keyId,
$headers = self::DEFAULT_HEADERS )
{
$headers = array_map( 'strtolower', $headers );
$signingString = $this->getSigningString( $request, $headers );
$keypair = RsaKeypair::fromPrivateKey( $privateKey );
$signature = base64_encode( $keypair->sign( $signingString, 'rsa256' ) );
$headersStr = implode( ' ', $headers );
return "keyId=\"$keyId\"," .
"algorithm=\"rsa-sha256\"," .
"headers=\"$headersStr\"," .
"signature=\"$signature\"";
}
/**
@ -33,9 +73,18 @@ class HttpSignatureService
*/
public function verify( Request $request, string $publicKey )
{
// TODO fail verification if date is > 300 seconds ago to prevent replay attacks
$params = array();
$headers = $request->headers;
if ( ! $headers->has( 'date' ) ) {
return false;
}
$now = $this->dateTimeProvider->getTime( 'http-signature.verify' );
$then = DateTime::createFromFormat( DateTime::RFC2822, $headers->get( 'date' ) );
if ( abs( $now->getTimestamp() - $then->getTimestamp() ) > self::REPLAY_THRESHOLD ) {
return false;
}
if ( $headers->has( 'signature' ) ) {
$params = $this->parseSignatureParams( $headers->get( 'signature' ) );
} else if ( $headers->has( 'authorization' ) &&
@ -43,19 +92,21 @@ class HttpSignatureService
$paramsStr = substr( $headers->get( 'authorization' ), 10 );
$params = $this->parseSignatureParams( $paramsStr );
}
if ( count( $params ) === 0 ) {
return false;
}
$targetHeaders = array( 'date' );
if ( array_key_exists( 'headers', $params ) ) {
$targetHeaders = $params['headers'];
}
$signingString = $this->getSigningString( $request, $targetHeaders );
$signature = base64_decode( $params['signature'] );
// TODO handle different algorithms here, checking the 'algorithm' param and the key headers
return openssl_verify(
$signingString, $signature, $publicKey, OPENSSL_ALGO_SHA256
) === 1;
$keypair = RsaKeypair::fromPublicKey( $publicKey );
return $keypair->verify($signingString, $signature, 'sha256');
}
/**
@ -71,7 +122,7 @@ class HttpSignatureService
foreach ( $headers as $header ) {
$component = "${header}: ";
if ( $header == '(request-target)' ) {
$method = strtolower( $request->method );
$method = strtolower( $request->getMethod());
$path = $request->getRequestUri();
$component = $component . $method . ' ' . $path;
} else {
@ -81,7 +132,7 @@ class HttpSignatureService
}
$signingComponents[] = $component;
}
return implode( '\n', $signingComponents );
return implode( "\n", $signingComponents );
}
/**
@ -94,12 +145,12 @@ class HttpSignatureService
private function parseSignatureParams( string $paramsStr )
{
$params = array();
$split = HeaderUtils::split( $paramsStr, ',= ' );
$split = HeaderUtils::split( $paramsStr, ',=' );
foreach ( $split as $paramArr ) {
$paramName = $paramArr[0][0];
$paramName = $paramArr[0];
$paramValue = $paramArr[1];
if ( count( $paramValue ) === 1 ) {
$paramValue = $paramValue[0];
if ( $paramName == 'headers' ) {
$paramValue = explode(' ', $paramValue);
}
$params[$paramName] = $paramValue;
}

View File

@ -22,7 +22,7 @@ class RsaKeypair
*/
private $privateKey;
private function __construct( string $publicKey, string $privateKey )
public function __construct( string $publicKey, string $privateKey )
{
$this->publicKey = $publicKey;
$this->privateKey = $privateKey;
@ -53,9 +53,11 @@ class RsaKeypair
*
* Throws a BadMethodCallException if this RsaKeypair does not have a private key.
* @param string $data The data to sign
* @param string $hash The hash algorithm to use. One of:
* 'md2', 'md5', 'sha1', 'sha256', 'sha384', 'sha512'. Default: 'sha256'
* @return string The signature
*/
public function sign( $data )
public function sign( $data, $hash = 'sha256' )
{
if ( empty( $this->privateKey ) ) {
throw new BadMethodCallException(
@ -64,6 +66,7 @@ class RsaKeypair
}
$rsa = new RSA();
$rsa->setHash( 'sha256' );
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
$rsa->loadKey( $this->privateKey );
return $rsa->sign( $data );
}
@ -73,12 +76,15 @@ class RsaKeypair
*
* @param string $data The data
* @param string $signature The signature
* @param string $hash The hash algorithm to use. One of:
* 'md2', 'md5', 'sha1', 'sha256', 'sha384', 'sha512'. Default: 'sha256'
* @return bool
*/
public function verify( $data, $signature )
public function verify( $data, $signature, $hash = 'sha256' )
{
$rsa = new RSA();
$rsa->setHash( 'sha256' );
$rsa->setHash( $hash );
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
$rsa->loadKey( $this->publicKey );
return $rsa->verify( $data, $signature );
}
@ -108,5 +114,19 @@ class RsaKeypair
{
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 function fromPrivateKey( string $privateKey)
{
return new RsaKeypair( '', $privateKey );
}
}
?>

View File

@ -1,73 +0,0 @@
<?php
// tests:
// - signature for request that specifies a header but is missing that header
// - signature for request with malformed Signature header
namespace ActivityPub\Test\Auth;
use ActivityPub\Auth\HttpSignatureService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
class HttpSignatureServiceTest extends TestCase
{
const PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
oYi+1hqp1fIekaxsyQIDAQAB
-----END PUBLIC KEY-----";
const PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB
AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA
QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK
kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u
412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc
mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7
kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA==
-----END RSA PRIVATE KEY-----";
private $httpSignatureService;
public function setUp()
{
$this->httpSignatureService = new HttpSignatureService();
}
private static function getRequest()
{
$request = Request::create(
'https://example.com/foo',
Request::METHOD_POST,
array( 'param' => 'value', 'pet' => 'dog' ),
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 testItVerifies()
{
$request = self::getRequest();
$authHeader = 'Signature keyId="Test",algorithm="rsa-sha256",signature="SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM="';
$request->headers->set( 'authorization', $authHeader );
$verified = $this->httpSignatureService->verify( $request, self::PUBLIC_KEY );
$this->assertTrue( $verified );
}
}
?>

View File

@ -0,0 +1,217 @@
<?php
namespace ActivityPub\Test\Crypto;
use DateTime;
use ActivityPub\Crypto\HttpSignatureService;
use ActivityPub\Test\TestUtils\TestDateTimeProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
class HttpSignatureServiceTest extends TestCase
{
const PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
oYi+1hqp1fIekaxsyQIDAQAB
-----END PUBLIC KEY-----";
const PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB
AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA
QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK
kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u
412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc
mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7
kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA==
-----END RSA PRIVATE KEY-----";
private $httpSignatureService;
public function setUp()
{
$dateTimeProvider = new TestDateTimeProvider( array(
'http-signature.verify' => DateTime::createFromFormat(
DateTime::RFC2822, 'Sun, 05 Jan 2014 21:31:40 GMT'
),
) );
$this->httpSignatureService = new HttpSignatureService( $dateTimeProvider );
}
private static function getRequest()
{
$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 testItVerifies()
{
$testCases = array(
array(
'id' => 'defaultTest',
'headers' => array(
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",signature="SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM="',
),
'expectedResult' => true,
),
array(
'id' => 'basicTest',
'headers' => array(
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date", signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
),
'expectedResult' => true,
),
array(
'id' => 'allHeadersTest',
'headers' => array(
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
),
'expectedResult' => true,
),
array(
'id' => 'defaultTestSigHeader',
'headers' => array(
'Signature' => 'keyId="Test",algorithm="rsa-sha256",signature="SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM="',
),
'expectedResult' => true,
),
array(
'id' => 'basicTestSigHeader',
'headers' => array(
'Signature' => 'keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date", signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
),
'expectedResult' => true,
),
array(
'id' => 'allHeadersTestSigHeader',
'headers' => array(
'Signature' => 'keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
),
'expectedResult' => true,
),
array(
'id' => 'noHeaders',
'headers' => array(),
'expectedResult' => false,
),
array(
'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(
'id' => 'malformedHeader',
'headers' => array(
'Authorization' => 'not a real auth header',
),
'expectedResult' => false,
),
array(
'id' => 'partlyMalformedHeader',
'headers' => array(
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers-malformed="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
),
'expectedResult' => false,
),
array(
'id' => 'dateTooFarInPast',
'headers' => array(
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date", signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
),
'expectedResult' => false,
'currentDatetime' => DateTime::createFromFormat(
DateTime::RFC2822, 'Sun, 05 Jan 2014 21:36:41 GMT'
),
),
array(
'id' => 'dateTooFarInFuture',
'headers' => array(
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date", signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
),
'expectedResult' => false,
'currentDatetime' => DateTime::createFromFormat(
DateTime::RFC2822, 'Sun, 05 Jan 2014 21:26:39 GMT'
),
),
);
foreach ( $testCases as $testCase ) {
if ( array_key_exists( 'currentDatetime', $testCase ) ) {
$dateTimeProvider = new TestDateTimeProvider( array(
'http-signature.verify' => $testCase['currentDatetime'],
) );
$this->httpSignatureService = new HttpSignatureService( $dateTimeProvider );
}
$request = self::getRequest();
foreach ( $testCase['headers'] as $header => $value ) {
$request->headers->set( $header, $value );
}
$actual = $this->httpSignatureService->verify( $request, self::PUBLIC_KEY );
$this->assertEquals(
$testCase['expectedResult'], $actual, "Error on test $testCase[id]"
);
}
}
public function testItSigns()
{
$testCases = array(
array(
'id' => 'basicTest',
'keyId' => 'Test',
'expected' => 'keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date",signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
),
array(
'id' => 'allHeadersTest',
'keyId' => 'Test',
'headers' => array(
'(request-target)',
'host',
'date',
'content-type',
'digest',
'content-length',
),
'expected' => 'keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
),
);
foreach ( $testCases as $testCase ) {
$request = self::getRequest();
if ( array_key_exists( 'headers', $testCase ) ) {
$actual = $this->httpSignatureService->sign(
$request, self::PRIVATE_KEY, $testCase['keyId'], $testCase['headers']
);
} else {
$actual= $this->httpSignatureService->sign(
$request, self::PRIVATE_KEY, $testCase['keyId']
);
}
$this->assertEquals(
$testCase['expected'], $actual, "Error on test $testCase[id]"
);
}
}
}
?>

View File

@ -39,7 +39,7 @@ class ObjectsServiceTest extends SQLiteTestCase
);
$this->entityManager = EntityManager::create( $dbParams, $dbConfig );
$this->dateTimeProvider = new TestDateTimeProvider(
new DateTime( "12:00" ), new DateTime( "12:01" )
array( 'create' => new DateTime( "12:00" ), 'update' => new DateTime( "12:01" ) )
);
$this->objectsService = new ObjectsService(
$this->entityManager, $this->dateTimeProvider

View File

@ -9,21 +9,20 @@ use ActivityPub\Utils\DateTimeProvider;
*/
class TestDateTimeProvider implements DateTimeProvider
{
protected $createTime;
protected $updateTime;
protected $context;
public function __construct( DateTime $createTime, DateTime $updateTime )
/**
* @param array $context An array mapping context strings to DateTime instances
*/
public function __construct( $context )
{
$this->createTime = $createTime;
$this->updateTime = $updateTime;
$this->context = $context;
}
public function getTime( $context = '' )
{
if ( $context === 'create' ) {
return $this->createTime;
} else if ( $context === 'update' ) {
return $this->updateTime;
if ( array_key_exists( $context, $this->context )) {
return $this->context[$context];
} else {
return new DateTime( 'now' );
}