非常に不便なアクセス制御システムが使用されている場合、サポートする内部プロジェクトがあります。
いわゆる
ACL 。
アクセスを提供するには、管理者がアクセスを必要とする個々のリソースごとに多くのアクションを実行する必要があるという点で不便です。
ユーザーの苦情が重要なポイントに達し始めた後、私はシステムを簡素化またはやり直すという課題を抱えていました。
私は既存のコードを見て、手直しをしたくなかったので、既成のソリューションを見つけることにしました。 すぐに言わなければならない-私は何かを書く前に、「自転車」の大敵です。私はいつも既製の適切な解決策を見つけようとします。 だから今回はそうだったが、役に立たなかった。 たとえば、
PEARコレクションにはいくつかのソリューションがありますが、適切なソリューションを選択しませんでした。 多くのコードが非推奨になっているため(PHPバージョン5.3を使用しています)、自分でACLを記述することが決定されました。
これについて話したいです。 コード例は最小限にとどめます-作業のアイデアと原則のみを説明します。
しかし、希望する人
はすべてのライブラリコード
upd-2011 (
utf8 )を
取得し 、参照し、必要に応じて使用できます。
そもそも、私はその仕事の一般的な原理を自分で明確にすることに決めました。現実の世界とのそのような類似を想像しました。
PPC。 (チェックポイント)
バブスカは座っており、彼女の前には雑誌があります。 雑誌には、訪問者がチェックポイントを通過する部屋のリストがあります。
実際、施設には多くの施設がありますが、雑誌では祖母の施設は少なくなっています。 アクセスを制御する必要があるのはそれらだけです。
雑誌の各部屋には、入場を許可された訪問者のリストがあり、
特定の人々のグループのほかに示されています。 (たとえば、技術部門のすべての従業員は部屋303に入ることができます)。
この雑誌が案内するバブスカと訪問者の手元にある文書が、施設へのアクセスを管理しています。
これは、システムのクライアント部分です。
今行政一部:
定期的に、セキュリティの責任者がチェックポイントに来ます。 彼はジャーナルに変更を加えます。
将来のシステムに設定した要件:
- リソースへの権利は、ユーザー、ユーザー、グループ、グループに付与できます。
- ポイント番号1から、ユーザーはグループに属することができます。
- システム(少なくとも最初のバージョンでは)は最も単純です。つまり、特定のユーザーにリソースに対する権利を与えるかどうかを意味します。
- リソースの権利を継承する必要があります。
つまり、 example.com / someitemなどのリソースがユーザーPupkinのみによって所有されている場合、自動的に彼と彼(ただしセクション5を参照)だけがリソースにアクセスできます。
ただし、一部の下位リソースについて権限が再定義されていない場合のみ。 権限が上書きされると、ツリー内で新しい方法でさらに継承されます。
- システムには必須の(システム)グループが必要です。
- 許可されていないユーザー
- 許可ユーザー
- スーパー。
さらに、スーパーユーザーは、これらの要件の前の段落からのPupkinのリソースを含む、あらゆるリソースに対する権利を持っています。
- さて、パラグラフ4に従って、リソースは従属的かつ親になることができます。
ここのリソースとは、一般に、サイト上の任意のパスを意味します。
親リソースは、これがスラッシュ(/)へのパスの最後の要素を消去する場合に取得できます。
たとえば、同じexample.com/someitem/item2-erase / item2-親を取得します:
-example.com/someitem
- 前の段落から、すべてのサイトリソースに共通の祖先が1つ存在する必要があることになります。これはルートリソースです。
この場合、これはexample.comですが、「/」として保存し、システム、つまりsystemにも必須です。
- システム要素(ユーザーグループとルートリソース)はシステムから削除できません。
- システムは、クライアントコードで使用するために可能な限りシンプルにする必要があります。
- まず第一に、私の意見では十分です。
要件が定義され、データベースの構造が明確になります。
テーブル:(指定された最小必須フィールド)
ユーザー
+ --------------------------
+同上| 名
+ --------------------------
グループ
+ --------------------------
+同上| 名
+ --------------------------
資源
+ --------------------------
+ id | 名
+ --------------------------
グループのユーザー
+ -------------------------------------------------
+ id | id_gruppy | ユーザーID
+ -------------------------------------------------
リソースへのユーザー権限
+ -------------------------------------------------
+ id | id_resursa | ユーザーID
+ -------------------------------------------------
リソースグループの権利
+ -------------------------------------------------
+ id | id_resursa | グループID
+ -------------------------------------------------
MySQLのデータベーススキーマは、 docsディレクトリのライブラリアーカイブにあります。
ここでは、認証システムを個別に記述し、最初に
PEAR :: Authを使用し、ACLを使用したことをすぐに規定する必要がありますが、PEAR :: Authの既存のバージョンには、多くの古い(PHP 5.3の)コードもあるため、認可は彼自身を書くことにしました。 一般的にシンプルであることが判明しました。 この記事のフレームワークでは説明しません。 私は承認システムから自分自身を解放しようとしたと言うことができます、任意のシステムを使用できます、唯一の条件は、IAuthインターフェイスを実装する必要があることです(説明されたライブラリのアーカイブにあります)
さらに、-記述されたデータベーススキーマは、開発テストを整理するためにのみ一時的に必要でした。これは、データストレージコンテナから独立したシステムを作成することがすぐに決定されたためです。 つまり、ユーザー、グループ、リソース、権限を持つ実際のデータは、さまざまな場所、さまざまなデータベースに保存できます。一部はファイルにのみ、誰かはコードのどこかに直接保存することもできます(よくわかりませんが、リソースこのような小さなユーザーは、数人のユーザーが作業することがわかっているため、データベースは必要ありません。 ポイントは、システムが何も知らないことであり、データがどこにどのように保存されているかを知る必要がないということです。
したがって、システムはすぐにデータストレージから抽象化され、
アダプターを介してデータを処理する必要があります。
したがって、-アダプタインターフェイスを記述し、今のところ、たとえばMySQLの場合、1つのアダプタを実装する必要があります。
アダプターのインターフェースは次のようなものです。
などなど。
アクションがアダプタの必要なものを明確にする必要があります。
MySQL用アダプターの1つのメソッドの実装例
public function getGroups($orderBy=null, $getUsers=false, $getResources=false) { if ( empty($orderBy) ) { $orderBy = $this->tables['group']['namecol']; } $query = "SELECT * FROM {$this->tables['group']['name']} ORDER BY {$orderBy}"; $res = $this->_connection->query($query); if ( !$res ) { return false; } $groups = $res->fetchAll(PDO::FETCH_ASSOC); if ( !$getUsers && !$getResources ) { return $groups; } foreach ($groups as & $_g) { if ( $getUsers ) { $_g['users'] = $this->getUsersForGroup($_g[ $this->tables['group']['idcol'] ]); } if ( $getResources ) { $_g['resources'] = $this->getResourcesForGroup($_g[ $this->tables['group']['idcol'] ]); } } return $groups; }
){ public function getGroups($orderBy=null, $getUsers=false, $getResources=false) { if ( empty($orderBy) ) { $orderBy = $this->tables['group']['namecol']; } $query = "SELECT * FROM {$this->tables['group']['name']} ORDER BY {$orderBy}"; $res = $this->_connection->query($query); if ( !$res ) { return false; } $groups = $res->fetchAll(PDO::FETCH_ASSOC); if ( !$getUsers && !$getResources ) { return $groups; } foreach ($groups as & $_g) { if ( $getUsers ) { $_g['users'] = $this->getUsersForGroup($_g[ $this->tables['group']['idcol'] ]); } if ( $getResources ) { $_g['resources'] = $this->getResourcesForGroup($_g[ $this->tables['group']['idcol'] ]); } } return $groups; }
システム自体には、共通の祖先の継承者という2つのクラスがあります。
1.顧客
2.管理者
クライアントのタスク:
現在のユーザーの要求されたリソースに対する権限を確認し、アクセスを許可するか、「アクセスが拒否されました」ページに送信します。
ところで、アクセスがない場合のシステムからの出口は個別の機能で決定され、これらの機能の名前のみが構成中にシステムに転送されます。
ログインページに送信する()および
アクセスが拒否される()という 2つの機能のみが必要です。
ちなみに、原則として、私自身のプロジェクトでは、通常、エラー403および404、「ページが見つかりません」の1ページを作成します。
「アクセスが拒否されました」(403エラー)を表示するときに、潜在的な攻撃者に「...そのようなページが存在するので、Vasyaだけがそこに行くべきではない」という追加情報を既に提供しているため、これはより信頼性の高いオプションのようです。 」
管理部分のタスク:
システム管理。
グループ、ユーザーのグループへの追加、リソース、権利、上記のすべての削除など。
クライアントコードでシステムを使用する。
クライアントコードで使用されるシステムは、このように、非常に簡単です:
index.php(アプリケーションエントリポイント)
<?php $acl = new AuthAcl_Client($options); $acl->start();
)。 <?php $acl = new AuthAcl_Client($options); $acl->start();
つまり、リソースを要求したユーザーに権限がない場合(たとえば、404ページが表示される)、コードがクライアントアプリケーションにまったく到達しないか、アプリケーションが引き続き動作します。
$ aclオブジェクトのコンストラクターで、必要なパラメーターを渡します。これは通常、配列です。ほとんどの場合、システム自体にはほとんどの要素のデフォルト値があるため、最も単純な場合はまったく存在しません。
しかし、通常、この配列パラメータには、などの項目が含まれています。
- ログインとパスワードを認証フォームに入力するためのフィールドの名前(システムはそれらを認証システムにさらに渡します)。
- データにアクセスするためのアダプタの名前(例えば「MySQLの」)
- データベースに接続するための情報。
- ユーザー、グループ、リソース、および権利を格納するテーブル名。
- 暗号化されている場合、パスワードはどのように暗号化されていますか(たとえば、true、md5)
- そして、構成および構成できるその他のすべて。
メソッドの開始()で近似的に次の作業で発生します。
- 要求されたリソースに対する権利が決定されます。
リソースは「無許可ユーザー」グループに属している可能性があります。つまり、すべてのユーザーが利用できるようになると、システムは動作を停止し、制御はクライアントアプリケーションに移されます。
- それ以外の場合、ユーザーはログインする必要があります。
- 一つは、認証システムのオブジェクトを取得し、制御が渡されます。 この時点で、コードは認証ページに移動し、作業が停止します。
- 承認オブジェクトがユーザーが承認されていることを報告した場合、このリソースに対するユーザー権限がチェックされます。
存在しない場合、コードは「no access()」関数を呼び出し、ユーザーの判断でユーザーを地獄に送ります。 - それ以外の場合は、 - メソッドが終了すると、パスは、クライアントアプリケーションに制御します。
私はいつも最大限にテストでコードをカバーしようとしていますが、今回も同じでした。 書かれた
単体テスト(アーカイブ内にあります)。その後、目的の機能がすべて実装されました。
ここでは、原則として、すべて私が伝えたかったものを。
コードライブラリ全体はこの記事に添付します。
誰かが便利にこの経験を来る場合、私は非常に幸せになります。 また、提案されたソリューションの弱点の批判を聞いて幸せ。
(zipアーカイブ内)AuthAclコードライブラリ
UPD:07/31/2011
utf-8のバージョン:
AuthAcl-utf8ライブラリコード