Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ ff9290ec

History | View | Annotate | Download (12.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 49790d9d Sofia Papagiannaki
AUTH_TOKEN_DURATION, BILLING_FIELDS, QUEUE_CONNECTION, SITENAME, \
56 49790d9d Sofia Papagiannaki
EMAILCHANGE_ACTIVATION_DAYS
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 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
63 890b0eaf Sofia Papagiannaki
    """
64 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
65 890b0eaf Sofia Papagiannaki
    """
66 0905ccd2 Sofia Papagiannaki
    # Use UserManager to get the create_user method, etc.
67 0905ccd2 Sofia Papagiannaki
    objects = UserManager()
68 6c736ed7 Kostas Papadimitriou
69 5ed6816e Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
70 5ed6816e Sofia Papagiannaki
    provider = models.CharField('Provider', max_length=255, blank=True)
71 6c736ed7 Kostas Papadimitriou
72 64cd4730 Antony Chazapis
    #for invitations
73 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
74 a196eb7e Sofia Papagiannaki
    level = models.IntegerField('Inviter level', default=user_level)
75 ebd369d0 Sofia Papagiannaki
    invitations = models.IntegerField('Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
76 6c736ed7 Kostas Papadimitriou
77 64cd4730 Antony Chazapis
    auth_token = models.CharField('Authentication Token', max_length=32,
78 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
79 0905ccd2 Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
80 0905ccd2 Sofia Papagiannaki
    auth_token_expires = models.DateTimeField('Token expiration date', null=True)
81 6c736ed7 Kostas Papadimitriou
82 64cd4730 Antony Chazapis
    updated = models.DateTimeField('Update date')
83 890b0eaf Sofia Papagiannaki
    is_verified = models.BooleanField('Is verified?', default=False)
84 6c736ed7 Kostas Papadimitriou
85 15efc749 Sofia Papagiannaki
    # ex. screen_name for twitter, eppn for shibboleth
86 15efc749 Sofia Papagiannaki
    third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
87 6c736ed7 Kostas Papadimitriou
88 ebd369d0 Sofia Papagiannaki
    email_verified = models.BooleanField('Email verified?', default=False)
89 6c736ed7 Kostas Papadimitriou
90 59f598f1 Sofia Papagiannaki
    has_credits = models.BooleanField('Has credits?', default=False)
91 270dd48d Sofia Papagiannaki
    has_signed_terms = models.BooleanField('Agree with the terms?', default=False)
92 0a569195 Sofia Papagiannaki
    date_signed_terms = models.DateTimeField('Signed terms date', null=True, blank=True)
93 59f598f1 Sofia Papagiannaki
    
94 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
95 18ffbee1 Sofia Papagiannaki
    __groupnames = []
96 18ffbee1 Sofia Papagiannaki
    
97 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
98 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
99 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
100 18ffbee1 Sofia Papagiannaki
        if self.id:
101 18ffbee1 Sofia Papagiannaki
            self.__groupnames = [g.name for g in self.groups.all()]
102 18ffbee1 Sofia Papagiannaki
        else:
103 18ffbee1 Sofia Papagiannaki
            self.is_active = False
104 18ffbee1 Sofia Papagiannaki
    
105 0905ccd2 Sofia Papagiannaki
    @property
106 0905ccd2 Sofia Papagiannaki
    def realname(self):
107 0905ccd2 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
108 6c736ed7 Kostas Papadimitriou
109 0905ccd2 Sofia Papagiannaki
    @realname.setter
110 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
111 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
112 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
113 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
114 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
115 0905ccd2 Sofia Papagiannaki
        else:
116 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
117 6c736ed7 Kostas Papadimitriou
118 64cd4730 Antony Chazapis
    @property
119 64cd4730 Antony Chazapis
    def invitation(self):
120 64cd4730 Antony Chazapis
        try:
121 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
122 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
123 64cd4730 Antony Chazapis
            return None
124 6c736ed7 Kostas Papadimitriou
125 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
126 64cd4730 Antony Chazapis
        if update_timestamps:
127 64cd4730 Antony Chazapis
            if not self.id:
128 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
129 64cd4730 Antony Chazapis
            self.updated = datetime.now()
130 18ffbee1 Sofia Papagiannaki
        
131 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
132 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
133 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
134 18ffbee1 Sofia Papagiannaki
        
135 9c01d9d1 Sofia Papagiannaki
        if not self.id:
136 9c01d9d1 Sofia Papagiannaki
            # set username
137 9c01d9d1 Sofia Papagiannaki
            while not self.username:
138 9c01d9d1 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
139 9c01d9d1 Sofia Papagiannaki
                try:
140 9c01d9d1 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
141 9c01d9d1 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
142 9c01d9d1 Sofia Papagiannaki
                    self.username = username
143 9c01d9d1 Sofia Papagiannaki
            if not self.provider:
144 9c01d9d1 Sofia Papagiannaki
                self.provider = 'local'
145 9c01d9d1 Sofia Papagiannaki
        report_user_event(self)
146 591d0505 Sofia Papagiannaki
        self.validate_unique_email_isactive()
147 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
148 18ffbee1 Sofia Papagiannaki
        
149 18ffbee1 Sofia Papagiannaki
        # set group if does not exist
150 18ffbee1 Sofia Papagiannaki
        groupname = 'shibboleth' if self.provider == 'shibboleth' else 'default'
151 18ffbee1 Sofia Papagiannaki
        if groupname not in self.__groupnames:
152 18ffbee1 Sofia Papagiannaki
            try:
153 18ffbee1 Sofia Papagiannaki
                group = Group.objects.get(name = groupname)
154 18ffbee1 Sofia Papagiannaki
                self.groups.add(group)
155 18ffbee1 Sofia Papagiannaki
            except Group.DoesNotExist, e:
156 18ffbee1 Sofia Papagiannaki
                logger.exception(e)
157 64cd4730 Antony Chazapis
    
158 64cd4730 Antony Chazapis
    def renew_token(self):
159 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
160 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
161 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
162 64cd4730 Antony Chazapis
        md5.update(asctime())
163 6c736ed7 Kostas Papadimitriou
164 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
165 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
166 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
167 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
168 6c736ed7 Kostas Papadimitriou
169 64cd4730 Antony Chazapis
    def __unicode__(self):
170 0905ccd2 Sofia Papagiannaki
        return self.username
171 0a569195 Sofia Papagiannaki
    
172 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
173 0a569195 Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username = self.username)
174 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
175 0a569195 Sofia Papagiannaki
        if q.count() != 0:
176 0a569195 Sofia Papagiannaki
            return True
177 0a569195 Sofia Papagiannaki
        return False
178 0a569195 Sofia Papagiannaki
    
179 591d0505 Sofia Papagiannaki
    def validate_unique_email_isactive(self):
180 0a569195 Sofia Papagiannaki
        """
181 0a569195 Sofia Papagiannaki
        Implements a unique_together constraint for email and is_active fields.
182 0a569195 Sofia Papagiannaki
        """
183 0a569195 Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username = self.username)
184 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
185 0a569195 Sofia Papagiannaki
        q = q.filter(is_active = self.is_active)
186 0a569195 Sofia Papagiannaki
        if q.count() != 0:
187 0a569195 Sofia Papagiannaki
            raise ValidationError({'__all__':[_('Another account with the same email & is_active combination found.')]})
188 09e7393c Sofia Papagiannaki
    
189 09e7393c Sofia Papagiannaki
    def signed_terms(self):
190 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
191 09e7393c Sofia Papagiannaki
        if not term:
192 09e7393c Sofia Papagiannaki
            return True
193 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
194 09e7393c Sofia Papagiannaki
            return False
195 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
196 09e7393c Sofia Papagiannaki
            return False
197 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
198 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
199 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
200 09e7393c Sofia Papagiannaki
            self.save()
201 09e7393c Sofia Papagiannaki
            return False
202 09e7393c Sofia Papagiannaki
        return True
203 09e7393c Sofia Papagiannaki
204 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
205 270dd48d Sofia Papagiannaki
    """
206 270dd48d Sofia Papagiannaki
    Model for approval terms
207 270dd48d Sofia Papagiannaki
    """
208 6c736ed7 Kostas Papadimitriou
209 270dd48d Sofia Papagiannaki
    date = models.DateTimeField('Issue date', db_index=True, default=datetime.now())
210 270dd48d Sofia Papagiannaki
    location = models.CharField('Terms location', max_length=255)
211 270dd48d Sofia Papagiannaki
212 64cd4730 Antony Chazapis
class Invitation(models.Model):
213 890b0eaf Sofia Papagiannaki
    """
214 890b0eaf Sofia Papagiannaki
    Model for registring invitations
215 890b0eaf Sofia Papagiannaki
    """
216 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
217 64cd4730 Antony Chazapis
                                null=True)
218 64cd4730 Antony Chazapis
    realname = models.CharField('Real name', max_length=255)
219 ebd369d0 Sofia Papagiannaki
    username = models.CharField('Unique ID', max_length=255, unique=True)
220 64cd4730 Antony Chazapis
    code = models.BigIntegerField('Invitation code', db_index=True)
221 64cd4730 Antony Chazapis
    is_consumed = models.BooleanField('Consumed?', default=False)
222 64cd4730 Antony Chazapis
    created = models.DateTimeField('Creation date', auto_now_add=True)
223 64cd4730 Antony Chazapis
    consumed = models.DateTimeField('Consumption date', null=True, blank=True)
224 64cd4730 Antony Chazapis
    
225 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
226 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
227 8f5a3a06 Sofia Papagiannaki
        if not self.id:
228 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
229 8f5a3a06 Sofia Papagiannaki
    
230 64cd4730 Antony Chazapis
    def consume(self):
231 64cd4730 Antony Chazapis
        self.is_consumed = True
232 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
233 64cd4730 Antony Chazapis
        self.save()
234 6c736ed7 Kostas Papadimitriou
235 64cd4730 Antony Chazapis
    def __unicode__(self):
236 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
237 9c01d9d1 Sofia Papagiannaki
238 9c01d9d1 Sofia Papagiannaki
def report_user_event(user):
239 9c01d9d1 Sofia Papagiannaki
    def should_send(user):
240 9c01d9d1 Sofia Papagiannaki
        # report event incase of new user instance
241 9c01d9d1 Sofia Papagiannaki
        # or if specific fields are modified
242 9c01d9d1 Sofia Papagiannaki
        if not user.id:
243 9c01d9d1 Sofia Papagiannaki
            return True
244 9c01d9d1 Sofia Papagiannaki
        db_instance = AstakosUser.objects.get(id = user.id)
245 9c01d9d1 Sofia Papagiannaki
        for f in BILLING_FIELDS:
246 9c01d9d1 Sofia Papagiannaki
            if (db_instance.__getattribute__(f) != user.__getattribute__(f)):
247 9c01d9d1 Sofia Papagiannaki
                return True
248 9c01d9d1 Sofia Papagiannaki
        return False
249 6c736ed7 Kostas Papadimitriou
250 3a9f4931 Sofia Papagiannaki
    if QUEUE_CONNECTION and should_send(user):
251 6c736ed7 Kostas Papadimitriou
252 6c736ed7 Kostas Papadimitriou
        from astakos.im.queue.userevent import UserEvent
253 6c736ed7 Kostas Papadimitriou
        from synnefo.lib.queue import exchange_connect, exchange_send, \
254 6c736ed7 Kostas Papadimitriou
                exchange_close
255 6c736ed7 Kostas Papadimitriou
256 59f598f1 Sofia Papagiannaki
        eventType = 'create' if not user.id else 'modify'
257 59f598f1 Sofia Papagiannaki
        body = UserEvent(QUEUE_CLIENT_ID, user, eventType, {}).format()
258 3a9f4931 Sofia Papagiannaki
        conn = exchange_connect(QUEUE_CONNECTION)
259 9e19989d Sofia Papagiannaki
        parts = urlparse(QUEUE_CONNECTION)
260 270dd48d Sofia Papagiannaki
        exchange = parts.path[1:]
261 270dd48d Sofia Papagiannaki
        routing_key = '%s.user' % exchange
262 9c01d9d1 Sofia Papagiannaki
        exchange_send(conn, routing_key, body)
263 68cb6899 Sofia Papagiannaki
        exchange_close(conn)
264 8f5a3a06 Sofia Papagiannaki
265 8f5a3a06 Sofia Papagiannaki
def _generate_invitation_code():
266 8f5a3a06 Sofia Papagiannaki
    while True:
267 8f5a3a06 Sofia Papagiannaki
        code = randint(1, 2L**63 - 1)
268 8f5a3a06 Sofia Papagiannaki
        try:
269 8f5a3a06 Sofia Papagiannaki
            Invitation.objects.get(code=code)
270 8f5a3a06 Sofia Papagiannaki
            # An invitation with this code already exists, try again
271 8f5a3a06 Sofia Papagiannaki
        except Invitation.DoesNotExist:
272 09e7393c Sofia Papagiannaki
            return code
273 09e7393c Sofia Papagiannaki
274 09e7393c Sofia Papagiannaki
def get_latest_terms():
275 09e7393c Sofia Papagiannaki
    try:
276 09e7393c Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
277 09e7393c Sofia Papagiannaki
        return term
278 09e7393c Sofia Papagiannaki
    except IndexError:
279 09e7393c Sofia Papagiannaki
        pass
280 49790d9d Sofia Papagiannaki
    return None
281 49790d9d Sofia Papagiannaki
282 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
283 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
284 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
285 49790d9d Sofia Papagiannaki
        """
286 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
287 49790d9d Sofia Papagiannaki
        ``User`` if valid.
288 49790d9d Sofia Papagiannaki

289 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
290 49790d9d Sofia Papagiannaki
        after activating.
291 49790d9d Sofia Papagiannaki

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

294 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
295 49790d9d Sofia Papagiannaki
        return ``None``.
296 49790d9d Sofia Papagiannaki

297 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
298 49790d9d Sofia Papagiannaki

299 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
300 49790d9d Sofia Papagiannaki
        """
301 49790d9d Sofia Papagiannaki
        try:
302 49790d9d Sofia Papagiannaki
            email_change = self.model.objects.get(activation_key=activation_key)
303 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
304 49790d9d Sofia Papagiannaki
                email_change.delete()
305 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
306 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
307 49790d9d Sofia Papagiannaki
            try:
308 49790d9d Sofia Papagiannaki
                AstakosUser.objects.get(email=email_change.new_email_address)
309 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
310 49790d9d Sofia Papagiannaki
                pass
311 49790d9d Sofia Papagiannaki
            else:
312 49790d9d Sofia Papagiannaki
                raise ValueError(_('The new email address is reserved.'))
313 49790d9d Sofia Papagiannaki
            # update user
314 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
315 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
316 49790d9d Sofia Papagiannaki
            user.save()
317 49790d9d Sofia Papagiannaki
            email_change.delete()
318 49790d9d Sofia Papagiannaki
            return user
319 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
320 49790d9d Sofia Papagiannaki
            raise ValueError(_('Invalid activation key'))
321 49790d9d Sofia Papagiannaki
322 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
323 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.'))
324 49790d9d Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, unique=True, related_name='emailchange_user')
325 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
326 49790d9d Sofia Papagiannaki
    activation_key = models.CharField(max_length=40, unique=True, db_index=True)
327 49790d9d Sofia Papagiannaki
328 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
329 49790d9d Sofia Papagiannaki
330 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
331 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
332 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
333 ff9290ec Sofia Papagiannaki
334 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
335 ff9290ec Sofia Papagiannaki
    try:
336 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
337 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
338 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
339 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
340 ff9290ec Sofia Papagiannaki
        extended_user.renew_token()
341 ff9290ec Sofia Papagiannaki
        extended_user.save()
342 ff9290ec Sofia Papagiannaki
    except:
343 ff9290ec Sofia Papagiannaki
        pass
344 ff9290ec Sofia Papagiannaki
345 ff9290ec Sofia Papagiannaki
def superuser_post_syncdb(sender, **kwargs):
346 ff9290ec Sofia Papagiannaki
    # if there was created a superuser
347 ff9290ec Sofia Papagiannaki
    # associate it with an AstakosUser
348 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
349 ff9290ec Sofia Papagiannaki
    for u in admins:
350 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
351 ff9290ec Sofia Papagiannaki
352 ff9290ec Sofia Papagiannaki
post_syncdb.connect(superuser_post_syncdb)
353 ff9290ec Sofia Papagiannaki
354 ff9290ec Sofia Papagiannaki
def superuser_post_save(sender, instance, **kwargs):
355 ff9290ec Sofia Papagiannaki
    if instance.is_superuser:
356 ff9290ec Sofia Papagiannaki
        create_astakos_user(instance)
357 ff9290ec Sofia Papagiannaki
358 ff9290ec Sofia Papagiannaki
post_save.connect(superuser_post_save, sender=User)