first commit
This commit is contained in:
commit
291d3241aa
|
@ -0,0 +1,6 @@
|
||||||
|
build
|
||||||
|
vendor
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
.phpunit*
|
||||||
|
composer.lock
|
|
@ -0,0 +1,72 @@
|
||||||
|
language: php
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
|
services:
|
||||||
|
- mysql
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
include:
|
||||||
|
# Laravel 5.1
|
||||||
|
- php: 5.6
|
||||||
|
env: LARAVEL=5.1.* PHPUNIT=^5.7
|
||||||
|
- php: 7.0
|
||||||
|
env: LARAVEL=5.1.* PHPUNIT=^5.7
|
||||||
|
- php: 7.1
|
||||||
|
env: LARAVEL=5.1.* PHPUNIT=^5.7
|
||||||
|
|
||||||
|
# Laravel 5.5
|
||||||
|
- php: 7.0
|
||||||
|
env: LARAVEL=5.5.* PHPUNIT=~6.0
|
||||||
|
- php: 7.1
|
||||||
|
env: LARAVEL=5.5.* PHPUNIT=~6.0
|
||||||
|
- php: 7.2
|
||||||
|
env: LARAVEL=5.5.* PHPUNIT=~6.0
|
||||||
|
- php: 7.3
|
||||||
|
env: LARAVEL=5.5.* PHPUNIT=~6.0
|
||||||
|
|
||||||
|
# Laravel 5.6
|
||||||
|
- php: 7.1
|
||||||
|
env: LARAVEL=5.6.* PHPUNIT=~7.0
|
||||||
|
- php: 7.2
|
||||||
|
env: LARAVEL=5.6.* PHPUNIT=~7.0
|
||||||
|
- php: 7.3
|
||||||
|
env: LARAVEL=5.6.* PHPUNIT=~7.0
|
||||||
|
|
||||||
|
# Laravel 5.7
|
||||||
|
- php: 7.1
|
||||||
|
env: LARAVEL=5.7.* PHPUNIT=~7.5
|
||||||
|
- php: 7.2
|
||||||
|
env: LARAVEL=5.7.* PHPUNIT=~7.5
|
||||||
|
- php: 7.3
|
||||||
|
env: LARAVEL=5.7.* PHPUNIT=~7.5
|
||||||
|
|
||||||
|
# Laravel 5.8
|
||||||
|
- php: 7.1
|
||||||
|
env: LARAVEL=5.8.* PHPUNIT=~7.5
|
||||||
|
- php: 7.2
|
||||||
|
env: LARAVEL=5.8.* PHPUNIT=~8.0
|
||||||
|
- php: 7.3
|
||||||
|
env: LARAVEL=5.8.* PHPUNIT=~8.0
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- travis_retry composer self-update
|
||||||
|
- travis_retry composer require laravel/framework:$LARAVEL --no-update --no-interaction
|
||||||
|
- travis_retry composer require laravel/laravel:$LARAVEL phpunit/phpunit:$PHPUNIT --no-update --no-interaction --dev
|
||||||
|
- mysql -e 'create database if not exists lauthz;'
|
||||||
|
|
||||||
|
install:
|
||||||
|
- travis_retry composer install --no-suggest --no-interaction
|
||||||
|
|
||||||
|
script:
|
||||||
|
- vendor/bin/phpunit --version
|
||||||
|
- mkdir -p build/logs
|
||||||
|
- vendor/bin/phpunit
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- travis_retry vendor/bin/php-coveralls -v
|
|
@ -0,0 +1,201 @@
|
||||||
|
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.
|
|
@ -0,0 +1,327 @@
|
||||||
|
<h1 align="center">
|
||||||
|
Laravel Authorization
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<strong>Laravel-authz is an authorization library for the laravel framework.</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://travis-ci.org/php-casbin/laravel-authz">
|
||||||
|
<img src="https://travis-ci.org/php-casbin/laravel-authz.svg?branch=master" alt="Build Status">
|
||||||
|
</a>
|
||||||
|
<a href="https://coveralls.io/github/php-casbin/laravel-authz">
|
||||||
|
<img src="https://coveralls.io/repos/github/php-casbin/laravel-authz/badge.svg" alt="Coverage Status">
|
||||||
|
</a>
|
||||||
|
<a href="https://packagist.org/packages/casbin/laravel-authz">
|
||||||
|
<img src="https://poser.pugx.org/casbin/laravel-authz/v/stable" alt="Latest Stable Version">
|
||||||
|
</a>
|
||||||
|
<a href="https://packagist.org/packages/casbin/laravel-authz">
|
||||||
|
<img src="https://poser.pugx.org/casbin/laravel-authz/downloads" alt="Total Downloads">
|
||||||
|
</a>
|
||||||
|
<a href="https://packagist.org/packages/casbin/laravel-authz">
|
||||||
|
<img src="https://poser.pugx.org/casbin/laravel-authz/license" alt="License">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
It's based on [Casbin](https://github.com/php-casbin/php-casbin), an authorization library that supports access control models like ACL, RBAC, ABAC.
|
||||||
|
|
||||||
|
All you need to learn to use `Casbin` first.
|
||||||
|
|
||||||
|
* [Installation](#installation)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Quick start](#quick-start)
|
||||||
|
* [Using Enforcer Api](#using-enforcer-api)
|
||||||
|
* [Using a middleware](#using-a-middleware)
|
||||||
|
* [basic Enforcer Middleware](#basic-enforcer-middleware)
|
||||||
|
* [HTTP Request Middleware ( RESTful is also supported )](#http-request-middleware--restful-is-also-supported-)
|
||||||
|
* [Using artisan commands](#using-artisan-commands)
|
||||||
|
* [Cache](#using-cache)
|
||||||
|
* [Thinks](#thinks)
|
||||||
|
* [License](#license)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Require this package in the `composer.json` of your Laravel project. This will download the package.
|
||||||
|
|
||||||
|
```
|
||||||
|
composer require casbin/laravel-authz
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Lauthz\LauthzServiceProvider` is `auto-discovered` and registered by default, but if you want to register it yourself:
|
||||||
|
|
||||||
|
Add the ServiceProvider in `config/app.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
'providers' => [
|
||||||
|
/*
|
||||||
|
* Package Service Providers...
|
||||||
|
*/
|
||||||
|
Lauthz\LauthzServiceProvider::class,
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The Enforcer facade is also `auto-discovered`, but if you want to add it manually:
|
||||||
|
|
||||||
|
Add the Facade in `config/app.php`
|
||||||
|
|
||||||
|
```php
|
||||||
|
'aliases' => [
|
||||||
|
// ...
|
||||||
|
'Enforcer' => Lauthz\Facades\Enforcer::class,
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
To publish the config, run the vendor publish command:
|
||||||
|
|
||||||
|
```
|
||||||
|
php artisan vendor:publish
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create a new model config file named `config/lauthz-rbac-model.conf` and a new lauthz config file named `config/lauthz.php`.
|
||||||
|
|
||||||
|
|
||||||
|
To migrate the migrations, run the migrate command:
|
||||||
|
|
||||||
|
```
|
||||||
|
php artisan migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create a new table named `rules`
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Quick start
|
||||||
|
|
||||||
|
Once installed you can do stuff like this:
|
||||||
|
|
||||||
|
```php
|
||||||
|
|
||||||
|
use Enforcer;
|
||||||
|
|
||||||
|
// adds permissions to a user
|
||||||
|
Enforcer::addPermissionForUser('eve', 'articles', 'read');
|
||||||
|
// adds a role for a user.
|
||||||
|
Enforcer::addRoleForUser('eve', 'writer');
|
||||||
|
// adds permissions to a rule
|
||||||
|
Enforcer::addPolicy('writer', 'articles','edit');
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check if a user has a permission like this:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// to check if a user has permission
|
||||||
|
if (Enforcer::enforce("eve", "articles", "edit")) {
|
||||||
|
// permit eve to edit articles
|
||||||
|
} else {
|
||||||
|
// deny the request, show an error
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Enforcer Api
|
||||||
|
|
||||||
|
It provides a very rich api to facilitate various operations on the Policy:
|
||||||
|
|
||||||
|
Gets all roles:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::getAllRoles(); // ['writer', 'reader']
|
||||||
|
```
|
||||||
|
|
||||||
|
Gets all the authorization rules in the policy.:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::getPolicy();
|
||||||
|
```
|
||||||
|
|
||||||
|
Gets the roles that a user has.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::getRolesForUser('eve'); // ['writer']
|
||||||
|
```
|
||||||
|
|
||||||
|
Gets the users that has a role.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::getUsersForRole('writer'); // ['eve']
|
||||||
|
```
|
||||||
|
|
||||||
|
Determines whether a user has a role.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::hasRoleForUser('eve', 'writer'); // true or false
|
||||||
|
```
|
||||||
|
|
||||||
|
Adds a role for a user.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::addRoleForUser('eve', 'writer');
|
||||||
|
```
|
||||||
|
|
||||||
|
Adds a permission for a user or role.
|
||||||
|
|
||||||
|
```php
|
||||||
|
// to user
|
||||||
|
Enforcer::addPermissionForUser('eve', 'articles', 'read');
|
||||||
|
// to role
|
||||||
|
Enforcer::addPermissionForUser('writer', 'articles','edit');
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes a role for a user.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::deleteRoleForUser('eve', 'writer');
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes all roles for a user.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::deleteRolesForUser('eve');
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes a role.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::deleteRole('writer');
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes a permission.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::deletePermission('articles', 'read'); // returns false if the permission does not exist (aka not affected).
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes a permission for a user or role.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::deletePermissionForUser('eve', 'articles', 'read');
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes permissions for a user or role.
|
||||||
|
|
||||||
|
```php
|
||||||
|
// to user
|
||||||
|
Enforcer::deletePermissionsForUser('eve');
|
||||||
|
// to role
|
||||||
|
Enforcer::deletePermissionsForUser('writer');
|
||||||
|
```
|
||||||
|
|
||||||
|
Gets permissions for a user or role.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::getPermissionsForUser('eve'); // return array
|
||||||
|
```
|
||||||
|
|
||||||
|
Determines whether a user has a permission.
|
||||||
|
|
||||||
|
```php
|
||||||
|
Enforcer::hasPermissionForUser('eve', 'articles', 'read'); // true or false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a middleware
|
||||||
|
|
||||||
|
This package comes with `EnforcerMiddleware`, `RequestMiddleware` middlewares. You can add them inside your `app/Http/Kernel.php` file.
|
||||||
|
|
||||||
|
```php
|
||||||
|
protected $routeMiddleware = [
|
||||||
|
// ...
|
||||||
|
// a basic Enforcer Middleware
|
||||||
|
'enforcer' => \Lauthz\Middlewares\EnforcerMiddleware::class,
|
||||||
|
// an HTTP Request Middleware
|
||||||
|
'http_request' => \Lauthz\Middlewares\RequestMiddleware::class,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
#### basic Enforcer Middleware
|
||||||
|
|
||||||
|
Then you can protect your routes using middleware rules:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Route::group(['middleware' => ['enforcer:articles,read']], function () {
|
||||||
|
// pass
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### HTTP Request Middleware ( RESTful is also supported )
|
||||||
|
|
||||||
|
If you need to authorize a Request,you need to define the model configuration first in `config/lauthz-rbac-model.conf`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[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.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, using middleware rules:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Route::group(['middleware' => ['http_request']], function () {
|
||||||
|
Route::resource('photo', 'PhotoController');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using artisan commands
|
||||||
|
|
||||||
|
You can create a policy from a console with artisan commands.
|
||||||
|
|
||||||
|
To user:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan policy:add eve,articles,read
|
||||||
|
```
|
||||||
|
|
||||||
|
To Role:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan policy:add writer,articles,edit
|
||||||
|
```
|
||||||
|
|
||||||
|
Adds a role for a user:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan role:assign eve writer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using cache
|
||||||
|
|
||||||
|
Authorization rules are cached to speed up performance. The default is off.
|
||||||
|
|
||||||
|
Sets your own cache configs in Laravel's `config/lauthz.php`.
|
||||||
|
|
||||||
|
```php
|
||||||
|
'cache' => [
|
||||||
|
// changes whether Lauthz will cache the rules.
|
||||||
|
'enabled' => false,
|
||||||
|
|
||||||
|
// cache store
|
||||||
|
'store' => 'default',
|
||||||
|
|
||||||
|
// cache Key
|
||||||
|
'key' => 'rules',
|
||||||
|
|
||||||
|
// ttl \DateTimeInterface|\DateInterval|int|null
|
||||||
|
'ttl' => 24 * 60,
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
## Thinks
|
||||||
|
|
||||||
|
[Casbin](https://github.com/php-casbin/php-casbin) in Laravel. You can find the full documentation of Casbin [on the website](https://casbin.org/).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the [Apache 2.0 license](LICENSE).
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"name": "casbin/laravel-authz",
|
||||||
|
"keywords": ["laravel","casbin", "permission", "access-control", "authorization", "rbac", "acl", "abac", "authz"],
|
||||||
|
"description": "An authorization library that supports access control models like ACL, RBAC, ABAC in Laravel. ",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "TechLee",
|
||||||
|
"email": "techlee@qq.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"require": {
|
||||||
|
"laravel/framework": "~5.1",
|
||||||
|
"casbin/casbin": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "~5.7|~6.0|~7.0|~8.0",
|
||||||
|
"php-coveralls/php-coveralls": "^2.1",
|
||||||
|
"mockery/mockery": "^1.0",
|
||||||
|
"laravel/laravel": "~5.1"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Lauthz\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Lauthz\\Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Lauthz\\LauthzServiceProvider"
|
||||||
|
],
|
||||||
|
"aliases": {
|
||||||
|
"Enforcer": "Lauthz\\Facades\\Enforcer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
[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
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
/*
|
||||||
|
*Default Lauthz driver
|
||||||
|
*/
|
||||||
|
'default' => 'basic',
|
||||||
|
|
||||||
|
'basic' => [
|
||||||
|
/*
|
||||||
|
* Casbin model setting.
|
||||||
|
*/
|
||||||
|
'model' => [
|
||||||
|
// Available Settings: "file", "text"
|
||||||
|
'config_type' => 'file',
|
||||||
|
|
||||||
|
'config_file_path' => config_path('lauthz-rbac-model.conf'),
|
||||||
|
|
||||||
|
'config_text' => '',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Casbin adapter .
|
||||||
|
*/
|
||||||
|
'adapter' => Lauthz\Adapters\DatabaseAdapter::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Database setting.
|
||||||
|
*/
|
||||||
|
'database' => [
|
||||||
|
// Database connection for following tables.
|
||||||
|
'connection' => '',
|
||||||
|
|
||||||
|
// Rule table name.
|
||||||
|
'rules_table' => 'rules',
|
||||||
|
],
|
||||||
|
|
||||||
|
'log' => [
|
||||||
|
// changes whether Lauthz will log messages to the Logger.
|
||||||
|
'enabled' => false,
|
||||||
|
|
||||||
|
// Casbin Logger
|
||||||
|
'logger' => Lauthz\Logger::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
'cache' => [
|
||||||
|
// changes whether Lauthz will cache the rules.
|
||||||
|
'enabled' => false,
|
||||||
|
|
||||||
|
// cache store
|
||||||
|
'store' => 'default',
|
||||||
|
|
||||||
|
// cache Key
|
||||||
|
'key' => 'rules',
|
||||||
|
|
||||||
|
// ttl \DateTimeInterface|\DateInterval|int|null
|
||||||
|
'ttl' => 24 * 60,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
|
||||||
|
class CreateRulesTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$connection = config('lauthz.basic.database.connection') ?: config('database.default');
|
||||||
|
Schema::connection($connection)->create(config('lauthz.basic.database.rules_table'), function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->string('ptype')->nullable();
|
||||||
|
$table->string('v0')->nullable();
|
||||||
|
$table->string('v1')->nullable();
|
||||||
|
$table->string('v2')->nullable();
|
||||||
|
$table->string('v3')->nullable();
|
||||||
|
$table->string('v4')->nullable();
|
||||||
|
$table->string('v5')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$connection = config('lauthz.basic.database.connection') ?: config('database.default');
|
||||||
|
Schema::connection($connection)->dropIfExists(config('lauthz.basic.database.rules_table'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Application Test Suite">
|
||||||
|
<directory>./tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<filter>
|
||||||
|
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||||
|
<directory suffix=".php">./src</directory>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
<logging>
|
||||||
|
<log type="coverage-clover" target="build/logs/clover.xml"/>
|
||||||
|
<log type="coverage-html" target="build/html"/>
|
||||||
|
</logging>
|
||||||
|
<php>
|
||||||
|
<env name="APP_ENV" value="testing"/>
|
||||||
|
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||||
|
<env name="CACHE_DRIVER" value="array"/>
|
||||||
|
<env name="MAIL_DRIVER" value="array"/>
|
||||||
|
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||||
|
<env name="SESSION_DRIVER" value="array"/>
|
||||||
|
|
||||||
|
<env name="DB_DATABASE" value="lauthz"/>
|
||||||
|
<env name="DB_USERNAME" value="root"/>
|
||||||
|
</php>
|
||||||
|
</phpunit>
|
|
@ -0,0 +1,167 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Adapters;
|
||||||
|
|
||||||
|
use Lauthz\Models\Rule;
|
||||||
|
use Lauthz\Contracts\DatabaseAdapter as DatabaseAdapterContract;
|
||||||
|
use Casbin\Persist\AdapterHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DatabaseAdapter.
|
||||||
|
*
|
||||||
|
* @author techlee@qq.com
|
||||||
|
*/
|
||||||
|
class DatabaseAdapter implements DatabaseAdapterContract
|
||||||
|
{
|
||||||
|
use AdapterHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rules eloquent model.
|
||||||
|
*
|
||||||
|
* @var Rule
|
||||||
|
*/
|
||||||
|
protected $eloquent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the DatabaseAdapter constructor.
|
||||||
|
*
|
||||||
|
* @param Rule $eloquent
|
||||||
|
*/
|
||||||
|
public function __construct(Rule $eloquent)
|
||||||
|
{
|
||||||
|
$this->eloquent = $eloquent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* savePolicyLine function.
|
||||||
|
*
|
||||||
|
* @param string $ptype
|
||||||
|
* @param array $rule
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function savePolicyLine($ptype, array $rule)
|
||||||
|
{
|
||||||
|
$col['ptype'] = $ptype;
|
||||||
|
foreach ($rule as $key => $value) {
|
||||||
|
$col['v'.strval($key)] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->eloquent->create($col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loads all policy rules from the storage.
|
||||||
|
*
|
||||||
|
* @param Model $model
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function loadPolicy($model)
|
||||||
|
{
|
||||||
|
$rows = $this->eloquent->getAllFromCache();
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$line = implode(', ', array_slice(array_values($row), 1));
|
||||||
|
$this->loadPolicyLine(trim($line), $model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* saves all policy rules to the storage.
|
||||||
|
*
|
||||||
|
* @param Model $model
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function savePolicy($model)
|
||||||
|
{
|
||||||
|
foreach ($model->model['p'] as $ptype => $ast) {
|
||||||
|
foreach ($ast->policy as $rule) {
|
||||||
|
$this->savePolicyLine($ptype, $rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($model->model['g'] as $ptype => $ast) {
|
||||||
|
foreach ($ast->policy as $rule) {
|
||||||
|
$this->savePolicyLine($ptype, $rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a policy rule to the storage.
|
||||||
|
* This is part of the Auto-Save feature.
|
||||||
|
*
|
||||||
|
* @param string $sec
|
||||||
|
* @param string $ptype
|
||||||
|
* @param array $rule
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function addPolicy($sec, $ptype, $rule)
|
||||||
|
{
|
||||||
|
return $this->savePolicyLine($ptype, $rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is part of the Auto-Save feature.
|
||||||
|
*
|
||||||
|
* @param string $sec
|
||||||
|
* @param string $ptype
|
||||||
|
* @param array $rule
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function removePolicy($sec, $ptype, $rule)
|
||||||
|
{
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
$instance = $this->eloquent->where('ptype', $ptype);
|
||||||
|
|
||||||
|
foreach ($rule as $key => $value) {
|
||||||
|
$instance->where('v'.strval($key), $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($instance->get() as $model) {
|
||||||
|
if ($model->delete()) {
|
||||||
|
++$count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RemoveFilteredPolicy removes policy rules that match the filter from the storage.
|
||||||
|
* This is part of the Auto-Save feature.
|
||||||
|
*
|
||||||
|
* @param string $sec
|
||||||
|
* @param string $ptype
|
||||||
|
* @param int $fieldIndex
|
||||||
|
* @param mixed ...$fieldValues
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function removeFilteredPolicy($sec, $ptype, $fieldIndex, ...$fieldValues)
|
||||||
|
{
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
$instance = $this->eloquent->where('ptype', $ptype);
|
||||||
|
foreach (range(0, 5) as $value) {
|
||||||
|
if ($fieldIndex <= $value && $value < $fieldIndex + count($fieldValues)) {
|
||||||
|
$instance->where('v'.strval($value), $fieldValues[$value - $fieldIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($instance->get() as $model) {
|
||||||
|
if ($model->delete()) {
|
||||||
|
++$count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Commands;
|
||||||
|
|
||||||
|
use Lauthz\Facades\Enforcer;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PolicyAdd class.
|
||||||
|
*/
|
||||||
|
class PolicyAdd extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'policy:add
|
||||||
|
{policy : the rule separated by commas}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Adds an authorization rule to the current policy';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$params = explode(',', $this->argument('policy'));
|
||||||
|
array_walk($params, function (&$value) {
|
||||||
|
$value = trim($value);
|
||||||
|
});
|
||||||
|
$ret = Enforcer::addPolicy(...$params);
|
||||||
|
if ($ret) {
|
||||||
|
$this->info('Policy `'.implode(', ', $params).'` created');
|
||||||
|
} else {
|
||||||
|
$this->error('Policy `'.implode(', ', $params).'` creation failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Commands;
|
||||||
|
|
||||||
|
use Lauthz\Facades\Enforcer;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RoleAssign class.
|
||||||
|
*/
|
||||||
|
class RoleAssign extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'role:assign
|
||||||
|
{user : the identifier of user}
|
||||||
|
{role : the name of role}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Adds a role for a user.';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$user = $this->argument('user');
|
||||||
|
$role = $this->argument('role');
|
||||||
|
|
||||||
|
$ret = Enforcer::addRoleForUser($user, $role);
|
||||||
|
if ($ret) {
|
||||||
|
$this->info('Added `'.$role.'` role to `'.$user.'` successfully');
|
||||||
|
} else {
|
||||||
|
$this->error('Added `'.$role.'` role to `'.$user.'` failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Contracts;
|
||||||
|
|
||||||
|
use Casbin\Persist\Adapter;
|
||||||
|
|
||||||
|
interface DatabaseAdapter extends Adapter
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Contracts;
|
||||||
|
|
||||||
|
interface Factory
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz;
|
||||||
|
|
||||||
|
use Casbin\Enforcer;
|
||||||
|
use Casbin\Model\Model;
|
||||||
|
use Casbin\Log\Log;
|
||||||
|
use Lauthz\Contracts\Factory;
|
||||||
|
use Illuminate\Support\Manager;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mixin \Casbin\Enforcer
|
||||||
|
*/
|
||||||
|
class EnforcerManager extends Manager implements Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the default driver name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDefaultDriver()
|
||||||
|
{
|
||||||
|
return $this->app['config']['lauthz.default'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the Basic Enforcer driver.
|
||||||
|
*
|
||||||
|
* @param array $config
|
||||||
|
*
|
||||||
|
* @return \Casbin\Enforcer
|
||||||
|
*/
|
||||||
|
public function createBasicDriver()
|
||||||
|
{
|
||||||
|
$config = $this->getConfig('basic');
|
||||||
|
|
||||||
|
if ($logger = Arr::get($config, 'log.logger')) {
|
||||||
|
Log::setLogger(new $logger($this->app['log']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = new Model();
|
||||||
|
$configType = Arr::get($config, 'model.config_type');
|
||||||
|
if ('file' == $configType) {
|
||||||
|
$model->loadModel(Arr::get($config, 'model.config_file_path', ''));
|
||||||
|
} elseif ('text' == $configType) {
|
||||||
|
$model->loadModelFromText(Arr::get($config, 'model.config_text', ''));
|
||||||
|
}
|
||||||
|
$adapter = Arr::get($config, 'adapter');
|
||||||
|
if (!is_null($adapter)) {
|
||||||
|
$adapter = $this->app->make($adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Enforcer($model, $adapter, Arr::get($config, 'log.enabled', false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the lauthz driver configuration.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getConfig($name)
|
||||||
|
{
|
||||||
|
return $this->app['config']["lauthz.{$name}"];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Exceptions;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
|
||||||
|
class UnauthorizedException extends HttpException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new exception instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct(403, 'This action is unauthorized.');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Facades;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see \Casbin\Enforcer
|
||||||
|
*/
|
||||||
|
class Enforcer extends Facade
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the registered name of the component.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function getFacadeAccessor()
|
||||||
|
{
|
||||||
|
return 'enforcer';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz;
|
||||||
|
|
||||||
|
use Lauthz\Models\Rule;
|
||||||
|
use Lauthz\Observers\RuleObserver;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class LauthzServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Perform post-registration booting of services.
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
if ($this->app->runningInConsole()) {
|
||||||
|
$this->publishes([__DIR__.'/../database/migrations' => database_path('migrations')], 'laravel-lauthz-migrations');
|
||||||
|
$this->publishes([__DIR__.'/../config/lauthz-rbac-model.conf' => config_path('lauthz-rbac-model.conf')], 'laravel-lauthz-config');
|
||||||
|
$this->publishes([__DIR__.'/../config/lauthz.php' => config_path('lauthz.php')], 'laravel-lauthz-config');
|
||||||
|
|
||||||
|
$this->commands([
|
||||||
|
Commands\PolicyAdd::class,
|
||||||
|
Commands\RoleAssign::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->mergeConfigFrom(__DIR__.'/../config/lauthz.php', 'lauthz');
|
||||||
|
|
||||||
|
$this->bootObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boot Observer.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function bootObserver()
|
||||||
|
{
|
||||||
|
Rule::observe(new RuleObserver());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register bindings in the container.
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->app->singleton('enforcer', function ($app) {
|
||||||
|
return new EnforcerManager($app);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz;
|
||||||
|
|
||||||
|
use Casbin\Log\Logger as LoggerContract;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class Logger implements LoggerContract
|
||||||
|
{
|
||||||
|
public $enable = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LoggerInterface
|
||||||
|
*/
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
public function __construct(LoggerInterface $logger)
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* controls whether print the message.
|
||||||
|
*
|
||||||
|
* @param bool $enable
|
||||||
|
*/
|
||||||
|
public function enableLog($enable)
|
||||||
|
{
|
||||||
|
$this->enable = $enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns if logger is enabled.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEnabled()
|
||||||
|
{
|
||||||
|
return $this->enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* formats using the default formats for its operands and logs the message.
|
||||||
|
*
|
||||||
|
* @param mixed ...$v
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function write(...$v)
|
||||||
|
{
|
||||||
|
if (!$this->enable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$content = '';
|
||||||
|
foreach ($v as $value) {
|
||||||
|
if (\is_array($value) || \is_object($value)) {
|
||||||
|
$value = json_encode($value);
|
||||||
|
}
|
||||||
|
$content .= $value;
|
||||||
|
}
|
||||||
|
$this->logger->info($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* formats according to a format specifier and logs the message.
|
||||||
|
*
|
||||||
|
* @param $format
|
||||||
|
* @param mixed ...$v
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function writef($format, ...$v)
|
||||||
|
{
|
||||||
|
if (!$this->enable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->logger->info(sprintf($format, ...$v));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Middlewares;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Lauthz\Exceptions\UnauthorizedException;
|
||||||
|
use Lauthz\Facades\Enforcer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic Enforcer Middleware.
|
||||||
|
*/
|
||||||
|
class EnforcerMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @param mixed ...$args
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next, ...$args)
|
||||||
|
{
|
||||||
|
if (Auth::guest()) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
// return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
$identifier = $user->getAuthIdentifier();
|
||||||
|
if (method_exists($user, 'getAuthzIdentifier')) {
|
||||||
|
$identifier = $user->getAuthzIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Enforcer::enforce($identifier, ...$args)) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Middlewares;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Lauthz\Exceptions\UnauthorizedException;
|
||||||
|
use Lauthz\Facades\Enforcer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A HTTP Request Middleware.
|
||||||
|
*/
|
||||||
|
class RequestMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The authentication factory instance.
|
||||||
|
*
|
||||||
|
* @var \Illuminate\Contracts\Auth\Factory
|
||||||
|
*/
|
||||||
|
protected $auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new middleware instance.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Contracts\Auth\Factory $auth
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Auth $auth)
|
||||||
|
{
|
||||||
|
$this->auth = $auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @param mixed ...$args
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
if (Auth::guest()) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
$identifier = $user->getAuthIdentifier();
|
||||||
|
if (method_exists($user, 'getAuthzIdentifier')) {
|
||||||
|
$identifier = $user->getAuthzIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Enforcer::enforce($identifier, $request->getPathInfo(), $request->method())) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rule Model.
|
||||||
|
*/
|
||||||
|
class Rule extends Model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* a cache store.
|
||||||
|
*
|
||||||
|
* @var \Illuminate\Cache\Repository
|
||||||
|
*/
|
||||||
|
protected $store;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fillable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = ['ptype', 'v0', 'v1', 'v2', 'v3', 'v4', 'v5'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Eloquent model instance.
|
||||||
|
*
|
||||||
|
* @param array $attributes
|
||||||
|
*/
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
$connection = $this->config('database.connection') ?: config('database.default');
|
||||||
|
|
||||||
|
$this->setConnection($connection);
|
||||||
|
$this->setTable($this->config('database.rules_table'));
|
||||||
|
|
||||||
|
parent::__construct($attributes);
|
||||||
|
|
||||||
|
$this->initCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets rules from caches.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function getAllFromCache()
|
||||||
|
{
|
||||||
|
$get = function () {
|
||||||
|
return $this->get()->toArray();
|
||||||
|
};
|
||||||
|
if (!$this->config('cache.enabled', false)) {
|
||||||
|
return $get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->store->remember($this->config('cache.key'), $this->config('cache.ttl'), $get);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh Cache.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function refreshCache()
|
||||||
|
{
|
||||||
|
if (!$this->config('cache.enabled', false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->forgetCache();
|
||||||
|
$this->getAllFromCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forget Cache.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function forgetCache()
|
||||||
|
{
|
||||||
|
$this->store->forget($this->config('cache.key'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init cache.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function initCache()
|
||||||
|
{
|
||||||
|
$driver = config('lauthz.default');
|
||||||
|
$store = $this->config('cache.store', 'default');
|
||||||
|
$store = 'default' == $store ? null : $store;
|
||||||
|
$this->store = Cache::store($store);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets config value by key.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param string $default
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function config($key = null, $default = null)
|
||||||
|
{
|
||||||
|
$driver = config('lauthz.default');
|
||||||
|
|
||||||
|
return config('lauthz.'.$driver.'.'.$key, $default);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Observers;
|
||||||
|
|
||||||
|
use Lauthz\Models\Rule;
|
||||||
|
|
||||||
|
class RuleObserver
|
||||||
|
{
|
||||||
|
public function saved(Rule $rule)
|
||||||
|
{
|
||||||
|
$rule->refreshCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleted(Rule $rule)
|
||||||
|
{
|
||||||
|
$rule->refreshCache();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests\Commands;
|
||||||
|
|
||||||
|
use Lauthz\Facades\Enforcer;
|
||||||
|
use Lauthz\Tests\TestCase;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
|
class PolicyAddTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
public function testHandle()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Enforcer::enforce('eve', 'articles', 'read'));
|
||||||
|
$exitCode = Artisan::call('policy:add', ['policy' => 'eve, articles, read']);
|
||||||
|
$this->assertTrue(0 === $exitCode);
|
||||||
|
$this->assertTrue(Enforcer::enforce('eve', 'articles', 'read'));
|
||||||
|
|
||||||
|
$exitCode = Artisan::call('policy:add', ['policy' => 'eve, articles, read']);
|
||||||
|
$this->assertFalse(0 === $exitCode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests\Commands;
|
||||||
|
|
||||||
|
use Lauthz\Facades\Enforcer;
|
||||||
|
use Lauthz\Tests\TestCase;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
|
class RoleAssignTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
public function testHandle()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Enforcer::hasRoleForUser('eve', 'writer'));
|
||||||
|
$exitCode = Artisan::call('role:assign', ['user' => 'eve', 'role' => 'writer']);
|
||||||
|
$this->assertTrue(0 === $exitCode);
|
||||||
|
$exitCode = Artisan::call('role:assign', ['user' => 'eve', 'role' => 'writer']);
|
||||||
|
$this->assertFalse(0 === $exitCode);
|
||||||
|
$this->assertTrue(Enforcer::hasRoleForUser('eve', 'writer'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests;
|
||||||
|
|
||||||
|
use Enforcer;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
|
|
||||||
|
class DatabaseAdapterTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
public function testEnforce()
|
||||||
|
{
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data1', 'read'));
|
||||||
|
|
||||||
|
$this->assertFalse(Enforcer::enforce('bob', 'data1', 'read'));
|
||||||
|
$this->assertTrue(Enforcer::enforce('bob', 'data2', 'write'));
|
||||||
|
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data2', 'read'));
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data2', 'write'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddPolicy()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Enforcer::enforce('eve', 'data3', 'read'));
|
||||||
|
Enforcer::addPermissionForUser('eve', 'data3', 'read');
|
||||||
|
$this->assertTrue(Enforcer::enforce('eve', 'data3', 'read'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSavePolicy()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Enforcer::enforce('alice', 'data4', 'read'));
|
||||||
|
|
||||||
|
$model = Enforcer::getModel();
|
||||||
|
$model->clearPolicy();
|
||||||
|
$model->addPolicy('p', 'p', ['alice', 'data4', 'read']);
|
||||||
|
|
||||||
|
$adapter = Enforcer::getAdapter();
|
||||||
|
$adapter->savePolicy($model);
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data4', 'read'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemovePolicy()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Enforcer::enforce('alice', 'data5', 'read'));
|
||||||
|
|
||||||
|
Enforcer::addPermissionForUser('alice', 'data5', 'read');
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data5', 'read'));
|
||||||
|
|
||||||
|
Enforcer::deletePermissionForUser('alice', 'data5', 'read');
|
||||||
|
$this->assertFalse(Enforcer::enforce('alice', 'data5', 'read'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveFilteredPolicy()
|
||||||
|
{
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data1', 'read'));
|
||||||
|
Enforcer::removeFilteredPolicy(1, 'data1');
|
||||||
|
$this->assertFalse(Enforcer::enforce('alice', 'data1', 'read'));
|
||||||
|
$this->assertTrue(Enforcer::enforce('bob', 'data2', 'write'));
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data2', 'read'));
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data2', 'write'));
|
||||||
|
Enforcer::removeFilteredPolicy(1, 'data2', 'read');
|
||||||
|
$this->assertTrue(Enforcer::enforce('bob', 'data2', 'write'));
|
||||||
|
$this->assertFalse(Enforcer::enforce('alice', 'data2', 'read'));
|
||||||
|
$this->assertTrue(Enforcer::enforce('alice', 'data2', 'write'));
|
||||||
|
Enforcer::removeFilteredPolicy(2, 'write');
|
||||||
|
$this->assertFalse(Enforcer::enforce('bob', 'data2', 'write'));
|
||||||
|
$this->assertFalse(Enforcer::enforce('alice', 'data2', 'write'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests;
|
||||||
|
|
||||||
|
use Lauthz\Middlewares\EnforcerMiddleware;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class EnforcerMiddlewareTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
public function testNotLogin()
|
||||||
|
{
|
||||||
|
$this->assertEquals($this->middleware('data1', 'read'), 'Unauthorized Exception');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAfterLogin()
|
||||||
|
{
|
||||||
|
$this->login('alice');
|
||||||
|
$this->assertEquals($this->middleware('data1', 'read'), 200);
|
||||||
|
$this->assertEquals($this->middleware('data2', 'read'), 200);
|
||||||
|
$this->assertEquals($this->middleware('data2', 'write'), 200);
|
||||||
|
|
||||||
|
$this->login('bob');
|
||||||
|
$this->assertEquals($this->middleware('data1', 'read'), 'Unauthorized Exception');
|
||||||
|
$this->assertEquals($this->middleware('data2', 'write'), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function middleware(...$args)
|
||||||
|
{
|
||||||
|
return parent::runMiddleware(EnforcerMiddleware::class, new Request(), ...$args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests;
|
||||||
|
|
||||||
|
use Mockery as m;
|
||||||
|
use Monolog\Logger as Monolog;
|
||||||
|
use Lauthz\Logger;
|
||||||
|
|
||||||
|
class LoggerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testLogger()
|
||||||
|
{
|
||||||
|
if (class_exists(\Illuminate\Log\Logger::class)) {
|
||||||
|
$writer = new \Illuminate\Log\Logger($monolog = m::mock(Monolog::class));
|
||||||
|
} else {
|
||||||
|
$writer = new \Illuminate\Log\Writer($monolog = m::mock(Monolog::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
$logger = new Logger($writer);
|
||||||
|
|
||||||
|
$logger->enableLog(false);
|
||||||
|
$this->assertFalse($logger->isEnabled());
|
||||||
|
|
||||||
|
$logger->enableLog(true);
|
||||||
|
$this->assertTrue($logger->isEnabled());
|
||||||
|
|
||||||
|
$monolog->shouldReceive('info')->once()->with('foo', []);
|
||||||
|
$logger->write('foo');
|
||||||
|
|
||||||
|
$monolog->shouldReceive('info')->once()->with('foo1foo2', []);
|
||||||
|
$logger->write('foo1', 'foo2');
|
||||||
|
|
||||||
|
$monolog->shouldReceive('info')->once()->with(json_encode(['foo1', 'foo2']), []);
|
||||||
|
$logger->write(['foo1', 'foo2']);
|
||||||
|
|
||||||
|
$monolog->shouldReceive('info')->once()->with(sprintf('There are %u million cars in %s.', 2, 'Shanghai'), []);
|
||||||
|
$logger->writef('There are %u million cars in %s.', 2, 'Shanghai');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests\Models;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Authenticatable;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||||
|
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||||
|
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||||
|
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
||||||
|
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||||
|
|
||||||
|
class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract
|
||||||
|
{
|
||||||
|
use Authenticatable, Authorizable, CanResetPassword;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unique Authorization identifier for the user.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAuthzIdentifier()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests;
|
||||||
|
|
||||||
|
use Lauthz\Middlewares\RequestMiddleware;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Lauthz\Models\Rule;
|
||||||
|
|
||||||
|
class RequestMiddlewareTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
public function testNotLogin()
|
||||||
|
{
|
||||||
|
$this->assertEquals($this->middleware('/foo', 'GET'), 'Unauthorized Exception');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAfterLogin()
|
||||||
|
{
|
||||||
|
$this->login('alice');
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo', 'GET')), 200);
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo/1', 'GET')), 200);
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo', 'POST')), 200);
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo/1', 'PUT')), 200);
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo/1', 'DELETE')), 200);
|
||||||
|
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo/2', 'GET')), 200);
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo/2', 'PUT')), 200);
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo/2', 'DELETE')), 200);
|
||||||
|
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo1/123', 'GET')), 200);
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo1/123', 'POST')), 200);
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/foo1/123', 'PUT')), 'Unauthorized Exception');
|
||||||
|
|
||||||
|
$this->assertEquals($this->middleware(Request::create('/proxy', 'GET')), 'Unauthorized Exception');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function middleware($request)
|
||||||
|
{
|
||||||
|
return parent::runMiddleware(RequestMiddleware::class, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initConfig()
|
||||||
|
{
|
||||||
|
parent::initConfig();
|
||||||
|
$this->app['config']->set('lauthz.basic.model.config_type', 'text');
|
||||||
|
$text = <<<'EOT'
|
||||||
|
[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.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
|
||||||
|
EOT;
|
||||||
|
$this->app['config']->set('lauthz.basic.model.config_text', $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initTable()
|
||||||
|
{
|
||||||
|
Rule::truncate();
|
||||||
|
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => '/foo', 'v2' => 'GET']);
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => '/foo/:id', 'v2' => 'GET']);
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => '/foo', 'v2' => 'POST']);
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => '/foo/:id', 'v2' => 'PUT']);
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => '/foo/:id', 'v2' => 'DELETE']);
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => '/foo1/*', 'v2' => '(GET)|(POST)']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Lauthz\Models\Rule;
|
||||||
|
|
||||||
|
class RuleCacheTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseMigrations;
|
||||||
|
|
||||||
|
public function testEnableCache()
|
||||||
|
{
|
||||||
|
$this->enableCache();
|
||||||
|
|
||||||
|
DB::connection()->enableQueryLog();
|
||||||
|
|
||||||
|
app(Rule::class)->forgetCache();
|
||||||
|
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
$this->assertCount(1, DB::getQueryLog());
|
||||||
|
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
$this->assertCount(1, DB::getQueryLog());
|
||||||
|
|
||||||
|
DB::flushQueryLog();
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
$this->assertCount(0, DB::getQueryLog());
|
||||||
|
|
||||||
|
$rule = Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => 'data1', 'v2' => 'read']);
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
$this->assertCount(2, DB::getQueryLog());
|
||||||
|
|
||||||
|
$rule->delete();
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
$this->assertCount(4, DB::getQueryLog());
|
||||||
|
|
||||||
|
DB::flushQueryLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDisableCache()
|
||||||
|
{
|
||||||
|
$this->app['config']->set('lauthz.basic.cache.enabled', false);
|
||||||
|
|
||||||
|
DB::connection()->enableQueryLog();
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
$this->assertCount(1, DB::getQueryLog());
|
||||||
|
|
||||||
|
$rule = Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => 'data1', 'v2' => 'read']);
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
$this->assertCount(3, DB::getQueryLog());
|
||||||
|
|
||||||
|
$rule->delete();
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
app(Rule::class)->getAllFromCache();
|
||||||
|
$this->assertCount(6, DB::getQueryLog());
|
||||||
|
|
||||||
|
DB::flushQueryLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function enableCache()
|
||||||
|
{
|
||||||
|
$this->app['config']->set('lauthz.basic.cache.enabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initTable()
|
||||||
|
{
|
||||||
|
Rule::truncate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lauthz\Tests;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Console\Kernel;
|
||||||
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Lauthz\Exceptions\UnauthorizedException;
|
||||||
|
use Lauthz\Models\Rule;
|
||||||
|
use Lauthz\Tests\Models\User;
|
||||||
|
|
||||||
|
abstract class TestCase extends BaseTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates the application.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Foundation\Application
|
||||||
|
*/
|
||||||
|
public function createApplication()
|
||||||
|
{
|
||||||
|
$this->app = require __DIR__.'/../vendor/laravel/laravel/bootstrap/app.php';
|
||||||
|
|
||||||
|
$this->app->booting(function () {
|
||||||
|
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
|
||||||
|
$loader->alias('Enforcer', \Lauthz\Facades\Enforcer::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->make(Kernel::class)->bootstrap();
|
||||||
|
|
||||||
|
$this->app->register(\Lauthz\LauthzServiceProvider::class);
|
||||||
|
$this->initConfig();
|
||||||
|
|
||||||
|
$this->artisan('vendor:publish', ['--provider' => 'Lauthz\LauthzServiceProvider']);
|
||||||
|
$this->artisan('migrate', ['--force' => true]);
|
||||||
|
|
||||||
|
if (method_exists($this, 'afterApplicationCreated')) {
|
||||||
|
$this->afterApplicationCreated(function () {
|
||||||
|
$this->initTable();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$this->initTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->app;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initConfig()
|
||||||
|
{
|
||||||
|
$this->app['config']->set('database.default', 'mysql');
|
||||||
|
$this->app['config']->set('database.connections.mysql.charset', 'utf8');
|
||||||
|
$this->app['config']->set('database.connections.mysql.collation', 'utf8_unicode_ci');
|
||||||
|
// $app['config']->set('lauthz.log.enabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initTable()
|
||||||
|
{
|
||||||
|
Rule::truncate();
|
||||||
|
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'alice', 'v1' => 'data1', 'v2' => 'read']);
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'bob', 'v1' => 'data2', 'v2' => 'write']);
|
||||||
|
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'data2_admin', 'v1' => 'data2', 'v2' => 'read']);
|
||||||
|
Rule::create(['ptype' => 'p', 'v0' => 'data2_admin', 'v1' => 'data2', 'v2' => 'write']);
|
||||||
|
Rule::create(['ptype' => 'g', 'v0' => 'alice', 'v1' => 'data2_admin']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function runMiddleware($middleware, $request, ...$args)
|
||||||
|
{
|
||||||
|
$middleware = $this->app->make($middleware);
|
||||||
|
try {
|
||||||
|
return $middleware->handle($request, function () {
|
||||||
|
return (new Response())->setContent('<html></html>');
|
||||||
|
}, ...$args)->status();
|
||||||
|
} catch (UnauthorizedException $e) {
|
||||||
|
return 'Unauthorized Exception';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Exception';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function login($name)
|
||||||
|
{
|
||||||
|
Auth::login($this->user($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function user($name)
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->name = $name;
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue