BackendAuth.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. <?php
  2. /**
  3. * Author: lf
  4. * Blog: https://blog.feehi.com
  5. * Email: job@feehi.com
  6. * Created at: 2018-11-26 12:43
  7. */
  8. namespace console\controllers\helpers;
  9. use Yii;
  10. use ReflectionClass;
  11. use yii\base\Exception;
  12. use yii\helpers\FileHelper;
  13. class BackendAuth extends \yii\base\BaseObject
  14. {
  15. /**
  16. * @var string Directory where you want to generate RBAC Permission rules, often is backend/controllers
  17. */
  18. private $_controllerPath = "@backend/controllers";
  19. /**
  20. * @var array a list of $generateRBACControllerPath directory that to need generate controllers
  21. */
  22. private $_noNeedRBACControllers = ['AssetsController', 'SiteController'];
  23. /**
  24. * @var array a list of none generate RBAc items(execute result, collected the annotations of controller::actions() or controller::actionX())
  25. */
  26. private $_noNeedRBACRoutes = [];
  27. private $_unPropertyDocCommentsRoutes = [];
  28. private $_authItems = [];
  29. private $_dbAuthItems = null;
  30. public $error = null;
  31. public function init()
  32. {
  33. parent::init();
  34. $this->parseAuthItems();
  35. }
  36. public function setControllerPath($controllerPath)
  37. {
  38. $this->_controllerPath = $controllerPath;
  39. }
  40. public function getControllerPath()
  41. {
  42. return Yii::getAlias($this->_controllerPath);
  43. }
  44. public function setNoNeedRBACControllers(array $noNeedRbacControllers)
  45. {
  46. $this->_noNeedRBACControllers = $noNeedRbacControllers;
  47. }
  48. public function getNoNeedRBACControllers()
  49. {
  50. return $this->_noNeedRBACControllers;
  51. }
  52. public function setNoNeedRBACRoutes($routes)
  53. {
  54. $this->_noNeedRBACRoutes = $routes;
  55. }
  56. public function getNoNeedRBACRoutes()
  57. {
  58. return $this->_noNeedRBACRoutes;
  59. }
  60. public function getAuthItems()
  61. {
  62. return $this->_authItems;
  63. }
  64. public function getDbAuthItems()
  65. {
  66. if( $this->_dbAuthItems === null ) {
  67. $dbAuthItems = Yii::$app->getAuthManager()->getPermissions();
  68. $this->_dbAuthItems = $dbAuthItems;
  69. }
  70. return $this->_dbAuthItems;
  71. }
  72. public function setError($error){
  73. $this->error = $error;
  74. }
  75. public function getError()
  76. {
  77. return $this->error;
  78. }
  79. public function parseControllerPartialUrl($subDirControllerName)
  80. {
  81. $subDirControllerName = explode('/', $subDirControllerName);
  82. $controllerName = str_replace('Controller', '', array_pop($subDirControllerName));
  83. $controllerTemp = lcfirst($controllerName);
  84. $controllerString = "";
  85. for ($i = 0; $i < strlen($controllerTemp); $i++) {
  86. $str = ord($controllerTemp[$i]);
  87. if ($str > 64 && $str < 91) {
  88. $controllerString .= '-' . strtolower($controllerTemp[$i]);
  89. } else {
  90. $controllerString .= $controllerTemp[$i];
  91. }
  92. }
  93. $subDirControllerName[] = $controllerString;
  94. return implode('/', $subDirControllerName);
  95. }
  96. public function getActionAuthItemsByDocComment($docComments)
  97. {
  98. if( strpos( $docComments, "@auth - item" ) !== false ){
  99. $temp = explode("@auth", $docComments)[1];
  100. }else if( strpos( $docComments, "@auth" ) !== false ){
  101. $temp = explode("@auth", $docComments)[1];
  102. }else{
  103. return false;
  104. }
  105. if( strpos($temp, '@') !== false ){
  106. $temp = explode('@', $temp)[0];
  107. $temp = trim($temp);
  108. $temp = trim($temp, "*");
  109. $temp = trim($temp);
  110. }else{
  111. $temp = explode('*/', $temp)[0];
  112. $temp = trim($temp);
  113. }
  114. $arr = explode("\n", $temp);
  115. $authItems = [];
  116. foreach ($arr as $s) {
  117. if (strpos($s, '- item') === false) {
  118. continue;
  119. };
  120. $s = explode('- item', $s)[1];
  121. $s = trim($s);
  122. $row = explode(" ", $s);
  123. $authItem = [];
  124. foreach ($row as $v) {
  125. $t = explode("=", $v);
  126. if (isset($t[1])) {
  127. $authItem[$t[0]] = $t[1];
  128. }
  129. }
  130. if( isset( $authItem['rbac'] ) && ($authItem['rbac'] == 'false' || $authItem['rbac'] == 'no' ) ){
  131. $authItems[] = $authItem;
  132. }else {
  133. $httpMethods = [];
  134. if( strpos($authItem['method'], ',') !== false ) {
  135. $httpMethods = explode(',', $authItem['method']);
  136. foreach ($httpMethods as $key => $httpMethod){
  137. $httpMethod = trim($httpMethod);
  138. $httpMethods[$key] = $this->filterAlphabetToLower($httpMethod);
  139. }
  140. }else{
  141. $authItem['method'] = trim($authItem['method']);
  142. $httpMethod = $this->filterAlphabetToLower($authItem['method']);
  143. $httpMethods[] = $httpMethod;
  144. }
  145. $authItem['methods'] = $httpMethods;
  146. unset($authItem['method']);
  147. $authItems[] = $authItem;
  148. }
  149. }
  150. return $authItems;
  151. }
  152. public function parseActionPartialUrl($actionName)
  153. {
  154. $actionTemp = lcfirst($actionName);
  155. $actionString = "";
  156. for ($i = 0; $i < strlen($actionTemp); $i++) {
  157. $str = ord($actionTemp[$i]);
  158. if ($str > 64 && $str < 91) {
  159. $actionString .= '-' . strtolower($actionTemp[$i]);
  160. } else {
  161. $actionString .= $actionTemp[$i];
  162. }
  163. }
  164. return $actionString;
  165. }
  166. public function parseAuthItems()
  167. {
  168. $controllerPath = $this->getControllerPath();
  169. $files = [];
  170. foreach (FileHelper::findFiles($controllerPath) as $file) {
  171. $files[] = str_replace($controllerPath . DIRECTORY_SEPARATOR, '', $file);
  172. }
  173. foreach($files as $file ) {
  174. if( !strpos($file, "Controller") ) continue;
  175. $subDirControllerName = str_replace('.php', '', $file);
  176. if (in_array($subDirControllerName, $this->getNoNeedRBACControllers())) {
  177. Yii::info($subDirControllerName . "不受权限控制,跳过");
  178. continue;
  179. }
  180. $class = new ReflectionClass("\\backend\\controllers\\" . str_replace('/', '\\', $subDirControllerName));
  181. $controllerPartialUrl = $this->parseControllerPartialUrl($subDirControllerName);
  182. $methods = $class->getMethods();
  183. foreach ($methods as $method) {
  184. $actions = [];
  185. $authItems = [];
  186. $actionNum = 0;
  187. if ($method->getName() === 'actions') {
  188. $obj = $class->newInstanceArgs([1, 2]);
  189. $actions = $obj->actions();
  190. if( empty($actions) ) continue;
  191. $authItems = $this->getActionAuthItemsByDocComment($method->getDocComment());
  192. $actionNum = count($actions);
  193. } else if (strpos($method->getName(), 'action') === 0) {
  194. $action = str_replace('action', '', $method->getName());
  195. $authItems = $this->getActionAuthItemsByDocComment($method->getDocComment());
  196. $actions[$action] = $action;
  197. $actionNum = 1;
  198. }
  199. if (!is_array($authItems)) {
  200. continue;
  201. }
  202. if ( count($authItems) !== $actionNum ) {
  203. $this->_unPropertyDocCommentsRoutes[] = $controllerPartialUrl;
  204. $error = "$subDirControllerName::actions或actionX 注释和action数量不匹配 注释(doc comment)数量" . count($authItems) . " 方法(action method)数量" . count($actions) . "(actions or actionX doc comment not equal action method quantity)";
  205. throw new Exception($error);
  206. }
  207. $j = 0;
  208. foreach ($actions as $action => $none) {
  209. $actionPartialUrl = $this->parseActionPartialUrl($action);
  210. $url = '/' . $controllerPartialUrl . '/' . $actionPartialUrl;
  211. if( isset( $authItems[$j]['rbac'] ) && ( in_array($authItems[$j]['rbac'], ['false', 'no']) ) ){
  212. $this->_noNeedRBACRoutes[] = $url;
  213. continue;
  214. }
  215. foreach ($authItems[$j]['methods'] as $httpMethod) {
  216. $httpMethod = strtoupper(trim($httpMethod));
  217. $description = call_user_func(function()use($httpMethod, $authItems, $j){
  218. if( isset($authItems[$j]['description-' . strtolower($httpMethod)]) ){
  219. return $authItems[$j]['description-' . strtolower($httpMethod)];
  220. }
  221. switch ($httpMethod){
  222. case 'GET':
  223. return $authItems[$j]['description'] . '(查看)';
  224. case 'POST':
  225. return $authItems[$j]['description'] . '(确定)';
  226. }
  227. });
  228. $sort = call_user_func(function()use($httpMethod, $authItems, $j){
  229. if( isset($authItems[$j]['sort-' . strtolower($httpMethod)]) ){
  230. return $authItems[$j]['sort-' . strtolower($httpMethod)];
  231. }
  232. $sort = isset( $authItems[$j]['sort'] ) ? $authItems[$j]['sort'] : 0;
  233. return $sort;
  234. });
  235. $this->_authItems = array_merge($this->_authItems, [[
  236. 'name' => $url . ':' . $httpMethod,
  237. 'route' => $url,
  238. 'description' => $description,
  239. 'group' => $authItems[$j]['group'],
  240. 'category' => $authItems[$j]['category'],
  241. 'method' => $httpMethod,
  242. 'sort' => $sort,
  243. ]]);
  244. }
  245. $j++;
  246. }
  247. }
  248. }
  249. }
  250. private function filterAlphabetToLower($str)
  251. {
  252. $alphabet = "";
  253. for ($i = 0; $i < strlen($str); $i++) {
  254. $n = ord($str[$i]);
  255. if ($n > 64 && $n < 123) {
  256. $alphabet .= strtolower($str[$i]);
  257. }
  258. }
  259. return $alphabet;
  260. }
  261. }