.github/FUNDING.yml 0000644 00000001235 15247527047 0007737 0 ustar 00 # These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: casbin
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
.github/workflows/build.yml 0000644 00000003663 15247527047 0012010 0 ustar 00 name: build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
php: [ 7.1, 7.2, 7.3, 7.4, 8.0 ]
stability: [ prefer-lowest, prefer-stable ]
name: PHP ${{ matrix.php }} - ${{ matrix.stability }}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2
coverage: xdebug
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
run: composer install --prefer-dist --no-progress --no-suggest
- name: Run phpstan analyse
run: ./vendor/bin/phpstan analyse
- name: Run test suite
run: ./vendor/bin/phpunit -v
- name: Run Coveralls
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_PARALLEL: true
COVERALLS_FLAG_NAME: ${{ runner.os }} - ${{ matrix.php }} - ${{ matrix.stability }}
run: ./vendor/bin/php-coveralls --coverage_clover=build/logs/clover.xml -v
upload-coverage:
runs-on: ubuntu-latest
needs: [ test ]
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
semantic-release:
runs-on: ubuntu-latest
needs: [ test, upload-coverage ]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '14.17'
- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: npx semantic-release .gitignore 0000644 00000000113 15247527047 0006544 0 ustar 00 /vendor/
composer.lock
.idea/
*.iml
# coverage report
/build
.phpunit.* .releaserc.yml 0000644 00000000202 15247527047 0007321 0 ustar 00 plugins:
- "@semantic-release/commit-analyzer"
- "@semantic-release/release-notes-generator"
- "@semantic-release/github" LICENSE 0000644 00000026135 15247527047 0005575 0 ustar 00 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.md 0000644 00000032741 15247527047 0006047 0 ustar 00 PHP-Casbin
====
[](https://scrutinizer-ci.com/g/php-casbin/php-casbin/?branch=master)
[](https://github.com/php-casbin/php-casbin/actions)
[](https://coveralls.io/github/php-casbin/php-casbin)
[](https://packagist.org/packages/casbin/casbin)
[](https://packagist.org/packages/casbin/casbin)
[](https://packagist.org/packages/casbin/casbin)
[](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:
[](https://github.com/casbin/casbin) | [](https://github.com/casbin/jcasbin) | [](https://github.com/casbin/node-casbin) | [](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
[](https://github.com/casbin/pycasbin) | [](https://github.com/casbin/Casbin.NET) | [](https://github.com/casbin4d/Casbin4D) | [](https://github.com/casbin/casbin-rs)
----|----|----|----
[PyCasbin](https://github.com/casbin/pycasbin) | [Casbin.NET](https://github.com/casbin/Casbin.NET) | [Casbin4D](https://github.com/casbin4d/Casbin4D) | [Casbin-RS](https://github.com/casbin/casbin-rs)
production-ready | production-ready | experimental | production-ready
## 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://casbin.org/docs/en/management-api): the primitive API that provides full support for php-casbin policy management.
- [RBAC API](https://casbin.org/docs/en/rbac-api): 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.


## 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.md 0000644 00000033504 15247527047 0006425 0 ustar 00 PHP-Casbin
====
[](https://scrutinizer-ci.com/g/php-casbin/php-casbin/?branch=master)
[](https://github.com/php-casbin/php-casbin/actions)
[](https://coveralls.io/github/php-casbin/php-casbin)
[](https://packagist.org/packages/casbin/casbin)
[](https://packagist.org/packages/casbin/casbin)
[](https://packagist.org/packages/casbin/casbin)
[](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支持的编程语言:
[](https://github.com/casbin/casbin) | [](https://github.com/casbin/jcasbin) | [](https://github.com/casbin/node-casbin) | [](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)
可用于生产环境 | 可用于生产环境 | 可用于生产环境 | 可用于生产环境
[](https://github.com/casbin/pycasbin) | [](https://github.com/casbin4d/Casbin4D) | [](https://github.com/Devolutions/casbin-net) | [](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`:


## 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.json 0000644 00000001700 15247527047 0007301 0 ustar 00 {
"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|^5.0",
"s1lentium/iptools": "^1.1"
},
"autoload": {
"psr-4": {
"Casbin\\": "src/"
}
},
"require-dev": {
"phpunit/phpunit": "~7.0|~8.0|~9.0",
"php-coveralls/php-coveralls": "^2.1",
"phpstan/phpstan": "^0.12",
"mockery/mockery": "^1.2"
},
"autoload-dev": {
"psr-4": {
"Casbin\\Tests\\": "tests/"
}
}
}
examples/abac_model.conf 0000644 00000000244 15247527047 0011314 0 ustar 00 [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.Owner examples/abac_rule_model.conf 0000644 00000000312 15247527047 0012337 0 ustar 00 [request_definition]
r = sub, obj, act
[policy_definition]
p = sub_rule, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act
examples/abac_rule_policy.csv 0000644 00000000101 15247527047 0012400 0 ustar 00 p, r.sub.Age > 18, /data1, read
p, r.sub.Age < 60, /data2, write
examples/basic_inverse_policy.csv 0000644 00000000052 15247527047 0013304 0 ustar 00 p, alice, data1, write
p, bob, data2, read examples/basic_model.conf 0000644 00000000302 15247527047 0011502 0 ustar 00 [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 examples/basic_policy.csv 0000644 00000000052 15247527047 0011551 0 ustar 00 p, alice, data1, read
p, bob, data2, write examples/basic_with_root_model.conf 0000644 00000000325 15247527047 0013605 0 ustar 00 [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.conf 0000644 00000000246 15247527047 0015366 0 ustar 00 [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.act examples/basic_without_resources_policy.csv 0000644 00000000034 15247527047 0015426 0 ustar 00 p, alice, read
p, bob, write examples/basic_without_users_model.conf 0000644 00000000246 15247527047 0014515 0 ustar 00 [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.act examples/basic_without_users_policy.csv 0000644 00000000036 15247527047 0014557 0 ustar 00 p, data1, read
p, data2, write examples/error/error_model.conf 0000644 00000000301 15247527047 0012702 0 ustar 00 [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 examples/error/error_policy.csv 0000644 00000000047 15247527047 0012756 0 ustar 00 p, alice, data1, read
bob, data2, write examples/ipmatch_model.conf 0000644 00000000311 15247527047 0012046 0 ustar 00 [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.act examples/ipmatch_policy.csv 0000644 00000000073 15247527047 0012120 0 ustar 00 p, 192.168.2.0/24, data1, read
p, 10.0.0.0/16, data2, write examples/keyget2_model.conf 0000644 00000000362 15247527047 0012001 0 ustar 00 [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 && keyGet2(r.obj, p.obj, 'resource') in ('age', 'name') && regexMatch(r.act, p.act) examples/keyget_model.conf 0000644 00000000371 15247527047 0011717 0 ustar 00 [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 || keyGet(r.obj, p.obj) in ('age','name')) && regexMatch(r.act, p.act) examples/keymatch2_model.conf 0000644 00000000325 15247527047 0012315 0 ustar 00 [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.csv 0000644 00000000121 15247527047 0012354 0 ustar 00 p, alice, /alice_data/:resource, GET
p, alice, /alice_data2/:id/using/:resId, GET examples/keymatch_custom_model.conf 0000644 00000000332 15247527047 0013623 0 ustar 00 [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.conf 0000644 00000000324 15247527047 0012232 0 ustar 00 [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.csv 0000644 00000000245 15247527047 0012301 0 ustar 00 p, 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/multiple_policy_definitions_model.conf 0000644 00000000532 15247527047 0016233 0 ustar 00 [request_definition]
r = sub, obj, act
r2 = sub, obj, act
[policy_definition]
p = sub, obj, act
p2= sub_rule, obj, act, eft
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
#RABC
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
#ABAC
m2 = eval(p2.sub_rule) && r2.obj == p2.obj && r2.act == p2.act
examples/multiple_policy_definitions_policy.csv 0000644 00000000252 15247527047 0016277 0 ustar 00 p, data2_admin, data2, read
p2, r2.sub.Age > 18 && r2.sub.Age < 60, /data1, read, allow
p2, r2.sub.Age > 60 && r2.sub.Age < 100, /data1, read, deny
g, alice, data2_admin examples/priority_indeterminate_policy.csv 0000644 00000000045 15247527047 0015263 0 ustar 00 p, alice, data1, read, intdeterminate examples/priority_model.conf 0000644 00000000337 15247527047 0012312 0 ustar 00 [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.act examples/priority_model_explicit.conf 0000644 00000000351 15247527047 0014207 0 ustar 00 [request_definition]
r = sub, obj, act
[policy_definition]
p = priority, 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.act examples/priority_policy.csv 0000644 00000000437 15247527047 0012360 0 ustar 00 p, 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_group examples/priority_policy_explicit.csv 0000644 00000000507 15247527047 0014257 0 ustar 00 p, 10, data1_deny_group, data1, read, deny
p, 10, data1_deny_group, data1, write, deny
p, 10, data2_allow_group, data2, read, allow
p, 10, data2_allow_group, data2, write, allow
p, 1, alice, data1, write, allow
p, 1, alice, data1, read, allow
p, 1, bob, data2, read, deny
g, bob, data2_allow_group
g, alice, data1_deny_group examples/rbac_model.conf 0000644 00000000337 15247527047 0011340 0 ustar 00 [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.act examples/rbac_model_matcher_using_in_op_bracket.conf 0000644 00000000377 15247527047 0017133 0 ustar 00 [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.act || r.obj in ['data2', 'data3'] examples/rbac_policy.csv 0000644 00000000172 15247527047 0011402 0 ustar 00 p, alice, data1, read
p, bob, data2, write
p, data2_admin, data2, read
p, data2_admin, data2, write
g, alice, data2_admin examples/rbac_with_all_pattern_model.conf 0000644 00000000406 15247527047 0014755 0 ustar 00 [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 = r.sub == p.sub && g(r.obj, p.obj, r.dom) && r.dom == p.dom && r.act == p.act
examples/rbac_with_all_pattern_policy.csv 0000644 00000000146 15247527047 0015023 0 ustar 00 p, alice, domain1, book_group, read
p, alice, domain2, book_group, write
g, /book/:id, book_group, *
examples/rbac_with_deny_model.conf 0000644 00000000404 15247527047 0013405 0 ustar 00 [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.act examples/rbac_with_deny_policy.csv 0000644 00000000263 15247527047 0013455 0 ustar 00 p, 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_admin examples/rbac_with_domain_pattern_model.conf 0000644 00000000406 15247527047 0015454 0 ustar 00 [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.act
examples/rbac_with_domain_pattern_policy.csv 0000644 00000000251 15247527047 0015517 0 ustar 00 p, admin, domain1, data1, read
p, admin, domain1, data1, write
p, admin, domain2, data2, read
p, admin, domain2, data2, write
g, alice, admin, *
g, bob, admin, domain2
examples/rbac_with_domains_model.conf 0000644 00000000405 15247527047 0014101 0 ustar 00 [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.act examples/rbac_with_domains_policy.csv 0000644 00000000256 15247527047 0014152 0 ustar 00 p, 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, domain2 examples/rbac_with_hierarchy_policy.csv 0000644 00000000331 15247527047 0014470 0 ustar 00 p, 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_admin examples/rbac_with_hierarchy_with_domains_policy.csv 0000644 00000000417 15247527047 0017242 0 ustar 00 p, 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, domain1 examples/rbac_with_not_deny_model.conf 0000644 00000000345 15247527047 0014271 0 ustar 00 [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.act
examples/rbac_with_pattern_model.conf 0000644 00000000366 15247527047 0014132 0 ustar 00 [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) && regexMatch(r.act, p.act)
examples/rbac_with_pattern_policy.csv 0000644 00000000502 15247527047 0014167 0 ustar 00 p, alice, /pen/1, GET
p, alice, /pen2/1, GET
p, book_admin, book_group, GET
p, pen_admin, pen_group, GET
g, alice, book_admin
g, bob, pen_admin
g, cathy, /book/1/2/3/4/5
g, cathy, pen_admin
g2, /book/*, book_group
g2, /book/:id, book_group
g2, /pen/:id, pen_group
g2, /book2/{id}, book_group
g2, /pen2/{id}, pen_group examples/rbac_with_resource_roles_model.conf 0000644 00000000353 15247527047 0015504 0 ustar 00 [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.act examples/rbac_with_resource_roles_policy.csv 0000644 00000000231 15247527047 0015544 0 ustar 00 p, 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_group examples/subject_priority_model.conf 0000644 00000000346 15247527047 0014031 0 ustar 00 [request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act, eft
[role_definition]
g = _, _
[policy_effect]
e = subjectPriority(p.eft) || deny
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act examples/subject_priority_model_with_domain.conf 0000644 00000000477 15247527047 0016420 0 ustar 00 [request_definition]
r = sub, obj, dom, act
[policy_definition]
# sub can't change position and must be first
p = sub, obj, dom, act, eft
[role_definition]
g = _, _, _
[policy_effect]
e = subjectPriority(p.eft) || deny
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act examples/subject_priority_policy.csv 0000644 00000000414 15247527047 0014072 0 ustar 00 p, root, data1, read, deny
p, admin, data1, read, deny
p, editor, data1, read, deny
p, subscriber, data1, read, deny
p, jane, data1, read, allow
p, alice, data1, read, allow
g, admin, root
g, editor, admin
g, subscriber, admin
g, jane, editor
g, alice, subscriber examples/subject_priority_policy_with_domain.csv 0000644 00000000310 15247527047 0016447 0 ustar 00 p, admin, data1, domain1, write, deny
p, alice, data1, domain1, write, allow
p, admin, data2, domain2, write, deny
p, bob, data2, domain2, write, allow
g, alice, admin, domain1
g, bob, admin, domain2 phpstan.neon 0000644 00000000233 15247527047 0007115 0 ustar 00 parameters:
reportUnmatchedIgnoredErrors: false
checkMissingIterableValueType: false
tipsOfTheDay: false
level: 7
paths:
- src
phpunit.xml 0000644 00000001505 15247527047 0006773 0 ustar 00
./tests/
./src
src/CachedEnforcer.php 0000644 00000003660 15247527047 0010721 0 ustar 00 enableCache = 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.php 0000644 00000014020 15247527047 0010470 0 ustar 00 >
*/
public $data = [];
/**
* Create an empty configuration representation from file.
*
* @param string $confName
*
* @return ConfigContract
* @throws CasbinException
*/
public static function newConfig(string $confName): ConfigContract
{
$c = new static();
$c->parse($confName);
return $c;
}
/**
* Create an empty configuration representation from text.
*
* @param string $text
*
* @return ConfigContract
* @throws CasbinException
*/
public static function newConfigFromText(string $text): ConfigContract
{
$c = new Config();
$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);
return $buf === false ? false : $this->parseBuffer($buf);
}
/**
* @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 == null ? "" : $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.php 0000644 00000002115 15247527047 0012170 0 ustar 00 '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->rmMap = [];
$this->eft = new DefaultEffector();
$this->watcher = null;
$this->enabled = true;
$this->autoSave = true;
$this->autoBuildRoleLinks = true;
$this->autoNotifyWatcher = true;
$this->initRmMap();
}
/**
* 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|null
*/
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();
});
}
/**
* Gets the current role manager.
*
* @return RoleManager
*/
public function getRoleManager(): RoleManager
{
return $this->rmMap['g'];
}
/**
* Gets the current role manager.
*
* @param RoleManager $rm
*/
public function setRoleManager(RoleManager $rm): void
{
$this->rmMap['g'] = $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
{
$flag = false;
$needToRebuild = false;
$newModel = clone $this->model;
$newModel->clearPolicy();
try {
$this->adapter->loadPolicy($newModel);
$newModel->printPolicy();
$newModel->sortPoliciesBySubjectHierarchy();
$newModel->sortPoliciesByPriority();
if ($this->autoBuildRoleLinks) {
$needToRebuild = true;
foreach ($this->rmMap as $rm) {
$rm->clear();
}
$newModel->buildRoleLinks($this->rmMap);
}
$this->model = $newModel;
} catch (InvalidFilePathException $e) {
// Ignore throw $e;
} catch (\Throwable $e) {
$flag = true;
throw $e;
} finally {
if ($flag) {
if ($this->autoBuildRoleLinks && $needToRebuild) {
$this->buildRoleLinks();
}
}
}
}
/**
* Reloads a filtered policy from file/database.
*
* @param mixed $filter
*
* @throws CasbinException
*/
public function _loadFilteredPolicy($filter): void
{
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->sortPoliciesByPriority();
$this->initRmMap();
$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();
$this->_loadFilteredPolicy($filter);
}
/**
* LoadIncrementalFilteredPolicy append a filtered policy from file/database.
*
* @param mixed $filter
* @return void
*/
public function loadIncrementalFilteredPolicy($filter): void
{
$this->_loadFilteredPolicy($filter);
}
/**
* 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.
*
* @throws CasbinException
*/
public function savePolicy(): void
{
if ($this->isFiltered()) {
throw new CasbinException('cannot save a filtered policy');
}
$this->adapter->savePolicy($this->model);
if ($this->watcher !== null && $this->autoNotifyWatcher) {
if ($this->watcher instanceof WatcherEx) {
$this->watcher->updateForSavePolicy($this->model);
} else {
$this->watcher->update();
}
}
}
/**
* initRmMap initializes rmMap.
*
* @return void
*/
public function initRmMap(): void
{
if (isset($this->model['g'])) {
foreach ($this->model['g'] as $ptype => $value) {
if (isset($this->rmMap[$ptype])) {
$rm = $this->rmMap[$ptype];
$rm->clear();
} else {
$this->rmMap[$ptype] = new DefaultRoleManager(10);
}
}
}
}
/**
* 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 notify the Watcher when it is added or removed.
*
* @param bool $enabled
*/
public function enableAutoNotifyWatcher(bool $enabled = true): void
{
$this->autoNotifyWatcher = $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
{
foreach ($this->rmMap as $rm) {
$rm->clear();
}
$this->model->buildRoleLinks($this->rmMap);
}
/**
* Use a custom matcher to decides whether a "subject" can access a "object" with the operation "action",
* input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
*
* @param string $matcher
* @param array $explains
* @param mixed ...$rvals
*
* @return bool
*
* @throws CasbinException
*/
protected function enforcing(string $matcher, &$explains = [], ...$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');
}
$rType = "r";
$pType = "p";
$eType = "e";
$mType = "m";
switch (true) {
case $rvals[0] instanceof EnforceContext:
$enforceContext = $rvals[0];
$rType = $enforceContext->rType;
$pType = $enforceContext->pType;
$eType = $enforceContext->eType;
$mType = $enforceContext->mType;
array_shift($rvals);
break;
default:
break;
}
$expString = '';
if ('' === $matcher) {
$expString = $this->model['m'][$mType]->value;
} else {
$expString = Util::removeComments(Util::escapeAssertion($matcher));
}
$rTokens = array_values($this->model['r'][$rType]->tokens);
$pTokens = array_values($this->model['p'][$pType]->tokens);
$rParameters = array_combine($rTokens, $rvals);
if (false == $rParameters) {
throw new CasbinException('invalid request size');
}
$expressionLanguage = $this->getExpressionLanguage($functions);
$expression = "";
$hasEval = Util::hasEval($expString);
if (!$hasEval) {
$expression = $expressionLanguage->parse($expString, array_merge($rTokens, $pTokens));
}
if (count($this->model['r']['r']->tokens) != count($rvals)) {
throw new CasbinException(sprintf('invalid request size: expected %d, got %d', count($this->model['r']['r']->tokens), count($rvals)));
}
$policyEffects = [];
$matcherResults = [];
$effect = 0;
$explainIndex = 0;
$policyLen = \count($this->model['p'][$pType]->policy);
if (0 != $policyLen) {
foreach ($this->model['p'][$pType]->policy as $policyIndex => $pvals) {
$parameters = array_combine($pTokens, $pvals);
if (false == $parameters) {
throw new CasbinException('invalid policy size');
}
if ($hasEval) {
$ruleNames = Util::getEvalValue($expString);
$replacements = [];
$pTokens_flipped = array_flip($pTokens);
foreach ($ruleNames as $ruleName) {
if (isset($pTokens_flipped[$ruleName])) {
$rule = Util::escapeAssertion($pvals[$pTokens_flipped[$ruleName]]);
$replacements[$ruleName] = $rule;
} else {
throw new CasbinException('please make sure rule exists in policy when using eval() in matcher');
}
}
$expWithRule = Util::replaceEvalWithMap($expString, $replacements);
$expression = $expressionLanguage->parse($expWithRule, array_merge($rTokens, $pTokens));
}
$parameters = array_merge($rParameters, $parameters);
$result = $expressionLanguage->evaluate($expression, $parameters);
// set to no-match at first
$matcherResults[$policyIndex] = 0;
if (\is_bool($result)) {
if ($result) {
$matcherResults[$policyIndex] = 1;
}
} elseif (\is_float($result)) {
if ($result != 0) {
$matcherResults[$policyIndex] = 1;
}
} else {
throw new CasbinException('matcher result should be bool, int or float');
}
if (isset($parameters[$pType . '_eft'])) {
$eft = $parameters[$pType . '_eft'];
if ('allow' == $eft) {
$policyEffects[$policyIndex] = Effector::ALLOW;
} elseif ('deny' == $eft) {
$policyEffects[$policyIndex] = Effector::DENY;
} else {
$policyEffects[$policyIndex] = Effector::INDETERMINATE;
}
} else {
$policyEffects[$policyIndex] = Effector::ALLOW;
}
list($effect, $explainIndex) = $this->eft->mergeEffects($this->model['e'][$eType]->value, array_slice($policyEffects, 0, $policyIndex + 1), array_slice($matcherResults, 0, $policyIndex + 1), $policyIndex, $policyLen);
if ($effect != Effector::INDETERMINATE) {
break;
}
}
} else {
if ($hasEval) {
throw new EvalFunctionException("please make sure rule exists in policy when using eval() in matcher");
}
$matcherResults[0] = 1;
$parameters = $rParameters;
foreach ($this->model['p'][$pType]->tokens as $token) {
$parameters[$token] = '';
}
$result = $expressionLanguage->evaluate($expression, $parameters);
if ($result) {
$policyEffects[0] = Effector::ALLOW;
} else {
$policyEffects[0] = Effector::INDETERMINATE;
}
list($effect, $explainIndex) = $this->eft->mergeEffects($this->model['e'][$eType]->value, $policyEffects, $matcherResults, 0, 1);
}
if ($explains !== null) {
if (($explainIndex != -1) && (count($this->model['p'][$pType]->policy) > $explainIndex)) {
$explains = $this->model['p'][$pType]->policy[$explainIndex];
}
}
$result = $effect == Effector::ALLOW;
if (Log::getLogger()->isEnabled()) {
$reqStr = 'Request: ';
$reqStr .= implode(', ', array_values($rvals));
$reqStr .= sprintf(" ---> %s\n", var_export($result, true));
$reqStr = 'Hit Policy: ';
if (count($explains) == count($explains, COUNT_RECURSIVE)) {
// if $explains is not multidimensional
$reqStr .= sprintf("%s \n", '[' . implode(', ', $explains) . ']');
} else {
// if $explains is multidimensional
foreach ($explains as $i => $pval) {
$reqStr .= sprintf("%s \n", '[' . implode(', ', $pval) . ']');
}
}
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
);
}
/**
* Decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
*
* @param mixed ...$rvals
*
* @return bool
*
* @throws CasbinException
*/
public function enforce(...$rvals): bool
{
$explains = [];
return $this->enforcing('', $explains, ...$rvals);
}
/**
* Use a custom matcher to decides whether a "subject" can access a "object" with the operation "action",
* input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
*
* @param string $matcher
* @param mixed ...$rvals
*
* @return bool
*
* @throws CasbinException
*/
public function enforceWithMatcher(string $matcher, ...$rvals): bool
{
$explains = [];
return $this->enforcing($matcher, $explains, ...$rvals);
}
/**
* EnforceEx explain enforcement by informing matched rules
*
* @param mixed ...$rvals
* @return array
*/
public function enforceEx(...$rvals)
{
$explain = [];
$result = $this->enforcing("", $explain, ...$rvals);
return [$result, $explain];
}
/**
* BuildIncrementalRoleLinks provides incremental build the role inheritance relations.
*
* @param integer $op policy operations.
* @param string $ptype policy type.
* @param string[][] $rules the rules.
* @return void
*/
public function buildIncrementalRoleLinks(int $op, string $ptype, array $rules): void
{
$this->model->buildIncrementalRoleLinks($this->rmMap, $op, "g", $ptype, $rules);
}
/**
* BatchEnforce enforce in batches
*
* @param string[][] $requests
* @return bool[]
*/
public function batchEnforce(array $requests): array
{
return array_map(function (array $request) {
return $this->enforce(...$request);
}, $requests);
}
/**
* BatchEnforceWithMatcher enforce with matcher in batches
*
* @param string $matcher
* @param string[][] $requests
* @return bool[]
*/
public function batchEnforceWithMatcher(string $matcher, array $requests): array
{
return array_map(function (array $request) use ($matcher) {
return $this->enforceWithMatcher($matcher, ...$request);
}, $requests);
}
/**
* AddNamedMatchingFunc add MatchingFunc by ptype RoleManager
*
* @param string $ptype
* @param string $name
* @param \Closure $fn
* @return boolean
*/
public function addNamedMatchingFunc(string $ptype, string $name, \Closure $fn): bool
{
if (isset($this->rmMap[$ptype])) {
$rm = $this->rmMap[$ptype];
$rm->addMatchingFunc($name, $fn);
return true;
}
return false;
}
/**
* AddNamedDomainMatchingFunc add MatchingFunc by ptype to RoleManager
*
* @param string $ptype
* @param string $name
* @param \Closure $fn
* @return boolean
*/
public function addNamedDomainMatchingFunc(string $ptype, string $name, \Closure $fn): bool
{
if (isset($this->rmMap[$ptype])) {
$rm = $this->rmMap[$ptype];
$rm->addDomainMatchingFunc($name, $fn);
return true;
}
return false;
}
}
src/Effector/DefaultEffector.php 0000644 00000007115 15247527047 0012664 0 ustar 00 $eft) {
if ($matches[$i] == 0) {
continue;
}
if ($eft == Effector::ALLOW) {
$result = Effector::ALLOW;
$explainIndex = $i;
break;
}
}
} elseif ($expr == '!some(where (p_eft == deny))') {
// if no deny rules are matched, then allow
$result = Effector::ALLOW;
foreach ($effects as $i => $eft) {
if ($matches[$i] == 0) {
continue;
}
if ($eft == Effector::DENY) {
$result = Effector::DENY;
$explainIndex = $i;
break;
}
}
} elseif ($expr == 'some(where (p_eft == allow)) && !some(where (p_eft == deny))') {
$result = Effector::INDETERMINATE;
foreach ($effects as $i => $eft) {
if ($matches[$i] == 0) {
continue;
}
if ($eft == Effector::ALLOW) {
// set hit rule to first matched allow rule, maybe overridden by the deny part
if ($result == Effector::INDETERMINATE) {
$explainIndex = $i;
}
$result = Effector::ALLOW;
} elseif ($eft == Effector::DENY) {
$result = Effector::DENY;
// set hit rule to the (first) matched deny rule
$explainIndex = $i;
break;
}
}
} elseif ($expr == 'priority(p_eft) || deny' || $expr == 'subjectPriority(p_eft) || deny') {
$result = Effector::INDETERMINATE;
foreach ($effects as $i => $eft) {
if ($matches[$i] == 0) {
continue;
}
if ($eft != Effector::INDETERMINATE) {
if ($eft == Effector::ALLOW) {
$result = Effector::ALLOW;
} else {
$result = Effector::DENY;
}
$explainIndex = $i;
break;
}
}
} else {
throw new CasbinException('unsupported effect');
}
return [$result, $explainIndex];
}
}
src/Effector/Effector.php 0000644 00000001050 15247527047 0011347 0 ustar 00 rType = "r" . $suffix;
$this->pType = "p" . $suffix;
$this->eType = "e" . $suffix;
$this->mType = "m" . $suffix;
}
}
src/Enforcer.php 0000644 00000041740 15247527047 0007632 0 ustar 00 model['g']['g']->rm->getRoles($name, ...$domain);
}
/**
* Gets the users that has a role.
*
* @param string $name
* @param string ...$domain
*
* @return string[]
*/
public function getUsersForRole(string $name, string ...$domain): array
{
return $this->model['g']['g']->rm->getUsers($name, ...$domain);
}
/**
* Determines whether a user has a role.
*
* @param string $name
* @param string $role
* @param string ...$domain
*
* @return bool
*/
public function hasRoleForUser(string $name, string $role, string ...$domain): bool
{
$roles = $this->getRolesForUser($name, ...$domain);
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
* @param string ...$domain
* @return bool
*/
public function addRoleForUser(string $user, string $role, string ...$domain): bool
{
return $this->addGroupingPolicy(...array_merge([$user, $role], $domain));
}
/**
* @param string $user
* @param string[] $roles
* @param string ...$domain
*
* @return bool
*/
public function addRolesForUser(string $user, array $roles, string ...$domain): bool
{
return $this->addGroupingPolicies(
array_map(function ($role) use ($user, $domain) {
return array_merge([$user, $role], $domain);
}, $roles)
);
}
/**
* 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
* @param string ...$domain
*
* @return bool
*/
public function deleteRoleForUser(string $user, string $role, string ...$domain): bool
{
return $this->removeGroupingPolicy(...array_merge([$user, $role], $domain));
}
/**
* Deletes all roles for a user.
* Returns false if the user does not have any roles (aka not affected).
*
* @param string $user
* @param string ...$domain
*
* @return bool
* @throws CasbinException
*/
public function deleteRolesForUser(string $user, string ...$domain): bool
{
if (count($domain) > 1) {
throw new CasbinException('error: domain should be 1 parameter');
}
return $this->removeFilteredGroupingPolicy(0, ...array_merge([$user, ''], $domain));
}
/**
* Deletes a user.
* Returns false if the user does not exist (aka not affected).
*
* @param string $user
*
* @return bool
*/
public function deleteUser(string $user): bool
{
return $this->removeFilteredGroupingPolicy(0, $user);
}
/**
* Deletes a role.
*
* @param string $role
* @return bool
*/
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);
}
/**
* AddPermissionsForUser adds multiple permissions for a user or role.
* Returns false if the user or role already has one of the permissions (aka not affected).
*
* @param string $user
* @param array ...$permissions
* @return bool
*/
public function addPermissionsForUser(string $user, array ...$permissions): bool
{
$rules = [];
foreach ($permissions as $permission) {
$rules[] = array_merge([$user], $permission);
}
return $this->addPolicies($rules);
}
/**
* 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
* @param string ...$domain
*
* @return array
*/
public function getPermissionsForUser(string $user, string ...$domain): array
{
$permission = [];
foreach ($this->model['p'] as $ptype => $assertion) {
$args = [];
$args[0] = $user;
foreach ($assertion->tokens as $i => $token) {
if ($token == sprintf('%s_dom', $ptype)) {
$args[$i] = $domain[0];
break;
}
}
$perm = $this->getFilteredPolicy(0, ...$args);
$permission = array_merge($permission, $perm);
}
return $permission;
}
/**
* 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);
foreach ($this->rmMap as $rm) {
$roles = $rm->getRoles($name, ...$domain);
foreach ($roles as $r) {
if (!isset($roleSet[$r])) {
$res[] = $r;
$q[] = $r;
$roleSet[$r] = true;
}
}
}
}
return $res;
}
/**
* GetImplicitUsersForRole gets implicit users for a role.
*
* @param string $name
* @param string ...$domain
* @return array
*/
public function getImplicitUsersForRole(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);
foreach ($this->rmMap as $rm) {
$roles = $rm->getUsers($name, ...$domain);
foreach ($roles as $r) {
if (!isset($roleSet[$r])) {
$res[] = $r;
$q[] = $r;
$roleSet[$r] = true;
}
}
}
}
return $res;
}
/**
* GetImplicitResourcesForUser returns all policies that user obtaining in domain
*
* @param string $user
* @param string ...$domain
* @return array
*/
public function getImplicitResourcesForUser(string $user, string ...$domain): array
{
$permissions = $this->getImplicitPermissionsForUser($user, ...$domain);
$res = [];
foreach ($permissions as $permission) {
if ($permission[0] == $user) {
$res[] = $permission;
continue;
}
$resLocal = [[$user]];
$tokensLength = count($permission);
$t = [[]];
foreach (array_slice($permission, 1) as $token) {
$tokens = $this->getImplicitUsersForRole($token, ...$domain);
$tokens[] = $token;
$t[] = $tokens;
}
for ($i = 1; $i < $tokensLength; $i++) {
$n = [];
foreach ($t[$i] as $tokens) {
foreach ($resLocal as $policy) {
$temp = [];
$temp = array_merge($temp, $policy);
$temp[] = $tokens;
$n[] = $temp;
}
}
$resLocal = $n;
}
$res = array_merge($res, $resLocal);
}
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
* @throws CasbinException
*/
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
* @throws CasbinException
*/
public function getImplicitUsersForPermission(string ...$permission): array
{
$pSubjects = $this->getAllSubjects();
$gInherit = $this->model->getValuesForFieldInPolicyAllTypes("g", 1);
$gSubjects = $this->model->getValuesForFieldInPolicyAllTypes("g", 0);
$subjects = array_merge($pSubjects, $gSubjects);
Util::ArrayRemoveDuplicates($subjects);
$subjects = array_diff($subjects, $gInherit);
$res = [];
foreach ($subjects as $user) {
$req = $permission;
array_unshift($req, $user);
$allowed = $this->enforce(...$req);
if ($allowed) {
$res[] = $user;
}
}
return $res;
}
/**
* GetAllUsersByDomain would get all users associated with the domain.
*
* @param string $domain
* @return string[]
*/
public function getAllUsersByDomain(string $domain): array
{
$m = [];
$g = $this->model['g']['g'];
$p = $this->model['p']['p'];
$users = [];
$index = $this->getDomainIndex('p');
$getUser = function (int $index, array $policies, string $domain, array $m): array {
if (count($policies) == 0 || count($policies[0]) <= $index) {
return [];
}
$res = [];
foreach ($policies as $policy) {
$ok = isset($m[$policy[0]]);
if ($policy[$index] == $domain && !$ok) {
$res[] = $policy[0];
$m[$policy[0]] = [];
}
}
return $res;
};
$users = array_merge($users, $getUser(2, $g->policy, $domain, $m));
$users = array_merge($users, $getUser($index, $p->policy, $domain, $m));
return $users;
}
/**
* Gets the users that has a role inside a domain. Add by Gordon.
*
* @param string $name
* @param string $domain
*
* @return array
*/
public function getUsersForRoleInDomain(string $name, string $domain): array
{
return $this->model['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);
}
/**
* DeleteAllUsersByDomain would delete all users associated with the domain.
*
* @param string $domain
* @return bool
*/
public function deleteAllUsersByDomain(string $domain): bool
{
$g = $this->model['g']['g'];
$p = $this->model['p']['p'];
$index = $this->getDomainIndex('p');
$getUser = function (int $index, array $policies, string $domain): array {
if (count($policies) == 0 || count($policies[0]) <= $index) {
return [];
}
$res = [];
foreach ($policies as $policy) {
if ($policy[$index] == $domain) {
$res[] = $policy;
}
}
return $res;
};
$users = $getUser(2, $g->policy, $domain);
$this->removeGroupingPolicies($users);
$users = $getUser($index, $p->policy, $domain);
$this->removePolicies($users);
return true;
}
/**
* DeleteDomains would delete all associated users and roles.
* It would delete all domains if parameter is not provided.
*
* @param string ...$domains
* @return bool
*/
public function deleteDomains(string ...$domains): bool
{
if (count($domains) == 0) {
$this->clearPolicy();
return true;
}
foreach ($domains as $domain) {
$this->deleteAllUsersByDomain($domain);
}
return true;
}
}
src/Exceptions/BatchOperationException.php 0000644 00000000303 15247527047 0014757 0 ustar 00 adapter) && $this->autoSave;
}
/**
* Adds a rule to the current policy.
*
* @param string $sec
* @param string $ptype
* @param array $rule
*
* @return bool
*/
protected function addPolicyInternal(string $sec, string $ptype, array $rule): bool
{
if ($this->model->hasPolicy($sec, $ptype, $rule)) {
return false;
}
if ($this->shouldPersist()) {
try {
$this->adapter->addPolicy($sec, $ptype, $rule);
} catch (NotImplementedException $e) {
}
}
$this->model->addPolicy($sec, $ptype, $rule);
if ($sec == "g") {
$this->buildIncrementalRoleLinks(Policy::POLICY_ADD, $ptype, [$rule]);
}
if ($this->watcher !== null && $this->autoNotifyWatcher) {
if ($this->watcher instanceof WatcherEx) {
$this->watcher->updateForAddPolicy($sec, $ptype, ...$rule);
} else {
$this->watcher->update();
}
}
return true;
}
/**
* Adds rules to the current policy.
*
* @param string $sec
* @param string $ptype
* @param array $rules
*
* @return bool
* @throws Exceptions\CasbinException
*/
protected function addPoliciesInternal(string $sec, string $ptype, array $rules): bool
{
if ($this->model->hasPolicies($sec, $ptype, $rules)) {
return false;
}
if ($this->shouldPersist() && $this->adapter instanceof BatchAdapter) {
try {
$this->adapter->addPolicies($sec, $ptype, $rules);
} catch (NotImplementedException $e) {
}
}
$this->model->addPolicies($sec, $ptype, $rules);
if ($sec == "g") {
$this->buildIncrementalRoleLinks(Policy::POLICY_ADD, $ptype, $rules);
}
if ($this->watcher !== null && $this->autoNotifyWatcher) {
$this->watcher->update();
}
return true;
}
/**
* @param string $sec
* @param string $ptype
* @param string[] $oldRule
* @param string[] $newRule
*
* @return bool
*/
protected function updatePolicyInternal(string $sec, string $ptype, array $oldRule, array $newRule): bool
{
if ($this->shouldPersist() && $this->adapter instanceof UpdatableAdapter) {
try {
$this->adapter->updatePolicy($sec, $ptype, $oldRule, $newRule);
} catch (NotImplementedException $e) {
}
}
$ruleUpdated = $this->model->updatePolicy($sec, $ptype, $oldRule, $newRule);
if (!$ruleUpdated) {
return false;
}
if ($sec == "g") {
// remove the old rule
$this->buildIncrementalRoleLinks(Policy::POLICY_REMOVE, $ptype, [$oldRule]);
// add the new rule
$this->buildIncrementalRoleLinks(Policy::POLICY_ADD, $ptype, [$newRule]);
}
if ($this->watcher !== null && $this->autoNotifyWatcher) {
try {
if ($this->watcher instanceof WatcherUpdatable) {
$this->watcher->updateForUpdatePolicy($oldRule, $newRule);
} else {
$this->watcher->update();
}
} catch (\Exception $e) {
Log::logPrint("An exception occurred:" . $e->getMessage());
return false;
}
}
return true;
}
protected function updatePoliciesInternal(string $sec, string $ptype, array $oldRules, array $newRules): bool
{
if ($this->shouldPersist() && $this->adapter instanceof UpdatableAdapter) {
try {
$this->adapter->updatePolicies($sec, $ptype, $oldRules, $newRules);
} catch (NotImplementedException $e) {
}
}
$ruleUpdated = $this->model->updatePolicies($sec, $ptype, $oldRules, $newRules);
if (!$ruleUpdated) {
return false;
}
if ($sec == "g") {
// remove the old rule
$this->buildIncrementalRoleLinks(Policy::POLICY_REMOVE, $ptype, $oldRules);
// add the new rule
$this->buildIncrementalRoleLinks(Policy::POLICY_ADD, $ptype, $newRules);
}
if ($this->watcher !== null && $this->autoNotifyWatcher) {
try {
if ($this->watcher instanceof WatcherUpdatable) {
$this->watcher->updateForUpdatePolicies($oldRules, $newRules);
} else {
$this->watcher->update();
}
} catch (\Exception $e) {
Log::logPrint("An exception occurred:" . $e->getMessage());
return false;
}
}
return true;
}
/**
* 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
{
if ($this->shouldPersist()) {
try {
$this->adapter->removePolicy($sec, $ptype, $rule);
} catch (NotImplementedException $e) {
}
}
$ruleRemoved = $this->model->removePolicy($sec, $ptype, $rule);
if (!$ruleRemoved) {
return false;
}
if ($sec == "g") {
$this->buildIncrementalRoleLinks(Policy::POLICY_REMOVE, $ptype, [$rule]);
}
if ($this->watcher !== null && $this->autoNotifyWatcher) {
if ($this->watcher instanceof WatcherEx) {
$this->watcher->updateForRemovePolicy($sec, $ptype, ...$rule);
} else {
$this->watcher->update();
}
}
return true;
}
/**
* Removes a rules from the current policy.
*
* @param string $sec
* @param string $ptype
* @param array $rules
*
* @return bool
*/
protected function removePoliciesInternal(string $sec, string $ptype, array $rules): bool
{
if (!$this->model->hasPolicies($sec, $ptype, $rules)) {
return false;
}
if ($this->shouldPersist() && $this->adapter instanceof BatchAdapter) {
try {
$this->adapter->removePolicies($sec, $ptype, $rules);
} catch (NotImplementedException $e) {
}
}
$ruleRemoved = $this->model->removePolicies($sec, $ptype, $rules);
if (!$ruleRemoved) {
return false;
}
if ($sec == "g") {
$this->buildIncrementalRoleLinks(Policy::POLICY_REMOVE, $ptype, $rules);
}
if ($this->watcher !== null && $this->autoNotifyWatcher) {
// error intentionally ignored
$this->watcher->update();
}
return true;
}
/**
* 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
{
if ($this->shouldPersist()) {
try {
$this->adapter->removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues);
} catch (NotImplementedException $e) {
}
}
$ruleRemoved = $this->model->removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues);
if (!$ruleRemoved) {
return false;
}
if ($sec == "g") {
$this->buildIncrementalRoleLinks(Policy::POLICY_REMOVE, $ptype, $ruleRemoved);
}
if ($this->watcher !== null && $this->autoNotifyWatcher) {
// error intentionally ignored
if ($this->watcher instanceof WatcherEx) {
$this->watcher->updateForRemoveFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues);
} else {
$this->watcher->update();
}
}
return true;
}
protected function updateFilteredPoliciesInternal(string $sec, string $ptype, array $newRules, int $fieldIndex, string ...$fieldValues): bool
{
$oldRules = [];
if ($this->shouldPersist()) {
try {
if ($this->adapter instanceof UpdatableAdapter) {
$oldRules = $this->adapter->updateFilteredPolicies($sec, $ptype, $newRules, $fieldIndex, ...$fieldValues);
}
} catch (NotImplementedException $e) {
}
}
$ruleChanged = $this->model->removePolicies($sec, $ptype, $oldRules);
$this->model->addPolicies($sec, $ptype, $newRules);
$ruleChanged = $ruleChanged && count($newRules) !== 0;
if (!$ruleChanged) {
return $ruleChanged;
}
if ($sec == "g") {
// remove the old rules
$this->buildIncrementalRoleLinks(Policy::POLICY_REMOVE, $ptype, $oldRules);
// add the new rules
$this->buildIncrementalRoleLinks(Policy::POLICY_ADD, $ptype, $newRules);
}
if ($this->watcher !== null && $this->autoNotifyWatcher) {
// error intentionally ignored
if ($this->watcher instanceof WatcherUpdatable) {
$this->watcher->updateForUpdatePolicies($oldRules, $newRules);
} else {
$this->watcher->update();
}
return $ruleChanged;
}
return $ruleChanged;
}
/**
* Undocumented function
*
* @param string $ptype
* @return int
*/
protected function getDomainIndex(string $ptype): int
{
$p = $this->model['p'][$ptype];
$pattern = sprintf("%s_dom", $ptype);
$index = count($p->tokens);
$tempIndex = array_search($pattern, $p->tokens);
if ($tempIndex !== false) {
$index = intval($tempIndex);
}
return $index;
}
}
src/Log/Log.php 0000644 00000002021 15247527047 0007316 0 ustar 00 write(...$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.php 0000644 00000001407 15247527047 0010023 0 ustar 00 path = 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/ManagementEnforcer.php 0000644 00000043022 15247527047 0011622 0 ustar 00 model->getValuesForFieldInPolicyAllTypes('p', 0);
}
/**
* 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->model->getValuesForFieldInPolicyAllTypes('p', 1);
}
/**
* 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->model->getValuesForFieldInPolicyAllTypes('p', 2);
}
/**
* 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->model->getValuesForFieldInPolicyAllTypes('g', 1);
}
/**
* 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);
}
/**
* AddPolicies adds authorization rules to the current policy.
* If the rule already exists, the function returns false for the corresponding rule and the rule will not be added.
* Otherwise the function returns true for the corresponding rule by adding the new rule.
*
* @param string[][] $rules
*
* @return bool
* @throws Exceptions\CasbinException
*/
public function addPolicies(array $rules): bool
{
return $this->addNamedPolicies('p', $rules);
}
/**
* 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);
}
/**
* AddNamedPolicies adds authorization rules to the current named policy.
* If the rule already exists, the function returns false for the corresponding rule and the rule will not be added.
* Otherwise the function returns true for the corresponding by adding the new rule.
*
* @param string $ptype
* @param string[][] $rules
*
* @return bool
* @throws Exceptions\CasbinException
*/
public function addNamedPolicies(string $ptype, array $rules): bool
{
return $this->addPoliciesInternal('p', $ptype, $rules);
}
/**
* 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 rules from the current policy.
*
* @param array $rules
*
* @return bool
*/
public function removePolicies(array $rules): bool
{
return $this->removeNamedPolicies('p', $rules);
}
/**
* Removes an authorization rule from the current policy.
*
* @param string[] $oldRule
* @param string[] $newRule
*
* @return bool
*/
public function updatePolicy(array $oldRule, array $newRule): bool
{
return $this->updateNamedPolicy("p", $oldRule, $newRule);
}
/**
* Updates an authorization rule from the current policy.
*
* @param string $ptype
* @param string[] $oldRule
* @param string[] $newRule
*
* @return bool
*/
public function updateNamedPolicy(string $ptype, array $oldRule, array $newRule): bool
{
return $this->updatePolicyInternal("p", $ptype, $oldRule, $newRule);
}
/**
* UpdatePolicies updates authorization rules from the current policies.
*
* @param string[][] $oldPolices
* @param string[][] $newPolicies
* @return boolean
*/
public function updatePolicies(array $oldPolices, array $newPolicies): bool
{
return $this->updateNamedPolicies("p", $oldPolices, $newPolicies);
}
/**
* Updates authorization rules from the current policy.
*
* @param string $ptype
* @param string[][] $oldPolices
* @param string[][] $newPolicies
* @return boolean
*/
public function updateNamedPolicies(string $ptype, array $oldPolices, array $newPolicies): bool
{
return $this->updatePoliciesInternal("p", $ptype, $oldPolices, $newPolicies);
}
public function updateFilteredPolicies(array $newPolicies, int $fieldIndex, string ...$fieldValues): bool
{
return $this->updateFilteredNamedPolicies("p", $newPolicies, $fieldIndex, ...$fieldValues);
}
/**
* Undocumented function
*
* @param string $ptype
* @param array $newPolicies
* @param integer $fieldIndex
* @param string ...$fieldValues
* @return boolean
*/
public function updateFilteredNamedPolicies(string $ptype, array $newPolicies, int $fieldIndex, string ...$fieldValues): bool
{
return $this->updateFilteredPoliciesInternal("p", $ptype, $newPolicies, $fieldIndex, ...$fieldValues);
}
/**
* 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 rules from the current named policy.
*
* @param string $ptype
* @param array $rules
*
* @return bool
*/
public function removeNamedPolicies(string $ptype, array $rules): bool
{
return $this->removePoliciesInternal('p', $ptype, $rules);
}
/**
* 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);
}
/**
* AddGroupingPolicy adds a role inheritance rules 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 array $rules
*
* @return bool
*/
public function addGroupingPolicies(array $rules): bool
{
return $this->addNamedGroupingPolicies('g', $rules);
}
/**
* 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;
}
/**
* AddNamedGroupingPolicy adds a named role inheritance rules 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 array $rules
*
* @return bool
*/
public function addNamedGroupingPolicies(string $ptype, array $rules): bool
{
$ruleAdded = $this->addPoliciesInternal('g', $ptype, $rules);
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 rules from the current policy.
*
* @param array $rules
*
* @return bool
*/
public function removeGroupingPolicies(array $rules): bool
{
return $this->removeNamedGroupingPolicies('g', $rules);
}
/**
* 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 rules from the current named policy.
*
* @param string $ptype
* @param array $rules
*
* @return bool
*/
public function removeNamedGroupingPolicies(string $ptype, array $rules): bool
{
$ruleRemoved = $this->removePoliciesInternal('g', $ptype, $rules);
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.php 0000644 00000005777 15247527047 0011110 0 ustar 00
*/
public $policyMap = [];
/**
* $rm.
*
* @var RoleManager
*/
public $rm;
/**
* $priorityIndex.
*
* @var int|bool
*/
public $priorityIndex;
public function initPriorityIndex(): void
{
$this->priorityIndex = false;
}
/**
* @param RoleManager $rm
*
* @throws CasbinException
*/
public function buildRoleLinks(RoleManager $rm): void
{
$this->rm = $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();
}
/**
* @param RoleManager $rm
* @param integer $op
* @param string[][] $rules
* @return void
*/
public function buildIncrementalRoleLinks(RoleManager $rm, int $op, array $rules): void
{
$this->rm = $rm;
$count = substr_count($this->value, '_');
if ($count < 2) {
throw new CasbinException('the number of "_" in role definition should be at least 2');
}
foreach ($rules as $rule) {
if (\count($rule) < $count) {
throw new CasbinException('grouping policy elements do not meet role definition');
}
if (\count($rule) > $count) {
$rule = array_slice($rule, 0, $count);
}
switch ($op) {
case Policy::POLICY_ADD:
$rm->addLink($rule[0], $rule[1], ...array_slice($rule, 2));
break;
case Policy::POLICY_REMOVE:
$rm->deleteLink($rule[0], $rule[1], ...array_slice($rule, 2));
break;
}
}
}
}
src/Model/FunctionMap.php 0000644 00000003265 15247527047 0011352 0 ustar 00
*/
private $functions = [];
/**
* @param string $name
* @param Closure $func
*/
public function addFunction(string $name, Closure $func): void
{
$this->functions[$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('keyGet', function (...$args) {
return BuiltinOperations::keyGetFunc(...$args);
});
$fm->addFunction('keyMatch2', function (...$args) {
return BuiltinOperations::keyMatch2Func(...$args);
});
$fm->addFunction('keyGet2', function (...$args) {
return BuiltinOperations::keyGet2Func(...$args);
});
$fm->addFunction('regexMatch', function (...$args) {
return BuiltinOperations::regexMatchFunc(...$args);
});
$fm->addFunction('ipMatch', function (...$args) {
return BuiltinOperations::ipMatchFunc(...$args);
});
$fm->addFunction('globMatch', function (...$args) {
return BuiltinOperations::globMatchFunc(...$args);
});
return $fm;
}
/**
* @return array
*/
public function getFunctions(): array
{
return $this->functions;
}
}
src/Model/Model.php 0000644 00000022617 15247527047 0010171 0 ustar 00
*/
protected $sectionNameMap = [
'r' => 'request_definition',
'p' => 'policy_definition',
'g' => 'role_definition',
'e' => 'policy_effect',
'm' => 'matchers',
];
public function __construct()
{
}
public function __clone()
{
$this->sectionNameMap = $this->sectionNameMap;
$newAstMap = [];
foreach ($this->items as $ptype => $ast) {
foreach ($ast as $i => $v) {
$newAstMap[$ptype][$i] = clone $v;
}
}
$this->items = $newAstMap;
}
/**
* @param ConfigContract $cfg
* @param string $sec
* @param string $key
*
* @return bool
* @throws CasbinException
*/
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
* @throws CasbinException
*/
public function addDef(string $sec, string $key, string $value): bool
{
if ('' == $value) {
return false;
}
$ast = new Assertion();
$ast->key = $key;
$ast->value = $value;
$ast->initPriorityIndex();
if ('r' == $sec || 'p' == $sec) {
$ast->tokens = explode(',', $ast->value);
foreach ($ast->tokens as $i => $token) {
$ast->tokens[$i] = $key . '_' . trim($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
* @throws CasbinException
*/
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
* @throws CasbinException
*/
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
* @throws CasbinException
*/
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
* @throws CasbinException
*/
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
* @throws CasbinException
*/
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();
}
public function getNameWithDomain(string $domain, string $name): string
{
return $domain . self::DEFAULT_SEPARATOR . $name;
}
public function getSubjectHierarchyMap(array $policies): array
{
$subjectHierarchyMap = [];
// Tree structure of role
$policyMap = [];
foreach ($policies as $policy) {
if (count($policy) < 2) {
throw new CasbinException('policy g expect 2 more params');
}
$domain = self::DEFAULT_DOMAIN;
if (count($policy) != 2) {
$domain = $policy[2];
}
$child = $this->getNameWithDomain($domain, $policy[0]);
$parent = $this->getNameWithDomain($domain, $policy[1]);
$policyMap[$parent][] = $child;
if (!isset($subjectHierarchyMap[$child])) {
$subjectHierarchyMap[$child] = 0;
}
if (!isset($subjectHierarchyMap[$parent])) {
$subjectHierarchyMap[$parent] = 0;
}
$subjectHierarchyMap[$child] = 1;
}
// Use queues for levelOrder
$queue = [];
foreach ($subjectHierarchyMap as $k => $v) {
$root = $k;
if ($v != 0) {
continue;
}
$lv = 0;
$queue[] = $root;
while (count($queue) != 0) {
$sz = count($queue);
for ($i = 0; $i < $sz; $i++) {
$node = $queue[array_key_first($queue)];
unset($queue[array_key_first($queue)]);
$nodeValue = $node;
$subjectHierarchyMap[$nodeValue] = $lv;
if (isset($policyMap[$nodeValue])) {
foreach ($policyMap[$nodeValue] as $child) {
$queue[] = $child;
}
}
}
$lv++;
}
}
return $subjectHierarchyMap;
}
public function sortPoliciesBySubjectHierarchy(): void
{
if ($this->items['e']['e']->value != 'subjectPriority(p_eft) || deny') {
return;
}
$subIndex = 0;
$domainIndex = -1;
foreach ($this->items['p'] as $ptype => $assertion) {
foreach ($assertion->tokens as $index => $token) {
if ($token == sprintf('%s_dom', $ptype)) {
$domainIndex = $index;
break;
}
}
$policies = &$assertion->policy;
$subjectHierarchyMap = $this->getSubjectHierarchyMap($this->items['g']['g']->policy);
usort($policies, function ($i, $j) use ($subIndex, $domainIndex, $subjectHierarchyMap): int {
$domain1 = self::DEFAULT_DOMAIN;
$domain2 = self::DEFAULT_DOMAIN;
if ($domainIndex != -1) {
$domain1 = $i[$domainIndex];
$domain2 = $j[$domainIndex];
}
$name1 = $this->getNameWithDomain($domain1, $i[$subIndex]);
$name2 = $this->getNameWithDomain($domain2, $j[$subIndex]);
$p1 = $subjectHierarchyMap[$name1];
$p2 = $subjectHierarchyMap[$name2];
if ($p1 == $p2) {
return 0;
}
return ($p1 > $p2) ? -1 : 1;
});
foreach ($assertion->policy as $i => $policy) {
$assertion->policyMap[implode(',', $policy)] = $i;
}
}
}
public function sortPoliciesByPriority(): void
{
foreach ($this->items['p'] as $ptype => $assertion) {
$index = array_search(sprintf("%s_priority", $ptype), $assertion->tokens);
if ($index !== false) {
$assertion->priorityIndex = intval($index);
} else {
continue;
}
$policies = &$assertion->policy;
usort($policies, function ($i, $j) use ($assertion): int {
$p1 = $i[$assertion->priorityIndex];
$p2 = $j[$assertion->priorityIndex];
if ($p1 == $p2) {
return 0;
}
return ($p1 < $p2) ? -1 : 1;
});
foreach ($assertion->policy as $i => $policy) {
$assertion->policyMap[implode(',', $policy)] = $i;
}
}
}
}
src/Model/Policy.php 0000644 00000032444 15247527047 0010367 0 ustar 00 >
* @author techlee@qq.com
*/
abstract class Policy implements ArrayAccess
{
public const POLICY_ADD = 0;
public const POLICY_REMOVE = 1;
const DEFAULT_SEP = ",";
/**
* All of the Model items.
*
* @var array>
*/
protected $items = [];
/**
* BuildIncrementalRoleLinks provides incremental build the role inheritance relations.
*
* @param RoleManager[] $rmMap
* @param integer $op
* @param string $sec
* @param string $ptype
* @param string[][] $rules
* @return void
*/
public function buildIncrementalRoleLinks(array $rmMap, int $op, string $sec, string $ptype, array $rules): void
{
if ($sec == "g") {
$this->items[$sec][$ptype]->buildIncrementalRoleLinks($rmMap[$ptype], $op, $rules);
}
}
/**
* Initializes the roles in RBAC.
*
* @param RoleManager[] $rmMap
* @throws CasbinException
*/
public function buildRoleLinks(array $rmMap): void
{
if (!isset($this->items['g'])) {
return;
}
foreach ($this->items['g'] as $ptype => $ast) {
$rm = $rmMap[$ptype];
$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 = [];
$this->items[$sec][$key]->policyMap = [];
}
}
}
/**
* Gets all rules in a policy.
*
* @param string $sec
* @param string $ptype
*
* @return string[][]
*/
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 string[][]
*/
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 string[] $rule
*
* @return bool
*/
public function hasPolicy(string $sec, string $ptype, array $rule): bool
{
if (!isset($this->items[$sec][$ptype])) {
return false;
}
return isset($this->items[$sec][$ptype]->policyMap[implode(self::DEFAULT_SEP, $rule)]);
}
/**
* Determines whether a model has any of the specified policies. If one is found we return true.
*
* @param string $sec
* @param string $ptype
* @param string[][] $rules
*
* @return bool
*/
public function hasPolicies(string $sec, string $ptype, array $rules): bool
{
foreach ($rules as $rule) {
if ($this->hasPolicy($sec, $ptype, $rule)) {
return true;
}
}
return false;
}
/**
* Adds a policy rule to the model.
*
* @param string $sec
* @param string $ptype
* @param string[] $rule
*/
public function addPolicy(string $sec, string $ptype, array $rule): void
{
$assertion = &$this->items[$sec][$ptype];
$assertion->policy[] = $rule;
$assertion->policyMap[implode(self::DEFAULT_SEP, $rule)] = count($this->items[$sec][$ptype]->policy) - 1;
if ($sec == 'p' && $assertion->priorityIndex !== false && $assertion->priorityIndex >= 0) {
$idxInsert = $rule[$assertion->priorityIndex];
for ($i = count($assertion->policy) - 1; $i > 0; $i--) {
$idx = $assertion->policy[$i-1][$assertion->priorityIndex];
if ($idx > $idxInsert) {
$assertion->policy[$i] = $assertion->policy[$i-1];
$assertion->policyMap[implode(self::DEFAULT_SEP, $assertion->policy[$i-1])]++;
} else {
break;
}
}
$assertion->policy[$i] = $rule;
$assertion->policyMap[implode(self::DEFAULT_SEP, $rule)] = $i;
}
}
/**
* Adds a policy rules to the model.
*
* @param string $sec
* @param string $ptype
* @param string[][] $rules
*/
public function addPolicies(string $sec, string $ptype, array $rules): void
{
foreach ($rules as $rule) {
$hashKey = implode(self::DEFAULT_SEP, $rule);
if (isset($this->items[$sec][$ptype]->policyMap[$hashKey])) {
continue;
}
$this->addPolicy($sec, $ptype, $rule);
}
}
/**
* Updates a policy rule from the model.
*
* @param string $sec
* @param string $ptype
* @param string[] $oldRule
* @param string[] $newRule
*
* @return bool
*/
public function updatePolicy(string $sec, string $ptype, array $oldRule, array $newRule): bool
{
$oldPolicy = implode(self::DEFAULT_SEP, $oldRule);
if (!isset($this->items[$sec][$ptype]->policyMap[$oldPolicy])) {
return false;
}
$index = $this->items[$sec][$ptype]->policyMap[$oldPolicy];
$this->items[$sec][$ptype]->policy[$index] = $newRule;
unset($this->items[$sec][$ptype]->policyMap[$oldPolicy]);
$this->items[$sec][$ptype]->policyMap[implode(self::DEFAULT_SEP, $newRule)] = $index;
return true;
}
/**
* UpdatePolicies updates a policy rule from the model.
*
* @param string $sec
* @param string $ptype
* @param string[][] $oldRules
* @param string[][] $newRules
* @return boolean
*/
public function updatePolicies(string $sec, string $ptype, array $oldRules, array $newRules): bool
{
$modifiedRuleIndex = [];
$newIndex = 0;
foreach ($oldRules as $oldIndex => $oldRule) {
$oldPolicy = implode(self::DEFAULT_SEP, $oldRule);
$index = $this->items[$sec][$ptype]->policyMap[$oldPolicy] ?? null;
if (is_null($index)) {
// rollback
foreach ($modifiedRuleIndex as $index => $oldNewIndex) {
$this->items[$sec][$ptype]->policy[$index] = $oldRules[$oldNewIndex[0]];
$oldPolicy = implode(self::DEFAULT_SEP, $oldRules[$oldNewIndex[0]]);
$newPolicy = implode(self::DEFAULT_SEP, $newRules[$oldNewIndex[1]]);
unset($this->items[$sec][$ptype]->policyMap[$newPolicy]);
$this->items[$sec][$ptype]->policyMap[$oldPolicy] = $index;
}
return false;
}
$this->items[$sec][$ptype]->policy[$index] = $newRules[$newIndex];
unset($this->items[$sec][$ptype]->policyMap[$oldPolicy]);
$this->items[$sec][$ptype]->policyMap[implode(self::DEFAULT_SEP, $newRules[$newIndex])] = $index;
$modifiedRuleIndex[$index] = [$oldIndex, $newIndex];
$newIndex++;
}
return true;
}
/**
* 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;
}
$hashKey = implode(self::DEFAULT_SEP, $rule);
if (!isset($this->items[$sec][$ptype]->policyMap[$hashKey])) {
return false;
}
$index = $this->items[$sec][$ptype]->policyMap[$hashKey];
array_splice($this->items[$sec][$ptype]->policy, $index, 1);
unset($this->items[$sec][$ptype]->policyMap[$hashKey]);
$count = count($this->items[$sec][$ptype]->policy);
for ($i = $index; $i < $count; $i++) {
$this->items[$sec][$ptype]->policyMap[implode(self::DEFAULT_SEP, $this->items[$sec][$ptype]->policy[$i])] = $i;
}
return true;
}
/**
* Removes a policy rules from the model.
*
* @param string $sec
* @param string $ptype
* @param string[][] $rules
*
* @return bool
*/
public function removePolicies(string $sec, string $ptype, array $rules): bool
{
if (!isset($this->items[$sec][$ptype])) {
return false;
}
foreach ($rules as $rule) {
$this->removePolicy($sec, $ptype, $rule);
}
return true;
}
/**
* Removes policy rules based on field filters from the model.
*
* @param string $sec
* @param string $ptype
* @param int $fieldIndex
* @param string ...$fieldValues
*
* If more than one rule is removed, return the removed rule array, otherwise return false
* @return string[][]|false
*/
public function removeFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues)
{
$tmp = [];
$effects = [];
$res = false;
if (!isset($this->items[$sec][$ptype])) {
return $res;
}
$this->items[$sec][$ptype]->policyMap = [];
foreach ($this->items[$sec][$ptype]->policy as $index => $rule) {
$matched = true;
foreach ($fieldValues as $i => $fieldValue) {
if ('' != $fieldValue && $rule[$fieldIndex + $i] != $fieldValue) {
$matched = false;
break;
}
}
if ($matched) {
$effects[] = $rule;
} else {
$tmp[] = $rule;
$this->items[$sec][$ptype]->policyMap[implode(self::DEFAULT_SEP, $rule)] = count($tmp) - 1;
}
}
if (count($tmp) != count($this->items[$sec][$ptype]->policy)) {
$this->items[$sec][$ptype]->policy = $tmp;
$res = true;
}
return $res ? $effects : false;
}
/**
* 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 string[]
*/
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;
}
/**
* Gets all values for a field for all rules in a policy of all ptypes, duplicated values are removed.
*
* @param string $sec
* @param int $fieldIndex
*
* @return string[]
*/
public function getValuesForFieldInPolicyAllTypes(string $sec, int $fieldIndex): array
{
$values = [];
foreach ($this->items[$sec] as $key => $ptype) {
$values = array_merge($values, $this->getValuesForFieldInPolicy($sec, $key, $fieldIndex));
}
Util::arrayRemoveDuplicates($values);
return $values;
}
/**
* Determine if the given Model option exists.
*
* @param mixed $offset
*
* @return bool
*/
public function offsetExists($offset): bool
{
return isset($this->items[$offset]);
}
/**
* Get a Model option.
*
* @param mixed $offset
*
* @return array|null
*/
public function offsetGet($offset): ?array
{
return isset($this->items[$offset]) ? $this->items[$offset] : null;
}
/**
* Set a Model option.
*
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->items[$offset] = $value;
}
/**
* Unset a Model option.
*
* @param mixed $offset
*/
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
}
src/Persist/Adapter.php 0000644 00000002556 15247527047 0011102 0 ustar 00 loadPolicyArray($tokens, $model);
}
/**
* Loads a policy rule to model.
*
* @param array $rule
* @param Model $model
*/
public function loadPolicyArray(array $rule, Model $model): void
{
$key = $rule[0];
$sec = $key[0];
if (!isset($model[$sec][$key])) {
return;
}
$assertions = $model[$sec];
$assertion = $assertions[$key];
if (!($assertion instanceof Assertion)) {
return;
}
$rule = \array_slice($rule, 1);
$assertion->policy[] = $rule;
$assertion->policyMap[implode(Policy::DEFAULT_SEP, $rule)] = count($assertion->policy) - 1;
$assertions[$key] = $assertion;
$model[$sec] = $assertions;
}
}
src/Persist/Adapters/FileAdapter.php 0000644 00000014110 15247527047 0013432 0 ustar 00 filePath = $filePath;
}
/**
* Loads all policy rules from the storage.
*
* @param 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 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 Model $model
* @throws InvalidFilePathException
*/
protected function loadPolicyFile(Model $model): void
{
$file = fopen($this->filePath, 'rb');
if (false === $file) {
throw new InvalidFilePathException(sprintf('Unable to access to the specified path "%s"', $this->filePath));
}
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 string[] $rule
*
* @throws NotImplementedException
*/
public function addPolicy(string $sec, string $ptype, array $rule): void
{
throw new NotImplementedException('not implemented');
}
/**
* Adds a policy rule to the storage.
*
* @param string $sec
* @param string $ptype
* @param string[][] $rules
*
* @throws NotImplementedException
*/
public function addPolicies(string $sec, string $ptype, array $rules): void
{
throw new NotImplementedException('not implemented');
}
/**
* Removes a policy rule from the storage.
*
* @param string $sec
* @param string $ptype
* @param string[] $rule
*
* @throws NotImplementedException
*/
public function removePolicy(string $sec, string $ptype, array $rule): void
{
throw new NotImplementedException('not implemented');
}
/**
* Removes a policy rules from the storage.
*
* @param string $sec
* @param string $ptype
* @param string[][] $rules
*
* @throws NotImplementedException
*/
public function removePolicies(string $sec, string $ptype, array $rules): 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');
}
/**
* Updates a policy rule from storage.
* This is part of the Auto-Save feature.
*
* @param string $sec
* @param string $ptype
* @param string[] $oldRule
* @param string[] $newPolicy
*/
public function updatePolicy(string $sec, string $ptype, array $oldRule, array $newPolicy): void
{
throw new NotImplementedException('not implemented');
}
/**
* UpdatePolicies updates some policy rules to storage, like db, redis.
*
* @param string $sec
* @param string $ptype
* @param string[][] $oldRules
* @param string[][] $newRules
* @return void
*/
public function updatePolicies(string $sec, string $ptype, array $oldRules, array $newRules): void
{
throw new NotImplementedException('not implemented');
}
/**
* UpdateFilteredPolicies deletes old rules and adds new rules.
*
* @param string $sec
* @param string $ptype
* @param array $newPolicies
* @param integer $fieldIndex
* @param string ...$fieldValues
* @return array
*/
public function updateFilteredPolicies(string $sec, string $ptype, array $newPolicies, int $fieldIndex, string ...$fieldValues): array
{
throw new NotImplementedException('not implemented');
}
}
src/Persist/Adapters/FileFilteredAdapter.php 0000755 00000010466 15247527047 0015126 0 ustar 00 filtered = true;
parent::__construct($filePath);
}
/**
* Loads all policy rules from the storage.
*
* @param 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
* @throws CannotSaveFilteredPolicy|CasbinException
*/
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
* @throws InvalidFilePathException
*/
protected function loadFilteredPolicyFile(Model $model, Filter $filter, callable $handler): void
{
$file = fopen($this->filePath, 'rb');
if (false === $file) {
throw new InvalidFilePathException(sprintf('Unable to access to the specified path "%s"', $this->filePath));
}
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 bool
*/
protected static function filterLine(string $line, Filter $filter): bool
{
$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 bool
*/
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.php 0000755 00000001216 15247527047 0012505 0 ustar 00 p = $p;
$this->g = $g;
}
}
src/Persist/BatchAdapter.php 0000644 00000001413 15247527047 0012033 0 ustar 00 name = $name;
}
/**
* @param self $role
*/
public function addRole(self $role): void
{
// determine whether this role has been added
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 ($this->hasDirectRole($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
* @param int $hierarchyLevel
* @param Closure $matchingFunc
*
* @return bool
*/
public function hasRoleWithMatchingFunc(string $name, int $hierarchyLevel, Closure $matchingFunc): bool
{
if ($this->hasDirectRoleWithMatchingFunc($name, $matchingFunc)) {
return true;
}
if ($hierarchyLevel <= 0) {
return false;
}
foreach ($this->roles as $role) {
if ($role->hasRoleWithMatchingFunc($name, $hierarchyLevel - 1, $matchingFunc)) {
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;
}
/**
* @param string $name
* @param Closure $matchingFunc
*
* @return bool
*/
public function hasDirectRoleWithMatchingFunc(string $name, Closure $matchingFunc): bool
{
foreach ($this->roles as $role) {
if ($role->name == $name || $matchingFunc($name, $role->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 string[]
*/
public function getRoles(): array
{
return array_map(function (Role $role) {
return $role->name;
}, $this->roles);
}
}
src/Rbac/DefaultRoleManager/RoleManager.php 0000644 00000024011 15247527047 0014623 0 ustar 00
*/
protected $allDomains;
/**
* @var int
*/
protected $maxHierarchyLevel;
/**
* @var bool
*/
protected $hasPattern;
/**
* @var Closure
*/
protected $matchingFunc;
/**
* @var bool
*/
protected $hasDomainPattern;
/**
* @var Closure
*/
protected $domainMatchingFunc;
/**
* RoleManager constructor.
*
* @param int $maxHierarchyLevel
*/
public function __construct(int $maxHierarchyLevel)
{
$this->allDomains[self::DEFAULT_DOMAIN] = new Roles();
$this->maxHierarchyLevel = $maxHierarchyLevel;
$this->hasPattern = false;
$this->hasDomainPattern = false;
}
/**
* Support use pattern in g.
*
* @param string $name
* @param Closure $fn
*/
public function addMatchingFunc(string $name, Closure $fn): void
{
$this->hasPattern = true;
$this->matchingFunc = $fn;
}
/**
* Support use domain pattern in g.
*
* @param string $name
* @param Closure $fn
*/
public function addDomainMatchingFunc(string $name, Closure $fn): void
{
$this->hasDomainPattern = true;
$this->domainMatchingFunc = $fn;
}
/**
* Clears all stored data and resets the role manager to the initial state.
*/
public function clear(): void
{
$this->allDomains = [];
$this->loadOrStoreRoles(self::DEFAULT_DOMAIN, new Roles());
}
/**
* 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
*
* @throws CasbinException
*/
public function addLink(string $name1, string $name2, string ...$domain): void
{
if (count($domain) > 1) {
throw new CasbinException('error: domain should be 1 parameter');
}
$domain = count($domain) == 0 ? [self::DEFAULT_DOMAIN] : $domain;
$patternDomain = $this->getPatternDomain($domain[0]);
foreach ($patternDomain as $domain) {
$allRoles = &$this->loadOrStoreRoles($domain, new Roles());
$role1 = &$allRoles->loadOrStore($name1, new Role($name1));
$role2 = &$allRoles->loadOrStore($name2, new Role($name2));
$role1->addRole($role2);
if ($this->hasPattern) {
foreach ($allRoles->toArray() as $key => $value) {
$matchingFunc = $this->matchingFunc;
if ($matchingFunc($key . '', $name1) && $name1 != $key) {
$valueRole = &$allRoles->loadOrStore($key . '', new Role($key . ''));
$valueRole->addRole($role1);
}
if ($matchingFunc($key . '', $name2) && $name2 != $key) {
$role2->addRole($value);
}
if ($matchingFunc($name1, $key . '') && $name1 != $key) {
$valueRole = &$allRoles->loadOrStore($key . '', new Role($key . ''));
$valueRole->addRole($role1);
}
if ($matchingFunc($name2, $key . '') && $name2 != $key) {
$role2->addRole($value);
}
}
}
}
}
/**
* Undocumented function
*
* @param string $domain
* @return string[]
*/
public function getPatternDomain(string $domain): array
{
$patternDomain = [$domain];
if ($this->hasDomainPattern) {
foreach ($this->allDomains as $key => $value) {
$domainMatchingFunc = $this->domainMatchingFunc;
if ($domain != $key . '' && $domainMatchingFunc($domain, $key . '')) {
$patternDomain[] = $key . '';
}
}
}
return $patternDomain;
}
/**
* 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
*
* @throws CasbinException
*/
public function deleteLink(string $name1, string $name2, string ...$domain): void
{
if (count($domain) > 1) {
throw new CasbinException('error: domain should be 1 parameter');
}
$domain = count($domain) == 0 ? [self::DEFAULT_DOMAIN] : $domain;
$allRoles = &$this->loadOrStoreRoles($domain[0], new Roles());
if (is_null($allRoles->load($name1)) || is_null($allRoles->load($name2))) {
throw new CasbinException('error: name1 or name2 does not exist');
}
$role1 = &$allRoles->loadOrStore($name1, new Role($name1));
$role2 = &$allRoles->loadOrStore($name2, new Role($name2));
$role1->deleteRole($role2);
}
/**
* 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
* @throws CasbinException
*/
public function hasLink(string $name1, string $name2, string ...$domain): bool
{
if (count($domain) > 1) {
throw new CasbinException('error: domain should be 1 parameter');
}
$domain = count($domain) == 0 ? [self::DEFAULT_DOMAIN] : $domain;
if ($name1 == $name2) {
return true;
}
$patternDomain = $this->getPatternDomain($domain[0]);
foreach ($patternDomain as $domain) {
$allRoles = &$this->loadOrStoreRoles($domain, new Roles());
if (!$allRoles->hasRole($name1, $this->matchingFunc) || !$allRoles->hasRole($name2, $this->matchingFunc)) {
continue;
}
if ($this->hasPattern) {
$flag = false;
foreach ($allRoles->toArray() as $key => $value) {
$matchingFunc = $this->matchingFunc;
if ($matchingFunc($name1, $key . '') && $value->hasRoleWithMatchingFunc($name2, $this->maxHierarchyLevel, $matchingFunc)) {
$flag = true;
break;
}
}
if ($flag) {
return true;
}
continue;
}
$role1 = &$allRoles->createRole($name1);
$result = $role1->hasRole($name2, $this->maxHierarchyLevel);
if ($result) {
return true;
}
continue;
}
return false;
}
/**
* Gets the roles that a subject inherits.
* domain is a prefix to the roles.
*
* @param string $name
* @param string ...$domain
*
* @return string[]
* @throws CasbinException
*/
public function getRoles(string $name, string ...$domain): array
{
if (count($domain) > 1) {
throw new CasbinException('error: domain should be 1 parameter');
}
$domain = count($domain) == 0 ? [self::DEFAULT_DOMAIN] : $domain;
$patternDomain = $this->getPatternDomain($domain[0]);
$gottenRoles = [];
foreach ($patternDomain as $domain) {
$allRoles = &$this->loadOrStoreRoles($domain, new Roles());
if (!$allRoles->hasRole($name, $this->matchingFunc)) {
continue;
}
$gottenRoles = array_merge($gottenRoles, $allRoles->createRole($name)->getRoles());
}
$gottenRoles = array_unique($gottenRoles);
return $gottenRoles;
}
/**
* 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 string[]
* @throws CasbinException
*/
public function getUsers(string $name, string ...$domain): array
{
if (count($domain) > 1) {
throw new CasbinException('error: domain should be 1 parameter');
}
$domain = count($domain) == 0 ? [self::DEFAULT_DOMAIN] : $domain;
$patternDomain = $this->getPatternDomain($domain[0]);
$names = [];
foreach ($patternDomain as $domain) {
$allRoles = &$this->loadOrStoreRoles($domain, new Roles());
if (!$allRoles->hasRole($name, $this->matchingFunc)) {
return [];
}
foreach ($allRoles->toArray() as $role) {
if ($role->hasDirectRole($name)) {
$names[] = $role->name;
}
}
}
return $names;
}
/**
* Prints all the roles to log.
*/
public function printRoles(): void
{
$line = [];
array_map(function (Roles $roles) use (&$line) {
array_map(function (Role $role) use (&$line) {
if ($text = $role->toString()) {
$line[] = $text;
}
}, $roles->toArray());
}, $this->allDomains);
Log::logPrint(implode(', ', $line));
}
/**
* @param string $domain
* @param Roles $roles
*
* @return Roles
*/
protected function &loadOrStoreRoles(string $domain, Roles $roles): Roles
{
if (!isset($this->allDomains[$domain])) {
$this->allDomains[$domain] = $roles;
}
return $this->allDomains[$domain];
}
}
src/Rbac/DefaultRoleManager/Roles.php 0000644 00000003110 15247527047 0013510 0 ustar 00
*/
private $roles = [];
/**
* @param string $name
* @param Closure|null $matchingFunc
*
* @return bool
*/
public function hasRole(string $name, ?Closure $matchingFunc): bool
{
$ok = false;
if ($matchingFunc instanceof Closure) {
foreach ($this->roles as $key => $role) {
if ($matchingFunc($name, $key)) {
$ok = true;
}
}
} else {
$ok = isset($this->roles[$name]);
}
return $ok;
}
/**
* @param string $name
*
* @return Role
*/
public function &createRole(string $name): Role
{
$role = &$this->loadOrStore($name, new Role($name));
return $role;
}
/**
* @param string $name
*
* @return Role|null
*/
public function load(string $name): ?Role
{
if (!isset($this->roles[$name])) {
return null;
}
return $this->roles[$name];
}
/**
* @param string $name
* @param Role $role
*
* @return Role
*/
public function &loadOrStore(string $name, Role $role): Role
{
if (!isset($this->roles[$name])) {
$this->roles[$name] = $role;
}
return $this->roles[$name];
}
/**
* @return array
*/
public function toArray(): array
{
return $this->roles;
}
}
src/Rbac/RoleManager.php 0000644 00000004702 15247527047 0011127 0 ustar 00 $i) {
if (substr($key1, 0, $i) == substr($key2, 0, $i)) {
return substr($key1, $i);
}
}
return '';
}
/**
* KeyGetFunc is the wrapper for KeyGet
*
* @param mixed ...$args
* @return string
*/
public static function keyGetFunc(...$args)
{
$name1 = $args[0];
$name2 = $args[1];
return self::keyGet($name1, $name2);
}
/**
* Determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
* For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource".
*
* @param string $key1
* @param string $key2
*
* @return bool
*/
public static function keyMatch2(string $key1, string $key2): bool
{
if ('*' === $key2) {
$key2 = '.*';
}
$key2 = str_replace(['/*'], ['/.*'], $key2);
$pattern = '/:[^\/]+/';
$key2 = preg_replace_callback(
$pattern,
function ($m) {
return '[^\/]+';
},
$key2
);
return self::regexMatch($key1, '^' . $key2 . '$');
}
/**
* The wrapper for KeyMatch2.
*
* @param mixed ...$args
*
* @return bool
*/
public static function keyMatch2Func(...$args): bool
{
$name1 = $args[0];
$name2 = $args[1];
return self::keyMatch2($name1, $name2);
}
/**
* KeyGet2 returns value matched pattern
* For example, "/resource1" matches "/:resource"
* if the pathVar == "resource", then "resource1" will be returned
*
* @param string $key1
* @param string $key2
* @param string $pathVar
* @return string
*/
public static function keyGet2(string $key1, string $key2, string $pathVar): string
{
$key2 = str_replace(['/*'], ['/.*'], $key2);
$pattern = '/:[^\/]+/';
$keys = [];
preg_match_all($pattern, $key2, $keys);
$keys = $keys[0];
$key2 = preg_replace_callback(
$pattern,
function ($m) {
return '([^\/]+)';
},
$key2
);
$key2 = "~^" . $key2 . "$~";
$values = [];
preg_match($key2, $key1, $values);
if (count($values) === 0) {
return '';
}
foreach ($keys as $i => $key) {
if ($pathVar == substr($key, 1)) {
return $values[$i + 1];
}
}
return '';
}
/**
* KeyGet2Func is the wrapper for KeyGet2
*
* @param mixed ...$args
* @return string
*/
public static function keyGet2Func(...$args)
{
$name1 = $args[0];
$name2 = $args[1];
$key = $args[2];
return self::keyGet2($name1, $name2, $key);
}
/**
* Determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
* For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}".
*
* @param string $key1
* @param string $key2
*
* @return bool
*/
public static function keyMatch3(string $key1, string $key2): bool
{
$key2 = str_replace(['/*'], ['/.*'], $key2);
$pattern = '/\{[^\/]+\}/';
$key2 = preg_replace_callback(
$pattern,
function ($m) {
return '[^\/]+';
},
$key2
);
return self::regexMatch($key1, '^' . $key2 . '$');
}
/**
* The wrapper for KeyMatch3.
*
* @param mixed ...$args
*
* @return bool
*/
public static function keyMatch3Func(...$args): bool
{
$name1 = $args[0];
$name2 = $args[1];
return self::keyMatch3($name1, $name2);
}
/**
* Determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
* Besides what KeyMatch3 does, KeyMatch4 can also match repeated patterns:
* "/parent/123/child/123" matches "/parent/{id}/child/{id}"
* "/parent/123/child/456" does not match "/parent/{id}/child/{id}"
* But KeyMatch3 will match both.
*
* @param string $key1
* @param string $key2
*
* @return bool
*/
public static function keyMatch4(string $key1, string $key2): bool
{
$key2 = str_replace(['/*'], ['/.*'], $key2);
$tokens = [];
$pattern = '/\{([^\/]+)\}/';
$key2 = preg_replace_callback(
$pattern,
function ($m) use (&$tokens) {
$tokens[] = $m[1];
return '([^\/]+)';
},
$key2
);
$matched = preg_match_all('~^' . $key2 . '$~', $key1, $matches);
if (!$matched) {
return false;
}
$values = [];
foreach ($tokens as $key => $token) {
if (!isset($values[$token])) {
$values[$token] = $matches[$key + 1];
}
if ($values[$token] != $matches[$key + 1]) {
return false;
}
}
return true;
}
/**
* The wrapper for KeyMatch4.
*
* @param mixed ...$args
*
* @return bool
*/
public static function keyMatch4Func(...$args): bool
{
$name1 = $args[0];
$name2 = $args[1];
return self::keyMatch4($name1, $name2);
}
/**
* Determines whether key1 matches the pattern of key2 and ignores the parameters in key2.
* For example, "/foo/bar?status=1&type=2" matches "/foo/bar"
*
* @param string $key1
* @param string $key2
*
* @return bool
*/
public static function keyMatch5(string $key1, string $key2): bool
{
$pos = strpos($key1, '?');
if ($pos == false) {
return $key1 == $key2;
}
return substr($key1, 0, $pos) == $key2;
}
/**
* the wrapper for KeyMatch5.
*
* @param mixed ...$args
*
* @return bool
*/
public static function keyMatch5Func(...$args): bool
{
$name1 = $args[0];
$name2 = $args[1];
return self::keyMatch5($name1, $name2);
}
/**
* Determines whether key1 matches the pattern of key2 in regular expression.
*
* @param string $key1
* @param string $key2
*
* @return bool
*/
public static function regexMatch(string $key1, string $key2): bool
{
return (bool)preg_match('~' . $key2 . '~', $key1);
}
/**
* The wrapper for RegexMatch.
*
* @param mixed ...$args
*
* @return bool
*/
public static function regexMatchFunc(...$args): bool
{
$name1 = $args[0];
$name2 = $args[1];
return self::regexMatch($name1, $name2);
}
/**
* Determines whether IP address ip1 matches the pattern of IP address ip2, ip2 can be an IP address or a CIDR
* pattern.
*
* @param string $ip1
* @param string $ip2
*
* @return bool
*
* @throws Exception
*/
public static function ipMatch(string $ip1, string $ip2): bool
{
$objIP1 = IP::parse($ip1);
$objIP2 = Range::parse($ip2);
return $objIP2->contains($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);
}
/**
* Returns true if the specified `string` matches the given glob `pattern`.
*
* @param string $str
* @param string $pattern
*
* @return bool
*
* @throws Exception
*/
public static function globMatch(string $str, string $pattern): bool
{
return fnmatch($pattern, $str, FNM_PATHNAME | FNM_PERIOD);
}
/**
* The wrapper for globMatch.
*
* @param mixed ...$args
*
* @return bool
*
* @throws Exception
*/
public static function globMatchFunc(...$args): bool
{
$str = $args[0];
$pattern = $args[1];
return self::globMatch($str, $pattern);
}
/**
* 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)) {
return $rm->hasLink($name1, $name2);
} else {
$domain = (string)$args[2];
return $rm->hasLink($name1, $name2, $domain);
}
};
}
}
src/Util/Util.php 0000644 00000006675 15247527047 0007731 0 ustar 00 [^),]*)\)/';
/**
* Escapes the dots in the assertion, because the expression evaluation doesn't support such variable names.
*
* @param string $s
*
* @return string
* @throws CasbinException
*/
public static function escapeAssertion(string $s): string
{
if (0 === strpos($s, "r") || 0 === strpos($s, "p")) {
$pos = strpos($s, '.');
if ($pos !== false) {
$s[$pos] = '_';
}
}
$s = preg_replace_callback("~(\|| |=|\)|\(|&|<|>|,|\+|-|!|\*|\/)((r|p)[0-9]*)(\.)~", function ($m) {
return $m[1] . $m[2] . '_';
}, $s);
if (null === $s) {
throw new CasbinException(sprintf('Unable to escape assertion "%s"', $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));
}
/**
* Determine whether matcher contains function eval.
*
* @param string $s
*
* @return bool
*/
public static function hasEval(string $s): bool
{
return (bool)preg_match(self::REGEXP, $s);
}
/**
* Replace function eval with the value of its parameters.
*
* @param string $s
* @param string $rule
*
* @return string
*/
public static function replaceEval(string $s, string $rule): string
{
return (string)preg_replace(self::REGEXP, '(' . $rule . ')', $s);
}
/**
* Returns the parameters of function eval.
*
* @param string $s
*
* @return array
*/
public static function getEvalValue(string $s): array
{
preg_match_all(self::REGEXP, $s, $matches);
return $matches['rule'];
}
/**
* ReplaceEvalWithMap replace function eval with the value of its parameters via given sets.
*
* @param string $src
* @param array $sets
* @return string
*/
public static function replaceEvalWithMap(string $src, array $sets): string
{
return preg_replace_callback(self::REGEXP, function ($s) use ($sets): string {
$s = $s[0];
preg_match(self::REGEXP, $s, $subs);
if ($subs === null) {
return $s;
}
$key = $subs[1];
if (isset($sets[$key])) {
$found = true;
$value = $sets[$key];
} else {
$found = false;
}
if (!$found) {
return $s;
}
return preg_replace(self::REGEXP, $s, $value);
}, $src);
}
}
tests/EnforcerTest.php 0000644 00000053317 15247527047 0011050 0 ustar 00 addDef('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);
$a->loadPolicy($e->getModel());
$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 testEnforceExBasic()
{
$e = new Enforcer($this->modelAndPolicyPath . '/basic_model.conf', $this->modelAndPolicyPath . '/basic_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'data1', 'read'), [true, ['alice', 'data1', 'read']]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'write'), [true, ['bob', 'data2', 'write']]);
$this->assertEquals($e->enforceEx('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 testEnforceExBasicNoPolicy()
{
$e = new Enforcer($this->modelAndPolicyPath . '/basic_model.conf');
$this->assertEquals($e->enforceEx('alice', 'data1', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('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 testEnforceExBasicWithRoot()
{
$e = new Enforcer($this->modelAndPolicyPath . '/basic_with_root_model.conf', $this->modelAndPolicyPath . '/basic_policy.csv');
$this->assertEquals($e->enforceEx('root', 'any', 'any'), [true, ['alice', 'data1', 'read']]);
}
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 testEnforceExBasicWithRootNoPolicy()
{
$e = new Enforcer($this->modelAndPolicyPath . '/basic_with_root_model.conf');
$this->assertEquals($e->enforceEx('alice', 'data1', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('alice', 'data1', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data1', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data1', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('root', 'data1', 'read'), [true, []]);
$this->assertEquals($e->enforceEx('root', 'data1', 'write'), [true, []]);
$this->assertEquals($e->enforceEx('root', 'data2', 'read'), [true, []]);
$this->assertEquals($e->enforceEx('root', 'data2', 'write'), [true, []]);
}
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 testEnforceExBasicWithoutResources()
{
$e = new Enforcer($this->modelAndPolicyPath . '/basic_without_resources_model.conf', $this->modelAndPolicyPath . '/basic_without_resources_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'read'), [true, ['alice', 'read']]);
$this->assertEquals($e->enforceEx('alice', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'write'), [true, ['bob', 'write']]);
$this->assertEquals($e->enforceEx('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 testEnforceExBasicWithoutUsers()
{
$e = new Enforcer($this->modelAndPolicyPath . '/basic_without_users_model.conf', $this->modelAndPolicyPath . '/basic_without_users_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('alice', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', '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 testEnforceExIpMatch()
{
$e = new Enforcer($this->modelAndPolicyPath . '/ipmatch_model.conf', $this->modelAndPolicyPath . '/ipmatch_policy.csv');
$this->assertEquals($e->enforceEx('192.168.2.1', 'data1', 'read'), [true, ['192.168.2.0/24', 'data1', 'read']]);
$this->assertEquals($e->enforceEx('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 testEnforceExKeyMatch()
{
$e = new Enforcer($this->modelAndPolicyPath . '/keymatch_model.conf', $this->modelAndPolicyPath . '/keymatch_policy.csv');
$this->assertEquals($e->enforceEx('alice', '/alice_data/test', 'GET'), [true, ['alice', '/alice_data/*', 'GET']]);
$this->assertEquals($e->enforceEx('alice', '/bob_data/test', 'GET'), [false, []]);
$this->assertEquals($e->enforceEx('cathy', '/cathy_data', 'GET'), [true, ['cathy', '/cathy_data', '(GET)|(POST)']]);
$this->assertEquals($e->enforceEx('cathy', '/cathy_data', 'POST'), [true, ['cathy', '/cathy_data', '(GET)|(POST)']]);
$this->assertEquals($e->enforceEx('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 testEnforceExKeyMatch2()
{
$e = new Enforcer($this->modelAndPolicyPath . '/keymatch2_model.conf', $this->modelAndPolicyPath . '/keymatch2_policy.csv');
$this->assertEquals($e->enforceEx('alice', '/alice_data/resource', 'GET'), [true, ['alice', '/alice_data/:resource', 'GET']]);
$this->assertEquals($e->enforceEx('alice', '/alice_data2/123/using/456', 'GET'), [true, ['alice', '/alice_data2/:id/using/:resId', 'GET']]);
}
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 testEnforceExPriority()
{
$e = new Enforcer($this->modelAndPolicyPath . '/priority_model.conf', $this->modelAndPolicyPath . '/priority_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'data1', 'read'), [true, ['alice', 'data1', 'read', 'allow']]);
$this->assertEquals($e->enforceEx('alice', 'data1', 'write'), [false, ['data1_deny_group', 'data1', 'write', 'deny']]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data1', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data1', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'read'), [true, ['data2_allow_group', 'data2', 'read', 'allow']]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'write'), [false, ['bob', 'data2', 'write', 'deny']]);
}
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 testEnforceExPriorityIndeterminate()
{
$e = new Enforcer($this->modelAndPolicyPath . '/priority_model.conf', $this->modelAndPolicyPath . '/priority_indeterminate_policy.csv');
$this->assertEquals($e->enforceEx('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 testEnforceExRbac()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'data1', 'read'), [true, ['alice', 'data1', 'read']]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'write'), [true, ['bob', 'data2', 'write']]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'read'), [true, ['data2_admin', 'data2', 'read']]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'write'), [true, ['data2_admin', 'data2', 'write']]);
}
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 testEnforceExRbacWithDeny()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_deny_model.conf', $this->modelAndPolicyPath . '/rbac_with_deny_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'data1', 'read'), [true, ['alice', 'data1', 'read', 'allow']]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'write'), [true, ['bob', 'data2', 'write', 'allow']]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'read'), [true, ['data2_admin', 'data2', 'read', 'allow']]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'write'), [false, ['alice', 'data2', 'write', 'deny']]);
}
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 testEnforceExRbacWithDomains()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'domain1', 'data1', 'read'), [true, ['admin', 'domain1', 'data1', 'read']]);
$this->assertEquals($e->enforceEx('alice', 'domain1', 'data1', 'write'), [true, ['admin', 'domain1', 'data1', 'write']]);
$this->assertEquals($e->enforceEx('alice', 'domain1', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('alice', 'domain1', 'data2', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'domain2', 'data1', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'domain2', 'data1', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'domain2', 'data2', 'read'), [true, ['admin', 'domain2', 'data2', 'read']]);
$this->assertEquals($e->enforceEx('bob', 'domain2', 'data2', 'write'), [true, ['admin', 'domain2', 'data2', 'write']]);
}
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 testEnforceExRbacWithNotDeny()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_not_deny_model.conf', $this->modelAndPolicyPath . '/rbac_with_deny_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'data2', 'write'), [false, ['alice', 'data2', 'write', 'deny']]);
}
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);
}
public function testEnforceExRbacWithResourceRoles()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_resource_roles_model.conf', $this->modelAndPolicyPath . '/rbac_with_resource_roles_policy.csv');
$this->assertEquals($e->enforceEx('alice', 'data1', 'read'), [true, ['alice', 'data1', 'read']]);
$this->assertEquals($e->enforceEx('alice', 'data1', 'write'), [true, ['data_group_admin', 'data_group', 'write']]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('alice', 'data2', 'write'), [true, ['data_group_admin', 'data_group', 'write']]);
$this->assertEquals($e->enforceEx('bob', 'data1', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data1', 'write'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'read'), [false, []]);
$this->assertEquals($e->enforceEx('bob', 'data2', 'write'), [true, ['bob', 'data2', 'write']]);
}
public function testMultiplePolicyDefinitions()
{
$e = new Enforcer($this->modelAndPolicyPath . '/multiple_policy_definitions_model.conf', $this->modelAndPolicyPath . '/multiple_policy_definitions_policy.csv');
$enforceContext = new EnforceContext('2');
$enforceContext->eType = "e";
$this->assertEquals($e->enforce('alice', 'data2', 'read'), true);
$tmp = new \stdClass();
$tmp->Age = 70;
$this->assertEquals($e->enforce($enforceContext, $tmp, '/data1', 'read'), false);
$tmp->Age = 30;
$this->assertEquals($e->enforce($enforceContext, $tmp, '/data1', 'read'), true);
}
public function testMatcherUsingInOperatorBracket()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model_matcher_using_in_op_bracket.conf');
$e->addPermissionForUser('alice', 'data1', 'read');
$this->assertTrue($e->enforce('alice', 'data1', 'read'));
$this->assertTrue($e->enforce('alice', 'data2', 'read'));
$this->assertTrue($e->enforce('alice', 'data3', 'read'));
$this->assertFalse($e->enforce('anyone', 'data1', 'read'));
$this->assertTrue($e->enforce('anyone', 'data2', 'read'));
$this->assertTrue($e->enforce('anyone', 'data3', 'read'));
}
}
tests/Unit/CachedEnforcerTest.php 0000644 00000003317 15247527047 0013052 0 ustar 00 modelAndPolicyPath . '/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.php 0000644 00000004314 15247527047 0012627 0 ustar 00 getAndSetConfig($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.ini 0000644 00000001273 15247527047 0011532 0 ustar 00 # 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/CoreEnforcerTest.php 0000644 00000026723 15247527047 0012601 0 ustar 00 modelAndPolicyPath . '/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 testGetRoleManager()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf');
$rm = $e->getRoleManager();
$this->assertTrue($rm instanceof RoleManager);
}
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 testAppendFilteredPolicy()
{
$e = new Enforcer();
$adapter = new FileFilteredAdapter($this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$e->initWithAdapter($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $adapter);
$e->loadPolicy();
// validate initial conditions
$this->assertTrue($e->hasPolicy('admin', 'domain1', 'data1', 'read'));
$this->assertTrue($e->hasPolicy('admin', 'domain2', 'data2', 'read'));
$filter = new Filter();
$filter->p = ['', 'domain1'];
$filter->g = ['', '', 'domain1'];
$e->loadFilteredPolicy($filter);
$this->assertTrue($adapter->isFiltered());
// only policies for domain1 should be loaded
$this->assertTrue($e->hasPolicy('admin', 'domain1', 'data1', 'read'));
$this->assertFalse($e->hasPolicy('admin', 'domain2', 'data2', 'read'));
// disable clear policy and load second domain
$filter = new Filter();
$filter->p = ['', 'domain2'];
$filter->g = ['', '', 'domain2'];
$e->loadIncrementalFilteredPolicy($filter);
// both domain policies should be loaded
$this->assertTrue($e->hasPolicy('admin', 'domain1', 'data1', 'read'));
$this->assertTrue($e->hasPolicy('admin', 'domain2', 'data2', 'read'));
}
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'));
}
public function testPriorityExplicit()
{
$e = new Enforcer($this->modelAndPolicyPath . '/priority_model_explicit.conf', $this->modelAndPolicyPath . '/priority_policy_explicit.csv');
$this->assertTrue($e->enforce('alice', 'data1', 'write'));
$this->assertTrue($e->enforce('alice', 'data1', 'read'));
$this->assertFalse($e->enforce('bob', 'data2', 'read'));
$this->assertTrue($e->enforce('bob', 'data2', 'write'));
$this->assertFalse($e->enforce('data1_deny_group', 'data1', 'read'));
$this->assertFalse($e->enforce('data1_deny_group', 'data1', 'write'));
$this->assertTrue($e->enforce('data2_allow_group', 'data2', 'read'));
$this->assertTrue($e->enforce('data2_allow_group', 'data2', 'write'));
$e->addPolicy('1', 'bob', 'data2', 'write', 'deny');
$this->assertTrue($e->enforce('alice', 'data1', 'write'));
$this->assertTrue($e->enforce('alice', 'data1', 'read'));
$this->assertFalse($e->enforce('bob', 'data2', 'read'));
$this->assertFalse($e->enforce('bob', 'data2', 'write'));
$this->assertFalse($e->enforce('data1_deny_group', 'data1', 'read'));
$this->assertFalse($e->enforce('data1_deny_group', 'data1', 'write'));
$this->assertTrue($e->enforce('data2_allow_group', 'data2', 'read'));
$this->assertTrue($e->enforce('data2_allow_group', 'data2', 'write'));
}
}
tests/Unit/EnforcerTest.php 0000644 00000055713 15247527047 0011771 0 ustar 00 modelAndPolicyPath . '/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'), []);
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$this->assertEquals(['admin'], $e->getRolesForUser('alice', 'domain1'));
$this->assertEquals([], $e->getRolesForUser('bob', 'domain1'));
$this->assertEquals([], $e->getRolesForUser('admin', 'domain1'));
$this->assertEquals([], $e->getRolesForUser('non_exist', 'domain1'));
$this->assertEquals([], $e->getRolesForUser('alice', 'domain2'));
$this->assertEquals(['admin'], $e->getRolesForUser('bob', 'domain2'));
$this->assertEquals([], $e->getRolesForUser('admin', 'domain2'));
$this->assertEquals([], $e->getRolesForUser('non_exist', 'domain2'));
}
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'));
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$this->assertTrue($e->hasRoleForUser('alice', 'admin', 'domain1'));
$this->assertFalse($e->hasRoleForUser('alice', 'admin', 'domain2'));
}
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']);
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$this->assertTrue($e->hasRoleForUser('alice', 'admin', 'domain1'));
$this->assertFalse($e->hasRoleForUser('bob', 'admin', 'domain1'));
$e->deleteRoleForUser('alice', 'admin', 'domain1');
$e->addRoleForUser('bob', 'admin', 'domain1');
$this->assertEquals([], $e->getRolesForUser('alice', 'domain1'));
$this->assertEquals(['admin'], $e->getRolesForUser('bob', 'domain1'));
$this->assertEquals(['admin'], $e->getRolesForUser('bob', 'domain2'));
$this->assertEquals([], $e->getRolesForUser('non_exist', 'domain1'));
$this->assertEquals([], $e->getRolesForUser('non_exist', 'domain2'));
}
public function testAddRolesForUser()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$e->addRolesForUser('alice', ["data1_admin", "data2_admin", "data3_admin"]);
// The "alice" already has "data2_admin" , it will be return false. So "alice" just has "data2_admin".
$this->assertEquals(["data2_admin"], $e->getRolesForUser('alice'));
$e->deleteRoleForUser('alice', 'data2_admin');
$e->addRolesForUser('alice', ["data1_admin", "data2_admin", "data3_admin"]);
$this->assertEquals(["data1_admin", "data2_admin", "data3_admin"], $e->getRolesForUser('alice'));
$this->assertTrue($e->enforce('alice', 'data1', 'read'));
$this->assertTrue($e->enforce('alice', 'data2', 'read'));
$this->assertTrue($e->enforce('alice', 'data2', 'write'));
}
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']);
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$this->assertTrue($e->hasRoleForUser('alice', 'admin', 'domain1'));
$this->assertFalse($e->hasRoleForUser('bob', 'admin', 'domain1'));
$e->deleteRoleForUser('alice', 'admin', 'domain1');
$e->addRoleForUser('bob', 'admin', 'domain1');
$this->assertFalse($e->hasRoleForUser('alice', 'admin', 'domain1'));
$this->assertTrue($e->hasRoleForUser('bob', 'admin', 'domain1'));
}
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 testAddPermissionsForUser()
{
$e = new Enforcer($this->modelAndPolicyPath . '/basic_without_resources_model.conf', $this->modelAndPolicyPath . '/basic_without_resources_policy.csv');
$e->addPermissionsForUser('jack', ['read'], ['write']);
$this->assertTrue($e->enforce('jack', 'read'));
$this->assertTrue($e->enforce('jack', '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->getPermissionsForUser('alice'), [['alice', 'data1', 'read']]);
$this->assertEquals($e->getPermissionsForUser('bob'), [['bob', 'data2', 'write']]);
$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']);
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_pattern_model.conf', $this->modelAndPolicyPath . '/rbac_with_pattern_policy.csv');
$roleManager = $e->getRoleManager();
if ($roleManager instanceof RoleManager) {
$roleManager->addMatchingFunc('matcher', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch($key1, $key2);
});
}
$this->assertEquals($e->getImplicitRolesForUser('cathy'), ['/book/1/2/3/4/5', 'pen_admin']);
$this->assertEquals($e->getRolesForUser('cathy'), ['/book/1/2/3/4/5', 'pen_admin']);
}
public function testGetImplicitResourcesForUser()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_pattern_model.conf', $this->modelAndPolicyPath . '/rbac_with_pattern_policy.csv');
$this->assertEqualsCanonicalizing([
["alice", "/pen/1", "GET"],
["alice", "/pen2/1", "GET"],
["alice", "/book/:id", "GET"],
["alice", "/book2/{id}", "GET"],
["alice", "/book/*", "GET"],
["alice", "book_group", "GET"],
], $e->getImplicitResourcesForUser('alice'));
$this->assertEqualsCanonicalizing([
["bob", "pen_group", "GET"],
["bob", "/pen/:id", "GET"],
["bob", "/pen2/{id}", "GET"],
], $e->getImplicitResourcesForUser('bob'));
$this->assertEqualsCanonicalizing([
["cathy", "pen_group", "GET"],
["cathy", "/pen/:id", "GET"],
["cathy", "/pen2/{id}", "GET"],
], $e->getImplicitResourcesForUser('cathy'));
}
public function testImplicitUsersForRole()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_pattern_model.conf', $this->modelAndPolicyPath . '/rbac_with_pattern_policy.csv');
$this->assertEqualsCanonicalizing(['alice'], $e->getImplicitUsersForRole('book_admin'));
$this->assertEqualsCanonicalizing(['cathy', 'bob'], $e->getImplicitUsersForRole('pen_admin'));
$this->assertEqualsCanonicalizing(['/book/*', '/book/:id', '/book2/{id}'], $e->getImplicitUsersForRole('book_group'));
$this->assertEqualsCanonicalizing(['/pen/:id', '/pen2/{id}'], $e->getImplicitUsersForRole('pen_group'));
}
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']);
}
public function testGetUsersForRoleInDomain()
{
$e = new Enforcer($this->modelAndPolicyPath . '/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'), []);
}
public function testGetAllUsersByDomain()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$this->assertEquals(['alice', 'admin'], $e->getAllUsersByDomain('domain1'));
$this->assertEquals(['bob', 'admin'], $e->getAllUsersByDomain('domain2'));
}
public function testFailedToLoadPolicy()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_pattern_model.conf', $this->modelAndPolicyPath . '/rbac_with_pattern_policy.csv');
$e->addNamedMatchingFunc('g2', 'matchingFunc', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$this->assertTrue($e->enforce('alice', '/pen/1', 'GET'));
$this->assertTrue($e->enforce('alice', '/pen2/1', 'GET'));
$e->setAdapter(new FileAdapter('not found'));
$e->loadPolicy();
$this->assertTrue($e->enforce('alice', '/pen/1', 'GET'));
$this->assertTrue($e->enforce('alice', '/pen2/1', 'GET'));
}
public function testReloadPolicyWithFunc()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_pattern_model.conf', $this->modelAndPolicyPath . '/rbac_with_pattern_policy.csv');
$e->addNamedMatchingFunc('g2', 'matchingFunc', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$this->assertTrue($e->enforce('alice', '/pen/1', 'GET'));
$this->assertTrue($e->enforce('alice', '/pen2/1', 'GET'));
$e->loadPolicy();
$this->assertTrue($e->enforce('alice', '/pen/1', 'GET'));
$this->assertTrue($e->enforce('alice', '/pen2/1', 'GET'));
}
public function testBatchEnforce()
{
$e = new Enforcer($this->modelAndPolicyPath . '/basic_model.conf', $this->modelAndPolicyPath . '/basic_policy.csv');
$res = $e->batchEnforce([
['alice', 'data1', 'read'],
['bob', 'data2', 'write'],
['jack', 'data3', 'read']
]);
$this->assertEquals([true, true, false], $res);
}
public function testSubjectPriority()
{
$e = new Enforcer($this->modelAndPolicyPath . '/subject_priority_model.conf', $this->modelAndPolicyPath . '/subject_priority_policy.csv');
$this->assertTrue($e->enforce('jane', 'data1', 'read'));
$this->assertTrue($e->enforce('alice', 'data1', 'read'));
}
public function testSubjectPriorityWithDomain()
{
$e = new Enforcer($this->modelAndPolicyPath . '/subject_priority_model_with_domain.conf', $this->modelAndPolicyPath . '/subject_priority_policy_with_domain.csv');
$this->assertTrue($e->enforce('alice', 'data1', 'domain1', 'write'));
$this->assertTrue($e->enforce('bob', 'data2', 'domain2', 'write'));
}
public function testDeleteAllUsersByDomain()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$e->deleteAllUsersByDomain('domain1');
$this->assertEquals([
['admin', 'domain2', 'data2', 'read'],
['admin', 'domain2', 'data2', 'write'],
], $e->getPolicy());
$this->assertEquals([
['bob', 'admin', 'domain2']
], $e->getGroupingPolicy());
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$e->deleteAllUsersByDomain('domain2');
$this->assertEquals([
['admin', 'domain1', 'data1', 'read'],
['admin', 'domain1', 'data1', 'write'],
], $e->getPolicy());
$this->assertEquals([
['alice', 'admin', 'domain1']
], $e->getGroupingPolicy());
}
public function testDeleteDomains()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$e->deleteDomains();
$this->assertEquals([], $e->getPolicy());
$this->assertEquals([], $e->getGroupingPolicy());
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$e->deleteDomains('domain1');
$this->assertEquals([
['admin', 'domain2', 'data2', 'read'],
['admin', 'domain2', 'data2', 'write'],
], $e->getPolicy());
$this->assertEquals([
['bob', 'admin', 'domain2']
], $e->getGroupingPolicy());
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domains_model.conf', $this->modelAndPolicyPath . '/rbac_with_domains_policy.csv');
$e->deleteDomains('domain1', 'domain2');
$this->assertEquals([], $e->getPolicy());
$this->assertEquals([], $e->getGroupingPolicy());
}
}
tests/Unit/Log/LogTest.php 0000644 00000001110 15247527047 0011446 0 ustar 00 enableLog(true);
$enable = Log::getLogger()->isEnabled();
$this->assertTrue($enable);
Log::getLogger()->enableLog(false);
$enable = Log::getLogger()->isEnabled();
$this->assertFalse($enable);
}
}
tests/Unit/Log/Logger/DefaultLoggerTest.php 0000644 00000001470 15247527047 0014701 0 ustar 00 enableLog(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/ManagementEnforcerTest.php 0000644 00000037713 15247527047 0013766 0 ustar 00 modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$this->assertEquals(['alice', 'bob', 'data2_admin'], $e->getAllNamedSubjects('p'));
$this->assertEquals([], $e->getAllNamedSubjects('g'));
}
public function testGetAllNamedObjects()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$this->assertEquals(['data1', 'data2'], $e->getAllNamedObjects('p'));
$this->assertEquals([], $e->getAllNamedObjects('g'));
}
public function testGetAllNamedActions()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$this->assertEquals(['read', 'write'], $e->getAllNamedActions('p'));
}
public function testGetAllNamedRoles()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$this->assertEquals(['data2_admin'], $e->getAllNamedRoles('g'));
}
public function testGetList()
{
$e = new Enforcer($this->modelAndPolicyPath . '/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');
$rules = [
['jack', 'data4', 'read'],
['katy', 'data4', 'write'],
['leyo', 'data4', 'read'],
['ham', 'data4', 'write'],
];
$e->addPolicies($rules);
$e->addPolicies($rules);
$this->assertEquals([
['data2_admin', 'data2', 'read'],
['data2_admin', 'data2', 'write'],
['eve', 'data3', 'read'],
['jack', 'data4', 'read'],
['katy', 'data4', 'write'],
['leyo', 'data4', 'read'],
['ham', 'data4', 'write'],
], $e->getPolicy());
$e->removePolicies($rules);
$e->removePolicies($rules);
$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'],
]);
}
public function testModifyGroupingPolicyAPI()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$this->assertEquals($e->getRolesForUser('alice'), ['data2_admin']);
$this->assertEquals($e->getRolesForUser('bob'), []);
$this->assertEquals($e->getRolesForUser('env'), []);
$this->assertEquals($e->getRolesForUser('non_exist'), []);
$result = $e->removeGroupingPolicy('alice', 'data2_admin');
$e->addGroupingPolicy('bob', 'data1_admin');
$e->addGroupingPolicy('eve', 'data3_admin');
$groupingRules = [
['ham', 'data4_admin'],
['jack', 'data5_admin'],
];
$e->addGroupingPolicies($groupingRules);
$this->assertEquals($e->getRolesForUser('ham'), ['data4_admin']);
$this->assertEquals($e->getRolesForUser('jack'), ['data5_admin']);
$e->removeGroupingPolicies($groupingRules);
$this->assertEquals($e->getRolesForUser('alice'), []);
$nameGroupingPolicy = ['alice', 'data2_admin'];
$this->assertEquals($e->getRolesForUser('alice'), []);
$e->addNamedGroupingPolicy('g', $nameGroupingPolicy);
$this->assertEquals($e->getRolesForUser('alice'), ['data2_admin']);
$e->removeNamedGroupingPolicy('g', $nameGroupingPolicy);
$e->addNamedGroupingPolicies('g', $groupingRules);
$e->addNamedGroupingPolicies('g', $groupingRules);
$this->assertEquals($e->getRolesForUser('ham'), ['data4_admin']);
$this->assertEquals($e->getRolesForUser('jack'), ['data5_admin']);
$e->removeNamedGroupingPolicies('g', $groupingRules);
$e->removeNamedGroupingPolicies('g', $groupingRules);
$this->assertEquals($e->getRolesForUser('alice'), []);
$this->assertEquals($e->getRolesForUser('bob'), ['data1_admin']);
$this->assertEquals($e->getRolesForUser('eve'), ['data3_admin']);
$this->assertEquals($e->getRolesForUser('non_exist'), []);
$this->assertEquals($e->getUsersForRole('data1_admin'), ['bob']);
$this->assertEquals($e->getUsersForRole('data2_admin'), []);
$this->assertEquals($e->getUsersForRole('data3_admin'), ['eve']);
$e->removeFilteredGroupingPolicy(0, 'bob');
$this->assertEquals($e->getRolesForUser('alice'), []);
$this->assertEquals($e->getRolesForUser('bob'), []);
$this->assertEquals($e->getRolesForUser('eve'), ['data3_admin']);
$this->assertEquals($e->getRolesForUser('non_exist'), []);
$this->assertEquals($e->getUsersForRole('data1_admin'), []);
$this->assertEquals($e->getUsersForRole('data2_admin'), []);
$this->assertEquals($e->getUsersForRole('data3_admin'), ['eve']);
}
public function testUpdatePolicy()
{
// p, alice, data1, read
// p, bob, data2, write
// p, data2_admin, data2, read
// p, data2_admin, data2, write
//
// g, alice, data2_admin
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$this->assertTrue($e->hasPolicy('alice', 'data1', 'read'));
$this->assertFalse($e->hasPolicy('alice', 'data1', 'write'));
$e->updatePolicy(['alice', 'data1', 'read'], ['alice', 'data1', 'write']);
$this->assertFalse($e->hasPolicy('alice', 'data1', 'read'));
$this->assertTrue($e->hasPolicy('alice', 'data1', 'write'));
}
public function testUpdatePolicies()
{
// p, alice, data1, read
// p, bob, data2, write
// p, data2_admin, data2, read
// p, data2_admin, data2, write
//
// g, alice, data2_admin
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $this->modelAndPolicyPath . '/rbac_policy.csv');
$watcherUpdatable = new SampleWatcherUpdatable();
$e->setWatcher($watcherUpdatable);
$this->assertTrue($e->hasPolicy('alice', 'data1', 'read'));
$this->assertFalse($e->hasPolicy('alice', 'data1', 'write'));
$this->assertTrue($e->hasPolicy('bob', 'data2', 'write'));
$this->assertFalse($e->hasPolicy('bob', 'data2', 'read'));
$oldPolicies = [
['alice', 'data1', 'read'],
['bob', 'data2', 'write']
];
$newPolicies = [
['alice', 'data1', 'write'],
['bob', 'data2', 'read']
];
$e->updatePolicies($newPolicies, $oldPolicies);
$watcherUpdatable->setUpdateCallback(function () {
throw new \Exception('');
});
$e->updatePolicies($oldPolicies, $newPolicies);
$this->assertFalse($e->hasPolicy('alice', 'data1', 'read'));
$this->assertTrue($e->hasPolicy('alice', 'data1', 'write'));
$this->assertFalse($e->hasPolicy('bob', 'data2', 'write'));
$this->assertTrue($e->hasPolicy('bob', 'data2', 'read'));
$watcher = new SampleWatcher();
$e->setWatcher($watcher);
$watcher->setUpdateCallback(function () {
});
$e->updatePolicies($newPolicies, $oldPolicies);
}
public function testupdateFilteredPolicies()
{
// p, alice, data1, read
// p, bob, data2, write
// p, data2_admin, data2, read
// p, data2_admin, data2, write
//
// g, alice, data2_admin
$testAdapter = Mockery::mock(FileAdapter::class);
$model = Mockery::type("Casbin\Model\Model");
$testAdapter->shouldReceive('loadPolicy')->once()->with($model)->andReturn(null);
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $testAdapter);
$rules = [
['alice', 'data1', 'read'],
['bob', 'data2', 'write']
];
$testAdapter->shouldReceive('addPolicies')->once()->with('p', 'p', $rules)->andReturn(null);
$e->addPolicies($rules);
$watcherUpdatable = new SampleWatcherUpdatable();
$e->setWatcher($watcherUpdatable);
$watcherUpdatable->setUpdateCallback(function () {
});
$this->assertTrue($e->hasPolicy('alice', 'data1', 'read'));
$this->assertFalse($e->hasPolicy('alice', 'data1', 'write'));
$this->assertTrue($e->hasPolicy('bob', 'data2', 'write'));
$this->assertFalse($e->hasPolicy('bob', 'data2', 'read'));
$testAdapter->shouldReceive('updateFilteredPolicies')->once()->with('p', 'p', [['alice', 'data1', 'write']], 0, 'alice', 'data1', 'read')->andReturn([['alice', 'data1', 'read']]);
$e->updateFilteredPolicies([['alice', 'data1', 'write']], 0, 'alice', 'data1', 'read');
$testAdapter->shouldReceive('updateFilteredPolicies')->once()->with('p', 'p', [['bob', 'data2', 'read']], 0, 'bob', 'data2', 'write')->andReturn([['bob', 'data2', 'write']]);
$e->updateFilteredPolicies([['bob', 'data2', 'read']], 0, 'bob', 'data2', 'write');
$this->assertFalse($e->hasPolicy('alice', 'data1', 'read'));
$this->assertTrue($e->hasPolicy('alice', 'data1', 'write'));
$this->assertFalse($e->hasPolicy('bob', 'data2', 'write'));
$this->assertTrue($e->hasPolicy('bob', 'data2', 'read'));
$watcher = new SampleWatcher();
$e->setWatcher($watcher);
$watcher->setUpdateCallback(function () {
});
$testAdapter->shouldReceive('updateFilteredPolicies')->once()->with('p', 'p', [['alice', 'data1', 'read']], 0, 'alice', 'data1', 'write')->andReturn([['alice', 'data1', 'write']]);
$e->updateFilteredPolicies([['alice', 'data1', 'read']], 0, 'alice', 'data1', 'write');
$this->assertFalse($e->hasPolicy('alice', 'data1', 'write'));
$this->assertTrue($e->hasPolicy('alice', 'data1', 'read'));
}
public function testupdateFilteredPoliciesWithoutWatcher()
{
$testAdapter = Mockery::mock(FileAdapter::class);
$model = Mockery::type("Casbin\Model\Model");
$testAdapter->shouldReceive('loadPolicy')->once()->with($model)->andReturn(null);
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_model.conf', $testAdapter);
$rules = [
['alice', 'data1', 'read'],
];
$testAdapter->shouldReceive('addPolicies')->once()->with('p', 'p', $rules)->andReturn(null);
$e->addPolicies($rules);
$this->assertTrue($e->hasPolicy('alice', 'data1', 'read'));
$this->assertFalse($e->hasPolicy('alice', 'data1', 'write'));
// throw exception
$testAdapter->shouldReceive('updateFilteredPolicies')->once()->with('p', 'p', [['alice', 'data1', 'write']], 0, 'alice', 'data1', 'read')->andReturn([['alice', 'data1', 'read']])->andThrow(new NotImplementedException());
$e->updateFilteredPolicies([['alice', 'data1', 'write']], 0, 'alice', 'data1', 'read');
$testAdapter->shouldReceive('updateFilteredPolicies')->once()->with('p', 'p', [['alice', 'data1', 'write']], 0, 'alice', 'data1', 'read')->andReturn([['alice', 'data1', 'read']]);
$e->updateFilteredPolicies([['alice', 'data1', 'write']], 0, 'alice', 'data1', 'read');
// if $ruleChanged is 0
$testAdapter->shouldReceive('updateFilteredPolicies')->once()->with('p', 'p', [], 0, 'alice', 'data1', 'read')->andReturn([['alice', 'data1', 'read']]);
$e->updateFilteredPolicies([], 0, 'alice', 'data1', 'read');
$this->assertFalse($e->hasPolicy('alice', 'data1', 'read'));
$this->assertTrue($e->hasPolicy('alice', 'data1', 'write'));
}
}
tests/Unit/Model/ModelTest.php 0000644 00000016455 15247527047 0012326 0 ustar 00 loadModelFromText($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'));
}
public function testABACPolicy()
{
$e = new Enforcer($this->modelAndPolicyPath . '/abac_rule_model.conf', $this->modelAndPolicyPath . '/abac_rule_policy.csv');
$sub1 = new User('alice', 18);
$sub2 = new User('alice', 20);
$sub3 = new User('alice', 65);
$this->assertEquals($e->enforce($sub1, '/data1', 'read'), false);
$this->assertEquals($e->enforce($sub1, '/data2', 'read'), false);
$this->assertEquals($e->enforce($sub1, '/data1', 'write'), false);
$this->assertEquals($e->enforce($sub1, '/data2', 'write'), true);
$this->assertEquals($e->enforce($sub2, '/data1', 'read'), true);
$this->assertEquals($e->enforce($sub2, '/data2', 'read'), false);
$this->assertEquals($e->enforce($sub2, '/data1', 'write'), false);
$this->assertEquals($e->enforce($sub2, '/data2', 'write'), true);
$this->assertEquals($e->enforce($sub3, '/data1', 'read'), true);
$this->assertEquals($e->enforce($sub3, '/data2', 'read'), false);
$this->assertEquals($e->enforce($sub3, '/data1', 'write'), false);
$this->assertEquals($e->enforce($sub3, '/data2', 'write'), false);
}
public function testEvalFunctionException()
{
$this->expectException(EvalFunctionException::class);
$this->expectExceptionMessage("please make sure rule exists in policy when using eval() in matcher");
$e = new Enforcer($this->modelAndPolicyPath . '/abac_rule_model.conf', "");
$sub1 = new User('alice', 18);
$e->enforce($sub1, '/data1', 'read');
}
public function testRBACModelWithPattern()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_pattern_model.conf', $this->modelAndPolicyPath . '/rbac_with_pattern_policy.csv');
// Here's a little confusing: the matching function here is not the custom function used in matcher.
// It is the matching function used by "g" (and "g2", "g3" if any..)
// You can see in policy that: "g2, /book/:id, book_group", so in "g2()" function in the matcher, instead
// of checking whether "/book/:id" equals the obj: "/book/1", it checks whether the pattern matches.
// You can see it as normal RBAC: "/book/:id" == "/book/1" becomes KeyMatch2("/book/:id", "/book/1")
$e->addNamedMatchingFunc('g2', 'keyMatch2', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$this->assertEquals($e->enforce('alice', '/book/1', 'GET'), true);
$this->assertEquals($e->enforce('alice', '/book/2', 'GET'), true);
$this->assertEquals($e->enforce('alice', '/pen/1', 'GET'), true);
$this->assertEquals($e->enforce('alice', '/pen/2', 'GET'), false);
$this->assertEquals($e->enforce('bob', '/book/1', 'GET'), false);
$this->assertEquals($e->enforce('bob', '/book/2', 'GET'), false);
$this->assertEquals($e->enforce('bob', '/pen/1', 'GET'), true);
$this->assertEquals($e->enforce('bob', '/pen/2', 'GET'), true);
// AddMatchingFunc() is actually setting a function because only one function is allowed,
// so when we set "KeyMatch3", we are actually replacing "KeyMatch2" with "KeyMatch3".
$e->addNamedMatchingFunc('g2', 'keyMatch2', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch3($key1, $key2);
});
$this->assertEquals($e->enforce('alice', '/book2/1', 'GET'), true);
$this->assertEquals($e->enforce('alice', '/book2/2', 'GET'), true);
$this->assertEquals($e->enforce('alice', '/pen2/1', 'GET'), true);
$this->assertEquals($e->enforce('alice', '/pen2/2', 'GET'), false);
$this->assertEquals($e->enforce('bob', '/book2/1', 'GET'), false);
$this->assertEquals($e->enforce('bob', '/book2/2', 'GET'), false);
$this->assertEquals($e->enforce('bob', '/pen2/1', 'GET'), true);
$this->assertEquals($e->enforce('bob', '/pen2/2', 'GET'), true);
}
public function testDomainMatchModel()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_domain_pattern_model.conf', $this->modelAndPolicyPath . '/rbac_with_domain_pattern_policy.csv');
$e->addNamedDomainMatchingFunc('g', 'keyMatch2', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$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('alice', 'domain2', 'data2', 'read'), true);
$this->assertEquals($e->enforce('alice', 'domain2', 'data2', 'write'), true);
$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 testAllMatchModel()
{
$e = new Enforcer($this->modelAndPolicyPath . '/rbac_with_all_pattern_model.conf', $this->modelAndPolicyPath . '/rbac_with_all_pattern_policy.csv');
$e->addNamedMatchingFunc('g', 'keyMatch2', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$e->addNamedDomainMatchingFunc('g', 'keyMatch2', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$this->assertEquals($e->enforce('alice', 'domain1', '/book/1', 'read'), true);
$this->assertEquals($e->enforce('alice', 'domain1', '/book/1', 'write'), false);
$this->assertEquals($e->enforce('alice', 'domain2', '/book/1', 'read'), false);
$this->assertEquals($e->enforce('alice', 'domain2', '/book/1', 'write'), true);
}
}
tests/Unit/Model/PolicyTest.php 0000644 00000013321 15247527047 0012512 0 ustar 00 loadModel($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 testHasPolicies()
{
$m = Model::newModelFromFile($this->modelAndPolicyPath . '/basic_model.conf');
$rules = [
['alice', 'domain1', 'data1', 'read'],
['alice', 'domain1', 'data2', 'read'],
['bob', 'domain2', 'data1', 'write'],
['bob', 'domain2', 'data2', 'write'],
];
$m->addPolicies('p', 'p', $rules);
$this->assertTrue($m->hasPolicies('p', 'p', [
['alice', 'domain1', 'data1', 'read'],
['bob', 'domain2', 'data1', 'write'],
]));
$this->assertFalse($m->hasPolicies('p', 'p', [
['alice', 'domain1', 'data1', 'write'],
]));
}
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 testUpdatePolicy()
{
$m = Model::newModelFromFile($this->modelAndPolicyPath . '/basic_model.conf');
$rules = [
['alice', 'domain1', 'data1', 'read'],
['alice', 'domain1', 'data2', 'read'],
['bob', 'domain2', 'data1', 'write'],
['bob', 'domain2', 'data2', 'write'],
];
$m->addPolicies('p', 'p', $rules);
$this->assertEquals($rules, $m->getPolicy('p', 'p'));
$this->assertFalse($m->hasPolicies('p', 'p', [
['alice', 'domain1', 'data1', 'write'],
]));
$m->updatePolicy('p', 'p', ['alice', 'domain1', 'data1', 'read'], ['alice', 'domain1', 'data1', 'write']);
$this->assertEquals([
['alice', 'domain1', 'data1', 'write'],
['alice', 'domain1', 'data2', 'read'],
['bob', 'domain2', 'data1', 'write'],
['bob', 'domain2', 'data2', 'write'],
], $m->getPolicy('p', 'p'));
}
public function testUpdatePolicies()
{
$m = Model::newModelFromFile($this->modelAndPolicyPath . '/basic_model.conf');
$rules = [
['alice', 'domain1', 'data1', 'read'],
['alice', 'domain1', 'data2', 'read'],
['bob', 'domain2', 'data1', 'write'],
['bob', 'domain2', 'data2', 'write'],
];
$m->addPolicies('p', 'p', $rules);
$this->assertEquals($rules, $m->getPolicy('p', 'p'));
$this->assertFalse($m->hasPolicies('p', 'p', [
['alice', 'domain1', 'data1', 'write'],
]));
$oldRules = [
['alice', 'domain1', 'data1', 'read'],
['alice', 'domain1', 'data2', 'read']
];
$newRules = [
['alice', 'domain1', 'data1', 'write'],
['alice', 'domain1', 'data2', 'write']
];
$m->updatePolicies('p', 'p', $oldRules, $newRules);
$this->assertEquals([
['alice', 'domain1', 'data1', 'write'],
['alice', 'domain1', 'data2', 'write'],
['bob', 'domain2', 'data1', 'write'],
['bob', 'domain2', 'data2', 'write'],
], $m->getPolicy('p', 'p'));
// trigger callback of addPolicies
$oldRules = [
['alice', 'domain1', 'data1', 'write'],
['alice', 'domain1', 'data2', 'read']
];
$this->assertFalse($m->updatePolicies('p', 'p', $oldRules, $newRules));
}
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('p1', 'p1', 1, 'domain1', 'data1');
$this->assertFalse($res);
$res = $m->removeFilteredPolicy('p', 'p', 1, 'domain1', 'data1');
$this->assertNotFalse($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/Model/User.php 0000644 00000000436 15247527047 0011334 0 ustar 00 Name = $Name;
$this->Age = $Age;
}
}
tests/Unit/Persist/Adapters/FileAdapterTest.php 0000644 00000001326 15247527047 0015571 0 ustar 00 loadModel($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.php 0000755 00000003772 15247527047 0017262 0 ustar 00 modelAndPolicyPath . '/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.csv 0000644 00000000037 15247527047 0016252 0 ustar 00 p, admin, domain1, data1, read2 tests/Unit/Persist/Adapters/rbac_policy_test.csv 0000644 00000000000 15247527047 0016066 0 ustar 00 tests/Unit/Rbac/DefaultRoleManager/RoleManagerTest.php 0000644 00000012363 15247527047 0016764 0 ustar 00 addLink('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']);
}
public function testDomainPatternRole()
{
$rm = new RoleManager(10);
$rm->addDomainMatchingFunc('keyMatch2', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$rm->addLink('u1', 'g1', 'domain1');
$rm->addLink('u2', 'g1', 'domain2');
$rm->addLink('u3', 'g1', '*');
$rm->addLink('u4', 'g2', 'domain3');
// Current role inheritance tree after deleting the links:
// domain1:g1 domain2:g1 domain3:g2
// / \ / \ |
// domain1:u1 *:g1 domain2:u2 domain3:u4
// |
// *:u3
$this->assertEquals($rm->hasLink('u1', 'g1', 'domain1'), true);
$this->assertEquals($rm->hasLink('u2', 'g1', 'domain1'), false);
$this->assertEquals($rm->hasLink('u2', 'g1', 'domain2'), true);
$this->assertEquals($rm->hasLink('u3', 'g1', 'domain1'), true);
$this->assertEquals($rm->hasLink('u3', 'g1', 'domain2'), true);
$this->assertEquals($rm->hasLink('u1', 'g2', 'domain1'), false);
$this->assertEquals($rm->hasLink('u4', 'g2', 'domain3'), true);
$this->assertEquals($rm->hasLink('u3', 'g2', 'domain3'), false);
$this->assertEquals($rm->getRoles('u3', 'domain1'), ['g1']);
$this->assertEquals($rm->getRoles('u1', 'domain1'), ['g1']);
$this->assertEquals($rm->getRoles('u3', 'domain2'), ['g1']);
$this->assertEquals($rm->getRoles('u1', 'domain2'), []);
$this->assertEquals($rm->getRoles('u4', 'domain3'), ['g2']);
}
public function testAllMatchingFunc()
{
$rm = new RoleManager(10);
$rm->addMatchingFunc('keyMatch2', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$rm->addDomainMatchingFunc('keyMatch2', function (string $key1, string $key2) {
return BuiltinOperations::keyMatch2($key1, $key2);
});
$rm->addLink('/book/:id', 'book_group', '*');
// Current role inheritance tree after deleting the links:
// *:book_group
// |
// *:/book/:id
$this->assertEquals($rm->hasLink('/book/1', 'book_group', 'domain1'), true);
$this->assertEquals($rm->hasLink('/book/2', 'book_group', 'domain1'), true);
}
public function testMatchingFuncOrder()
{
$rm = new RoleManager(10);
$rm->addMatchingFunc('regexMatch', function (string $key1, string $key2) {
return BuiltinOperations::regexMatch($key1, $key2);
});
$rm->addLink('g\\d+', 'root');
$rm->addLink('u1', 'g1');
$this->assertEquals($rm->hasLink('u1', 'root'), true);
$rm->clear();
$rm->AddLink('u1', 'g1');
$rm->AddLink('g\\d+', 'root');
$this->assertEquals($rm->hasLink('u1', 'root'), true);
$rm->clear();
$rm->AddLink('u1', 'g\\d+');
$this->assertEquals($rm->hasLink('u1', 'g1'), true);
$this->assertEquals($rm->hasLink('u1', 'g1'), true);
}
}
tests/Unit/Util/BuiltinOperationsTest.php 0000644 00000025004 15247527047 0014603 0 ustar 00 assertTrue($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'));
$this->assertFalse($this->keyMatch2Func('/alice/all', '/:/all'));
}
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}/*'));
$this->assertFalse($this->keyMatch3Func('/myid/using/myresid', '/{id/using/{resId}'));
}
public function testKeyMatch4Func()
{
$this->assertTrue($this->keyMatch4Func('/parent/123/child/123', '/parent/{id}/child/{id}'));
$this->assertFalse($this->keyMatch4Func('/parent/123/child/456', '/parent/{id}/child/{id}'));
$this->assertTrue($this->keyMatch4Func('/parent/123/child/123', '/parent/{id}/child/{another_id}'));
$this->assertTrue($this->keyMatch4Func('/parent/123/child/456', '/parent/{id}/child/{another_id}'));
$this->assertTrue($this->keyMatch4Func('/parent/123/child/123/book/123', '/parent/{id}/child/{id}/book/{id}'));
$this->assertFalse($this->keyMatch4Func('/parent/123/child/123/book/456', '/parent/{id}/child/{id}/book/{id}'));
$this->assertFalse($this->keyMatch4Func('/parent/123/child/456/book/123', '/parent/{id}/child/{id}/book/{id}'));
$this->assertFalse($this->keyMatch4Func('/parent/123/child/456/book/', '/parent/{id}/child/{id}/book/{id}'));
$this->assertFalse($this->keyMatch4Func('/parent/123/child/456', '/parent/{id}/child/{id}/book/{id}'));
$this->assertFalse($this->keyMatch4Func('/parent/123/child/123', '/parent/{i/d}/child/{i/d}'));
}
public function testKeyMatch5Func()
{
$this->assertTrue($this->keyMatch5Func('/parent/child', '/parent/child'));
$this->assertTrue($this->keyMatch5Func('/parent/child?status=1&type=2', '/parent/child'));
$this->assertFalse($this->keyMatch5Func('/parent?status=1&type=2', '/parent/child'));
$this->assertTrue($this->keyMatch5Func('/parent/child/?status=1&type=2', '/parent/child/'));
$this->assertFalse($this->keyMatch5Func('/parent/child/?status=1&type=2', '/parent/child'));
$this->assertFalse($this->keyMatch5Func('/parent/child?status=1&type=2', '/parent/child/'));
}
public function testGlobMatchFunc()
{
$this->assertTrue($this->globMatchFunc('/foo', '/foo'));
$this->assertTrue($this->globMatchFunc('/foo', '/foo*'));
$this->assertFalse($this->globMatchFunc('/foo', '/foo/*'));
$this->assertFalse($this->globMatchFunc('/prefix/foo', '*/foo'));
$this->assertTrue($this->globMatchFunc('/foo/bar', '/foo/*'));
}
public function testKeyGetFunc()
{
$this->assertEquals('', $this->keyGetFunc('/foo', '/foo'));
$this->assertEquals('', $this->keyGetFunc('/foo', '/foo*'));
$this->assertEquals('', $this->keyGetFunc('/foo', '/foo/*'));
$this->assertEquals('', $this->keyGetFunc('/foo/bar', '/foo'));
$this->assertEquals('/bar', $this->keyGetFunc('/foo/bar', '/foo*'));
$this->assertEquals('bar', $this->keyGetFunc('/foo/bar', '/foo/*'));
$this->assertEquals('', $this->keyGetFunc('/foobar', '/foo'));
$this->assertEquals('bar', $this->keyGetFunc('/foobar', '/foo*'));
$this->assertEquals('', $this->keyGetFunc('/foobar', '/foo/*'));
}
public function testKeyGet2Func()
{
$this->assertEquals('', $this->keyGet2Func("/foo", "/foo", "id"));
$this->assertEquals('', $this->keyGet2Func("/foo", "/foo*", "id"));
$this->assertEquals('', $this->keyGet2Func("/foo", "/foo/*", "id"));
$this->assertEquals('', $this->keyGet2Func("/foo/bar", "/foo", "id"));
// different with KeyMatch.
$this->assertEquals('', $this->keyGet2Func("/foo/bar", "/foo*", "id"));
$this->assertEquals('', $this->keyGet2Func("/foo/bar", "/foo/*", "id"));
$this->assertEquals('', $this->keyGet2Func("/foobar", "/foo", "id"));
// different with KeyMatch.
$this->assertEquals('', $this->keyGet2Func("/foobar", "/foo*", "id"));
$this->assertEquals('', $this->keyGet2Func("/foobar", "/foo/*", "id"));
$this->assertEquals('', $this->keyGet2Func("/", "/:resource", "resource"));
$this->assertEquals('resource1', $this->keyGet2Func("/resource1", "/:resource", "resource"));
$this->assertEquals('', $this->keyGet2Func("/myid", "/:id/using/:resId", "id"));
$this->assertEquals('myid', $this->keyGet2Func("/myid/using/myresid", "/:id/using/:resId", "id"));
$this->assertEquals('myresid', $this->keyGet2Func("/myid/using/myresid", "/:id/using/:resId", "resId"));
$this->assertEquals('', $this->keyGet2Func("/proxy/myid", "/proxy/:id/*", "id"));
$this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/", "/proxy/:id/*", "id"));
$this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/res", "/proxy/:id/*", "id"));
$this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/res/res2", "/proxy/:id/*", "id"));
$this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/res/res2/res3", "/proxy/:id/*", "id"));
$this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/res/res2/res3", "/proxy/:id/res/*", "id"));
$this->assertEquals('', $this->keyGet2Func('/proxy/', "/proxy/:id/*", "id"));
$this->assertEquals('alice', $this->keyGet2Func("/alice", "/:id", "id"));
$this->assertEquals('alice', $this->keyGet2Func("/alice/all", "/:id/all", "id"));
$this->assertEquals('', $this->keyGet2Func("/alice", "/:id/all", "id"));
$this->assertEquals('', $this->keyGet2Func("/alice/all", "/:id", "id"));
$this->assertEquals('', $this->keyGet2Func("/alice/all", "/:/all", ""));
}
}
tests/Unit/Util/UtilTest.php 0000644 00000011143 15247527047 0012045 0 ustar 00 assertEquals(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']);
}
public function testContainEval()
{
$this->assertEquals(Util::hasEval('eval() && a && b &&c'), true);
$this->assertEquals(Util::hasEval('eval) && a && b &&c'), false);
$this->assertEquals(Util::hasEval('eval)( && a && b &&c'), false);
$this->assertEquals(Util::hasEval('eval() && a && b &&c'), true);
$this->assertEquals(Util::hasEval('eval(c * (a + b)) && a && b &&c'), true);
$this->assertEquals(Util::hasEval('xeval() && a && b &&c'), false);
}
public function testReplaceEval()
{
$this->assertEquals(Util::replaceEval('eval() && a && b && c', 'a'), '(a) && a && b && c');
$this->assertEquals(Util::replaceEval('eval() && a && b && c', '(a)'), '((a)) && a && b && c');
}
public function testGetEvalValue()
{
$this->assertEquals(Util::getEvalValue('eval(a) && a && b && c'), ['a']);
$this->assertEquals(Util::getEvalValue('a && eval(a) && b && c'), ['a']);
$this->assertEquals(Util::getEvalValue('eval(a) && eval(b) && a && b && c'), ['a', 'b']);
$this->assertEquals(Util::getEvalValue('a && eval(a) && eval(b) && b && c'), ['a', 'b']);
}
public function testReplaceEvalWithMap()
{
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1)', ['rule1' => 'a == b']), 'a == b');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) && c && d', ['rule1' => 'a == b']), 'a == b && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1)', []), 'eval(rule1)');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) && c && d', []), 'eval(rule1) && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2)', ['rule1' => 'a == b', 'rule2' => 'a == c']), 'a == b || a == c');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2) && c && d', ['rule1' => 'a == b', 'rule2' => 'a == c']), 'a == b || a == c && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2)', ['rule1' => 'a == b']), 'a == b || eval(rule2)');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2) && c && d', ['rule1' => 'a == b']), 'a == b || eval(rule2) && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2)', ['rule2' => 'a == b']), 'eval(rule1) || a == b');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2) && c && d', ['rule2' => 'a == b']), 'eval(rule1) || a == b && c && d');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2)', []), 'eval(rule1) || eval(rule2)');
$this->assertEquals(Util::replaceEvalWithMap('eval(rule1) || eval(rule2) && c && d', []), 'eval(rule1) || eval(rule2) && c && d');
}
}
tests/Watcher/SampleWatcher.php 0000644 00000001740 15247527047 0012572 0 ustar 00 callback = $func;
}
/**
* update calls the update callback of other instances to synchronize their policy.
* It is usually called after changing the policy in DB, like savePolicy() method of Enforcer class,
* addPolicy(), removePolicy(), etc.
*/
public function update(): void
{
call_user_func($this->callback);
}
/**
* Close stops and releases the watcher, the callback function will not be called any more.
*/
public function close(): void
{
}
}
tests/Watcher/SampleWatcherEx.php 0000644 00000003655 15247527047 0013076 0 ustar 00 callback);
}
/**
* updateForRemovePolicy calls the update callback of other instances to synchronize their policy.
* It is called after removePolicy() method of Enforcer class
*
* @param string $sec
* @param string $ptype
* @param string ...$params
* @return void
*/
public function updateForRemovePolicy(string $sec, string $ptype, string ...$params): void
{
call_user_func($this->callback);
}
/**
* updateForRemoveFilteredPolicy calls the update callback of other instances to synchronize their policy.
* It is called after removeFilteredNamedGroupingPolicy() method of Enforcer class
*
* @param string $sec
* @param string $ptype
* @param integer $fieldIndex
* @param string ...$fieldValues
* @return void
*/
public function updateForRemoveFilteredPolicy(string $sec, string $ptype, int $fieldIndex, string ...$fieldValues): void
{
call_user_func($this->callback);
}
/**
* updateForSavePolicy calls the update callback of other instances to synchronize their policy.
* It is called after removeFilteredNamedGroupingPolicy() method of Enforcer class
*
* @param Model $model
* @return void
*/
public function updateForSavePolicy(Model $model): void
{
call_user_func($this->callback);
}
}
tests/Watcher/SampleWatcherUpdatable.php 0000644 00000001756 15247527047 0014423 0 ustar 00 callback);
}
/**
* updateForUpdatePolicies calls the update callback of other instances to synchronize their policy.
* It is called after updatePolicies() method of Enforcer class
*
* @param array $oldRules
* @param array $newRules
* @return void
*/
public function updateForUpdatePolicies(array $oldRules, array $newRules): void
{
call_user_func($this->callback);
}
}
tests/Watcher/WatcherExTest.php 0000644 00000003334 15247527047 0012566 0 ustar 00 isCalled = false;
$this->watcher = new SampleWatcherEx();
$this->enforcer = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv");
$this->enforcer->setWatcher($this->watcher);
}
public function testUpdateForSavePolicy()
{
$this->initWatcher();
$this->watcher->setUpdateCallback(function () {
$this->isCalled = true;
});
$this->watcher->updateForSavePolicy(new Model());
$this->assertTrue($this->isCalled);
}
public function testUpdateForAddPolicy()
{
$this->initWatcher();
$this->watcher->setUpdateCallback(function () {
$this->isCalled = true;
});
$this->watcher->updateForAddPolicy('p', 'p');
$this->assertTrue($this->isCalled);
}
public function testUpdateForRemovePolicy()
{
$this->initWatcher();
$this->watcher->setUpdateCallback(function () {
$this->isCalled = true;
});
$this->watcher->updateForRemovePolicy('p', 'p');
$this->assertTrue($this->isCalled);
}
public function testUpdateForRemoveFilteredPolicy()
{
$this->initWatcher();
$this->watcher->setUpdateCallback(function () {
$this->isCalled = true;
});
$this->watcher->updateForRemoveFilteredPolicy('p', 'p', 1);
$this->assertTrue($this->isCalled);
}
}
tests/Watcher/WatcherTest.php 0000644 00000001445 15247527047 0012272 0 ustar 00 isCalled = false;
$this->watcher = new SampleWatcher();
$this->enforcer = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv");
$this->enforcer->setWatcher($this->watcher);
}
public function testUpdate()
{
$this->initWatcher();
$this->watcher->setUpdateCallback(function () {
$this->isCalled = true;
});
$this->watcher->update();
$this->assertTrue($this->isCalled);
}
}
tests/Watcher/WatcherUpdatableTest.php 0000644 00000002166 15247527047 0014115 0 ustar 00 isCalled = false;
$this->watcher = new SampleWatcherUpdatable();
$this->enforcer = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv");
$this->enforcer->setWatcher($this->watcher);
}
public function testUpdateForUpdatePolicy()
{
$this->initWatcher();
$this->watcher->setUpdateCallback(function () {
$this->isCalled = true;
});
$this->watcher->updateForUpdatePolicy([], []);
$this->assertTrue($this->isCalled);
}
public function testUpdateForUpdatePolicies()
{
$this->initWatcher();
$this->watcher->setUpdateCallback(function () {
$this->isCalled = true;
});
$this->watcher->updateForUpdatePolicies([], []);
$this->assertTrue($this->isCalled);
}
}