1

モデルへの ACL の実装に関する Matthew Weier O'Phinney の投稿の多くを読んで、私はこれを実行する最善の方法に焦点を当ててきました。ただし、ドメイン オブジェクトのベスト プラクティスをさらに調査した結果、これらのモデルにはデータ マッパーや CRUD 操作への参照を含めるべきではないことがわかりました。

たとえば、在庫を維持し、販売注文と購入注文に基づいて企業との間の出荷を処理する ERM ソフトウェアを考えてみましょう。いくつかのドメインがあると思います...

  • 会社
  • 出荷
  • 注文
  • 製品
  • 組み立て
  • そして、いくつかの他の

会社はさまざまなタイプ (メーカー、サプライヤー、小売業者など) を持つことができるため、この情報はデータベース全体の多数のテーブル (会社、タイプ、company_types など) に保存されます。したがって、各データベース テーブルの Zend_Db_Table インスタンスのオブジェクトを使用する会社ドメイン用の Data Mapper があります。

私のコントローラー アクションでは、ロジックはほとんど必要ないことを理解しています。たとえば、新しい会社を作成すると、次のようになります...

public function createAction()
{
  // Receive JSON request from front end
  $data = Zend_Json::decode($request);
  $companyObj = new App_Model_Company();
  $companyObj->populate($data);
  $companyMapper = new App_Model_DataMapper_Company();
  $companyMapper->save($companyObj);
}

これを念頭に置いて、ACL チェックを DataMapper に組み込み、検証をドメイン オブジェクトに組み込むのが最善だと思います。__set私のドメイン オブジェクトはすべて、PHP のマジックと__getメソッドをオーバーロードする基本抽象クラスから拡張されます。各ドメイン オブジェクトのコンストラクターで、$_properties配列にキーを入力してオブジェクトのプロパティを定義します。このように、私の__set方法は次のようになります...

public function __set($property, $value)
{

    $className = __CLASS__;
    if(!array_key_exists($property, $this->_properties))
    {
        throw new Zend_Exception("Class [ $className ] has no property [ $property ]");
    }

    // @return Zend_Form
    $validator = $this->getValidator();

    /*
     * Validate provided $value against Zend_Form element $property
     */

    $this->properties[$property] = $value;
    }
}

すべてのデータ マッパーのsave()メソッド typehint App_Model_DomainObjectAbstract $obj.

質問 #1 - 私の Data Mapper はすべての CRUD アクションを処理し、Domain オブジェクトには実際にはそのドメインに固有のプロパティのみが含まれている必要があるため、ACL チェックは Data Mapper に属しているように感じます - これは受け入れられますか?

コントローラーで Data Mapper をインスタンス化することを避けようとしていましたが、この設計パターンをよりよく理解していると思うと、これは不合理に思えます。

質問 #2 - このプロセスを複雑にしすぎていませんか? 代わりに、メソッドZend_Controller_Plugin_Abstract内の着信要求に基づいて ACL を拡張および処理する ACL プラグインを作成する必要がありますか?preDispatch()

お時間をいただきありがとうございました!

4

2 に答える 2

2

ここにはコンセンサスがあります (@teresko の回答を注意深く読んでください) 。これは、 Decorator パターン(セキュリティ コンテナー)ACLsに最適です。

の特権定義がデータベースに格納されている場合、データベース上の定義とオブジェクトの実際の実装との間をACLマップするための DataMapper が必要です。aclZend_Aclresourcesrolesprivileges

おそらく、 ZF 1 の性質(多くのアンチパターン、グローバル状態など)により、コントローラー デコレーターを実装しないでしょう。代わりに、それをチェックするプラグイン (preDispatch) で横断的な懸念事項を使用します。したがって、ACL は初期化された最初のオブジェクトの 1 つでなければなりません。

controllerACL 定義がとの名前に基づいていることを考慮するとaction、プラグインは を呼び出して、設定AclMapperされたオブジェクトを取得しACL、現在のユーザーが特定のリソースへのアクセスを許可されているかどうかを確認します。

このコード例を確認してください:

class Admin_Plugin_AccessCheck extends Zend_Controller_Plugin_Abstract 
{
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        if($request->getModuleName() != 'admin')
        {
            return;
        }


        $auth = Zend_Auth::getInstance();
        $action = null;

        if(!$auth->hasIdentity())
        {
           $action = 'login'; 
        }
        else
        {
            /**
             * Note that this is not a good practice (singletons). 
             * But in this case it's avoiding re-loading the Acl from database
             *  every time you need it. Also, considering that ZF 1 is full of 
             * singletons, it'll not hurt, I think ;)
             * YOU CAN CHANGE THIS LINE TO $aclMapper->getAcl();
             */

            $acl = Acl::getInstance();

            $resource = $request->getModuleName() . ':' . $request->getControllerName();
            $privilege = $request->getActionName();

            $identity = $auth->getStorage()->read();
            $role = $identity->role_id;

            if($acl->has($resource))
            {
                if(!$acl->isAllowed($role,$resource,$privilege))
                {
                    $action = 'access-denied';
                }
            }
        }

        if($action)
        {
            $request->setControllerName('authentication')
                    ->setActionName($action)
                    ->setModuleName('admin');
        }
    }
}
于 2012-08-24T00:48:46.620 に答える
1

@質問 #1: いいえ、ACL はマッパー内に属していません。懸念事項の分離を念頭に置いてください。ACL をオブジェクトごとにベースにすることにした場合は、上記のリンクのようにデコレータ アプローチを使用することをお勧めします。ただし、デコレータはマッパーの周りに実装される可能性があります。この acl 構造を ZF1 によって提供されていると考えてください: resource: ドメイン エンティティ、たとえば classname role: user-role 権限: CRUD

<?php
class SecurityContainer {
    /**@var Zend_Acl*/
    protected $acl;

    /**@var DataMapper */
    protected $mapper;

    /**@var User|rolename*/
    protected $user;

    public function __construct($acl, $mapper, $user) {
        $this->acl = $acl;
        $this->mapper = $mapper;
        $this->user = $user;
    }

    public function __call($method, $entity) {
        if (method_exists($this->mapper, $method) {
            if ($this->acl->isAllowed($user, get_class($entity), $method) {
                $this->mapper->$method($entity);
        }
    }
}

これは質問 2につながり ます。それは、アプリケーション インターフェイスをどのように設計したかによって大きく異なります。各エンティティ タイプの CRUD 操作ごとに 1 つのアクションがある場合、ZF1 の多くのチュートリアルで示されているように、FrontController-Plugin だけで acl を実装できます。よりきめ細かい ACL が必要な場合、たとえばロール GUEST が会社名を更新する可能性がありますが、管理者はエンティティ全体を更新する可能性があります。または、複数のエンティティが変更されるアクションがある場合は、エンティティ ベースのアプローチが使用されます。いいものだ。

あなたが概説した設計に関するその他の考え: エンティティに自分自身を検証させるのは良い考えではないと思います。具体的なバリデータによって型が検証されるソリューションを実装してみてください。デコレータを再度使用することもできます ;) これはまだよりクリーンなソリューションです。

コントローラー アクション内でマッパーを使用してはならない理由があります。1 つは、データベースから切り離された受け入れテストを行うことが難しくなることです (ただし、実装によって異なります)。あなたは別のことを指摘しました:あなたの行動をできるだけ短くしてください。ACL と Validators を使用すると、アクションが大きくなります。@teresko が他の質問で述べたように、Servicelayer の実装を検討してください。これは、プロパティ ベースの ACL が必要な場合にも役立ちます。

どういうわけかあなたを助けたことを願っています。

于 2012-08-24T05:36:58.180 に答える