# -*- coding: utf-8 -*-

# Based on the WikiAccessControl by http://syo.cocolog-nifty.com/freely/tracwikiaccesscontrol.html

from trac.core import *
from trac.perm import IPermissionRequestor, IPermissionGroupProvider, IPermissionPolicy
from trac.wiki.model import WikiPage
from trac.config import ListOption
import re

class WikiPermissionPolicy(Component):
    u"""Wikiページのアクセス制限を細かく設定するためのプラグイン。
    PrivateWikiPluginを参考にしつつ作成。
    trac.iniのpermission_policiesにおいて
    WikiAccessControl,DefaultPermissionPolicy
        のような感じでWikiAccessControlを追加する必要があります。
        （なお、順番として先に指定する必要があります）
    """

    implements(IPermissionRequestor, IPermissionPolicy)

    # 以下の処理がいったい何を意味しているのか・・・？
    group_providers = ExtensionPoint(IPermissionGroupProvider)

    wikipages = ListOption('accesscontrol-wiki', 'control_pages',
                           doc=u"""アクセス制限を適用するwikiページ。
                           適用対象ページ名を正規表現で指定します。
                           複数ある場合は、カンマ区切りで指定可能です。
                           （そのため、カンマを含むページ名（あるのか？）を指定すると誤動作する可能性があります）
                           なお、個々のページに対してアクセスが可能な権限／グループは別途iniファイルに定義する必要があります。
                           """)
    """
        設定方法としては以下のような感じ
    [accesscontrol-wiki]
    control_pages = testpage
    testpage.action = WIKI_MODIFY
    testpage.regex = testpage.*,dumm.

    このような設定の場合、
    WIKI_ACCESS_TESTPAGE というPERMISSIONが追加されます。
    testpage.regex で指定されたページに関して WIKI_MODIFY の操作を実行するためには、
    WIKI_ACCESS_TESTPAGE が必要となります。
    (例え全ユーザに置いてWIKI_MODIFYが割り当てられていても、WIKI_ACCESS_TESTPAGEが
    割り当てられていないユーザに置いては、「このページを編集する」ボタンは表示されません)
    
    なお、WIKI_ACCESS_ALL というPERMISSIONも自動的に追加されます。
    このPERMISSIONが割り当てられているユーザは全ての WIKI_ACCESS_* PERMISSIONが付与されたことになります。
    """

    # IPermissionRequestor methods
    def get_permission_actions(self):
        # 権限の追加を行う。
        perm_actions = ['WIKI_ACCESS_' + a.upper() for a in self.wikipages]
        return perm_actions + [ ('WIKI_ACCESS_ALL', perm_actions) ]

    # IPermissionPolicy method
    def check_permission(self, action, username, resource, perm):
        # 戻り値がFalseの場合はその時点でアクセスを拒否。
        # Noneの場合は、次のIPermissionPolicy処理に処理を譲渡。
        # Trueの場合は、アクセスを許可（この場合、次のIPermissionPolicyの処理は実行されない？）

        # リソース情報がない場合は、そのまま処理を終了。
        # anonymousの場合でも、条件を満たせば閲覧可能とする。
        # ⇒基本的に全てのページは閲覧不可だが、ある特定のページのみ閲覧できるような設定は可能か？？
        # 　まぁ、これに関しては将来的な課題でもいいかもしれない。
        if resource is None or resource.id is None:
            return None

        # Wikiページの処理でない場合、そのまま処理を終了。
        if resource.realm != 'wiki' or not action.startswith('WIKI_'):
            return None

        return self.check_access_control(action, username, resource, perm)

    # アクセス制御を行う
    def check_access_control(self, action, username, resource, perm):
        # Wikiページの情報を取得
        wiki = WikiPage(self.env, resource.id)
        pagename = wiki.name

        # 処理対象のページか否かをチェック。処理対象でなければそのままNoneで終了。
        targetname = None
        break_flag = False
        for w in self.wikipages:
            regex = self.config.get('accesscontrol-wiki', '%s.regex' % unicode(w))
            if not regex:
                # 正規表現が入力されていない場合はそのままページ名をセットする
                regex = w
            for r in regex.split(','):
                # 正規表現のパラメータをカンマで分割する
                if re.match(r, pagename):
                    # 処理対象のページである
                    targetname = w.upper()
                    break_flag = True
                    break
            if break_flag:
                break

        if not targetname:
            # 処理対象のページでなかった場合は、この値をNoneにする。
            return None

        targetactions = self.config.get('accesscontrol-wiki', '%s.action' % unicode(w))
        targetaction = [x.strip() for x in targetactions.split(',')]
        if not action in targetaction:
            # 処理対象のアクションでない場合はそのまま戻る
            self.env.log.info("Not in target action")
            return None

        check = 'WIKI_ACCESS_%s' % targetname
        if 'WIKI_ACCESS_ALL' in perm(resource) or check in perm(resource):
            return True
        # ここまできた場合は、閲覧は不可となる
        return False

