1

シンプルにしましょう。

プロジェクトには 2 つのモデルしかありません。

  1. ユーザー (hasMany プロジェクト)
  2. プロジェクト (belongsTo ユーザー)

ユーザーは、自分が所有するプロジェクトに対してのみアクションを実行できます。他の誰のものでもない。

ログインしているユーザーが誰で、特定のプロジェクトを所有しているかどうかを手動で確認する方法は知っていますが、これを行うためのより良い、よりグローバルな方法はありますか? 複数のアクション内で同じ検証を繰り返す必要のない、より DRY な方法を探しています。たとえば、おそらく...のような構成設定はありますか?

Configure::write('Enforce_belongs_to', true);

...または Auth コンポーネントの設定/オプションかもしれません。

これはクレイジーかもしれませんが、私は尋ねたいと思いました。

4

2 に答える 2

1

私が答えているのが、最も乾燥した、ほぼ脱水状態の最善のアプローチであるかどうかはわかりませんが、私が考えることができる最も単純なことです.

Project モデルで、ユーザーに関連付けられたプロジェクト ID の配列を返す関数を作成します。

class Project extends AppModel {
    public function getByUserId($userId) {
        $projectsArray = array();

        if ($userId != "valid")
           //do all the checks, if it's not null, numeric, if the id exists, etc

        $projects = $this->Project->find('all', array('conditions'=>
                        array('user_id'=>$userId)));
        if (!empty($projects)) {
            foreach($projects as $i => $project)
                $projectsArray[] = $project['Project']['id'];
        }
        return $projectsArray;
    }
}

コメントでa に言及しfind('first')ていますが、最初のプロジェクトだけでなく、ユーザーに関連するすべてのプロジェクトが必要であると想定しています。そうでない場合は、その関数の単純な変更です。また、IDを取得しているだけですが、$id=>$name_project配列が必要な場合があります。

さて、「アクションの実行のみが許可されている」とはどういう意味かわかりませんが、制限されているのは編集だけですか? または、プロジェクトがユーザーのものでない場合、リストまたはビューを制限し、ユーザーに表示しないようにする必要がありますか?

最初のケースでは、編集を制限し、変更しbeforeSaveます。

public function beforeSave($options = array()) {
    if(!$this->id && !isset($this->data[$this->alias][$this->primaryKey])) {
        //INSERT
        //not doing anything
    } else {
        //UPDATE
        //check if project inside allowed projects array
        $allowed = $this->getByUserId(CakeSession::read("Auth.User.id"));
        if (!in_array($this->id, $allowed))
           return false; //or throw error and catch it in the controller
    }
     return true;
}

コードはテストされていませんが、一般的には、レコードの更新の直前に「ユーザーの」プロジェクトではないプロジェクトの編集を防ぎます。新しいプロジェクトの挿入は誰にとっても無料だと思います。この投稿によると、このフィルターを除くすべての保存関数saveAllは最初にこのフィルターを通過するため、saveAll関数を上書きし、のような検証を追加する必要がありますbeforeSave(そこの回答で説明されているように)。

そして 2 番目の部分では、ユーザーが自分のプロジェクトではなく他のプロジェクトがあることに気付かないように結果をフィルタリングしますbeforeFindドキュメントは、ユーザーの役割に基づいて結果を制限することについて話しているので、私たちは正しい方向に進んでいると思います.

public function beforeFind($queryData) {
    //force the condition
    $allowed = $this->getByUserId(CakeSession::read("Auth.User.id"));
    $queryData['conditions'][$this->alias.'.user_id'] = $allowed;

    return $queryData;
}

$allowed配列には id 値しかないため、句のように機能INしますが、その配列構造を変更する場合は、それに応じてこれらの関数を変更してください。

以上です。ここでは、より基本的なケース、編集、表示、削除について考えています...アップ、削除...beforeDelete機能も変更して、他のプロパティを削除したい悪意のあるユーザーを回避します。ロジックは同じままです (プロジェクト ID が許可された配列にあるかどうかを確認し、そうでない場合は false を返すか、エラーをスローします)、その関数の例をここに追加しません。しかし、それは基本的なことです。何らかの理由で許可されたプロジェクトをコントローラーに入れたい場合は、getByUserId関数を呼び出して、beforeFilterそこでその ids 配列を処理します。セッションに保存することもできますが、プロジェクトを追加または削除するときにそのセッションを維持する必要があります。

すべてを表示および編集できるスーパー管理者が必要な場合getByUserIdは、ユーザーのロールをチェックする条件を に追加するだけで、それが管理者の場合はすべてのプロジェクトを返します。

また、心に留めておいてください: プロジェクトには多くの...サブプロジェクトがある可能性があります (想像力はあまりありません)。そのため、プロジェクトに関連するユーザーはサブプロジェクトを追加できますが、以前と同じ悪意のあるユーザーが、サブプロジェクトが持つ隠し project_id を変更して編集します。 . その場合、サブプロジェクトに検証を追加して、彼のものではないプロジェクトに関連するモデルに対するアクションを回避することをお勧めします。セキュリティ コンポーネントが適切に配置されていて、フォームから編集および削除アクションに到達できる場合、セキュリティ コンポーネントを適切に使用するとフォームの改ざんが回避されるため、これは些細なことです。ただし、「サブプロジェクト」インスタンスも検証する必要があるかどうかを検討してください。

Ayo Akinyemiが述べたように、これらすべてを動作として使用できます。私は個人的にそうしていませんが、要件を満たしています。ここで変更されたすべてのコールバックは、動作で変更したものです。ロジック、列名 (ハードコードされていない変数である必要がありますuser_id) などをカプセル化する必要がありますが、他のケーキプロジェクトで再利用できます。StrongBelongBehaviorまたはのようなものMoreDRYBehavior。そして、あなたがそれをしたらそれを共有してください:)

Auth コンポーネントにあなたが望むことを行う方法があるかどうかはわかりませんが、それが最良の選択肢だと思います。誰かが私を啓発するまで (私はこの問題をあまり調査していません)、これが私が使用する解決策です。

于 2013-08-14T04:02:48.667 に答える
1

Nunserの回答に加えて、動作の一般的な概念を次に示します。その後、該当するモデルに取り付けることができます。

    class StrongBelongBehavior extends ModelBehavior
    {
       public function beforeFind(  Model $Model, $query = array() ) { 
         $query['conditions'] = array_merge( (array)$query['conditions'], array( $Model->alias.'.user_id' => CakeSession::read("Auth.User.id" ) );
         return $query;
       }

       public function beforeSave( Model $Model ) {
        $projectId = Hash::get( $Model->data, 'Poject.id' );
        if( $projectId ) {
           $Model->loadModel('UserProject'); // UserProject is a custom model
           $canEdit =  $Model->UserProject->projectIDExists( $projectId ); // returns true if projectId belongs to the current user
           if ( ! $canEdit  ) {
             return false;
           }
        }
       return true;
       }
     }
于 2013-08-14T12:57:07.820 に答える