[[../]]
*任意のページごとの閲覧・編集制限 -- [[Ynak]] [#b0e3a2f6]
*任意のページごとの閲覧・編集制限 -- [[Ynak]] [#v9339fac]

※旧「BASIC認証で任意のページごとに閲覧制限や編集制限をかける方法」。名前が長すぎなので修正しました。
※旧「BASIC認証で任意のページごとに閲覧制限や編集制限をかける方法~1.4対応版」。名前が長すぎたので修正しました。--[[Ynak]]

#contents

**やりたいこと [#f26d6529]
任意のページごとに閲覧制限や編集制限をかけたい。((Wikiの思想に逆行するのは重々承知の上です。))
**やりたいこと [#n42484d9]
任意のページごとに閲覧制限や編集制限をかけたい。

具体的には
+××ページは全員が閲覧できるが、内容を編集できるのは○○だけ。
+△△ページは□□だけが閲覧・編集できる。
+○△ページは□□だけが閲覧できるが、編集は○○だけ。
+○△ページは○○と□□が閲覧できるが、編集は○○だけ。

というようなことをやりたい。

**現状 [#n98e6dfb]
-他のWikiEnginesでは、実装されているものもある。
-PukiWiki1.3.xでは、実装されていない。
-かろうじて使用できるのがページ内容の凍結であるが、PukiWiki全体で1つのパスワード設定しかできないので、運用面で困る。
**現状 [#adb35022]
-PukiWiki1.3.4では、[[../任意のページごとの閲覧・編集制限]]の改造により対応した。
-PukiWiki1.4rc2では、[[PukiWiki/1.4/BasicAuth]]で紹介された方法が実装されているが、最終目標には足りない。

**方針 [#tb83a099]
[[PukiWiki/1.4/BasicAuth]]で紹介されている方法をそのまま使えば、全ページに対する編集権限を設定できる。
**方針 [#q932cfc2]
PukiWiki1.3.4と同様の方法で実装する。

更に、コメント部で紹介された方法((多少修正が必要であるが。))を使用すれば、正規表現で定義されたページ名に対する編集権限を1セット設定できる。

しかし、最終目標には少々足りていないので、更なる改造が必要。

**仕様 [#s05d1c04]
**仕様 [#k68a7826]
pukiwiki.ini.phpで

|ユーザID|パスワード|
|正規表現によるページ名のパターン|閲覧できるユーザID(カンマ区切り)|
|正規表現によるページ名のパターン|編集できるユーザID(カンマ区切り)|

を定義することで、ちょっとUNIXライクな読み書き((関係ないけど、送りがなを詰めると''読書''になるって知ってた?))権限の設定ができる。
を定義することで、ちょっとUNIXライクな読み書き権限の設定ができる。

更に、1.4対応版では以下に対応した。
-単語検索で、閲覧制限のかかったページ内容を検索対象にする・しないを選択できる。
--現在ログイン中のユーザに閲覧許可されたページの内容は検索対象となり、許可されないページの内容は無視される。
--ページ名は閲覧制限の対象外であるため、常に検索対象となる。
-includeプラグインで、閲覧制限のかかったページのincludeを行った場合は、現在ログイン中のユーザに閲覧許可されたページはincludeされ、許可されないページの内容はincludeされない。
-calendar_viewerプラグインで、(以下同文)。

**改造方法((diff形式じゃなくてすみません。)) [#rb4e61a7]
PukiWiki1.3.4をベースに改造する方法は以下の通り。

***ja.lng [#sc6cff98]
**改造方法((diff形式じゃなくてすみません。)) [#z09a482c]
PukiWiki1.4rc2をベースに改造する方法は以下の通り。

***ja.lng [#zc8f5ab3]
以下を追加する。
 $_msg_auth = 'PukiWikiAuth';
 $_title_cannotread = '$1 は閲覧できません';

***en.lng [#v8077a9e]
***en.lng [#l587107a]
以下を追加する。
 $_msg_auth = 'PukiWikiAuth';
 $_title_cannotread = '$1 is not readable';

***func.php [#ja82bba5]
以下を追加する。
 /////////////////////////////////////////////////////////////////
 // Basic認証による権限チェック
 function edit_auth() {
      global $get, $_title_cannotedit;
      global $edit_auth, $auth_users, $_msg_auth;
      global $vars, $edit_auth_pages;
  
     // 編集認証フラグをチェック (システム全体として編集認証するかどうか)
      if (!$edit_auth) {
          return;
      }
 
      // 認証が必要なページかどうかをチェック
      $edit_auth_users = "";
      while (list($key, $val) = each($edit_auth_pages)) {
          if (preg_match($key, $vars['page'])) {
              $edit_auth_users = $val;
             break;
         }
      }
      if ($edit_auth_users == "") {
          return;
      }
 
     // 認証を行う
     if ((!isset($_SERVER['PHP_AUTH_USER']) or
         !preg_match("/".$_SERVER['PHP_AUTH_USER']."/", $edit_auth_users) or
          !array_key_exists($_SERVER['PHP_AUTH_USER'], $auth_users) or
          $auth_users[$_SERVER['PHP_AUTH_USER']] != $_SERVER['PHP_AUTH_PW']))
      {
          header('WWW-Authenticate: Basic realm="'.$_msg_auth.'"');
          header('HTTP/1.0 401 Unauthorized');
          // press cancel.
          $body = $title = str_replace('$1', htmlspecialchars(strip_bracket($get['page'])), $_title_cannotedit);
          $page = str_replace('$1',make_search($get['page']),$_title_cannotedit);
          catbody($title, $page, $body);
          exit;
      }
  }
 
 function read_auth() {
      global $get, $_title_cannotread;
      global $read_auth, $auth_users, $_msg_auth;
      global $vars, $read_auth_pages;
  
     // 閲覧認証フラグをチェック (システム全体として編集認証するかどうか)
      if (!$read_auth) {
          return;
      }
 
      // 認証が必要なページかどうかをチェック
      $read_auth_users = "";
      while (list($key, $val) = each($read_auth_pages)) {
          if (preg_match($key, $vars['page'])) {
              $read_auth_users = $val;
             break;
         }
      }
      if ($read_auth_users == "") {
          return;
      }
 
     // 認証を行う
     if ((!isset($_SERVER['PHP_AUTH_USER']) or
         !preg_match("/".$_SERVER['PHP_AUTH_USER']."/", $read_auth_users) or
          !array_key_exists($_SERVER['PHP_AUTH_USER'], $auth_users) or
          $auth_users[$_SERVER['PHP_AUTH_USER']] != $_SERVER['PHP_AUTH_PW']))
      {
          header('WWW-Authenticate: Basic realm="'.$_msg_auth.'"');
          header('HTTP/1.0 401 Unauthorized');
          // press cancel.
          $body = $title = str_replace('$1', htmlspecialchars(strip_bracket($get['page'])), $_title_cannotread);
          $page = str_replace('$1',make_search($get['page']),$_title_cannotread);
          catbody($title, $page, $body);
          exit;
      }
 }
***func.php [#s255aec7]
以下をコメントアウトする。
-111行付近 edit_auth
    //// 編集時の認証 ★まるごとコメントアウト
    //function edit_auth($page)
    //{
    //    global $edit_auth,$edit_auth_users,$_msg_auth,$_title_cannotedit;
    //
    //    if ($edit_auth and
    //        (!isset($_SERVER['PHP_AUTH_USER']) or
    //         !array_key_exists($_SERVER['PHP_AUTH_USER'],$edit_auth_users) or
    //         $edit_auth_users[$_SERVER['PHP_AUTH_USER']] != $_SERVER['PHP_AUTH_PW']))
    //    {
    //        header('WWW-Authenticate: Basic realm="'.$_msg_auth.'"');
    //        header('HTTP/1.0 401 Unauthorized');
    //        // press cancel.
    //        $body = $title = str_replace('$1',htmlspecialchars(strip_bracket($page)),$_title_cannotedit);
    //        $page = str_replace('$1',make_search($page),$_title_cannotedit);
    //        
    //        catbody($title,$page,$body);
    //        exit;
    //    }
    //}

***pukiwiki.php [#mfc298c7]
編集制限が必要な追加・編集・プレビュー等の箇所に
 edit_auth();
を、閲覧制限が必要な表示関係の箇所に
 read_auth();
を挿入する。
以下のように修正する。
-86行付近 check_editable 
    // ★まんべんなく変更してるのでまるごと置換した方がよいかも。
    // 編集不可能なページを編集しようとしたとき
    function check_editable($page, $auth_flag, $exit_flag)
    {
        global $script,$_title_cannotedit,$_msg_unfreeze;
        
        if (edit_auth($page, $auth_flag) and is_editable($page))
        {
            return true;
        }
        
        if ($exit_flag) {
            $body = $title = str_replace('$1',htmlspecialchars(strip_bracket($page)),$_title_cannotedit);
            if(is_freeze($page))
            {
                $body .= "(<a href=\"$script?cmd=unfreeze&amp;page=".
                    rawurlencode($page)."\">$_msg_unfreeze</a>)";
            }
            
            // Modified by Ynak -- fix bugs
            $page = str_replace('$1',make_search($page),$_title_cannotedit);
            
            catbody($title,$page,$body);
            exit;
        }
        return false;
    }

-103行付近
    // 追加
    else if(arg_check("add"))
-167行付近
    // 検索
    function do_search($word,$type='AND',$non_format=FALSE)
    {
        edit_auth(); // ★これを挿入!
-113行付近
    // 編集
    else if(arg_check("edit"))
    {
        edit_auth(); // ★これを挿入!
-124行付近
    // プレビュー
    else if(arg_check("preview") || $post["preview"] || $post["template"])
    {
        edit_auth(); // ★これを挿入!
-195行付近
    // 書き込みもしくは追加もしくはコメントの挿入
    else if($post["write"])
    {
        edit_auth(); // ★これを挿入!
-384行付近
    // 差分の表示
    else if(arg_check("diff"))
    {
        read_auth(); // ★これを挿入!
-462行付近
    // バックアップ
    else if($do_backup && arg_check("backup"))
    {
        read_auth(); // ★これを挿入!
-629行付近
        // WikiName、BracketNameが示すページを表示
        if(is_page($get["page"]))
        global $script,$vars,$whatsnew,$non_list,$search_non_list;
        global $_msg_andresult,$_msg_orresult,$_msg_notfoundresult;
        global $search_auth; // ★これを追加
        
        $database = array();
        $retval = array();
        
        $b_type = ($type == 'AND'); // AND:TRUE OR:FALSE
        $keys = preg_split('/\s+/',preg_quote($word,'/'),-1,PREG_SPLIT_NO_EMPTY);
        
        $_pages = get_existpages();
        $pages = array();
        
        foreach ($_pages as $page)
        {
            read_auth(); // ★これを挿入!
-641行付近
        else if(preg_match("/($InterWikiName)/",$get["page"],$match))
            if ($page == $whatsnew
                or (!$search_non_list and preg_match("/$non_list/",$page)))
            {
                continue;
            }
            
            // ★このif~else文を追加
            // 検索対象ページの制限をかけるかどうか (ページ名は制限外)
            if ($search_auth and !check_readable($page,false,false)) {
                $source = get_source(); // 検索対象ページ内容を空に。
            } else {
                $source = get_source($page);
            }
            
            if (!$non_format)
            {
                array_unshift($source,$page); // ページ名も検索対象に
            }

以下をまるごと追加する。
-一番最後とか
    // 編集認証
    // 第2,第3引数を変更する用途は今のところないが、閲覧との対象性を持たせた。
    function edit_auth($page, $auth_flag) {
        global $_title_cannotedit,$edit_auth,$auth_users,$_msg_auth,$edit_auth_pages;
     
        // 編集認証フラグをチェック (システム全体として編集認証するかどうか)
        if (!$edit_auth) {
            return true;
        }
    
        // 認証が必要なページかどうかをチェック
        $edit_auth_users = "";
        reset($edit_auth_pages);
        while (list($key, $val) = each($edit_auth_pages)) {
             if (preg_match($key, $page)) {
                 $edit_auth_users = $val;
                break;
            }
        }
        if ($edit_auth_users == "") {
            return true;
        }
    
        // 認証を行う
        if ((!isset($_SERVER['PHP_AUTH_USER']) or
            !preg_match("/".$_SERVER['PHP_AUTH_USER']."/", $edit_auth_users) or
            !array_key_exists($_SERVER['PHP_AUTH_USER'], $auth_users) or
            $auth_users[$_SERVER['PHP_AUTH_USER']] != $_SERVER['PHP_AUTH_PW']))
        {
            read_auth(); // ★これを挿入!
-722行付近
        if(preg_match("/^(($BracketName)|($WikiName))$/",$get["page"])) {
            edit_auth(); // ★これを挿入!
-736行付近
    // 何も指定されない場合、トップページを表示
    else
    {
        read_auth(); // ★これを挿入!
            if ($auth_flag) {
                header('WWW-Authenticate: Basic realm="'.$_msg_auth.'"');
                header('HTTP/1.0 401 Unauthorized');
            }
            return false;
        }
        return true;
    }
    
    // 閲覧不可能なページを閲覧しようとしたとき (?)
    // ※あまり必要性を感じないが、editの場合と対称性を持たせるために導入。
    function check_readable($page, $auth_flag, $exit_flag) {
        global $_title_cannotread;
        
        if (read_auth($page, $auth_flag)) {
            return true;
        }
        if ($exit_flag) {
            $body = $title = str_replace('$1',htmlspecialchars(strip_bracket($page)), $_title_cannotread);
            $page = str_replace('$1',make_search($page),$_title_cannotread);
            catbody($title, $page, $body);
            exit;
        }
        return false;
    }
    
    // 閲覧認証
    function read_auth($page, $auth_flag) {
        global $read_auth,$auth_users,$_msg_auth,$read_auth_pages;
     
        // 閲覧認証フラグをチェック
        if (!$read_auth) {
            return true;
        }
    
        // 認証が必要なページかどうかをチェック
        $read_auth_users = "";
        reset($read_auth_pages);
        while (list($key, $val) = each($read_auth_pages)) {
             if (preg_match($key, $page)) {
                 $read_auth_users = $val;
                break;
            }
        }
        if ($read_auth_users == "") {
            return true;
        }
    
        // 現在のログイン状態で認証チェック
        if ((!isset($_SERVER['PHP_AUTH_USER']) or
            !preg_match("/".$_SERVER['PHP_AUTH_USER']."/", $read_auth_users) or
             !array_key_exists($_SERVER['PHP_AUTH_USER'], $auth_users) or
            $auth_users[$_SERVER['PHP_AUTH_USER']] != $_SERVER['PHP_AUTH_PW']))
        {
            if ($auth_flag) {
                header('WWW-Authenticate: Basic realm="'.$_msg_auth.'"');
                header('HTTP/1.0 401 Unauthorized');
            }
            return false;
        }
        return true;
    }

***pukiwiki.ini.php [#hd8d4502]
***plugin/read.ini.php [#j9ecdfd0]
以下のように修正する。
-13行付近
    // WikiName、BracketNameが示すページを表示
    if (is_page($get['page'])) {
        check_readable($get['page'],true,true); // ★これを追加

***plugin/edit.ini.php [#p47b1f5d]
以下のように修正する。
-13行付近
    // check_editable($vars['page']); // ★コメントアウト
    check_editable($vars['page'], true, true); // ★追加

***plugin/diff.ini.php [#x32cfbaa]
以下のように修正する。
-11行付近
    global $_msg_notfound,$_msg_goto,$_msg_addline,$_msg_delline,$_title_diff;
    
    check_readable($get['page'],true,true); // ★追加

***plugin/backup.ini.php [#lf545e64]
以下のように修正する。
-16行付近
    if (!$do_backup) { return; }
    
    check_readable($get['page'], true, true); // ★追加

***plugin/calendar_viewer.ini.php [#bf9dc5ab]
以下のように修正する。
-180行付近
    $vars["page"] = $page;
    
    // ★このif~else文を追加
    // 現状で閲覧許可がある場合だけ表示する
    if (check_readable($page,false,false)) {
        $body = convert_html(get_source($page));
    } else {
        $body = $page." は閲覧制限がかかっているためcalendar_viewerによる参照はできません";
    }
    $r_page = rawurlencode($page);

***plugin/include.ini.php [#f2431a83]
以下のように修正する。
-37行付近
    $_page = $vars['page'];
    $get['page'] = $post['page'] = $vars['page'] = $page;
    // $body = convert_html(get_source($page)); // ★これをコメントアウトして
    // ★この下のif~elseを追加する
    // includeのときは、認証画面をいちいち出さず、後始末もこちらでつける
    if (check_readable($page, false, false)) {
        $body = convert_html(get_source($page));
    } else {
        $body = $page." は閲覧制限がかかっているためincludeできません";
    }

***plugin/そのほか [#n2b34772]
-calendar_edit.ini.php
-calendar_read.ini.php

無効化するのが望ましい?((よくわからなかったので自信なし。))

***pukiwiki.ini.php [#ta903a47]
以下のように設定を追加する。

下記は設定例のため、設定内容は適切に変更すること。

 //////////////////////////////////////////////////
 // 閲覧・編集制限の設定
 // 認証のアカウント
 // ユーザ名とパスワードを記入。
 $auth_users = array(
  'foo' => 'foo_passwd',
  'bar' => 'bar_passwd',
  'hoge' => 'hoge_passwd',
 );
 
 // 編集時に認証が必要か
 $edit_auth = 1;
  
 // 編集認証をかけるページ名のパターンを正規表現で設定する。
 // マッチしたページに編集認証をかける。
 // カンマ区切りで複数ユーザを書いても良い。
 $edit_auth_pages = array(
  '/Barの公開日記/' => 'bar',
  '/ひきこもるほげ/' => 'hoge',
  '/(ネタバレ|ねたばれ)/' => 'foo',
 );
 
 // 閲覧時に認証が必要か
 $read_auth = 1;
  
 // 閲覧認証をかけるページ名のパターンを正規表現で設定する。
 // マッチしたページに編集認証をかける。
 // カンマ区切りで複数ユーザを書いても良い。
 $read_auth_pages = array(
  '/ひきこもるほげ/' => 'hoge',
  '/(ネタバレ|ねたばれ)/' => 'foo,bar,hoge',
 );
    //////////////////////////////////////////////////
    // 閲覧・編集制限の設定
    // 認証のアカウント
    // ユーザ名とパスワードを記入。
    $auth_users = array(
    'foo' => 'foo_passwd',
    'bar' => 'bar_passwd',
    'hoge' => 'hoge_passwd',
    );
    
    // 編集時に認証が必要か
    $edit_auth = 1;
    
    // 編集認証をかけるページ名のパターンを正規表現で設定する。
    // マッチしたページに編集認証をかける。
    // カンマ区切りで複数ユーザを書いても良い。
    $edit_auth_pages = array(
    '/Barの公開日記/' => 'bar',
    '/ひきこもるほげ/' => 'hoge',
    '/(ネタバレ|ねたばれ)/' => 'foo',
    );
    
    // 閲覧時に認証が必要か
    $read_auth = 1;
    
    // 閲覧認証をかけるページ名のパターンを正規表現で設定する。
    // マッチしたページに編集認証をかける。
    // カンマ区切りで複数ユーザを書いても良い。
    $read_auth_pages = array(
    '/ひきこもるほげ/' => 'hoge',
    '/(ネタバレ|ねたばれ)/' => 'foo,bar,hoge',
    );
    
    // 検索時に認証が必要か
    // 0: 閲覧が許可されていないページ内容も検索対象とする
    // 1: 検索時のログインユーザに許可されたページのみ検索対象とする
    // ※どちらの場合も、ページ名は公開情報なので検索対象となる。
    $search_auth = 0;

上の例では
-「Barの公開日記」は、誰でも閲覧できて、barだけが編集できる。
-「ひきこもるほげ」ページは、hogeだけが閲覧・編集できる。
-「映画紹介~ネタバレ注意」ページは、foo, bar, hogeの三人だけが閲覧できて、かつ、編集はfooだけが可能。
のような設定となる。

かなり強引にUNIXっぽく表現すると、こうなる。
 アクセス権   ユーザID   グループ   ページ名
 -rw----r--   bar        なし       Barの公開日記
 -rw-------   hoge       なし       HogeOnly
 -rw-r-----   foo        hoges      映画紹介~ネタバレ注意

※グループhogesには、barとhogeが所属するとする。

**注意点 [#ae8cccdb]
-初めてPHPをさわった人間が改造しているので、実装的に問題があるかも。
**注意点 [#s0de784b]
-PHPをさわったばかり人間が改造しているので、実装的に問題があるかも。
--添削希望。

-閲覧認証をかけたページは、編集認証も必須。
--でないと、「編集」リンクから内容が読めてしまったり、挙動が怪しくなったりする。((UNIXでも、rw-(閲覧認証&編集認証の同時設定)はOKだけど、 -w-(編集認証のみ)はいろいろと問題が発生するのと似てる。))
--編集可能ユーザは、閲覧可能ユーザのサブセットとなるように設定すること。

-edit_auth_pagesなどのvalue部分と、BASIC認証のユーザ名の比較をpreg_matchで行っているので、あるユーザIDのサブセットとなるようなユーザIDがあるとややこしくなる。
--例えば、'hogefoobar'というユーザIDがあって、このユーザだけが編集できるようなページを以下のように設定する状況を考える。
 $edit_auth_pages = array(
  '/Secret/' => 'hogefoobar',
 );
--ここでもし、'hoge'、'foo'、'bar'というユーザIDが存在していれば、そのいずれかのユーザIDでBASIC認証を突破すれば、このSecretページにアクセスできてしまう。
--とりあえず、pukiwiki.ini.phpにユーザID設定をする人が気を付ければ良いので、気にしていませんが、良い回避法があれば修正してください。

*コメント [#fa4b735d]
-うおお、 おみごと。ページ単位の認証ってニーズ高いですもんね。 -- [[yateeight]] &new{2003-05-17 (土) 17:10:37};
-実は、オートリンクとNoBracketNameが素敵なので1.4に乗り換えようとしてます。で、1.4も同じように改造しようとしてますが、1.3.4とだいぶ作りが違うので難儀してるところです。 -- [[Ynak]] &new{2003-05-17 (土) 20:30:32};
-おっと、includeプラグインで無権限者による閲覧ができる穴発見。 -- [[Ynak]] &new{2003-05-17 (土) 21:10:03};
-他にもcalendar_viewerとかその手のプラグインで続々抜け穴を発見。プラグインを無効にするか、プラグインの頭のところでread_auth()を呼ぶようにしないとだめっぽいです。 -- [[Ynak]] &new{2003-05-17 (土) 21:23:11};
-編集系プラグインはあまりないですが、閲覧系プラグインは結構あるので対処も大変ですね・・・。 -- [[Ynak]] &new{2003-05-17 (土) 21:24:26};
-暫定対処として、include,calendar_read,calendar_editは使わないのではずして、calendar_viewerは改造して第1引数を無効にしてみました。 -- [[Ynak]] &new{2003-05-17 (土) 23:15:53};
-ページ単位じゃなくって、全体(どのページも)に認証かけるのはどうすればよろしいのでしょうか?「新規」も制限かけたいのですが。 -- [[Naox]] &new{2003-05-18 (日) 09:12:37};
-Wikino -- [[Naox]] &new{2003-05-18 (日) 09:13:42};
-全ページに均一に認証をかけるのであれば、ページ名を"/.*/"とかにするとか、[[PukiWiki/1.4/BasicAuth]]のそのままの方法でやるとかが良いと思います。 -- [[Ynak]] &new{2003-05-18 (日) 12:00:09};
-あと、ここの改造方法では、新規ページ作成も制限対象に含まれています。 -- [[Ynak]] &new{2003-05-18 (日) 12:02:16};
-./wiki/なディレクトリを、Webで公開しているディレクトリであれば、その文書ファイル名を直接指定したら読めませんか?ということで、そういう場合の読者の無意味さがあるので、注意書きでも追加しないとねぇ。ということが1点。ページ単位での制御を行う上で、ページを追加時に、この php に定義を追加していくことの運用の手間というところを、なんとかすると、もっとよくなると思います。の2点。で、config じゃないですけど、とあるページで制御しつつ、そのページに制限を行う。の組み合わせはどうかなぁ?と。 -- [[upk]] &new{2003-05-18 (日) 16:05:06};
*コメント [#r335f6c6]
-できるだけ1.4の設計思想(?)を優先して、きれいにまとめてみたつもりです。 -- [[Ynak]] &new{2003-05-20 (火) 23:28:16};

#comment


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Site admin: PukiWiki Development Team

PukiWiki 1.5.4+ © 2001-2022 PukiWiki Development Team. Powered by PHP 8.2.12. HTML convert time: 1.636 sec.

SourceForge