Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 1eec103a

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

299 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
300 49790d9d Sofia Papagiannaki
        after activating.
301 49790d9d Sofia Papagiannaki

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

304 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
305 49790d9d Sofia Papagiannaki
        return ``None``.
306 49790d9d Sofia Papagiannaki

307 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
308 49790d9d Sofia Papagiannaki

309 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
310 49790d9d Sofia Papagiannaki
        """
311 49790d9d Sofia Papagiannaki
        try:
312 49790d9d Sofia Papagiannaki
            email_change = self.model.objects.get(activation_key=activation_key)
313 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
314 49790d9d Sofia Papagiannaki
                email_change.delete()
315 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
316 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
317 49790d9d Sofia Papagiannaki
            try:
318 49790d9d Sofia Papagiannaki
                AstakosUser.objects.get(email=email_change.new_email_address)
319 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
320 49790d9d Sofia Papagiannaki
                pass
321 49790d9d Sofia Papagiannaki
            else:
322 49790d9d Sofia Papagiannaki
                raise ValueError(_('The new email address is reserved.'))
323 49790d9d Sofia Papagiannaki
            # update user
324 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
325 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
326 49790d9d Sofia Papagiannaki
            user.save()
327 49790d9d Sofia Papagiannaki
            email_change.delete()
328 49790d9d Sofia Papagiannaki
            return user
329 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
330 49790d9d Sofia Papagiannaki
            raise ValueError(_('Invalid activation key'))
331 49790d9d Sofia Papagiannaki
332 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
333 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.'))
334 49790d9d Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, unique=True, related_name='emailchange_user')
335 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
336 49790d9d Sofia Papagiannaki
    activation_key = models.CharField(max_length=40, unique=True, db_index=True)
337 49790d9d Sofia Papagiannaki
338 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
339 49790d9d Sofia Papagiannaki
340 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
341 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
342 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
343 ff9290ec Sofia Papagiannaki
344 6b03a847 Sofia Papagiannaki
class Service(models.Model):
345 6b03a847 Sofia Papagiannaki
    name = models.CharField('Name', max_length=255, unique=True)
346 6b03a847 Sofia Papagiannaki
    url = models.FilePathField()
347 6b03a847 Sofia Papagiannaki
    icon = models.FilePathField(blank=True)
348 6b03a847 Sofia Papagiannaki
    auth_token = models.CharField('Authentication Token', max_length=32,
349 6b03a847 Sofia Papagiannaki
                                  null=True, blank=True)
350 6b03a847 Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
351 6b03a847 Sofia Papagiannaki
    auth_token_expires = models.DateTimeField('Token expiration date', null=True)
352 6b03a847 Sofia Papagiannaki
    
353 6b03a847 Sofia Papagiannaki
    def save(self, **kwargs):
354 6b03a847 Sofia Papagiannaki
        if not self.id:
355 6b03a847 Sofia Papagiannaki
            self.renew_token()
356 6b03a847 Sofia Papagiannaki
        self.full_clean()
357 6b03a847 Sofia Papagiannaki
        super(Service, self).save(**kwargs)
358 6b03a847 Sofia Papagiannaki
    
359 6b03a847 Sofia Papagiannaki
    def renew_token(self):
360 6b03a847 Sofia Papagiannaki
        md5 = hashlib.md5()
361 6b03a847 Sofia Papagiannaki
        md5.update(self.name.encode('ascii', 'ignore'))
362 6b03a847 Sofia Papagiannaki
        md5.update(self.url.encode('ascii', 'ignore'))
363 6b03a847 Sofia Papagiannaki
        md5.update(asctime())
364 6b03a847 Sofia Papagiannaki
365 6b03a847 Sofia Papagiannaki
        self.auth_token = b64encode(md5.digest())
366 6b03a847 Sofia Papagiannaki
        self.auth_token_created = datetime.now()
367 6b03a847 Sofia Papagiannaki
        self.auth_token_expires = self.auth_token_created + \
368 6b03a847 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
369 6b03a847 Sofia Papagiannaki
370 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
371 ca828a10 Sofia Papagiannaki
    """
372 ca828a10 Sofia Papagiannaki
    Model for registring invitations
373 ca828a10 Sofia Papagiannaki
    """
374 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
375 1eec103a Sofia Papagiannaki
    email = models.EmailField()
376 ca828a10 Sofia Papagiannaki
377 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
378 ff9290ec Sofia Papagiannaki
    try:
379 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
380 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
381 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
382 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
383 ff9290ec Sofia Papagiannaki
        extended_user.renew_token()
384 ff9290ec Sofia Papagiannaki
        extended_user.save()
385 ff9290ec Sofia Papagiannaki
    except:
386 ff9290ec Sofia Papagiannaki
        pass
387 ff9290ec Sofia Papagiannaki
388 ff9290ec Sofia Papagiannaki
def superuser_post_syncdb(sender, **kwargs):
389 ff9290ec Sofia Papagiannaki
    # if there was created a superuser
390 ff9290ec Sofia Papagiannaki
    # associate it with an AstakosUser
391 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
392 ff9290ec Sofia Papagiannaki
    for u in admins:
393 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
394 ff9290ec Sofia Papagiannaki
395 ff9290ec Sofia Papagiannaki
post_syncdb.connect(superuser_post_syncdb)
396 ff9290ec Sofia Papagiannaki
397 ff9290ec Sofia Papagiannaki
def superuser_post_save(sender, instance, **kwargs):
398 ff9290ec Sofia Papagiannaki
    if instance.is_superuser:
399 ff9290ec Sofia Papagiannaki
        create_astakos_user(instance)
400 ff9290ec Sofia Papagiannaki
401 ff9290ec Sofia Papagiannaki
post_save.connect(superuser_post_save, sender=User)