<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Edgewall Software
# Copyright (C) 2007 <NAME> <<EMAIL>>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: <NAME> <<EMAIL>>


"""Permission policy enforcement through an authz-like configuration file.
Refer to SVN documentation for syntax of the authz file. Groups are supported.

Each section of the config is a glob to match against a Trac resource
descriptor. These descriptors are in the form:

    <realm>:<id>@<version>[/<realm>:<id>@<version> ...]

Resources are ordered left to right, from parent to child. If any component is
inapplicable, * is substituted.

eg. An attachment on WikiStart:

    wiki:WikiStart@117/attachment/FOO.JPG@*

or the Wiki module as a whole:

    wiki:*@*


Sections are checked against the current Trac resource **IN ORDER** of
appearance in the configuration file. ORDER IS CRITICAL.

Once a section matches, the current username is matched, **IN ORDER**, against
the keys of the section. If a key is prefixed with a @ it is treated as a
group. The username will match any of 'anonymous', 'authenticated', <username>
or '*', using normal Trac permission rules.

Example configuration:

    [groups]
    administrators = athomas

    [*/attachment:*]
    * = WIKI_VIEW, TICKET_VIEW

    [wiki:WikiStart@*]
    @administrators = WIKI_ADMIN
    anonymous = WIKI_VIEW
    * = WIKI_VIEW

    # Deny access to page templates
    [wiki:PageTemplates/*]
    * =

    # Match everything else
    [*]
    @administrators = TRAC_ADMIN
    anonymous = BROWSER_VIEW, CHANGESET_VIEW, FILE_VIEW, LOG_VIEW, MILESTONE_VIEW, POLL_VIEW, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_VIEW, SEARCH_VIEW, TICKET_CREATE, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, WIKI_CREATE, WIKI_MODIFY, WIKI_VIEW
    # Give authenticated users some extra permissions
    authenticated = REPO_SEARCH, XML_RPC


"""

import os
from fnmatch import fnmatch
from trac.core import *
from trac.config import Option
from trac.util.compat import set
from trac.perm import PermissionSystem, IPermissionPolicy
from configobj import ConfigObj


class AuthzPolicy(Component):
    implements(IPermissionPolicy)

    authz_file = Option('authz_policy', 'authz_file', None,
                        'Location of authz policy configuration file.')

    authz = None
    authz_mtime = None

    # IPermissionPolicy methods
    def check_permission(self, username, action, context):
        if self.authz_file and not self.authz_mtime or \
                os.path.getmtime(self.get_authz_file()) > self.authz_mtime:
            self.parse_authz()
        ctx_key = self.flatten_context(context)
        permissions = self.authz_permissions(ctx_key, username)
        if permissions is None:
            return None
        elif permissions:
            permissions = PermissionSystem(self.env).expand_actions(permissions)
        return action in permissions

    # Internal methods
    def get_authz_file(self):
        f = self.authz_file
        return os.path.isabs(f) and f or os.path.join(self.env.path, f)
    
    def parse_authz(self):
        self.env.log.debug('Parsing authz security policy %s' %
                           self.get_authz_file())
        self.authz = ConfigObj(self.get_authz_file())
        self.groups_by_user = {}
        for group, users in self.authz.get('groups', {}).iteritems():
            if isinstance(users, basestring):
                users = [users]
            for user in users:
                self.groups_by_user.setdefault(user, set()).add('@' + group)
        self.authz_mtime = os.path.getmtime(self.get_authz_file())

    def flatten_context(self, context):
        def flatten(context):
            # XXX Use of root realm is inconsistent. Sometimes it is the parent
            # object, other times not. XXX
            if not context or not (context.realm or context.id):
                return []
            parent = flatten(context.parent)
            return parent + ['%s:%s@%s' % (context.realm or '*',
                                           context.id or '*',
                                           context.version or '*')]
        return '/'.join(flatten(context))

    def authz_permissions(self, ctx_key, username):
        valid_users = ['*', 'anonymous']
        if username and username != 'anonymous':
            valid_users += ['authenticated', username]
        for ctx_glob in [a for a in self.authz.sections if a != 'groups']:
            if fnmatch(ctx_key, ctx_glob):
                section = self.authz[ctx_glob]
                for who, permissions in section.iteritems():
                    if who in valid_users or \
                            who in self.groups_by_user.get(username, []):
                        #self.env.log.debug('%s matched section %s' % (ctx_key, ctx_glob))
                        if isinstance(permissions, basestring):
                            return [permissions]
                        else:
                            return permissions
        return None
<commit_msg>AuthzPolicy: updated the plugin doc with some needy informations.<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Edgewall Software
# Copyright (C) 2007 <NAME> <<EMAIL>>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: <NAME> <<EMAIL>>


"""Permission policy enforcement through an authz-like configuration file.
Refer to SVN documentation for syntax of the authz file. Groups are supported.

Note that this plugin requires the `configobj` package:

    http://www.voidspace.org.uk/python/configobj.html
    
You should be able to install it by doing a simple `easy_install configobj`


Enabling this policy requires listing it in the trac.ini:

[trac]
permission_policies = AuthzPolicy, DefaultPermissionPolicy

[authz_policy]
authz_file = conf/authzpolicy.conf


This means that the AuthzPolicy permissions will be checked first, and only
if no rule is found will the DefaultPermissionPolicy be used.

The authzpolicy.conf file is a .ini style configuration file.

 - Each section of the config is a glob pattern used to match against a Trac
resource descriptor. These descriptors are in the form:

     <realm>:<id>@<version>[/<realm>:<id>@<version> ...]

   Resources are ordered left to right, from parent to child.
   If any component is inapplicable, * is substituted.

   e.g. An attachment on WikiStart:

     wiki:WikiStart@117/attachment/FOO.JPG@*

   any of the following sections would match it:

     [wiki:*]
     [wiki:WikiStart*]
     [wiki:WikiStart@*]
     [wiki:WikiStart@*/attachment/*]

   but be careful, not this one:

     [wiki:WikiStart@117/attachment/FOO.JPG]

   as the above won't match the @ part in the attachment resource descriptor.


 - Sections are checked against the current Trac resource **IN ORDER** of
   appearance in the configuration file. ORDER IS CRITICAL.

 - Once a section matches, the current username is matched, **IN ORDER**,
   against the keys of the section. If a key is prefixed with a @, it is
   treated as a group. The username will match any of 'anonymous',
   'authenticated', <username> or '*', using normal Trac permission rules.

Example configuration:

    [groups]
    administrators = athomas

    [*/attachment:*]
    * = WIKI_VIEW, TICKET_VIEW

    [wiki:WikiStart@*]
    @administrators = WIKI_ADMIN
    anonymous = WIKI_VIEW
    * = WIKI_VIEW

    # Deny access to page templates
    [wiki:PageTemplates/*]
    * =

    # Match everything else
    [*]
    @administrators = TRAC_ADMIN
    anonymous = BROWSER_VIEW, CHANGESET_VIEW, FILE_VIEW, LOG_VIEW, MILESTONE_VIEW, POLL_VIEW, REPORT_SQL_VIEW, REPORT_VIEW, ROADMAP_VIEW, SEARCH_VIEW, TICKET_CREATE, TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, WIKI_CREATE, WIKI_MODIFY, WIKI_VIEW
    # Give authenticated users some extra permissions
    authenticated = REPO_SEARCH, XML_RPC


"""

import os
from fnmatch import fnmatch
from trac.core import *
from trac.config import Option
from trac.util.compat import set
from trac.perm import PermissionSystem, IPermissionPolicy
from configobj import ConfigObj


class AuthzPolicy(Component):
    implements(IPermissionPolicy)

    authz_file = Option('authz_policy', 'authz_file', None,
                        'Location of authz policy configuration file.')

    authz = None
    authz_mtime = None

    # IPermissionPolicy methods
    def check_permission(self, username, action, context):
        if self.authz_file and not self.authz_mtime or \
                os.path.getmtime(self.get_authz_file()) > self.authz_mtime:
            self.parse_authz()
        ctx_key = self.flatten_context(context)
        permissions = self.authz_permissions(ctx_key, username)
        if permissions is None:
            return None
        elif permissions:
            permissions = PermissionSystem(self.env).expand_actions(permissions)
        return action in permissions

    # Internal methods
    def get_authz_file(self):
        f = self.authz_file
        return os.path.isabs(f) and f or os.path.join(self.env.path, f)
    
    def parse_authz(self):
        self.env.log.debug('Parsing authz security policy %s' %
                           self.get_authz_file())
        self.authz = ConfigObj(self.get_authz_file())
        self.groups_by_user = {}
        for group, users in self.authz.get('groups', {}).iteritems():
            if isinstance(users, basestring):
                users = [users]
            for user in users:
                self.groups_by_user.setdefault(user, set()).add('@' + group)
        self.authz_mtime = os.path.getmtime(self.get_authz_file())

    def flatten_context(self, context):
        def flatten(context):
            # XXX Use of root realm is inconsistent. Sometimes it is the parent
            # object, other times not. XXX
            if not context or not (context.realm or context.id):
                return []
            parent = flatten(context.parent)
            return parent + ['%s:%s@%s' % (context.realm or '*',
                                           context.id or '*',
                                           context.version or '*')]
        return '/'.join(flatten(context))

    def authz_permissions(self, ctx_key, username):
        valid_users = ['*', 'anonymous']
        if username and username != 'anonymous':
            valid_users += ['authenticated', username]
        for ctx_glob in [a for a in self.authz.sections if a != 'groups']:
            if fnmatch(ctx_key, ctx_glob):
                section = self.authz[ctx_glob]
                for who, permissions in section.iteritems():
                    if who in valid_users or \
                            who in self.groups_by_user.get(username, []):
                        #self.env.log.debug('%s matched section %s' % (ctx_key, ctx_glob))
                        if isinstance(permissions, basestring):
                            return [permissions]
                        else:
                            return permissions
        return None

{'fraction_non_alphanumeric': 0.08991246706892156, 'fraction_numerical': 0.0022945525622503613, 'mean_word_length': 2.6009791921664625, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5754076', 'n_tokens_mistral': 3448, 'n_tokens_neox': 3293, 'n_words': 1072}