Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 8e45d6fd

History | View | Annotate | Download (18.9 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
    identifier = models.URLField('URI identifier', unique=True, default='', db_index=True)
113 8e45d6fd Sofia Papagiannaki
    policy = models.ManyToManyField(Resource, null=True, blank=True, through='AstakosGroupQuota')
114 8e45d6fd Sofia Papagiannaki
    creation_date = models.DateTimeField('Creation date', default=datetime.now())
115 8e45d6fd Sofia Papagiannaki
    issue_date = models.DateTimeField('Issue date', null=True)
116 8e45d6fd Sofia Papagiannaki
    expiration_date = models.DateTimeField('Expiration date', null=True)
117 8e45d6fd Sofia Papagiannaki
    moderatation_enabled = models.BooleanField('Moderated membership?', default=False)
118 8e45d6fd Sofia Papagiannaki
    approval_date = models.DateTimeField('Activation date', null=True, blank=True)
119 8e45d6fd Sofia Papagiannaki
    estimated_participants = models.PositiveIntegerField('Estimated number of participants', null=True)
120 8e45d6fd Sofia Papagiannaki
    
121 8e45d6fd Sofia Papagiannaki
    @property
122 8e45d6fd Sofia Papagiannaki
    def is_disabled(self):
123 8e45d6fd Sofia Papagiannaki
        if not approval_date:
124 8e45d6fd Sofia Papagiannaki
            return False
125 8e45d6fd Sofia Papagiannaki
        return True
126 8e45d6fd Sofia Papagiannaki
    
127 8e45d6fd Sofia Papagiannaki
    @property
128 8e45d6fd Sofia Papagiannaki
    def is_active(self):
129 8e45d6fd Sofia Papagiannaki
        if self.is_disabled:
130 8e45d6fd Sofia Papagiannaki
            return False
131 8e45d6fd Sofia Papagiannaki
        if not self.issue_date:
132 8e45d6fd Sofia Papagiannaki
            return False
133 8e45d6fd Sofia Papagiannaki
        if not self.expiration_date:
134 8e45d6fd Sofia Papagiannaki
            return True
135 8e45d6fd Sofia Papagiannaki
        now = datetime.now()
136 8e45d6fd Sofia Papagiannaki
        if self.issue_date > now:
137 8e45d6fd Sofia Papagiannaki
            return False
138 8e45d6fd Sofia Papagiannaki
        if now >= self.expiration_date:
139 8e45d6fd Sofia Papagiannaki
            return False
140 8e45d6fd Sofia Papagiannaki
        return True
141 8e45d6fd Sofia Papagiannaki
    
142 8e45d6fd Sofia Papagiannaki
    @property
143 8e45d6fd Sofia Papagiannaki
    def participants(self):
144 8e45d6fd Sofia Papagiannaki
        if not self.id:
145 8e45d6fd Sofia Papagiannaki
            return 0
146 8e45d6fd Sofia Papagiannaki
        return self.user_set.count()
147 8e45d6fd Sofia Papagiannaki
    
148 8e45d6fd Sofia Papagiannaki
    def approve(self):
149 8e45d6fd Sofia Papagiannaki
        self.approval_date = datetime.now()
150 8e45d6fd Sofia Papagiannaki
        self.save()
151 8e45d6fd Sofia Papagiannaki
    
152 8e45d6fd Sofia Papagiannaki
    def disapprove(self):
153 8e45d6fd Sofia Papagiannaki
        self.approval_date = None
154 8e45d6fd Sofia Papagiannaki
        self.save()
155 8e45d6fd Sofia Papagiannaki
    
156 8e45d6fd Sofia Papagiannaki
    def approve_member(self, member):
157 8e45d6fd Sofia Papagiannaki
        m = self.membership_set.get(person=member)
158 8e45d6fd Sofia Papagiannaki
        m.date_joined = datetime.now()
159 8e45d6fd Sofia Papagiannaki
        m.save()
160 8e45d6fd Sofia Papagiannaki
    
161 8e45d6fd Sofia Papagiannaki
    def disapprove_member(self, member):
162 8e45d6fd Sofia Papagiannaki
        m = self.membership_set.remove(member)
163 8e45d6fd Sofia Papagiannaki
    
164 8e45d6fd Sofia Papagiannaki
    def get_members(self, approved=True):
165 8e45d6fd Sofia Papagiannaki
        if approved:
166 8e45d6fd Sofia Papagiannaki
            return self.membership_set().filter(is_approved=True)
167 8e45d6fd Sofia Papagiannaki
        return self.membership_set().all()
168 8e45d6fd Sofia Papagiannaki
169 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
170 890b0eaf Sofia Papagiannaki
    """
171 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
172 890b0eaf Sofia Papagiannaki
    """
173 0905ccd2 Sofia Papagiannaki
    # Use UserManager to get the create_user method, etc.
174 0905ccd2 Sofia Papagiannaki
    objects = UserManager()
175 6c736ed7 Kostas Papadimitriou
176 5ed6816e Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
177 5ed6816e Sofia Papagiannaki
    provider = models.CharField('Provider', max_length=255, blank=True)
178 6c736ed7 Kostas Papadimitriou
179 64cd4730 Antony Chazapis
    #for invitations
180 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
181 a196eb7e Sofia Papagiannaki
    level = models.IntegerField('Inviter level', default=user_level)
182 ebd369d0 Sofia Papagiannaki
    invitations = models.IntegerField('Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
183 6c736ed7 Kostas Papadimitriou
184 64cd4730 Antony Chazapis
    auth_token = models.CharField('Authentication Token', max_length=32,
185 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
186 0905ccd2 Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
187 0905ccd2 Sofia Papagiannaki
    auth_token_expires = models.DateTimeField('Token expiration date', null=True)
188 6c736ed7 Kostas Papadimitriou
189 64cd4730 Antony Chazapis
    updated = models.DateTimeField('Update date')
190 890b0eaf Sofia Papagiannaki
    is_verified = models.BooleanField('Is verified?', default=False)
191 6c736ed7 Kostas Papadimitriou
192 15efc749 Sofia Papagiannaki
    # ex. screen_name for twitter, eppn for shibboleth
193 15efc749 Sofia Papagiannaki
    third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
194 6c736ed7 Kostas Papadimitriou
195 ebd369d0 Sofia Papagiannaki
    email_verified = models.BooleanField('Email verified?', default=False)
196 6c736ed7 Kostas Papadimitriou
197 59f598f1 Sofia Papagiannaki
    has_credits = models.BooleanField('Has credits?', default=False)
198 270dd48d Sofia Papagiannaki
    has_signed_terms = models.BooleanField('Agree with the terms?', default=False)
199 0a569195 Sofia Papagiannaki
    date_signed_terms = models.DateTimeField('Signed terms date', null=True, blank=True)
200 59f598f1 Sofia Papagiannaki
    
201 751d24cf Sofia Papagiannaki
    activation_sent = models.DateTimeField('Activation sent data', null=True, blank=True)
202 751d24cf Sofia Papagiannaki
    
203 8e45d6fd Sofia Papagiannaki
    policy = models.ManyToManyField(Resource, null=True, through='AstakosUserQuota')
204 8e45d6fd Sofia Papagiannaki
    
205 8e45d6fd Sofia Papagiannaki
    astakos_groups = models.ManyToManyField(AstakosGroup, verbose_name=_('agroups'), blank=True,
206 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."),
207 8e45d6fd Sofia Papagiannaki
        through='Membership')
208 8e45d6fd Sofia Papagiannaki
    
209 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
210 18ffbee1 Sofia Papagiannaki
    __groupnames = []
211 18ffbee1 Sofia Papagiannaki
    
212 8e45d6fd Sofia Papagiannaki
    owner = models.ManyToManyField(AstakosGroup, related_name='owner', null=True)
213 8e45d6fd Sofia Papagiannaki
    
214 74b273d8 Sofia Papagiannaki
    class Meta:
215 74b273d8 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
216 74b273d8 Sofia Papagiannaki
    
217 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
218 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
219 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
220 18ffbee1 Sofia Papagiannaki
        if self.id:
221 18ffbee1 Sofia Papagiannaki
            self.__groupnames = [g.name for g in self.groups.all()]
222 18ffbee1 Sofia Papagiannaki
        else:
223 18ffbee1 Sofia Papagiannaki
            self.is_active = False
224 18ffbee1 Sofia Papagiannaki
    
225 0905ccd2 Sofia Papagiannaki
    @property
226 0905ccd2 Sofia Papagiannaki
    def realname(self):
227 0905ccd2 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
228 6c736ed7 Kostas Papadimitriou
229 0905ccd2 Sofia Papagiannaki
    @realname.setter
230 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
231 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
232 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
233 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
234 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
235 0905ccd2 Sofia Papagiannaki
        else:
236 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
237 6c736ed7 Kostas Papadimitriou
238 64cd4730 Antony Chazapis
    @property
239 64cd4730 Antony Chazapis
    def invitation(self):
240 64cd4730 Antony Chazapis
        try:
241 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
242 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
243 64cd4730 Antony Chazapis
            return None
244 6c736ed7 Kostas Papadimitriou
245 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
246 64cd4730 Antony Chazapis
        if update_timestamps:
247 64cd4730 Antony Chazapis
            if not self.id:
248 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
249 64cd4730 Antony Chazapis
            self.updated = datetime.now()
250 18ffbee1 Sofia Papagiannaki
        
251 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
252 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
253 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
254 18ffbee1 Sofia Papagiannaki
        
255 9c01d9d1 Sofia Papagiannaki
        if not self.id:
256 9c01d9d1 Sofia Papagiannaki
            # set username
257 9c01d9d1 Sofia Papagiannaki
            while not self.username:
258 9c01d9d1 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
259 9c01d9d1 Sofia Papagiannaki
                try:
260 9c01d9d1 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
261 9c01d9d1 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
262 9c01d9d1 Sofia Papagiannaki
                    self.username = username
263 9c01d9d1 Sofia Papagiannaki
            if not self.provider:
264 9c01d9d1 Sofia Papagiannaki
                self.provider = 'local'
265 9c01d9d1 Sofia Papagiannaki
        report_user_event(self)
266 591d0505 Sofia Papagiannaki
        self.validate_unique_email_isactive()
267 751d24cf Sofia Papagiannaki
        if self.is_active and self.activation_sent:
268 751d24cf Sofia Papagiannaki
            # reset the activation sent
269 751d24cf Sofia Papagiannaki
            self.activation_sent = None
270 8e45d6fd Sofia Papagiannaki
        
271 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
272 18ffbee1 Sofia Papagiannaki
        
273 18ffbee1 Sofia Papagiannaki
        # set group if does not exist
274 18ffbee1 Sofia Papagiannaki
        groupname = 'shibboleth' if self.provider == 'shibboleth' else 'default'
275 18ffbee1 Sofia Papagiannaki
        if groupname not in self.__groupnames:
276 18ffbee1 Sofia Papagiannaki
            try:
277 18ffbee1 Sofia Papagiannaki
                group = Group.objects.get(name = groupname)
278 18ffbee1 Sofia Papagiannaki
                self.groups.add(group)
279 18ffbee1 Sofia Papagiannaki
            except Group.DoesNotExist, e:
280 18ffbee1 Sofia Papagiannaki
                logger.exception(e)
281 64cd4730 Antony Chazapis
    
282 64cd4730 Antony Chazapis
    def renew_token(self):
283 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
284 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
285 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
286 64cd4730 Antony Chazapis
        md5.update(asctime())
287 6c736ed7 Kostas Papadimitriou
288 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
289 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
290 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
291 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
292 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
293 111f3da6 Sofia Papagiannaki
        logger._log(LOGGING_LEVEL, msg, [])
294 6c736ed7 Kostas Papadimitriou
295 64cd4730 Antony Chazapis
    def __unicode__(self):
296 0905ccd2 Sofia Papagiannaki
        return self.username
297 0a569195 Sofia Papagiannaki
    
298 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
299 0a569195 Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username = self.username)
300 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
301 0a569195 Sofia Papagiannaki
        if q.count() != 0:
302 0a569195 Sofia Papagiannaki
            return True
303 0a569195 Sofia Papagiannaki
        return False
304 0a569195 Sofia Papagiannaki
    
305 591d0505 Sofia Papagiannaki
    def validate_unique_email_isactive(self):
306 0a569195 Sofia Papagiannaki
        """
307 0a569195 Sofia Papagiannaki
        Implements a unique_together constraint for email and is_active fields.
308 0a569195 Sofia Papagiannaki
        """
309 0a569195 Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username = self.username)
310 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
311 0a569195 Sofia Papagiannaki
        q = q.filter(is_active = self.is_active)
312 0a569195 Sofia Papagiannaki
        if q.count() != 0:
313 0a569195 Sofia Papagiannaki
            raise ValidationError({'__all__':[_('Another account with the same email & is_active combination found.')]})
314 09e7393c Sofia Papagiannaki
    
315 09e7393c Sofia Papagiannaki
    def signed_terms(self):
316 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
317 09e7393c Sofia Papagiannaki
        if not term:
318 09e7393c Sofia Papagiannaki
            return True
319 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
320 09e7393c Sofia Papagiannaki
            return False
321 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
322 09e7393c Sofia Papagiannaki
            return False
323 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
324 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
325 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
326 09e7393c Sofia Papagiannaki
            self.save()
327 09e7393c Sofia Papagiannaki
            return False
328 09e7393c Sofia Papagiannaki
        return True
329 8e45d6fd Sofia Papagiannaki
    
330 8e45d6fd Sofia Papagiannaki
    def enroll_group(self, group):
331 8e45d6fd Sofia Papagiannaki
        self.membership_set.add(group)
332 8e45d6fd Sofia Papagiannaki
    
333 8e45d6fd Sofia Papagiannaki
    def get_astakos_groups(self, approved=True):
334 8e45d6fd Sofia Papagiannaki
        if approved:
335 8e45d6fd Sofia Papagiannaki
            return self.membership_set().filter(is_approved=True)
336 8e45d6fd Sofia Papagiannaki
        return self.membership_set().all()
337 8e45d6fd Sofia Papagiannaki
338 8e45d6fd Sofia Papagiannaki
class Membership(models.Model):
339 8e45d6fd Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
340 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup)
341 8e45d6fd Sofia Papagiannaki
    date_requested = models.DateField(default=datetime.now())
342 8e45d6fd Sofia Papagiannaki
    date_joined = models.DateField(null=True, db_index=True)
343 8e45d6fd Sofia Papagiannaki
    
344 8e45d6fd Sofia Papagiannaki
    class Meta:
345 8e45d6fd Sofia Papagiannaki
        unique_together = ("person", "group")
346 8e45d6fd Sofia Papagiannaki
    
347 8e45d6fd Sofia Papagiannaki
    @property
348 8e45d6fd Sofia Papagiannaki
    def is_approved(self):
349 8e45d6fd Sofia Papagiannaki
        if self.date_joined:
350 8e45d6fd Sofia Papagiannaki
            return True
351 8e45d6fd Sofia Papagiannaki
        return False
352 8e45d6fd Sofia Papagiannaki
353 8e45d6fd Sofia Papagiannaki
class AstakosGroupQuota(models.Model):
354 8e45d6fd Sofia Papagiannaki
    limit = models.PositiveIntegerField('Limit')
355 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
356 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup, blank=True)
357 8e45d6fd Sofia Papagiannaki
    
358 8e45d6fd Sofia Papagiannaki
    class Meta:
359 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "group")
360 8e45d6fd Sofia Papagiannaki
361 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
362 8e45d6fd Sofia Papagiannaki
    limit = models.PositiveIntegerField('Limit')
363 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
364 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
365 8e45d6fd Sofia Papagiannaki
    
366 8e45d6fd Sofia Papagiannaki
    class Meta:
367 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
368 09e7393c Sofia Papagiannaki
369 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
370 270dd48d Sofia Papagiannaki
    """
371 270dd48d Sofia Papagiannaki
    Model for approval terms
372 270dd48d Sofia Papagiannaki
    """
373 6c736ed7 Kostas Papadimitriou
374 270dd48d Sofia Papagiannaki
    date = models.DateTimeField('Issue date', db_index=True, default=datetime.now())
375 270dd48d Sofia Papagiannaki
    location = models.CharField('Terms location', max_length=255)
376 270dd48d Sofia Papagiannaki
377 64cd4730 Antony Chazapis
class Invitation(models.Model):
378 890b0eaf Sofia Papagiannaki
    """
379 890b0eaf Sofia Papagiannaki
    Model for registring invitations
380 890b0eaf Sofia Papagiannaki
    """
381 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
382 64cd4730 Antony Chazapis
                                null=True)
383 64cd4730 Antony Chazapis
    realname = models.CharField('Real name', max_length=255)
384 ebd369d0 Sofia Papagiannaki
    username = models.CharField('Unique ID', max_length=255, unique=True)
385 64cd4730 Antony Chazapis
    code = models.BigIntegerField('Invitation code', db_index=True)
386 64cd4730 Antony Chazapis
    is_consumed = models.BooleanField('Consumed?', default=False)
387 64cd4730 Antony Chazapis
    created = models.DateTimeField('Creation date', auto_now_add=True)
388 64cd4730 Antony Chazapis
    consumed = models.DateTimeField('Consumption date', null=True, blank=True)
389 64cd4730 Antony Chazapis
    
390 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
391 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
392 8f5a3a06 Sofia Papagiannaki
        if not self.id:
393 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
394 8f5a3a06 Sofia Papagiannaki
    
395 64cd4730 Antony Chazapis
    def consume(self):
396 64cd4730 Antony Chazapis
        self.is_consumed = True
397 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
398 64cd4730 Antony Chazapis
        self.save()
399 6c736ed7 Kostas Papadimitriou
400 64cd4730 Antony Chazapis
    def __unicode__(self):
401 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
402 9c01d9d1 Sofia Papagiannaki
403 9c01d9d1 Sofia Papagiannaki
def report_user_event(user):
404 9c01d9d1 Sofia Papagiannaki
    def should_send(user):
405 9c01d9d1 Sofia Papagiannaki
        # report event incase of new user instance
406 9c01d9d1 Sofia Papagiannaki
        # or if specific fields are modified
407 9c01d9d1 Sofia Papagiannaki
        if not user.id:
408 9c01d9d1 Sofia Papagiannaki
            return True
409 8e45d6fd Sofia Papagiannaki
        try:
410 8e45d6fd Sofia Papagiannaki
            db_instance = AstakosUser.objects.get(id = user.id)
411 8e45d6fd Sofia Papagiannaki
        except AstakosUser.DoesNotExist:
412 8e45d6fd Sofia Papagiannaki
            return True
413 9c01d9d1 Sofia Papagiannaki
        for f in BILLING_FIELDS:
414 9c01d9d1 Sofia Papagiannaki
            if (db_instance.__getattribute__(f) != user.__getattribute__(f)):
415 9c01d9d1 Sofia Papagiannaki
                return True
416 9c01d9d1 Sofia Papagiannaki
        return False
417 6c736ed7 Kostas Papadimitriou
418 3a9f4931 Sofia Papagiannaki
    if QUEUE_CONNECTION and should_send(user):
419 6c736ed7 Kostas Papadimitriou
420 6c736ed7 Kostas Papadimitriou
        from astakos.im.queue.userevent import UserEvent
421 6c736ed7 Kostas Papadimitriou
        from synnefo.lib.queue import exchange_connect, exchange_send, \
422 6c736ed7 Kostas Papadimitriou
                exchange_close
423 6c736ed7 Kostas Papadimitriou
424 59f598f1 Sofia Papagiannaki
        eventType = 'create' if not user.id else 'modify'
425 59f598f1 Sofia Papagiannaki
        body = UserEvent(QUEUE_CLIENT_ID, user, eventType, {}).format()
426 3a9f4931 Sofia Papagiannaki
        conn = exchange_connect(QUEUE_CONNECTION)
427 9e19989d Sofia Papagiannaki
        parts = urlparse(QUEUE_CONNECTION)
428 270dd48d Sofia Papagiannaki
        exchange = parts.path[1:]
429 270dd48d Sofia Papagiannaki
        routing_key = '%s.user' % exchange
430 9c01d9d1 Sofia Papagiannaki
        exchange_send(conn, routing_key, body)
431 68cb6899 Sofia Papagiannaki
        exchange_close(conn)
432 8f5a3a06 Sofia Papagiannaki
433 8f5a3a06 Sofia Papagiannaki
def _generate_invitation_code():
434 8f5a3a06 Sofia Papagiannaki
    while True:
435 8f5a3a06 Sofia Papagiannaki
        code = randint(1, 2L**63 - 1)
436 8f5a3a06 Sofia Papagiannaki
        try:
437 8f5a3a06 Sofia Papagiannaki
            Invitation.objects.get(code=code)
438 8f5a3a06 Sofia Papagiannaki
            # An invitation with this code already exists, try again
439 8f5a3a06 Sofia Papagiannaki
        except Invitation.DoesNotExist:
440 09e7393c Sofia Papagiannaki
            return code
441 09e7393c Sofia Papagiannaki
442 09e7393c Sofia Papagiannaki
def get_latest_terms():
443 09e7393c Sofia Papagiannaki
    try:
444 09e7393c Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
445 09e7393c Sofia Papagiannaki
        return term
446 09e7393c Sofia Papagiannaki
    except IndexError:
447 09e7393c Sofia Papagiannaki
        pass
448 49790d9d Sofia Papagiannaki
    return None
449 49790d9d Sofia Papagiannaki
450 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
451 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
452 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
453 49790d9d Sofia Papagiannaki
        """
454 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
455 49790d9d Sofia Papagiannaki
        ``User`` if valid.
456 49790d9d Sofia Papagiannaki

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

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

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

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

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