Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 660c7a4f

History | View | Annotate | Download (6.8 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

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

    
44
from astakos.im.settings import DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL, AUTH_TOKEN_DURATION, BILLING_FIELDS, QUEUE_EXCHANGE
45
from synnefo.lib.queue import exchange_connect, exchange_send, exchange_close, Receipt
46

    
47
QUEUE_CLIENT_ID = 3 # Astakos.
48

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

    
128
class Invitation(models.Model):
129
    """
130
    Model for registring invitations
131
    """
132
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
133
                                null=True)
134
    realname = models.CharField('Real name', max_length=255)
135
    username = models.CharField('Unique ID', max_length=255)
136
    code = models.BigIntegerField('Invitation code', db_index=True)
137
    #obsolete: we keep it just for transfering the data
138
    is_accepted = models.BooleanField('Accepted?', default=False)
139
    is_consumed = models.BooleanField('Consumed?', default=False)
140
    created = models.DateTimeField('Creation date', auto_now_add=True)
141
    #obsolete: we keep it just for transfering the data
142
    accepted = models.DateTimeField('Acceptance date', null=True, blank=True)
143
    consumed = models.DateTimeField('Consumption date', null=True, blank=True)
144
    
145
    def consume(self):
146
        self.is_consumed = True
147
        self.consumed = datetime.now()
148
        self.save()
149
        
150
    def __unicode__(self):
151
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
152

    
153
def report_user_event(user):
154
    def should_send(user):
155
        # report event incase of new user instance
156
        # or if specific fields are modified
157
        if not user.id:
158
            return True
159
        db_instance = AstakosUser.objects.get(id = user.id)
160
        for f in BILLING_FIELDS:
161
            if (db_instance.__getattribute__(f) != user.__getattribute__(f)):
162
                return True
163
        return False
164
    
165
    if QUEUE_EXCHANGE and should_send(user):
166
        l = [[elem, str(user.__getattribute__(elem))] for elem in BILLING_FIELDS]
167
        details = dict(l)
168
        details['eventType'] = 'create' if not user.id else 'modify'
169
        body = Receipt(QUEUE_CLIENT_ID, user.email, '', 0, details).format()
170
        conn = exchange_connect(QUEUE_EXCHANGE)
171
        routing_key = QUEUE_EXCHANGE.replace('#', body['id'])
172
        exchange_send(conn, routing_key, body)
173
        exchange_close(conn)