ac/kalinka

适用于PHP的灵活授权库

0.1.1 2014-04-09 16:54 UTC

This package is not auto-updated.

Last update: 2024-09-14 14:51:46 UTC


README

Build Status

Kalinka帮助您确定谁可以在您的应用程序中做什么。

API文档

此库用于授权,而不是认证。您需要不同的库来实际登录和注销用户、管理密码、OpenID等。Kalinka用于确定用户登录后可以执行哪些操作。

安装

您可以通过composer获取Kalinka

"require": {
    ...
    "ac/kalinka": "dev-master"
}

入门

为您的应用程序创建一个基本的抽象Guard类。Guard是您定义安全策略的地方。策略是返回布尔值的简单方法,其名称以"policy"开头。以下是一个示例

use AC\Kalinka\Guard\BaseGuard;

abstract class MyAppBaseGuard extends BaseGuard
{
    protected function policyAdmin($subject)
    {
        return $subject->isAdmin();
    }
}

上面的subject是要检查权限的用户。这可以是您的应用程序User类的实例(如上面的代码所假设的),也可以是用户名称的简单字符串。重要的是这可以让您了解有关用户的一切,以确定他们可以做什么。

MyAppBaseGuard是抽象的,因为它没有定义任何操作。操作是表示主题可能想要做的各种事情的字符串,您可能允许或不允许这些操作。对于您更具体的Guard类,您需要提供一个返回数组的getActions方法。

class DocumentGuard extends MyAppBaseGuard
{
    public function getActions()
    {
        return ["read", "write"];
    }
}

策略也可以接受一个object作为第二个参数,这是主题试图访问的特定资源。例如,假设您有文档,允许根据用户是否拥有该特定文档以及文档是否“解锁”来访问。

class DocumentGuard extends MyAppBaseGuard
{
    public function getActions()
    {
        return ["read", "write"];
    }

    protected function policyDocumentOwner($subject, $object)
    {
        return ($object->getOwnerId() == $subject->getId());
    }

    protected function policyDocumentUnlocked($subject, $object)
    {
        return !($object->isLocked());
    }
}

当您的应用程序想要进行实际的访问检查时,这些检查将通过Authorizer完成。Authorizer确定在任何给定情况下应用哪些Guard策略。您可以引用您的Guard中实现的政策,以及简单的默认“允许”策略,该策略始终允许访问。

如果您没有为操作定义任何策略,则默认情况下始终拒绝。

对于大多数基本用例,您可以从SimpleAuthorizer派生。

use AC\Kalinka\Authorizer\SimpleAuthorizer;
use MyApp\Guards\CommentGuard;
use MyApp\Guards\DocumentGuard;

class MyAppAuthorizer extends SimpleAuthorizer
{
    public function __construct($subject)
    {
        parent::__construct($subject);

        $this->registerGuards([
            "document" => new DocumentGuard,
            "comment" => new CommentGuard,
            // ... and so on for all your protected resources
        ]);

        $this->registerPolicies([
            "document" => [
                "read" => "allow",
                "write" => "documentOwner"
            ],
            "comment" => [
                "read" => "allow",
                "create" => "allow",
                "delete" => "admin"
            ],
            // ...
        ]);
    }
}

在所有这些之后,我们就可以进行授权了!每次您想要检查对某些资源的访问是否允许时,只需创建您的Authorizer类的实例并调用can方法。

use MyApp\MyAppAuthorizer;

$auth = new MyAppAuthorizer($currentUser);
if ($auth->can("write", "document", $someDocument)) {
    $someDocument->setContent($newValue);
} else {
    print "Access denied!\n";
}

组合策略

假设您只想在文档解锁且用户拥有它们的情况下允许编辑文档。这可以通过提供策略列表而不是单个字符串来实现。

// ...
$this->registerPolicies([
    "document" => [
        "read" => "allow",
        "write" => [
            "documentUnlocked",
            "documentOwner"
        ]
    ],
    // ...
]);

有时,如果几个不同的策略中的任何一个允许操作,则允许执行操作,即使其他策略不允许。假设对于编写文档的目的,管理员资格与文档所有者相同,但文档必须解锁的规则适用于所有人。在这种情况下,您可以使用内嵌列表。

// ...
$this->registerPolicies([
    "document" => [
        "read" => "allow",
        "write" => [
            "documentUnlocked",
            ["documentOwner", "admin"]
        ]
    ],
    // ...
]);

这里的原理是,外层列表是AND连接的,而内层列表是OR连接的。

如果您需要更复杂的策略,您总是可以将其实现为自己的策略。策略可以通过BaseGuard上的checkPolicy方法相互调用。

角色

SimpleAuthorizer 只适用于非常直接的配置,其中相同的策略适用于所有人。在实际系统中,更常见的情况是人们可以属于多个不同的角色,每个角色都有可能在各种不同的情况下访问资源。实现这一点的最简单方法是扩展 RoleAuthorizer,它提供了一个 registerRolePolicies() 方法,该方法替代了 SimpleAuthorizerregisterPolicies() 方法的功能。

use AC\Kalinka\Authorizer\RoleAuthorizer;
use MyApp\Guards\CommentGuard;
use MyApp\Guards\DocumentGuard;

class MyAppAuthorizer extends RoleAuthorizer
{
    public function __construct(MyUserClass $user)
    {
        $roleNames = [];
        foreach ($user->getRoles() as $role) {
            $roleNames[] = $role->getName();
        }
        parent::__construct($user, $roleNames);

        $this->registerGuards([
            "document" => new DocumentGuard,
            "comment" => new CommentGuard,
            // ... and so on for all your protected resources
        ]);

        $this->registerRolePolicies([
            "guest" => [
                "document" => [
                    "read" => "allow",
                ],
                "comment" => [
                    "read" => "allow",
                ]
                // ...
            ],
            "customer" => [
                "document" => [
                    "read" => "allow",
                    "write" => [
                        "documentUnlocked",
                        "documentOwner"
                    ]
                ],
                "comment" => [
                    "read" => "allow",
                    "create" => "allow",
                ],
                // ...
            ],
            // ...
        ]);
    }
}

角色以字符串列表的形式提供。当进行权限检查时,会逐个尝试每个角色;如果用户被分配的任何角色允许该操作,则整体允许。

与我们在 MyAppBaseGuardpolicyAdmin() 方法中添加的角色策略相比,这是一个更灵活的解决方案。

RoleAuthorizer 还提供了一个名为 'superuser' 的特殊角色,它自动允许所有操作。

部分包含的角色

有时您可能会遇到特殊的情况,其中所需的权限与您的角色不完全匹配。例如,您可能有一个拥有“贡献者”角色所有权利的用户,但在处理评论时也可以充当“管理员”。您可以通过使用 RoleAuthorizer 派生方法中的 registerRoleInclusions() 方法来处理这种情况。

use AC\Kalinka\Authorizer\RoleAuthorizer;

class MyAppAuthorizer extends RoleAuthorizer
{
    public function __construct(MyUserClass $user)
    {
        // ...

        if ($user->isCommentAdmin()) {
            $this->registerRoleInclusions([
                "comment" => "administrator"
            ]);
        }
    }
}

也可以仅包含角色中的特定操作

$this->registerRoleInclusions([
    "comment" => ["update" => "administrator", "delete" => "administrator"]
]);

这些包含的部分被视为另一个角色;如果任何包含的策略列表批准,或者如果用户常规角色的任何策略列表批准,则允许访问。