Skip to content

Commit b56b707

Browse files
committed
implemented OAuthListener with basic tests
1 parent 8f4be01 commit b56b707

File tree

2 files changed

+184
-3
lines changed

2 files changed

+184
-3
lines changed

lib/Bitbucket/API/Http/Listener/OAuthListener.php

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
*/
2424
class OAuthListener implements ListenerInterface
2525
{
26+
const ENDPOINT_REQUEST_TOKEN = 'oauth/request_token';
27+
const ENDPOINT_ACCESS_TOKEN = 'oauth/access_token';
28+
const ENDPOINT_AUTHORIZE = 'oauth/authenticate';
29+
2630
/**
2731
* @var array
2832
*/
@@ -34,7 +38,7 @@ class OAuthListener implements ListenerInterface
3438
'oauth_signature_method' => 'HMAC-SHA1',
3539
'oauth_callback' => '',
3640
'oauth_verifier' => '',
37-
'oauth_version' => ''
41+
'oauth_version' => '1.0'
3842
);
3943

4044
/**
@@ -64,7 +68,7 @@ public function __construct(
6468

6569
$this->token = (!is_null($token)) ?
6670
$token :
67-
!isset($this->config['oauth_token']) ?
71+
empty($this->config['oauth_token']) ?
6872
new OAuth1\Token\NullToken :
6973
new OAuth1\Token\Token($this->config['oauth_token'], $this->config['oauth_token_secret'])
7074
;
@@ -89,14 +93,104 @@ public function getName()
8993
* {@inheritDoc}
9094
*/
9195
public function preSend(RequestInterface $request)
92-
{}
96+
{
97+
$params = $this->getParametersToSign($request);
98+
$req = OAuth1\Request\Request::fromConsumerAndToken(
99+
$this->consumer, $this->token, $request->getMethod(), $request->getUrl(), $params
100+
);
101+
102+
$req->signRequest($this->signature, $this->consumer, $this->token);
103+
104+
$request->addHeader($req->toHeader());
105+
}
93106

94107
/**
95108
* {@inheritDoc}
96109
*/
97110
public function postSend(RequestInterface $request, MessageInterface $response)
98111
{}
99112

113+
/**
114+
* Include OAuth and request body parameters
115+
*
116+
* @access protected
117+
* @param RequestInterface $request
118+
* @return array
119+
*
120+
* @see http://oauth.net/core/1.0/#sig_norm_param
121+
*/
122+
protected function getParametersToSign(RequestInterface $request)
123+
{
124+
return array_merge($this->getOAuthParameters($request), $this->getContentAsParameters($request));
125+
}
126+
127+
/**
128+
* Include/exclude optional parameters
129+
*
130+
* The exclusion/inclusion is based on current request resource
131+
*
132+
* @access protected
133+
* @param RequestInterface $request
134+
* @return array
135+
*/
136+
protected function getOAuthParameters(RequestInterface $request)
137+
{
138+
$params = $this->filterOAuthParameters(array('oauth_token', 'oauth_version'));
139+
140+
if ($this->isEndpointRequested(self::ENDPOINT_REQUEST_TOKEN, $request)) {
141+
$params = $this->filterOAuthParameters(array('oauth_callback'));
142+
} elseif ($this->isEndpointRequested(self::ENDPOINT_ACCESS_TOKEN, $request)) {
143+
$params = $this->filterOAuthParameters(array('oauth_token', 'oauth_verifier'));
144+
}
145+
146+
return $params;
147+
}
148+
149+
/**
150+
* White list based filter
151+
*
152+
* @param array $include
153+
* @return array
154+
*/
155+
protected function filterOAuthParameters(array $include)
156+
{
157+
$final = array();
158+
159+
foreach ($include as $key => $value) {
160+
if (!empty($this->config[$value])) {
161+
$final[$value] = $this->config[$value];
162+
}
163+
}
164+
165+
return $final;
166+
}
167+
168+
/**
169+
* Transform request content to associative array
170+
*
171+
* @access protected
172+
* @param RequestInterface $request
173+
* @return array
174+
*/
175+
protected function getContentAsParameters(RequestInterface $request)
176+
{
177+
parse_str($request->getContent(), $parts);
178+
179+
return $parts;
180+
}
181+
182+
/**
183+
* Check if specified endpoint is in current request
184+
*
185+
* @param string $endpoint
186+
* @param RequestInterface $request
187+
* @return bool
188+
*/
189+
protected function isEndpointRequested($endpoint, RequestInterface $request)
190+
{
191+
return strpos($request->getResource(), $endpoint) !== false;
192+
}
193+
100194
/**
101195
* Bitbucket supports only HMAC-SHA1 and PlainText signatures.
102196
*
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace Bitbucket\Tests\API\Http\Listener;
4+
5+
use Bitbucket\Tests\API as Tests;
6+
use Bitbucket\API\Http\Listener\OAuthListener;
7+
use Bitbucket\API\Http\Client;
8+
9+
/**
10+
* @author Alexandru G. <alex@gentle.ro>
11+
*/
12+
class OAuthListenerTest extends Tests\TestCase
13+
{
14+
public function testInvalidSignatureShouldFallbackToHmacSha1()
15+
{
16+
$oauth = new OAuthListener(array('oauth_signature_method' => 'dummy'));
17+
$reflection = new \ReflectionClass($oauth);
18+
$property = $reflection->getProperty('signature');
19+
20+
$property->setAccessible(true);
21+
22+
$this->assertInstanceOf('JacobKiers\OAuth\SignatureMethod\HmacSha1', $property->getValue($oauth));
23+
}
24+
25+
public function testTokenInstantiateForOneLegged()
26+
{
27+
$oauth = new OAuthListener(array(
28+
'oauth_consumer_key' => 'aaa',
29+
'oauth_consumer_secret' => 'bbb'
30+
));
31+
32+
$reflection = new \ReflectionClass($oauth);
33+
$property = $reflection->getProperty('token');
34+
35+
$property->setAccessible(true);
36+
37+
$this->assertInstanceOf('JacobKiers\OAuth\Token\NullToken', $property->getValue($oauth));
38+
}
39+
40+
public function testTokenInstantiateforThreeLegged()
41+
{
42+
$oauth = new OAuthListener(array(
43+
'oauth_consumer_key' => 'aaa',
44+
'oauth_consumer_secret' => 'bbb',
45+
'oauth_token' => 'ccc',
46+
'oauth_token_secret' => 'ddd'
47+
));
48+
49+
$reflection = new \ReflectionClass($oauth);
50+
$property = $reflection->getProperty('token');
51+
52+
$property->setAccessible(true);
53+
54+
$this->assertInstanceOf('JacobKiers\OAuth\Token\Token', $property->getValue($oauth));
55+
}
56+
57+
public function testFilterOAuthParameters()
58+
{
59+
$method = $this->getMethod('\Bitbucket\API\Http\Listener\OAuthListener', 'filterOAuthParameters');
60+
$actual = $method->invokeArgs(new OAuthListener(array()), array(array('invalid_option', 'oauth_version')));
61+
62+
$this->assertArrayHasKey('oauth_version', $actual);
63+
$this->assertArrayNotHasKey('invalid_option', $actual);
64+
}
65+
66+
public function testMakeSureRequestIncludesOAuthHeader()
67+
{
68+
$oauth_params = array(
69+
'oauth_consumer_key' => 'aaa',
70+
'oauth_consumer_secret' => 'bbb'
71+
);
72+
$listener = new OAuthListener($oauth_params);
73+
$bb = new \Bitbucket\API\Api($this->getBrowserMock());
74+
75+
$bb->getClient()->addListener($listener);
76+
77+
$bb->requestGet('/dummy');
78+
79+
$auth_header = $bb->getClient()->getLastRequest()->getHeader('Authorization');
80+
81+
$this->assertContains('OAuth', $auth_header);
82+
$this->assertContains(
83+
sprintf('oauth_consumer_key="%s"', $oauth_params['oauth_consumer_key']),
84+
$auth_header
85+
);
86+
}
87+
}

0 commit comments

Comments
 (0)