<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Edgewall Software
# Copyright (C) 2007 Alec Thomas <alec@swapoff.org>
# 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: Alec Thomas <alec@swapoff.org>

from fnmatch import fnmatch
from itertools import groupby
import os

from trac.core import *
from trac.config import ConfigurationError, Option
from trac.perm import PermissionSystem, IPermissionPolicy
from trac.util import lazy
from trac.util.text import to_unicode
from trac.util.translation import _

ConfigObj = None
try:
    from configobj import ConfigObj, ConfigObjError
except ImportError:
    pass

class AuthzPolicy(Component):
    """Permission policy using an authz-like configuration file.

    Refer to SVN documentation for syntax of the authz file. Groups are
    supported.

    As the fine-grained permissions brought by this permission policy are
    often used in complement of the other pemission policies (like the
    `DefaultPermissionPolicy`), there's no need to redefine all the
    permissions here. Only additional rights or restrictions should be added.

    === Installation ===
    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 `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.

    === Configuration ===
    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. If the version pattern is
       not specified explicitely, all versions (`@*`) is added implicitly

       Example: Match the WikiStart page
       {{{
       [wiki:*]
       [wiki:WikiStart*]
       [wiki:WikiStart@*]
       [wiki:WikiStart]
       }}}

       Example: Match the attachment `wiki:WikiStart@117/attachment/FOO.JPG@*`
       on WikiStart
       {{{
       [wiki:*]
       [wiki:WikiStart*]
       [wiki:WikiStart@*]
       [wiki:WikiStart@*/attachment/*]
       [wiki:WikiStart@117/attachment/FOO.JPG]
       }}}

     - 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. If a key is prefixed with a `!`, the permission is
       denied rather than granted. 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
    }}}
    """
    implements(IPermissionPolicy)

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

    authz = None
    authz_mtime = None

    # IPermissionPolicy methods

    def check_permission(self, action, username, resource, perm):
        if ConfigObj is None:
            self.log.error('configobj package not found')
            return None

        if self.authz_file and not self.authz_mtime or \
                os.path.getmtime(self.get_authz_file) > self.authz_mtime:
            self.parse_authz()
        resource_key = self.normalise_resource(resource)
        self.log.debug('Checking %s on %s', action, resource_key)
        permissions = self.authz_permissions(resource_key, username)
        if permissions is None:
            return None                 # no match, can't decide
        elif permissions == ['']:
            return False                # all actions are denied

        # FIXME: expand all permissions once for all
        ps = PermissionSystem(self.env)
        for deny, perms in groupby(permissions,
                                   key=lambda p: p.startswith('!')):
            if deny and action in ps.expand_actions([p[1:] for p in perms]):
                return False            # action is explicitly denied
            elif action in ps.expand_actions(perms):
                return True            # action is explicitly granted

        return None                    # no match for action, can't decide

    # Internal methods

    @lazy
    def get_authz_file(self):
        authz_file = self.authz_file if os.path.isabs(self.authz_file) \
                                     else os.path.join(self.env.path,
                                                       self.authz_file)
        try:
            os.stat(authz_file)
        except OSError, e:
            self.log.error("Error parsing authz permission policy file: %s",
                           to_unicode(e.strerror + ' ' + e.filename))
            raise ConfigurationError()
        return authz_file

    def parse_authz(self):
        self.log.debug('Parsing authz security policy %s',
                       self.get_authz_file)
        try:
            self.authz = ConfigObj(self.get_authz_file, encoding='utf8',
                                   raise_errors=True)
        except ConfigObjError, e:
            self.log.error("Error parsing authz permission policy file: %s",
                           to_unicode(e.message))
            raise ConfigurationError()
        groups = {}
        for group, users in self.authz.get('groups', {}).iteritems():
            if isinstance(users, basestring):
                users = [users]
            groups[group] = users

        self.groups_by_user = {}

        def add_items(group, items):
            for item in items:
                if item.startswith('@'):
                    add_items(group, groups[item[1:]])
                else:
                    self.groups_by_user.setdefault(item, set()).add(group)

        for group, users in groups.iteritems():
            add_items('@' + group, users)

        self.authz_mtime = os.path.getmtime(self.get_authz_file)

    def normalise_resource(self, resource):
        def flatten(resource):
            if not resource:
                return ['*:*@*']
            if not (resource.realm or resource.id):
                return ['%s:%s@%s' % (resource.realm or '*',
                                      resource.id or '*',
                                      resource.version or '*')]
            # XXX Due to the mixed functionality in resource we can end up with
            # ticket, ticket:1, ticket:1@10. This code naively collapses all
            # subsets of the parent resource into one. eg. ticket:1@10
            parent = resource.parent
            while parent and (resource.realm == parent.realm or
                              (resource.realm == parent.realm and
                               resource.id == parent.id)):
                parent = parent.parent
            if parent:
                parent = flatten(parent)
            else:
                parent = []
            return parent + ['%s:%s@%s' % (resource.realm or '*',
                                           resource.id or '*',
                                           resource.version or '*')]
        return '/'.join(flatten(resource))

    def authz_permissions(self, resource_key, username):
        # TODO: Handle permission negation in sections. eg. "if in this
        # ticket, remove TICKET_MODIFY"
        if username and username != 'anonymous':
            valid_users = ['*', 'authenticated', username]
        else:
            valid_users = ['*', 'anonymous']
        for resource_section in [a for a in self.authz.sections
                                 if a != 'groups']:
            resource_glob = resource_section
            if '@' not in resource_glob:
                resource_glob += '@*'

            if fnmatch(resource_key, resource_glob):
                section = self.authz[resource_section]
                for who, permissions in section.iteritems():
                    if who in valid_users or \
                            who in self.groups_by_user.get(username, []):
                        self.log.debug('%s matched section %s for user %s',
                                       resource_key, resource_glob, username)
                        if isinstance(permissions, basestring):
                            return [permissions]
                        else:
                            return permissions
        return None
<commit_msg>1.1.2dev: merge [11997] from 1.0-stable (refs #11272)<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Edgewall Software
# Copyright (C) 2007 Alec Thomas <alec@swapoff.org>
# 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: Alec Thomas <alec@swapoff.org>

from fnmatch import fnmatch
from itertools import groupby
import os

from trac.core import *
from trac.config import ConfigurationError, Option
from trac.perm import PermissionSystem, IPermissionPolicy
from trac.util import lazy
from trac.util.text import to_unicode
from trac.util.translation import _

ConfigObj = None
try:
    from configobj import ConfigObj, ConfigObjError
except ImportError:
    pass

class AuthzPolicy(Component):
    """Permission policy using an authz-like configuration file.

    Refer to SVN documentation for syntax of the authz file. Groups are
    supported.

    As the fine-grained permissions brought by this permission policy are
    often used in complement of the other pemission policies (like the
    `DefaultPermissionPolicy`), there's no need to redefine all the
    permissions here. Only additional rights or restrictions should be added.

    === Installation ===
    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 `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.

    === Configuration ===
    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. If the version pattern is
       not specified explicitely, all versions (`@*`) is added implicitly

       Example: Match the WikiStart page
       {{{
       [wiki:*]
       [wiki:WikiStart*]
       [wiki:WikiStart@*]
       [wiki:WikiStart]
       }}}

       Example: Match the attachment `wiki:WikiStart@117/attachment/FOO.JPG@*`
       on WikiStart
       {{{
       [wiki:*]
       [wiki:WikiStart*]
       [wiki:WikiStart@*]
       [wiki:WikiStart@*/attachment/*]
       [wiki:WikiStart@117/attachment/FOO.JPG]
       }}}

     - 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. If a key is prefixed with a `!`, the permission is
       denied rather than granted. 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
    }}}
    """
    implements(IPermissionPolicy)

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

    authz = None
    authz_mtime = None

    # IPermissionPolicy methods

    def check_permission(self, action, username, resource, perm):
        if ConfigObj is None:
            self.log.error('configobj package not found')
            return None

        if self.authz_file and not self.authz_mtime or \
                os.path.getmtime(self.get_authz_file) > self.authz_mtime:
            self.parse_authz()
        resource_key = self.normalise_resource(resource)
        self.log.debug('Checking %s on %s', action, resource_key)
        permissions = self.authz_permissions(resource_key, username)
        if permissions is None:
            return None                 # no match, can't decide
        elif permissions == ['']:
            return False                # all actions are denied

        # FIXME: expand all permissions once for all
        ps = PermissionSystem(self.env)
        for deny, perms in groupby(permissions,
                                   key=lambda p: p.startswith('!')):
            if deny and action in ps.expand_actions([p[1:] for p in perms]):
                return False            # action is explicitly denied
            elif action in ps.expand_actions(perms):
                return True            # action is explicitly granted

        return None                    # no match for action, can't decide

    # Internal methods

    @lazy
    def get_authz_file(self):
        authz_file = self.authz_file if os.path.isabs(self.authz_file) \
                                     else os.path.join(self.env.path,
                                                       self.authz_file)
        try:
            os.stat(authz_file)
        except OSError, e:
            self.log.error("Error parsing authz permission policy file: %s",
                           to_unicode(e.strerror + ' ' + e.filename))
            raise ConfigurationError()
        return authz_file

    def parse_authz(self):
        self.log.debug('Parsing authz security policy %s',
                       self.get_authz_file)
        try:
            self.authz = ConfigObj(self.get_authz_file, encoding='utf8',
                                   raise_errors=True)
        except ConfigObjError, e:
            self.log.error("Error parsing authz permission policy file: %s",
                           to_unicode(e))
            raise ConfigurationError()
        groups = {}
        for group, users in self.authz.get('groups', {}).iteritems():
            if isinstance(users, basestring):
                users = [users]
            groups[group] = users

        self.groups_by_user = {}

        def add_items(group, items):
            for item in items:
                if item.startswith('@'):
                    add_items(group, groups[item[1:]])
                else:
                    self.groups_by_user.setdefault(item, set()).add(group)

        for group, users in groups.iteritems():
            add_items('@' + group, users)

        self.authz_mtime = os.path.getmtime(self.get_authz_file)

    def normalise_resource(self, resource):
        def flatten(resource):
            if not resource:
                return ['*:*@*']
            if not (resource.realm or resource.id):
                return ['%s:%s@%s' % (resource.realm or '*',
                                      resource.id or '*',
                                      resource.version or '*')]
            # XXX Due to the mixed functionality in resource we can end up with
            # ticket, ticket:1, ticket:1@10. This code naively collapses all
            # subsets of the parent resource into one. eg. ticket:1@10
            parent = resource.parent
            while parent and (resource.realm == parent.realm or
                              (resource.realm == parent.realm and
                               resource.id == parent.id)):
                parent = parent.parent
            if parent:
                parent = flatten(parent)
            else:
                parent = []
            return parent + ['%s:%s@%s' % (resource.realm or '*',
                                           resource.id or '*',
                                           resource.version or '*')]
        return '/'.join(flatten(resource))

    def authz_permissions(self, resource_key, username):
        # TODO: Handle permission negation in sections. eg. "if in this
        # ticket, remove TICKET_MODIFY"
        if username and username != 'anonymous':
            valid_users = ['*', 'authenticated', username]
        else:
            valid_users = ['*', 'anonymous']
        for resource_section in [a for a in self.authz.sections
                                 if a != 'groups']:
            resource_glob = resource_section
            if '@' not in resource_glob:
                resource_glob += '@*'

            if fnmatch(resource_key, resource_glob):
                section = self.authz[resource_section]
                for who, permissions in section.iteritems():
                    if who in valid_users or \
                            who in self.groups_by_user.get(username, []):
                        self.log.debug('%s matched section %s for user %s',
                                       resource_key, resource_glob, username)
                        if isinstance(permissions, basestring):
                            return [permissions]
                        else:
                            return permissions
        return None
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Edgewall Software
# Copyright (C) 2007 Alec Thomas <alec@swapoff.org>
# 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: Alec Thomas <alec@swapoff.org>

from fnmatch import fnmatch
from itertools import groupby
import os

from trac.core import *
from trac.config import ConfigurationError, Option
from trac.perm import PermissionSystem, IPermissionPolicy
from trac.util import lazy
from trac.util.text import to_unicode
from trac.util.translation import _

ConfigObj = None
try:
    from configobj import ConfigObj, ConfigObjError
except ImportError:
    pass

class AuthzPolicy(Component):
    """Permission policy using an authz-like configuration file.

    Refer to SVN documentation for syntax of the authz file. Groups are
    supported.

    As the fine-grained permissions brought by this permission policy are
    often used in complement of the other pemission policies (like the
    `DefaultPermissionPolicy`), there's no need to redefine all the
    permissions here. Only additional rights or restrictions should be added.

    === Installation ===
    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 `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.

    === Configuration ===
    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. If the version pattern is
       not specified explicitely, all versions (`@*`) is added implicitly

       Example: Match the WikiStart page
       {{{
       [wiki:*]
       [wiki:WikiStart*]
       [wiki:WikiStart@*]
       [wiki:WikiStart]
       }}}

       Example: Match the attachment `wiki:WikiStart@117/attachment/FOO.JPG@*`
       on WikiStart
       {{{
       [wiki:*]
       [wiki:WikiStart*]
       [wiki:WikiStart@*]
       [wiki:WikiStart@*/attachment/*]
       [wiki:WikiStart@117/attachment/FOO.JPG]
       }}}

     - 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. If a key is prefixed with a `!`, the permission is
       denied rather than granted. 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
    }}}
    """
    implements(IPermissionPolicy)

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

    authz = None
    authz_mtime = None

    # IPermissionPolicy methods

    def check_permission(self, action, username, resource, perm):
        if not self.authz_mtime or \
                os.path.getmtime(self.get_authz_file) > self.authz_mtime:
            self.parse_authz()
        resource_key = self.normalise_resource(resource)
        self.log.debug('Checking %s on %s', action, resource_key)
        permissions = self.authz_permissions(resource_key, username)
        if permissions is None:
            return None                 # no match, can't decide
        elif permissions == ['']:
            return False                # all actions are denied

        # FIXME: expand all permissions once for all
        ps = PermissionSystem(self.env)
        for deny, perms in groupby(permissions,
                                   key=lambda p: p.startswith('!')):
            if deny and action in ps.expand_actions([p[1:] for p in perms]):
                return False            # action is explicitly denied
            elif action in ps.expand_actions(perms):
                return True            # action is explicitly granted

        return None                    # no match for action, can't decide

    # Internal methods

    @lazy
    def get_authz_file(self):
        if not self.authz_file:
            self.log.error('The `[authz_policy] authz_file` configuration '
                           'option in trac.ini is empty or not defined.')
            raise ConfigurationError()

        authz_file = self.authz_file if os.path.isabs(self.authz_file) \
                                     else os.path.join(self.env.path,
                                                       self.authz_file)
        try:
            os.stat(authz_file)
        except OSError, e:
            self.log.error("Error parsing authz permission policy file: %s",
                           to_unicode(e))
            raise ConfigurationError()
        return authz_file

    def parse_authz(self):
        if ConfigObj is None:
            self.log.error('ConfigObj package not found.')
            raise ConfigurationError()
        self.log.debug('Parsing authz security policy %s',
                       self.get_authz_file)
        try:
            self.authz = ConfigObj(self.get_authz_file, encoding='utf8',
                                   raise_errors=True)
        except ConfigObjError, e:
            self.log.error("Error parsing authz permission policy file: %s",
                           to_unicode(e))
            raise ConfigurationError()
        if not self.authz:
            self.log.error("The authz file is empty.")
            raise ConfigurationError()
        groups = {}
        for group, users in self.authz.get('groups', {}).iteritems():
            if isinstance(users, basestring):
                users = [users]
            groups[group] = users

        self.groups_by_user = {}

        def add_items(group, items):
            for item in items:
                if item.startswith('@'):
                    add_items(group, groups[item[1:]])
                else:
                    self.groups_by_user.setdefault(item, set()).add(group)

        for group, users in groups.iteritems():
            add_items('@' + group, users)

        self.authz_mtime = os.path.getmtime(self.get_authz_file)

    def normalise_resource(self, resource):
        def flatten(resource):
            if not resource:
                return ['*:*@*']
            if not (resource.realm or resource.id):
                return ['%s:%s@%s' % (resource.realm or '*',
                                      resource.id or '*',
                                      resource.version or '*')]
            # XXX Due to the mixed functionality in resource we can end up with
            # ticket, ticket:1, ticket:1@10. This code naively collapses all
            # subsets of the parent resource into one. eg. ticket:1@10
            parent = resource.parent
            while parent and (resource.realm == parent.realm or
                              (resource.realm == parent.realm and
                               resource.id == parent.id)):
                parent = parent.parent
            if parent:
                parent = flatten(parent)
            else:
                parent = []
            return parent + ['%s:%s@%s' % (resource.realm or '*',
                                           resource.id or '*',
                                           resource.version or '*')]
        return '/'.join(flatten(resource))

    def authz_permissions(self, resource_key, username):
        # TODO: Handle permission negation in sections. eg. "if in this
        # ticket, remove TICKET_MODIFY"
        if username and username != 'anonymous':
            valid_users = ['*', 'authenticated', username]
        else:
            valid_users = ['*', 'anonymous']
        for resource_section in [a for a in self.authz.sections
                                 if a != 'groups']:
            resource_glob = resource_section
            if '@' not in resource_glob:
                resource_glob += '@*'

            if fnmatch(resource_key, resource_glob):
                section = self.authz[resource_section]
                for who, permissions in section.iteritems():
                    if who in valid_users or \
                            who in self.groups_by_user.get(username, []):
                        self.log.debug('%s matched section %s for user %s',
                                       resource_key, resource_glob, username)
                        if isinstance(permissions, basestring):
                            return [permissions]
                        else:
                            return permissions
        return None
<commit_msg>1.1.2dev: Merged [12558] from 1.0-stable (fix for #11498)<commit_after># -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Edgewall Software
# Copyright (C) 2007 Alec Thomas <alec@swapoff.org>
# 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: Alec Thomas <alec@swapoff.org>

from fnmatch import fnmatch
from itertools import groupby
import os

from trac.core import *
from trac.config import ConfigurationError, Option
from trac.perm import PermissionSystem, IPermissionPolicy
from trac.util import lazy
from trac.util.text import to_unicode
from trac.util.translation import _

ConfigObj = None
try:
    from configobj import ConfigObj, ConfigObjError
except ImportError:
    pass

class AuthzPolicy(Component):
    """Permission policy using an authz-like configuration file.

    Refer to SVN documentation for syntax of the authz file. Groups are
    supported.

    As the fine-grained permissions brought by this permission policy are
    often used in complement of the other pemission policies (like the
    `DefaultPermissionPolicy`), there's no need to redefine all the
    permissions here. Only additional rights or restrictions should be added.

    === Installation ===
    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 `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.

    === Configuration ===
    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. If the version pattern is
       not specified explicitely, all versions (`@*`) is added implicitly

       Example: Match the WikiStart page
       {{{
       [wiki:*]
       [wiki:WikiStart*]
       [wiki:WikiStart@*]
       [wiki:WikiStart]
       }}}

       Example: Match the attachment `wiki:WikiStart@117/attachment/FOO.JPG@*`
       on WikiStart
       {{{
       [wiki:*]
       [wiki:WikiStart*]
       [wiki:WikiStart@*]
       [wiki:WikiStart@*/attachment/*]
       [wiki:WikiStart@117/attachment/FOO.JPG]
       }}}

     - 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. If a key is prefixed with a `!`, the permission is
       denied rather than granted. 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
    }}}
    """
    implements(IPermissionPolicy)

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

    authz = None
    authz_mtime = None

    # IPermissionPolicy methods

    def check_permission(self, action, username, resource, perm):
        if not self.authz_mtime or \
                os.path.getmtime(self.get_authz_file) > self.authz_mtime:
            self.parse_authz()
        resource_key = self.normalise_resource(resource)
        self.log.debug('Checking %s on %s', action, resource_key)
        permissions = self.authz_permissions(resource_key, username)
        if permissions is None:
            return None                 # no match, can't decide
        elif permissions == ['']:
            return False                # all actions are denied

        # FIXME: expand all permissions once for all
        ps = PermissionSystem(self.env)
        for deny, perms in groupby(permissions,
                                   key=lambda p: p.startswith('!')):
            if deny and action in ps.expand_actions([p[1:] for p in perms]):
                return False            # action is explicitly denied
            elif action in ps.expand_actions(perms):
                return True            # action is explicitly granted

        return None                    # no match for action, can't decide

    # Internal methods

    @lazy
    def get_authz_file(self):
        if not self.authz_file:
            self.log.error('The `[authz_policy] authz_file` configuration '
                           'option in trac.ini is empty or not defined.')
            raise ConfigurationError()

        authz_file = self.authz_file if os.path.isabs(self.authz_file) \
                                     else os.path.join(self.env.path,
                                                       self.authz_file)
        try:
            os.stat(authz_file)
        except OSError, e:
            self.log.error("Error parsing authz permission policy file: %s",
                           to_unicode(e))
            raise ConfigurationError()
        return authz_file

    def parse_authz(self):
        if ConfigObj is None:
            self.log.error('ConfigObj package not found.')
            raise ConfigurationError()
        self.log.debug('Parsing authz security policy %s',
                       self.get_authz_file)
        try:
            self.authz = ConfigObj(self.get_authz_file, encoding='utf8',
                                   raise_errors=True)
        except ConfigObjError, e:
            self.log.error("Error parsing authz permission policy file: %s",
                           to_unicode(e))
            raise ConfigurationError()
        if not self.authz:
            self.log.error("The authz file is empty.")
            raise ConfigurationError()
        groups = {}
        for group, users in self.authz.get('groups', {}).iteritems():
            if isinstance(users, basestring):
                users = [users]
            groups[group] = map(to_unicode, users)

        self.groups_by_user = {}

        def add_items(group, items):
            for item in items:
                if item.startswith('@'):
                    add_items(group, groups[item[1:]])
                else:
                    self.groups_by_user.setdefault(item, set()).add(group)

        for group, users in groups.iteritems():
            add_items('@' + group, users)

        self.authz_mtime = os.path.getmtime(self.get_authz_file)

    def normalise_resource(self, resource):
        def flatten(resource):
            if not resource:
                return ['*:*@*']
            if not (resource.realm or resource.id):
                return ['%s:%s@%s' % (resource.realm or '*',
                                      resource.id or '*',
                                      resource.version or '*')]
            # XXX Due to the mixed functionality in resource we can end up with
            # ticket, ticket:1, ticket:1@10. This code naively collapses all
            # subsets of the parent resource into one. eg. ticket:1@10
            parent = resource.parent
            while parent and (resource.realm == parent.realm or
                              (resource.realm == parent.realm and
                               resource.id == parent.id)):
                parent = parent.parent
            if parent:
                parent = flatten(parent)
            else:
                parent = []
            return parent + ['%s:%s@%s' % (resource.realm or '*',
                                           resource.id or '*',
                                           resource.version or '*')]
        return '/'.join(flatten(resource))

    def authz_permissions(self, resource_key, username):
        # TODO: Handle permission negation in sections. eg. "if in this
        # ticket, remove TICKET_MODIFY"
        if username and username != 'anonymous':
            valid_users = ['*', 'authenticated', username]
        else:
            valid_users = ['*', 'anonymous']
        for resource_section in [a for a in self.authz.sections
                                   if a != 'groups']:
            resource_glob = to_unicode(resource_section)
            if '@' not in resource_glob:
                resource_glob += '@*'

            if fnmatch(resource_key, resource_glob):
                section = self.authz[resource_section]
                for who, permissions in section.iteritems():
                    who = to_unicode(who)
                    if who in valid_users or \
                            who in self.groups_by_user.get(username, []):
                        self.log.debug('%s matched section %s for user %s',
                                       resource_key, resource_glob, username)
                        if isinstance(permissions, basestring):
                            return [permissions]
                        else:
                            return permissions
        return None
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Copyright 2015, Foxugly. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.

from django.utils.translation import ugettext_lazy as _
from django.db import models
from django import forms
from django.forms import ModelForm
from utils.models import Country, TemplateTrimester
from years.models import Year
from categories.models import Category
from django.conf import settings
from django.utils.text import slugify
import os
import uuid

class ModelTrimester(models.Model):
    name = models.TextField(_("Name of the model trimester"))
    categories = models.ManyToManyField(Category, verbose_name=_('categories'), blank=True)

    def __str__(self):
        return '[%d] %s' % (self.pk, self.name)

class ModelTrimesterForm(ModelForm):
    class Meta:
        model = ModelTrimester
        fields = '__all__'

class Company(models.Model):
    name = models.TextField(_("Name of the company"))
    slug = models.SlugField(unique=True)
    description = models.TextField(_("Description of the company"), blank=True, null=True)
    vat_number = models.CharField(_("TVA number"), unique=True, max_length=10, blank=True, null=True)
    address_1 = models.CharField(_("address"), max_length=128, blank=True, null=True)
    address_2 = models.CharField(_("address cont'd"), max_length=128, blank=True, null=True)
    zip_code = models.CharField(_("zip code"), max_length=5, blank=True, null=True)
    city = models.CharField(_("city"), max_length=128, blank=True, null=True)
    country = models.ForeignKey(Country, blank=True)
    random = models.CharField(max_length=16, blank=True, null=True)
    sales_revenue = models.IntegerField(_("Sales revenue"),
                                        choices=(
                                            (1, _("Lower than 50.000 euros")),
                                            (2, _("Between 50.000 euros and 250.000 euros")),
                                            (3, _("Between 250.000 euros and 700.000 euros")),
                                            (4, _("Between 700.000 euros and 4.500.000 euros")),
                                            (5, _("More than 4.500.000 euros"))
                                        ),
                                        default=1)
    number_employees = models.IntegerField(_("Number of employees"),
                                           choices=(
                                               (1, _("1")),
                                               (2, _("Between 1 and 10")),
                                               (3, _("Between 10 and 50")),
                                               (4, _("More than 50"))
                                           ),
                                           default=1)
    creation_date = models.DateField(_("Creation date"), blank=True, null=True)
    website = models.URLField(_("Website"), blank=True, null=True)
    years = models.ManyToManyField(Year, blank=True)
    active = models.BooleanField(_('active'), default=False)
    favorite = models.BooleanField(_('favorite'), default=False)
    model_trimester = models.ForeignKey(ModelTrimester, null=True, blank=True)

    def as_json(self):
        return dict(id=self.id, name=self.name)

    def get_name(self):
        return self.name.encode('utf-8')

    def add_year(self, year):
        self.years.add(year)

    def get_years(self):
        return self.years.order_by('fiscal_year__id')

    def get_active_years(self):
        return self.years.filter(active=True).order_by('fiscal_year__id')

    def __str__(self):
        return '%s' % (self.get_name())

    def get_absolute_path(self):
        return os.path.join(settings.MEDIA_ROOT, settings.STOCK_DIR, self.slug + '_' + self.random)

    def get_relative_path(self):
        if self.get_absolute_path():
            return os.path.join(settings.MEDIA_URL, settings.STOCK_DIR, self.slug + '_' + self.random)

    def create_directory(self):
        if not os.path.isdir(self.get_absolute_path()):
            os.mkdir(self.get_absolute_path(), 0o711)

    def save(self, *args, **kwargs):
        if not self.random:
            self.random = str(uuid.uuid4().hex.upper()[0:16])
        create = True
        if not self.slug:
            self.slug = slugify(self.name)
            create = False
        super(Company, self).save(*args, **kwargs)
        if create:
            self.create_directory()

    def delete(self, **kwargs):
        for y in self.years.all():
            y.delete(kwargs)
        os.rmdir(self.get_absolute_path())
        super(Company, self).delete()

class CompanyForm(ModelForm):
    n = 'companyform'
    name = forms.CharField()

    class Meta:
        model = Company
        fields = ['name', 'description', 'vat_number', 'creation_date', 'sales_revenue', 'number_employees',
                  'address_1', 'address_2', 'zip_code', 'city', 'country']

    def __init__(self, *args, **kw):
        super(CompanyForm, self).__init__(*args, **kw)
        self.fields['country'].widget.attrs['class'] = 'select2100-nosearch'
        self.fields['sales_revenue'].widget.attrs['class'] = 'select2100-nosearch'
        self.fields['number_employees'].widget.attrs['class'] = 'select2100-nosearch'
        self.fields['creation_date'].widget.attrs['class'] = 'datepicker'
        self.fields['description'].widget.attrs['rows'] = 2

    def save(self):
        instance = super(CompanyForm, self).save(commit=False)
        if not instance.slug:
            instance.slug = slugify(instance.name)
        instance.save()
        return instance
<commit_msg>add functionnalities<commit_after># -*- coding: utf-8 -*-
#
# Copyright 2015, Foxugly. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.

from django.utils.translation import ugettext_lazy as _
from django.db import models
from django import forms
from django.forms import ModelForm
from utils.models import Country, TemplateTrimester
from years.models import Year
from categories.models import TypeCategory
from django.conf import settings
from django.utils.text import slugify
import os
import uuid

class ModelTrimester(models.Model):
    name = models.TextField(_("Name of the model trimester"))
    categories = models.ManyToManyField(TypeCategory, verbose_name=_('Types of category'), blank=True)

    def __str__(self):
        return '[%d] %s' % (self.pk, self.name)

class ModelTrimesterForm(ModelForm):
    class Meta:
        model = ModelTrimester
        fields = '__all__'

class Company(models.Model):
    name = models.TextField(_("Name of the company"))
    slug = models.SlugField(unique=True)
    description = models.TextField(_("Description of the company"), blank=True, null=True)
    vat_number = models.CharField(_("TVA number"), unique=True, max_length=10, blank=True, null=True)
    address_1 = models.CharField(_("address"), max_length=128, blank=True, null=True)
    address_2 = models.CharField(_("address cont'd"), max_length=128, blank=True, null=True)
    zip_code = models.CharField(_("zip code"), max_length=5, blank=True, null=True)
    city = models.CharField(_("city"), max_length=128, blank=True, null=True)
    country = models.ForeignKey(Country, blank=True)
    random = models.CharField(max_length=16, blank=True, null=True)
    sales_revenue = models.IntegerField(_("Sales revenue"),
                                        choices=(
                                            (1, _("Lower than 50.000 euros")),
                                            (2, _("Between 50.000 euros and 250.000 euros")),
                                            (3, _("Between 250.000 euros and 700.000 euros")),
                                            (4, _("Between 700.000 euros and 4.500.000 euros")),
                                            (5, _("More than 4.500.000 euros"))
                                        ),
                                        default=1)
    number_employees = models.IntegerField(_("Number of employees"),
                                           choices=(
                                               (1, _("1")),
                                               (2, _("Between 1 and 10")),
                                               (3, _("Between 10 and 50")),
                                               (4, _("More than 50"))
                                           ),
                                           default=1)
    creation_date = models.DateField(_("Creation date"), blank=True, null=True)
    website = models.URLField(_("Website"), blank=True, null=True)
    years = models.ManyToManyField(Year, blank=True)
    active = models.BooleanField(_('active'), default=False)
    favorite = models.BooleanField(_('favorite'), default=False)
    model_trimester = models.ForeignKey(ModelTrimester, null=True, blank=True)

    def as_json(self):
        return dict(id=self.id, name=self.name)

    def get_name(self):
        return self.name.encode('utf-8')

    def add_year(self, year):
        self.years.add(year)

    def get_years(self):
        return self.years.order_by('fiscal_year__id')

    def get_active_years(self):
        return self.years.filter(active=True).order_by('fiscal_year__id')

    def __str__(self):
        return '%s' % (self.get_name())

    def get_absolute_path(self):
        return os.path.join(settings.MEDIA_ROOT, settings.STOCK_DIR, self.slug + '_' + self.random)

    def get_relative_path(self):
        if self.get_absolute_path():
            return os.path.join(settings.MEDIA_URL, settings.STOCK_DIR, self.slug + '_' + self.random)

    def create_directory(self):
        if not os.path.isdir(self.get_absolute_path()):
            os.mkdir(self.get_absolute_path(), 0o711)

    def save(self, *args, **kwargs):
        if not self.random:
            self.random = str(uuid.uuid4().hex.upper()[0:16])
        create = True
        if not self.slug:
            self.slug = slugify(self.name)
            create = False
        super(Company, self).save(*args, **kwargs)
        if create:
            self.create_directory()

    def delete(self, **kwargs):
        for y in self.years.all():
            y.delete(kwargs)
        os.rmdir(self.get_absolute_path())
        super(Company, self).delete()

class CompanyForm(ModelForm):
    n = 'companyform'
    name = forms.CharField()

    class Meta:
        model = Company
        fields = ['name', 'description', 'vat_number', 'creation_date', 'sales_revenue', 'number_employees',
                  'address_1', 'address_2', 'zip_code', 'city', 'country']

    def __init__(self, *args, **kw):
        super(CompanyForm, self).__init__(*args, **kw)
        self.fields['country'].widget.attrs['class'] = 'select2100-nosearch'
        self.fields['sales_revenue'].widget.attrs['class'] = 'select2100-nosearch'
        self.fields['number_employees'].widget.attrs['class'] = 'select2100-nosearch'
        self.fields['creation_date'].widget.attrs['class'] = 'datepicker'
        self.fields['description'].widget.attrs['rows'] = 2

    def save(self):
        instance = super(CompanyForm, self).save(commit=False)
        if not instance.slug:
            instance.slug = slugify(instance.name)
        instance.save()
        return instance
<|endoftext|>


<commit_before>#!/usr/bin/env python
# Copyright 2014 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
Script to set v8's version file to the version given by the latest tag.

The script is intended to be run as a gclient hook. The script will write a
generated version file into the checkout.

On build systems where no git is available and where the version information
is not necessary, the version generation can be bypassed with the option
--skip.
"""

import optparse
import os
import re
import subprocess
import sys

CWD = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
VERSION_CC = os.path.join(CWD, "src", "version.cc")
VERSION_GEN_CC = os.path.join(CWD, "src", "version_gen.cc")

def generate_version_file():
  tag = subprocess.check_output(
      "git describe --tags",
      shell=True,
      cwd=CWD,
  ).strip()
  assert tag

  # Check for commits not exactly matching a tag. Those are candidate builds
  # for the next version. The output has the form
  # <tag name>-<n commits>-<hash>.
  if "-" in tag:
    version = tag.split("-")[0]
    candidate = "1"
  else:
    version = tag
    candidate = "0"
  version_levels = version.split(".")

  # Set default patch level if none is given.
  if len(version_levels) == 3:
    version_levels.append("0")
  assert len(version_levels) == 4

  major, minor, build, patch = version_levels

  # Increment build level for candidate builds.
  if candidate == "1":
    build = str(int(build) + 1)
    patch = "0"

  # Modify version_gen.cc with the new values.
  output = []
  with open(VERSION_CC, "r") as f:
    for line in f:
      for definition, substitute in (
          ("MAJOR_VERSION", major),
          ("MINOR_VERSION", minor),
          ("BUILD_NUMBER", build),
          ("PATCH_LEVEL", patch),
          ("IS_CANDIDATE_VERSION", candidate)):
        if line.startswith("#define %s" % definition):
          line =  re.sub("\d+$", substitute, line)
      output.append(line)

  # Prepare log message.
  candidate_txt = " (candidate)" if candidate == "1" else ""
  patch_txt = ".%s" % patch if patch != "0" else ""
  version_txt = ("%s.%s.%s%s%s" %
                 (major, minor, build, patch_txt, candidate_txt))
  log_message = "Modifying version_gen.cc. Set V8 version to %s" %  version_txt
  return "".join(output), log_message

def bypass_version_file():
  with open(VERSION_CC, "r") as f:
    return f.read(), "Bypassing V8 version creation."

def main():
  parser = optparse.OptionParser()
  parser.add_option("--skip",
                    help="Use raw version.cc file (disables version "
                         "generation and uses a dummy version).",
                    default=False, action="store_true")
  (options, args) = parser.parse_args()

  if options.skip:
    version_file_content, log_message = bypass_version_file()
  else:
    version_file_content, log_message = generate_version_file()

  old_content = ""
  if os.path.exists(VERSION_GEN_CC):
    with open(VERSION_GEN_CC, "r") as f:
      old_content = f.read()

  # Only generate version file if content has changed.
  if old_content != version_file_content:
    with open(VERSION_GEN_CC, "w") as f:
      f.write(version_file_content)
    # Log what was calculated.
    print log_message

  return 0

if __name__ == "__main__":
  sys.exit(main())
<commit_msg>Ensure tags are fetched when generating the V8 version.<commit_after>#!/usr/bin/env python
# Copyright 2014 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
Script to set v8's version file to the version given by the latest tag.

The script is intended to be run as a gclient hook. The script will write a
generated version file into the checkout.

On build systems where no git is available and where the version information
is not necessary, the version generation can be bypassed with the option
--skip.
"""

import optparse
import os
import re
import subprocess
import sys

CWD = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
VERSION_CC = os.path.join(CWD, "src", "version.cc")
VERSION_GEN_CC = os.path.join(CWD, "src", "version_gen.cc")

def generate_version_file():
  # Make sure the tags are fetched.
  subprocess.check_output(
      "git fetch origin +refs/tags/*:refs/tags/*",
      shell=True,
      cwd=CWD)
  tag = subprocess.check_output(
      "git describe --tags",
      shell=True,
      cwd=CWD,
  ).strip()
  assert tag

  # Check for commits not exactly matching a tag. Those are candidate builds
  # for the next version. The output has the form
  # <tag name>-<n commits>-<hash>.
  if "-" in tag:
    version = tag.split("-")[0]
    candidate = "1"
  else:
    version = tag
    candidate = "0"
  version_levels = version.split(".")

  # Set default patch level if none is given.
  if len(version_levels) == 3:
    version_levels.append("0")
  assert len(version_levels) == 4

  major, minor, build, patch = version_levels

  # Increment build level for candidate builds.
  if candidate == "1":
    build = str(int(build) + 1)
    patch = "0"

  # Modify version_gen.cc with the new values.
  output = []
  with open(VERSION_CC, "r") as f:
    for line in f:
      for definition, substitute in (
          ("MAJOR_VERSION", major),
          ("MINOR_VERSION", minor),
          ("BUILD_NUMBER", build),
          ("PATCH_LEVEL", patch),
          ("IS_CANDIDATE_VERSION", candidate)):
        if line.startswith("#define %s" % definition):
          line =  re.sub("\d+$", substitute, line)
      output.append(line)

  # Prepare log message.
  candidate_txt = " (candidate)" if candidate == "1" else ""
  patch_txt = ".%s" % patch if patch != "0" else ""
  version_txt = ("%s.%s.%s%s%s" %
                 (major, minor, build, patch_txt, candidate_txt))
  log_message = "Modifying version_gen.cc. Set V8 version to %s" %  version_txt
  return "".join(output), log_message

def bypass_version_file():
  with open(VERSION_CC, "r") as f:
    return f.read(), "Bypassing V8 version creation."

def main():
  parser = optparse.OptionParser()
  parser.add_option("--skip",
                    help="Use raw version.cc file (disables version "
                         "generation and uses a dummy version).",
                    default=False, action="store_true")
  (options, args) = parser.parse_args()

  if options.skip:
    version_file_content, log_message = bypass_version_file()
  else:
    version_file_content, log_message = generate_version_file()

  old_content = ""
  if os.path.exists(VERSION_GEN_CC):
    with open(VERSION_GEN_CC, "r") as f:
      old_content = f.read()

  # Only generate version file if content has changed.
  if old_content != version_file_content:
    with open(VERSION_GEN_CC, "w") as f:
      f.write(version_file_content)
    # Log what was calculated.
    print log_message

  return 0

if __name__ == "__main__":
  sys.exit(main())
<|endoftext|>


<commit_before>import gettext
gettext.install('pynote')

__version__ = '0.2'
__license__ = 'MIT'
__copyright__ = '(c) 2013-2014 Stefan Tatschner <stefan@sevenbyte.org>'
<commit_msg>Bumped version number<commit_after>import gettext
gettext.install('pynote')

__version__ = '0.2.1'
__license__ = 'MIT'
__copyright__ = '(c) 2013-2014 Stefan Tatschner <stefan@sevenbyte.org>'
<|endoftext|>


<commit_before># Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Tests for running and managing PostgreSQL with Flocker.
"""
from unittest import skipUnless

from twisted.python.filepath import FilePath
from twisted.trial.unittest import TestCase

from flocker.node._model import Application, DockerImage, AttachedVolume, Port
from flocker.testtools import loop_until

from .testtools import (assert_expected_deployment, flocker_deploy, get_nodes,
                        require_flocker_cli)

try:
    from psycopg2 import connect, OperationalError
    PSYCOPG2_INSTALLED = True
except ImportError:
    PSYCOPG2_INSTALLED = False

POSTGRES_INTERNAL_PORT = 5432
POSTGRES_EXTERNAL_PORT = 5432

POSTGRES_APPLICATION_NAME = u"postgres-volume-example"
POSTGRES_IMAGE = u"postgres"
POSTGRES_VOLUME_MOUNTPOINT = u'/var/lib/postgresql/data'

POSTGRES_APPLICATION = Application(
    name=POSTGRES_APPLICATION_NAME,
    image=DockerImage.from_string(POSTGRES_IMAGE + u':latest'),
    ports=frozenset([
        Port(internal_port=POSTGRES_INTERNAL_PORT,
             external_port=POSTGRES_EXTERNAL_PORT),
        ]),
    volume=AttachedVolume(
        name=POSTGRES_APPLICATION_NAME,
        mountpoint=FilePath(POSTGRES_VOLUME_MOUNTPOINT),
    ),
)

class PostgresTests(TestCase):
    """
    Tests for running and managing PostgreSQL with Flocker.

    Similar to:
    http://doc-dev.clusterhq.com/gettingstarted/examples/postgres.html
    """
    @require_flocker_cli
    def setUp(self):
        """
        Deploy PostgreSQL to a node.
        """
        getting_nodes = get_nodes(self, num_nodes=2)

        def deploy_postgres(node_ips):
            self.node_1, self.node_2 = node_ips

            postgres_deployment = {
                u"version": 1,
                u"nodes": {
                    self.node_1: [POSTGRES_APPLICATION_NAME],
                    self.node_2: [],
                },
            }

            self.postgres_deployment_moved = {
                u"version": 1,
                u"nodes": {
                    self.node_1: [],
                    self.node_2: [POSTGRES_APPLICATION_NAME],
                },
            }

            self.postgres_application = {
                u"version": 1,
                u"applications": {
                    POSTGRES_APPLICATION_NAME: {
                        u"image": POSTGRES_IMAGE,
                        u"ports": [{
                            u"internal": POSTGRES_INTERNAL_PORT,
                            u"external": POSTGRES_EXTERNAL_PORT,
                        }],
                        u"volume": {
                            # The location within the container where the data
                            # volume will be mounted; see:
                            # https://github.com/docker-library/postgres/blob/
                            # docker/Dockerfile.template
                            u"mountpoint": POSTGRES_VOLUME_MOUNTPOINT,
                        },
                    },
                },
            }

            flocker_deploy(self, postgres_deployment,
                           self.postgres_application)

        getting_nodes.addCallback(deploy_postgres)
        return getting_nodes

    def test_deploy(self):
        """
        Verify that Docker reports that PostgreSQL is running on one node and
        not another.
        """
        d = assert_expected_deployment(self, {
            self.node_1: set([POSTGRES_APPLICATION]),
            self.node_2: set([]),
        })

        return d

    def test_moving_postgres(self):
        """
        It is possible to move PostgreSQL to a new node.
        """
        flocker_deploy(self, self.postgres_deployment_moved,
                       self.postgres_application)

        asserting_postgres_moved = assert_expected_deployment(self, {
            self.node_1: set([]),
            self.node_2: set([POSTGRES_APPLICATION]),
        })

        return asserting_postgres_moved

    def _get_postgres_connection(self, host, user, port, database=None):
        """
        Returns a ``Deferred`` which fires with a psycopg2 connection when one
        has been created.

        See http://pythonhosted.org//psycopg2/module.html#psycopg2.connect for
        parameter information.
        """
        def connect_to_postgres():
            try:
                return connect(host=host, user=user, port=port,
                               database=database)
            except OperationalError:
                return False

        d = loop_until(connect_to_postgres)
        return d

    @skipUnless(PSYCOPG2_INSTALLED, "Psycopg2 not installed")
    def test_moving_postgres_data(self):
        """
        PostgreSQL and its data can be deployed and moved with Flocker. In
        particular, if PostgreSQL is deployed to a node, and data added to it,
        and then the application is moved to another node, the data remains
        available.
        """
        # SQL injection is not a real concern here, and it seems impossible
        # to pass some these variables via psycopg2 so string concatenation
        # is used.
        database = b'flockertest'
        table = b'testtable'
        user = b'postgres'
        column = b'testcolumn'
        data = 3

        connecting_to_application = self._get_postgres_connection(
            host=self.node_1, user=user, port=POSTGRES_EXTERNAL_PORT)

        def create_database(connection_to_application):
            connection_to_application.autocommit = True
            with connection_to_application.cursor() as application_cursor:
                application_cursor.execute("CREATE DATABASE " + database + ";")

        connecting_to_application.addCallback(create_database)

        def connect_to_database(ignored):
            getting_postgres = self._get_postgres_connection(
                host=self.node_1,
                user=user,
                port=POSTGRES_EXTERNAL_PORT,
                database=database,
            )

            return getting_postgres

        connecting_to_database = connecting_to_application.addCallback(
            connect_to_database)

        def add_data_node_1(connection_to_db):
            with connection_to_db as db_connection_node_1:
                with db_connection_node_1.cursor() as db_node_1_cursor:
                    db_node_1_cursor.execute(
                        "CREATE TABLE " + table + " (" + column + " int);")
                    db_node_1_cursor.execute(
                        "INSERT INTO " + table + " (" + column +
                        ") VALUES (%(data)s);", {'data': data})
                    db_node_1_cursor.execute("SELECT * FROM " + table + ";")
                    db_connection_node_1.commit()
                    self.assertEqual(db_node_1_cursor.fetchone()[0], data)

        connecting_to_database.addCallback(add_data_node_1)

        def get_postgres_node_2(ignored):
            """
            Move PostgreSQL to ``node_2`` and return a ``Deferred`` which fires
            with a connection to the previously created database on ``node_2``.
            """
            flocker_deploy(self, self.postgres_deployment_moved,
                           self.postgres_application)

            getting_postgres = self._get_postgres_connection(
                host=self.node_2,
                user=user,
                port=POSTGRES_EXTERNAL_PORT,
                database=database,
            )

            return getting_postgres

        getting_postgres_2 = connecting_to_database.addCallback(
            get_postgres_node_2)

        def verify_data_moves(connection_2):
            with connection_2 as db_connection_node_2:
                with db_connection_node_2.cursor() as db_node_2_cursor:
                    db_node_2_cursor.execute("SELECT * FROM " + table + ";")
                    self.assertEqual(db_node_2_cursor.fetchone()[0], data)

        verifying_data_moves = getting_postgres_2.addCallback(
            verify_data_moves)
        return verifying_data_moves
<commit_msg>Addressed review comment: Removed unnecessary variables<commit_after># Copyright Hybrid Logic Ltd.  See LICENSE file for details.

"""
Tests for running and managing PostgreSQL with Flocker.
"""
from unittest import skipUnless

from twisted.python.filepath import FilePath
from twisted.trial.unittest import TestCase

from flocker.node._model import Application, DockerImage, AttachedVolume, Port
from flocker.testtools import loop_until

from .testtools import (assert_expected_deployment, flocker_deploy, get_nodes,
                        require_flocker_cli)

try:
    from psycopg2 import connect, OperationalError
    PSYCOPG2_INSTALLED = True
except ImportError:
    PSYCOPG2_INSTALLED = False

POSTGRES_INTERNAL_PORT = 5432
POSTGRES_EXTERNAL_PORT = 5432

POSTGRES_APPLICATION_NAME = u"postgres-volume-example"
POSTGRES_IMAGE = u"postgres"
POSTGRES_VOLUME_MOUNTPOINT = u'/var/lib/postgresql/data'

POSTGRES_APPLICATION = Application(
    name=POSTGRES_APPLICATION_NAME,
    image=DockerImage.from_string(POSTGRES_IMAGE + u':latest'),
    ports=frozenset([
        Port(internal_port=POSTGRES_INTERNAL_PORT,
             external_port=POSTGRES_EXTERNAL_PORT),
        ]),
    volume=AttachedVolume(
        name=POSTGRES_APPLICATION_NAME,
        mountpoint=FilePath(POSTGRES_VOLUME_MOUNTPOINT),
    ),
)

class PostgresTests(TestCase):
    """
    Tests for running and managing PostgreSQL with Flocker.

    Similar to:
    http://doc-dev.clusterhq.com/gettingstarted/examples/postgres.html
    """
    @require_flocker_cli
    def setUp(self):
        """
        Deploy PostgreSQL to a node.
        """
        getting_nodes = get_nodes(self, num_nodes=2)

        def deploy_postgres(node_ips):
            self.node_1, self.node_2 = node_ips

            postgres_deployment = {
                u"version": 1,
                u"nodes": {
                    self.node_1: [POSTGRES_APPLICATION_NAME],
                    self.node_2: [],
                },
            }

            self.postgres_deployment_moved = {
                u"version": 1,
                u"nodes": {
                    self.node_1: [],
                    self.node_2: [POSTGRES_APPLICATION_NAME],
                },
            }

            self.postgres_application = {
                u"version": 1,
                u"applications": {
                    POSTGRES_APPLICATION_NAME: {
                        u"image": POSTGRES_IMAGE,
                        u"ports": [{
                            u"internal": POSTGRES_INTERNAL_PORT,
                            u"external": POSTGRES_EXTERNAL_PORT,
                        }],
                        u"volume": {
                            # The location within the container where the data
                            # volume will be mounted; see:
                            # https://github.com/docker-library/postgres/blob/
                            # docker/Dockerfile.template
                            u"mountpoint": POSTGRES_VOLUME_MOUNTPOINT,
                        },
                    },
                },
            }

            flocker_deploy(self, postgres_deployment,
                           self.postgres_application)

        getting_nodes.addCallback(deploy_postgres)
        return getting_nodes

    def test_deploy(self):
        """
        Verify that Docker reports that PostgreSQL is running on one node and
        not another.
        """
        return assert_expected_deployment(self, {
            self.node_1: set([POSTGRES_APPLICATION]),
            self.node_2: set([]),
        })

    def test_moving_postgres(self):
        """
        It is possible to move PostgreSQL to a new node.
        """
        flocker_deploy(self, self.postgres_deployment_moved,
                       self.postgres_application)

        return assert_expected_deployment(self, {
            self.node_1: set([]),
            self.node_2: set([POSTGRES_APPLICATION]),
        })

    def _get_postgres_connection(self, host, user, port, database=None):
        """
        Returns a ``Deferred`` which fires with a psycopg2 connection when one
        has been created.

        See http://pythonhosted.org//psycopg2/module.html#psycopg2.connect for
        parameter information.
        """
        def connect_to_postgres():
            try:
                return connect(host=host, user=user, port=port,
                               database=database)
            except OperationalError:
                return False

        d = loop_until(connect_to_postgres)
        return d

    @skipUnless(PSYCOPG2_INSTALLED, "Psycopg2 not installed")
    def test_moving_postgres_data(self):
        """
        PostgreSQL and its data can be deployed and moved with Flocker. In
        particular, if PostgreSQL is deployed to a node, and data added to it,
        and then the application is moved to another node, the data remains
        available.
        """
        # SQL injection is not a real concern here, and it seems impossible
        # to pass some these variables via psycopg2 so string concatenation
        # is used.
        database = b'flockertest'
        table = b'testtable'
        user = b'postgres'
        column = b'testcolumn'
        data = 3

        connecting_to_application = self._get_postgres_connection(
            host=self.node_1, user=user, port=POSTGRES_EXTERNAL_PORT)

        def create_database(connection_to_application):
            connection_to_application.autocommit = True
            with connection_to_application.cursor() as application_cursor:
                application_cursor.execute("CREATE DATABASE " + database + ";")

        connecting_to_application.addCallback(create_database)

        def connect_to_database(ignored):
            return self._get_postgres_connection(
                host=self.node_1,
                user=user,
                port=POSTGRES_EXTERNAL_PORT,
                database=database,
            )

        connecting_to_database = connecting_to_application.addCallback(
            connect_to_database)

        def add_data_node_1(connection_to_db):
            with connection_to_db as db_connection_node_1:
                with db_connection_node_1.cursor() as db_node_1_cursor:
                    db_node_1_cursor.execute(
                        "CREATE TABLE " + table + " (" + column + " int);")
                    db_node_1_cursor.execute(
                        "INSERT INTO " + table + " (" + column +
                        ") VALUES (%(data)s);", {'data': data})
                    db_node_1_cursor.execute("SELECT * FROM " + table + ";")
                    db_connection_node_1.commit()
                    self.assertEqual(db_node_1_cursor.fetchone()[0], data)

        connecting_to_database.addCallback(add_data_node_1)

        def get_postgres_node_2(ignored):
            """
            Move PostgreSQL to ``node_2`` and return a ``Deferred`` which fires
            with a connection to the previously created database on ``node_2``.
            """
            flocker_deploy(self, self.postgres_deployment_moved,
                           self.postgres_application)

            return self._get_postgres_connection(
                host=self.node_2,
                user=user,
                port=POSTGRES_EXTERNAL_PORT,
                database=database,
            )

        getting_postgres_2 = connecting_to_database.addCallback(
            get_postgres_node_2)

        def verify_data_moves(connection_2):
            with connection_2 as db_connection_node_2:
                with db_connection_node_2.cursor() as db_node_2_cursor:
                    db_node_2_cursor.execute("SELECT * FROM " + table + ";")
                    self.assertEqual(db_node_2_cursor.fetchone()[0], data)

        verifying_data_moves = getting_postgres_2.addCallback(
            verify_data_moves)
        return verifying_data_moves
<|endoftext|>


<commit_before>import string

template = string.Template("""#
# Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
#
# Control-node configuration options
#

[DEFAULT]
# bgp_config_file=bgp_config.xml
# bgp_end_of_rib_timeout=30
# bgp_port=179
# collectors= # Provided by discovery server
# gr_helper_bgp_enable=0
# gr_helper_xmpp_enable=0
hostip=$__contrail_host_ip__ # Resolved IP of `hostname`
hostname=$__contrail_hostname__ # Retrieved as `hostname`
# http_server_port=8083
# log_category=
# log_disable=0
log_file=/var/log/contrail/contrail-control.log
# log_files_count=10
# log_file_size=10485760 # 10MB
log_level=SYS_NOTICE
log_local=1
# test_mode=0
# xmpp_auth_enable=0
# xmpp_server_cert=/etc/contrail/ssl/certs/server.pem
# xmpp_server_key=/etc/contrail/ssl/private/server-privkey.pem
# xmpp_ca_cert=/etc/contrail/ssl/certs/ca-cert.pem
# xmpp_end_of_rib_timeout=30
# xmpp_server_port=5269

# Sandesh send rate limit can be used to throttle system logs transmitted per
# second. System logs are dropped if the sending rate is exceeded
# sandesh_send_rate_limit=100

[DISCOVERY]
# port=5998
server=$__contrail_discovery_ip__ # discovery-server IP address

[IFMAP]
certs_store=$__contrail_cert_ops__
password=$__contrail_ifmap_paswd__
# server_url= # Provided by discovery server, e.g. https://127.0.0.1:8443
user=$__contrail_ifmap_usr__
# stale_entries_cleanup_timeout=300 # in seconds
# end_of_rib_timeout=10 # in seconds
# peer_response_wait_time=60 # in seconds

""")
<commit_msg>Change template to reflect modified GR parameter in contrail-control.conf<commit_after>import string

template = string.Template("""#
# Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
#
# Control-node configuration options
#

[DEFAULT]
# bgp_config_file=bgp_config.xml
# bgp_end_of_rib_timeout=30
# bgp_port=179
# collectors= # Provided by discovery server
# gr_helper_bgp_disable=0
# gr_helper_xmpp_disable=0
hostip=$__contrail_host_ip__ # Resolved IP of `hostname`
hostname=$__contrail_hostname__ # Retrieved as `hostname`
# http_server_port=8083
# log_category=
# log_disable=0
log_file=/var/log/contrail/contrail-control.log
# log_files_count=10
# log_file_size=10485760 # 10MB
log_level=SYS_NOTICE
log_local=1
# test_mode=0
# xmpp_auth_enable=0
# xmpp_server_cert=/etc/contrail/ssl/certs/server.pem
# xmpp_server_key=/etc/contrail/ssl/private/server-privkey.pem
# xmpp_ca_cert=/etc/contrail/ssl/certs/ca-cert.pem
# xmpp_end_of_rib_timeout=30
# xmpp_server_port=5269

# Sandesh send rate limit can be used to throttle system logs transmitted per
# second. System logs are dropped if the sending rate is exceeded
# sandesh_send_rate_limit=100

[DISCOVERY]
# port=5998
server=$__contrail_discovery_ip__ # discovery-server IP address

[IFMAP]
certs_store=$__contrail_cert_ops__
password=$__contrail_ifmap_paswd__
# server_url= # Provided by discovery server, e.g. https://127.0.0.1:8443
user=$__contrail_ifmap_usr__
# stale_entries_cleanup_timeout=300 # in seconds
# end_of_rib_timeout=10 # in seconds
# peer_response_wait_time=60 # in seconds

""")
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
""" pynpoint config singleton """

class ConfigError(Exception):
    """ A config error """
    pass

class Config(object):
    """ Borg style Config object, the shared state is only initialized once.
    All configuration options are set as properties on this object with in
    the format of sectionname_key.

    For example:
       {"server": {"hostname": "somehost"}} -> Config().server_hostname
    """

    __default_configs = ("pynpoint.cfg",        # Sane Defaults
                         "/etc/pynpoint.cfg",   # Site level configs
                         "~/.pynpoint.cfg")     # Are we running as a user?
    __shared_state = {}

    def __new__(cls, config_files=__default_configs):
        self = object.__new__(cls)
        self.__dict__ = cls.__shared_state
        return self

    def __init__(self, config_files=__default_configs):
        if not self.__dict__:
            print "Assimilating your biological distinctiveness"
            for config_file in config_files:
                self._parse_config_file(config_file)

            if not self.__dict__:
                raise ConfigError("Didn't find configuration files, bailing.")

    def __getattr__(self, item):
        if item in self.__dict__:
            return self[item]
        else:
            return None

    def _parse_config_yaml(self, cfg):
        try:
            import yaml
            return yaml.load(cfg) # Returns None
        except yaml.ParserError as e:
            raise ValueError(e.args)
        except ImportError:
            return None

    def _parse_config_json(self, cfg):
        try:
            import json
            return json.JSONDecoder().decode(cfg)
        except (ImportError, ValueError):
            return None

    def _parse_config_file(self, config_file):
        """ Parses a configuration file as a dict and populates __dict__
        with them. __dict__ is Config.__shared_state
        """

        parsers = (self._parse_config_json, self._parse_config_yaml)

        try:
            with open(config_file) as fp:
                cfg = fp.read()

                for parser in parsers:
                    config = parser(cfg)
                    if config:
                        break

                for (section_name, section) in config.items():
                    for (item, value) in section.items():
                        config_attr = "%s_%s" % (section_name, item)
                        self.__setattr__(config_attr, value)
        except (AttributeError, TypeError, IOError):
            pass
        except ValueError as e:
            raise ConfigError(e.args)
<commit_msg>DRY up the config parsers<commit_after># -*- coding: utf-8 -*-
""" pynpoint config singleton """

class ConfigError(Exception):
    """ A config error """
    pass

class Config(object):
    """ Borg style Config object, the shared state is only initialized once.
    All configuration options are set as properties on this object with in
    the format of sectionname_key.

    For example:
       {"server": {"hostname": "somehost"}} -> Config().server_hostname
    """

    __default_configs = ("pynpoint.cfg",        # Sane Defaults
                         "/etc/pynpoint.cfg",   # Site level configs
                         "~/.pynpoint.cfg")     # Are we running as a user?
    __shared_state = {}

    def __new__(cls, config_files=__default_configs):
        self = object.__new__(cls)
        self.__dict__ = cls.__shared_state
        return self

    def __init__(self, config_files=__default_configs):
        if not self.__dict__:
            print "Assimilating your biological distinctiveness"
            for config_file in config_files:
                self._parse_config_file(config_file)

            if not self.__dict__:
                raise ConfigError("Didn't find configuration files, bailing.")

    def __getattr__(self, item):
        if item in self.__dict__:
            return self[item]
        else:
            return None

    def _parse_config_yaml(self, cfg):
        try:
            import yaml
            return yaml.load(cfg) # Returns None
        except yaml.ParserError as e:
            raise ValueError(e.args)

    def _parse_config_json(self, cfg):
        import json
        return json.JSONDecoder().decode(cfg)

    def _parse_config_file(self, config_file):
        """ Parses a configuration file as a dict and populates __dict__
        with them. __dict__ is Config.__shared_state
        """

        parsers = (self._parse_config_json, self._parse_config_yaml)

        try:
            with open(config_file) as fp:
                cfg = fp.read()

                for parser in parsers:
                    try:
                        config = parser(cfg)
                    except (ValueError, ImportError):
                        pass

                for (section_name, section) in config.items():
                    for (item, value) in section.items():
                        config_attr = "%s_%s" % (section_name, item)
                        self.__setattr__(config_attr, value)
        except (AttributeError, TypeError, IOError):
            pass
<|endoftext|>