PHP应用程序的简单JSON基础AWS IAM风格ACL,利用强大的声明在应用程序中利用细粒度权限。

资助包维护!
rennokki


README

CI codecov StyleCI Latest Stable Version Total Downloads Monthly Downloads License

PHP应用程序的简单JSON基础AWS IAM风格ACL,利用强大的声明在应用程序中利用细粒度权限。 🔐

🚀 安装

您可以通过composer安装此包

composer require renoki-co/acl

🙌 使用方法

如果您熟悉ARNs策略的工作方式,现在您可以使用相同的语法来定义和检查您的ACL策略。

您可以查看更多IAM示例来了解如何定义您的策略。

ACL系统的角色是为可以执行一组资源上某些操作的特定实体分配策略或规则/声明,这样您就可以在应用程序中验证它们。

要定义一个演员类,您需要用HasPolicies特性来提升它

use RenokiCo\Acl\Concerns\HasPolicies;
use RenokiCo\Acl\Contracts\RuledByPolicies;

class Account implements RuledByPolicies
{
    use HasPolicies;

    public $id;

    /**
     * Resolve the account ID of the current actor.
     * This value will be used in ARNs for ARNable static instances,
     * to see if the current actor can perform ID-agnostic resource actions.
     *
     * @return null|string|int
     */
    public function resolveArnAccountId()
    {
        return $this->id;
    }

    /**
     * Resolve the region of the current actor.
     * This value will be used in ARNs for ARNable static instances,
     * to see if the current actor can perform ID-agnostic resource actions.
     *
     * @return null|string|int
     */
    public function resolveArnRegion()
    {
        return $_GET['region'] ?? 'local';
    }
}

每次您需要演员检查权限时,您都必须在其类中加载它们。如果您正在使用ORM/DTO,您可以轻松地将它们与演员本身一起存储在数据库中,并且您可以与它们一起提取策略。

use RenokiCo\Acl\Acl;
use RenokiCo\Acl\Statement;

$policy = Acl::createPolicy([
    Statement::make(
        effect: 'Allow',
        action: 'server:List',
        resource: [
            'arn:php:default:local:123:server',
        ],
    ),
]);

$account = Account::readFromDatabase('123');

$account->loadPolicies($policy);
$account->isAllowedTo('server:List', 'arn:php:default:local:123:server'); // true

子路径

您的某些资源可能允许子路径,例如,您希望允许某些用户访问该磁盘中的某些文件。

$policy = Acl::createPolicy([
    Statement::make(
        effect: 'Allow',
        action: 'disk:ReadFile',
        resource: [
            'arn:php:default:local:123:disk/etc/*',
        ],
    ),
]);

$account->isAllowedTo('disk:ReadFile', 'arn:php:default:local:123:disk/etc/hosts'); // true
$account->isAllowedTo('disk:ReadFile', 'arn:php:default:local:123:disk/var/log/httpd.log'); // false

如果您有一个disk:ListFilesAndFolders 操作,请记住子路径必须以/ 结尾以匹配模式

$policy = Acl::createPolicy([
    Statement::make(
        effect: 'Allow',
        action: 'disk:ListFilesAndFolders',
        resource: [
            'arn:php:default:local:123:disk/etc/*',
        ],
    ),
]);

$account->isAllowedTo('disk:ListFilesAndFolders', 'arn:php:default:local:123:disk/etc/'); // true
$account->isAllowedTo('disk:ListFilesAndFolders', 'arn:php:default:local:123:disk/etc'); // false

🧬 可ARN化

PHP更面向对象。可ARN化可以帮助将您的类,如DTO或模型,转换为ARN的简单版本,这样您就不必每次都编写所有ARN,而是将它们传递给isAllowedTo()方法,具体取决于它是一个资源无关的ARN,还是一个指向特定资源的ARN。

资源无关ARN与资源ARN

资源无关ARN用于如list 或create之类的操作。它们不指向特定资源,而是一般权限,可以允许列出或创建资源。例如,arn:php:default:local:123:server

资源ARN是指向特定资源的ARN。例如,deletemodify之类的操作是很好的示例,可以与这些ARN结合使用。例如,arn:php:default:local:123:server/1arn:php:default:local:123:backup/1

解析区域和账户ID

让我们以这个ARN示例为例:arn:php:default:local:123:server

由于这个ARN是无关的,没有两个关键组件,Server类就不能被正确地转换为ARN

  • 区域,在这种情况下是local
  • 账户ID,在这种情况下是123

尽管这些值有默认值,但您必须让ACL服务知道这些值应该是什么。

对于这些值,您可以参考AWS的示例:它允许您选择区域(在控制台:通过右上角的选择器手动更改区域;在API:通过指定--region参数),并且您必须认证到账户,在这种情况下,您的当前登录会话知道您的账户ID。

在访问控制列表(ACL)中,在运行任何逻辑之前,您需要设置解析器,以便在从参与者视角生成 ARN 时返回正确的值。

使用 ARNables 与参与者

假设您有一个类,它是数据库存储的 Server 实例的 ORM/DTO 类,该实例属于账户/用户。

use RenokiCo\Acl\Concerns\HasArn;
use RenokiCo\Acl\Contracts\Arnable;
use RenokiCo\Acl\BuildResourceArn;

class Server implements Arnable
{
    use HasArn;

    public string $id;
    public string $accountId;
    public string $name;
    public string $ip;

    public function arnResourceAccountId()
    {
        return $this->accountId;
    }

    public function arnResourceId()
    {
        return $this->id;
    }
}

您现在可以传递服务器类名而不是传递完整的 ARN 到 ->isAllowedTo

$policy = Acl::createPolicy([
    Statement::make(
        effect: 'Allow',
        action: 'server:List',
        resource: [
            'arn:php:default:local:123:server',
        ],
    ),
    Statement::make(
        effect: 'Allow',
        action: 'server:Delete',
        resource: [
            'arn:php:default:local:123:server/1',
        ],
    ),
]);

$account = Account::readFromDatabase('123');
$account->loadPolicies($policy);

$account->isAllowedTo('server:List', Server::class); // true

要检查特定资源 ARN 的权限,您可以传递对象本身到 ARN 参数。

$server = Server::readFromDatabase('1');

$account->isAllowedTo('server:Delete', $server); // true

如您之前所见,在参与者实例上,您可以指定它们的账户标识符。在 ARN arn:php:default:local:123:server 中,123 部分是账户 ID 或账户标识符。因此,将 resolveArnAccountId 设置为返回 123,策略将允许参与者对该特定资源执行 server:List 操作。

使用 ARNables 进行子路径设置

如果不明显的话,资源无关的 ARN 不支持子路径设置。

ARNables 通过调用 ->withArnSubpathing() 返回带有子路径的 ARN。

// 'arn:php:default:local:123:disk/etc/hosts'
$account->isAllowedTo('disk:ReadFile', $disk->withArnSubpathing('etc/hosts'));

// 'arn:php:default:local:123:disk/etc/'
$account->isAllowedTo('disk:ReadFile', $disk->withArnSubpathing('etc/'));

使用包含参与者的组

在更复杂的场景下,如果您有一个将更多参与者分组在一起的模式,例如 Team 包含更多的 Account,您仍然需要在用户级别实现策略检查,但将“账户 ID”解析为更类似于团队 ID,只要资源是在 Team 下创建的。

class Team
{
    //
}
use RenokiCo\Acl\Concerns\HasPolicies;
use RenokiCo\Acl\Contracts\RuledByPolicies;

class Account implements RuledByPolicies
{
    use HasPolicies;

    public $id;
    public $teamId;

    public function resolveArnAccountId()
    {
        return $this->teamId;
    }
}

稍后,权限检查将完全像之前一样进行,但检查将来自“团队”,因此每个单独的参与者(在这种情况下,Account)的权限可以由该团队的拥有者定义。

命名约定、默认值和 ARN 部分

默认情况下,每个 Arnable 实例都会根据其类的基本名称设置其资源名称(每个服务应该是唯一的)。

例如,Server 类是 baremetal 服务的一部分,该服务为使用裸金属、IP、磁盘等提供客户服务。在 baremetal 服务下的资源名称将是 server,并且它将有一个类似以下的 ARN:

arn:php:baremetal:local:team-1:server

以下是一些根据类名生成资源名称的示例

  • DockerImage -> dockerimage
  • Backup -> backup
  • 2FA -> 2fa

您可以通过覆盖 arnResourceType 方法来覆盖资源名称。

use RenokiCo\Acl\Concerns\HasArn;
use RenokiCo\Acl\Contracts\Arnable;
use RenokiCo\Acl\BuildResourceArn;

class DemoServer implements Arnable
{
    use HasArn;

    public static function arnResourceType()
    {
        return 'server';
    }
}

或者,您还可以修改 Arnable 的资源 ARN 的其他部分。

class Server implements Arnable
{
    use HasArn;

    public function arnResourcePartition()
    {
        return 'php';
    }

    public function arnResourceService()
    {
        return 'baremetal';
    }

    public function arnResourceRegion()
    {
        return $this->region;
    }
}

为了便于理解,请考虑以下表格,它将 ARN 分解成组件,并指定哪些部分由哪个代码解析。

示例 ARN 是 arn:php:baremetal:local:team-1:server(/*?),其中 (/*?) 可以是 /some-id 或根本不存在。

它们解析的顺序如下,后者会覆盖前者(如果适用和可用):

Resource Agnostic -> Resource -> Actor modifier

🏘 跨账户权限

一些 AWS 服务支持跨账户权限。例如,您可以让任何其他参与者(Account)与您的服务交互,而无需明确允许访问您的账户或加入您的团队。策略应配置为指定任何参与者标识符到 ARN。

📏 指南

大多数指南都是 IAM 风格的,但我们将遍历其中的一些。您还可以在 AWS ARN 文档AWS IAM 文档 中阅读指南。

使用独特的名称作为前缀

确保您的Action在每个资源上都有独特的名称。例如,考虑以下例子,将相同的List命令分开,但用于不同的服务

$policy = Acl::createPolicy([
    Statement::make(
        effect: 'Allow',
        action: [
            'server:List',
            'container:List',
        ],
        resource: [
            'arn:php:default:local:123:server',
            'arn:php:docker-manager:local:123:container',
        ],
    ),
]);

$account->isAllowedTo('server:List', 'arn:php:default:local:123:server'); // true
$account->isAllowedTo('container:List', 'arn:php:docker-manager:local:123:container'); // true

避免检查通配符

关于通配符资源的某些误解可能是这样的

$policy = Acl::createPolicy([
    Statement::make(
        effect: 'Allow',
        action: 'server:List',
        resource: 'arn:php:default:local:123:server/123',
    ),
]);

$account->isAllowedTo('server:List', 'arn:php:default:local:123:server/*'); // Not allowed.

$account->isAllowedTo('server:*', 'arn:php:default:local:123:server/123'); // Not allowed too.

在这种情况下,调用任何两个检查都会抛出InvalidArnException异常。

默认情况下阻止通配符检查,因为检查用户“是否可以列出所有特定的服务器”并不相关,以保留对特定资源有意义的操作,在检查的情况下,我们希望对操作和/或资源非常具体。

建议为列表命令定义一个不带特定资源的语句,并为资源特定的操作(如删除关闭)定义一个带特定资源的语句

$policy = Acl::createPolicy([
    Statement::make(
        effect: 'Allow',
        action: [
            'server:List',
            'server:Create',
        ],
        resource: 'arn:php:default:local:123:server',
    ),
    Statement::make(
        effect: 'Allow',
        action: [
            'server:Describe',
            'server:Update',
            'server:Delete',
        ],
        resource: 'arn:php:default:local:123:server/*',
    ),
]);

$account->isAllowedTo('server:List', 'arn:php:default:local:123:server');
$account->isAllowedTo('server:Create', 'arn:php:default:local:123:server');

$account->isAllowedTo('server:Describe', 'arn:php:default:local:123:server/123');
$account->isAllowedTo('server:Update', 'arn:php:default:local:123:server/123');
$account->isAllowedTo('server:Delete', 'arn:php:default:local:123:server/123');

🐛 测试

vendor/bin/phpunit

🤝 贡献

有关详细信息,请参阅CONTRIBUTING

🔒 安全性

如果您发现任何安全相关的问题,请通过电子邮件alex@renoki.org联系,而不是使用问题跟踪器。

🎉 致谢