web-dev-qa-db-ja.com

ユーザー権限チェックはMVCのどこで誰によって行われるべきですか?

ユーザー権限チェックは、モデルまたはコントローラーのどちらで行う必要がありますか?そして、誰が権限チェック、Userオブジェクト、またはUserManagementヘルパーを処理する必要がありますか?

それはどこで起こるべきですか?

コントローラのチェックイン:

class MyController {
  void performSomeAction() {
    if (user.hasRightPermissions()) {
      model.someAction();
    }
  }
  ...

コントローラーにチェックがあると、モデルを単純なアクションにできるため、コントローラーにすべてのロジックを保持できます。

モデルのチェックイン:

class MyModel {
  void someAction() {
    if (user.hasRightPermissions()) {
      ...
    }
  }
  ...

モデルにチェックを入れることにより、モデルを複雑にしますが、コントローラーで想定されていないことをユーザーが誤って実行できないようにしてください。

そして誰が?

場所が決まったら、誰がチェックを行うべきですか?ユーザー?

Class User {
  bool hasPermissions(int permissionMask) {
    ...
  }
  ...

しかし、実際にユーザーが何を処理できるかを知るのはユーザーの責任ではないので、おそらくいくつかのヘルパークラスでしょうか。

Class UserManagement {
  bool hasPermissions(User user, int permissionMask) {
    ...
  }
  ...

たった一つの質問をするのはよくあることですが、これらは一緒にうまく答えられると思います。

27
kba

いつものように、「依存する」

  • 権限チェックは機能的に機能しますそれらを置くのに便利な場所ならどこでも、
  • しかし、技術的な質問をしている場合、答えは「チェックを実行するために必要なデータを所有するオブジェクトにチェックを入れる」かもしれません(これはおそらくコントローラーです)。
  • しかし、哲学的な質問をしている場合は、別の答えを提案します:ユーザーに実行が許可されていないアクションを表示しない

したがって、後者の場合は、ブール型プロパティとして実装されたコントローラーで権限チェックを行い、そのプロパティを、アクションを制御するユーザーインターフェイスのボタンまたはパネルのVisibleプロパティにバインドします。

ユーザーとして、私が実行できないアクションのボタンを表示するのはイライラします。私は楽しみから取り残されているような気がします;)

21
Steven A. Lowe

セキュリティは分野横断的な懸念事項であるため、複数のレイヤーで実装する必要があります。以下はMVCの例ですが、その概念は他のアーキテクチャーやパターンに適用されるため、適用のポイントを特定するだけです。

どこで発生しますか?

Viewsには、ユーザーの権限に基づいて、一部のユーザーに対して表示または非表示にする必要があるUI要素(ウィジェット、ボタン、メニューなど)が含まれる場合があります。 view engineの責任である可能性があります。すべてのビューがこれを単独で処理することを望まないからです。承認を行う要素のタイプに応じて、この責任を別の場所に移動します。たとえば、表示する必要がある項目と表示しない項目があるメニューを考えてみます。アイテムはどこかにリストとして実装され、権限に基づいてそのリストをフィルタリングしてから、ビューに転送できます。

コントローラはリクエストに応答するため、ユーザーがアクションを実行する権限を持っていない場合は、アクションが呼び出される前に確認して責任を移動する必要がありますアクションの呼び出し元をコントローラーに保持するのではなく、これには、コントローラーをクリーンな状態に保つという利点があります。また、アクセス許可に何か変更があった場合、それらの変更を適用するためにコントローラーをふるいにかける必要はありません。

リソースは、権限に基づいて表示されます。これは通常、databaseレベルで行われます。これは、データベースからすべてをプルしてアクセス許可を適用する必要がないためです。

ご覧のように、承認する対象に応じて、これを行う必要のある場所がいくつかあります。セキュリティポリシーが変更されたときに、できればアプリケーションのコードを変更せずに簡単に適用できるように、目標はできるだけ控えめにすることです。これは、権限セットがかなり小さく、頻繁に変更されない小さなアプリケーションには有効でない場合があります。しかし、エンタープライズアプリケーションでは、話はかなり異なります。

誰がやるべきですか?

明らかにモデルではありません。各層には、承認を処理する実施ポイントが必要です。上のイタリック体のテキストは、各レベルの可能な実施ポイントを強調しています。

[〜#〜] xacml [〜#〜] をご覧ください。そのまま実装する必要はありませんが、従うことができるいくつかの指示が得られます。

7
devnull

以下のスキームを使用しています。ほとんどのユーザー権限チェックは、2つの一般的なケースに分けられます。

  • パラメーターアクションをチェックせずにユーザーロールに基づいてコントローラーアクションにアクセスするユーザーは、
  • 特定のユーザーと特定のモデル間のロジックまたは関係に基づいたモデルへのユーザーアクセス。

属性のチェックなしのコントローラーアクションへのアクセスは、通常、MVCフレームワークに実装されています。これは非常に簡単です。ルールを定義し、ユーザーに役割を割り当てます。ルールでの役割を検索するアクションの権限がユーザーにあることを確認するだけです。

特定のモデルへのユーザーアクセスは、モデルで定義する必要があります。 (アクターは基本ユーザークラスです。顧客、販売者、ゲストのいずれかになると仮定します。)

interface ICheckAccess
{
    public function checkAccess(Actor $actor, $role);
}

class SomeModel implements ICheckAccess
{
    public function checkAccess(Actor $actor, $role)
    {
        // Your permissions logic can be as sophisticated as you want.
    }
}

そのロジックをモデルに配置すると、利益が得られます。アクセスチェックメソッドを継承できます。追加のクラスを作成する必要はありません。一般的なOOPの利点を使用できます。

次に、アクセスチェックを簡略化するために、単純さと優れたスタイルのために、ほとんどの場合は既に実装されているいくつかの前提を採用します。

  • 通常、コントローラーはいくつかのモデルクラスに関連しています。
  • アクセスがチェックされるアクションは、パラメータとして単一のモデルIDを取ります。
  • このパラメーターは、基本コントローラークラスのメソッドから常に均一にアクセスできます。
  • アクションは、idアクションが実行するモデルに対応するコントローラーに配置されます。

これらの仮定により、モデルIDを使用するアクションを特定のモデルインスタンスに関連付けることができます。実際、ほとんどのアクションは、上記の仮定に合わせて簡単に変換および移動できます。

次に、いくつかの基本抽象コントローラークラスを定義して継承する必要があります。

abstract class ModelController
{
    // Retrieve model from database using id from action parameter.
    public abstract function loadModel($id);

    // Returns rules for user role to pass to SomeModel::checkAccess()
    // Something like array('view' => 'viewer', 'delete' => 'owner', 'update' => 'owner')
    public abstract function modelRules();

    public abstract fucntion getIdParameter();

    public function filterModelAccess()
    {
        $id = $this->getIdParameter();
        if(!$this->checkModelAccess($id))
            throw new HttpException(403);
    }

    public function checkModelAccess($id)
    {
        $model = $this->loadModel($id);
        $actor = My::app()->getActor();
        $rules = $this->modelRules();
        $role = $rules[My::app()->getActionName()];
        return $model->chechAccess($actor, $role);
    }
}

メニューを作成し、リンクを表示するかどうかを決定するときに、SomeController :: checkModelAccess($ id)メソッドを呼び出すことができます。

1
George Sovetov

モデルビューの両方

ビュー内-現在のユーザーに制限されているUI要素をUIに表示しないため

(たとえば、「削除」ボタンは適切な権限を持つユーザーに表示されます)

モデル内-アプリにはおそらく何らかのAPIが含まれているためですよね? APIは権限もチェックする必要があり、おそらくモデルを再利用します。

(たとえば、UIに「削除」ボタンがあるand「http:/ server/API/DeleteEntry/123」APIメソッドと同時に

1
jitbit

MVCはプレゼンテーションパターンです。そのため、ビューとコントローラーは、プレゼンテーションに関する責任のみを持つ必要があります。エキスパートモード、実験的なUI機能、さまざまなデザインなど、一部の権限はプレゼンテーションに適用されます。これらはMVCコントローラーで処理できます。

他の多くの種類の権限は、アプリケーションのいくつかの層に関連しています。たとえば、データの表示のみが可能で変更はできないユーザーが必要な場合:

  • プレゼンテーション層は編集機能を非表示にする必要があります
  • とにかく編集機能が呼び出された場合、これは(ビジネスレイヤーのアプリケーション固有の部分ではなく、ドメイン固有の部分ではなく、TrainEditorではなく、TrainEditorによって)検出される可能性があり、例外が発生する可能性があります。
  • データアクセスレイヤーも書き込みをチェックできますが、ビジネスレイヤーの知識を必要とするより複雑な種類のアクセス許可については、すぐに理解する必要があります。

このアプローチにはいくつかの重複があります。ただし、プレゼンテーションは通常揮発性であるため、プレゼンテーション層が意図したとおりに機能する場合の冗長チェックを意味する場合でも、アプリケーションの通常はより安定した部分で権限をチェックするための良いケースを作ることができます。

0
Patrick