/extensions/adapter/security/access/AuthRbac.php

https://github.com/Howard3/li3_access · PHP · 196 lines · 110 code · 29 blank · 57 comment · 19 complexity · 375009790a0dd7a2d021d0e1f8b358f2 MD5 · raw file

  1. <?php
  2. namespace li3_access\extensions\adapter\security\access;
  3. use lithium\security\Auth;
  4. use lithium\core\ConfigException;
  5. use li3_access\security\Access;
  6. class AuthRbac extends \lithium\core\Object {
  7. /**
  8. * @var array $_autoConfig
  9. * @see lithium\core\Object::$_autoConfig
  10. */
  11. protected $_autoConfig = array('roles');
  12. /**
  13. * @var mixed $_roles null if unset array otherwise with fetched AuthObjects
  14. */
  15. protected $_roles = null;
  16. /**
  17. * The `Rbac` adapter will iterate trough the rbac data Array.
  18. *
  19. * @param mixed $user The user data array that holds all necessary information about
  20. * the user requesting access. Or false (because Auth::check() can return false).
  21. * This is an optional parameter, bercause we will fetch the users data trough Auth
  22. * seperately.
  23. * @param object $request The Lithium Request object.
  24. * @param array $options An array of additional options for the _getRolesByAuth method.
  25. * @return Array An empty array if access is allowed and an array with reasons for denial if denied.
  26. */
  27. public function check($requester, $request, array $options = array()) {
  28. if (empty($this->_roles)) {
  29. throw new ConfigException('No roles defined for adapter configuration.');
  30. }
  31. $roleDefaults = array(
  32. 'message' => '',
  33. 'redirect' => '',
  34. 'allow' => true,
  35. 'requesters' => '*',
  36. 'match' => '*::*'
  37. );
  38. $message = $options['message'];
  39. $redirect = $options['redirect'];
  40. $accessable = false;
  41. foreach ($this->_roles as $role) {
  42. $role += $roleDefaults;
  43. // Check to see if this role applies to this request
  44. if (!static::parseMatch($role['match'], $request)) {
  45. continue;
  46. }
  47. $accessable = true;
  48. if (($role['allow'] === false) ||
  49. (!static::_hasRole($role['requesters'], $request, $options)) ||
  50. (is_array($role['allow']) && !static::_parseClosures($role['allow'], $request, $role))
  51. ) {
  52. $accessable = false;
  53. }
  54. if (!$accessable) {
  55. $message = !empty($role['message']) ? $role['message'] : $message;
  56. $redirect = !empty($role['redirect']) ? $role['redirect'] : $redirect;
  57. }
  58. }
  59. return !$accessable ? compact('message', 'redirect') : array();
  60. }
  61. /**
  62. * parseMatch Matches the current request parameters against a set of given parameters.
  63. * Can match against a shorthand string (Controller::action) or a full array. If a parameter
  64. * is provided then it must have an equivilent in the Request objects parmeters in order
  65. * to validate. * Is also acceptable to match a parameter without a specific value.
  66. *
  67. * @param mixed $match A set of parameters to validate the request against.
  68. * @param mixed $request A lithium Request object.
  69. * @access public
  70. * @return boolean True if a match is found.
  71. */
  72. public static function parseMatch($match, $request) {
  73. if (empty($match)) {
  74. return false;
  75. }
  76. if (is_array($match)) {
  77. if (!static::_parseClosures($match, $request)) {
  78. return false;
  79. }
  80. }
  81. $params = array();
  82. foreach ((array) $match as $key => $param) {
  83. if (is_string($param)) {
  84. if (preg_match('/^[A-Za-z0-9_\*]+::[A-Za-z0-9_\*]+$/', $param, $regexMatches)) {
  85. list($controller, $action) = explode('::', reset($regexMatches));
  86. $params += compact('controller', 'action');
  87. continue;
  88. }
  89. }
  90. $params[$key] = $param;
  91. }
  92. foreach ($params as $type => $value) {
  93. if ($value === '*') {
  94. continue;
  95. }
  96. if ($type === 'controller') {
  97. $value = \lithium\util\Inflector::underscore($value);
  98. }
  99. if (!array_key_exists($type, $request->params) || $value !== $request->params[$type]) {
  100. return false;
  101. }
  102. }
  103. return true;
  104. }
  105. /**
  106. * _parseClosures Itterates over an array and runs any anonymous functions it
  107. * finds. Returns true if all of the closures it runs evaluate to true. $match
  108. * is passed by refference and any closures found are removed from it before the
  109. * method is complete.
  110. *
  111. * @param array $data
  112. * @param mixed $request
  113. * @static
  114. * @access protected
  115. * @return void
  116. */
  117. protected static function _parseClosures(array &$data = array(), $request = null, array &$roleOptions = array()) {
  118. $return = true;
  119. foreach ($data as $key => $item) {
  120. if (is_callable($item)) {
  121. if ($return === true) {
  122. $return = (boolean) $item($request, $roleOptions);
  123. }
  124. unset($data[$key]);
  125. }
  126. }
  127. return $return;
  128. }
  129. /**
  130. * @todo reduce Model Overhead (will duplicated in each model)
  131. *
  132. * @param Request $request Object
  133. * @return array|mixed $roles Roles with attachted User Models
  134. */
  135. protected static function _getRolesByAuth($request, array $options = array()){
  136. $roles = array('*' => '*');
  137. foreach (array_keys(Auth::config()) as $key){
  138. if ($check = Auth::check($key, $request, $options)) {
  139. $roles[$key] = $check;
  140. }
  141. }
  142. return $roles = array_filter($roles);
  143. }
  144. /**
  145. * _hasRole Compares the results from _getRolesByAuth with the array passed to it.
  146. *
  147. * @param mixed $requesters
  148. * @param mixed $request
  149. * @param array $options
  150. * @access protected
  151. * @return void
  152. */
  153. protected function _hasRole($requesters, $request, array $options = array()) {
  154. $authed = array_keys(static::_getRolesByAuth($request, $options));
  155. $requesters = (array) $requesters;
  156. if (in_array('*', $requesters)) {
  157. return true;
  158. }
  159. foreach ($requesters as $requester) {
  160. if (in_array($requester, $authed)) {
  161. return true;
  162. }
  163. }
  164. return false;
  165. }
  166. }
  167. ?>