├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── src ├── Adapter │ └── DatabaseAdapter.php ├── Install.php ├── Model │ └── RuleModel.php ├── Permission.php ├── Watcher │ └── RedisWatcher.php └── config │ └── plugin │ └── tinywan │ └── casbin │ ├── app.php │ ├── bootstrap.php │ ├── permission.php │ └── rbac-model.conf └── tests ├── DatabaseAdapterTest.php └── TestCase.php /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | vendor 3 | .idea 4 | .vscode 5 | .phpunit* 6 | composer.lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🔰🔰🔰 迁移说明 2 | > **了为方便维护,源仓库 [webman-casbin](https://github.com/Tinywan/webman-casbin ) 插件贡献给了 [PHP-Casbin](https://github.com/php-casbin/webman-permission) 官方** 3 | 4 | > **为了方便维护,源仓库 [webman-casbin](https://github.com/Tinywan/webman-casbin ) 插件贡献给了 [PHP-Casbin](https://github.com/php-casbin/webman-permission) 官方** 5 | 6 | > **为了方便维护,源仓库 [webman-casbin](https://github.com/Tinywan/webman-casbin ) 插件贡献给了 [PHP-Casbin](https://github.com/php-casbin/webman-permission) 官方** 7 | 8 | ## 🔰🔰🔰 立即使用 https://github.com/php-casbin/webman-permission 9 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinywan/casbin", 3 | "description": "Webman Casbin Plugin", 4 | "type": "library", 5 | "license": "MIT", 6 | "require": { 7 | "php": ">=7.4", 8 | "casbin/casbin": "^3.20", 9 | "topthink/think-orm": "^2.0", 10 | "php-di/php-di": "^6.3", 11 | "workerman/redis": "^1.0" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "Tinywan\\Casbin\\": "src" 16 | } 17 | }, 18 | "autoload-dev": { 19 | "psr-4": { 20 | "Tinywan\\Tests\\": "tests/" 21 | } 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "~7.0|~8.0|~9.0", 25 | "php-coveralls/php-coveralls": "^2.1", 26 | "workerman/webman": "^1.0" 27 | }, 28 | "repositories": { 29 | "packagist": { 30 | "type": "composer", 31 | "url": "https://mirrors.aliyun.com/composer//" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Adapter/DatabaseAdapter.php: -------------------------------------------------------------------------------- 1 | model = $model; 53 | } 54 | 55 | /** 56 | * Filter the rule. 57 | * 58 | * @param array $rule 59 | * @return array 60 | */ 61 | public function filterRule(array $rule): array 62 | { 63 | $rule = array_values($rule); 64 | 65 | $i = count($rule) - 1; 66 | for (; $i >= 0; $i--) { 67 | if ($rule[$i] != '' && !is_null($rule[$i])) { 68 | break; 69 | } 70 | } 71 | 72 | return array_slice($rule, 0, $i + 1); 73 | } 74 | 75 | /** 76 | * savePolicyLine function. 77 | * 78 | * @param string $ptype 79 | * @param array $rule 80 | * 81 | * @return void 82 | */ 83 | public function savePolicyLine($ptype, array $rule) 84 | { 85 | $col['ptype'] = $ptype; 86 | foreach ($rule as $key => $value) { 87 | $col['v' . strval($key) . ''] = $value; 88 | } 89 | $this->model->insert($col); 90 | } 91 | 92 | /** 93 | * loads all policy rules from the storage. 94 | * 95 | * @param Model $model 96 | */ 97 | public function loadPolicy(Model $model): void 98 | { 99 | $rows = $this->model->field(['ptype', 'v0', 'v1', 'v2', 'v3', 'v4', 'v5'])->select()->toArray(); 100 | foreach ($rows as $row) { 101 | // $line = implode(', ', array_filter(array_slice($row, 1), function ($val) { 102 | // return '' != $val && !is_null($val); 103 | // })); 104 | // $this->loadPolicyLine(trim($line), $model); 105 | $this->loadPolicyArray($this->filterRule($row), $model); 106 | } 107 | } 108 | 109 | /** 110 | * saves all policy rules to the storage. 111 | * 112 | * @param Model $model 113 | */ 114 | public function savePolicy(Model $model): void 115 | { 116 | foreach ($model['p'] as $ptype => $ast) { 117 | foreach ($ast->policy as $rule) { 118 | $this->savePolicyLine($ptype, $rule); 119 | } 120 | } 121 | 122 | foreach ($model['g'] as $ptype => $ast) { 123 | foreach ($ast->policy as $rule) { 124 | $this->savePolicyLine($ptype, $rule); 125 | } 126 | } 127 | } 128 | 129 | /** 130 | * adds a policy rule to the storage. 131 | * This is part of the Auto-Save feature. 132 | * 133 | * @param string $sec 134 | * @param string $ptype 135 | * @param array $rule 136 | */ 137 | public function addPolicy(string $sec, string $ptype, array $rule): void 138 | { 139 | $this->savePolicyLine($ptype, $rule); 140 | } 141 | 142 | /** 143 | * Adds a policy rules to the storage. 144 | * This is part of the Auto-Save feature. 145 | * 146 | * @param string $sec 147 | * @param string $ptype 148 | * @param string[][] $rules 149 | */ 150 | public function addPolicies(string $sec, string $ptype, array $rules): void 151 | { 152 | $cols = []; 153 | $i = 0; 154 | 155 | foreach ($rules as $rule) { 156 | $temp['ptype'] = $ptype; 157 | foreach ($rule as $key => $value) { 158 | $temp['v' . strval($key)] = $value; 159 | } 160 | $cols[$i++] = $temp; 161 | $temp = []; 162 | } 163 | $this->model->insertAll($cols); 164 | } 165 | 166 | /** 167 | * This is part of the Auto-Save feature. 168 | * 169 | * @param string $sec 170 | * @param string $ptype 171 | * @param array $rule 172 | */ 173 | public function removePolicy(string $sec, string $ptype, array $rule): void 174 | { 175 | $count = 0; 176 | 177 | $instance = $this->model->where('ptype', $ptype); 178 | 179 | foreach ($rule as $key => $value) { 180 | $instance->where('v' . strval($key), $value); 181 | } 182 | 183 | foreach ($instance->select() as $model) { 184 | if ($model->delete()) { 185 | ++$count; 186 | } 187 | } 188 | } 189 | 190 | /** 191 | * Removes policy rules from the storage. 192 | * This is part of the Auto-Save feature. 193 | * 194 | * @param string $sec 195 | * @param string $ptype 196 | * @param string[][] $rules 197 | */ 198 | public function removePolicies(string $sec, string $ptype, array $rules): void 199 | { 200 | Db::transaction(function () use ($sec, $ptype, $rules) { 201 | foreach ($rules as $rule) { 202 | $this->removePolicy($sec, $ptype, $rule); 203 | } 204 | }); 205 | } 206 | 207 | /** 208 | * @param string $sec 209 | * @param string $ptype 210 | * @param int $fieldIndex 211 | * @param string|null ...$fieldValues 212 | * @return array 213 | * @throws Throwable 214 | */ 215 | public function _removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, ?string ...$fieldValues): array 216 | { 217 | $count = 0; 218 | $removedRules = []; 219 | 220 | $instance = $this->model->where('ptype', $ptype); 221 | foreach (range(0, 5) as $value) { 222 | if ($fieldIndex <= $value && $value < $fieldIndex + count($fieldValues)) { 223 | if ('' != $fieldValues[$value - $fieldIndex]) { 224 | $instance->where('v' . strval($value), $fieldValues[$value - $fieldIndex]); 225 | } 226 | } 227 | } 228 | 229 | foreach ($instance->select() as $model) { 230 | $item = $model->hidden(['id', 'ptype'])->toArray(); 231 | $item = $this->filterRule($item); 232 | $removedRules[] = $item; 233 | if ($model->cache('tauthz')->delete()) { 234 | ++$count; 235 | } 236 | } 237 | 238 | return $removedRules; 239 | } 240 | 241 | /** 242 | * RemoveFilteredPolicy removes policy rules that match the filter from the storage. 243 | * This is part of the Auto-Save feature. 244 | * 245 | * @param string $sec 246 | * @param string $ptype 247 | * @param int $fieldIndex 248 | * @param string ...$fieldValues 249 | */ 250 | public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): void 251 | { 252 | $this->_removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues); 253 | } 254 | 255 | /** 256 | * Updates a policy rule from storage. 257 | * This is part of the Auto-Save feature. 258 | * 259 | * @param string $sec 260 | * @param string $ptype 261 | * @param string[] $oldRule 262 | * @param string[] $newPolicy 263 | */ 264 | public function updatePolicy(string $sec, string $ptype, array $oldRule, array $newPolicy): void 265 | { 266 | $instance = $this->model->where('ptype', $ptype); 267 | foreach ($oldRule as $key => $value) { 268 | $instance->where('v' . strval($key), $value); 269 | } 270 | $instance = $instance->find(); 271 | 272 | foreach ($newPolicy as $key => $value) { 273 | $column = 'v' . strval($key); 274 | $instance->$column = $value; 275 | } 276 | 277 | $instance->save(); 278 | } 279 | 280 | /** 281 | * UpdatePolicies updates some policy rules to storage, like db, redis. 282 | * 283 | * @param string $sec 284 | * @param string $ptype 285 | * @param string[][] $oldRules 286 | * @param string[][] $newRules 287 | * @return void 288 | */ 289 | public function updatePolicies(string $sec, string $ptype, array $oldRules, array $newRules): void 290 | { 291 | Db::transaction(function () use ($sec, $ptype, $oldRules, $newRules) { 292 | foreach ($oldRules as $i => $oldRule) { 293 | $this->updatePolicy($sec, $ptype, $oldRule, $newRules[$i]); 294 | } 295 | }); 296 | } 297 | 298 | /** 299 | * UpdateFilteredPolicies deletes old rules and adds new rules. 300 | * 301 | * @param string $sec 302 | * @param string $ptype 303 | * @param array $newPolicies 304 | * @param integer $fieldIndex 305 | * @param string ...$fieldValues 306 | * @return array 307 | */ 308 | public function updateFilteredPolicies(string $sec, string $ptype, array $newPolicies, int $fieldIndex, string ...$fieldValues): array 309 | { 310 | 311 | $oldRules = []; 312 | DB::transaction(function () use ($sec, $ptype, $fieldIndex, $fieldValues, $newPolicies, &$oldRules) { 313 | $oldRules = $this->_removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues); 314 | $this->addPolicies($sec, $ptype, $newPolicies); 315 | }); 316 | 317 | return $oldRules; 318 | } 319 | 320 | /** 321 | * Returns true if the loaded policy has been filtered. 322 | * 323 | * @return bool 324 | */ 325 | public function isFiltered(): bool 326 | { 327 | return $this->filtered; 328 | } 329 | 330 | /** 331 | * Sets filtered parameter. 332 | * 333 | * @param bool $filtered 334 | */ 335 | public function setFiltered(bool $filtered): void 336 | { 337 | $this->filtered = $filtered; 338 | } 339 | 340 | /** 341 | * Loads only policy rules that match the filter. 342 | * 343 | * @param Model $model 344 | * @param mixed $filter 345 | */ 346 | public function loadFilteredPolicy(Model $model, $filter): void 347 | { 348 | $instance = $this->model; 349 | 350 | if (is_string($filter)) { 351 | $instance = $instance->whereRaw($filter); 352 | } elseif ($filter instanceof Filter) { 353 | foreach ($filter->p as $k => $v) { 354 | $where[$v] = $filter->g[$k]; 355 | $instance = $instance->where($v, $filter->g[$k]); 356 | } 357 | } elseif ($filter instanceof \Closure) { 358 | $instance = $instance->where($filter); 359 | } else { 360 | throw new InvalidFilterTypeException('invalid filter type'); 361 | } 362 | $rows = $instance->select()->hidden(['id'])->toArray(); 363 | foreach ($rows as $row) { 364 | $row = array_filter($row, function ($value) { 365 | return !is_null($value) && $value !== ''; 366 | }); 367 | $line = implode(', ', array_filter($row, function ($val) { 368 | return '' != $val && !is_null($val); 369 | })); 370 | $this->loadPolicyLine(trim($line), $model); 371 | } 372 | $this->setFiltered(true); 373 | } 374 | } -------------------------------------------------------------------------------- /src/Install.php: -------------------------------------------------------------------------------- 1 | 'config/plugin/tinywan/casbin'); 12 | 13 | /** 14 | * Install 15 | * @return void 16 | */ 17 | public static function install() 18 | { 19 | static::installByRelation(); 20 | } 21 | 22 | /** 23 | * Uninstall 24 | * @return void 25 | */ 26 | public static function uninstall() 27 | { 28 | self::uninstallByRelation(); 29 | } 30 | 31 | /** 32 | * installByRelation 33 | * @return void 34 | */ 35 | public static function installByRelation() 36 | { 37 | foreach (static::$pathRelation as $source => $dest) { 38 | if ($pos = strrpos($dest, '/')) { 39 | $parent_dir = base_path().'/'.substr($dest, 0, $pos); 40 | if (!is_dir($parent_dir)) { 41 | mkdir($parent_dir, 0777, true); 42 | } 43 | } 44 | //symlink(__DIR__ . "/$source", base_path()."/$dest"); 45 | copy_dir(__DIR__ . "/$source", base_path()."/$dest"); 46 | } 47 | } 48 | 49 | /** 50 | * uninstallByRelation 51 | * @return void 52 | */ 53 | public static function uninstallByRelation() 54 | { 55 | foreach (static::$pathRelation as $source => $dest) { 56 | $path = base_path()."/$dest"; 57 | if (!is_dir($path) && !is_file($path)) { 58 | continue; 59 | } 60 | /*if (is_link($path) { 61 | unlink($path); 62 | }*/ 63 | remove_dir($path); 64 | } 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/Model/RuleModel.php: -------------------------------------------------------------------------------- 1 | 'int', 28 | 'ptype' => 'string', 29 | 'v0' => 'string', 30 | 'v1' => 'string', 31 | 'v2' => 'string', 32 | 'v3' => 'string', 33 | 'v4' => 'string', 34 | 'v5' => 'string' 35 | ]; 36 | 37 | /** 38 | * 架构函数 39 | * @access public 40 | * @param array $data 数据 41 | */ 42 | public function __construct($data = []) 43 | { 44 | $this->connection = $this->config('database.connection') ?: ''; 45 | $this->table = $this->config('database.rules_table'); 46 | $this->name = $this->config('database.rules_name'); 47 | parent::__construct($data); 48 | } 49 | 50 | /** 51 | * Gets config value by key. 52 | * 53 | * @param string|null $key 54 | * @param null $default 55 | * 56 | * @return mixed 57 | */ 58 | protected function config(string $key = null, $default = null) 59 | { 60 | $driver = config('plugin.tinywan.casbin.permission.default'); 61 | return config('plugin.tinywan.casbin.permission.' . $driver . '.' . $key, $default); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Permission.php: -------------------------------------------------------------------------------- 1 | loadModel(config('plugin.tinywan.casbin.permission.basic.model.config_file_path')); 69 | } elseif ('text' == $configType) { 70 | $model->loadModel(config('plugin.tinywan.casbin.permission.basic.model.config_text')); 71 | } 72 | if (is_null(static::$_manager)) { 73 | static::$_manager = new Enforcer($model, Container::get(config('plugin.tinywan.casbin.permission.basic.adapter')),false); 74 | } 75 | 76 | $watcher = new RedisWatcher(config('redis.default')); 77 | static::$_manager->setWatcher($watcher); 78 | $watcher->setUpdateCallback(function () { 79 | static::$_manager->loadPolicy(); 80 | }); 81 | } 82 | } 83 | 84 | /** 85 | * @param $name 86 | * @param $arguments 87 | * @return mixed 88 | * @author Tinywan(ShaoBo Wan) 89 | */ 90 | public static function __callStatic($name, $arguments) 91 | { 92 | return static::$_manager->{$name}(...$arguments); 93 | } 94 | } -------------------------------------------------------------------------------- /src/Watcher/RedisWatcher.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 32 | * 'password' => '', 33 | * 'port' => 6379, 34 | * 'database' => 0, 35 | * 'channel' => '/casbin', 36 | * ] 37 | */ 38 | public function __construct(array $config) 39 | { 40 | $this->pubRedis = $this->createRedisClient($config); 41 | $this->subRedis = $this->createRedisClient($config); 42 | $this->channel = $config['channel'] ?? '/casbin'; 43 | 44 | $this->subRedis->subscribe([$this->channel], function ($channel, $message) { 45 | if ($this->callback) { 46 | call_user_func($this->callback); 47 | } 48 | }); 49 | } 50 | 51 | /** 52 | * Sets the callback function that the watcher will call when the policy in DB has been changed by other instances. 53 | * A classic callback is loadPolicy() method of Enforcer class. 54 | * 55 | * @param Closure $func 56 | */ 57 | public function setUpdateCallback(Closure $func): void 58 | { 59 | $this->callback = $func; 60 | } 61 | 62 | /** 63 | * Update calls the update callback of other instances to synchronize their policy. 64 | * It is usually called after changing the policy in DB, like savePolicy() method of Enforcer class, 65 | * addPolicy(), removePolicy(), etc. 66 | */ 67 | public function update(): void 68 | { 69 | $this->pubRedis->publish($this->channel, 'casbin rules updated'); 70 | } 71 | 72 | /** 73 | * Close stops and releases the watcher, the callback function will not be called any more. 74 | */ 75 | public function close(): void 76 | { 77 | $this->pubRedis->close(); 78 | $this->subRedis->close(); 79 | } 80 | 81 | /** 82 | * Create redis client 83 | * 84 | * @param array $config 85 | * @return Client 86 | */ 87 | private function createRedisClient(array $config): Client 88 | { 89 | $config['host'] = $config['host'] ?? '127.0.0.1'; 90 | $config['port'] = $config['port'] ?? 6379; 91 | $config['password'] = $config['password'] ?? ''; 92 | $config['database'] = $config['database'] ?? 0; 93 | 94 | $redis = new Client('redis://' . $config['host'] . ':' . $config['port']); 95 | $redis->auth($config['password'] ?? ''); 96 | 97 | return $redis; 98 | } 99 | } -------------------------------------------------------------------------------- /src/config/plugin/tinywan/casbin/app.php: -------------------------------------------------------------------------------- 1 | true, 4 | ]; -------------------------------------------------------------------------------- /src/config/plugin/tinywan/casbin/bootstrap.php: -------------------------------------------------------------------------------- 1 | 'basic', 9 | 'basic' => [ 10 | # Model 设置 11 | 'model' => [ 12 | 'config_type' => 'file', 13 | 'config_file_path' => config_path() . '/plugin/tinywan/casbin/rbac-model.conf', 14 | 'config_text' => '', 15 | ], 16 | # 适配器 17 | 'adapter' => \Tinywan\Casbin\Adapter\DatabaseAdapter::class, 18 | 'database' => [ 19 | 'connection' => '', 20 | 'rules_table' => 'casbin_rule', 21 | 'rules_name' => null 22 | ], 23 | ] 24 | ]; -------------------------------------------------------------------------------- /src/config/plugin/tinywan/casbin/rbac-model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act -------------------------------------------------------------------------------- /tests/DatabaseAdapterTest.php: -------------------------------------------------------------------------------- 1 | delete(); 23 | RuleModel::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => 'data1', 'v2' => 'read']); 24 | RuleModel::create(['ptype' => 'p', 'v0' => 'bob', 'v1' => 'data2', 'v2' => 'write']); 25 | RuleModel::create(['ptype' => 'p', 'v0' => 'data2_admin', 'v1' => 'data2', 'v2' => 'read']); 26 | RuleModel::create(['ptype' => 'p', 'v0' => 'data2_admin', 'v1' => 'data2', 'v2' => 'write']); 27 | RuleModel::create(['ptype' => 'g', 'v0' => 'alice', 'v1' => 'data2_admin']); 28 | } 29 | 30 | protected function _setUp() 31 | { 32 | $this->initTable(); 33 | $this->start(); 34 | } 35 | 36 | } --------------------------------------------------------------------------------