models.py 7.03 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.

18 19
from __future__ import absolute_import

20
from itertools import chain
21 22
from logging import getLogger

23
from django.conf import settings
24
from django.contrib.auth.models import User, Group
25
from django.contrib.auth.signals import user_logged_in
26
from django.core.urlresolvers import reverse
27
from django.db.models import (
28
    Model, ForeignKey, OneToOneField, CharField, IntegerField, TextField,
29
    DateTimeField, permalink, BooleanField
30
)
31
from django.template.loader import render_to_string
32
from django.utils.translation import ugettext_lazy as _, override, ugettext
33 34 35 36

from model_utils.models import TimeStampedModel
from model_utils.fields import StatusField
from model_utils import Choices
37

38
from acl.models import AclBase
39

40 41
logger = getLogger(__name__)

42

43
class Favourite(Model):
44
    instance = ForeignKey("vm.Instance")
45
    user = ForeignKey(User)
46 47


48 49 50 51 52 53 54 55 56
class Notification(TimeStampedModel):
    STATUS = Choices(('new', _('new')),
                     ('delivered', _('delivered')),
                     ('read', _('read')))

    status = StatusField()
    to = ForeignKey(User)
    subject = CharField(max_length=128)
    message = TextField()
57
    valid_until = DateTimeField(null=True, default=None)
58 59 60 61 62

    class Meta:
        ordering = ['-created']

    @classmethod
63
    def send(cls, user, subject, template, context={}, valid_until=None):
64 65 66 67 68 69 70
        try:
            language = user.profile.preferred_language
        except:
            language = None
        with override(language):
            context['user'] = user
            rendered = render_to_string(template, context)
71
            subject = ugettext(unicode(subject))
72 73
        return cls.objects.create(to=user, subject=subject, message=rendered,
                                  valid_until=valid_until)
74 75


76 77 78 79 80 81 82 83 84
class Profile(Model):
    user = OneToOneField(User)
    preferred_language = CharField(verbose_name=_('preferred language'),
                                   choices=settings.LANGUAGES,
                                   max_length=32,
                                   default=settings.LANGUAGE_CODE, blank=False)
    org_id = CharField(  # may be populated from eduPersonOrgId field
        unique=True, blank=True, null=True, max_length=64,
        help_text=_('Unique identifier of the person, e.g. a student number.'))
85
    instance_limit = IntegerField(default=5)
86
    use_gravatar = BooleanField(default=False)
87 88 89
    email_notifications = BooleanField(
        verbose_name=_("Email notifications"), default=True,
        help_text=_('Wether user wants to get digested email notifications.'))
90

91 92 93
    def notify(self, subject, template, context={}, valid_until=None):
        return Notification.send(self.user, subject, template, context,
                                 valid_until)
94

95
    def get_absolute_url(self):
Kálmán Viktor committed
96 97
        return reverse("dashboard.views.profile",
                       kwargs={'username': self.user.username})
98

Őry Máté committed
99

100 101 102 103 104 105 106 107 108 109
class GroupProfile(AclBase):
    ACL_LEVELS = (
        ('operator', _('operator')),
        ('owner', _('owner')),
    )

    group = OneToOneField(Group)
    org_id = CharField(
        unique=True, blank=True, null=True, max_length=64,
        help_text=_('Unique identifier of the group at the organization.'))
110 111 112 113 114 115
    description = TextField(blank=True)

    def save(self, *args, **kwargs):
        if not self.org_id:
            self.org_id = None
        super(GroupProfile, self).save(*args, **kwargs)
116 117 118 119 120 121 122 123

    @classmethod
    def search(cls, name):
        try:
            return cls.objects.get(org_id=name).group
        except cls.DoesNotExist:
            return Group.objects.get(name=name)

124 125 126 127 128
    @permalink
    def get_absolute_url(self):
        return ('dashboard.views.group-detail', None,
                {'pk': self.group.pk})

129 130

def get_or_create_profile(self):
131
    obj, created = GroupProfile.objects.get_or_create(group_id=self.pk)
132 133 134 135 136
    return obj

Group.profile = property(get_or_create_profile)


137
def create_profile(sender, user, request, **kwargs):
138 139
    if not user.pk:
        return False
140 141 142 143 144
    profile, created = Profile.objects.get_or_create(user=user)
    return created

user_logged_in.connect(create_profile)

145
if hasattr(settings, 'SAML_ORG_ID_ATTRIBUTE'):
146
    logger.debug("Register save_org_id to djangosaml2 pre_user_save")
147 148
    from djangosaml2.signals import pre_user_save

149
    def save_org_id(sender, **kwargs):
150
        logger.debug("save_org_id called by %s", sender.username)
151
        attributes = kwargs.pop('attributes')
152
        atr = settings.SAML_ORG_ID_ATTRIBUTE
153 154 155 156 157 158
        try:
            value = attributes[atr][0]
        except Exception as e:
            value = None
            logger.info("save_org_id couldn't find attribute. %s", unicode(e))

159 160 161 162
        if sender.pk is None:
            sender.save()
            logger.debug("save_org_id saved user %s", unicode(sender))

163 164
        profile, created = Profile.objects.get_or_create(user=sender)
        if created or profile.org_id != value:
165 166
            logger.info("org_id of %s added to user %s's profile",
                        value, sender.username)
167 168
            profile.org_id = value
            profile.save()
169 170 171
        else:
            logger.debug("org_id of %s already added to user %s's profile",
                         value, sender.username)
172
        memberatrs = getattr(settings, 'SAML_GROUP_ATTRIBUTES', [])
173 174
        for group in chain(*[attributes[i]
                             for i in memberatrs if i in attributes]):
175 176 177 178 179 180 181 182 183 184
            try:
                g = GroupProfile.search(group)
            except Group.DoesNotExist:
                logger.debug('cant find membergroup %s', group)
            else:
                logger.debug('could find membergroup %s (%s)',
                             group, unicode(g))
                g.user_set.add(sender)

        owneratrs = getattr(settings, 'SAML_GROUP_OWNER_ATTRIBUTES', [])
185 186
        for group in chain(*[attributes[i]
                             for i in owneratrs if i in attributes]):
187 188 189 190 191 192 193 194 195 196
            try:
                g = GroupProfile.search(group)
            except Group.DoesNotExist:
                logger.debug('cant find ownergroup %s', group)
            else:
                logger.debug('could find ownergroup %s (%s)',
                             group, unicode(g))
                g.profile.set_level(sender, 'owner')

        return False  # User did not change
197

198 199
    pre_user_save.connect(save_org_id)

200 201
else:
    logger.debug("Do not register save_org_id to djangosaml2 pre_user_save")