Gentics Portal.Node PHP API
 All Classes Namespaces Functions Variables Pages
EAuthServiceBase.php
1 <?php
2 /**
3  * EAuthServiceBase class file.
4  *
5  * @author Maxim Zemskov <nodge@yandex.ru>
6  * @link http://code.google.com/p/yii-eauth/
7  * @license http://www.opensource.org/licenses/bsd-license.php
8  */
9 
10 require_once 'IAuthService.php';
11 
12 /**
13  * EAuthServiceBase is a base class for providers.
14  * @package application.extensions.eauth
15  */
16 abstract class EAuthServiceBase extends CComponent implements IAuthService {
17 
18  /**
19  * @var string the service name.
20  */
21  protected $name;
22 
23  /**
24  *
25  * @var string the service title to display in views.
26  */
27  protected $title;
28 
29  /**
30  * @var string the service type (e.g. OpenID, OAuth).
31  */
32  protected $type;
33 
34  /**
35  * @var array arguments for the jQuery.eauth() javascript function.
36  */
37  protected $jsArguments = array();
38 
39  /**
40  * @var array authorization attributes.
41  * @see getAttribute
42  * @see getItem
43  */
44  protected $attributes = array();
45 
46  /**
47  * @var boolean whether user was successfuly authenticated.
48  * @see getIsAuthenticated
49  */
50  protected $authenticated = false;
51 
52  /**
53  * @var boolean whether is attributes was fetched.
54  */
55  private $fetched = false;
56 
57  /**
58  * @var EAuth the {@link EAuth} application component.
59  */
60  private $component;
61 
62  /**
63  * @var string the redirect url after successful authorization.
64  */
65  private $redirectUrl = '';
66 
67  /**
68  * @var string the redirect url after unsuccessful authorization (e.g. user canceled).
69  */
70  private $cancelUrl = '';
71 
72  /**
73  * PHP getter magic method.
74  * This method is overridden so that service attributes can be accessed like properties.
75  * @param string $name property name.
76  * @return mixed property value.
77  * @see getAttribute
78  */
79  public function __get($name) {
80  if ($this->hasAttribute($name))
81  return $this->getAttribute($name);
82  else
83  return parent::__get($name);
84  }
85 
86  /**
87  * Checks if a attribute value is null.
88  * This method overrides the parent implementation by checking
89  * if the attribute is null or not.
90  * @param string $name the attribute name.
91  * @return boolean whether the attribute value is null.
92  */
93  public function __isset($name) {
94  if ($this->hasAttribute($name))
95  return true;
96  else
97  return parent::__isset($name);
98  }
99 
100  /**
101  * Initialize the component.
102  * Sets the default {@link redirectUrl} and {@link cancelUrl}.
103  * @param EAuth $component the component instance.
104  * @param array $options properties initialization.
105  */
106  public function init($component, $options = array()) {
107  if (isset($component))
108  $this->setComponent($component);
109 
110  foreach ($options as $key => $val)
111  $this->$key = $val;
112 
113  $this->setRedirectUrl(Yii::app()->user->returnUrl);
114  $server = Yii::app()->request->getHostInfo();
115  $path = Yii::app()->request->getPathInfo();
116  $this->setCancelUrl($server.'/'.$path);
117  }
118 
119  /**
120  * Returns service name(id).
121  * @return string the service name(id).
122  */
123  public function getServiceName() {
124  return $this->name;
125  }
126 
127  /**
128  * Returns service title.
129  * @return string the service title.
130  */
131  public function getServiceTitle() {
132  return $this->title;
133  }
134 
135  /**
136  * Returns service type (e.g. OpenID, OAuth).
137  * @return string the service type (e.g. OpenID, OAuth).
138  */
139  public function getServiceType() {
140  return $this->type;
141  }
142 
143  /**
144  * Returns arguments for the jQuery.eauth() javascript function.
145  * @return array the arguments for the jQuery.eauth() javascript function.
146  */
147  public function getJsArguments() {
148  return $this->jsArguments;
149  }
150 
151  /**
152  * Sets {@link EAuth} application component
153  * @param EAuth $component the application auth component.
154  */
155  public function setComponent($component) {
156  $this->component = $component;
157  }
158 
159  /**
160  * Returns the {@link EAuth} application component.
161  * @return EAuth the {@link EAuth} application component.
162  */
163  public function getComponent() {
164  return $this->component;
165  }
166 
167  /**
168  * Sets redirect url after successful authorization.
169  * @param string url to redirect.
170  */
171  public function setRedirectUrl($url) {
172  $this->redirectUrl = $url;
173  }
174 
175  /**
176  * Returns the redirect url after successful authorization.
177  * @return string the redirect url after successful authorization.
178  */
179  public function getRedirectUrl() {
180  return $this->redirectUrl;
181  }
182 
183  /**
184  * Sets redirect url after unsuccessful authorization (e.g. user canceled).
185  * @param string url to redirect.
186  */
187  public function setCancelUrl($url) {
188  $this->cancelUrl = $url;
189  }
190 
191  /**
192  * Returns the redirect url after unsuccessful authorization (e.g. user canceled).
193  * @return string the redirect url after unsuccessful authorization (e.g. user canceled).
194  */
195  public function getCancelUrl() {
196  return $this->cancelUrl;
197  }
198 
199  /**
200  * Authenticate the user.
201  * @return boolean whether user was successfuly authenticated.
202  */
203  public function authenticate() {
204  return $this->getIsAuthenticated();
205  }
206 
207  /**
208  * Whether user was successfuly authenticated.
209  * @return boolean whether user was successfuly authenticated.
210  */
211  public function getIsAuthenticated() {
212  return $this->authenticated;
213  }
214 
215  /**
216  * Redirect to the url. If url is null, {@link redirectUrl} will be used.
217  * @param string $url url to redirect.
218  */
219  public function redirect($url = null) {
220  $this->component->redirect(isset($url) ? $url : $this->redirectUrl, true);
221  }
222 
223  /**
224  * Redirect to the {@link cancelUrl} or simply close the popup window.
225  */
226  public function cancel($url = null) {
227  $this->component->redirect(isset($url) ? $url : $this->cancelUrl, false);
228  }
229 
230  /**
231  * Makes the curl request to the url.
232  * @param string $url url to request.
233  * @param array $options HTTP request options. Keys: query, data, referer.
234  * @param boolean $parseJson Whether to parse response in json format.
235  * @return string the response.
236  */
237  protected function makeRequest($url, $options = array(), $parseJson = true) {
238  $ch = $this->initRequest($url, $options);
239 
240  if (isset($options['referer']))
241  curl_setopt($ch, CURLOPT_REFERER, $options['referer']);
242 
243  if (isset($options['query'])) {
244  $url_parts = parse_url($url);
245  if (isset($url_parts['query'])) {
246  $old_query = http_build_query($url_parts['query']);
247  $url_parts['query'] = array_merge($url_parts['query'], $options['query']);
248  $new_query = http_build_query($url_parts['query']);
249  $url = str_replace($old_query, $new_query, $url);
250  }
251  else {
252  $url_parts['query'] = $options['query'];
253  $new_query = http_build_query($url_parts['query']);
254  $url .= '?'.$new_query;
255  }
256  }
257 
258  if (isset($options['data'])) {
259  curl_setopt($ch, CURLOPT_POST, 1);
260  curl_setopt($ch, CURLOPT_POSTFIELDS, $options['data']);
261  }
262 
263  curl_setopt($ch, CURLOPT_URL, $url);
264 
265  $result = curl_exec($ch);
266  $headers = curl_getinfo($ch);
267 
268  if (curl_errno($ch) > 0)
269  throw new EAuthException(curl_error($ch), curl_errno($ch));
270 
271  if ($headers['http_code'] != 200) {
272  Yii::log(
273  'Invalid response http code: '.$headers['http_code'].'.'.PHP_EOL.
274  'URL: '.$url.PHP_EOL.
275  'Options: '.var_export($options, true).PHP_EOL.
276  'Result: '.$result,
277  CLogger::LEVEL_ERROR, 'application.extensions.eauth'
278  );
279  throw new EAuthException(Yii::t('eauth', 'Invalid response http code: {code}.', array('{code}' => $headers['http_code'])), $headers['http_code']);
280  }
281 
282  curl_close($ch);
283 
284  if ($parseJson)
285  $result = $this->parseJson($result);
286 
287  return $result;
288  }
289 
290  /**
291  * Initializes a new session and return a cURL handle.
292  * @param string $url url to request.
293  * @param array $options HTTP request options. Keys: query, data, referer.
294  * @param boolean $parseJson Whether to parse response in json format.
295  * @return cURL handle.
296  */
297  protected function initRequest($url, $options = array()) {
298  $ch = curl_init();
299  //curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // error with open_basedir or safe mode
300  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
301  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
302  curl_setopt($ch, CURLOPT_HEADER, 0);
303  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
304  curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
305  return $ch;
306  }
307 
308  /**
309  * Parse response from {@link makeRequest} in json format and check OAuth errors.
310  * @param string $response Json string.
311  * @return object result.
312  */
313  protected function parseJson($response) {
314  try {
315  $result = json_decode($response);
316  $error = $this->fetchJsonError($result);
317  if (!isset($result)) {
318  throw new EAuthException(Yii::t('eauth', 'Invalid response format.', array()), 500);
319  }
320  else if (isset($error)) {
321  throw new EAuthException($error['message'], $error['code']);
322  }
323  else
324  return $result;
325  }
326  catch(Exception $e) {
327  throw new EAuthException($e->getMessage(), $e->getCode());
328  }
329  }
330 
331  /**
332  * Returns the error info from json.
333  * @param stdClass $json the json response.
334  * @return array the error array with 2 keys: code and message. Should be null if no errors.
335  */
336  protected function fetchJsonError($json) {
337  if (isset($json->error)) {
338  return array(
339  'code' => 500,
340  'message' => 'Unknown error occurred.',
341  );
342  }
343  else
344  return null;
345  }
346 
347  /**
348  * @return string a prefix for the name of the session variables storing eauth session data.
349  */
350  protected function getStateKeyPrefix() {
351  return '__eauth_'.$this->getServiceName().'__';
352  }
353 
354  /**
355  * Stores a variable in eauth session.
356  * @param string $key variable name.
357  * @param mixed $value variable value.
358  * @param mixed $defaultValue default value. If $value===$defaultValue, the variable will be
359  * removed from the session.
360  * @see getState
361  */
362  protected function setState($key, $value, $defaultValue = null) {
363  $session = Yii::app()->session;
364  $key = $this->getStateKeyPrefix().$key;
365  if($value === $defaultValue)
366  unset($session[$key]);
367  else
368  $session[$key] = $value;
369  }
370 
371  /**
372  * Returns a value indicating whether there is a state of the specified name.
373  * @param string $key state name.
374  * @return boolean whether there is a state of the specified name.
375  */
376  protected function hasState($key) {
377  $session = Yii::app()->session;
378  $key = $this->getStateKeyPrefix().$key;
379  return isset($session[$key]);
380  }
381 
382  /**
383  * Returns the value of a variable that is stored in eauth session.
384  * @param string $key variable name.
385  * @param mixed $defaultValue default value.
386  * @return mixed the value of the variable. If it doesn't exist in the session,
387  * the provided default value will be returned.
388  * @see setState
389  */
390  protected function getState($key, $defaultValue = null) {
391  $session = Yii::app()->session;
392  $key = $this->getStateKeyPrefix().$key;
393  return isset($session[$key]) ? $session[$key] : $defaultValue;
394  }
395 
396  /**
397  * Fetch attributes array.
398  * @return boolean whether the attributes was successfully fetched.
399  */
400  protected function fetchAttributes() {
401  return true;
402  }
403 
404  /**
405  * Fetch attributes array.
406  * This function is internally used to handle fetched state.
407  */
408  protected function _fetchAttributes() {
409  if (!$this->fetched) {
410  $this->fetched = true;
411  $result = $this->fetchAttributes();
412  if (isset($result))
413  $this->fetched = $result;
414  }
415  }
416 
417  /**
418  * Returns the user unique id.
419  * @return mixed the user id.
420  */
421  public function getId() {
422  $this->_fetchAttributes();
423  return $this->attributes['id'];
424  }
425 
426  /**
427  * Returns the array that contains all available authorization attributes.
428  * @return array the attributes.
429  */
430  public function getAttributes() {
431  $this->_fetchAttributes();
432  $attributes = array();
433  foreach ($this->attributes as $key => $val) {
434  $attributes[$key] = $this->getAttribute($key);
435  }
436  return $attributes;
437  }
438 
439  /**
440  * Returns the authorization attribute value.
441  * @param string $key the attribute name.
442  * @param mixed $default the default value.
443  * @return mixed the attribute value.
444  */
445  public function getAttribute($key, $default = null) {
446  $this->_fetchAttributes();
447  $getter = 'get'.$key;
448  if (method_exists($this, $getter))
449  return $this->$getter();
450  else
451  return isset($this->attributes[$key]) ? $this->attributes[$key] : $default;
452  }
453 
454  /**
455  * Whether the authorization attribute exists.
456  * @param string $key the attribute name.
457  * @return boolean true if attribute exists, false otherwise.
458  */
459  public function hasAttribute($key) {
460  $this->_fetchAttributes();
461  return isset($this->attributes[$key]);
462  }
463 
464  /**
465  * Returns the object with a human-readable representation of the current authorization.
466  * @return stdClass the object.
467  */
468  public function getItem() {
469  $item = new stdClass;
470  $item->title = $this->getAttribute('name');
471  if (empty($this->title))
472  $item->title = $this->getId();
473  if ($this->hasAttribute('url'))
474  $item->url = $this->getAttribute('url');
475  return $item;
476  }
477 
478  /**
479  * Returns the array that contains all available authorization attributes.
480  * @return array the attributes.
481  * @deprecated because getAttributes is more semantic.
482  */
483  public function getItemAttributes() {
484  return $this->getAttributes();
485  }
486 }