<commit_before>import re
from peewee import PrimaryKeyField, CharField, ForeignKeyField, IntegerField,\
    Model, Proxy
from playhouse.fields import ManyToManyField
import users

# Create a proxy to DB that can
# be instantiated at runtime
db_proxy = Proxy()

GroupToCapabilityProxy = Proxy()
UserToGroupProxy = Proxy()


class BaseModel(Model):
    class Meta:
        database = db_proxy  # Use proxy for our DB.


class Capability(BaseModel):
    """Capability model

    A capability is composed by a :attr:`domain`
    and an :attr:`action`. It represent the possibility
    to perform a specific set of actions on the resources
    described by the domain

    :attr:`domain` is a regular expression that
        describe all the resources involved in the capability.
        You can use :func:`simToReg` and :func:`regToSim` utility function
        to easily manipulate domain regular expressions.
    :attr:`action` is a bitmask representing
        the actions involved in the capability
        you can use :class:`Action` class to compose the bitmask
    """

    domain = CharField()
    action = IntegerField()

    class Meta:
        indexes = ((('domain', 'action'), True),)

    @classmethod
    def simToReg(self, sim):
        """Convert simplified domain expression to regular expression"""
        # remove initial slash if present
        res = re.sub('^/', '', sim)
        res = re.sub('/$', '', res)
        return '^/?' + re.sub('\*', '[^/]+', res) + '/?$'

    @classmethod
    def regToSim(self, reg):
        """Convert regular expression to simplified domain expression"""
        return re.sub('\[\^/\]\+', '*', reg[3:-3])

    def match_domain(self, dom):
        """Check if the given :param:`dom` is included in this capability domain"""
        return bool(re.match(self.domain, dom))

    def match_action(self, act):
        """Check if the given :param:`act` is allowed from this capability"""
        return (self.action & act) == act

    def match(self, dom, act):
        """Check if the given :param:`domain` and :param:`act` are allowed
        by this capability"""
        return self.match_domain(dom) and self.match_action(act)


class Action():
    """Actions utiliy class

    You can use this class attributes to compose the actions bitmask::
        bitmask = Action.CREATE | Action.DELETE

    The following actions are supported:
     - CREATE
     - READ
     - UPDATE
     - DELETE
    """

    # the index of the action in the list correspond to the its position in the bitmask
    ACTIONS = ['CREATE', 'READ', 'UPDATE', 'DELETE']

    @classmethod
    def bitmask_to_list(cls, bitmask):
        '''convert an actions bitmask into a list of action strings'''
        res = []
        for a in cls.ACTIONS:
            aBit = cls.action_bitmask(a)
            if ((bitmask & aBit) == aBit):
                res.append(a)
        return res

    @classmethod
    def list_to_bitmask(cls, actions):
        '''convert list of actions into the corresponding bitmask'''
        bitmask = 0
        for a in actions:
            bitmask |= cls.action_bitmask(a)
        return bitmask

    @classmethod
    def action_bitmask(cls, action):
        '''return the bitmask associated withe the given action name'''
        return 2**cls.ACTIONS.index(action.upper())

for i, act in enumerate(Action.ACTIONS):
    setattr(Action, act.upper(), 2**i)


class Group(BaseModel):
    """Group model

    A group has a set of capabilities and a
    number of users belonging to it.
    It's an handy way of grouping users with the same capability.
    """
    id = PrimaryKeyField()
    name = CharField(unique=True)
    capabilities = ManyToManyField(Capability, related_name='groups', through_model=GroupToCapabilityProxy)

    def can(self, domain, action):
        for cap in self.capabilities:
            if(cap.match(domain, action)):
                return True
        return False


class User(BaseModel):
    """User model"""
    id = PrimaryKeyField()
    name = CharField(unique=True)
    pwd_hash = CharField(max_length=255, null=True)
    groups = ManyToManyField(Group, related_name='users', through_model=UserToGroupProxy)

    def __init__(self, **kargs):
        super(User, self).__init__()
        if 'name' in kargs:
            self.name = kargs['name']
        if 'password' in kargs:
            self.set_password(kargs['password'])

    def set_password(self, password):
        """set user password

        Generate random salt, derivate the given password using pbkdf2
        algorith and store a summarizing string in :attr:`pwd_hash`.
        For hash format refer to `passlib documentation <https://pythonhosted.org/passlib/lib/passlib.hash.pbkdf2_digest.html#format-algorithm>`_.
        """
        self.pwd_hash = users.pwdCryptCtx.encrypt(password)

    def verify_password(self, password):
        """Check if the given password is the same
        stored for this user"""
        return users.pwdCryptCtx.verify(password, self.pwd_hash)

    @property
    def capabilities(self):
        return (Capability
                .select()
                .join(GroupToCapability).join(Group)
                .join(UserToGroup).join(User)
                .where(User.id == self.id))

    def can(self, domain, action):
        """Can perform :param:`action` on the given :param:`domain`."""
        for cap in self.capabilities:
            if(cap.match(domain, action)):
                return True
        return False


class GroupToCapability(BaseModel):
    group = ForeignKeyField(Group, on_delete='CASCADE')
    capability = ForeignKeyField(Capability, on_delete='CASCADE')


class UserToGroup(BaseModel):
    user = ForeignKeyField(User, on_delete='CASCADE')
    group = ForeignKeyField(Group, on_delete='CASCADE')


GroupToCapabilityProxy.initialize(GroupToCapability)
UserToGroupProxy.initialize(UserToGroup)<commit_msg>Added explicit primaryKey to ManyToMany tables<commit_after>import re
from peewee import PrimaryKeyField, CharField, ForeignKeyField, IntegerField,\
    Model, Proxy, CompositeKey
from playhouse.fields import ManyToManyField
import users

# Create a proxy to DB that can
# be instantiated at runtime
db_proxy = Proxy()

GroupToCapabilityProxy = Proxy()
UserToGroupProxy = Proxy()


class BaseModel(Model):
    class Meta:
        database = db_proxy  # Use proxy for our DB.


class Capability(BaseModel):
    """Capability model

    A capability is composed by a :attr:`domain`
    and an :attr:`action`. It represent the possibility
    to perform a specific set of actions on the resources
    described by the domain

    :attr:`domain` is a regular expression that
        describe all the resources involved in the capability.
        You can use :func:`simToReg` and :func:`regToSim` utility function
        to easily manipulate domain regular expressions.
    :attr:`action` is a bitmask representing
        the actions involved in the capability
        you can use :class:`Action` class to compose the bitmask
    """

    domain = CharField()
    action = IntegerField()

    class Meta:
        indexes = ((('domain', 'action'), True),)

    @classmethod
    def simToReg(self, sim):
        """Convert simplified domain expression to regular expression"""
        # remove initial slash if present
        res = re.sub('^/', '', sim)
        res = re.sub('/$', '', res)
        return '^/?' + re.sub('\*', '[^/]+', res) + '/?$'

    @classmethod
    def regToSim(self, reg):
        """Convert regular expression to simplified domain expression"""
        return re.sub('\[\^/\]\+', '*', reg[3:-3])

    def match_domain(self, dom):
        """Check if the given :param:`dom` is included in this capability domain"""
        return bool(re.match(self.domain, dom))

    def match_action(self, act):
        """Check if the given :param:`act` is allowed from this capability"""
        return (self.action & act) == act

    def match(self, dom, act):
        """Check if the given :param:`domain` and :param:`act` are allowed
        by this capability"""
        return self.match_domain(dom) and self.match_action(act)


class Action():
    """Actions utiliy class

    You can use this class attributes to compose the actions bitmask::
        bitmask = Action.CREATE | Action.DELETE

    The following actions are supported:
     - CREATE
     - READ
     - UPDATE
     - DELETE
    """

    # the index of the action in the list correspond to the its position in the bitmask
    ACTIONS = ['CREATE', 'READ', 'UPDATE', 'DELETE']

    @classmethod
    def bitmask_to_list(cls, bitmask):
        '''convert an actions bitmask into a list of action strings'''
        res = []
        for a in cls.ACTIONS:
            aBit = cls.action_bitmask(a)
            if ((bitmask & aBit) == aBit):
                res.append(a)
        return res

    @classmethod
    def list_to_bitmask(cls, actions):
        '''convert list of actions into the corresponding bitmask'''
        bitmask = 0
        for a in actions:
            bitmask |= cls.action_bitmask(a)
        return bitmask

    @classmethod
    def action_bitmask(cls, action):
        '''return the bitmask associated withe the given action name'''
        return 2**cls.ACTIONS.index(action.upper())

for i, act in enumerate(Action.ACTIONS):
    setattr(Action, act.upper(), 2**i)


class Group(BaseModel):
    """Group model

    A group has a set of capabilities and a
    number of users belonging to it.
    It's an handy way of grouping users with the same capability.
    """
    id = PrimaryKeyField()
    name = CharField(unique=True)
    capabilities = ManyToManyField(Capability, related_name='groups', through_model=GroupToCapabilityProxy)

    def can(self, domain, action):
        for cap in self.capabilities:
            if(cap.match(domain, action)):
                return True
        return False


class User(BaseModel):
    """User model"""
    id = PrimaryKeyField()
    name = CharField(unique=True)
    pwd_hash = CharField(max_length=255, null=True)
    groups = ManyToManyField(Group, related_name='users', through_model=UserToGroupProxy)

    def __init__(self, **kargs):
        super(User, self).__init__()
        if 'name' in kargs:
            self.name = kargs['name']
        if 'password' in kargs:
            self.set_password(kargs['password'])

    def set_password(self, password):
        """set user password

        Generate random salt, derivate the given password using pbkdf2
        algorith and store a summarizing string in :attr:`pwd_hash`.
        For hash format refer to `passlib documentation <https://pythonhosted.org/passlib/lib/passlib.hash.pbkdf2_digest.html#format-algorithm>`_.
        """
        self.pwd_hash = users.pwdCryptCtx.encrypt(password)

    def verify_password(self, password):
        """Check if the given password is the same
        stored for this user"""
        return users.pwdCryptCtx.verify(password, self.pwd_hash)

    @property
    def capabilities(self):
        return (Capability
                .select()
                .join(GroupToCapability).join(Group)
                .join(UserToGroup).join(User)
                .where(User.id == self.id))

    def can(self, domain, action):
        """Can perform :param:`action` on the given :param:`domain`."""
        for cap in self.capabilities:
            if(cap.match(domain, action)):
                return True
        return False


class GroupToCapability(BaseModel):
    group = ForeignKeyField(Group, on_delete='CASCADE')
    capability = ForeignKeyField(Capability, on_delete='CASCADE')

    class Meta:
            primary_key = CompositeKey('group', 'capability')


class UserToGroup(BaseModel):
    user = ForeignKeyField(User, on_delete='CASCADE')
    group = ForeignKeyField(Group, on_delete='CASCADE')

    class Meta:
            primary_key = CompositeKey('user', 'group')

GroupToCapabilityProxy.initialize(GroupToCapability)
UserToGroupProxy.initialize(UserToGroup)
{'fraction_non_alphanumeric': 0.1060126582278481, 'fraction_numerical': 0.0016655562958027982, 'mean_word_length': 2.7979127134724857, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7101106', 'n_tokens_mistral': 3435, 'n_tokens_neox': 3314, 'n_words': 1081}