1 <?php
2 3 4 5 6 7
8
9 defined('FOF_INCLUDED') or die;
10
11 12 13 14 15 16 17 18 19 20
21 class FOFDispatcher extends FOFUtilsObject
22 {
23
24 protected $config = array();
25
26
27 protected $input = array();
28
29
30 public $defaultView = 'cpanel';
31
32
33
34
35
36 protected $fofAuth_timeStep = 6;
37
38
39 protected $fofAuth_Key = null;
40
41
42 protected $fofAuth_Formats = array('json', 'csv', 'xml', 'raw');
43
44 45 46 47 48 49
50 protected $fofAuth_LogoutOnReturn = true;
51
52
53 protected $fofAuth_AuthMethods = array(
54 55
56 'HTTPBasicAuth_TOTP',
57 58
59 'QueryString_TOTP',
60
61 'HTTPBasicAuth_Plaintext',
62 63
64 'QueryString_Plaintext',
65 66
67 'SplitQueryString_Plaintext',
68 );
69
70
71 private $_fofAuth_isLoggedIn = false;
72
73
74 private $_fofAuth_CryptoKey = '';
75
76 77 78 79 80 81 82 83 84 85 86
87 public static function &getAnInstance($option = null, $view = null, $config = array())
88 {
89 static $instances = array();
90
91 $hash = $option . $view;
92
93 if (!array_key_exists($hash, $instances))
94 {
95 $instances[$hash] = self::getTmpInstance($option, $view, $config);
96 }
97
98 return $instances[$hash];
99 }
100
101 102 103 104 105 106 107 108 109
110 public static function &getTmpInstance($option = null, $view = null, $config = array())
111 {
112 if (array_key_exists('input', $config))
113 {
114 if ($config['input'] instanceof FOFInput)
115 {
116 $input = $config['input'];
117 }
118 else
119 {
120 if (!is_array($config['input']))
121 {
122 $config['input'] = (array) $config['input'];
123 }
124
125 $config['input'] = array_merge($_REQUEST, $config['input']);
126 $input = new FOFInput($config['input']);
127 }
128 }
129 else
130 {
131 $input = new FOFInput;
132 }
133
134 $config['option'] = !is_null($option) ? $option : $input->getCmd('option', 'com_foobar');
135 $config['view'] = !is_null($view) ? $view : $input->getCmd('view', '');
136
137 $input->set('option', $config['option']);
138 $input->set('view', $config['view']);
139
140 $config['input'] = $input;
141
142 $className = ucfirst(str_replace('com_', '', $config['option'])) . 'Dispatcher';
143
144 if (!class_exists($className))
145 {
146 $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
147
148 $searchPaths = array(
149 $componentPaths['main'],
150 $componentPaths['main'] . '/dispatchers',
151 $componentPaths['admin'],
152 $componentPaths['admin'] . '/dispatchers'
153 );
154
155 if (array_key_exists('searchpath', $config))
156 {
157 array_unshift($searchPaths, $config['searchpath']);
158 }
159
160 $filesystem = FOFPlatform::getInstance()->getIntegrationObject('filesystem');
161
162 $path = $filesystem->pathFind(
163 $searchPaths, 'dispatcher.php'
164 );
165
166 if ($path)
167 {
168 require_once $path;
169 }
170 }
171
172 if (!class_exists($className))
173 {
174 $className = 'FOFDispatcher';
175 }
176
177 $instance = new $className($config);
178
179 return $instance;
180 }
181
182 183 184 185 186
187 public function __construct($config = array())
188 {
189
190 $this->config = $config;
191
192
193 if (array_key_exists('input', $config))
194 {
195 $this->input = $config['input'];
196 }
197 else
198 {
199 $this->input = new FOFInput;
200 }
201
202
203 $this->component = $this->input->getCmd('option', 'com_foobar');
204
205
206 $configProvider = new FOFConfigProvider;
207 $this->defaultView = $configProvider->get($this->component . '.dispatcher.default_view', $this->defaultView);
208
209
210 $this->view = $this->input->getCmd('view', null);
211
212 if (empty($this->view))
213 {
214
215 $task = $this->input->getCmd('task', '');
216
217 if (!empty($task) && (strstr($task, '.') !== false))
218 {
219 list($this->view, $task) = explode('.', $task, 2);
220 $this->input->set('task', $task);
221 }
222 }
223
224 if (empty($this->view))
225 {
226 $this->view = $this->defaultView;
227 }
228
229 $this->layout = $this->input->getCmd('layout', null);
230
231
232 if (array_key_exists('option', $config))
233 {
234 $this->component = $config['option'];
235 }
236
237 if (array_key_exists('view', $config))
238 {
239 $this->view = empty($config['view']) ? $this->view : $config['view'];
240 }
241
242 if (array_key_exists('layout', $config))
243 {
244 $this->layout = $config['layout'];
245 }
246
247 $this->input->set('option', $this->component);
248 $this->input->set('view', $this->view);
249 $this->input->set('layout', $this->layout);
250
251 if (array_key_exists('authTimeStep', $config))
252 {
253 $this->fofAuth_timeStep = empty($config['authTimeStep']) ? 6 : $config['authTimeStep'];
254 }
255 }
256
257 258 259 260 261 262 263 264
265 public function dispatch()
266 {
267 $platform = FOFPlatform::getInstance();
268
269 if (!$platform->authorizeAdmin($this->input->getCmd('option', 'com_foobar')))
270 {
271 return $platform->raiseError(403, JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'));
272 }
273
274 $this->transparentAuthentication();
275
276
277 $platform->loadTranslations($this->component);
278
279 $canDispatch = true;
280
281 if ($platform->isCli())
282 {
283 $canDispatch = $canDispatch && $this->onBeforeDispatchCLI();
284 }
285
286 $canDispatch = $canDispatch && $this->onBeforeDispatch();
287
288 if (!$canDispatch)
289 {
290
291 if(!$platform->isCli())
292 {
293 $platform->setHeader('Status', '403 Forbidden', true);
294 }
295
296 return $platform->raiseError(403, JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'));
297 }
298
299
300 $option = $this->input->getCmd('option', 'com_foobar');
301 $view = $this->input->getCmd('view', $this->defaultView);
302 $task = $this->input->getCmd('task', null);
303
304 if (empty($task))
305 {
306 $task = $this->getTask($view);
307 }
308
309
310 if (in_array($task, array('edit', 'add', 'read')))
311 {
312 $view = FOFInflector::singularize($view);
313 }
314 elseif (in_array($task, array('browse')))
315 {
316 $view = FOFInflector::pluralize($view);
317 }
318
319 $this->input->set('view', $view);
320 $this->input->set('task', $task);
321
322 $config = $this->config;
323 $config['input'] = $this->input;
324
325 $controller = FOFController::getTmpInstance($option, $view, $config);
326 $status = $controller->execute($task);
327
328 if (!$this->onAfterDispatch())
329 {
330
331 if(!$platform->isCli())
332 {
333 $platform->setHeader('Status', '403 Forbidden', true);
334 }
335
336 return $platform->raiseError(403, JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'));
337 }
338
339 $format = $this->input->get('format', 'html', 'cmd');
340 $format = empty($format) ? 'html' : $format;
341
342 if ($controller->hasRedirect())
343 {
344 $controller->redirect();
345 }
346 }
347
348 349 350 351 352 353 354 355
356 protected function getTask($view)
357 {
358
359 $request_task = $this->input->getCmd('task', null);
360 $task = FOFInflector::isPlural($view) ? 'browse' : 'edit';
361
362
363 $id = $this->input->get('id', null, 'int');
364
365 if ($id == 0)
366 {
367 $ids = $this->input->get('ids', array(), 'array');
368
369 if (!empty($ids))
370 {
371 $id = array_shift($ids);
372 }
373 }
374
375
376
377 if (!isset($_SERVER['REQUEST_METHOD']))
378 {
379 $_SERVER['REQUEST_METHOD'] = 'GET';
380 }
381
382 $requestMethod = strtoupper($_SERVER['REQUEST_METHOD']);
383
384 switch ($requestMethod)
385 {
386 case 'POST':
387 case 'PUT':
388 if (!is_null($id))
389 {
390 $task = 'save';
391 }
392 break;
393
394 case 'DELETE':
395 if ($id != 0)
396 {
397 $task = 'delete';
398 }
399 break;
400
401 case 'GET':
402 default:
403
404 if (($task == 'edit') && ($id == 0))
405 {
406 $task = 'add';
407 }
408
409
410 elseif (($task == 'edit') && FOFPlatform::getInstance()->isFrontend())
411 {
412 $task = 'read';
413 }
414 break;
415 }
416
417 return $task;
418 }
419
420 421 422 423 424 425
426 public function onBeforeDispatch()
427 {
428 return true;
429 }
430
431 432 433 434 435
436 public function onBeforeDispatchCLI()
437 {
438 JLoader::import('joomla.environment.uri');
439 JLoader::import('joomla.application.component.helper');
440
441
442 $this->_originalPhpScript = '';
443
444
445 $option = $this->input->get('option', '', 'cmd');
446
447 if ($option)
448 {
449 $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($option);
450
451 if (!defined('JPATH_COMPONENT'))
452 {
453 define('JPATH_COMPONENT', $componentPaths['main']);
454 }
455
456 if (!defined('JPATH_COMPONENT_SITE'))
457 {
458 define('JPATH_COMPONENT_SITE', $componentPaths['site']);
459 }
460
461 if (!defined('JPATH_COMPONENT_ADMINISTRATOR'))
462 {
463 define('JPATH_COMPONENT_ADMINISTRATOR', $componentPaths['admin']);
464 }
465 }
466
467 return true;
468 }
469
470 471 472 473 474
475 public function onAfterDispatch()
476 {
477
478 if ($this->fofAuth_LogoutOnReturn && $this->_fofAuth_isLoggedIn)
479 {
480 FOFPlatform::getInstance()->logoutUser();
481 }
482
483 return true;
484 }
485
486 487 488 489 490
491 public function transparentAuthentication()
492 {
493
494 if (!FOFPlatform::getInstance()->getUser()->guest)
495 {
496 return;
497 }
498
499
500 $format = $this->input->getCmd('format', 'html');
501
502 if (!in_array($format, $this->fofAuth_Formats))
503 {
504 return;
505 }
506
507 foreach ($this->fofAuth_AuthMethods as $method)
508 {
509
510 if ($this->_fofAuth_isLoggedIn)
511 {
512 continue;
513 }
514
515
516 $authInfo = null;
517
518 switch ($method)
519 {
520 case 'HTTPBasicAuth_TOTP':
521
522 if (empty($this->fofAuth_Key))
523 {
524 continue;
525 }
526
527 if (!isset($_SERVER['PHP_AUTH_USER']))
528 {
529 continue;
530 }
531
532 if (!isset($_SERVER['PHP_AUTH_PW']))
533 {
534 continue;
535 }
536
537 if ($_SERVER['PHP_AUTH_USER'] != '_fof_auth')
538 {
539 continue;
540 }
541
542 $encryptedData = $_SERVER['PHP_AUTH_PW'];
543
544 $authInfo = $this->_decryptWithTOTP($encryptedData);
545 break;
546
547 case 'QueryString_TOTP':
548 $encryptedData = $this->input->get('_fofauthentication', '', 'raw');
549
550 if (empty($encryptedData))
551 {
552 continue;
553 }
554
555 $authInfo = $this->_decryptWithTOTP($encryptedData);
556 break;
557
558 case 'HTTPBasicAuth_Plaintext':
559 if (!isset($_SERVER['PHP_AUTH_USER']))
560 {
561 continue;
562 }
563
564 if (!isset($_SERVER['PHP_AUTH_PW']))
565 {
566 continue;
567 }
568
569 $authInfo = array(
570 'username' => $_SERVER['PHP_AUTH_USER'],
571 'password' => $_SERVER['PHP_AUTH_PW']
572 );
573 break;
574
575 case 'QueryString_Plaintext':
576 $jsonencoded = $this->input->get('_fofauthentication', '', 'raw');
577
578 if (empty($jsonencoded))
579 {
580 continue;
581 }
582
583 $authInfo = json_decode($jsonencoded, true);
584
585 if (!is_array($authInfo))
586 {
587 $authInfo = null;
588 }
589 elseif (!array_key_exists('username', $authInfo) || !array_key_exists('password', $authInfo))
590 {
591 $authInfo = null;
592 }
593 break;
594
595 case 'SplitQueryString_Plaintext':
596 $authInfo = array(
597 'username' => $this->input->get('_fofauthentication_username', '', 'raw'),
598 'password' => $this->input->get('_fofauthentication_password', '', 'raw'),
599 );
600
601 if (empty($authInfo['username']))
602 {
603 $authInfo = null;
604 }
605
606 break;
607
608 default:
609 continue;
610
611 break;
612 }
613
614
615 if (!is_array($authInfo))
616 {
617 continue;
618 }
619
620 $this->_fofAuth_isLoggedIn = FOFPlatform::getInstance()->loginUser($authInfo);
621 }
622 }
623
624 625 626 627 628 629 630 631
632 private function _decryptWithTOTP($encryptedData)
633 {
634 if (empty($this->fofAuth_Key))
635 {
636 $this->_fofAuth_CryptoKey = null;
637
638 return null;
639 }
640
641 $totp = new FOFEncryptTotp($this->fofAuth_timeStep);
642 $period = $totp->getPeriod();
643 $period--;
644
645 for ($i = 0; $i <= 2; $i++)
646 {
647 $time = ($period + $i) * $this->fofAuth_timeStep;
648 $otp = $totp->getCode($this->fofAuth_Key, $time);
649 $this->_fofAuth_CryptoKey = hash('sha256', $this->fofAuth_Key . $otp);
650
651 $aes = new FOFEncryptAes($this->_fofAuth_CryptoKey);
652 $ret = $aes->decryptString($encryptedData);
653 $ret = rtrim($ret, "\000");
654
655 $ret = json_decode($ret, true);
656
657 if (!is_array($ret))
658 {
659 continue;
660 }
661
662 if (!array_key_exists('username', $ret))
663 {
664 continue;
665 }
666
667 if (!array_key_exists('password', $ret))
668 {
669 continue;
670 }
671
672
673 return $ret;
674 }
675
676
677 $this->_fofAuth_CryptoKey = null;
678
679 return null;
680 }
681
682 683 684 685 686 687 688 689
690 private function _createDecryptionKey($time = null)
691 {
692 $totp = new FOFEncryptTotp($this->fofAuth_timeStep);
693 $otp = $totp->getCode($this->fofAuth_Key, $time);
694
695 $key = hash('sha256', $this->fofAuth_Key . $otp);
696
697 return $key;
698 }
699
700 701 702 703 704
705 public static function isCliAdmin()
706 {
707 static $isCLI = null;
708 static $isAdmin = null;
709
710 if (is_null($isCLI) && is_null($isAdmin))
711 {
712 $isCLI = FOFPlatform::getInstance()->isCli();
713 $isAdmin = FOFPlatform::getInstance()->isBackend();
714 }
715
716 return array($isCLI, $isAdmin);
717 }
718 }
719