0

PHP-Bouncerと呼ばれる PHP 用のオープン ソース ロール ベース アクセス コントロール ライブラリに取り組んでいます。PHP-Bouncer を使用すると、ユーザーはロールのリスト、各ロールがアクセスできるページを定義できます。各ロールは、他のページをオーバーライドするページのリストも定義できます (オーバーライドされたページに移動すると、オーバーライドしているページにリダイレクトされます)。 . これがどのように機能するかの例を次に示します (ドキュメントのAccess Managed Exampleから)。

$bouncer = new Bouncer();
// Add a role     Name,      Array of pages role provides
    $bouncer->addRole("Public", array("index.php", "about.php", "fail.php"));
// Add a role          Name,              Array of pages role provides
    $bouncer->addRole("Registered User", array("myaccount.php", "editaccount.php", "viewusers.php"));
// Add a role          Name,   Array of pages role provides       List of pages that are overridden by other pages
    $bouncer->addRole("Admin", array("stats.php", "manageusers.php"), array("viewusers.php" => "manageusers.php"));

// Here we add some users. The user class here extends the BouncerUser class, so it can still do whatever you
// would normally create a user class to do..
    $publicUser         = new User();
    $registeredUser     = new User();
    $adminUser          = new User();
    $registeredAndAdmin = new User();

    $publicUser->addRole("Public");

    $registeredUser->addRole("Public"); // We add the public group to all users since they need it to see index.php
    $registeredUser->addRole("Registered User");

    $adminUser->addRole("Public"); // We add the public group to all users since they need it to see index.php
    $adminUser->addRole("Admin");

    $registeredAndAdmin->addRole("Public"); // We add the public group to all users since they need it to see index.php
    $registeredAndAdmin->addRole("Registered User");
    $registeredAndAdmin->addRole("Admin");

    $bouncer->manageAccess($publicUser->getRoles(), substr($_SERVER["PHP_SELF"], 1), "fail.php");

ここに私が抱えている問題があります: 上記の manageAccess 関数では、役割が適切に定義されていて、すべてのユーザーが fail.php にアクセスできる (または fail.php が $bouncer オブジェクトを実装していない) 限り、すべてがうまく機能します。誰かが固有の競合 (ページをそれ自体でオーバーライドするなど) を持つロールを作成したり、すべてのユーザーに失敗ページへのアクセス権を付与することに失敗したりするとすぐに、manageAccess 関数は無限ループに陥ります。これはダメなので直してほしいです。ただし、無限ループを防ぎながら、いくつかのリダイレクトを許可するための最良のアプローチは何であるかはわかりません(最大2〜3回のリダイレクトが望ましい動作になる可能性があります)。これが manageAccess 関数です。

/**
 * @param array  $roleList
 * @param string $url
 * @param string $failPage
 */
public function manageAccess($roleList, $url, $failPage = "index.php"){
    $granted = false;
    foreach($roleList as $role){
        if(array_key_exists($role, $this->roles)){
            $obj = $this->roles[$role];
            /** @var $obj BouncerRole */
            $response = $obj->verifyAccess($url);
            if($response->getIsOverridden()){ // If access to the page is overridden forward the user to the overriding page
                $loc            = ($obj->getOverridingPage($url) !== false) ? $obj->getOverridingPage($url) : $failPage;
                $locationString = "Location: ".$loc;
                header($locationString);
                // I broke something in the last commit, perhaps this comment will help?
            }
            if($response->getIsAccessible()){ // If this particular role contains access to the page set granted to true
                $granted = true; // We don't return yet in case another role overrides.
            }
        }
    }
    // If we are here, we know that the page has not been overridden
    // so let's check to see if access has been granted by any of our roles.
    // If not, the user doesn't have access so we'll forward them on to the failure page.
    if(!$granted){
        $locationString = "Location: ".$failPage."?url=".urlencode($url)."&roles=".urlencode(serialize($roleList));
        header($locationString);
    }
}

助言がありますか?

4

1 に答える 1

1

必要な機能は「いくつかのリダイレクト」を許可することであるため、これにアプローチすることを考えることができる最良の方法は、リダイレクトを発行するたびにインクリメントするパラメーターを作成することです$_SESSION(または、うまくいくと$_GET 思います)。ユーザーの観点からすると、これは対処するのが非常に煩わしいことですが、それはあなたの Web サイトです。

$_SESSIONパラメータに名前を付けたとしましょうnum_redirects:

  1. リダイレクトする前に、 が設定されているかどうかを確認してください$_SESSION['num_redirects']。そうでない場合は、 に設定し0ます。
  2. の現在の値を取得し$_SESSION['num_redirects']、リダイレクトのしきい値を超えているかどうかを確認します。
  3. しきい値を超えている場合は、リダイレクトせず、単純にdie();
  4. しきい値を超えていない場合は、インクリメント$_SESSION['num_redirects']し、元に戻してリダイレクトします。

したがって、そのコードは次のようになります (また、次の呼び出しで URL クエリ文字列を改善しましたhttp_build_query():

どこかで、しきい値を定義する必要があります。

define( 'MAX_NUM_REDIRECTS', 3);

次に、次のことができます。

// Assuming session_start(); has already occurred
if(!$granted) {
    if( !isset( $_SESSION['num_redirects'])) {
        $_SESSION['num_redirects'] = 0;
    }

    // Check if too many redirects have occurred
    if( $_SESSION['num_redirects'] >= MAX_NUM_REDIRECTS) {
        die( "Severe Error: Misconfigured roles - Maximum number of redirects reached\n");
    }

    // If we get here, we can redirect the user, just add 1 to the redirect count
    $_SESSION['num_redirects'] += 1;

    $query_string = http_build_query( array(
        'url' => $url,
        'roles' => serialize($roleList)
    ));
    $locationString = "Location: " . $failPage . '?' . $query_string;
    header($locationString);
    exit(); // Probably also want to kill the script here
}

それでおしまい!への最初の呼び出しに同じロジックを追加する必要があることに注意してくださいheader()。この機能を別の場所で繰り返す場合は、ヘルパー関数内にリダイレクトをカプセル化することをお勧めします。

function redirect( $url, array $params = array()) {
    if( !isset( $_SESSION['num_redirects'])) {
        $_SESSION['num_redirects'] = 0;
    }

    // Check if too many redirects have occurred
    if( $_SESSION['num_redirects'] >= MAX_NUM_REDIRECTS) {
        die( "Severe Error: Maximum number of redirects reached\n");
    }

    // If we get here, we can redirect the user, just add 1 to the redirect count
    $_SESSION['num_redirects'] += 1;

    $query_string = http_build_query( $params);
    $locationString = "Location: " . $url . ( !empty( $query_string) ? ('?' . $query_string) : '');
    header($locationString);
    exit(); // Probably also want to kill the script here
}

次に、次のように呼び出すことができます。

if(!$granted){
    redirect( $failPage, array(
        'url' => $url,
        'roles' => serialize($roleList)
    ));
}

このようにして、リダイレクトに必要なすべてのロジックをその 1 つの関数内にカプセル化できます。

于 2012-07-27T22:07:28.367 に答える