Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 9e19989d

History | View | Annotate | Download (7.4 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
# 
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
# 
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
# 
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
# 
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
# 
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import hashlib
35
import uuid
36

    
37
from time import asctime
38
from datetime import datetime, timedelta
39
from base64 import b64encode
40
from urlparse import urlparse
41

    
42
from django.db import models
43
from django.contrib.auth.models import User, UserManager
44

    
45
from astakos.im.settings import DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL, AUTH_TOKEN_DURATION, BILLING_FIELDS, QUEUE_CONNECTION
46
from astakos.im.queue.userevent import UserEvent
47
from synnefo.lib.queue import exchange_connect, exchange_send, exchange_close, Receipt
48

    
49
QUEUE_CLIENT_ID = 3 # Astakos.
50

    
51
class AstakosUser(User):
52
    """
53
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
54
    """
55
    # Use UserManager to get the create_user method, etc.
56
    objects = UserManager()
57
    
58
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
59
    provider = models.CharField('Provider', max_length=255, blank=True)
60
    
61
    #for invitations
62
    user_level = DEFAULT_USER_LEVEL
63
    level = models.IntegerField('Inviter level', default=user_level)
64
    invitations = models.IntegerField('Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
65
    
66
    auth_token = models.CharField('Authentication Token', max_length=32,
67
                                  null=True, blank=True)
68
    auth_token_created = models.DateTimeField('Token creation date', null=True)
69
    auth_token_expires = models.DateTimeField('Token expiration date', null=True)
70
    
71
    updated = models.DateTimeField('Update date')
72
    is_verified = models.BooleanField('Is verified?', default=False)
73
    
74
    # ex. screen_name for twitter, eppn for shibboleth
75
    third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
76
    
77
    email_verified = models.BooleanField('Email verified?', default=False)
78
    
79
    has_credits = models.BooleanField('Has credits?', default=False)
80
    has_signed_terms = models.BooleanField('Agree with the terms?', default=False)
81
    date_signed_terms = models.DateTimeField('Signed terms date', null=True)
82
    
83
    @property
84
    def realname(self):
85
        return '%s %s' %(self.first_name, self.last_name)
86
    
87
    @realname.setter
88
    def realname(self, value):
89
        parts = value.split(' ')
90
        if len(parts) == 2:
91
            self.first_name = parts[0]
92
            self.last_name = parts[1]
93
        else:
94
            self.last_name = parts[0]
95
    
96
    @property
97
    def invitation(self):
98
        try:
99
            return Invitation.objects.get(username=self.email)
100
        except Invitation.DoesNotExist:
101
            return None
102
    
103
    def save(self, update_timestamps=True, **kwargs):
104
        if update_timestamps:
105
            if not self.id:
106
                self.date_joined = datetime.now()
107
            self.updated = datetime.now()
108
        if not self.id:
109
            # set username
110
            while not self.username:
111
                username =  uuid.uuid4().hex[:30]
112
                try:
113
                    AstakosUser.objects.get(username = username)
114
                except AstakosUser.DoesNotExist, e:
115
                    self.username = username
116
            self.is_active = False
117
            if not self.provider:
118
                self.provider = 'local'
119
        report_user_event(self)
120
        super(AstakosUser, self).save(**kwargs)
121
    
122
    def renew_token(self):
123
        md5 = hashlib.md5()
124
        md5.update(self.username)
125
        md5.update(self.realname.encode('ascii', 'ignore'))
126
        md5.update(asctime())
127
        
128
        self.auth_token = b64encode(md5.digest())
129
        self.auth_token_created = datetime.now()
130
        self.auth_token_expires = self.auth_token_created + \
131
                                  timedelta(hours=AUTH_TOKEN_DURATION)
132
    
133
    def __unicode__(self):
134
        return self.username
135

    
136
class ApprovalTerms(models.Model):
137
    """
138
    Model for approval terms
139
    """
140
    
141
    date = models.DateTimeField('Issue date', db_index=True, default=datetime.now())
142
    location = models.CharField('Terms location', max_length=255)
143

    
144
class Invitation(models.Model):
145
    """
146
    Model for registring invitations
147
    """
148
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
149
                                null=True)
150
    realname = models.CharField('Real name', max_length=255)
151
    username = models.CharField('Unique ID', max_length=255, unique=True)
152
    code = models.BigIntegerField('Invitation code', db_index=True)
153
    #obsolete: we keep it just for transfering the data
154
    is_accepted = models.BooleanField('Accepted?', default=False)
155
    is_consumed = models.BooleanField('Consumed?', default=False)
156
    created = models.DateTimeField('Creation date', auto_now_add=True)
157
    #obsolete: we keep it just for transfering the data
158
    accepted = models.DateTimeField('Acceptance date', null=True, blank=True)
159
    consumed = models.DateTimeField('Consumption date', null=True, blank=True)
160
    
161
    def consume(self):
162
        self.is_consumed = True
163
        self.consumed = datetime.now()
164
        self.save()
165
        
166
    def __unicode__(self):
167
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
168

    
169
def report_user_event(user):
170
    def should_send(user):
171
        # report event incase of new user instance
172
        # or if specific fields are modified
173
        if not user.id:
174
            return True
175
        db_instance = AstakosUser.objects.get(id = user.id)
176
        for f in BILLING_FIELDS:
177
            if (db_instance.__getattribute__(f) != user.__getattribute__(f)):
178
                return True
179
        return False
180
    
181
    if QUEUE_CONNECTION and should_send(user):
182
        eventType = 'create' if not user.id else 'modify'
183
        body = UserEvent(QUEUE_CLIENT_ID, user, eventType, {}).format()
184
        conn = exchange_connect(QUEUE_CONNECTION)
185
        parts = urlparse(QUEUE_CONNECTION)
186
        exchange = parts.path[1:]
187
        routing_key = '%s.user' % exchange
188
        exchange_send(conn, routing_key, body)
189
        exchange_close(conn)