Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 373daf6a

History | View | Annotate | Download (18.8 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 6c736ed7 Kostas Papadimitriou
#
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 6c736ed7 Kostas Papadimitriou
#
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 6c736ed7 Kostas Papadimitriou
#
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 6c736ed7 Kostas Papadimitriou
#
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 6c736ed7 Kostas Papadimitriou
#
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 64cd4730 Antony Chazapis
34 64cd4730 Antony Chazapis
import hashlib
35 15efc749 Sofia Papagiannaki
import uuid
36 18ffbee1 Sofia Papagiannaki
import logging
37 0a569195 Sofia Papagiannaki
import json
38 64cd4730 Antony Chazapis
39 64cd4730 Antony Chazapis
from time import asctime
40 64cd4730 Antony Chazapis
from datetime import datetime, timedelta
41 64cd4730 Antony Chazapis
from base64 import b64encode
42 0a569195 Sofia Papagiannaki
from urlparse import urlparse, urlunparse
43 8f5a3a06 Sofia Papagiannaki
from random import randint
44 64cd4730 Antony Chazapis
45 49790d9d Sofia Papagiannaki
from django.db import models, IntegrityError
46 18ffbee1 Sofia Papagiannaki
from django.contrib.auth.models import User, UserManager, Group
47 0a569195 Sofia Papagiannaki
from django.utils.translation import ugettext as _
48 0a569195 Sofia Papagiannaki
from django.core.exceptions import ValidationError
49 49790d9d Sofia Papagiannaki
from django.template.loader import render_to_string
50 49790d9d Sofia Papagiannaki
from django.core.mail import send_mail
51 49790d9d Sofia Papagiannaki
from django.db import transaction
52 ff9290ec Sofia Papagiannaki
from django.db.models.signals import post_save, post_syncdb
53 64cd4730 Antony Chazapis
54 49790d9d Sofia Papagiannaki
from astakos.im.settings import DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL, \
55 111f3da6 Sofia Papagiannaki
    AUTH_TOKEN_DURATION, BILLING_FIELDS, QUEUE_CONNECTION, SITENAME, \
56 111f3da6 Sofia Papagiannaki
    EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL
57 9c01d9d1 Sofia Papagiannaki
58 9c01d9d1 Sofia Papagiannaki
QUEUE_CLIENT_ID = 3 # Astakos.
59 64cd4730 Antony Chazapis
60 18ffbee1 Sofia Papagiannaki
logger = logging.getLogger(__name__)
61 18ffbee1 Sofia Papagiannaki
62 8e45d6fd Sofia Papagiannaki
class Service(models.Model):
63 8e45d6fd Sofia Papagiannaki
    name = models.CharField('Name', max_length=255, unique=True, db_index=True)
64 8e45d6fd Sofia Papagiannaki
    url = models.FilePathField()
65 8e45d6fd Sofia Papagiannaki
    icon = models.FilePathField(blank=True)
66 8e45d6fd Sofia Papagiannaki
    auth_token = models.CharField('Authentication Token', max_length=32,
67 8e45d6fd Sofia Papagiannaki
                                  null=True, blank=True)
68 8e45d6fd Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
69 8e45d6fd Sofia Papagiannaki
    auth_token_expires = models.DateTimeField('Token expiration date', null=True)
70 8e45d6fd Sofia Papagiannaki
    
71 8e45d6fd Sofia Papagiannaki
    def save(self, **kwargs):
72 8e45d6fd Sofia Papagiannaki
        if not self.id:
73 8e45d6fd Sofia Papagiannaki
            self.renew_token()
74 8e45d6fd Sofia Papagiannaki
        self.full_clean()
75 8e45d6fd Sofia Papagiannaki
        super(Service, self).save(**kwargs)
76 8e45d6fd Sofia Papagiannaki
    
77 8e45d6fd Sofia Papagiannaki
    def renew_token(self):
78 8e45d6fd Sofia Papagiannaki
        md5 = hashlib.md5()
79 8e45d6fd Sofia Papagiannaki
        md5.update(self.name.encode('ascii', 'ignore'))
80 8e45d6fd Sofia Papagiannaki
        md5.update(self.url.encode('ascii', 'ignore'))
81 8e45d6fd Sofia Papagiannaki
        md5.update(asctime())
82 8e45d6fd Sofia Papagiannaki
83 8e45d6fd Sofia Papagiannaki
        self.auth_token = b64encode(md5.digest())
84 8e45d6fd Sofia Papagiannaki
        self.auth_token_created = datetime.now()
85 8e45d6fd Sofia Papagiannaki
        self.auth_token_expires = self.auth_token_created + \
86 8e45d6fd Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
87 8e45d6fd Sofia Papagiannaki
    
88 8e45d6fd Sofia Papagiannaki
    def __str__(self):
89 8e45d6fd Sofia Papagiannaki
        return self.name
90 8e45d6fd Sofia Papagiannaki
91 8e45d6fd Sofia Papagiannaki
class ResourceMetadata(models.Model):
92 8e45d6fd Sofia Papagiannaki
    key = models.CharField('Name', max_length=255, unique=True, db_index=True)
93 8e45d6fd Sofia Papagiannaki
    value = models.CharField('Value', max_length=255)
94 8e45d6fd Sofia Papagiannaki
95 8e45d6fd Sofia Papagiannaki
class Resource(models.Model):
96 8e45d6fd Sofia Papagiannaki
    name = models.CharField('Name', max_length=255, unique=True, db_index=True)
97 8e45d6fd Sofia Papagiannaki
    meta = models.ManyToManyField(ResourceMetadata)
98 8e45d6fd Sofia Papagiannaki
    service = models.ForeignKey(Service)
99 8e45d6fd Sofia Papagiannaki
    
100 8e45d6fd Sofia Papagiannaki
    def __str__(self):
101 8e45d6fd Sofia Papagiannaki
        return '%s : %s' % (self.service, self.name)
102 8e45d6fd Sofia Papagiannaki
103 8e45d6fd Sofia Papagiannaki
class GroupKind(models.Model):
104 8e45d6fd Sofia Papagiannaki
    name = models.CharField('Name', max_length=255, unique=True, db_index=True)
105 8e45d6fd Sofia Papagiannaki
    
106 8e45d6fd Sofia Papagiannaki
    def __str__(self):
107 8e45d6fd Sofia Papagiannaki
        return self.name
108 8e45d6fd Sofia Papagiannaki
109 8e45d6fd Sofia Papagiannaki
class AstakosGroup(Group):
110 8e45d6fd Sofia Papagiannaki
    kind = models.ForeignKey(GroupKind)
111 8e45d6fd Sofia Papagiannaki
    desc = models.TextField('Description', null=True)
112 8e45d6fd Sofia Papagiannaki
    policy = models.ManyToManyField(Resource, null=True, blank=True, through='AstakosGroupQuota')
113 8e45d6fd Sofia Papagiannaki
    creation_date = models.DateTimeField('Creation date', default=datetime.now())
114 8e45d6fd Sofia Papagiannaki
    issue_date = models.DateTimeField('Issue date', null=True)
115 8e45d6fd Sofia Papagiannaki
    expiration_date = models.DateTimeField('Expiration date', null=True)
116 373daf6a Sofia Papagiannaki
    moderatation_enabled = models.BooleanField('Moderated membership?', default=True)
117 8e45d6fd Sofia Papagiannaki
    approval_date = models.DateTimeField('Activation date', null=True, blank=True)
118 76c68fd6 Sofia Papagiannaki
    estimated_participants = models.PositiveIntegerField('Estimated #participants', null=True)
119 8e45d6fd Sofia Papagiannaki
    
120 8e45d6fd Sofia Papagiannaki
    @property
121 8e45d6fd Sofia Papagiannaki
    def is_disabled(self):
122 8e45d6fd Sofia Papagiannaki
        if not approval_date:
123 8e45d6fd Sofia Papagiannaki
            return False
124 8e45d6fd Sofia Papagiannaki
        return True
125 8e45d6fd Sofia Papagiannaki
    
126 8e45d6fd Sofia Papagiannaki
    @property
127 8e45d6fd Sofia Papagiannaki
    def is_active(self):
128 8e45d6fd Sofia Papagiannaki
        if self.is_disabled:
129 8e45d6fd Sofia Papagiannaki
            return False
130 8e45d6fd Sofia Papagiannaki
        if not self.issue_date:
131 8e45d6fd Sofia Papagiannaki
            return False
132 8e45d6fd Sofia Papagiannaki
        if not self.expiration_date:
133 8e45d6fd Sofia Papagiannaki
            return True
134 8e45d6fd Sofia Papagiannaki
        now = datetime.now()
135 8e45d6fd Sofia Papagiannaki
        if self.issue_date > now:
136 8e45d6fd Sofia Papagiannaki
            return False
137 8e45d6fd Sofia Papagiannaki
        if now >= self.expiration_date:
138 8e45d6fd Sofia Papagiannaki
            return False
139 8e45d6fd Sofia Papagiannaki
        return True
140 8e45d6fd Sofia Papagiannaki
    
141 8e45d6fd Sofia Papagiannaki
    @property
142 8e45d6fd Sofia Papagiannaki
    def participants(self):
143 8e45d6fd Sofia Papagiannaki
        if not self.id:
144 8e45d6fd Sofia Papagiannaki
            return 0
145 8e45d6fd Sofia Papagiannaki
        return self.user_set.count()
146 8e45d6fd Sofia Papagiannaki
    
147 8e45d6fd Sofia Papagiannaki
    def approve(self):
148 8e45d6fd Sofia Papagiannaki
        self.approval_date = datetime.now()
149 8e45d6fd Sofia Papagiannaki
        self.save()
150 8e45d6fd Sofia Papagiannaki
    
151 8e45d6fd Sofia Papagiannaki
    def disapprove(self):
152 8e45d6fd Sofia Papagiannaki
        self.approval_date = None
153 8e45d6fd Sofia Papagiannaki
        self.save()
154 8e45d6fd Sofia Papagiannaki
    
155 8e45d6fd Sofia Papagiannaki
    def approve_member(self, member):
156 373daf6a Sofia Papagiannaki
        m, created = self.membership_set.get_or_create(person=member, group=self)
157 8e45d6fd Sofia Papagiannaki
        m.date_joined = datetime.now()
158 8e45d6fd Sofia Papagiannaki
        m.save()
159 373daf6a Sofia Papagiannaki
        
160 8e45d6fd Sofia Papagiannaki
    def disapprove_member(self, member):
161 8e45d6fd Sofia Papagiannaki
        m = self.membership_set.remove(member)
162 8e45d6fd Sofia Papagiannaki
    
163 8e45d6fd Sofia Papagiannaki
    def get_members(self, approved=True):
164 8e45d6fd Sofia Papagiannaki
        if approved:
165 8e45d6fd Sofia Papagiannaki
            return self.membership_set().filter(is_approved=True)
166 8e45d6fd Sofia Papagiannaki
        return self.membership_set().all()
167 8e45d6fd Sofia Papagiannaki
168 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
169 890b0eaf Sofia Papagiannaki
    """
170 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
171 890b0eaf Sofia Papagiannaki
    """
172 0905ccd2 Sofia Papagiannaki
    # Use UserManager to get the create_user method, etc.
173 0905ccd2 Sofia Papagiannaki
    objects = UserManager()
174 6c736ed7 Kostas Papadimitriou
175 5ed6816e Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
176 5ed6816e Sofia Papagiannaki
    provider = models.CharField('Provider', max_length=255, blank=True)
177 6c736ed7 Kostas Papadimitriou
178 64cd4730 Antony Chazapis
    #for invitations
179 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
180 a196eb7e Sofia Papagiannaki
    level = models.IntegerField('Inviter level', default=user_level)
181 ebd369d0 Sofia Papagiannaki
    invitations = models.IntegerField('Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
182 6c736ed7 Kostas Papadimitriou
183 64cd4730 Antony Chazapis
    auth_token = models.CharField('Authentication Token', max_length=32,
184 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
185 0905ccd2 Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
186 0905ccd2 Sofia Papagiannaki
    auth_token_expires = models.DateTimeField('Token expiration date', null=True)
187 6c736ed7 Kostas Papadimitriou
188 64cd4730 Antony Chazapis
    updated = models.DateTimeField('Update date')
189 890b0eaf Sofia Papagiannaki
    is_verified = models.BooleanField('Is verified?', default=False)
190 6c736ed7 Kostas Papadimitriou
191 15efc749 Sofia Papagiannaki
    # ex. screen_name for twitter, eppn for shibboleth
192 15efc749 Sofia Papagiannaki
    third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
193 6c736ed7 Kostas Papadimitriou
194 ebd369d0 Sofia Papagiannaki
    email_verified = models.BooleanField('Email verified?', default=False)
195 6c736ed7 Kostas Papadimitriou
196 59f598f1 Sofia Papagiannaki
    has_credits = models.BooleanField('Has credits?', default=False)
197 270dd48d Sofia Papagiannaki
    has_signed_terms = models.BooleanField('Agree with the terms?', default=False)
198 0a569195 Sofia Papagiannaki
    date_signed_terms = models.DateTimeField('Signed terms date', null=True, blank=True)
199 59f598f1 Sofia Papagiannaki
    
200 751d24cf Sofia Papagiannaki
    activation_sent = models.DateTimeField('Activation sent data', null=True, blank=True)
201 751d24cf Sofia Papagiannaki
    
202 8e45d6fd Sofia Papagiannaki
    policy = models.ManyToManyField(Resource, null=True, through='AstakosUserQuota')
203 8e45d6fd Sofia Papagiannaki
    
204 8e45d6fd Sofia Papagiannaki
    astakos_groups = models.ManyToManyField(AstakosGroup, verbose_name=_('agroups'), blank=True,
205 8e45d6fd Sofia Papagiannaki
        help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."),
206 8e45d6fd Sofia Papagiannaki
        through='Membership')
207 8e45d6fd Sofia Papagiannaki
    
208 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
209 18ffbee1 Sofia Papagiannaki
    __groupnames = []
210 18ffbee1 Sofia Papagiannaki
    
211 8e45d6fd Sofia Papagiannaki
    owner = models.ManyToManyField(AstakosGroup, related_name='owner', null=True)
212 8e45d6fd Sofia Papagiannaki
    
213 74b273d8 Sofia Papagiannaki
    class Meta:
214 74b273d8 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
215 74b273d8 Sofia Papagiannaki
    
216 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
217 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
218 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
219 18ffbee1 Sofia Papagiannaki
        if self.id:
220 18ffbee1 Sofia Papagiannaki
            self.__groupnames = [g.name for g in self.groups.all()]
221 18ffbee1 Sofia Papagiannaki
        else:
222 18ffbee1 Sofia Papagiannaki
            self.is_active = False
223 18ffbee1 Sofia Papagiannaki
    
224 0905ccd2 Sofia Papagiannaki
    @property
225 0905ccd2 Sofia Papagiannaki
    def realname(self):
226 0905ccd2 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
227 6c736ed7 Kostas Papadimitriou
228 0905ccd2 Sofia Papagiannaki
    @realname.setter
229 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
230 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
231 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
232 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
233 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
234 0905ccd2 Sofia Papagiannaki
        else:
235 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
236 6c736ed7 Kostas Papadimitriou
237 64cd4730 Antony Chazapis
    @property
238 64cd4730 Antony Chazapis
    def invitation(self):
239 64cd4730 Antony Chazapis
        try:
240 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
241 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
242 64cd4730 Antony Chazapis
            return None
243 6c736ed7 Kostas Papadimitriou
244 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
245 64cd4730 Antony Chazapis
        if update_timestamps:
246 64cd4730 Antony Chazapis
            if not self.id:
247 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
248 64cd4730 Antony Chazapis
            self.updated = datetime.now()
249 18ffbee1 Sofia Papagiannaki
        
250 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
251 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
252 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
253 18ffbee1 Sofia Papagiannaki
        
254 9c01d9d1 Sofia Papagiannaki
        if not self.id:
255 9c01d9d1 Sofia Papagiannaki
            # set username
256 9c01d9d1 Sofia Papagiannaki
            while not self.username:
257 9c01d9d1 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
258 9c01d9d1 Sofia Papagiannaki
                try:
259 9c01d9d1 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
260 9c01d9d1 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
261 9c01d9d1 Sofia Papagiannaki
                    self.username = username
262 9c01d9d1 Sofia Papagiannaki
            if not self.provider:
263 9c01d9d1 Sofia Papagiannaki
                self.provider = 'local'
264 9c01d9d1 Sofia Papagiannaki
        report_user_event(self)
265 591d0505 Sofia Papagiannaki
        self.validate_unique_email_isactive()
266 751d24cf Sofia Papagiannaki
        if self.is_active and self.activation_sent:
267 751d24cf Sofia Papagiannaki
            # reset the activation sent
268 751d24cf Sofia Papagiannaki
            self.activation_sent = None
269 8e45d6fd Sofia Papagiannaki
        
270 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
271 18ffbee1 Sofia Papagiannaki
        
272 18ffbee1 Sofia Papagiannaki
        # set group if does not exist
273 18ffbee1 Sofia Papagiannaki
        groupname = 'shibboleth' if self.provider == 'shibboleth' else 'default'
274 18ffbee1 Sofia Papagiannaki
        if groupname not in self.__groupnames:
275 18ffbee1 Sofia Papagiannaki
            try:
276 18ffbee1 Sofia Papagiannaki
                group = Group.objects.get(name = groupname)
277 18ffbee1 Sofia Papagiannaki
                self.groups.add(group)
278 18ffbee1 Sofia Papagiannaki
            except Group.DoesNotExist, e:
279 18ffbee1 Sofia Papagiannaki
                logger.exception(e)
280 64cd4730 Antony Chazapis
    
281 64cd4730 Antony Chazapis
    def renew_token(self):
282 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
283 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
284 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
285 64cd4730 Antony Chazapis
        md5.update(asctime())
286 6c736ed7 Kostas Papadimitriou
287 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
288 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
289 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
290 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
291 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
292 111f3da6 Sofia Papagiannaki
        logger._log(LOGGING_LEVEL, msg, [])
293 6c736ed7 Kostas Papadimitriou
294 64cd4730 Antony Chazapis
    def __unicode__(self):
295 0905ccd2 Sofia Papagiannaki
        return self.username
296 0a569195 Sofia Papagiannaki
    
297 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
298 0a569195 Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username = self.username)
299 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
300 0a569195 Sofia Papagiannaki
        if q.count() != 0:
301 0a569195 Sofia Papagiannaki
            return True
302 0a569195 Sofia Papagiannaki
        return False
303 0a569195 Sofia Papagiannaki
    
304 591d0505 Sofia Papagiannaki
    def validate_unique_email_isactive(self):
305 0a569195 Sofia Papagiannaki
        """
306 0a569195 Sofia Papagiannaki
        Implements a unique_together constraint for email and is_active fields.
307 0a569195 Sofia Papagiannaki
        """
308 0a569195 Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username = self.username)
309 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
310 0a569195 Sofia Papagiannaki
        q = q.filter(is_active = self.is_active)
311 0a569195 Sofia Papagiannaki
        if q.count() != 0:
312 0a569195 Sofia Papagiannaki
            raise ValidationError({'__all__':[_('Another account with the same email & is_active combination found.')]})
313 09e7393c Sofia Papagiannaki
    
314 09e7393c Sofia Papagiannaki
    def signed_terms(self):
315 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
316 09e7393c Sofia Papagiannaki
        if not term:
317 09e7393c Sofia Papagiannaki
            return True
318 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
319 09e7393c Sofia Papagiannaki
            return False
320 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
321 09e7393c Sofia Papagiannaki
            return False
322 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
323 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
324 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
325 09e7393c Sofia Papagiannaki
            self.save()
326 09e7393c Sofia Papagiannaki
            return False
327 09e7393c Sofia Papagiannaki
        return True
328 8e45d6fd Sofia Papagiannaki
    
329 8e45d6fd Sofia Papagiannaki
    def enroll_group(self, group):
330 8e45d6fd Sofia Papagiannaki
        self.membership_set.add(group)
331 8e45d6fd Sofia Papagiannaki
    
332 8e45d6fd Sofia Papagiannaki
    def get_astakos_groups(self, approved=True):
333 8e45d6fd Sofia Papagiannaki
        if approved:
334 8e45d6fd Sofia Papagiannaki
            return self.membership_set().filter(is_approved=True)
335 8e45d6fd Sofia Papagiannaki
        return self.membership_set().all()
336 8e45d6fd Sofia Papagiannaki
337 8e45d6fd Sofia Papagiannaki
class Membership(models.Model):
338 8e45d6fd Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
339 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup)
340 8e45d6fd Sofia Papagiannaki
    date_requested = models.DateField(default=datetime.now())
341 8e45d6fd Sofia Papagiannaki
    date_joined = models.DateField(null=True, db_index=True)
342 8e45d6fd Sofia Papagiannaki
    
343 8e45d6fd Sofia Papagiannaki
    class Meta:
344 8e45d6fd Sofia Papagiannaki
        unique_together = ("person", "group")
345 8e45d6fd Sofia Papagiannaki
    
346 8e45d6fd Sofia Papagiannaki
    @property
347 8e45d6fd Sofia Papagiannaki
    def is_approved(self):
348 8e45d6fd Sofia Papagiannaki
        if self.date_joined:
349 8e45d6fd Sofia Papagiannaki
            return True
350 8e45d6fd Sofia Papagiannaki
        return False
351 8e45d6fd Sofia Papagiannaki
352 8e45d6fd Sofia Papagiannaki
class AstakosGroupQuota(models.Model):
353 8e45d6fd Sofia Papagiannaki
    limit = models.PositiveIntegerField('Limit')
354 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
355 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup, blank=True)
356 8e45d6fd Sofia Papagiannaki
    
357 8e45d6fd Sofia Papagiannaki
    class Meta:
358 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "group")
359 8e45d6fd Sofia Papagiannaki
360 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
361 8e45d6fd Sofia Papagiannaki
    limit = models.PositiveIntegerField('Limit')
362 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
363 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
364 8e45d6fd Sofia Papagiannaki
    
365 8e45d6fd Sofia Papagiannaki
    class Meta:
366 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
367 09e7393c Sofia Papagiannaki
368 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
369 270dd48d Sofia Papagiannaki
    """
370 270dd48d Sofia Papagiannaki
    Model for approval terms
371 270dd48d Sofia Papagiannaki
    """
372 6c736ed7 Kostas Papadimitriou
373 270dd48d Sofia Papagiannaki
    date = models.DateTimeField('Issue date', db_index=True, default=datetime.now())
374 270dd48d Sofia Papagiannaki
    location = models.CharField('Terms location', max_length=255)
375 270dd48d Sofia Papagiannaki
376 64cd4730 Antony Chazapis
class Invitation(models.Model):
377 890b0eaf Sofia Papagiannaki
    """
378 890b0eaf Sofia Papagiannaki
    Model for registring invitations
379 890b0eaf Sofia Papagiannaki
    """
380 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
381 64cd4730 Antony Chazapis
                                null=True)
382 64cd4730 Antony Chazapis
    realname = models.CharField('Real name', max_length=255)
383 ebd369d0 Sofia Papagiannaki
    username = models.CharField('Unique ID', max_length=255, unique=True)
384 64cd4730 Antony Chazapis
    code = models.BigIntegerField('Invitation code', db_index=True)
385 64cd4730 Antony Chazapis
    is_consumed = models.BooleanField('Consumed?', default=False)
386 64cd4730 Antony Chazapis
    created = models.DateTimeField('Creation date', auto_now_add=True)
387 64cd4730 Antony Chazapis
    consumed = models.DateTimeField('Consumption date', null=True, blank=True)
388 64cd4730 Antony Chazapis
    
389 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
390 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
391 8f5a3a06 Sofia Papagiannaki
        if not self.id:
392 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
393 8f5a3a06 Sofia Papagiannaki
    
394 64cd4730 Antony Chazapis
    def consume(self):
395 64cd4730 Antony Chazapis
        self.is_consumed = True
396 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
397 64cd4730 Antony Chazapis
        self.save()
398 6c736ed7 Kostas Papadimitriou
399 64cd4730 Antony Chazapis
    def __unicode__(self):
400 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
401 9c01d9d1 Sofia Papagiannaki
402 9c01d9d1 Sofia Papagiannaki
def report_user_event(user):
403 9c01d9d1 Sofia Papagiannaki
    def should_send(user):
404 9c01d9d1 Sofia Papagiannaki
        # report event incase of new user instance
405 9c01d9d1 Sofia Papagiannaki
        # or if specific fields are modified
406 9c01d9d1 Sofia Papagiannaki
        if not user.id:
407 9c01d9d1 Sofia Papagiannaki
            return True
408 8e45d6fd Sofia Papagiannaki
        try:
409 8e45d6fd Sofia Papagiannaki
            db_instance = AstakosUser.objects.get(id = user.id)
410 8e45d6fd Sofia Papagiannaki
        except AstakosUser.DoesNotExist:
411 8e45d6fd Sofia Papagiannaki
            return True
412 9c01d9d1 Sofia Papagiannaki
        for f in BILLING_FIELDS:
413 9c01d9d1 Sofia Papagiannaki
            if (db_instance.__getattribute__(f) != user.__getattribute__(f)):
414 9c01d9d1 Sofia Papagiannaki
                return True
415 9c01d9d1 Sofia Papagiannaki
        return False
416 6c736ed7 Kostas Papadimitriou
417 3a9f4931 Sofia Papagiannaki
    if QUEUE_CONNECTION and should_send(user):
418 6c736ed7 Kostas Papadimitriou
419 6c736ed7 Kostas Papadimitriou
        from astakos.im.queue.userevent import UserEvent
420 6c736ed7 Kostas Papadimitriou
        from synnefo.lib.queue import exchange_connect, exchange_send, \
421 6c736ed7 Kostas Papadimitriou
                exchange_close
422 6c736ed7 Kostas Papadimitriou
423 59f598f1 Sofia Papagiannaki
        eventType = 'create' if not user.id else 'modify'
424 59f598f1 Sofia Papagiannaki
        body = UserEvent(QUEUE_CLIENT_ID, user, eventType, {}).format()
425 3a9f4931 Sofia Papagiannaki
        conn = exchange_connect(QUEUE_CONNECTION)
426 9e19989d Sofia Papagiannaki
        parts = urlparse(QUEUE_CONNECTION)
427 270dd48d Sofia Papagiannaki
        exchange = parts.path[1:]
428 270dd48d Sofia Papagiannaki
        routing_key = '%s.user' % exchange
429 9c01d9d1 Sofia Papagiannaki
        exchange_send(conn, routing_key, body)
430 68cb6899 Sofia Papagiannaki
        exchange_close(conn)
431 8f5a3a06 Sofia Papagiannaki
432 8f5a3a06 Sofia Papagiannaki
def _generate_invitation_code():
433 8f5a3a06 Sofia Papagiannaki
    while True:
434 8f5a3a06 Sofia Papagiannaki
        code = randint(1, 2L**63 - 1)
435 8f5a3a06 Sofia Papagiannaki
        try:
436 8f5a3a06 Sofia Papagiannaki
            Invitation.objects.get(code=code)
437 8f5a3a06 Sofia Papagiannaki
            # An invitation with this code already exists, try again
438 8f5a3a06 Sofia Papagiannaki
        except Invitation.DoesNotExist:
439 09e7393c Sofia Papagiannaki
            return code
440 09e7393c Sofia Papagiannaki
441 09e7393c Sofia Papagiannaki
def get_latest_terms():
442 09e7393c Sofia Papagiannaki
    try:
443 09e7393c Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
444 09e7393c Sofia Papagiannaki
        return term
445 09e7393c Sofia Papagiannaki
    except IndexError:
446 09e7393c Sofia Papagiannaki
        pass
447 49790d9d Sofia Papagiannaki
    return None
448 49790d9d Sofia Papagiannaki
449 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
450 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
451 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
452 49790d9d Sofia Papagiannaki
        """
453 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
454 49790d9d Sofia Papagiannaki
        ``User`` if valid.
455 49790d9d Sofia Papagiannaki

456 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
457 49790d9d Sofia Papagiannaki
        after activating.
458 49790d9d Sofia Papagiannaki

459 49790d9d Sofia Papagiannaki
        If the key is not valid or has expired, return ``None``.
460 49790d9d Sofia Papagiannaki

461 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
462 49790d9d Sofia Papagiannaki
        return ``None``.
463 49790d9d Sofia Papagiannaki

464 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
465 49790d9d Sofia Papagiannaki

466 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
467 49790d9d Sofia Papagiannaki
        """
468 49790d9d Sofia Papagiannaki
        try:
469 49790d9d Sofia Papagiannaki
            email_change = self.model.objects.get(activation_key=activation_key)
470 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
471 49790d9d Sofia Papagiannaki
                email_change.delete()
472 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
473 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
474 49790d9d Sofia Papagiannaki
            try:
475 49790d9d Sofia Papagiannaki
                AstakosUser.objects.get(email=email_change.new_email_address)
476 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
477 49790d9d Sofia Papagiannaki
                pass
478 49790d9d Sofia Papagiannaki
            else:
479 49790d9d Sofia Papagiannaki
                raise ValueError(_('The new email address is reserved.'))
480 49790d9d Sofia Papagiannaki
            # update user
481 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
482 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
483 49790d9d Sofia Papagiannaki
            user.save()
484 49790d9d Sofia Papagiannaki
            email_change.delete()
485 49790d9d Sofia Papagiannaki
            return user
486 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
487 49790d9d Sofia Papagiannaki
            raise ValueError(_('Invalid activation key'))
488 49790d9d Sofia Papagiannaki
489 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
490 49790d9d Sofia Papagiannaki
    new_email_address = models.EmailField(_(u'new e-mail address'), help_text=_(u'Your old email address will be used until you verify your new one.'))
491 49790d9d Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, unique=True, related_name='emailchange_user')
492 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
493 49790d9d Sofia Papagiannaki
    activation_key = models.CharField(max_length=40, unique=True, db_index=True)
494 49790d9d Sofia Papagiannaki
495 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
496 49790d9d Sofia Papagiannaki
497 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
498 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
499 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
500 ff9290ec Sofia Papagiannaki
501 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
502 ca828a10 Sofia Papagiannaki
    """
503 ca828a10 Sofia Papagiannaki
    Model for registring invitations
504 ca828a10 Sofia Papagiannaki
    """
505 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
506 1eec103a Sofia Papagiannaki
    email = models.EmailField()
507 ca828a10 Sofia Papagiannaki
508 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
509 ff9290ec Sofia Papagiannaki
    try:
510 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
511 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
512 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
513 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
514 ff9290ec Sofia Papagiannaki
        extended_user.renew_token()
515 ff9290ec Sofia Papagiannaki
        extended_user.save()
516 ff9290ec Sofia Papagiannaki
    except:
517 ff9290ec Sofia Papagiannaki
        pass
518 ff9290ec Sofia Papagiannaki
519 ff9290ec Sofia Papagiannaki
def superuser_post_syncdb(sender, **kwargs):
520 ff9290ec Sofia Papagiannaki
    # if there was created a superuser
521 ff9290ec Sofia Papagiannaki
    # associate it with an AstakosUser
522 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
523 ff9290ec Sofia Papagiannaki
    for u in admins:
524 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
525 ff9290ec Sofia Papagiannaki
526 ff9290ec Sofia Papagiannaki
post_syncdb.connect(superuser_post_syncdb)
527 ff9290ec Sofia Papagiannaki
528 ff9290ec Sofia Papagiannaki
def superuser_post_save(sender, instance, **kwargs):
529 ff9290ec Sofia Papagiannaki
    if instance.is_superuser:
530 ff9290ec Sofia Papagiannaki
        create_astakos_user(instance)
531 ff9290ec Sofia Papagiannaki
532 ff9290ec Sofia Papagiannaki
post_save.connect(superuser_post_save, sender=User)