carrooi / security
Nette 框架的模块化安全系统
Requires
- php: >5.6
- nette/application: ^2.4
- nette/di: ^2.4
- nette/security: ^2.4
- nette/utils: ^2.4
Requires (Dev)
- mockery/mockery: ^0.9.9
- nette/bootstrap: ^2.4
- nette/mail: ^2.4
- nette/tester: ^1.7
- tracy/tracy: ^2.4
README
基于 nette/security 构建的扩展授权。
如果您想创建模块化网站并保持所有组件解耦,并使用“自定义”权限检查,则此包很有用。
现在您可以非常容易地检查例如给定用户是否是某本书的作者等。
这个想法来自 nette/addons 网站。
安装
$ composer require carrooi/security
$ composer update
然后在您的 config.neon 中启用 nette 扩展。
extensions: authorization: Carrooi\Security\DI\SecurityExtension
配置
extendsions: authorization: Carrooi\Security\DI\SecurityExtension authorization: default: true resources: book: default: false actions: view: true add: loggedIn: true edit: roles: [admin] delete: roles: [admin]
嗯,目前还没有模块化…… 我们只是说资源 book
有一个 view
动作,任何人都可以访问,add
动作对登录用户开放,而 edit
和 delete
动作对具有 admin
角色的用户开放。
还有两个 default
选项。使用第一个选项,我们说对未知动作的每个 ->isAllowed()
调用将自动返回 true
。但第二个 default
将覆盖所有 book
动作的此选项为 false
。
这意味着例如 ->isAllowed('book', 'detail')
将返回 false
,但 ->isAllowed('user', 'detail')
将返回 true
。
其他资源和动作
如果 default
选项不够用,您可以使用星号创建默认资源或默认动作。
authorization: resources: favorites: actions: *: loggedIn: true
自定义资源授权者
现在让我们手动创建书籍的相同授权。
services: - App\Model\Books authorization: resources: book: App\Model\Books
App\Model\Books
必须是已注册的服务。
namespace App\Model; use Carrooi\Security\Authorization\IResourceAuthorizator; use Carrooi\Security\User\User; /** * @author David Kudera */ class Books implements IResourceAuthorizator { /** * @return array */ public function getActions() { return [ 'view', 'add', 'edit', 'delete', ]; } /** * @param \Carrooi\Security\User\User $user * @param string $action * @param mixed $data * @return bool */ public function isAllowed(User $user, $action, $data = null) { if ($action === 'view') { return true; } if ($action === 'add' && $user->isLoggedIn()) { return true; } if (in_array($action, ['edit', 'delete']) && $user->isInRole('admin')) { return true; } return false; } }
您还可以从 getActions()
方法返回 *
以告诉授权者可以接受任何动作。
使用对象作为资源
在前面的代码中,您可能已经注意到在 isAllowed
方法中有一个未使用的参数 $data
。想象一下,您想允许所有用户更新或删除自己的书籍。首先,您需要注册从对象到资源名称的某种“翻译器”(比如映射器)。
authorization: targetResources: App\Model\Book: book
现在,每次您将 App\Model\Book
对象作为资源传递时,它将被自动转换为 book
资源,然后与您在前面示例中注册的 App\Model\Books
服务一起处理。
namespace App\Presenters; use Nette\Application\BadRequestException; use Nette\Application\ForbiddenRequestException; /** * @author David Kudera */ class BooksPresenter extends BasePresenter { // ... /** * @param int $id * @throws \Nette\Application\BadRequestException * @throws \Nette\Application\ForbiddenRequestException */ public function actionEdit($id) { $this->book = $this->books->findOneById($id); if (!$this->book) { throw new BadRequestException; } if (!$this->getUser()->isAllowed($this->book, 'edit')) { throw new ForbiddenRequestException; } } }
// ... class Books implements IResourceAuthorizator { // ... public function isAllowed(User $user, $action, $data = null) { // ... if ( in_array($action, ['edit', 'delete']) && $data instanceof Book && ( $user->isInRole('admin') || $data->getAuthor()->getId() === $user->getId() ) ) { return true; } return false; } }
或者,您可以编写“魔法”的 is<action>Allowed
方法
class Books implements IResourceAuthorizator { public function isAllowed(User $user, $action, $data = null) { return false; } public function isEditAllowed(User $user, $data = null) { return true; } }
连接到展示者
class BasePresenter extends Nette\Application\UI\Presenter { use Carrooi\Security\Authorization\TPresenterAuthorization; public function checkRequirements($element) { if ($element instanceof Nette\Reflection\Method) { if (!$this->checkMethodRequirements($element)) { throw new Nette\Application\ForbiddenRequestException; } } } }
现在,您可以使用注解来设置当前资源和动作
class BookPresenter extends BasePresenter { /** * @resource(book) * @action(view) */ public function actionDefault() { } }
保护展示者组件和信号
您可以限制任何组件或信号到某些动作。这样,没有人可以从添加动作访问编辑表单等。
class BasePresenter extends Nette\Application\UI\Presenter { use Carrooi\Security\Authorization\TPresenterAuthorization; public function checkRequirements($element) { // ... } protected function createComponent($name) { $this->checkComponentRequirements($name); return parent::createComponent($name); } }
class BookPresenter extends BasePresenter { /** * @action(edit) */ protected function createComponentEditForm() { } /** * @action(default, detail) */ protected function createComponentFavoriteButton() { } /** * @action(*) */ protected function createComponentReadLaterButton() { } }
请注意,组件或信号的 actions 是展示者动作,而不是授权配置中的 actions。
现在 editForm
组件只能在 edit
动作时渲染,favoriteButton
只能在 default
或 detail
动作时渲染,而 readLaterButton
可在任何地方渲染。
相同的 @action
注解也可以用于信号。
展示者安全模式
默认情况下,此包将尝试检查动作、渲染、处理和创建组件方法。但是,如果您省略了一些注解,则不会发生任何操作,并且该方法将被允许。这可以通过启用严格模式来更改。
authorization: actions: strict signals: strict components: strict
其他选项是 true
或 false
,其中 true
是默认值。
编译器扩展
您自己的 DI 编译器扩展可以实现接口 Carrooi\Security\DI\ITargetResourcesProvider
以实现资源映射器。
namespace App\DI; use Carrooi\Security\DI\ITargetResourcesProvider; use Nette\DI\CompilerExtension; /** * @author David Kudera */ class AppExtension extends CompilerExtension implements ITargetResourcesProvider { /** * @return array */ public function getTargetResources() { return [ 'App\Model\Book' => 'book', ]; } }
扩展用户类
如果您想扩展 Nette\Security\User
类,请小心,因为 carrooi\security
已经为了自身的需求扩展了这个类。