carrooi/security

Nette 框架的模块化安全系统

3.0.0 2017-05-17 11:13 UTC

This package is auto-updated.

Last update: 2024-09-06 03:54:00 UTC


README

Build Status Donate

基于 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 动作对登录用户开放,而 editdelete 动作对具有 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 只能在 defaultdetail 动作时渲染,而 readLaterButton 可在任何地方渲染。

相同的 @action 注解也可以用于信号。

展示者安全模式

默认情况下,此包将尝试检查动作、渲染、处理和创建组件方法。但是,如果您省略了一些注解,则不会发生任何操作,并且该方法将被允许。这可以通过启用严格模式来更改。

authorization:
	actions: strict
	signals: strict
	components: strict

其他选项是 truefalse,其中 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 已经为了自身的需求扩展了这个类。

变更日志

  • 2.0.0

    • 允许资源有多个自定义授权器 #9向后不兼容
  • 1.2.1

    • 使用未注册的资源对象时抛出异常 #8
  • 1.2.0

    • 在目标授权器中添加对接口的支持 #5
    • 重构测试 #2
    • 移除对魔术演示者资源获取器的支持 #7
    • 修复带有私有 is<action>Allowed 的魔术资源验证器 #4
  • 1.1.0

    • 添加对魔术资源验证器方法 is<action>Allowed 的支持
  • 1.0.3

    • 添加懒注册资源授权器 - 在某些情况下防止循环引用
  • 1.0.2

    • 检查给定的授权对象是否为已注册目标资源类的子类
  • 1.0.1

    • 添加默认资源和操作(星号)
  • 1.0.0

    • 初始提交