Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 63fa03fe

History | View | Annotate | Download (8.7 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 64cd4730 Antony Chazapis
38 64cd4730 Antony Chazapis
from time import asctime
39 64cd4730 Antony Chazapis
from datetime import datetime, timedelta
40 64cd4730 Antony Chazapis
from base64 import b64encode
41 4c0174a9 Sofia Papagiannaki
from urlparse import urlparse
42 8f5a3a06 Sofia Papagiannaki
from random import randint
43 64cd4730 Antony Chazapis
44 64cd4730 Antony Chazapis
from django.db import models
45 18ffbee1 Sofia Papagiannaki
from django.contrib.auth.models import User, UserManager, Group
46 64cd4730 Antony Chazapis
47 3a9f4931 Sofia Papagiannaki
from astakos.im.settings import DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL, AUTH_TOKEN_DURATION, BILLING_FIELDS, QUEUE_CONNECTION
48 9c01d9d1 Sofia Papagiannaki
49 9c01d9d1 Sofia Papagiannaki
QUEUE_CLIENT_ID = 3 # Astakos.
50 64cd4730 Antony Chazapis
51 18ffbee1 Sofia Papagiannaki
logger = logging.getLogger(__name__)
52 18ffbee1 Sofia Papagiannaki
53 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
54 890b0eaf Sofia Papagiannaki
    """
55 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
56 890b0eaf Sofia Papagiannaki
    """
57 0905ccd2 Sofia Papagiannaki
    # Use UserManager to get the create_user method, etc.
58 0905ccd2 Sofia Papagiannaki
    objects = UserManager()
59 6c736ed7 Kostas Papadimitriou
60 5ed6816e Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
61 5ed6816e Sofia Papagiannaki
    provider = models.CharField('Provider', max_length=255, blank=True)
62 6c736ed7 Kostas Papadimitriou
63 64cd4730 Antony Chazapis
    #for invitations
64 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
65 a196eb7e Sofia Papagiannaki
    level = models.IntegerField('Inviter level', default=user_level)
66 ebd369d0 Sofia Papagiannaki
    invitations = models.IntegerField('Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
67 6c736ed7 Kostas Papadimitriou
68 64cd4730 Antony Chazapis
    auth_token = models.CharField('Authentication Token', max_length=32,
69 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
70 0905ccd2 Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
71 0905ccd2 Sofia Papagiannaki
    auth_token_expires = models.DateTimeField('Token expiration date', null=True)
72 6c736ed7 Kostas Papadimitriou
73 64cd4730 Antony Chazapis
    updated = models.DateTimeField('Update date')
74 890b0eaf Sofia Papagiannaki
    is_verified = models.BooleanField('Is verified?', default=False)
75 6c736ed7 Kostas Papadimitriou
76 15efc749 Sofia Papagiannaki
    # ex. screen_name for twitter, eppn for shibboleth
77 15efc749 Sofia Papagiannaki
    third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
78 6c736ed7 Kostas Papadimitriou
79 ebd369d0 Sofia Papagiannaki
    email_verified = models.BooleanField('Email verified?', default=False)
80 6c736ed7 Kostas Papadimitriou
81 59f598f1 Sofia Papagiannaki
    has_credits = models.BooleanField('Has credits?', default=False)
82 270dd48d Sofia Papagiannaki
    has_signed_terms = models.BooleanField('Agree with the terms?', default=False)
83 270dd48d Sofia Papagiannaki
    date_signed_terms = models.DateTimeField('Signed terms date', null=True)
84 59f598f1 Sofia Papagiannaki
    
85 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
86 18ffbee1 Sofia Papagiannaki
    __groupnames = []
87 18ffbee1 Sofia Papagiannaki
    
88 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
89 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
90 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
91 18ffbee1 Sofia Papagiannaki
        if self.id:
92 18ffbee1 Sofia Papagiannaki
            self.__groupnames = [g.name for g in self.groups.all()]
93 18ffbee1 Sofia Papagiannaki
        else:
94 18ffbee1 Sofia Papagiannaki
            self.is_active = False
95 18ffbee1 Sofia Papagiannaki
    
96 0905ccd2 Sofia Papagiannaki
    @property
97 0905ccd2 Sofia Papagiannaki
    def realname(self):
98 0905ccd2 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
99 6c736ed7 Kostas Papadimitriou
100 0905ccd2 Sofia Papagiannaki
    @realname.setter
101 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
102 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
103 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
104 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
105 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
106 0905ccd2 Sofia Papagiannaki
        else:
107 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
108 6c736ed7 Kostas Papadimitriou
109 64cd4730 Antony Chazapis
    @property
110 64cd4730 Antony Chazapis
    def invitation(self):
111 64cd4730 Antony Chazapis
        try:
112 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
113 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
114 64cd4730 Antony Chazapis
            return None
115 6c736ed7 Kostas Papadimitriou
116 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
117 64cd4730 Antony Chazapis
        if update_timestamps:
118 64cd4730 Antony Chazapis
            if not self.id:
119 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
120 64cd4730 Antony Chazapis
            self.updated = datetime.now()
121 18ffbee1 Sofia Papagiannaki
        
122 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
123 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
124 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
125 18ffbee1 Sofia Papagiannaki
        
126 9c01d9d1 Sofia Papagiannaki
        if not self.id:
127 9c01d9d1 Sofia Papagiannaki
            # set username
128 9c01d9d1 Sofia Papagiannaki
            while not self.username:
129 9c01d9d1 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
130 9c01d9d1 Sofia Papagiannaki
                try:
131 9c01d9d1 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
132 9c01d9d1 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
133 9c01d9d1 Sofia Papagiannaki
                    self.username = username
134 9c01d9d1 Sofia Papagiannaki
            if not self.provider:
135 9c01d9d1 Sofia Papagiannaki
                self.provider = 'local'
136 9c01d9d1 Sofia Papagiannaki
        report_user_event(self)
137 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
138 18ffbee1 Sofia Papagiannaki
        
139 18ffbee1 Sofia Papagiannaki
        # set group if does not exist
140 18ffbee1 Sofia Papagiannaki
        groupname = 'shibboleth' if self.provider == 'shibboleth' else 'default'
141 18ffbee1 Sofia Papagiannaki
        if groupname not in self.__groupnames:
142 18ffbee1 Sofia Papagiannaki
            try:
143 18ffbee1 Sofia Papagiannaki
                group = Group.objects.get(name = groupname)
144 18ffbee1 Sofia Papagiannaki
                self.groups.add(group)
145 18ffbee1 Sofia Papagiannaki
            except Group.DoesNotExist, e:
146 18ffbee1 Sofia Papagiannaki
                logger.exception(e)
147 64cd4730 Antony Chazapis
    
148 64cd4730 Antony Chazapis
    def renew_token(self):
149 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
150 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
151 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
152 64cd4730 Antony Chazapis
        md5.update(asctime())
153 6c736ed7 Kostas Papadimitriou
154 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
155 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
156 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
157 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
158 6c736ed7 Kostas Papadimitriou
159 64cd4730 Antony Chazapis
    def __unicode__(self):
160 0905ccd2 Sofia Papagiannaki
        return self.username
161 64cd4730 Antony Chazapis
162 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
163 270dd48d Sofia Papagiannaki
    """
164 270dd48d Sofia Papagiannaki
    Model for approval terms
165 270dd48d Sofia Papagiannaki
    """
166 6c736ed7 Kostas Papadimitriou
167 270dd48d Sofia Papagiannaki
    date = models.DateTimeField('Issue date', db_index=True, default=datetime.now())
168 270dd48d Sofia Papagiannaki
    location = models.CharField('Terms location', max_length=255)
169 270dd48d Sofia Papagiannaki
170 64cd4730 Antony Chazapis
class Invitation(models.Model):
171 890b0eaf Sofia Papagiannaki
    """
172 890b0eaf Sofia Papagiannaki
    Model for registring invitations
173 890b0eaf Sofia Papagiannaki
    """
174 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
175 64cd4730 Antony Chazapis
                                null=True)
176 64cd4730 Antony Chazapis
    realname = models.CharField('Real name', max_length=255)
177 ebd369d0 Sofia Papagiannaki
    username = models.CharField('Unique ID', max_length=255, unique=True)
178 64cd4730 Antony Chazapis
    code = models.BigIntegerField('Invitation code', db_index=True)
179 64cd4730 Antony Chazapis
    #obsolete: we keep it just for transfering the data
180 64cd4730 Antony Chazapis
    is_accepted = models.BooleanField('Accepted?', default=False)
181 64cd4730 Antony Chazapis
    is_consumed = models.BooleanField('Consumed?', default=False)
182 64cd4730 Antony Chazapis
    created = models.DateTimeField('Creation date', auto_now_add=True)
183 64cd4730 Antony Chazapis
    #obsolete: we keep it just for transfering the data
184 64cd4730 Antony Chazapis
    accepted = models.DateTimeField('Acceptance date', null=True, blank=True)
185 64cd4730 Antony Chazapis
    consumed = models.DateTimeField('Consumption date', null=True, blank=True)
186 64cd4730 Antony Chazapis
    
187 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
188 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
189 8f5a3a06 Sofia Papagiannaki
        if not self.id:
190 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
191 8f5a3a06 Sofia Papagiannaki
    
192 64cd4730 Antony Chazapis
    def consume(self):
193 64cd4730 Antony Chazapis
        self.is_consumed = True
194 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
195 64cd4730 Antony Chazapis
        self.save()
196 6c736ed7 Kostas Papadimitriou
197 64cd4730 Antony Chazapis
    def __unicode__(self):
198 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
199 9c01d9d1 Sofia Papagiannaki
200 9c01d9d1 Sofia Papagiannaki
def report_user_event(user):
201 9c01d9d1 Sofia Papagiannaki
    def should_send(user):
202 9c01d9d1 Sofia Papagiannaki
        # report event incase of new user instance
203 9c01d9d1 Sofia Papagiannaki
        # or if specific fields are modified
204 9c01d9d1 Sofia Papagiannaki
        if not user.id:
205 9c01d9d1 Sofia Papagiannaki
            return True
206 9c01d9d1 Sofia Papagiannaki
        db_instance = AstakosUser.objects.get(id = user.id)
207 9c01d9d1 Sofia Papagiannaki
        for f in BILLING_FIELDS:
208 9c01d9d1 Sofia Papagiannaki
            if (db_instance.__getattribute__(f) != user.__getattribute__(f)):
209 9c01d9d1 Sofia Papagiannaki
                return True
210 9c01d9d1 Sofia Papagiannaki
        return False
211 6c736ed7 Kostas Papadimitriou
212 3a9f4931 Sofia Papagiannaki
    if QUEUE_CONNECTION and should_send(user):
213 6c736ed7 Kostas Papadimitriou
214 6c736ed7 Kostas Papadimitriou
        from astakos.im.queue.userevent import UserEvent
215 6c736ed7 Kostas Papadimitriou
        from synnefo.lib.queue import exchange_connect, exchange_send, \
216 6c736ed7 Kostas Papadimitriou
                exchange_close
217 6c736ed7 Kostas Papadimitriou
218 59f598f1 Sofia Papagiannaki
        eventType = 'create' if not user.id else 'modify'
219 59f598f1 Sofia Papagiannaki
        body = UserEvent(QUEUE_CLIENT_ID, user, eventType, {}).format()
220 3a9f4931 Sofia Papagiannaki
        conn = exchange_connect(QUEUE_CONNECTION)
221 9e19989d Sofia Papagiannaki
        parts = urlparse(QUEUE_CONNECTION)
222 270dd48d Sofia Papagiannaki
        exchange = parts.path[1:]
223 270dd48d Sofia Papagiannaki
        routing_key = '%s.user' % exchange
224 9c01d9d1 Sofia Papagiannaki
        exchange_send(conn, routing_key, body)
225 68cb6899 Sofia Papagiannaki
        exchange_close(conn)
226 8f5a3a06 Sofia Papagiannaki
227 8f5a3a06 Sofia Papagiannaki
def _generate_invitation_code():
228 8f5a3a06 Sofia Papagiannaki
    while True:
229 8f5a3a06 Sofia Papagiannaki
        code = randint(1, 2L**63 - 1)
230 8f5a3a06 Sofia Papagiannaki
        try:
231 8f5a3a06 Sofia Papagiannaki
            Invitation.objects.get(code=code)
232 8f5a3a06 Sofia Papagiannaki
            # An invitation with this code already exists, try again
233 8f5a3a06 Sofia Papagiannaki
        except Invitation.DoesNotExist:
234 8f5a3a06 Sofia Papagiannaki
            return code