public authenticate ( Imbo\EventManager\EventInterface $event ) | ||
$event | Imbo\EventManager\EventInterface |
public function authenticate(EventInterface $event)
{
$response = $event->getResponse();
$request = $event->getRequest();
$config = $event->getConfig();
// Whether or not the authentication info is in the request headers
$fromHeaders = $request->headers->has('x-imbo-authenticate-timestamp') && $request->headers->has('x-imbo-authenticate-signature');
// Fetch timestamp header, fallback to query param
$timestamp = $request->headers->get('x-imbo-authenticate-timestamp', $request->query->get('timestamp'));
if (!$timestamp) {
$exception = new RuntimeException('Missing authentication timestamp', 400);
$exception->setImboErrorCode(Exception::AUTH_MISSING_PARAM);
} else {
if (!$this->timestampIsValid($timestamp)) {
$exception = new RuntimeException('Invalid timestamp: ' . $timestamp, 400);
$exception->setImboErrorCode(Exception::AUTH_INVALID_TIMESTAMP);
} else {
if ($this->timestampHasExpired($timestamp)) {
$exception = new RuntimeException('Timestamp has expired: ' . $timestamp, 400);
$exception->setImboErrorCode(Exception::AUTH_TIMESTAMP_EXPIRED);
}
}
}
if (isset($exception)) {
throw $exception;
}
// Fetch signature header, fallback to query param
$signature = $request->headers->get('x-imbo-authenticate-signature', $request->query->get('signature'));
if (!$signature) {
$exception = new RuntimeException('Missing authentication signature', 400);
$exception->setImboErrorCode(Exception::AUTH_MISSING_PARAM);
}
if (isset($exception)) {
throw $exception;
}
$publicKey = $request->getPublicKey();
$privateKey = $event->getAccessControl()->getPrivateKey($publicKey);
$url = $request->getRawUri();
if (!$fromHeaders) {
// Remove the signature and timestamp from the query parameters as they are not used
// when generating the HMAC
$url = rtrim(preg_replace('/(?<=(\\?|&))(signature|timestamp)=[^&]+&?/', '', $url), '&?');
}
// See if we should modify the protocol for the incoming request
$uris = [$url];
$protocol = $config['authentication']['protocol'];
if ($protocol === 'both') {
$uris = [preg_replace('#^https?#', 'http', $url), preg_replace('#^https?#', 'https', $url)];
} else {
if (in_array($protocol, ['http', 'https'])) {
$uris = [preg_replace('#^https?#', $protocol, $url)];
}
}
// Add the URL used for auth to the response headers
$response->headers->set('X-Imbo-AuthUrl', implode(', ', $uris));
foreach ($uris as $uri) {
if ($this->signatureIsValid($request->getMethod(), $uri, $publicKey, $privateKey, $timestamp, $signature)) {
return;
}
}
$exception = new RuntimeException('Signature mismatch', 400);
$exception->setImboErrorCode(Exception::AUTH_SIGNATURE_MISMATCH);
throw $exception;
}
/** * @dataProvider getRewrittenSignatureData * @covers Imbo\EventListener\Authenticate::authenticate * @covers Imbo\EventListener\Authenticate::signatureIsValid * @covers Imbo\EventListener\Authenticate::timestampIsValid * @covers Imbo\EventListener\Authenticate::timestampHasExpired */ public function testApprovesSignaturesWhenConfigurationForcesProtocol($serverUrl, $protocol, $authHeader, $shouldMatch, $signature, $timestamp) { if (!$shouldMatch) { $this->setExpectedException('Imbo\\Exception\\RuntimeException', 'Signature mismatch', 400); } $this->accessControl->expects($this->once())->method('getPrivateKey')->will($this->returnValue('key')); $this->headers->expects($this->at(0))->method('has')->with('x-imbo-authenticate-timestamp')->will($this->returnValue(false)); $this->headers->expects($this->at(1))->method('get')->with('x-imbo-authenticate-timestamp', $timestamp)->will($this->returnValue($timestamp)); $this->headers->expects($this->at(2))->method('get')->with('x-imbo-authenticate-signature', $signature)->will($this->returnValue($signature)); $this->query->expects($this->at(0))->method('get')->with('timestamp')->will($this->returnValue($timestamp)); $this->query->expects($this->at(1))->method('get')->with('signature')->will($this->returnValue($signature)); $this->request->expects($this->once())->method('getRawUri')->will($this->returnValue($serverUrl)); $this->request->expects($this->once())->method('getPublicKey')->will($this->returnValue('christer')); $this->request->expects($this->any())->method('getMethod')->will($this->returnValue('PUT')); $responseHeaders = $this->getMock('Symfony\\Component\\HttpFoundation\\ResponseHeaderBag'); $responseHeaders->expects($this->once())->method('set')->with('X-Imbo-AuthUrl', $authHeader); $this->response->headers = $responseHeaders; $this->listener->authenticate($this->getEventMock(['authentication' => ['protocol' => $protocol]])); }