models.py 5.93 KB
Newer Older
Őry Máté committed
1 2
import logging

Őry Máté committed
3 4 5 6 7 8 9 10
from django.contrib.auth.models import User, Group
from django.contrib.contenttypes.generic import (
    GenericForeignKey, GenericRelation
)
from django.contrib.contenttypes.models import ContentType
from django.db.models import (
    ManyToManyField, ForeignKey, CharField, Model, IntegerField
)
Őry Máté committed
11

Őry Máté committed
12 13
logger = logging.getLogger(__name__)

Őry Máté committed
14 15 16 17 18

class Level(Model):

    """Definition of a permission level.

19
    Instances are automatically populated based on AclBase."""
Őry Máté committed
20 21 22 23 24
    name = CharField('name', max_length=50)
    content_type = ForeignKey(ContentType)
    codename = CharField('codename', max_length=100)
    weight = IntegerField('weight', null=True)

Őry Máté committed
25 26 27
    def __unicode__(self):
        return "<%s/%s>" % (unicode(self.content_type), self.name)

Őry Máté committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
    class Meta:
        unique_together = (('content_type', 'codename'),
                           # ('content_type', 'weight'),
                           # TODO find a way of temp. disabling this constr.
                           )


class ObjectLevel(Model):

    """Permission level for a specific object."""
    level = ForeignKey(Level)
    content_type = ForeignKey(ContentType)
    object_id = CharField(max_length=255)
    content_object = GenericForeignKey()
    users = ManyToManyField(User)
    groups = ManyToManyField(Group)

Őry Máté committed
45 46 47
    def __unicode__(self):
        return "<%s: %s>" % (unicode(self.content_object), unicode(self.level))

Őry Máté committed
48 49 50 51 52 53 54 55 56
    class Meta:
        unique_together = (('content_type', 'object_id', 'level'),)


class AclBase(Model):

    """Define permission levels for Users/Groups per object."""
    object_level_set = GenericRelation(ObjectLevel)

57 58
    @classmethod
    def get_level_object(cls, level):
59 60

        """Get Level object for this model by codename."""
61
        ct = ContentType.objects.get_for_model(cls)
Őry Máté committed
62 63 64
        return Level.objects.get(codename=level, content_type=ct)

    def set_level(self, whom, level):
65 66 67 68 69 70 71 72

        """Set level of object for a user or group.

        :param whom: user or group the level is set for
        :type whom: User or Group
        :param level: codename of level to set
        :type level: Level or str or unicode
        """
Őry Máté committed
73 74 75 76 77
        if isinstance(whom, User):
            self.set_user_level(whom, level)
        elif isinstance(whom, Group):
            self.set_group_level(whom, level)
        else:
78
            raise AttributeError('"whom" must be a User or Group object.')
Őry Máté committed
79 80

    def set_user_level(self, user, level):
81 82 83 84 85 86 87 88

        """Set level of object for a user.

        :param whom: user the level is set for
        :type whom: User
        :param level: codename of level to set
        :type level: Level or str or unicode
        """
Őry Máté committed
89 90
        logger.info('%s.set_user_level(%s, %s) called',
                    *[unicode(p) for p in [self, user, level]])
Őry Máté committed
91 92 93 94 95 96 97 98 99 100 101 102
        if isinstance(level, basestring):
            level = self.get_level_object(level)
        if not self.object_level_set.filter(level_id=level.pk).exists():
            self.object_level_set.create(level=level)
        for i in self.object_level_set.all():
            if i.level_id != level.pk:
                i.users.remove(user)
            else:
                i.users.add(user)
            i.save()

    def set_group_level(self, group, level):
103 104 105 106 107 108 109 110

        """Set level of object for a user.

        :param whom: user the level is set for
        :type whom: User or unicode or str
        :param level: codename of level to set
        :type level: str or unicode
        """
Őry Máté committed
111 112
        logger.info('%s.set_group_level(%s, %s) called',
                    *[unicode(p) for p in [self, group, level]])
Őry Máté committed
113 114 115 116 117 118 119 120 121 122 123 124 125
        if isinstance(level, basestring):
            level = self.get_level_object(level)
        #self.object_level_set.get_or_create(level=level, content_object=self)
        if not self.object_level_set.filter(level_id=level.pk).exists():
            self.object_level_set.create(level=level)
        for i in self.object_level_set.all():
            if i.level_id != level.pk:
                i.groups.remove(group)
            else:
                i.groups.add(group)
            i.save()

    def has_level(self, user, level, group_also=True):
Őry Máté committed
126 127
        logger.debug('%s.has_level(%s, %s, %s) called',
                     *[unicode(p) for p in [self, user, level, group_also]])
128 129 130
        if getattr(user, 'is_superuser', False):
            logger.debug('- superuser granted')
            return True
Őry Máté committed
131 132
        if isinstance(level, basestring):
            level = self.get_level_object(level)
Őry Máté committed
133
            logger.debug("- level set by str: %s", unicode(level))
Őry Máté committed
134 135 136

        object_levels = self.object_level_set.filter(
            level__weight__gte=level.weight).all()
137 138 139 140 141 142
        groups = user.groups.values_list('id', flat=True) if group_also else []
        for i in object_levels:
            if i.users.filter(pk=user.pk).exists():
                return True
            if group_also and i.groups.filter(pk__in=groups).exists():
                return True
Őry Máté committed
143 144 145
        return False

    def get_users_with_level(self):
Őry Máté committed
146
        logger.debug('%s.get_users_with_level() called', unicode(self))
Őry Máté committed
147 148 149 150 151
        object_levels = (self.object_level_set.select_related(
            'users', 'level').all())
        users = []
        for object_level in object_levels:
            name = object_level.level.codename
Őry Máté committed
152 153 154
            olusers = object_level.users.all()
            users.extend([(u, name) for u in olusers])
            logger.debug('- %s: %s' % (name, [u.username for u in olusers]))
Őry Máté committed
155 156 157
        return users

    def get_groups_with_level(self):
Őry Máté committed
158
        logger.debug('%s.get_groups_with_level() called', unicode(self))
Őry Máté committed
159 160 161 162 163
        object_levels = (self.object_level_set.select_related(
            'groups', 'level').all())
        groups = []
        for object_level in object_levels:
            name = object_level.level.codename
Őry Máté committed
164 165 166
            olgroups = object_level.groups.all()
            groups.extend([(g, name) for g in olgroups])
            logger.debug('- %s: %s' % (name, [g.name for g in olgroups]))
Őry Máté committed
167 168 169 170
        return groups

    class Meta:
        abstract = True