レコードオブジェクトのレベルでの柔軟なアクセス制御

みなさんこんにちは!







Djangoベースのプロジェクトでは、異なるユーザーが同じモデル内の個々のオブジェクトにアクセスできない場合、またはその逆の場合、レコード(オブジェクト)のレベルで柔軟なアクセス制御を使用することがよくあります。







私たちのプロジェクトで必要なデータアクセスポリシー、適切な既製のシステムがなかった理由、レコードレベルでの新しいアクセス制御システムがどのように登場したかを正確に説明したいと思います。







最も細心の注意を払うために、システムデバイス、その内部ロジック、およびその処理方法の詳細を以下に示します。







待てない人のために



Django-Accessプロジェクト







既存のシステム



ジャンガにはすでにいくつかのエントリーレベルのアクセス制御システムがあります。 最も有名で安定したシステムはDjango-GuardianDjango-Authorityです。







ジャンゴ保護者



最初のシステムであるDjango-Guardianでは、ユーザーとユーザーが対話できるオブジェクトとの間に、型なしの関係(つまり、データベース内のレコード)を作成する必要があります。 ユーザーとオブジェクトの各ペアには、このような個別の権利レコードが必要です。 データベース内のこれらのレコードの数は、そのようなシステムでアクセス権が規制されているユーザーとオブジェクトの数の積として計算されます。







プロジェクトデータベース内のユーザーおよび管理オブジェクトの数が無制限の場合、権利レコードの数は非常に急速に増加することを理解するのは簡単です。 また、ユーザーを削除する際のこれらのレコードの削除の管理、ユーザーのアクセス領域を変更したり、あるユーザーの可視性から別のユーザーの可視性にオブジェクトを移動した場合の権利レコードのグループ編集により、プロジェクトでDjango-Guardianを使用する可能性は低くなりました。







ジャンゴ権威



2番目のシステムDjango-Authorityは 、共通のタグを介してユーザーと管理対象エンティティとの関係を確立することにより、最初のシステムの問題を解決しようとします。 このような各タグは、ユーザーまたはコントロールオブジェクトに関連付けられたタグテーブル内のエントリです。 同じ名前のタグが特定のユーザーと特定のオブジェクトに関連付けられている場合、このユーザーはこのコントロールオブジェクトにアクセスできると見なされます。







この場合のタグエントリの数は大幅に少なくなり、ユーザーとオブジェクトの数の合計に比例しますが、これは許容範囲です。 ただし、このようなシステムでは、非常に珍しいタグ命名システムを維持する必要があります。 実際には、そのような名前はそれぞれ「スコープ」に対応します(可視性など)。 このスコープに属するすべてのオブジェクトには、このスコープにアクセスできるユーザーに加えて、対応するタグがあります。







Django-Guardianでは根本的に解決されていないパフォーマンスの問題は、Django-Authorityではかなり許容できる範囲で解決されています。







残念ながら、このシステムの開発は長い間中断されています。 確立された関係に従ってアクセスを制御する管理パネルとの統合はありません。 管理者ベースのアプリケーションインターフェイスを開発したかったのですが、このシステムを使用して、とにかく管理者を編集する必要がありました。







まだ管理パネルを編集する必要があるので、独自のアクセス制御システムを作成してみませんか?







新しいシステムに必要なもの



配布する権利



当初、私たちのシステムは、オブジェクトの可視性を決定することにのみ焦点を当て、それらの多くを「水平に」分割しました。 可視性に陥ったオブジェクトを管理する権利は、モデル(タイプ)に応じて、ジャンガの伝統的な権利システムに従って「垂直に」分配されました。







このような分離は、ある点まではまったく受け入れられるものでしたが、アクセスを「クロス」に分散する必要がある場合、システムが粗すぎることが判明しました。 本当に。 ユーザーオブジェクトへのアクセスを配布しましょう。 ユーザーは自分のグループの管理者であり、このグループからユーザーレコードを編集および削除することもできます。 一方、グループの管理者であるユーザーは、他のグループの通常のユーザーになりたいと考えています。 ただし、管理者はレコードに同じアクセス権を持っているため、レコードが表示されるとすぐに、どのグループでも関係ありません。







したがって、「水平方向」に、オブジェクトの可視性だけでなく、オブジェクトに対して実行される操作の全範囲を制御する必要があることが明らかになりました。 従来、オブジェクトで最も一般的で一般的に使用される4種類のアクションが定義されており、頭字語CRUD(作成、読み取り、更新、削除)によって組み合わされることもあります。









権利定義セット



オブジェクトのサブセットに対する特定のアクションの許可を規制する必要があります。 Jungでオブジェクトの特定のサブセットを操作する最も自然で効率的な方法は、 QuerySet



を使用することです。 オブジェクトの特定のサブセットを処理する必要がある場合はいつでも使用します。







ただし、 QuerySet



は、必要なセットオプションの1つ、つまり過去および未来のすべてのオブジェクトを含む 、このモデルのすべてのオブジェクトのセットを記述しません。 実際、このセットはモデル自体によって決定され、Djangoの「伝統的な」権利が定義される唯一の種類のセットです。 実際、 QuerySet



基づいてアクセス権をチェックしているとしましょう。 空のQuerySet



を受け取ったので、少なくとも一部のオブジェクトを表示するための十分な権限がないため、またはデータベース内にオブジェクトが形成されていないために、オブジェクトがその中にあるかどうかを確認できません見るために。







したがって、 QuerySet



を使用して特定のオブジェクトのセットを定義するか、モデルを使用して、これまでに存在した、または将来作成されたこのモデルのすべてのオブジェクトを念頭に置いて、権限が定義されるオブジェクトのQuerySet



を定義します。







変更する必要があるもの



管理者



実際には、上記のすべてを管理パネルに適用する必要があります。 表示されているオブジェクトのリストが表示され、確立された権限に応じて、オブジェクトの追加、編集、または削除が許可および禁止されます。







既存の管理者の動作を変更するには、それらのメソッドの一部の代わりに(または追加して)、新しいアクセス制御システムによって課せられた制限と権限を考慮したコードが呼び出されることを確認する必要があります。 これは、 Mixinプログラミングテンプレートを使用して行うのが最適です。これは、基本クラスのリストの最上部で、他の基本クラスからのメソッド呼び出しをインターセプトするクラスを定義します。







従来の許可システム



QuerySet



で定義されたサブセットだけでなく、モデルQuerySet



で定義されたこのタイプのすべてのオブジェクトのセットに対する権限も決定する必要があります。 したがって、プロジェクトで使用できる(または使用できない) 可能性の 1つとして、 Permissionオブジェクトに基づくジャンガの権利の「伝統的な」モデルを定義します。







権利を説明すべき場所



最初は、権利の配布方法に関する情報を投稿するのに最適な場所はモデルだと思われます。 古いシステムはこれにオブジェクトマネージャを使用しました。これは、モデルオブジェクトへのアクセスを提供し、モデルクラス(プロパティオブジェクト )の定義に挿入することで再定義できるDjangaのものです。







ただし、判明したように、この方法にはいくつかの欠点があります。







まず、モデルオブジェクトにアクセスする方法は、Django アプリケーション (インストールされたパッケージから変更せずに使用されることが多いサブシステム)のプロパティではなく、 プロジェクト全体のプロパティです。 同じアプリケーション(パッケージ)が異なるプロジェクトで使用される場合、このアプリケーションのモデルオブジェクトへのアクセスは、これらのプロジェクトで異なる方法で定義される可能性が非常に高くなります。







第二に、アクセスルールを定義すると、いくつかのアプリケーション(たとえば、 auth )の境界を越えることができます(ほとんどの場合そうなります)。 それらの1つで説明されているため、定義には別のアプリケーション(パッケージ)との不必要な通信が必要になる場合があります。







したがって、プロジェクトには、個々のアプリケーションとは無関係に、プロジェクトのさまざまなオブジェクト(パッケージ)へのアクセスルールのレジストリが必要です。 このレジストリは、モデルの使用時にインポートされるさまざまなモジュールから構造的に取り込むことができます。 このようなレジストリには、独自のモデルだけでなく、プロジェクトに関連するすべてのアプリケーション(パッケージ)からインポートされたモデルのアクセスルールの定義が含まれます。







権利を記述する方法



この問題に対する失敗した、狭すぎる解決策は、既存のパッケージに不必要な制限をもたらしました。







他のパッケージとは異なり、この目的のためだけに設計されたいくつかの特別なモデルの助けを借りてではなく、プロジェクトとそのリクエストに既に存在するモデルに適用されるコードの助けを借りて権利を説明します。







幸いなことに、コードはメソッドまたは関数内だけでなく、ラムダ式内にも配置できます。 このメソッドを使用して、最も明白でシンプルで頻繁に使用されるアクセス制限ルールを説明します。







アクセス制限ルールの実行のコンテキスト



通常、アクセスは「現在の」ユーザーに対して制限されます。 ただし、アクセスが制限されているコンテキスト要素は現在のユーザーだけではないことを忘れてはなりません。 「現在のビジネス」、選択した国、言語、またはアクセスを提供する決定時に関連するその他の要因がアクセスに影響する可能性があります。







したがって、アクセスルールを定義するコードは、アクセス制限のコンテキストとして、 リクエスト全体( Request )を受け取ります。 このコンテキストから正確に制限の対象となるものは、このコード自体によって決定されるべきです。







システムクラス構造



アクセスマネージャー



システムの機能を定義する中心的なクラスは、アクセスマネージャーmanagers.AccessManager



クラスです。 一方では、さまざまなオブジェクトのアクセス制限ルールを定義するプラグインオブジェクトを登録できます。他方では、このクラスのオブジェクトを使用して、プログラムでそのような定義が必要な場合にオブジェクトおよびセットに関する権限を決定する操作を実行します。







アクセス制限ルールを作成する



アクセス制限ルールは、プラグインオブジェクトを作成および登録することにより作成されます。







マネージャークラスのメソッドregister/unregister_plugin(s)



使用すると、プラグインのレジストリを操作できます。 1つのモデルクラスの複数のプラグインがレジストリに追加されることはありません。 register_plugins



メソッドは、モデルがキーとして機能する辞書を受け取り、 register_plugin



はモデルクラスとプラグインオブジェクトを個別のパラメーターとして受け取ります。







ヘルパーマネージャークラスメソッドget_default_plugin



は、デフォルトで登録済みプラグインを返し、 get_default_plugin



は渡されたモデルクラスに登録されたプラグインを探します。 モデルのプラグインを検索する場合、継承が考慮されますが、モデルではないクラスは検索から除外されます。 モデルのプラグインが見つからない場合、デフォルトのプラグインが返されます。







plugins



モジュールの定義済みプラグインクラスには、他のプラグインを結合するApplyAblePlugin



CheckAblePlugin



およびCheckAblePlugin



アクセス制限ルールを動的に定義するプラグイン、およびCheckAblePlugin



オブジェクトの分析に基づいた従来のアクセス制限ルールと同様のアクセス制限ルールを実装するDjangoAccessPlugin



が含まれます。







アクセス制限チェック



動的に定義された属性をapply_something



すると、 check_something



アクセスマネージャーでapply_something



メソッドとapply_something



メソッドを呼び出すsomething



ができます。ここで、 something



が任意の有効な名前です。 この名前は、システムから要求される能力- 能力 -の名前です。 たとえば、表示権( visible



能力)を取得するには、 check_visible



およびappy_visible



メソッドcheck_visible



appy_visible



ます。







check_something



メソッドはモデルを受け取り、それに関連する機能の制限を決定し、 appy_something



appy_something



メソッドに渡され、メソッドはこのリクエストのオブジェクトのリストに関する機能の制限を決定します。







マネージャーは、登録されたプラグインを検索し、それから、またはデフォルトでプラグインから同様の方法で要求します。 欠落しているメソッドとは、要求されたセットのすべてのオブジェクトに関して、指定された能力で要求されたアクションを解決することを意味します。 プラグインが見つかった場合、チェックします。







モデル全体へのアクセスを制限する



モデル全体へのアクセスは、 check_



プレフィックスを持つプラグインメソッドによって制限されます。 モデルと、権利チェックのコンテキストを定義するRequest



オブジェクトがメソッドに渡されます。 メソッドがFalseを返す場合、アクセスは拒否されます。 アクセスを許可するために、通常は辞書が返されます。これにより、 CompoundPlugin



処理するときに返された値を組み合わせることができます。 値を返すこのやや予期しない方法により、 check_appendable



を追加するためのアクセスをリクエストするときにそれらを使用できます。







個々のオブジェクトへのアクセスを制限する



個々のオブジェクトへのアクセス制限の分析は、指定されたアクセスが許可されているオブジェクトのみを残して、 QuerySet



クエリにフィルターを課すことを意味します。







このオーバーレイは、 apply_



プレフィックスを付けたプラグインメソッドによって実行されます。 QuerySet



と、権利チェックのコンテキストを定義するRequest



オブジェクトがメソッドに渡されます。 このメソッドは、渡されたQuerySet



フィルターを適用し、指定されたコンテキストの指定されたアクセスメソッドを許可するオブジェクトのみにオブジェクトのセットを制限し、フィルターされたQuerySet



を返します。







標準チェック



システムは、システムオブジェクトに関してコンテキストから次の機能をチェックします。









同時に、特定のオブジェクトに対するチェックはcheck_appendable



ためcheck_appendable



な機能はcheck_appendable



それぞれcheck_appendable



メソッドを使用して、モデル全体に​​関してのみチェックされます。それらはすでに作成されています。







残りの能力は、モデル全体とオブジェクトの特定のリストの両方に関してテストされます。 合計で、標準チェックの場合、次のプラグインメソッドが呼び出されます(定義されている場合)。









カスタムチェック



どのアプリケーションでもAccessManager



オブジェクトを作成し、標準機能と非標準機能の両方をチェックするように要求できます。 これを行うために、アプリケーションはcheck_



またはcheck_



apply_



と、要求された能力に対応するサフィックスを持つメソッドをリクエストします。







要求された能力に対応するメソッドが見つかったプラグインで定義されていない場合、利用可能と見なされます。 この場合のcheck_



メソッドは空の辞書を返し、 check_



は変更されていないQuerySet



です。







管理者



admin



モジュールには、標準のDzhangovskoy管理パネルの任意のクラスと混合できる特別なクラスAccessControlMixin



が含まれていAccessControlMixin



。 このクラスは、オブジェクトへのアクセス順序の決定に関与するメソッドをオーバーライドし、プロジェクトに設定されたアクセス制限ルールに従ってアクセスを制限します。







管理パネルをゼロから構築するために、 AccessModelAdmin



AccessTabularInline



およびAccessStackedInline



クラスも定義されています。これらのクラスは、 AccessTabularInline



とまったく同じ方法で使用できます。 本質的に、これらのクラスはAccessControlMixin



と対応するAccessControlMixin



クラスの純粋な組み合わせです。









パッケージの機能を示すために、パッケージのソースコードの一部である例を使用します。







この例では、標準パッケージsomeapp



モデルを使用し、独自の追加アプリケーションsomeapp



を使用して、モデルの2つのクラスを定義しています。









例では、次のアクセススキームを定義しています。









グループの追加アクセスルールは、追加時に作成者がグループに含まれることも決定します。 これは、新しく作成されたグループが追加後に作成者が利用できるようにするために行われます。







したがって、この例では、パッケージの機能を使用し、最小限の追加コードを追加することにより、個々のユーザー機能と共通スペースの両方へのアクセスが制御された快適で安全な環境が作成されます。







おわりに



Django-Accessパッケージのアクセス制限ルールの機能定義により、アクセス制御のための複雑な任意ルールの設定が簡単になり、コードでプロジェクトやレコードでデータベースを乱雑にする追加エンティティの作成を回避できます。







プロジェクトの開発に参加し、バグを探し、問題を作成します。 プルリクエストは大歓迎です。








All Articles