1: <?php
2:
3: namespace Guzzle\Plugin\Oauth;
4:
5: use Guzzle\Common\Event;
6: use Guzzle\Common\Collection;
7: use Guzzle\Http\Message\RequestInterface;
8: use Guzzle\Http\Message\EntityEnclosingRequestInterface;
9: use Guzzle\Http\QueryString;
10: use Guzzle\Http\Url;
11: use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12:
13: 14: 15: 16:
17: class OauthPlugin implements EventSubscriberInterface
18: {
19: 20: 21:
22: protected $config;
23:
24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
39: public function __construct($config)
40: {
41: $this->config = Collection::fromConfig($config, array(
42: 'version' => '1.0',
43: 'consumer_key' => 'anonymous',
44: 'consumer_secret' => 'anonymous',
45: 'signature_method' => 'HMAC-SHA1',
46: 'signature_callback' => function($stringToSign, $key) {
47: return hash_hmac('sha1', $stringToSign, $key, true);
48: }
49: ), array(
50: 'signature_method', 'signature_callback', 'version',
51: 'consumer_key', 'consumer_secret'
52: ));
53: }
54:
55: 56: 57:
58: public static function getSubscribedEvents()
59: {
60: return array(
61: 'request.before_send' => array('onRequestBeforeSend', -1000)
62: );
63: }
64:
65: 66: 67: 68: 69: 70:
71: public function onRequestBeforeSend(Event $event)
72: {
73: $timestamp = $this->getTimestamp($event);
74: $request = $event['request'];
75: $nonce = $this->generateNonce($request);
76:
77: $authorizationParams = array(
78: 'oauth_callback' => $this->config['callback'],
79: 'oauth_consumer_key' => $this->config['consumer_key'],
80: 'oauth_nonce' => $nonce,
81: 'oauth_signature' => $this->getSignature($request, $timestamp, $nonce),
82: 'oauth_signature_method' => $this->config['signature_method'],
83: 'oauth_timestamp' => $timestamp,
84: 'oauth_token' => $this->config['token'],
85: 'oauth_verifier' => $this->config['verifier'],
86: 'oauth_version' => $this->config['version'],
87: );
88:
89: $request->setHeader(
90: 'Authorization',
91: $this->buildAuthorizationHeader($authorizationParams)
92: );
93:
94: return $authorizationParams;
95: }
96:
97: 98: 99: 100: 101: 102: 103:
104: private function ($authorizationParams)
105: {
106: $authorizationString = 'OAuth ';
107: foreach ($authorizationParams as $key => $val) {
108: if ($val) {
109: $authorizationString .= $key . '="' . urlencode($val) . '", ';
110: }
111: }
112:
113: return substr($authorizationString, 0, -2);
114: }
115:
116: 117: 118: 119: 120: 121: 122: 123: 124:
125: public function getSignature(RequestInterface $request, $timestamp, $nonce)
126: {
127: $string = $this->getStringToSign($request, $timestamp, $nonce);
128: $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']);
129:
130: return base64_encode(call_user_func($this->config['signature_callback'], $string, $key));
131: }
132:
133: 134: 135: 136: 137: 138: 139: 140: 141:
142: public function getStringToSign(RequestInterface $request, $timestamp, $nonce)
143: {
144: $params = $this->getParamsToSign($request, $timestamp, $nonce);
145:
146:
147: $params = $this->prepareParameters($params);
148:
149:
150: $parameterString = new QueryString($params);
151:
152: $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null);
153:
154: return strtoupper($request->getMethod()) . '&'
155: . rawurlencode($url) . '&'
156: . rawurlencode((string) $parameterString);
157: }
158:
159: 160: 161: 162: 163: 164: 165: 166: 167:
168: public function getParamsToSign(RequestInterface $request, $timestamp, $nonce)
169: {
170: $params = new Collection(array(
171: 'oauth_callback' => $this->config['callback'],
172: 'oauth_consumer_key' => $this->config['consumer_key'],
173: 'oauth_nonce' => $nonce,
174: 'oauth_signature_method' => $this->config['signature_method'],
175: 'oauth_timestamp' => $timestamp,
176: 'oauth_token' => $this->config['token'],
177: 'oauth_verifier' => $this->config['verifier'],
178: 'oauth_version' => $this->config['version']
179: ));
180:
181:
182: $params->merge($request->getQuery());
183:
184:
185: if ($this->shouldPostFieldsBeSigned($request))
186: {
187: $params->merge($request->getPostFields());
188: }
189:
190:
191: $params = $params->getAll();
192: ksort($params);
193:
194: return $params;
195: }
196:
197: 198: 199: 200: 201: 202: 203: 204: 205:
206: public function shouldPostFieldsBeSigned($request)
207: {
208: if (!$this->config->get('disable_post_params') &&
209: $request instanceof EntityEnclosingRequestInterface &&
210: false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded'))
211: {
212: return true;
213: }
214:
215: return false;
216: }
217:
218: 219: 220: 221: 222: 223: 224: 225:
226: public function generateNonce(RequestInterface $request)
227: {
228: return sha1(uniqid('', true) . $request->getUrl());
229: }
230:
231: 232: 233: 234: 235: 236: 237:
238: public function getTimestamp(Event $event)
239: {
240: return $event['timestamp'] ?: time();
241: }
242:
243: 244: 245: 246: 247: 248: 249:
250: protected function prepareParameters($data)
251: {
252: ksort($data);
253: foreach ($data as $key => &$value) {
254: switch (gettype($value)) {
255: case 'NULL':
256: unset($data[$key]);
257: break;
258: case 'array':
259: $data[$key] = self::prepareParameters($value);
260: break;
261: case 'boolean':
262: $data[$key] = $value ? 'true' : 'false';
263: break;
264: }
265: }
266:
267: return $data;
268: }
269: }
270: