.gitignore000064400000000113152475270340006540 0ustar00/vendor/ composer.lock .idea/ *.iml # coverage report /build .phpunit.*.travis.yml000064400000000455152475270340006672 0ustar00language: php matrix: fast_finish: true include: - php: 7.1 - php: 7.2 - php: 7.3 - php: 7.4snapshot install: - composer install --prefer-dist --dev --no-interaction script: - mkdir -p build/logs - vendor/bin/phpunit after_script: - travis_retry vendor/bin/php-coveralls -v LICENSE000064400000026135152475270340005571 0ustar00 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. README.md000064400000033472152475270340006045 0ustar00PHP-Casbin ==== [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/php-casbin/php-casbin/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/php-casbin/php-casbin/?branch=master) [![Build Status](https://travis-ci.org/php-casbin/php-casbin.svg?branch=master)](https://travis-ci.org/php-casbin/php-casbin) [![Coverage Status](https://coveralls.io/repos/github/php-casbin/php-casbin/badge.svg)](https://coveralls.io/github/php-casbin/php-casbin) [![Latest Stable Version](https://poser.pugx.org/casbin/casbin/v/stable)](https://packagist.org/packages/casbin/casbin) [![Total Downloads](https://poser.pugx.org/casbin/casbin/downloads)](https://packagist.org/packages/casbin/casbin) [![License](https://poser.pugx.org/casbin/casbin/license)](https://packagist.org/packages/casbin/casbin) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/casbin/lobby) [Documentation](https://casbin.org/docs/en/overview) | [Tutorials](https://github.com/php-casbin/casbin-tutorials) | [Extensions](https://github.com/php-casbin) **Breaking News**: [Laravel-authz](https://github.com/php-casbin/laravel-authz) is now available, an authorization library for the Laravel framework. **PHP-Casbin** is a powerful and efficient open-source access control library for PHP projects. It provides support for enforcing authorization based on various [access control models](https://en.wikipedia.org/wiki/Computer_security_model). ## All the languages supported by Casbin: [![golang](https://casbin.org/img/langs/golang.png)](https://github.com/casbin/casbin) | [![java](https://casbin.org/img/langs/java.png)](https://github.com/casbin/jcasbin) | [![nodejs](https://casbin.org/img/langs/nodejs.png)](https://github.com/casbin/node-casbin) | [![php](https://casbin.org/img/langs/php.png)](https://github.com/php-casbin/php-casbin) ----|----|----|---- [Casbin](https://github.com/casbin/casbin) | [jCasbin](https://github.com/casbin/jcasbin) | [node-Casbin](https://github.com/casbin/node-casbin) | [PHP-Casbin](https://github.com/php-casbin/php-casbin) production-ready | production-ready | production-ready | production-ready [![python](https://casbin.org/img/langs/python.png)](https://github.com/casbin/pycasbin) | [![dotnet](https://casbin.org/img/langs/dotnet.png)](https://github.com/casbin-net/Casbin.NET) | [![delphi](https://casbin.org/img/langs/delphi.png)](https://github.com/casbin4d/Casbin4D) | [![rust](https://casbin.org/img/langs/rust.png)](https://github.com/Devolutions/casbin-rs) ----|----|----|---- [PyCasbin](https://github.com/casbin/pycasbin) | [Casbin.NET](https://github.com/casbin-net/Casbin.NET) | [Casbin4D](https://github.com/casbin4d/Casbin4D) | [Casbin-RS](https://github.com/Devolutions/casbin-rs) production-ready | production-ready | experimental | WIP ## Installation Require this package in the `composer.json` of your project. This will download the package: ``` composer require casbin/casbin ``` ## Get started 1. New a Casbin enforcer with a model file and a policy file: ```php require_once './vendor/autoload.php'; use Casbin\Enforcer; $e = new Enforcer("path/to/model.conf", "path/to/policy.csv"); ``` 2. Add an enforcement hook into your code right before the access happens: ```php $sub = "alice"; // the user that wants to access a resource. $obj = "data1"; // the resource that is going to be accessed. $act = "read"; // the operation that the user performs on the resource. if ($e->enforce($sub, $obj, $act) === true) { // permit alice to read data1 } else { // deny the request, show an error } ``` ## Table of contents - [Supported models](#supported-models) - [How it works?](#how-it-works) - [Features](#features) - [Documentation](#documentation) - [Online editor](#online-editor) - [Tutorials](#tutorials) - [Policy management](#policy-management) - [Policy persistence](#policy-persistence) - [Role manager](#role-manager) - [Examples](#examples) - [Middlewares](#middlewares) - [Our adopters](#our-adopters) ## Supported models 1. [**ACL (Access Control List)**](https://en.wikipedia.org/wiki/Access_control_list) 2. **ACL with [superuser](https://en.wikipedia.org/wiki/Superuser)** 3. **ACL without users**: especially useful for systems that don't have authentication or user log-ins. 3. **ACL without resources**: some scenarios may target for a type of resources instead of an individual resource by using permissions like ``write-article``, ``read-log``. It doesn't control the access to a specific article or log. 4. **[RBAC (Role-Based Access Control)](https://en.wikipedia.org/wiki/Role-based_access_control)** 5. **RBAC with resource roles**: both users and resources can have roles (or groups) at the same time. 6. **RBAC with domains/tenants**: users can have different role sets for different domains/tenants. 7. **[ABAC (Attribute-Based Access Control)](https://en.wikipedia.org/wiki/Attribute-Based_Access_Control)**: syntax sugar like ``resource.Owner`` can be used to get the attribute for a resource. 8. **[RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer)**: supports paths like ``/res/*``, ``/res/:id`` and HTTP methods like ``GET``, ``POST``, ``PUT``, ``DELETE``. 9. **Deny-override**: both allow and deny authorizations are supported, deny overrides the allow. 10. **Priority**: the policy rules can be prioritized like firewall rules. ## How it works? In php-casbin, an access control model is abstracted into a CONF file based on the **PERM metamodel (Policy, Effect, Request, Matchers)**. So switching or upgrading the authorization mechanism for a project is just as simple as modifying a configuration. You can customize your own access control model by combining the available models. For example, you can get RBAC roles and ABAC attributes together inside one model and share one set of policy rules. The most basic and simplest model in php-casbin is ACL. ACL's model CONF is: ```ini # Request definition [request_definition] r = sub, obj, act # Policy definition [policy_definition] p = sub, obj, act # Policy effect [policy_effect] e = some(where (p.eft == allow)) # Matchers [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act ``` An example policy for ACL model is like: ``` p, alice, data1, read p, bob, data2, write ``` It means: - alice can read data1 - bob can write data2 ## Features What php-casbin does: 1. enforce the policy in the classic ``{subject, object, action}`` form or a customized form as you defined, both allow and deny authorizations are supported. 2. handle the storage of the access control model and its policy. 3. manage the role-user mappings and role-role mappings (aka role hierarchy in RBAC). 4. support built-in superuser like ``root`` or ``administrator``. A superuser can do anything without explict permissions. 5. multiple built-in operators to support the rule matching. For example, ``keyMatch`` can map a resource key ``/foo/bar`` to the pattern ``/foo*``. What php-casbin does NOT do: 1. authentication (aka verify ``username`` and ``password`` when a user logs in) 2. manage the list of users or roles. I believe it's more convenient for the project itself to manage these entities. Users usually have their passwords, and php-casbin is not designed as a password container. However, php-casbin stores the user-role mapping for the RBAC scenario. ## Documentation https://casbin.org/docs/en/overview ## Online editor You can also use the online editor (http://casbin.org/editor/) to write your php-casbin model and policy in your web browser. It provides functionality such as ``syntax highlighting`` and ``code completion``, just like an IDE for a programming language. ## Tutorials https://casbin.org/docs/en/tutorials ## Policy management php-casbin provides two sets of APIs to manage permissions: - [Management API](https://github.com/php-casbin/php-casbin/blob/master/src/ManagementApi.php): the primitive API that provides full support for php-casbin policy management. See [here](https://github.com/php-casbin/php-casbin/blob/master/tests/Unit/ManagementApiTest.php) for examples. - [RBAC API](https://github.com/php-casbin/php-casbin/blob/master/src/RbacApi.php): a more friendly API for RBAC. This API is a subset of Management API. The RBAC users could use this API to simplify the code. See [here](https://github.com/php-casbin/php-casbin/blob/master/tests/Unit/RbacApiTest.php) for examples. We also provide a web-based UI for model management and policy management: ![model editor](https://hsluoyz.github.io/casbin/ui_model_editor.png) ![policy editor](https://hsluoyz.github.io/casbin/ui_policy_editor.png) ## Policy persistence https://casbin.org/docs/en/adapters ## Role manager https://casbin.org/docs/en/role-managers ## Examples Model | Model file | Policy file ----|------|---- ACL | [basic_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_model.conf) | [basic_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_policy.csv) ACL with superuser | [basic_model_with_root.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_with_root_model.conf) | [basic_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_policy.csv) ACL without users | [basic_model_without_users.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_without_users_model.conf) | [basic_policy_without_users.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_without_users_policy.csv) ACL without resources | [basic_model_without_resources.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_without_resources_model.conf) | [basic_policy_without_resources.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_without_resources_policy.csv) RBAC | [rbac_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_model.conf) | [rbac_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_policy.csv) RBAC with resource roles | [rbac_model_with_resource_roles.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_resource_roles_model.conf) | [rbac_policy_with_resource_roles.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_resource_roles_policy.csv) RBAC with domains/tenants | [rbac_model_with_domains.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_domains_model.conf) | [rbac_policy_with_domains.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_domains_policy.csv) ABAC | [abac_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/abac_model.conf) | N/A RESTful | [keymatch_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/keymatch_model.conf) | [keymatch_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/keymatch_policy.csv) Deny-override | [rbac_model_with_deny.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_deny_model.conf) | [rbac_policy_with_deny.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_deny_policy.csv) Priority | [priority_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/priority_policy.csv) ## Middlewares Authz middlewares for web frameworks: https://casbin.org/docs/en/middlewares ## Our adopters https://casbin.org/docs/en/adopters ## Contributors This project exists thanks to all the people who contribute. ## Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/casbin#backer)] ## Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/casbin#sponsor)] ## License This project is licensed under the [Apache 2.0 license](LICENSE). ## Contact If you have any issues or feature requests, please contact us. PR is welcomed. - https://github.com/php-casbin/php-casbin/issues - techlee@qq.com - Tencent QQ group: [546057381](//shang.qq.com/wpa/qunwpa?idkey=8ac8b91fc97ace3d383d0035f7aa06f7d670fd8e8d4837347354a31c18fac885) README_CN.md000064400000033461152475270340006423 0ustar00PHP-Casbin ==== [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/php-casbin/php-casbin/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/php-casbin/php-casbin/?branch=master) [![Build Status](https://travis-ci.org/php-casbin/php-casbin.svg?branch=master)](https://travis-ci.org/php-casbin/php-casbin) [![Coverage Status](https://coveralls.io/repos/github/php-casbin/php-casbin/badge.svg)](https://coveralls.io/github/php-casbin/php-casbin) [![Latest Stable Version](https://poser.pugx.org/casbin/casbin/v/stable)](https://packagist.org/packages/casbin/casbin) [![Total Downloads](https://poser.pugx.org/casbin/casbin/downloads)](https://packagist.org/packages/casbin/casbin) [![License](https://poser.pugx.org/casbin/casbin/license)](https://packagist.org/packages/casbin/casbin) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/casbin/lobby) **好消息**: [Laravel-authz](https://github.com/php-casbin/laravel-authz) 现已发布,一个专为Laravel打造的授权库. **PHP-Casbin** 是一个强大的、高效的开源访问控制框架,它支持基于各种[访问控制模型](https://en.wikipedia.org/wiki/Computer_security_model)的权限管理。 ## Casbin支持的编程语言: [![golang](https://casbin.org/img/langs/golang.png)](https://github.com/casbin/casbin) | [![java](https://casbin.org/img/langs/java.png)](https://github.com/casbin/jcasbin) | [![nodejs](https://casbin.org/img/langs/nodejs.png)](https://github.com/casbin/node-casbin) | [![php](https://casbin.org/img/langs/php.png)](https://github.com/php-casbin/php-casbin) ----|----|----|---- [Casbin](https://github.com/casbin/casbin) | [jCasbin](https://github.com/casbin/jcasbin) | [node-Casbin](https://github.com/casbin/node-casbin) | [PHP-Casbin](https://github.com/php-casbin/php-casbin) 可用于生产环境 | 可用于生产环境 | 可用于生产环境 | 可用于生产环境 [![python](https://casbin.org/img/langs/python.png)](https://github.com/casbin/pycasbin) | [![delphi](https://casbin.org/img/langs/delphi.png)](https://github.com/casbin4d/Casbin4D) | [![dotnet](https://casbin.org/img/langs/dotnet.png)](https://github.com/Devolutions/casbin-net) | [![rust](https://casbin.org/img/langs/rust.png)](https://github.com/Devolutions/casbin-rs) ----|----|----|---- [PyCasbin](https://github.com/casbin/pycasbin) | [Casbin4D](https://github.com/casbin4d/Casbin4D) | [Casbin-Net](https://github.com/Devolutions/casbin-net) | [Casbin-RS](https://github.com/Devolutions/casbin-rs) 可用于生产环境 | experimental | WIP | WIP ## 安装 通过`Composer`安装: ``` composer require casbin/casbin ``` ## 快速开始 1. 通过`model`和`policy`文件初始化一个`Enforcer`实例: ```php require_once './vendor/autoload.php'; use Casbin\Enforcer; $e = new Enforcer("path/to/model.conf", "path/to/policy.csv"); ``` 2. 在需要进行访问控制的位置,通过以下代码进行权限验证: ```php $sub = "alice"; // the user that wants to access a resource. $obj = "data1"; // the resource that is going to be accessed. $act = "read"; // the operation that the user performs on the resource. if ($e->enforce($sub, $obj, $act) === true) { // permit alice to read data1 } else { // deny the request, show an error } ``` ## 目录 - [支持的Models](#支持的Models) - [工作原理](#工作原理) - [特性](#特性) - [文档](#文档) - [在线编辑器](#在线编辑器) - [教程](#教程) - [Policy管理](#Policy管理) - [Policy持久化](#Policy持久化) - [Role管理](#Role管理) - [例子](#例子) - [我们的采用者](#我们的采用者) - [协议](#协议) - [联系](#联系) ## 支持的Models 1. [**ACL (Access Control List)**](https://en.wikipedia.org/wiki/Access_control_list) 2. **ACL with [superuser](https://en.wikipedia.org/wiki/Superuser)** 3. **ACL without users**: especially useful for systems that don't have authentication or user log-ins. 3. **ACL without resources**: some scenarios may target for a type of resources instead of an individual resource by using permissions like ``write-article``, ``read-log``. It doesn't control the access to a specific article or log. 4. **[RBAC (Role-Based Access Control)](https://en.wikipedia.org/wiki/Role-based_access_control)** 5. **RBAC with resource roles**: both users and resources can have roles (or groups) at the same time. 6. **RBAC with domains/tenants**: users can have different role sets for different domains/tenants. 7. **[ABAC (Attribute-Based Access Control)](https://en.wikipedia.org/wiki/Attribute-Based_Access_Control)**: syntax sugar like ``resource.Owner`` can be used to get the attribute for a resource. 8. **[RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer)**: supports paths like ``/res/*``, ``/res/:id`` and HTTP methods like ``GET``, ``POST``, ``PUT``, ``DELETE``. 9. **Deny-override**: both allow and deny authorizations are supported, deny overrides the allow. 10. **Priority**: the policy rules can be prioritized like firewall rules. ## 工作原理 在 Casbin 中, 访问控制模型被抽象为基于 **PERM (Policy, Effect, Request, Matcher)** 的一个文件。 因此,切换或升级项目的授权机制与修改配置一样简单。 您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中获得RBAC角色和ABAC属性,并共享一组policy规则。 Casbin中最基本、最简单的`model`是ACL。ACL中的`Model` CONF为: ```ini # Request definition [request_definition] r = sub, obj, act # Policy definition [policy_definition] p = sub, obj, act # Policy effect [policy_effect] e = some(where (p.eft == allow)) # Matchers [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act ``` ACL `Model`的示例`Policy`如下: ``` p, alice, data1, read p, bob, data2, write ``` 这表示: - alice对data1有读权限 - bob对data2有写权限 ## 特性 Casbin 做了什么: 1. 自定义请求的格式,默认的请求格式为``{subject, object, action}``。 2. 访问控制模型及其策略的存储。 3. 支持RBAC中的多层角色继承,不止主体可以有角色,资源也可以具有角色。 4. 支持超级用户,如 ``root`` 或 ``Administrator``,超级用户可以不受授权策略的约束访问任意资源。 5. 支持多种内置的操作符,如 ``keyMatch``,方便对路径式的资源进行管理,如 ``/foo/bar`` 可以映射到 ``/foo*``。 Casbin 不做的事情: 1. 身份认证 `authentication`(即验证用户的用户名、密码),`casbin`只负责访问控制。应该有其他专门的组件负责身份认证,然后由`casbin`进行访问控制,二者是相互配合的关系。 2. 管理用户列表或角色列表。 `Casbin` 认为由项目自身来管理用户、角色列表更为合适, 用户通常有他们的密码,但是 `Casbin`的设计思想并不是把它作为一个存储密码的容器。 而是存储RBAC方案中用户和角色之间的映射关系。 ## 文档 https://casbin.org/docs/zh-CN/overview ## 在线编辑器 你也可以使用在线编辑器(https://casbin.org/editor/) 在你的浏览器里编写Casbin模型和策略。 它提供了一些比如 `语法高亮`以及`代码补全`这样的功能,就像编程语言的IDE一样。 ## 教程 https://casbin.org/docs/zh-CN/tutorials ## Policy管理 Casbin 提供两组 API 来管理权限: - [管理API](https://github.com/php-casbin/php-casbin/blob/master/src/ManagementApi.php): Casbin的底层原生API,支持全部的策略管理功能。点击 [这里](https://github.com/php-casbin/php-casbin/blob/master/tests/Unit/ManagementApiTest.php) 查看更多例子。 - [RBAC API](https://github.com/php-casbin/php-casbin/blob/master/src/RbacApi.php): 对于RBAC, 是一个更加友好的 API。 此 API 是管理 API 中的一个子集。 RBAC 用户可以使用此 API 来简化代码。 点击 [这里](https://github.com/php-casbin/php-casbin/blob/master/tests/Unit/RbacApiTest.php) 查看更多例子。 同时也提供了一个简单的前端页面来管理`Model`和`Policy`: ![model editor](https://hsluoyz.github.io/casbin/ui_model_editor.png) ![policy editor](https://hsluoyz.github.io/casbin/ui_policy_editor.png) ## Policy持久化 在`Casbin`中,适配器(`adapter`,`Casbin`的中间件)实现了`policy`规则写入持久层的细节。 `Casbin`的用户可以调用`adapter`的`loadPolicy()`方法从持久层中加载`policy`规则, 同样也可以调用`savePolicy()`方法将`Policy`规则保存到持久层中。 为了保持代码轻量, 我们没有将`adapter`的代码放在主库中。 以下是`PHP-Casbin`支持的适配器:(欢迎更多新的第三方贡献的适配器,可以联系我们添加在下面) Adapter | Type | Author | Description ----|------|----|---- [File Adapter (内置)](https://casbin.org/docs/zh-CN/policy-storage#file-adapter-built-in) | File | php-casbin | 存储到[.CSV (Comma-Separated Values)](https://en.wikipedia.org/wiki/Comma-separated_values) 文件中 [Database Adapter](https://github.com/php-casbin/database-adapter) | Database | php-casbin | 支持存储到MySQL, PostgreSQL, SQLite, Microsoft SQL Server数据库的适配器 更多适配器的内容,请参考文档: https://casbin.org/docs/zh-CN/policy-storage ## Role管理 角色管理器用于在`Casbin`中管理`RBAC`多层角色继承(用户-角色的关系)。角色管理器可以从Casbin的`Policy`规则或者外部数据源(如LDAP, Okta, Auth0, Azure AD等)获取角色数据。我们支持多种角色管理器,为了保持代码轻量,我们没有将除了内置的默认的角色管理器以外的角色管理器放在主库中。以下是支持的角色管理器:(欢迎更多新的第三方贡献的角色管理器,可以联系我们添加在下面) Role manager | Author | Description ----|----|---- [Default Role Manager (内置)](https://github.com/php-casbin/php-casbin/blob/master/src/Rbac/DefaultRoleManager/RoleManager.php) | php-casbin | 支持多层角色继承 提示: 所有的角色管理器必须实现[RoleManager](https://github.com/php-casbin/php-casbin/blob/master/src/Rbac/RoleManager.php) 接口。 可以参考[Default Role Manager](https://github.com/php-casbin/php-casbin/blob/master/src/Rbac/DefaultRoleManager/RoleManager.php) 。 ## 例子 Model | Model file | Policy file ----|------|---- ACL | [basic_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_model.conf) | [basic_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_policy.csv) ACL with superuser | [basic_model_with_root.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_with_root_model.conf) | [basic_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_policy.csv) ACL without users | [basic_model_without_users.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_without_users_model.conf) | [basic_policy_without_users.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_without_users_policy.csv) ACL without resources | [basic_model_without_resources.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_without_resources_model.conf) | [basic_policy_without_resources.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/basic_without_resources_policy.csv) RBAC | [rbac_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_model.conf) | [rbac_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_policy.csv) RBAC with resource roles | [rbac_model_with_resource_roles.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_resource_roles_model.conf) | [rbac_policy_with_resource_roles.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_resource_roles_policy.csv) RBAC with domains/tenants | [rbac_model_with_domains.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_domains_model.conf) | [rbac_policy_with_domains.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_domains_policy.csv) ABAC | [abac_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/abac_model.conf) | N/A RESTful | [keymatch_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/keymatch_model.conf) | [keymatch_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/keymatch_policy.csv) Deny-override | [rbac_model_with_deny.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_deny_model.conf) | [rbac_policy_with_deny.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/rbac_with_deny_policy.csv) Priority | [priority_model.conf](https://github.com/php-casbin/php-casbin/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/php-casbin/php-casbin/blob/master/examples/priority_policy.csv) ## 我们的采用者 ### Web框架 - [Laravel](https://laravel.com/): 为WEB艺术家创造的PHP框架, 通过这个扩展: [laravel-casbin](https://github.com/php-casbin/laravel-casbin) - [Yii PHP Framework](https://www.yiiframework.com/): 一个高性能的,适用于开发WEB2.0应用的PHP框架, 通过这个扩展: [yii-casbin](https://github.com/php-casbin/yii-casbin) - [CakePHP](https://cakephp.org/): 快速、稳定的PHP框架, 通过这个扩展: [cake-casbin](https://github.com/php-casbin/cake-casbin) - [ThinkPHP](http://www.thinkphp.cn/): 一个免费开源的,快速、简单的面向对象的轻量级PHP开发框架, 通过这个扩展: [think-casbin](https://github.com/php-casbin/think-casbin) ## 协议 `PHP-Casbin` 采用 [Apache 2.0 license](LICENSE) 开源协议发布。 ## 联系 有问题或者功能建议,请联系我们或者提交PR: - https://github.com/php-casbin/php-casbin/issues - techlee@qq.com - QQ群: [546057381](//shang.qq.com/wpa/qunwpa?idkey=8ac8b91fc97ace3d383d0035f7aa06f7d670fd8e8d4837347354a31c18fac885) composer.json000064400000001557152475270340007307 0ustar00{ "name": "casbin/casbin", "description": "a powerful and efficient open-source access control library for php projects.", "authors": [ { "name": "TechLee", "email": "techlee@qq.com" } ], "license": "Apache-2.0", "keywords": [ "casbin", "rbac", "acl", "authorization", "permission", "abac", "access control" ], "require": { "php": ">=7.1.0", "symfony/expression-language": "^3.4|^4.0", "s1lentium/iptools": "^1.1" }, "autoload": { "psr-4": { "Casbin\\": "src/" } }, "require-dev": { "phpunit/phpunit": "~7.0|~8.0", "php-coveralls/php-coveralls": "^2.1" }, "autoload-dev": { "psr-4": { "Casbin\\Tests\\": "tests/" } } } examples/abac_model.conf000064400000000244152475270340011310 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == r.obj.Ownerexamples/basic_inverse_policy.csv000064400000000052152475270340013300 0ustar00p, alice, data1, write p, bob, data2, readexamples/basic_model.conf000064400000000302152475270340011476 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.actexamples/basic_policy.csv000064400000000052152475270340011545 0ustar00p, alice, data1, read p, bob, data2, writeexamples/basic_with_root_model.conf000064400000000325152475270340013601 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"examples/basic_without_resources_model.conf000064400000000246152475270340015362 0ustar00[request_definition] r = sub, act [policy_definition] p = sub, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.act == p.actexamples/basic_without_resources_policy.csv000064400000000034152475270340015422 0ustar00p, alice, read p, bob, writeexamples/basic_without_users_model.conf000064400000000246152475270340014511 0ustar00[request_definition] r = obj, act [policy_definition] p = obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.obj == p.obj && r.act == p.actexamples/basic_without_users_policy.csv000064400000000036152475270340014553 0ustar00p, data1, read p, data2, writeexamples/error/error_model.conf000064400000000301152475270340012676 0ustar00[request_definition] r = sub, obj, act [policy_definition p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.actexamples/error/error_policy.csv000064400000000047152475270340012752 0ustar00p, alice, data1, read bob, data2, writeexamples/ipmatch_model.conf000064400000000311152475270340012042 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = ipMatch(r.sub, p.sub) && r.obj == p.obj && r.act == p.actexamples/ipmatch_policy.csv000064400000000073152475270340012114 0ustar00p, 192.168.2.0/24, data1, read p, 10.0.0.0/16, data2, writeexamples/keymatch2_model.conf000064400000000325152475270340012311 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)examples/keymatch2_policy.csv000064400000000121152475270340012350 0ustar00p, alice, /alice_data/:resource, GET p, alice, /alice_data2/:id/using/:resId, GETexamples/keymatch_custom_model.conf000064400000000332152475270340013617 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && keyMatchCustom(r.obj, p.obj) && regexMatch(r.act, p.act)examples/keymatch_model.conf000064400000000324152475270340012226 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)examples/keymatch_policy.csv000064400000000245152475270340012275 0ustar00p, alice, /alice_data/*, GET p, alice, /alice_data/resource1, POST p, bob, /alice_data/resource2, GET p, bob, /bob_data/*, POST p, cathy, /cathy_data, (GET)|(POST)examples/priority_indeterminate_policy.csv000064400000000045152475270340015257 0ustar00p, alice, data1, read, intdeterminateexamples/priority_model.conf000064400000000337152475270340012306 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = priority(p.eft) || deny [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actexamples/priority_policy.csv000064400000000437152475270340012354 0ustar00p, alice, data1, read, allow p, data1_deny_group, data1, read, deny p, data1_deny_group, data1, write, deny p, alice, data1, write, allow g, alice, data1_deny_group p, data2_allow_group, data2, read, allow p, bob, data2, read, deny p, bob, data2, write, deny g, bob, data2_allow_groupexamples/rbac_model.conf000064400000000337152475270340011334 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actexamples/rbac_policy.csv000064400000000172152475270340011376 0ustar00p, alice, data1, read p, bob, data2, write p, data2_admin, data2, read p, data2_admin, data2, write g, alice, data2_adminexamples/rbac_with_deny_model.conf000064400000000404152475270340013401 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) && !some(where (p.eft == deny)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actexamples/rbac_with_deny_policy.csv000064400000000263152475270340013451 0ustar00p, alice, data1, read, allow p, bob, data2, write, allow p, data2_admin, data2, read, allow p, data2_admin, data2, write, allow p, alice, data2, write, deny g, alice, data2_adminexamples/rbac_with_domains_model.conf000064400000000405152475270340014075 0ustar00[request_definition] r = sub, dom, obj, act [policy_definition] p = sub, dom, obj, act [role_definition] g = _, _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.actexamples/rbac_with_domains_policy.csv000064400000000256152475270340014146 0ustar00p, admin, domain1, data1, read p, admin, domain1, data1, write p, admin, domain2, data2, read p, admin, domain2, data2, write g, alice, admin, domain1 g, bob, admin, domain2examples/rbac_with_hierarchy_policy.csv000064400000000331152475270340014464 0ustar00p, alice, data1, read p, bob, data2, write p, data1_admin, data1, read p, data1_admin, data1, write p, data2_admin, data2, read p, data2_admin, data2, write g, alice, admin g, admin, data1_admin g, admin, data2_adminexamples/rbac_with_hierarchy_with_domains_policy.csv000064400000000417152475270340017236 0ustar00p, role:reader, domain1, data1, read p, role:writer, domain1, data1, write p, alice, domain1, data2, read p, alice, domain2, data2, read g, role:global_admin, role:reader, domain1 g, role:global_admin, role:writer, domain1 g, alice, role:global_admin, domain1examples/rbac_with_not_deny_model.conf000064400000000344152475270340014264 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act, eft [role_definition] g = _, _ [policy_effect] e = !some(where (p_eft == deny)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.actexamples/rbac_with_resource_roles_model.conf000064400000000353152475270340015500 0ustar00[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ g2 = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.actexamples/rbac_with_resource_roles_policy.csv000064400000000231152475270340015540 0ustar00p, alice, data1, read p, bob, data2, write p, data_group_admin, data_group, write g, alice, data_group_admin g2, data1, data_group g2, data2, data_groupphpunit.xml000064400000001505152475270340006767 0ustar00 ./tests/ ./src src/CachedEnforcer.php000064400000003753152475270340010720 0ustar00enableCache = true; } /** * determines whether to enable cache on Enforce(). When enableCache is enabled, cached result (true | false) will be returned for previous decisions. * * @param bool $enableCache */ public function enableCache(bool $enableCache): void { $this->enableCache = $enableCache; } /** * Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). * if rvals is not string , ingore the cache. * * @param mixed ...$rvals * * @return bool * * @throws Exceptions\CasbinException */ public function enforce(...$rvals): bool { if (!$this->enableCache) { return parent::enforce(...$rvals); } $key = ''; foreach ($rvals as $rval) { if (is_string($rval)) { $key .= $rval.'$$'; } else { return parent::enforce(...$rvals); } } if (isset(self::$m[$key])) { return self::$m[$key]; } else { $res = parent::enforce(...$rvals); self::$m[$key] = $res; return $res; } } /** * deletes all the existing cached decisions. */ public function invalidateCache(): void { self::$m = []; } } src/Config/Config.php000064400000013567152475270340010503 0ustar00parse($confName); return $c; } /** * create an empty configuration representation from text. * * @param string $text * * @return ConfigContract */ public static function newConfigFromText(string $text): ConfigContract { $c = new static(); $c->parseBuffer($text); return $c; } /** * adds a new section->key:value to the configuration. * * @param string $section * @param string $option * @param string $value * * @return bool */ public function addConfig(string $section, string $option, string $value): bool { if (empty($section)) { $section = self::DEFAULT_SECTION; } if (!isset($this->data[$section])) { $this->data[$section] = []; } $this->data[$section][$option] = $value; return true; } /** * @param string $fname * * @return bool * * @throws CasbinException */ private function parse(string $fname): bool { $buf = file_get_contents($fname); $res = $this->parseBuffer($buf); return $res; } /** * @param string $buf * * @return bool * * @throws CasbinException */ private function parseBuffer(string $buf): bool { $section = ''; $lineNum = 0; $buffer = ''; $canWrite = null; $buf = preg_replace('/[\r\n]+/', PHP_EOL, $buf); $buf = explode(PHP_EOL, $buf); for ($i = 0, $len = \count($buf); $i <= $len; ++$i) { if ($canWrite) { $this->write($section, $lineNum, $buffer); $canWrite = false; } ++$lineNum; $line = isset($buf[$i]) ? $buf[$i] : ''; if ($i == \count($buf)) { if (\strlen($buffer) > 0) { $this->write($section, $lineNum, $buffer); } break; } $line = trim($line); if ('' == $line || self::DEFAULT_COMMENT == substr($line, 0, 1) || self::DEFAULT_COMMENT_SEM == substr($line, 0, 1)) { $canWrite = true; continue; } elseif ('[' == substr($line, 0, 1) && ']' == substr($line, -1)) { if (\strlen($buffer) > 0) { $this->write($section, $lineNum, $buffer); $canWrite = false; } $section = substr($line, 1, -1); } else { $p = ''; if (self::DEFAULT_MULTI_LINE_SEPARATOR == substr($line, -1)) { $p = trim(substr($line, 0, -1)); } else { $p = $line; $canWrite = true; } $buffer .= $p; } } return true; } /** * @param string $section * @param int $lineNum * @param string $b * * @throws CasbinException */ private function write(string $section, int $lineNum, string &$b): void { if (\strlen($b) <= 0) { return; } $optionVal = explode('=', $b, 2); if (2 != \count($optionVal)) { throw new CasbinException(sprintf('parse the content error : line %d , %s = ?', $lineNum, current($optionVal))); } $option = trim($optionVal[0]); $value = trim($optionVal[1]); $this->addConfig($section, $option, $value); $b = ''; } /** * lookups up the value using the provided key and converts the value to a string. * * @param string $key * * @return string */ public function getString(string $key): string { return $this->get($key); } /** * lookups up the value using the provided key and converts the value to an array of string * by splitting the string by comma. * * @param string $key * * @return array */ public function getStrings(string $key): array { $v = $this->get($key); if ('' == $v) { return []; } return explode(',', $v); } /** * sets the value for the specific key in the Config. * * @param string $key * @param string $value * * @throws CasbinException */ public function set(string $key, string $value): void { if (0 == \strlen($key)) { throw new CasbinException('key is empty'); } $section = ''; $keys = explode('::', strtolower($key)); if (\count($keys) >= 2) { $section = $keys[0]; $option = $keys[1]; } else { $option = $keys[0]; } $this->addConfig($section, $option, $value); } /** * section.key or key. * * @param string $key * * @return string */ public function get(string $key): string { $keys = explode('::', $key); if (\count($keys) >= 2) { $section = $keys[0]; $option = $keys[1]; } else { $section = self::DEFAULT_SECTION; $option = $keys[0]; } return isset($this->data[$section][$option]) ? $this->data[$section][$option] : ''; } } src/Config/ConfigContract.php000064400000001777152475270340012201 0ustar00 'mysql', // mysql,pgsql,sqlite,sqlsrv * 'hostname' => '127.0.0.1', * 'database' => 'test', * 'username' => 'root', * 'password' => '123456', * 'hostport' => '3306', * ]); * $e = new Enforcer("path/to/basic_model.conf", $a). * * @param mixed ...$params * * @throws CasbinException */ public function __construct(...$params) { $parsedParamLen = 0; $paramLen = \count($params); if ($paramLen >= 1) { if (\is_bool($enableLog = $params[$paramLen - 1])) { $this->enableLog($enableLog); ++$parsedParamLen; } } if (2 == $paramLen - $parsedParamLen) { $p0 = $params[0]; if (\is_string($p0)) { $p1 = $params[1]; if (\is_string($p1)) { $this->initWithFile($p0, $p1); } else { $this->initWithAdapter($p0, $p1); } } else { if (\is_string($params[1])) { throw new CasbinException('Invalid parameters for enforcer.'); } else { $this->initWithModelAndAdapter($p0, $params[1]); } } } elseif (1 == $paramLen - $parsedParamLen) { $p0 = $params[0]; if (\is_string($p0)) { $this->initWithFile($p0, ''); } else { $this->initWithModelAndAdapter($p0, null); } } elseif (0 == $paramLen - $parsedParamLen) { // pass } else { throw new CasbinException('Invalid parameters for enforcer.'); } } /** * initializes an enforcer with a model file and a policy file. * * @param string $modelPath * @param string $policyPath * * @throws CasbinException */ public function initWithFile(string $modelPath, string $policyPath): void { $adapter = new FileAdapter($policyPath); $this->initWithAdapter($modelPath, $adapter); } /** * initializes an enforcer with a database adapter. * * @param string $modelPath * @param Adapter $adapter * * @throws CasbinException */ public function initWithAdapter(string $modelPath, Adapter $adapter): void { $m = Model::newModelFromFile($modelPath); $this->initWithModelAndAdapter($m, $adapter); $this->modelPath = $modelPath; } /** * initWithModelAndAdapter initializes an enforcer with a model and a database adapter. * * @param Model $m * @param Adapter|null $adapter */ public function initWithModelAndAdapter(Model $m, Adapter $adapter = null): void { $this->adapter = $adapter; $this->model = $m; $this->model->printModel(); $this->fm = Model::loadFunctionMap(); $this->initialize(); // Do not initialize the full policy when using a filtered adapter $ok = $this->adapter instanceof FilteredAdapter ? $this->adapter->isFiltered() : false; if (!\is_null($this->adapter) && !$ok) { $this->loadPolicy(); } } /** * initializes an enforcer with a database adapter. */ protected function initialize(): void { $this->rm = new DefaultRoleManager(10); $this->eft = new DefaultEffector(); $this->watcher = null; $this->enabled = true; $this->autoSave = true; $this->autoBuildRoleLinks = true; } /** * reloads the model from the model CONF file. * Because the policy is attached to a model, so the policy is invalidated and needs to be reloaded by calling LoadPolicy(). * * @throws CasbinException */ public function loadModel(): void { $this->model = Model::newModelFromFile($this->modelPath); $this->model->printModel(); $this->fm = Model::LoadFunctionMap(); $this->initialize(); } /** * gets the current model. * * @return Model */ public function getModel(): Model { return $this->model; } /** * sets the current model. * * @param Model $model */ public function setModel(Model $model): void { $this->model = $model; $this->fm = $this->model->loadFunctionMap(); $this->initialize(); } /** * gets the current adapter. * * @return Adapter */ public function getAdapter(): Adapter { return $this->adapter; } /** * sets the current adapter. * * @param Adapter $adapter */ public function setAdapter(Adapter $adapter): void { $this->adapter = $adapter; } /** * sets the current watcher. * * @param Watcher $watcher */ public function setWatcher(Watcher $watcher): void { $this->watcher = $watcher; $this->watcher->setUpdateCallback(function () { $this->loadPolicy(); }); } /** * sets the current role manager. * * @param RoleManager $rm */ public function setRoleManager(RoleManager $rm): void { $this->rm = $rm; } /** * sets the current effector. * * @param Effector $eft */ public function setEffector(Effector $eft): void { $this->eft = $eft; } /** * clears all policy. */ public function clearPolicy(): void { $this->model->clearPolicy(); } /** * reloads the policy from file/database. */ public function loadPolicy(): void { $this->model->clearPolicy(); try { $this->adapter->loadPolicy($this->model); } catch (InvalidFilePathException $e) { //throw $e; } $this->model->printPolicy(); if ($this->autoBuildRoleLinks) { $this->buildRoleLinks(); } } /** * reloads a filtered policy from file/database. * * @param mixed $filter * * @throws CasbinException */ public function loadFilteredPolicy($filter): void { $this->model->clearPolicy(); if ($this->adapter instanceof FilteredAdapter) { $filteredAdapter = $this->adapter; $filteredAdapter->loadFilteredPolicy($this->model, $filter); } else { throw new CasbinException('filtered policies are not supported by this adapter'); } $this->model->printPolicy(); if ($this->autoBuildRoleLinks) { $this->buildRoleLinks(); } } /** * returns true if the loaded policy has been filtered. * * @return bool */ public function isFiltered(): bool { if (!$this->adapter instanceof FilteredAdapter) { return false; } $filteredAdapter = $this->adapter; return $filteredAdapter->isFiltered(); } /** * saves the current policy (usually after changed with Casbin API) back to file/database. * * @return mixed * * @throws CasbinException */ public function savePolicy(): void { if ($this->isFiltered()) { throw new CasbinException('cannot save a filtered policy'); } $this->adapter->savePolicy($this->model); if (null !== $this->watcher) { $this->watcher->update(); } } /** * changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function. * * @param bool $enabled */ public function enableEnforce(bool $enabled = true): void { $this->enabled = $enabled; } /** * changes whether Casbin will log messages to the Logger. * * @param bool $enabled */ public function enableLog(bool $enabled = true): void { Log::getLogger()->enableLog($enabled); } /** * controls whether to save a policy rule automatically to the adapter when it is added or removed. * * @param bool $autoSave */ public function enableAutoSave(bool $autoSave = true): void { $this->autoSave = $autoSave; } /** * controls whether to rebuild the role inheritance relations when a role is added or deleted. * * @param bool $autoBuildRoleLinks */ public function enableAutoBuildRoleLinks(bool $autoBuildRoleLinks = true): void { $this->autoBuildRoleLinks = $autoBuildRoleLinks; } /** * manually rebuild the role inheritance relations. */ public function buildRoleLinks(): void { $this->rm->clear(); $this->model->buildRoleLinks($this->rm); } /** * decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). * * @param mixed ...$rvals * * @return bool|mixed * * @throws CasbinException */ public function enforce(...$rvals): bool { if (!$this->enabled) { return true; } $functions = $this->fm->getFunctions(); if (isset($this->model['g'])) { foreach ($this->model['g'] as $key => $ast) { $rm = $ast->rM; $functions[$key] = BuiltinOperations::GenerateGFunction($rm); } } if (!isset($this->model['m']['m'])) { throw new CasbinException('model is undefined'); } $expString = $this->getExpString($this->model['m']['m']->value); $rTokens = array_values($this->model['r']['r']->tokens); $pTokens = array_values($this->model['p']['p']->tokens); $rParameters = array_combine($rTokens, $rvals); if (false === $rParameters) { throw new CasbinException('invalid request size'); } $expressionLanguage = $this->getExpressionLanguage($functions); $expression = $expressionLanguage->parse($expString, array_merge($rTokens, $pTokens)); $policyEffects = []; $matcherResults = []; $policyLen = \count($this->model['p']['p']->policy); if (0 != $policyLen) { foreach ($this->model['p']['p']->policy as $i => $pvals) { $parameters = array_combine($pTokens, $pvals); if (false === $parameters) { throw new CasbinException('invalid policy size'); } $parameters = array_merge($rParameters, $parameters); $result = $expressionLanguage->evaluate($expression, $parameters); if (\is_bool($result)) { if (!$result) { $policyEffects[$i] = Effector::INDETERMINATE; continue; } } elseif (\is_float($result)) { if (0 == $result) { $policyEffects[$i] = Effector::INDETERMINATE; continue; } else { $matcherResults[$i] = $result; } } else { throw new CasbinException('matcher result should be bool, int or float'); } if (isset($parameters['p_eft'])) { $eft = $parameters['p_eft']; if ('allow' == $eft) { $policyEffects[$i] = Effector::ALLOW; } elseif ('deny' == $eft) { $policyEffects[$i] = Effector::DENY; } else { $policyEffects[$i] = Effector::INDETERMINATE; } } else { $policyEffects[$i] = Effector::ALLOW; } if (isset($this->model['e']['e']) && 'priority(p_eft) || deny' == $this->model['e']['e']->value) { break; } } } else { $parameters = $rParameters; foreach ($this->model['p']['p']->tokens as $token) { $parameters[$token] = ''; } $result = $expressionLanguage->evaluate($expression, $parameters); if ($result) { $policyEffects[0] = Effector::ALLOW; } else { $policyEffects[0] = Effector::INDETERMINATE; } } $result = $this->eft->mergeEffects($this->model['e']['e']->value, $policyEffects, $matcherResults); if (Log::getLogger()->isEnabled()) { $reqStr = 'Request: '; $reqStr .= implode(', ', array_values($rvals)); $reqStr .= sprintf(' ---> %s', (string) $result); Log::logPrint($reqStr); } return $result; } /** * @param array $functions * * @return ExpressionLanguage */ protected function getExpressionLanguage(array $functions): ExpressionLanguage { $expressionLanguage = new ExpressionLanguage(); foreach ($functions as $key => $func) { $expressionLanguage->register($key, function (...$args) use ($key) { return sprintf($key.'(%1$s)', implode(',', $args)); }, function ($arguments, ...$args) use ($func) { return $func(...$args); }); } return $expressionLanguage; } /** * @param string $expString * * @return string */ protected function getExpString(string $expString): string { return preg_replace_callback( '/([\s\S]*in\s+)\(([\s\S]+)\)([\s\S]*)/', function ($m) { return $m[1].'['.$m[2].']'.$m[3]; }, $expString ); } } src/Exceptions/CannotSaveFilteredPolicy.php000075500000000302152475270340015074 0ustar00model->addPolicy($sec, $ptype, $rule); if (!$ruleAdded) { return $ruleAdded; } if (!is_null($this->adapter) && $this->autoSave) { try { $this->adapter->addPolicy($sec, $ptype, $rule); } catch (NotImplementedException $e) { } if (!is_null($this->watcher)) { // error intentionally ignored $this->watcher->update(); } } return $ruleAdded; } /** * removes a rule from the current policy. * * @param string $sec * @param string $ptype * @param array $rule * * @return bool */ protected function removePolicyInternal(string $sec, string $ptype, array $rule): bool { $ruleRemoved = $this->model->removePolicy($sec, $ptype, $rule); if (!$ruleRemoved) { return $ruleRemoved; } if (!is_null($this->adapter) && $this->autoSave) { try { $this->adapter->removePolicy($sec, $ptype, $rule); } catch (NotImplementedException $e) { } if (!is_null($this->watcher)) { // error intentionally ignored $this->watcher->update(); } } return $ruleRemoved; } /** * removes rules based on field filters from the current policy. * * @param string $sec * @param string $ptype * @param int $fieldIndex * @param string ...$fieldValues * * @return bool */ protected function removeFilteredPolicyInternal(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): bool { $ruleRemoved = $this->model->removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues); if (!$ruleRemoved) { return $ruleRemoved; } if (!is_null($this->adapter) && $this->autoSave) { try { $this->adapter->removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues); } catch (NotImplementedException $e) { } if (!is_null($this->watcher)) { // error intentionally ignored $this->watcher->update(); } } return $ruleRemoved; } } src/Log/Log.php000064400000002021152475270340007312 0ustar00write(...$v); } /** * prints the log with the format. * * @param string $format * @param mixed ...$v */ public static function logPrintf(string $format, ...$v): void { self::$logger->writef($format, ...$v); } } Log::setLogger(new DefaultLogger()); src/Log/Logger.php000064400000001407152475270340010017 0ustar00path = sys_get_temp_dir(); } /** * enableLog. * * @param bool $enable */ public function enableLog(bool $enable): void { $this->enable = $enable; } /** * @return bool */ public function isEnabled(): bool { return $this->enable; } /** * @param mixed ...$v */ public function write(...$v): void { if ($this->enable) { $content = date('Y-m-d H:i:s '); foreach ($v as $value) { if (\is_array($value)) { $value = json_encode($value); } elseif (\is_object($value)) { $value = json_encode($value); } $content .= $value; } $content .= PHP_EOL; $this->save($content); } } /** * @param string $format * @param mixed ...$v */ public function writef(string $format, ...$v): void { if ($this->enable) { $content = date('Y-m-d H:i:s '); $content .= sprintf($format, ...$v); $content .= PHP_EOL; $this->save($content); } } /** * @param string $content */ public function save(string $content): void { $file = $this->path.DIRECTORY_SEPARATOR.$this->name; file_put_contents($file, $content, FILE_APPEND | LOCK_EX); } } src/ManagementApi.php000064400000027464152475270340010600 0ustar00getAllNamedSubjects('p'); } /** * gets the list of subjects that show up in the current named policy. * * @param string $ptype * * @return array */ public function getAllNamedSubjects(string $ptype): array { return $this->model->getValuesForFieldInPolicy('p', $ptype, 0); } /** * gets the list of objects that show up in the current policy. * * @return array */ public function getAllObjects(): array { return $this->getAllNamedObjects('p'); } /** * gets the list of objects that show up in the current named policy. * * @param string $ptype * * @return array */ public function getAllNamedObjects(string $ptype): array { return $this->model->getValuesForFieldInPolicy('p', $ptype, 1); } /** * gets the list of actions that show up in the current policy. * * @return array */ public function getAllActions(): array { return $this->getAllNamedActions('p'); } /** * gets the list of actions that show up in the current named policy. * * @param string $ptype * * @return array */ public function getAllNamedActions(string $ptype): array { return $this->model->getValuesForFieldInPolicy('p', $ptype, 2); } /** * gets the list of roles that show up in the current policy. * * @return array */ public function getAllRoles(): array { return $this->getAllNamedRoles('g'); } /** * gets the list of roles that show up in the current named policy. * * @param string $ptype * * @return array */ public function getAllNamedRoles(string $ptype): array { return $this->model->getValuesForFieldInPolicy('g', $ptype, 1); } /** * gets all the authorization rules in the policy. * * @return array */ public function getPolicy(): array { return $this->getNamedPolicy('p'); } /** * gets all the authorization rules in the policy, field filters can be specified. * * @param int $fieldIndex * @param string ...$fieldValues * * @return array */ public function getFilteredPolicy(int $fieldIndex, string ...$fieldValues): array { return $this->getFilteredNamedPolicy('p', $fieldIndex, ...$fieldValues); } /** * gets all the authorization rules in the named policy. * * @param string $ptype * * @return array */ public function getNamedPolicy(string $ptype): array { return $this->model->getPolicy('p', $ptype); } /** * gets all the authorization rules in the named policy, field filters can be specified. * * @param string $ptype * @param int $fieldIndex * @param string ...$fieldValues * * @return array */ public function getFilteredNamedPolicy(string $ptype, int $fieldIndex, string ...$fieldValues): array { return $this->model->getFilteredPolicy('p', $ptype, $fieldIndex, ...$fieldValues); } /** * gets all the role inheritance rules in the policy. * * @return array */ public function getGroupingPolicy(): array { return $this->getNamedGroupingPolicy('g'); } /** * gets all the role inheritance rules in the policy, field filters can be specified. * * @param int $fieldIndex * @param string ...$fieldValues * * @return array */ public function getFilteredGroupingPolicy(int $fieldIndex, string ...$fieldValues): array { return $this->getFilteredNamedGroupingPolicy('g', $fieldIndex, ...$fieldValues); } /** * gets all the role inheritance rules in the policy. * * @param string $ptype * * @return array */ public function getNamedGroupingPolicy(string $ptype): array { return $this->model->getPolicy('g', $ptype); } /** * gets all the role inheritance rules in the policy, field filters can be specified. * * @param string $ptype * @param int $fieldIndex * @param string ...$fieldValues * * @return array */ public function getFilteredNamedGroupingPolicy(string $ptype, int $fieldIndex, string ...$fieldValues): array { return $this->model->getFilteredPolicy('g', $ptype, $fieldIndex, ...$fieldValues); } /** * determines whether an authorization rule exists. * * @param mixed ...$params * * @return bool */ public function hasPolicy(...$params): bool { return $this->hasNamedPolicy('p', ...$params); } /** * determines whether a named authorization rule exists. * * @param string $ptype * @param mixed ...$params * * @return bool */ public function hasNamedPolicy(string $ptype, ...$params): bool { if (1 == count($params) && is_array($params[0])) { $params = $params[0]; } return $this->model->hasPolicy('p', $ptype, $params); } /** * AddPolicy adds an authorization rule to the current policy. * If the rule already exists, the function returns false and the rule will not be added. * Otherwise the function returns true by adding the new rule. * * @param mixed ...$params * * @return bool */ public function addPolicy(...$params): bool { return $this->addNamedPolicy('p', ...$params); } /** * AddNamedPolicy adds an authorization rule to the current named policy. * If the rule already exists, the function returns false and the rule will not be added. * Otherwise the function returns true by adding the new rule. * * @param string $ptype * @param mixed ...$params * * @return bool */ public function addNamedPolicy(string $ptype, ...$params): bool { if (1 == count($params) && is_array($params[0])) { $params = $params[0]; } return $this->addPolicyInternal('p', $ptype, $params); } /** * removes an authorization rule from the current policy. * * @param mixed ...$params * * @return bool */ public function removePolicy(...$params): bool { return $this->removeNamedPolicy('p', ...$params); } /** * removes an authorization rule from the current policy, field filters can be specified. * * @param int $fieldIndex * @param string ...$fieldValues * * @return bool */ public function removeFilteredPolicy(int $fieldIndex, string ...$fieldValues): bool { return $this->removeFilteredNamedPolicy('p', $fieldIndex, ...$fieldValues); } /** * removes an authorization rule from the current named policy. * * @param string $ptype * @param mixed ...$params * * @return bool */ public function removeNamedPolicy(string $ptype, ...$params): bool { if (1 == count($params) && is_array($params[0])) { $params = $params[0]; } return $this->removePolicyInternal('p', $ptype, $params); } /** * removes an authorization rule from the current named policy, field filters can be specified. * * @param string $ptype * @param int $fieldIndex * @param string ...$fieldValues * * @return bool */ public function removeFilteredNamedPolicy(string $ptype, int $fieldIndex, string ...$fieldValues): bool { return $this->removeFilteredPolicyInternal('p', $ptype, $fieldIndex, ...$fieldValues); } /** * determines whether a role inheritance rule exists. * * @param mixed ...$params * * @return bool */ public function hasGroupingPolicy(...$params): bool { return $this->hasNamedGroupingPolicy('g', ...$params); } /** * determines whether a named role inheritance rule exists. * * @param string $ptype * @param mixed ...$params * * @return bool */ public function hasNamedGroupingPolicy(string $ptype, ...$params): bool { if (1 == count($params) && is_array($params[0])) { $params = $params[0]; } return $this->model->hasPolicy('g', $ptype, $params); } /** * AddGroupingPolicy adds a role inheritance rule to the current policy. * If the rule already exists, the function returns false and the rule will not be added. * Otherwise the function returns true by adding the new rule. * * @param mixed ...$params * * @return bool */ public function addGroupingPolicy(...$params): bool { return $this->addNamedGroupingPolicy('g', ...$params); } /** * AddNamedGroupingPolicy adds a named role inheritance rule to the current policy. * If the rule already exists, the function returns false and the rule will not be added. * Otherwise the function returns true by adding the new rule. * * @param string $ptype * @param mixed ...$params * * @return bool */ public function addNamedGroupingPolicy(string $ptype, ...$params): bool { if (1 == count($params) && is_array($params[0])) { $params = $params[0]; } $ruleAdded = $this->addPolicyInternal('g', $ptype, $params); if ($this->autoBuildRoleLinks) { $this->buildRoleLinks(); } return $ruleAdded; } /** * removes a role inheritance rule from the current policy. * * @param mixed ...$params * * @return bool */ public function removeGroupingPolicy(...$params): bool { return $this->removeNamedGroupingPolicy('g', ...$params); } /** * removes a role inheritance rule from the current policy, field filters can be specified. * * @param int $fieldIndex * @param string ...$fieldValues * * @return bool */ public function removeFilteredGroupingPolicy(int $fieldIndex, string ...$fieldValues): bool { return $this->removeFilteredNamedGroupingPolicy('g', $fieldIndex, ...$fieldValues); } /** * removes a role inheritance rule from the current named policy. * * @param string $ptype * @param mixed ...$params * * @return bool */ public function removeNamedGroupingPolicy(string $ptype, ...$params): bool { if (1 == count($params) && is_array($params[0])) { $params = $params[0]; } $ruleRemoved = $this->removePolicyInternal('g', $ptype, $params); if ($this->autoBuildRoleLinks) { $this->buildRoleLinks(); } return $ruleRemoved; } /** * removes a role inheritance rule from the current named policy, field filters can be specified. * * @param string $ptype * @param int $fieldIndex * @param string ...$fieldValues * * @return bool */ public function removeFilteredNamedGroupingPolicy(string $ptype, int $fieldIndex, string ...$fieldValues): bool { $ruleRemoved = $this->removeFilteredPolicyInternal('g', $ptype, $fieldIndex, ...$fieldValues); if ($this->autoBuildRoleLinks) { $this->buildRoleLinks(); } return $ruleRemoved; } /** * adds a customized function. * * @param string $name * @param \Closure $func */ public function addFunction(string $name, \Closure $func): void { $this->fm->addFunction($name, $func); } } src/Model/Assertion.php000064400000003210152475270340011060 0ustar00rM = $rm; $count = substr_count($this->value, '_'); foreach ($this->policy as $rule) { if ($count < 2) { throw new CasbinException('the number of "_" in role definition should be at least 2'); } if (\count($rule) < $count) { throw new CasbinException('grouping policy elements do not meet role definition'); } if (2 == $count) { $this->rM->addLink($rule[0], $rule[1]); } elseif (3 == $count) { $this->rM->addLink($rule[0], $rule[1], $rule[2]); } elseif (4 == $count) { $this->rM->addLink($rule[0], $rule[1], $rule[2], $rule[3]); } } Log::logPrint('Role links for: '.$this->key); $this->rM->printRoles(); } } src/Model/FunctionMap.php000064400000002355152475270340011345 0ustar00functions[$name] = $func; } /** * loads an initial function map. * * @return FunctionMap */ public static function loadFunctionMap(): self { $fm = new self(); $fm->addFunction('keyMatch', function (...$args) { return BuiltinOperations::keyMatchFunc(...$args); }); $fm->addFunction('keyMatch2', function (...$args) { return BuiltinOperations::keyMatch2Func(...$args); }); $fm->addFunction('regexMatch', function (...$args) { return BuiltinOperations::regexMatchFunc(...$args); }); $fm->addFunction('ipMatch', function (...$args) { return BuiltinOperations::ipMatchFunc(...$args); }); return $fm; } /** * @return array */ public function getFunctions(): array { return $this->functions; } } src/Model/Model.php000064400000012442152475270340010160 0ustar00 'request_definition', 'p' => 'policy_definition', 'g' => 'role_definition', 'e' => 'policy_effect', 'm' => 'matchers', ]; public function __construct() { } /** * @param ConfigContract $cfg * @param string $sec * @param string $key * * @return bool */ private function loadAssertion(ConfigContract $cfg, string $sec, string $key): bool { $value = $cfg->getString($this->sectionNameMap[$sec].'::'.$key); return $this->addDef($sec, $key, $value); } /** * adds an assertion to the model. * * @param string $sec * @param string $key * @param string $value * * @return bool */ public function addDef(string $sec, string $key, string $value): bool { if ('' == $value) { return false; } $ast = new Assertion(); $ast->key = $key; $ast->value = $value; if ('r' == $sec || 'p' == $sec) { $ast->tokens = explode(', ', $ast->value); foreach ($ast->tokens as $i => $token) { $ast->tokens[$i] = $key.'_'.$token; } } else { $ast->value = Util::removeComments(Util::escapeAssertion($ast->value)); } $this->items[$sec][$key] = $ast; return true; } /** * @param int $i * * @return string */ private function getKeySuffix(int $i): string { if (1 == $i) { return ''; } return (string) $i; } /** * @param ConfigContract $cfg * @param string $sec */ private function loadSection(ConfigContract $cfg, string $sec): void { $i = 1; for (; ;) { if (!$this->loadAssertion($cfg, $sec, $sec.$this->getKeySuffix($i))) { break; } else { ++$i; } } } /** * creates an empty model. * * @return Model */ public static function newModel(): self { return new self(); } /** * creates a model from a .CONF file. * * @param string $path * * @return Model */ public static function newModelFromFile(string $path): self { $m = self::newModel(); $m->loadModel($path); return $m; } /** * creates a model from a string which contains model text. * * @param string $text * * @return Model */ public static function newModelFromString(string $text): self { $m = self::newModel(); $m->loadModelFromText($text); return $m; } /** * loads the model from model CONF file. * * @param string $path */ public function loadModel(string $path): void { $cfg = Config::newConfig($path); $this->loadSection($cfg, 'r'); $this->loadSection($cfg, 'p'); $this->loadSection($cfg, 'e'); $this->loadSection($cfg, 'm'); $this->loadSection($cfg, 'g'); } /** * loads the model from the text. * * @param string $text */ public function loadModelFromText(string $text): void { $cfg = Config::newConfigFromText($text); $this->loadSection($cfg, 'r'); $this->loadSection($cfg, 'p'); $this->loadSection($cfg, 'e'); $this->loadSection($cfg, 'm'); $this->loadSection($cfg, 'g'); } /** * prints the model to the log. */ public function printModel(): void { Log::logPrint('Model:'); foreach ($this->items as $k => $v) { foreach ($v as $i => $j) { Log::logPrintf('%s.%s: %s', $k, $i, $j->value); } } } /** * loads an initial function map. * * @return FunctionMap */ public static function loadFunctionMap(): FunctionMap { return FunctionMap::loadFunctionMap(); } /** * Determine if the given Model option exists. * * @param mixed $offset * * @return bool */ public function offsetExists($offset) { return isset($this->items[$offset]); } /** * Get a Model option. * * @param mixed $offset * * @return mixed */ public function offsetGet($offset) { return isset($this->items[$offset]) ? $this->items[$offset] : null; } /** * Set a Model option. * * @param mixed $offset * @param mixed $value */ public function offsetSet($offset, $value) { if (is_null($offset)) { $this->items[] = $value; } else { $this->items[$offset] = $value; } } /** * Unset a Model option. * * @param mixed $offset */ public function offsetUnset($offset) { unset($this->items[$offset]); } } src/Model/Policy.php000064400000012666152475270340010367 0ustar00items['g'])) { return; } foreach ($this->items['g'] as $ast) { $ast->buildRoleLinks($rm); } } /** * prints the policy to log. */ public function printPolicy(): void { Log::logPrint('Policy:'); foreach (['p', 'g'] as $sec) { if (!isset($this->items[$sec])) { return; } foreach ($this->items[$sec] as $key => $ast) { Log::logPrint($key, ': ', $ast->value, ': ', $ast->policy); } } } /** * clears all current policy. */ public function clearPolicy(): void { foreach (['p', 'g'] as $sec) { if (!isset($this->items[$sec])) { return; } foreach ($this->items[$sec] as $key => $ast) { $this->items[$sec][$key]->policy = []; } } } /** * gets all rules in a policy. * * @param string $sec * @param string $ptype * * @return array */ public function getPolicy(string $sec, string $ptype): array { return $this->items[$sec][$ptype]->policy; } /** * gets rules based on field filters from a policy. * * @param string $sec * @param string $ptype * @param int $fieldIndex * @param string ...$fieldValues * * @return array */ public function getFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): array { $res = []; foreach ($this->items[$sec][$ptype]->policy as $rule) { $matched = true; foreach ($fieldValues as $i => $fieldValue) { if ('' != $fieldValue && $rule[$fieldIndex + $i] != $fieldValue) { $matched = false; break; } } if ($matched) { $res[] = $rule; } } return $res; } /** * determines whether a model has the specified policy rule. * * @param string $sec * @param string $ptype * @param array $rule * * @return bool */ public function hasPolicy(string $sec, string $ptype, array $rule): bool { if (!isset($this->items[$sec][$ptype])) { return false; } return in_array($rule, $this->items[$sec][$ptype]->policy, true); } /** * adds a policy rule to the model. * * @param string $sec * @param string $ptype * @param array $rule * * @return bool */ public function addPolicy(string $sec, string $ptype, array $rule): bool { if (!$this->hasPolicy($sec, $ptype, $rule)) { $this->items[$sec][$ptype]->policy[] = $rule; return true; } return false; } /** * removes a policy rule from the model. * * @param string $sec * @param string $ptype * @param array $rule * * @return bool */ public function removePolicy(string $sec, string $ptype, array $rule): bool { if (!isset($this->items[$sec][$ptype])) { return false; } $offset = array_search($rule, $this->items[$sec][$ptype]->policy, true); if (false === $offset) { return false; } array_splice($this->items[$sec][$ptype]->policy, $offset, 1); return true; } /** * removes policy rules based on field filters from the model. * * @param string $sec * @param string $ptype * @param int $fieldIndex * @param mixed ...$fieldValues * * @return bool */ public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): bool { $tmp = []; $res = false; if (!isset($this->items[$sec][$ptype])) { return $res; } foreach ($this->items[$sec][$ptype]->policy as $rule) { $matched = true; foreach ($fieldValues as $i => $fieldValue) { if ('' != $fieldValue && $rule[$fieldIndex + $i] != $fieldValue) { $matched = false; break; } } if ($matched) { $res = true; } else { $tmp[] = $rule; } } $this->items[$sec][$ptype]->policy = $tmp; return $res; } /** * gets all values for a field for all rules in a policy, duplicated values are removed. * * @param string $sec * @param string $ptype * @param int $fieldIndex * * @return array */ public function getValuesForFieldInPolicy(string $sec, string $ptype, int $fieldIndex): array { $values = []; if (!isset($this->items[$sec][$ptype])) { return $values; } foreach ($this->items[$sec][$ptype]->policy as $rule) { $values[] = $rule[$fieldIndex]; } Util::arrayRemoveDuplicates($values); return $values; } } src/Persist/Adapter.php000064400000002530152475270340011066 0ustar00policy[] = \array_slice($tokens, 1); $model[$sec] = $assertions; } } src/Persist/Adapters/FileAdapter.php000064400000007521152475270340013436 0ustar00filePath = $filePath; } /** * loads all policy rules from the storage. * * @param \Casbin\Model\Model $model * * @throws CasbinException */ public function loadPolicy(Model $model): void { if (!file_exists($this->filePath)) { throw new InvalidFilePathException('invalid file path, file path cannot be empty'); } $this->loadPolicyFile($model); } /** * saves all policy rules to the storage. * * @param \Casbin\Model\Model $model * * @throws CasbinException */ public function savePolicy(Model $model): void { if ('' == $this->filePath) { throw new InvalidFilePathException('invalid file path, file path cannot be empty'); } $writeString = ''; if (isset($model['p'])) { foreach ($model['p'] as $ptype => $ast) { foreach ($ast->policy as $rule) { $writeString .= $ptype.', '; $writeString .= Util::arrayToString($rule); $writeString .= PHP_EOL; } } } if (isset($model['g'])) { foreach ($model['g'] as $ptype => $ast) { foreach ($ast->policy as $rule) { $writeString .= $ptype.', '; $writeString .= Util::arrayToString($rule); $writeString .= PHP_EOL; } } } $this->savePolicyFile(rtrim($writeString, PHP_EOL)); } /** * @param \Casbin\Model\Model $model */ protected function loadPolicyFile(Model $model): void { $file = fopen($this->filePath, 'rb'); while ($line = fgets($file)) { $this->loadPolicyLine(trim($line), $model); } fclose($file); } /** * @param string $text */ protected function savePolicyFile(string $text): void { file_put_contents($this->filePath, $text, LOCK_EX); } /** * adds a policy rule to the storage. * * @param string $sec * @param string $ptype * @param array $rule * * @throws NotImplementedException */ public function addPolicy(string $sec, string $ptype, array $rule): void { throw new NotImplementedException('not implemented'); } /** * removes a policy rule from the storage. * * @param string $sec * @param string $ptype * @param array $rule * * @throws NotImplementedException */ public function removePolicy(string $sec, string $ptype, array $rule): void { throw new NotImplementedException('not implemented'); } /** * removes policy rules that match the filter from the storage. * * @param string $sec * @param string $ptype * @param int $fieldIndex * @param string ...$fieldValues * * @throws NotImplementedException */ public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): void { throw new NotImplementedException('not implemented'); } } src/Persist/Adapters/FileFilteredAdapter.php000075500000010267152475270340015121 0ustar00filtered = true; parent::__construct($filePath); } /** * loads all policy rules from the storage. * * @param \Casbin\Model\Model $model * * @throws CasbinException */ public function loadPolicy(Model $model): void { $this->filtered = false; parent::loadPolicy($model); } /** * loads only policy rules that match the filter. * * @param Model $model * @param mixed $filter * * @throws CasbinException */ public function loadFilteredPolicy(Model $model, $filter): void { if (is_null($filter)) { $this->loadPolicy($model); return; } if (!file_exists($this->filePath)) { throw new InvalidFilePathException('invalid file path, file path cannot be empty'); } if (!$filter instanceof Filter) { throw new InvalidFilterTypeException('invalid filter type'); } $this->loadFilteredPolicyFile($model, $filter, [$this, 'loadPolicyLine']); $this->filtered = true; } /** * returns true if the loaded policy has been filtered. * * @return bool */ public function isFiltered(): bool { return $this->filtered; } /** * SavePolicy saves all policy rules to the storage. * * @param Model $model * * @return void */ public function savePolicy(Model $model): void { if ($this->filtered) { throw new CannotSaveFilteredPolicy('cannot save a filtered policy'); } parent.savePolicy($model); } /** * loadFilteredPolicyFile function. * * @param Model $model * @param Filter $filter * @param callable $handler * * @return void */ protected function loadFilteredPolicyFile(Model $model, Filter $filter, callable $handler): void { $file = fopen($this->filePath, 'rb'); while ($line = fgets($file)) { $line = trim($line); if (self::filterLine($line, $filter)) { continue; } call_user_func($handler, $line, $model); } } /** * filterLine function. * * @param string $line * @param Filter $filter * * @return boolean */ protected static function filterLine(string $line, Filter $filter): bool { if (is_null($filter)) { return false; } $p = explode(',', $line); if (0 == \count($p)) { return true; } $filterSlice = []; switch (trim($p[0])) { case 'p': $filterSlice = $filter->p; break; case 'g': $filterSlice = $filter->g; break; } return self::filterWords($p, $filterSlice); } /** * filterWords function. * * @param array $line * @param array $filter * * @return boolean */ protected static function filterWords(array $line, array $filter): bool { if (count($line) < count($filter) + 1) { return true; } $skipLine = false; foreach ($filter as $i => $v) { if (strlen($v) > 0 && \trim($v) != trim($line[$i + 1])) { $skipLine = true; break; } } return $skipLine; } } src/Persist/Adapters/Filter.php000075500000001202152475270340012474 0ustar00p = $p; $this->g = $g; } } src/Persist/FilteredAdapter.php000064400000001247152475270340012551 0ustar00allRoles = []; $this->maxHierarchyLevel = $maxHierarchyLevel; } /** * @param string $name * * @return bool */ protected function hasRole(string $name): bool { return isset($this->allRoles[$name]); } /** * @param string $name * * @return Role */ protected function createRole(string $name): Role { if (!isset($this->allRoles[$name])) { $this->allRoles[$name] = new Role($name); } return $this->allRoles[$name]; } /** * clears all stored data and resets the role manager to the initial state. */ public function clear(): void { $this->allRoles = []; } /** * adds the inheritance link between role: name1 and role: name2. * aka role: name1 inherits role: name2. * domain is a prefix to the roles. * * @param string $name1 * @param string $name2 * @param string ...$domain */ public function addLink(string $name1, string $name2, string ...$domain): void { $prefix = self::getPrefix(...$domain); $this->createRole($prefix.$name1)->addRole( $this->createRole($prefix.$name2) ); } /** * deletes the inheritance link between role: name1 and role: name2. * aka role: name1 does not inherit role: name2 any more. * domain is a prefix to the roles. * * @param string $name1 * @param string $name2 * @param string ...$domain */ public function deleteLink(string $name1, string $name2, string ...$domain): void { $prefix = self::getPrefix(...$domain); list($name1, $name2) = array_map(function ($name) use ($prefix) { $name = $prefix.$name; if (!$this->hasRole($name)) { throw new CasbinException('error: name1 or name2 does not exist'); } return $name; }, [$name1, $name2]); $this->createRole($name1)->deleteRole( $this->createRole($name2) ); } /** * determines whether role: name1 inherits role: name2. * domain is a prefix to the roles. * * @param string $name1 * @param string $name2 * @param string ...$domain * * @return bool */ public function hasLink(string $name1, string $name2, string ...$domain): bool { $prefix = self::getPrefix(...$domain); if ($name1 == $name2) { return true; } list($name1, $name2) = array_map(function ($name) use ($prefix) { return $prefix.$name; }, [$name1, $name2]); if (!$this->hasRole($name1) || !$this->hasRole($name2)) { return false; } return $this->createRole($name1)->hasRole($name2, $this->maxHierarchyLevel); } /** * gets the roles that a subject inherits. * domain is a prefix to the roles. * * @param string $name * @param string ...$domain * * @return array */ public function getRoles(string $name, string ...$domain): array { $prefix = self::getPrefix(...$domain); $name = $prefix.$name; if (!$this->hasRole($name)) { return []; } $roles = $this->createRole($name)->getRoles(); if ('' != $prefix) { array_walk($roles, function (&$role, $key, $len) { $role = \substr($role, $len); }, \strlen($prefix)); } return $roles; } /** * gets the users that inherits a subject. * domain is an unreferenced parameter here, may be used in other implementations. * * @param string $name * @param string ...$domain * * @return array */ public function getUsers(string $name, string ...$domain): array { $prefix = self::getPrefix(...$domain); $name = $prefix.$name; if (!$this->hasRole($name)) { // throw new CasbinException('error: name does not exist'); return []; } $names = []; $len = \strlen($prefix); array_map(function ($role) use (&$names, $name, $len) { if ($role->hasDirectRole($name)) { $names[] = $len > 0 ? \substr($role->name, $len) : $role->name; } }, $this->allRoles); return $names; } /** * prints all the roles to log. */ public function printRoles(): void { $line = []; array_map(function ($role) use (&$line) { if ($text = $role->toString()) { $line[] = $text; } }, $this->allRoles); Log::logPrint(implode(', ', $line)); } /** * Get the prefix of the roles from domain. * * @param string ...$domain * * @return string */ private static function getPrefix(string ...$domain): string { $size = \count($domain); if ($size > 1) { throw new CasbinException('error: domain should be 1 parameter'); } return 1 == $size ? $domain[0].'::' : ''; } } src/Rbac/Role.php000064400000004523152475270340007631 0ustar00name = $name; } /** * @param self $role */ public function addRole(self $role): void { foreach ($this->roles as $rr) { if ($rr->name == $role->name) { return; } } $this->roles[] = $role; } /** * @param self $role */ public function deleteRole(self $role): void { foreach ($this->roles as $key => $rr) { if ($rr->name == $role->name) { unset($this->roles[$key]); return; } } } /** * @param string $name * @param int $hierarchyLevel * * @return bool */ public function hasRole(string $name, int $hierarchyLevel): bool { if ($name == $this->name) { return true; } if ($hierarchyLevel <= 0) { return false; } foreach ($this->roles as $role) { if ($role->hasRole($name, $hierarchyLevel - 1)) { return true; } } return false; } /** * @param string $name * * @return bool */ public function hasDirectRole(string $name): bool { foreach ($this->roles as $role) { if ($role->name == $name) { return true; } } return false; } /** * @return string */ public function toString(): string { $len = \count($this->roles); if (0 == $len) { return ''; } $names = implode(', ', $this->getRoles()); if (1 == $len) { return $this->name.' < '.$names; } else { return $this->name.' < ('.$names.')'; } } /** * @return array */ public function getRoles(): array { return array_map(function ($role) { return $role->name; }, $this->roles); } } src/Rbac/RoleManager.php000064400000004024152475270340011120 0ustar00model['g']['g']->rM->getRoles($name); } /** * gets the users that has a role. * * @param string $name * * @return array */ public function getUsersForRole(string $name): array { return $this->model['g']['g']->rM->getUsers($name); } /** * determines whether a user has a role. * * @param string $name * @param string $role * * @return bool */ public function hasRoleForUser(string $name, string $role): bool { $roles = $this->getRolesForUser($name); return in_array($role, $roles, true); } /** * adds a role for a user. * returns false if the user already has the role (aka not affected). * * @param string $user * @param string $role * * @return bool */ public function addRoleForUser(string $user, string $role): bool { return $this->addGroupingPolicy($user, $role); } /** * deletes a role for a user. * returns false if the user does not have the role (aka not affected). * * @param string $user * @param string $role * * @return bool */ public function deleteRoleForUser(string $user, string $role): bool { return $this->removeGroupingPolicy($user, $role); } /** * deletes all roles for a user. * returns false if the user does not have any roles (aka not affected). * * @param string $user * * @return bool */ public function deleteRolesForUser(string $user): bool { return $this->removeFilteredGroupingPolicy(0, $user); } /** * deletes a user. * returns false if the user does not exist (aka not affected). * * @param string $user * * @return bool */ public function deleteUser($user): bool { return $this->removeFilteredGroupingPolicy(0, $user); } /** * deletes a role. * * @param string $role */ public function deleteRole(string $role): bool { $res1 = $this->removeFilteredGroupingPolicy(1, $role); $res2 = $this->removeFilteredPolicy(0, $role); return $res1 || $res2; } /** * deletes a permission. * returns false if the permission does not exist (aka not affected). * * @param string ...$permission * * @return bool */ public function deletePermission(string ...$permission): bool { return $this->removeFilteredPolicy(1, ...$permission); } /** * adds a permission for a user or role. * returns false if the user or role already has the permission (aka not affected). * * @param string $user * @param string ...$permission * * @return bool */ public function addPermissionForUser(string $user, string ...$permission): bool { $params = array_merge([$user], $permission); return $this->addPolicy(...$params); } /** * deletes a permission for a user or role. * returns false if the user or role does not have the permission (aka not affected). * * @param string $user * @param string ...$permission * * @return bool */ public function deletePermissionForUser(string $user, string ...$permission): bool { $params = array_merge([$user], $permission); return $this->removePolicy(...$params); } /** * deletes permissions for a user or role. * returns false if the user or role does not have any permissions (aka not affected). * * @param string $user * * @return bool */ public function deletePermissionsForUser(string $user): bool { return $this->removeFilteredPolicy(0, $user); } /** * gets permissions for a user or role. * * @param string $user * * @return array */ public function getPermissionsForUser(string $user): array { return $this->getFilteredPolicy(0, $user); } /** * determines whether a user has a permission. * * @param string $user * @param string ...$permission * * @return bool */ public function hasPermissionForUser(string $user, string ...$permission): bool { $params = array_merge([$user], $permission); return $this->hasPolicy($params); } /** * gets implicit roles that a user has. * Compared to getRolesForUser(), this function retrieves indirect roles besides direct roles. * For example: * g, alice, role:admin * g, role:admin, role:user. * * getRolesForUser("alice") can only get: ["role:admin"]. * But getImplicitRolesForUser("alice") will get: ["role:admin", "role:user"]. * * @param string $name * @param string ...$domain * * @return array */ public function getImplicitRolesForUser(string $name, string ...$domain): array { $res = []; $roleSet = []; $roleSet[$name] = true; $q = []; $q[] = $name; for (; count($q) > 0;) { $name = $q[0]; $q = array_slice($q, 1); $roles = $this->rm->getRoles($name, ...$domain); foreach ($roles as $r) { if (!isset($roleSet[$r])) { $res[] = $r; $q[] = $r; $roleSet[$r] = true; } } } return $res; } /** * gets implicit permissions for a user or role. * Compared to getPermissionsForUser(), this function retrieves permissions for inherited roles. * For example: * p, admin, data1, read * p, alice, data2, read * g, alice, admin. * * getPermissionsForUser("alice") can only get: [["alice", "data2", "read"]]. * But getImplicitPermissionsForUser("alice") will get: [["admin", "data1", "read"], ["alice", "data2", "read"]]. * * @param string $user * @param string ...$domain * * @return array */ public function getImplicitPermissionsForUser(string $user, string ...$domain): array { $roles = array_merge( [$user], $this->getImplicitRolesForUser($user, ...$domain) ); $len = \count($domain); if ($len > 1) { throw new CasbinException('error: domain should be 1 parameter'); } $res = []; foreach ($roles as $role) { if (1 == $len) { $permissions = $this->getPermissionsForUserInDomain($role, $domain[0]); } else { $permissions = $this->getPermissionsForUser($role); } $res = array_merge($res, $permissions); } return $res; } /** * gets implicit users for a permission. * For example: * p, admin, data1, read * p, bob, data1, read * g, alice, admin * getImplicitUsersForPermission("data1", "read") will get: ["alice", "bob"]. * Note: only users will be returned, roles (2nd arg in "g") will be excluded. * * @param string ...$permission * * @return array */ public function getImplicitUsersForPermission(string ...$permission): array { $subjects = $this->getAllSubjects(); $roles = $this->getAllRoles(); $users = array_diff($subjects, $roles); $res = []; foreach ($users as $user) { $req = $permission; array_unshift($req, $user); $allowed = $this->enforce(...$req); if ($allowed) { $res[] = $user; } } return $res; } } src/RbacApiWithDomains.php000064400000003715152475270340011533 0ustar00model['g']['g']->rM->getUsers($name, $domain); } /** * gets the roles that a user has inside a domain. * * @param string $name * @param string $domain * * @return array */ public function getRolesForUserInDomain(string $name, string $domain): array { return $this->model['g']['g']->rM->getRoles($name, $domain); } /** * gets permissions for a user or role inside a domain. * * @param string $name * @param string $domain * * @return array */ public function getPermissionsForUserInDomain(string $name, string $domain): array { return $this->getFilteredPolicy(0, $name, $domain); } /** * adds a role for a user inside a domain. * returns false if the user already has the role (aka not affected). * * @param string $user * @param string $role * @param string $domain * * @return bool */ public function addRoleForUserInDomain(string $user, string $role, string $domain): bool { return $this->addGroupingPolicy($user, $role, $domain); } /** * deletes a role for a user inside a domain. * returns false if the user does not have the role (aka not affected). * * @param string $user * @param string $role * @param string $domain * * @return bool */ public function deleteRoleForUserInDomain(string $user, string $role, string $domain): bool { return $this->removeGroupingPolicy($user, $role, $domain); } } src/Util/BuiltinOperations.php000064400000012460152475270340012447 0ustar00contains($objIP1); } /** * the wrapper for IPMatch. * * @param mixed ...$args * * @return bool * * @throws \Exception */ public static function ipMatchFunc(...$args): bool { $ip1 = $args[0]; $ip2 = $args[1]; return self::ipMatch($ip1, $ip2); } /** * the factory method of the g(_, _) function. * * @param RoleManager|null $rm * * @return \Closure */ public static function generateGFunction(RoleManager $rm = null): \Closure { return function (...$args) use ($rm) { $name1 = $args[0]; $name2 = $args[1]; if (null === $rm) { return $name1 == $name2; } elseif (2 == \count($args)) { $res = $rm->hasLink($name1, $name2); return $res; } else { $domain = (string) $args[2]; $res = $rm->hasLink($name1, $name2, $domain); return $res; } }; } } src/Util/Util.php000064400000002753152475270340007716 0ustar00|,|\+|-|!|\*|\/)(r|p)(\.)~", function ($m) { return $m[1].$m[2].'_'; }, $s); return $s; } /** * removes the comments starting with # in the text. * * @param string $s * * @return string */ public static function removeComments(string $s): string { $pos = strpos($s, '#'); return false === $pos ? $s : trim(substr($s, 0, $pos)); } /** * gets a printable string for a string array. * * @param array $s * * @return string */ public static function arrayToString(array $s): string { return implode(', ', $s); } /** * removes any duplicated elements in a string array. * * @param array $s */ public static function arrayRemoveDuplicates(array &$s): void { $s = array_keys(array_flip($s)); } } tests/EnforcerTest.php000064400000024567152475270340011051 0ustar00addDef('r', 'r', 'sub, obj, act'); $m->addDef('p', 'p', 'sub, obj, act'); $m->addDef('e', 'e', 'some(where (p.eft == allow))'); $m->addDef('m', 'm', 'r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)'); $a = new FileAdapter($this->modelAndPolicyPath.'/keymatch_policy.csv'); $e = new Enforcer($m, $a); $this->assertTrue($e->enforce('alice', '/alice_data/resource1', 'GET')); $this->assertFalse($e->enforce('bob', '/alice_data/resource1', 'GET')); $e = new Enforcer($m); $this->assertTrue($e->enforce('alice', '/alice_data/resource1', 'GET')); $this->assertFalse($e->enforce('bob', '/alice_data/resource1', 'GET')); } public function testKeyMatchModelInMemoryDeny() { $m = Model::newModel(); $m->addDef('r', 'r', 'sub, obj, act'); $m->addDef('p', 'p', 'sub, obj, act'); $m->addDef('e', 'e', '!some(where (p.eft == deny))'); $m->addDef('m', 'm', 'r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)'); $a = new FileAdapter($this->modelAndPolicyPath.'/keymatch_policy.csv'); $e = new Enforcer($m, $a); $this->assertTrue($e->enforce('alice', '/alice_data/resource1', 'GET')); } public function testRBACModelInMemoryIndeterminate() { $m = Model::newModel(); $m->addDef('r', 'r', 'sub, obj, act'); $m->addDef('p', 'p', 'sub, obj, act'); $m->addDef('g', 'g', '_, _'); $m->addDef('e', 'e', 'some(where (p.eft == allow))'); $m->addDef('m', 'm', 'g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act'); $e = new Enforcer($m); $e->addPermissionForUser('alice', 'data1', 'invalid'); $this->assertFalse($e->enforce('alice', 'data1', 'read')); } public function testEnforceBasic() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $this->assertEquals($e->enforce('alice', 'data1', 'read'), true); $this->assertEquals($e->enforce('alice', 'data2', 'read'), false); $this->assertEquals($e->enforce('bob', 'data2', 'write'), true); $this->assertEquals($e->enforce('bob', 'data1', 'write'), false); } public function testEnforceBasicNoPolicy() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_model.conf'); $this->assertEquals($e->enforce('alice', 'data1', 'read'), false); $this->assertEquals($e->enforce('alice', 'data2', 'read'), false); $this->assertEquals($e->enforce('bob', 'data2', 'write'), false); $this->assertEquals($e->enforce('bob', 'data1', 'write'), false); } public function testEnforceBasicWithRoot() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_with_root_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $this->assertEquals($e->enforce('root', 'any', 'any'), true); } public function testEnforceBasicWithRootNoPolicy() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_with_root_model.conf'); $this->assertFalse($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); $this->assertFalse($e->enforce('alice', 'data2', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'write')); $this->assertFalse($e->enforce('bob', 'data1', 'read')); $this->assertFalse($e->enforce('bob', 'data1', 'write')); $this->assertFalse($e->enforce('bob', 'data2', 'read')); $this->assertFalse($e->enforce('bob', 'data2', 'write')); $this->assertTrue($e->enforce('root', 'data1', 'read')); $this->assertTrue($e->enforce('root', 'data1', 'write')); $this->assertTrue($e->enforce('root', 'data2', 'read')); $this->assertTrue($e->enforce('root', 'data2', 'write')); } public function testEnforceBasicWithoutResources() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_without_resources_model.conf', $this->modelAndPolicyPath.'/basic_without_resources_policy.csv'); $this->assertEquals($e->enforce('alice', 'read'), true); $this->assertEquals($e->enforce('alice', 'write'), false); $this->assertEquals($e->enforce('bob', 'write'), true); $this->assertEquals($e->enforce('bob', 'read'), false); } public function testEnforceBasicWithoutUsers() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_without_users_model.conf', $this->modelAndPolicyPath.'/basic_without_users_policy.csv'); $this->assertEquals($e->enforce('data1', 'read'), true); $this->assertEquals($e->enforce('data1', 'write'), false); $this->assertEquals($e->enforce('data2', 'write'), true); $this->assertEquals($e->enforce('data2', 'read'), false); } public function testEnforceIpMatch() { $e = new Enforcer($this->modelAndPolicyPath.'/ipmatch_model.conf', $this->modelAndPolicyPath.'/ipmatch_policy.csv'); $this->assertEquals($e->enforce('192.168.2.1', 'data1', 'read'), true); $this->assertEquals($e->enforce('192.168.3.1', 'data1', 'read'), false); } public function testEnforceKeyMatch() { $e = new Enforcer($this->modelAndPolicyPath.'/keymatch_model.conf', $this->modelAndPolicyPath.'/keymatch_policy.csv'); $this->assertEquals($e->enforce('alice', '/alice_data/test', 'GET'), true); $this->assertEquals($e->enforce('alice', '/bob_data/test', 'GET'), false); $this->assertEquals($e->enforce('cathy', '/cathy_data', 'GET'), true); $this->assertEquals($e->enforce('cathy', '/cathy_data', 'POST'), true); $this->assertEquals($e->enforce('cathy', '/cathy_data/12', 'POST'), false); } public function testEnforceKeyMatch2() { $e = new Enforcer($this->modelAndPolicyPath.'/keymatch2_model.conf', $this->modelAndPolicyPath.'/keymatch2_policy.csv'); $this->assertEquals($e->enforce('alice', '/alice_data/resource', 'GET'), true); $this->assertEquals($e->enforce('alice', '/alice_data2/123/using/456', 'GET'), true); } public function testEnforcePriority() { $e = new Enforcer($this->modelAndPolicyPath.'/priority_model.conf', $this->modelAndPolicyPath.'/priority_policy.csv'); $this->assertEquals($e->enforce('alice', 'data1', 'read'), true); $this->assertEquals($e->enforce('alice', 'data1', 'write'), false); $this->assertEquals($e->enforce('alice', 'data2', 'read'), false); $this->assertEquals($e->enforce('alice', 'data2', 'read'), false); $this->assertEquals($e->enforce('bob', 'data1', 'read'), false); $this->assertEquals($e->enforce('bob', 'data1', 'write'), false); $this->assertEquals($e->enforce('bob', 'data2', 'read'), true); $this->assertEquals($e->enforce('bob', 'data2', 'write'), false); } public function testEnforcePriorityIndeterminate() { $e = new Enforcer($this->modelAndPolicyPath.'/priority_model.conf', $this->modelAndPolicyPath.'/priority_indeterminate_policy.csv'); $this->assertEquals($e->enforce('alice', 'data1', 'read'), false); } public function testEnforceRbac() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $this->assertEquals($e->enforce('alice', 'data1', 'read'), true); $this->assertEquals($e->enforce('bob', 'data2', 'write'), true); $this->assertEquals($e->enforce('alice', 'data2', 'read'), true); $this->assertEquals($e->enforce('alice', 'data2', 'write'), true); } public function testEnforceRbacWithDeny() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_deny_model.conf', $this->modelAndPolicyPath.'/rbac_with_deny_policy.csv'); $this->assertEquals($e->enforce('alice', 'data1', 'read'), true); $this->assertEquals($e->enforce('bob', 'data2', 'write'), true); $this->assertEquals($e->enforce('alice', 'data2', 'read'), true); $this->assertEquals($e->enforce('alice', 'data2', 'write'), false); } public function testEnforceRbacWithDomains() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_domains_model.conf', $this->modelAndPolicyPath.'/rbac_with_domains_policy.csv'); $this->assertEquals($e->enforce('alice', 'domain1', 'data1', 'read'), true); $this->assertEquals($e->enforce('alice', 'domain1', 'data1', 'write'), true); $this->assertEquals($e->enforce('alice', 'domain1', 'data2', 'read'), false); $this->assertEquals($e->enforce('alice', 'domain1', 'data2', 'write'), false); $this->assertEquals($e->enforce('bob', 'domain2', 'data1', 'read'), false); $this->assertEquals($e->enforce('bob', 'domain2', 'data1', 'write'), false); $this->assertEquals($e->enforce('bob', 'domain2', 'data2', 'read'), true); $this->assertEquals($e->enforce('bob', 'domain2', 'data2', 'write'), true); } public function testEnforceRbacWithNotDeny() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_not_deny_model.conf', $this->modelAndPolicyPath.'/rbac_with_deny_policy.csv'); $this->assertEquals($e->enforce('alice', 'data2', 'write'), false); } public function testEnforceRbacWithResourceRoles() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_resource_roles_model.conf', $this->modelAndPolicyPath.'/rbac_with_resource_roles_policy.csv'); $this->assertEquals($e->enforce('alice', 'data1', 'read'), true); $this->assertEquals($e->enforce('alice', 'data1', 'write'), true); $this->assertEquals($e->enforce('alice', 'data2', 'read'), false); $this->assertEquals($e->enforce('alice', 'data2', 'write'), true); $this->assertEquals($e->enforce('bob', 'data1', 'read'), false); $this->assertEquals($e->enforce('bob', 'data1', 'write'), false); $this->assertEquals($e->enforce('bob', 'data2', 'read'), false); $this->assertEquals($e->enforce('bob', 'data2', 'write'), true); } } tests/Unit/CachedEnforcerTest.php000064400000003305152475270340013043 0ustar00modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); $this->assertFalse($e->enforce('alice', 'data2', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'write')); $e->removePolicy('alice', 'data1', 'read'); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); $this->assertFalse($e->enforce('alice', 'data2', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'write')); $e->invalidateCache(); $this->assertFalse($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); $this->assertFalse($e->enforce('alice', 'data2', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'write')); } public function testEnableCache() { $e = new CachedEnforcer($this->modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $e->enableCache(false); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $e->removePolicy('alice', 'data1', 'read'); $this->assertFalse($e->enforce('alice', 'data1', 'read')); } } tests/Unit/Config/ConfigTest.php000064400000004310152475270340012617 0ustar00getAndSetConfig($cfg); } public function testNewConfigFromText() { $cfg = Config::newConfigFromText(file_get_contents(__DIR__.'/test.ini')); $this->getAndSetConfig($cfg); try { $cfg = Config::newConfigFromText(<<<'EOT' [mysql] mysql.dev.host = 127.0.0.1 mysql.dev.user EOT ); } catch (\Exception $e) { $this->assertTrue($e instanceof CasbinException); } } private function getAndSetConfig(Config $cfg) { // $cfg = Config::newConfigFromText(file_get_contents(__DIR__.'/test.ini')); $this->assertEquals('act.wiki', $cfg->getString('url')); $v = $cfg->getStrings('redis::redis.key'); $this->assertTrue(2 == \count($v) && 'push1' == $v[0] && 'push2' == $v[1]); $v = $cfg->getString('mysql::mysql.dev.host'); $this->assertEquals('127.0.0.1', $v); $cfg->set('other::key1', 'new test key'); $v = $cfg->getString('other::key1'); $this->assertEquals('new test key', $v); $v = $cfg->getString('multi1::name'); $this->assertEquals('r.sub==p.sub&&r.obj==p.obj', $v); $v = $cfg->getString('multi2::name'); $this->assertEquals('r.sub==p.sub&&r.obj==p.obj', $v); $v = $cfg->getString('multi3::name'); $this->assertEquals('r.sub==p.sub&&r.obj==p.obj', $v); $v = $cfg->getString('multi4::name'); $this->assertEquals('', $v); $v = $cfg->getString('multi5::name'); $this->assertEquals('r.sub==p.sub&&r.obj==p.obj', $v); $v = $cfg->getStrings('noexist'); $this->assertEquals([], $v); try { $cfg->set('', ''); } catch (\Exception $e) { $this->assertTrue($e instanceof CasbinException); } $cfg->set('nosec', 'nosec'); $this->assertEquals('nosec', $cfg->getString('nosec')); } } tests/Unit/Config/test.ini000064400000001273152475270340011526 0ustar00# test config debug = true url = act.wiki ; redis config [redis] redis.key = push1,push2 ; mysql config [mysql] mysql.dev.host = 127.0.0.1 mysql.dev.user = root mysql.dev.pass = 123456 mysql.dev.db = test mysql.master.host = 10.0.0.1 mysql.master.user = root mysql.master.pass = 89dds)2$#d mysql.master.db = act ; math config [math] math.i64 = 64 math.f64 = 64.1 ; other config [other] name = ATC自动化测试^-^&($#……# key1 = test key # multi-line test [multi1] name = r.sub==p.sub \ &&r.obj==p.obj\ \ [multi2] name = r.sub==p.sub \ &&r.obj==p.obj [multi3] name = r.sub==p.sub \ &&r.obj==p.obj [multi4] name = \ \ \ [multi5] name = r.sub==p.sub \ &&r.obj==p.obj\ \tests/Unit/EnforcerTest.php000064400000020663152475270340011761 0ustar00modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv', true); $this->assertTrue($e->enforce('alice', 'data1', 'read')); // The log can also be enabled or disabled at run-time. $e->enableLog(false); $this->assertTrue($e->enforce('alice', 'data1', 'read')); } public function testEnableAutoSave() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $e->enableAutoSave(false); // Because AutoSave is disabled, the policy change only affects the policy in Casbin enforcer, // it doesn't affect the policy in the storage. $e->removePolicy('alice', 'data1', 'read'); // Reload the policy from the storage to see the effect. $e->loadPolicy(); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); $e->enableAutoSave(true); // Because AutoSave is enabled, the policy change not only affects the policy in Casbin enforcer, // but also affects the policy in the storage. $e->removePolicy('alice', 'data1', 'read'); // However, the file adapter doesn't implement the AutoSave feature, so enabling it has no effect at all here. // Reload the policy from the storage to see the effect. $e->loadPolicy(); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); } public function testInitEmpty() { $e = new Enforcer(true); $m = Model::newModelFromString( <<<'EOT' [request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act EOT ); $e->setModel($m); $adapter = new FileAdapter($this->modelAndPolicyPath.'/basic_policy.csv'); $e->setAdapter($adapter); $e->loadPolicy(); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'read')); } public function testGetAndSetModel() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $e2 = new Enforcer($this->modelAndPolicyPath.'/basic_with_root_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $this->assertFalse($e->enforce('root', 'data1', 'read')); $e->setModel($e2->getModel()); $this->assertTrue($e->enforce('root', 'data1', 'read')); } public function testGetAndSetAdapter() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $e2 = new Enforcer($this->modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_inverse_policy.csv'); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); $a2 = $e2->getAdapter(); $e->setAdapter($a2); $e->loadModel(); $e->loadPolicy(); $this->assertFalse($e->enforce('alice', 'data1', 'read')); $this->assertTrue($e->enforce('alice', 'data1', 'write')); } public function testSetAdapterFromFile() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_model.conf'); $adapter = new FileAdapter($this->modelAndPolicyPath.'/basic_policy.csv'); $e->setAdapter($adapter); $e->loadPolicy(); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'read')); } public function testClearPolicy() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $e->clearPolicy(); $this->assertFalse($e->enforce('alice', 'data1', 'read')); } public function testSavePolicy() { $policyFile = __DIR__.'/Persist/Adapters/rbac_policy_test.csv'; file_put_contents($policyFile, '', LOCK_EX); $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $policyFile); $this->assertEquals($e->enforce('alice', 'data1', 'read'), false); $this->assertEquals($e->enforce('bob', 'data2', 'write'), false); $this->assertEquals($e->enforce('alice', 'data2', 'read'), false); $this->assertEquals($e->enforce('alice', 'data2', 'write'), false); $m = $e->getModel(); $m->addPolicy('p', 'p', ['alice', 'data1', 'read']); $m->addPolicy('p', 'p', ['bob', 'data2', 'write']); $m->addPolicy('p', 'p', ['data2_admin', 'data2', 'read']); $m->addPolicy('p', 'p', ['data2_admin', 'data2', 'write']); $m->addPolicy('g', 'g', ['alice', 'data2_admin']); $e->savePolicy(); $e2 = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $policyFile); $this->assertEquals($e2->enforce('alice', 'data1', 'read'), true); $this->assertEquals($e2->enforce('bob', 'data2', 'write'), true); $this->assertEquals($e2->enforce('alice', 'data2', 'read'), true); $this->assertEquals($e2->enforce('alice', 'data2', 'write'), true); file_put_contents($policyFile, '', LOCK_EX); } public function testFilteredPolicy() { $adapter = new FileFilteredAdapter($this->modelAndPolicyPath.'/rbac_with_domains_policy.csv'); $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_domains_model.conf', $adapter); $this->assertTrue($e->isFiltered()); $e->loadPolicy(); $this->assertTrue($e->hasPolicy('admin', 'domain1', 'data1', 'read')); $this->assertTrue($e->hasPolicy('admin', 'domain2', 'data2', 'read')); $e->loadFilteredPolicy(new Filter( ['', 'domain1'], ['', '', 'domain1'] )); $this->assertTrue($e->hasPolicy('admin', 'domain1', 'data1', 'read')); $this->assertFalse($e->hasPolicy('admin', 'domain2', 'data2', 'read')); $th = null; try { $e->savePolicy(); } catch (\Throwable $th) { //throw $th; } $this->assertInstanceOf(CasbinException::class, $th); $th = null; try { $e->getAdapter()->savePolicy($e->getModel()); } catch (\Throwable $th) { //throw $th; } $this->assertInstanceOf(CasbinException::class, $th); } public function testEnableEnforce() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_model.conf', $this->modelAndPolicyPath.'/basic_policy.csv'); $e->enableEnforce(false); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertTrue($e->enforce('alice', 'data1', 'write')); $this->assertTrue($e->enforce('alice', 'data2', 'read')); $this->assertTrue($e->enforce('alice', 'data2', 'write')); $this->assertTrue($e->enforce('bob', 'data1', 'read')); $this->assertTrue($e->enforce('bob', 'data1', 'write')); $this->assertTrue($e->enforce('bob', 'data2', 'read')); $this->assertTrue($e->enforce('bob', 'data2', 'write')); $e->enableEnforce(true); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); $this->assertFalse($e->enforce('alice', 'data2', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'write')); $this->assertFalse($e->enforce('bob', 'data1', 'read')); $this->assertFalse($e->enforce('bob', 'data1', 'write')); $this->assertFalse($e->enforce('bob', 'data2', 'read')); $this->assertTrue($e->enforce('bob', 'data2', 'write')); } } tests/Unit/Log/LogTest.php000064400000001110152475270340011442 0ustar00enableLog(true); $enable = Log::getLogger()->isEnabled(); $this->assertTrue($enable); Log::getLogger()->enableLog(false); $enable = Log::getLogger()->isEnabled(); $this->assertFalse($enable); } } tests/Unit/Log/Logger/DefaultLoggerTest.php000064400000001464152475270340014700 0ustar00enableLog(true); $enable = $logger->isEnabled(); $this->assertTrue($enable); $path = $logger->path; $name = $logger->name; $logfile = $logger->path.DIRECTORY_SEPARATOR.$logger->name; if (file_exists($logfile)) { unlink($logfile); } $logger->write('testing logger'); $logger->write(['testing', 'logger']); $logger->writef('testing %s', 'DefaultLogger'); $this->assertTrue(file_exists($logfile)); } } tests/Unit/ManagementApiTest.php000064400000011041152475270340012712 0ustar00modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $this->assertEquals($e->getAllSubjects(), ['alice', 'bob', 'data2_admin']); $this->assertEquals($e->getAllObjects(), ['data1', 'data2']); $this->assertEquals($e->getAllActions(), ['read', 'write']); $this->assertEquals($e->getAllRoles(), ['data2_admin']); } public function testGetPolicyAPI() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $this->assertEquals($e->getPolicy(), [ ['alice', 'data1', 'read'], ['bob', 'data2', 'write'], ['data2_admin', 'data2', 'read'], ['data2_admin', 'data2', 'write'], ]); $this->assertEquals($e->getFilteredPolicy(0, 'alice'), [['alice', 'data1', 'read']]); $this->assertEquals($e->getFilteredPolicy(0, 'bob'), [['bob', 'data2', 'write']]); $this->assertEquals($e->getFilteredPolicy(0, 'data2_admin'), [['data2_admin', 'data2', 'read'], ['data2_admin', 'data2', 'write']]); $this->assertEquals($e->getFilteredPolicy(1, 'data1'), [['alice', 'data1', 'read']]); $this->assertEquals($e->getFilteredPolicy(1, 'data2'), [['bob', 'data2', 'write'], ['data2_admin', 'data2', 'read'], ['data2_admin', 'data2', 'write']]); $this->assertEquals($e->getFilteredPolicy(2, 'read'), [['alice', 'data1', 'read'], ['data2_admin', 'data2', 'read']]); $this->assertEquals($e->getFilteredPolicy(2, 'write'), [['bob', 'data2', 'write'], ['data2_admin', 'data2', 'write']]); $this->assertEquals($e->getFilteredPolicy(0, 'data2_admin', 'data2'), [['data2_admin', 'data2', 'read'], ['data2_admin', 'data2', 'write']]); // Note: "" (empty string) in fieldValues means matching all values. $this->assertEquals($e->getFilteredPolicy(0, 'data2_admin', '', 'read'), [['data2_admin', 'data2', 'read']]); $this->assertEquals($e->getFilteredPolicy(1, 'data2', 'write'), [['bob', 'data2', 'write'], ['data2_admin', 'data2', 'write']]); $this->assertTrue($e->hasPolicy(['alice', 'data1', 'read'])); $this->assertTrue($e->hasPolicy(['bob', 'data2', 'write'])); $this->assertFalse($e->hasPolicy(['alice', 'data2', 'read'])); $this->assertFalse($e->hasPolicy(['bob', 'data3', 'write'])); $this->assertEquals($e->getGroupingPolicy(), [['alice', 'data2_admin']]); $this->assertEquals($e->getFilteredGroupingPolicy(0, 'alice'), [['alice', 'data2_admin']]); $this->assertEquals($e->getFilteredGroupingPolicy(0, 'bob'), []); $this->assertEquals($e->getFilteredGroupingPolicy(1, 'data1_admin'), []); $this->assertEquals($e->getFilteredGroupingPolicy(1, 'data2_admin'), [['alice', 'data2_admin']]); // Note: "" (empty string) in fieldValues means matching all values. $this->assertEquals($e->getFilteredGroupingPolicy(0, '', 'data2_admin'), [['alice', 'data2_admin']]); $this->assertTrue($e->hasGroupingPolicy(['alice', 'data2_admin'])); $this->assertFalse($e->hasGroupingPolicy(['bob', 'data2_admin'])); } public function testModifyPolicyAPI() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $this->assertEquals($e->getPolicy(), [ ['alice', 'data1', 'read'], ['bob', 'data2', 'write'], ['data2_admin', 'data2', 'read'], ['data2_admin', 'data2', 'write'], ]); $e->removePolicy('alice', 'data1', 'read'); $e->removePolicy('bob', 'data2', 'write'); $e->removePolicy('alice', 'data1', 'read'); $e->addPolicy('eve', 'data3', 'read'); $e->addPolicy('eve', 'data3', 'read'); $namedPolicy = ['eve', 'data3', 'read']; $e->removeNamedPolicy('p', $namedPolicy); $e->addNamedPolicy('p', $namedPolicy); $this->assertEquals($e->getPolicy(), [ ['data2_admin', 'data2', 'read'], ['data2_admin', 'data2', 'write'], ['eve', 'data3', 'read'], ]); $e->removeFilteredPolicy(1, 'data2'); $this->assertEquals($e->getPolicy(), [ ['eve', 'data3', 'read'], ]); } } tests/Unit/Model/ModelTest.php000064400000002200152475270340012301 0ustar00loadModelFromText($text); $rule = ['alice', 'data1', 'read']; $m->addPolicy('p', 'p', $rule); $rule = ['bob', 'data2', 'write']; $m->addPolicy('p', 'p', $rule); $e = new Enforcer($m); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'write')); $this->assertFalse($e->enforce('bob', 'data1', 'read')); $this->assertTrue($e->enforce('bob', 'data2', 'write')); } } tests/Unit/Model/PolicyTest.php000064400000005000152475270340012501 0ustar00loadModel($this->modelAndPolicyPath.'/basic_model.conf'); $rule = ['admin', 'domain1', 'data1', 'read']; $m->addPolicy('p', 'p', $rule); $this->assertTrue($m->getPolicy('p', 'p') == [$rule]); } public function testHasPolicy() { $m = new Model(); $m->loadModel($this->modelAndPolicyPath.'/basic_model.conf'); $rule = ['admin', 'domain1', 'data1', 'read']; $m->addPolicy('p', 'p', $rule); $this->assertTrue($m->hasPolicy('p', 'p', $rule)); } public function testAddPolicy() { $m = new Model(); $m->loadModel($this->modelAndPolicyPath.'/basic_model.conf'); $rule = ['admin', 'domain1', 'data1', 'read']; $this->assertFalse($m->hasPolicy('p', 'p', $rule)); $m->addPolicy('p', 'p', $rule); $this->assertTrue($m->hasPolicy('p', 'p', $rule)); } public function testRemovePolicy() { $m = new Model(); $m->loadModel($this->modelAndPolicyPath.'/basic_model.conf'); $rule = ['admin', 'domain1', 'data1', 'read']; $m->addPolicy('p', 'p', $rule); $this->assertTrue($m->hasPolicy('p', 'p', $rule)); $m->removePolicy('p', 'p', $rule); $this->assertFalse($m->hasPolicy('p', 'p', $rule)); $this->assertFalse($m->removePolicy('p', 'p', $rule)); } public function testRemoveFilteredPolicy() { $m = new Model(); $m->loadModel($this->modelAndPolicyPath.'/rbac_with_domains_model.conf'); $rule = ['admin', 'domain1', 'data1', 'read']; $m->addPolicy('p', 'p', $rule); $res = $m->removeFilteredPolicy('p', 'p', 1, 'domain1', 'data1'); $this->assertTrue($res); $res = $m->removeFilteredPolicy('p', 'p', 1, 'domain1', 'read'); $this->assertFalse($res); } public function testGetValuesForFieldInPolicy() { $m = new Model(); $m->loadModel($this->modelAndPolicyPath.'/rbac_with_domains_model.conf'); $rule = ['admin', 'domain1', 'data1', 'read']; $m->addPolicy('p', 'p', $rule); $res = $m->getValuesForFieldInPolicy('p', 'p', 1); $this->assertTrue(['domain1'] == $res); } } tests/Unit/Persist/Adapters/FileAdapterTest.php000064400000001320152475270340015557 0ustar00loadModel($this->modelAndPolicyPath.'/basic_model.conf'); $rule = ['admin', 'domain1', 'data1', 'read2']; $m->addPolicy('p', 'p', $rule); $res = $adapter->savePolicy($m); $this->assertFalse(false === $res); } } tests/Unit/Persist/Adapters/FileFilteredAdapterTest.php000075500000003764152475270340017257 0ustar00modelAndPolicyPath.'/rbac_with_domains_policy.csv'); $this->assertTrue($adapter->isFiltered()); $m = new Model(); $m->loadModel($this->modelAndPolicyPath.'/rbac_with_domains_model.conf'); $adapter->loadFilteredPolicy($m, null); $this->assertFalse($adapter->isFiltered()); $this->assertTrue($m->hasPolicy('p', 'p', ['admin', 'domain1', 'data1', 'read'])); $this->assertTrue($m->hasPolicy('p', 'p', ['admin', 'domain2', 'data2', 'read'])); $m->clearPolicy(); $filter = new Filter(); $filter->p = ['', 'domain1']; $filter->g = ['', '', 'domain1']; $adapter->loadFilteredPolicy($m, $filter); $this->assertTrue($adapter->isFiltered()); $this->assertTrue($m->hasPolicy('p', 'p', ['admin', 'domain1', 'data1', 'read'])); $this->assertFalse($m->hasPolicy('p', 'p', ['admin', 'domain2', 'data2', 'read'])); try { $adapter->savePolicy($m); } catch (\Throwable $th) { $this->assertInstanceOf(CasbinException::class, $th); } try { $adapter->loadFilteredPolicy($m, new \stdClass()); } catch (\Throwable $th) { $this->assertInstanceOf(CasbinException::class, $th); } try { $adapter = new FileFilteredAdapter(''); $adapter->loadFilteredPolicy($m, $filter); } catch (\Throwable $th) { $this->assertInstanceOf(CasbinException::class, $th); } } } tests/Unit/Persist/Adapters/basic_policy_test.csv000064400000000037152475270340016246 0ustar00p, admin, domain1, data1, read2tests/Unit/Persist/Adapters/rbac_policy_test.csv000064400000000000152475270340016062 0ustar00tests/Unit/Rbac/DefaultRoleManager/RoleManagerTest.php000064400000004145152475270340016757 0ustar00addLink('u1', 'g1'); $res = $rm->hasLink('u1', 'g1'); $this->assertTrue($res); } public function testDeleteLink() { $rm = new RoleManager(3); try { $rm->deleteLink('u1', 'g1'); } catch (\Exception $e) { $this->assertTrue($e instanceof CasbinException); } $rm->addLink('u1', 'g1'); $res = $rm->hasLink('u1', 'g1'); $this->assertTrue($res); $rm->deleteLink('u1', 'g1'); $res = $rm->hasLink('u1', 'g1'); $this->assertFalse($res); } public function testGetRoles() { $rm = new RoleManager(3); $rm->addLink('u1', 'g1'); $rm->addLink('u2', 'g1'); $rm->addLink('u3', 'g2'); $rm->addLink('u4', 'g2'); $rm->addLink('u4', 'g3'); $rm->addLink('g1', 'g3'); // Current role inheritance tree: // g3 g2 // / \ / \ // g1 u4 u3 // / \ // u1 u2 $this->assertEquals($rm->getRoles('u1'), ['g1']); $this->assertEquals($rm->getRoles('u4'), ['g2', 'g3']); } public function testGetUsers() { $rm = new RoleManager(3); $rm->addLink('u1', 'g1'); $rm->addLink('u2', 'g1'); $rm->addLink('u3', 'g2'); $rm->addLink('u4', 'g2'); $rm->addLink('u4', 'g3'); $rm->addLink('g1', 'g3'); // Current role inheritance tree: // g3 g2 // / \ / \ // g1 u4 u3 // / \ // u1 u2 $this->assertEquals($rm->getUsers('g1'), ['u1', 'u2']); $this->assertEquals($rm->getUsers('g3'), ['g1', 'u4']); } } tests/Unit/RbacApiTest.php000064400000020102152475270340011503 0ustar00modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $this->assertEquals($e->getRolesForUser('alice'), ['data2_admin']); $this->assertEquals($e->getRolesForUser('bob'), []); $this->assertEquals($e->getRolesForUser('data2_admin'), []); $this->assertEquals($e->getRolesForUser('non_exist'), []); } public function testGetUsersForRole() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $this->assertEquals($e->getUsersForRole('data2_admin'), ['alice']); } public function testHasRoleForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $this->assertTrue($e->hasRoleForUser('alice', 'data2_admin')); $this->assertFalse($e->hasRoleForUser('alice', 'data1_admin')); } public function testAddRoleForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $e->addRoleForUser('alice', 'data1_admin'); $this->assertEquals($e->getRolesForUser('alice'), ['data2_admin', 'data1_admin']); } public function testDeleteRoleForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $e->addRoleForUser('alice', 'data1_admin'); $this->assertEquals($e->getRolesForUser('alice'), ['data2_admin', 'data1_admin']); $e->deleteRoleForUser('alice', 'data1_admin'); $this->assertEquals($e->getRolesForUser('alice'), ['data2_admin']); } public function testDeleteRolesForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $e->deleteRolesForUser('alice'); $this->assertEquals($e->getRolesForUser('alice'), []); } public function testDeleteUser() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $e->deleteUser('alice'); $this->assertEquals($e->getRolesForUser('alice'), []); } public function testDeleteRole() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_policy.csv'); $e->deleteRole('data2_admin'); $this->assertTrue($e->enforce('alice', 'data1', 'read')); $this->assertFalse($e->enforce('alice', 'data1', 'write')); $this->assertFalse($e->enforce('alice', 'data2', 'read')); $this->assertFalse($e->enforce('alice', 'data2', 'write')); $this->assertFalse($e->enforce('bob', 'data1', 'read')); $this->assertFalse($e->enforce('bob', 'data1', 'write')); $this->assertFalse($e->enforce('bob', 'data2', 'read')); $this->assertTrue($e->enforce('bob', 'data2', 'write')); } public function testDeletePermission() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_without_resources_model.conf', $this->modelAndPolicyPath.'/basic_without_resources_policy.csv'); $e->deletePermission('read'); $this->assertFalse($e->enforce('alice', 'read')); $this->assertFalse($e->enforce('alice', 'write')); $this->assertFalse($e->enforce('bob', 'read')); $this->assertTrue($e->enforce('bob', 'write')); } public function testAddPermissionForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_without_resources_model.conf', $this->modelAndPolicyPath.'/basic_without_resources_policy.csv'); $e->deletePermission('read'); $e->addPermissionForUser('bob', 'read'); $this->assertTrue($e->enforce('bob', 'read')); $this->assertTrue($e->enforce('bob', 'write')); } public function testDeletePermissionForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_without_resources_model.conf', $this->modelAndPolicyPath.'/basic_without_resources_policy.csv'); $e->addPermissionForUser('bob', 'read'); $this->assertTrue($e->enforce('bob', 'read')); $e->deletePermissionForUser('bob', 'read'); $this->assertFalse($e->enforce('bob', 'read')); $this->assertTrue($e->enforce('bob', 'write')); } public function testDeletePermissionsForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_without_resources_model.conf', $this->modelAndPolicyPath.'/basic_without_resources_policy.csv'); $e->deletePermissionsForUser('bob'); $this->assertTrue($e->enforce('alice', 'read')); $this->assertFalse($e->enforce('bob', 'read')); $this->assertFalse($e->enforce('bob', 'write')); } public function testGetPermissionsForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_without_resources_model.conf', $this->modelAndPolicyPath.'/basic_without_resources_policy.csv'); $this->assertEquals($e->getPermissionsForUser('alice'), [['alice', 'read']]); } public function testHasPermissionForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/basic_without_resources_model.conf', $this->modelAndPolicyPath.'/basic_without_resources_policy.csv'); $this->assertTrue($e->hasPermissionForUser('alice', ...['read'])); $this->assertFalse($e->hasPermissionForUser('alice', ...['write'])); $this->assertFalse($e->hasPermissionForUser('bob', ...['read'])); $this->assertTrue($e->hasPermissionForUser('bob', ...['write'])); } public function testGetImplicitRolesForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_with_hierarchy_policy.csv'); $this->assertEquals($e->getImplicitRolesForUser('alice'), ['admin', 'data1_admin', 'data2_admin']); $this->assertEquals($e->getImplicitRolesForUser('bob'), []); $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_domains_model.conf', $this->modelAndPolicyPath.'/rbac_with_hierarchy_with_domains_policy.csv'); $this->assertEquals($e->getImplicitRolesForUser('alice', 'domain1'), ['role:global_admin', 'role:reader', 'role:writer']); } public function testGetImplicitPermissionsForUser() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_with_hierarchy_policy.csv'); $this->assertEquals($e->getImplicitPermissionsForUser('alice'), [ ['alice', 'data1', 'read'], ['data1_admin', 'data1', 'read'], ['data1_admin', 'data1', 'write'], ['data2_admin', 'data2', 'read'], ['data2_admin', 'data2', 'write'], ]); $this->assertEquals($e->getImplicitPermissionsForUser('bob'), [ ['bob', 'data2', 'write'], ]); $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_domains_model.conf', $this->modelAndPolicyPath.'/rbac_with_hierarchy_with_domains_policy.csv'); $this->assertEquals($e->getImplicitPermissionsForUser('alice', 'domain1'), [ ['alice', 'domain1', 'data2', 'read'], ['role:reader', 'domain1', 'data1', 'read'], ['role:writer', 'domain1', 'data1', 'write'], ]); } public function testGetImplicitUsersForPermission() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_model.conf', $this->modelAndPolicyPath.'/rbac_with_hierarchy_policy.csv'); $this->assertEquals($e->getImplicitUsersForPermission('data1', 'read'), ['alice']); $this->assertEquals($e->getImplicitUsersForPermission('data1', 'write'), ['alice']); $this->assertEquals($e->getImplicitUsersForPermission('data2', 'read'), ['alice']); $this->assertEquals($e->getImplicitUsersForPermission('data2', 'write'), ['alice', 'bob']); } } tests/Unit/RbacApiWithDomainsTest.php000064400000007752152475270340013672 0ustar00modelAndPolicyPath.'/rbac_with_domains_model.conf', $this->modelAndPolicyPath.'/rbac_with_domains_policy.csv'); $this->assertEquals($e->getUsersForRoleInDomain('admin', 'domain1'), ['alice']); $this->assertEquals($e->getUsersForRoleInDomain('non_exist', 'domain1'), []); $this->assertEquals($e->getUsersForRoleInDomain('admin', 'domain2'), ['bob']); $this->assertEquals($e->getUsersForRoleInDomain('non_exist', 'domain2'), []); $e->deleteRoleForUserInDomain('alice', 'admin', 'domain1'); $e->addRoleForUserInDomain('bob', 'admin', 'domain1'); $this->assertEquals($e->getUsersForRoleInDomain('admin', 'domain1'), ['bob']); $this->assertEquals($e->getUsersForRoleInDomain('non_exist', 'domain1'), []); $this->assertEquals($e->getUsersForRoleInDomain('admin', 'domain2'), ['bob']); $this->assertEquals($e->getUsersForRoleInDomain('non_exist', 'domain2'), []); } public function testGetRolesForUserInDomain() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_domains_model.conf', $this->modelAndPolicyPath.'/rbac_with_domains_policy.csv'); $this->assertEquals($e->getRolesForUserInDomain('alice', 'domain1'), ['admin']); $this->assertEquals($e->getRolesForUserInDomain('bob', 'domain1'), []); $this->assertEquals($e->getRolesForUserInDomain('admin', 'domain1'), []); $this->assertEquals($e->getRolesForUserInDomain('non_exist', 'domain1'), []); $this->assertEquals($e->getRolesForUserInDomain('alice', 'domain2'), []); $this->assertEquals($e->getRolesForUserInDomain('bob', 'domain2'), ['admin']); $this->assertEquals($e->getRolesForUserInDomain('admin', 'domain2'), []); $this->assertEquals($e->getRolesForUserInDomain('non_exist', 'domain2'), []); $e->deleteRoleForUserInDomain('alice', 'admin', 'domain1'); $e->addRoleForUserInDomain('bob', 'admin', 'domain1'); $this->assertEquals($e->getRolesForUserInDomain('alice', 'domain1'), []); $this->assertEquals($e->getRolesForUserInDomain('bob', 'domain1'), ['admin']); $this->assertEquals($e->getRolesForUserInDomain('admin', 'domain1'), []); $this->assertEquals($e->getRolesForUserInDomain('non_exist', 'domain1'), []); $this->assertEquals($e->getRolesForUserInDomain('alice', 'domain2'), []); $this->assertEquals($e->getRolesForUserInDomain('bob', 'domain2'), ['admin']); $this->assertEquals($e->getRolesForUserInDomain('admin', 'domain2'), []); $this->assertEquals($e->getRolesForUserInDomain('non_exist', 'domain2'), []); } public function testGetPermissionsForUserInDomain() { $e = new Enforcer($this->modelAndPolicyPath.'/rbac_with_domains_model.conf', $this->modelAndPolicyPath.'/rbac_with_domains_policy.csv'); $this->assertEquals($e->getPermissionsForUserInDomain('alice', 'domain1'), []); $this->assertEquals($e->getPermissionsForUserInDomain('bob', 'domain1'), []); $this->assertEquals($e->getPermissionsForUserInDomain('admin', 'domain1'), [['admin', 'domain1', 'data1', 'read'], ['admin', 'domain1', 'data1', 'write']]); $this->assertEquals($e->getPermissionsForUserInDomain('non_exist', 'domain1'), []); $this->assertEquals($e->getPermissionsForUserInDomain('alice', 'domain2'), []); $this->assertEquals($e->getPermissionsForUserInDomain('bob', 'domain2'), []); $this->assertEquals($e->getPermissionsForUserInDomain('admin', 'domain2'), [['admin', 'domain2', 'data2', 'read'], ['admin', 'domain2', 'data2', 'write']]); $this->assertEquals($e->getPermissionsForUserInDomain('non_exist', 'domain2'), []); } } tests/Unit/Util/BuiltinOperationsTest.php000064400000010750152475270340014601 0ustar00assertTrue($this->keyMatchFunc('/foo', '/foo')); $this->assertTrue($this->keyMatchFunc('/foo', '/foo*')); $this->assertFalse($this->keyMatchFunc('/foo', '/foo/*')); $this->assertFalse($this->keyMatchFunc('/foo/bar', '/foo')); $this->assertTrue($this->keyMatchFunc('/foo/bar', '/foo*')); $this->assertTrue($this->keyMatchFunc('/foo/bar', '/foo/*')); $this->assertFalse($this->keyMatchFunc('/foobar', '/foo')); $this->assertTrue($this->keyMatchFunc('/foobar', '/foo*')); $this->assertFalse($this->keyMatchFunc('/foobar', '/foo/*')); } public function testKeyMatch2Func() { $this->assertTrue($this->keyMatch2Func('/foo', '/foo')); $this->assertTrue($this->keyMatch2Func('/foo', '/foo*')); $this->assertFalse($this->keyMatch2Func('/foo', '/foo/*')); $this->assertFalse($this->keyMatch2Func('/foo/bar', '/foo')); $this->assertFalse($this->keyMatch2Func('/foo/bar', '/foo*')); $this->assertTrue($this->keyMatch2Func('/foo/bar', '/foo/*')); $this->assertFalse($this->keyMatch2Func('/foobar', '/foo')); $this->assertFalse($this->keyMatch2Func('/foobar', '/foo*')); $this->assertFalse($this->keyMatch2Func('/foobar', '/foo/*')); $this->assertFalse($this->keyMatch2Func('/', '/:resource')); $this->assertTrue($this->keyMatch2Func('/resource1', '/:resource')); $this->assertFalse($this->keyMatch2Func('/myid', '/:id/using/:resId')); $this->assertTrue($this->keyMatch2Func('/myid/using/myresid', '/:id/using/:resId')); $this->assertFalse($this->keyMatch2Func('/proxy/myid', '/proxy/:id/*')); $this->assertTrue($this->keyMatch2Func('/proxy/myid/', '/proxy/:id/*')); $this->assertTrue($this->keyMatch2Func('/proxy/myid/res', '/proxy/:id/*')); $this->assertTrue($this->keyMatch2Func('/proxy/myid/res/res2', '/proxy/:id/*')); $this->assertTrue($this->keyMatch2Func('/proxy/myid/res/res2/res3', '/proxy/:id/*')); $this->assertFalse($this->keyMatch2Func('/proxy/', '/proxy/:id/*')); $this->assertTrue($this->keyMatch2Func('/alice', '/:id')); $this->assertTrue($this->keyMatch2Func('/alice/all', '/:id/all')); $this->assertFalse($this->keyMatch2Func('/alice', '/:id/all')); $this->assertFalse($this->keyMatch2Func('/alice/all', '/:id')); } public function testKeyMatch3Func() { $this->assertTrue($this->keyMatch3Func('/foo', '/foo')); $this->assertTrue($this->keyMatch3Func('/foo', '/foo*')); $this->assertFalse($this->keyMatch3Func('/foo', '/foo/*')); $this->assertFalse($this->keyMatch3Func('/foo/bar', '/foo')); $this->assertFalse($this->keyMatch3Func('/foo/bar', '/foo*')); $this->assertTrue($this->keyMatch3Func('/foo/bar', '/foo/*')); $this->assertFalse($this->keyMatch3Func('/foobar', '/foo')); $this->assertFalse($this->keyMatch3Func('/foobar', '/foo*')); $this->assertFalse($this->keyMatch3Func('/foobar', '/foo/*')); $this->assertFalse($this->keyMatch3Func('/', '/{resource}')); $this->assertTrue($this->keyMatch3Func('/resource1', '/{resource}')); $this->assertFalse($this->keyMatch3Func('/myid', '/{id}/using/{resId}')); $this->assertTrue($this->keyMatch3Func('/myid/using/myresid', '/{id}/using/{resId}')); $this->assertFalse($this->keyMatch3Func('/proxy/myid', '/proxy/{id}/*')); $this->assertTrue($this->keyMatch3Func('/proxy/myid/', '/proxy/{id}/*')); $this->assertTrue($this->keyMatch3Func('/proxy/myid/res', '/proxy/{id}/*')); $this->assertTrue($this->keyMatch3Func('/proxy/myid/res/res2', '/proxy/{id}/*')); $this->assertTrue($this->keyMatch3Func('/proxy/myid/res/res2/res3', '/proxy/{id}/*')); $this->assertFalse($this->keyMatch3Func('/proxy/', '/proxy/{id}/*')); } } tests/Unit/Util/UtilTest.php000064400000003555152475270340012051 0ustar00assertEquals(Util::escapeAssertion('p.attr.value == p.attr'), 'p_attr.value == p_attr'); $this->assertEquals(Util::escapeAssertion('r.attr.value == p.attr'), 'r_attr.value == p_attr'); $this->assertEquals(Util::escapeAssertion('r.attp.value || p.attr'), 'r_attp.value || p_attr'); $this->assertEquals(Util::escapeAssertion('r.attp.value &&p.attr'), 'r_attp.value &&p_attr'); $this->assertEquals(Util::escapeAssertion('r.attp.value >p.attr'), 'r_attp.value >p_attr'); $this->assertEquals(Util::escapeAssertion('r.attp.value assertEquals(Util::escapeAssertion('r.attp.value +p.attr'), 'r_attp.value +p_attr'); $this->assertEquals(Util::escapeAssertion('r.attp.value -p.attr'), 'r_attp.value -p_attr'); $this->assertEquals(Util::escapeAssertion('r.attp.value *p.attr'), 'r_attp.value *p_attr'); $this->assertEquals(Util::escapeAssertion('r.attp.value /p.attr'), 'r_attp.value /p_attr'); $this->assertEquals(Util::escapeAssertion('!r.attp.value /p.attr'), '!r_attp.value /p_attr'); $this->assertEquals(Util::escapeAssertion('g(r.sub, p.sub) == p.attr'), 'g(r_sub, p_sub) == p_attr'); $this->assertEquals(Util::escapeAssertion('g(r.sub,p.sub) == p.attr'), 'g(r_sub,p_sub) == p_attr'); $this->assertEquals(Util::escapeAssertion('(r.attp.value || p.attr)p.u'), '(r_attp.value || p_attr)p_u'); } public function testArrayRemoveDuplicates() { $a = ['green', 'red', 'green', 'blue', 'red']; Util::arrayRemoveDuplicates($a); $this->assertEquals($a, ['green', 'red', 'blue']); } }