darrylkuhn/enforce

使用Eloquent模型强制实施访问控制限制

1.1 2014-06-13 19:45 UTC

This package is not auto-updated.

Last update: 2024-09-24 00:50:13 UTC


README

Enforce是Laravel4的一个插件,它提供了一个优雅的方式来定义自定义数据访问执行器,用于您的Eloquent ORM模型。

快速入门

composer.json文件的require键中添加以下内容

"darrylkuhn/enforce": "dev-master"

运行Composer更新命令

$ composer update

config/app.php中,将现有的'Eloquent'键替换为'Enforce\Model'

'aliases' => array(

    'App'        => 'Illuminate\Support\Facades\App',
    'Artisan'    => 'Illuminate\Support\Facades\Artisan',
    ...
    'Eloquent'   => 'Enforce\Model',

),

配置

Enforce使用标准的Laravel配置。创建app/config/enforce.php,内容如下

<?php
return [
    'byDefault' => false,
];

当然,如果您愿意,可以将此设置为true,但在这样做之前请阅读整个快速入门指南(有很好的理由在启用enforce之前初始化应用程序)。

使用方法

您的模型应继续扩展Eloquent。幕后它们实际上扩展了Enforce\Model,它反过来又扩展了\Illuminate\Database\Eloquent\Model。

您的模型现在继承了一些新的静态方法,包括enforceOnRead()和enforceFilter()。

  • enforceOnRead()接受一个参数,即一个模型或集合。默认情况下,enforceOnRead()不执行任何操作,它只是简单地返回传递给它的模型或集合,这是您添加自定义执行规则的地方。
  • enforceFilter()接受一个模型或集合,并过滤掉任何不符合指定键和值的模型。例如,如果您想确保当前认证的用户只能访问他们自己的用户模型,您可以使用以下代码实现这样的限制
<?php

class User extends Eloquent
{
	public static function enforceOnRead( $models )
    {
        // If the user is not logged in then they can't read user data period
        if ( !Auth::check() ) 
        {
            return null;
        }
        // Filter out any results that don't belong to the user
        else 
        {
            $key = 'id';
            $refrenceValue = Auth::user()->id;
            return self::enforceFilter($models, $key, $refrenceValue);
        }
    }
}

这会过滤掉ID不匹配当前认证用户ID的所有模型。现在调用User::find($id);将返回过滤后的结果。为了更有用,假设您想允许“管理员”访问所有模型,您可以实现以下代码

<?php

class User extends Eloquent
{
	public static function enforceOnRead( $models )
    {
        // If the user is not logged in then they can't read user data period
        if ( !Auth::check() ) 
        {
            return null;
        }
        // If our user isn't an admin then we need to be sure to 
        // filter out any results that aren't theirs
        elseif ( ! Auth::user()->isAdmin() )
        {
            $key = 'id';
            $refrenceValue = Auth::user()->id;
            return self::enforceFilter($models, $key, $refrenceValue);
        }
        // Otherwise they can see anything.
        else 
        {
            return $models;
        }
    }
}

enforceFilter()可以接受复杂的键(例如,$key = 'primaryCompany()->locations[0]->id';可以正常评估)。

如果需要,您可以通过在调用中显式设置执行为false来绕过执行,例如User::find($id, ['*'], false);

启动

在某些情况下,在应用程序达到某些状态之前关闭执行可能是有利的。在上面的例子中,如果执行是开启的,并且我们没有在调用User::find()时显式设置执行为false,认证过程将失败。这是因为规则要求一个有效的认证用户才能访问用户模型,而认证系统使用用户模型进行认证——鸡生蛋,蛋生鸡。有几种解决方法;当然,您可以在认证子系统中的调用上添加标记,但这可能需要修改Laravel核心(这不被推荐)。假设您在使用过滤器在路由之前认证用户,我的建议是使用执行关闭(byDefault => false)初始化应用程序,然后在认证完成后添加一个过滤器将其切换为true。例如,在filters.php中添加以下过滤器

Route::filter('app.applyEnforce', function()
{
    // Make sure our models enforce their access rules by default from here on out
    Config::set('enforce.byDefault', true);
});

然后将其包含在适当的路由调用中

Route::group(array('before' => array('auth.basic', 'app.applyEnforce') ), function()
{
    // User Management
    Route::resource('users/{id}/roles', 'UserRoleController', ['only' => ['index', 'store', 'delete', 'describe']]);
});

这会首先认证用户,然后开启执行。