Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 394c9834

History | View | Annotate | Download (38 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 ef20ea07 Sofia Papagiannaki
from urlparse import urlparse
42 d2633501 Kostas Papadimitriou
from urllib import quote
43 8f5a3a06 Sofia Papagiannaki
from random import randint
44 ffb1e7a8 Sofia Papagiannaki
from collections import defaultdict
45 64cd4730 Antony Chazapis
46 49790d9d Sofia Papagiannaki
from django.db import models, IntegrityError
47 9a06d96f Olga Brani
from django.contrib.auth.models import User, UserManager, Group, Permission
48 0a569195 Sofia Papagiannaki
from django.utils.translation import ugettext as _
49 49790d9d Sofia Papagiannaki
from django.db import transaction
50 0a569195 Sofia Papagiannaki
from django.core.exceptions import ValidationError
51 c0b26605 Sofia Papagiannaki
from django.db.models.signals import (
52 c0b26605 Sofia Papagiannaki
    pre_save, post_save, post_syncdb, post_delete
53 c0b26605 Sofia Papagiannaki
)
54 9a06d96f Olga Brani
from django.contrib.contenttypes.models import ContentType
55 9a06d96f Olga Brani
56 fc1e2f02 Sofia Papagiannaki
from django.dispatch import Signal
57 e6759494 Sofia Papagiannaki
from django.db.models import Q
58 d2633501 Kostas Papadimitriou
from django.core.urlresolvers import reverse
59 d2633501 Kostas Papadimitriou
from django.utils.http import int_to_base36
60 d2633501 Kostas Papadimitriou
from django.contrib.auth.tokens import default_token_generator
61 8f8c43b2 Sofia Papagiannaki
from django.conf import settings
62 bf0c6de5 Sofia Papagiannaki
from django.utils.importlib import import_module
63 d2633501 Kostas Papadimitriou
from django.core.validators import email_re
64 64cd4730 Antony Chazapis
65 fc1e2f02 Sofia Papagiannaki
from astakos.im.settings import (DEFAULT_USER_LEVEL, INVITATIONS_PER_LEVEL,
66 bd4f356c Sofia Papagiannaki
                                 AUTH_TOKEN_DURATION, BILLING_FIELDS,
67 bd4f356c Sofia Papagiannaki
                                 EMAILCHANGE_ACTIVATION_DAYS, LOGGING_LEVEL)
68 c0b26605 Sofia Papagiannaki
from astakos.im.endpoints.qh import (
69 c0b26605 Sofia Papagiannaki
    register_users, send_quota, register_resources
70 bf0c6de5 Sofia Papagiannaki
)
71 d2633501 Kostas Papadimitriou
from astakos.im import auth_providers
72 fc1e2f02 Sofia Papagiannaki
from astakos.im.endpoints.aquarium.producer import report_user_event
73 9a06d96f Olga Brani
from astakos.im.functions import send_invitation
74 f8f86e83 root
#from astakos.im.tasks import propagate_groupmembers_quota
75 9c01d9d1 Sofia Papagiannaki
76 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
77 64cd4730 Antony Chazapis
78 18ffbee1 Sofia Papagiannaki
logger = logging.getLogger(__name__)
79 18ffbee1 Sofia Papagiannaki
80 9a06d96f Olga Brani
DEFAULT_CONTENT_TYPE = None
81 9a06d96f Olga Brani
try:
82 9a06d96f Olga Brani
    content_type = ContentType.objects.get(app_label='im', model='astakosuser')
83 9a06d96f Olga Brani
except:
84 9a06d96f Olga Brani
    content_type = DEFAULT_CONTENT_TYPE
85 9a06d96f Olga Brani
86 9a06d96f Olga Brani
RESOURCE_SEPARATOR = '.'
87 9a06d96f Olga Brani
88 9ee0c6a2 Sofia Papagiannaki
inf = float('inf')
89 5ce3ce4f Sofia Papagiannaki
90 8e45d6fd Sofia Papagiannaki
class Service(models.Model):
91 8e45d6fd Sofia Papagiannaki
    name = models.CharField('Name', max_length=255, unique=True, db_index=True)
92 8e45d6fd Sofia Papagiannaki
    url = models.FilePathField()
93 8e45d6fd Sofia Papagiannaki
    icon = models.FilePathField(blank=True)
94 8e45d6fd Sofia Papagiannaki
    auth_token = models.CharField('Authentication Token', max_length=32,
95 8e45d6fd Sofia Papagiannaki
                                  null=True, blank=True)
96 8e45d6fd Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
97 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
98 5ce3ce4f Sofia Papagiannaki
        'Token expiration date', null=True)
99 5ce3ce4f Sofia Papagiannaki
100 8e45d6fd Sofia Papagiannaki
    def renew_token(self):
101 8e45d6fd Sofia Papagiannaki
        md5 = hashlib.md5()
102 8e45d6fd Sofia Papagiannaki
        md5.update(self.name.encode('ascii', 'ignore'))
103 8e45d6fd Sofia Papagiannaki
        md5.update(self.url.encode('ascii', 'ignore'))
104 8e45d6fd Sofia Papagiannaki
        md5.update(asctime())
105 8e45d6fd Sofia Papagiannaki
106 8e45d6fd Sofia Papagiannaki
        self.auth_token = b64encode(md5.digest())
107 8e45d6fd Sofia Papagiannaki
        self.auth_token_created = datetime.now()
108 8e45d6fd Sofia Papagiannaki
        self.auth_token_expires = self.auth_token_created + \
109 5ce3ce4f Sofia Papagiannaki
            timedelta(hours=AUTH_TOKEN_DURATION)
110 5ce3ce4f Sofia Papagiannaki
111 8e45d6fd Sofia Papagiannaki
    def __str__(self):
112 8e45d6fd Sofia Papagiannaki
        return self.name
113 8e45d6fd Sofia Papagiannaki
114 9a06d96f Olga Brani
    @property
115 9a06d96f Olga Brani
    def resources(self):
116 9a06d96f Olga Brani
        return self.resource_set.all()
117 9a06d96f Olga Brani
118 9a06d96f Olga Brani
    @resources.setter
119 9a06d96f Olga Brani
    def resources(self, resources):
120 9a06d96f Olga Brani
        for s in resources:
121 9a06d96f Olga Brani
            self.resource_set.create(**s)
122 2e90e3ec Kostas Papadimitriou
123 9a06d96f Olga Brani
    def add_resource(self, service, resource, uplimit, update=True):
124 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
125 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
126 9a06d96f Olga Brani
        if update:
127 9a06d96f Olga Brani
            AstakosUserQuota.objects.update_or_create(user=self,
128 9a06d96f Olga Brani
                                                      resource=resource,
129 9a06d96f Olga Brani
                                                      defaults={'uplimit': uplimit})
130 9a06d96f Olga Brani
        else:
131 9a06d96f Olga Brani
            q = self.astakosuserquota_set
132 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
133 9a06d96f Olga Brani
134 5ce3ce4f Sofia Papagiannaki
135 8e45d6fd Sofia Papagiannaki
class ResourceMetadata(models.Model):
136 8e45d6fd Sofia Papagiannaki
    key = models.CharField('Name', max_length=255, unique=True, db_index=True)
137 8e45d6fd Sofia Papagiannaki
    value = models.CharField('Value', max_length=255)
138 8e45d6fd Sofia Papagiannaki
139 5ce3ce4f Sofia Papagiannaki
140 8e45d6fd Sofia Papagiannaki
class Resource(models.Model):
141 8e45d6fd Sofia Papagiannaki
    name = models.CharField('Name', max_length=255, unique=True, db_index=True)
142 8e45d6fd Sofia Papagiannaki
    meta = models.ManyToManyField(ResourceMetadata)
143 8e45d6fd Sofia Papagiannaki
    service = models.ForeignKey(Service)
144 9a06d96f Olga Brani
    desc = models.TextField('Description', null=True)
145 9a06d96f Olga Brani
    unit = models.CharField('Name', null=True, max_length=255)
146 9a06d96f Olga Brani
    group = models.CharField('Group', null=True, max_length=255)
147 5ce3ce4f Sofia Papagiannaki
148 8e45d6fd Sofia Papagiannaki
    def __str__(self):
149 9a06d96f Olga Brani
        return '%s%s%s' % (self.service, RESOURCE_SEPARATOR, self.name)
150 8e45d6fd Sofia Papagiannaki
151 5ce3ce4f Sofia Papagiannaki
152 8e45d6fd Sofia Papagiannaki
class GroupKind(models.Model):
153 8e45d6fd Sofia Papagiannaki
    name = models.CharField('Name', max_length=255, unique=True, db_index=True)
154 5ce3ce4f Sofia Papagiannaki
155 8e45d6fd Sofia Papagiannaki
    def __str__(self):
156 8e45d6fd Sofia Papagiannaki
        return self.name
157 8e45d6fd Sofia Papagiannaki
158 5ce3ce4f Sofia Papagiannaki
159 8e45d6fd Sofia Papagiannaki
class AstakosGroup(Group):
160 8e45d6fd Sofia Papagiannaki
    kind = models.ForeignKey(GroupKind)
161 2b1a5f5d Olga Brani
    homepage = models.URLField(
162 2b1a5f5d Olga Brani
        'Homepage Url', max_length=255, null=True, blank=True)
163 8e45d6fd Sofia Papagiannaki
    desc = models.TextField('Description', null=True)
164 9a06d96f Olga Brani
    policy = models.ManyToManyField(
165 9a06d96f Olga Brani
        Resource,
166 9a06d96f Olga Brani
        null=True,
167 9a06d96f Olga Brani
        blank=True,
168 9a06d96f Olga Brani
        through='AstakosGroupQuota'
169 9a06d96f Olga Brani
    )
170 9a06d96f Olga Brani
    creation_date = models.DateTimeField(
171 9a06d96f Olga Brani
        'Creation date',
172 9a06d96f Olga Brani
        default=datetime.now()
173 9a06d96f Olga Brani
    )
174 8e45d6fd Sofia Papagiannaki
    issue_date = models.DateTimeField('Issue date', null=True)
175 9a06d96f Olga Brani
    expiration_date = models.DateTimeField(
176 9a06d96f Olga Brani
        'Expiration date',
177 9a06d96f Olga Brani
         null=True
178 9a06d96f Olga Brani
    )
179 9a06d96f Olga Brani
    moderation_enabled = models.BooleanField(
180 9a06d96f Olga Brani
        'Moderated membership?',
181 9a06d96f Olga Brani
        default=True
182 9a06d96f Olga Brani
    )
183 9a06d96f Olga Brani
    approval_date = models.DateTimeField(
184 9a06d96f Olga Brani
        'Activation date',
185 9a06d96f Olga Brani
        null=True,
186 9a06d96f Olga Brani
        blank=True
187 9a06d96f Olga Brani
    )
188 9a06d96f Olga Brani
    estimated_participants = models.PositiveIntegerField(
189 9a06d96f Olga Brani
        'Estimated #members',
190 9a06d96f Olga Brani
        null=True,
191 9a06d96f Olga Brani
        blank=True,
192 9a06d96f Olga Brani
    )
193 9a06d96f Olga Brani
    max_participants = models.PositiveIntegerField(
194 9a06d96f Olga Brani
        'Maximum numder of participants',
195 9a06d96f Olga Brani
        null=True,
196 9a06d96f Olga Brani
        blank=True
197 9a06d96f Olga Brani
    )
198 2e90e3ec Kostas Papadimitriou
199 8e45d6fd Sofia Papagiannaki
    @property
200 8e45d6fd Sofia Papagiannaki
    def is_disabled(self):
201 ffb1e7a8 Sofia Papagiannaki
        if not self.approval_date:
202 ffb1e7a8 Sofia Papagiannaki
            return True
203 ffb1e7a8 Sofia Papagiannaki
        return False
204 5ce3ce4f Sofia Papagiannaki
205 8e45d6fd Sofia Papagiannaki
    @property
206 ffb1e7a8 Sofia Papagiannaki
    def is_enabled(self):
207 8e45d6fd Sofia Papagiannaki
        if self.is_disabled:
208 8e45d6fd Sofia Papagiannaki
            return False
209 8e45d6fd Sofia Papagiannaki
        if not self.issue_date:
210 8e45d6fd Sofia Papagiannaki
            return False
211 8e45d6fd Sofia Papagiannaki
        if not self.expiration_date:
212 8e45d6fd Sofia Papagiannaki
            return True
213 8e45d6fd Sofia Papagiannaki
        now = datetime.now()
214 8e45d6fd Sofia Papagiannaki
        if self.issue_date > now:
215 8e45d6fd Sofia Papagiannaki
            return False
216 8e45d6fd Sofia Papagiannaki
        if now >= self.expiration_date:
217 8e45d6fd Sofia Papagiannaki
            return False
218 8e45d6fd Sofia Papagiannaki
        return True
219 5ce3ce4f Sofia Papagiannaki
220 ffb1e7a8 Sofia Papagiannaki
    def enable(self):
221 fc1e2f02 Sofia Papagiannaki
        if self.is_enabled:
222 fc1e2f02 Sofia Papagiannaki
            return
223 8e45d6fd Sofia Papagiannaki
        self.approval_date = datetime.now()
224 8e45d6fd Sofia Papagiannaki
        self.save()
225 20d50182 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=self.approved_members)
226 f8f86e83 root
        #propagate_groupmembers_quota.apply_async(
227 f8f86e83 root
        #    args=[self], eta=self.issue_date)
228 f8f86e83 root
        #propagate_groupmembers_quota.apply_async(
229 f8f86e83 root
        #    args=[self], eta=self.expiration_date)
230 5ce3ce4f Sofia Papagiannaki
231 ffb1e7a8 Sofia Papagiannaki
    def disable(self):
232 fc1e2f02 Sofia Papagiannaki
        if self.is_disabled:
233 fc1e2f02 Sofia Papagiannaki
            return
234 8e45d6fd Sofia Papagiannaki
        self.approval_date = None
235 8e45d6fd Sofia Papagiannaki
        self.save()
236 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=self.approved_members)
237 5ce3ce4f Sofia Papagiannaki
238 ae497612 Olga Brani
    @transaction.commit_manually
239 01ac12d5 Sofia Papagiannaki
    def approve_member(self, person):
240 0f4fa26d Sofia Papagiannaki
        m, created = self.membership_set.get_or_create(person=person)
241 ae497612 Olga Brani
        try:
242 c0b26605 Sofia Papagiannaki
            m.approve()
243 ae497612 Olga Brani
        except:
244 ae497612 Olga Brani
            transaction.rollback()
245 ae497612 Olga Brani
            raise
246 ae497612 Olga Brani
        else:
247 ae497612 Olga Brani
            transaction.commit()
248 5ce3ce4f Sofia Papagiannaki
249 ae497612 Olga Brani
#     def disapprove_member(self, person):
250 ae497612 Olga Brani
#         self.membership_set.remove(person=person)
251 5ce3ce4f Sofia Papagiannaki
252 01ac12d5 Sofia Papagiannaki
    @property
253 01ac12d5 Sofia Papagiannaki
    def members(self):
254 032ade79 Sofia Papagiannaki
        q = self.membership_set.select_related().all()
255 032ade79 Sofia Papagiannaki
        return [m.person for m in q]
256 2e90e3ec Kostas Papadimitriou
257 01ac12d5 Sofia Papagiannaki
    @property
258 01ac12d5 Sofia Papagiannaki
    def approved_members(self):
259 032ade79 Sofia Papagiannaki
        q = self.membership_set.select_related().all()
260 032ade79 Sofia Papagiannaki
        return [m.person for m in q if m.is_approved]
261 5ce3ce4f Sofia Papagiannaki
262 01ac12d5 Sofia Papagiannaki
    @property
263 ffb1e7a8 Sofia Papagiannaki
    def quota(self):
264 0f4fa26d Sofia Papagiannaki
        d = defaultdict(int)
265 dfdc64d2 Sofia Papagiannaki
        for q in self.astakosgroupquota_set.select_related().all():
266 9ee0c6a2 Sofia Papagiannaki
            d[q.resource] += q.uplimit or inf
267 ffb1e7a8 Sofia Papagiannaki
        return d
268 2e90e3ec Kostas Papadimitriou
269 9a06d96f Olga Brani
    def add_policy(self, service, resource, uplimit, update=True):
270 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
271 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
272 9a06d96f Olga Brani
        if update:
273 9a06d96f Olga Brani
            AstakosGroupQuota.objects.update_or_create(
274 9a06d96f Olga Brani
                group=self,
275 9a06d96f Olga Brani
                resource=resource,
276 9a06d96f Olga Brani
                defaults={'uplimit': uplimit}
277 9a06d96f Olga Brani
            )
278 9a06d96f Olga Brani
        else:
279 9a06d96f Olga Brani
            q = self.astakosgroupquota_set
280 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
281 2e90e3ec Kostas Papadimitriou
282 9a06d96f Olga Brani
    @property
283 9a06d96f Olga Brani
    def policies(self):
284 9a06d96f Olga Brani
        return self.astakosgroupquota_set.select_related().all()
285 9a06d96f Olga Brani
286 9a06d96f Olga Brani
    @policies.setter
287 9a06d96f Olga Brani
    def policies(self, policies):
288 9a06d96f Olga Brani
        for p in policies:
289 9a06d96f Olga Brani
            service = p.get('service', None)
290 9a06d96f Olga Brani
            resource = p.get('resource', None)
291 9a06d96f Olga Brani
            uplimit = p.get('uplimit', 0)
292 9a06d96f Olga Brani
            update = p.get('update', True)
293 9a06d96f Olga Brani
            self.add_policy(service, resource, uplimit, update)
294 2e90e3ec Kostas Papadimitriou
295 01ac12d5 Sofia Papagiannaki
    @property
296 0f4fa26d Sofia Papagiannaki
    def owners(self):
297 0f4fa26d Sofia Papagiannaki
        return self.owner.all()
298 5ce3ce4f Sofia Papagiannaki
299 032ade79 Sofia Papagiannaki
    @property
300 032ade79 Sofia Papagiannaki
    def owner_details(self):
301 032ade79 Sofia Papagiannaki
        return self.owner.select_related().all()
302 032ade79 Sofia Papagiannaki
303 0f4fa26d Sofia Papagiannaki
    @owners.setter
304 0f4fa26d Sofia Papagiannaki
    def owners(self, l):
305 0f4fa26d Sofia Papagiannaki
        self.owner = l
306 0f4fa26d Sofia Papagiannaki
        map(self.approve_member, l)
307 8e45d6fd Sofia Papagiannaki
308 5ce3ce4f Sofia Papagiannaki
309 d2633501 Kostas Papadimitriou
310 6b9a334b Sofia Papagiannaki
class AstakosUserManager(UserManager):
311 d2633501 Kostas Papadimitriou
312 d2633501 Kostas Papadimitriou
    def get_auth_provider_user(self, provider, **kwargs):
313 d2633501 Kostas Papadimitriou
        """
314 d2633501 Kostas Papadimitriou
        Retrieve AstakosUser instance associated with the specified third party
315 d2633501 Kostas Papadimitriou
        id.
316 d2633501 Kostas Papadimitriou
        """
317 d2633501 Kostas Papadimitriou
        kwargs = dict(map(lambda x: ('auth_providers__%s' % x[0], x[1]),
318 d2633501 Kostas Papadimitriou
                          kwargs.iteritems()))
319 d2633501 Kostas Papadimitriou
        return self.get(auth_providers__module=provider, **kwargs)
320 d2633501 Kostas Papadimitriou
321 0905ccd2 Sofia Papagiannaki
class AstakosUser(User):
322 890b0eaf Sofia Papagiannaki
    """
323 890b0eaf Sofia Papagiannaki
    Extends ``django.contrib.auth.models.User`` by defining additional fields.
324 890b0eaf Sofia Papagiannaki
    """
325 d2633501 Kostas Papadimitriou
    affiliation = models.CharField('Affiliation', max_length=255, blank=True,
326 d2633501 Kostas Papadimitriou
                                   null=True)
327 d2633501 Kostas Papadimitriou
328 d2633501 Kostas Papadimitriou
    # DEPRECATED FIELDS: provider, third_party_identifier moved in
329 d2633501 Kostas Papadimitriou
    #                    AstakosUserProvider model.
330 d2633501 Kostas Papadimitriou
    provider = models.CharField('Provider', max_length=255, blank=True,
331 d2633501 Kostas Papadimitriou
                                null=True)
332 d2633501 Kostas Papadimitriou
    # ex. screen_name for twitter, eppn for shibboleth
333 d2633501 Kostas Papadimitriou
    third_party_identifier = models.CharField('Third-party identifier',
334 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
335 d2633501 Kostas Papadimitriou
                                              blank=True)
336 d2633501 Kostas Papadimitriou
337 6c736ed7 Kostas Papadimitriou
338 64cd4730 Antony Chazapis
    #for invitations
339 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
340 a196eb7e Sofia Papagiannaki
    level = models.IntegerField('Inviter level', default=user_level)
341 5ce3ce4f Sofia Papagiannaki
    invitations = models.IntegerField(
342 5ce3ce4f Sofia Papagiannaki
        'Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
343 6c736ed7 Kostas Papadimitriou
344 64cd4730 Antony Chazapis
    auth_token = models.CharField('Authentication Token', max_length=32,
345 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
346 0905ccd2 Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
347 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
348 5ce3ce4f Sofia Papagiannaki
        'Token expiration date', null=True)
349 6c736ed7 Kostas Papadimitriou
350 64cd4730 Antony Chazapis
    updated = models.DateTimeField('Update date')
351 890b0eaf Sofia Papagiannaki
    is_verified = models.BooleanField('Is verified?', default=False)
352 6c736ed7 Kostas Papadimitriou
353 ebd369d0 Sofia Papagiannaki
    email_verified = models.BooleanField('Email verified?', default=False)
354 6c736ed7 Kostas Papadimitriou
355 59f598f1 Sofia Papagiannaki
    has_credits = models.BooleanField('Has credits?', default=False)
356 5ce3ce4f Sofia Papagiannaki
    has_signed_terms = models.BooleanField(
357 9a06d96f Olga Brani
        'I agree with the terms', default=False)
358 5ce3ce4f Sofia Papagiannaki
    date_signed_terms = models.DateTimeField(
359 5ce3ce4f Sofia Papagiannaki
        'Signed terms date', null=True, blank=True)
360 5ce3ce4f Sofia Papagiannaki
361 5ce3ce4f Sofia Papagiannaki
    activation_sent = models.DateTimeField(
362 5ce3ce4f Sofia Papagiannaki
        'Activation sent data', null=True, blank=True)
363 5ce3ce4f Sofia Papagiannaki
364 5ce3ce4f Sofia Papagiannaki
    policy = models.ManyToManyField(
365 5ce3ce4f Sofia Papagiannaki
        Resource, null=True, through='AstakosUserQuota')
366 5ce3ce4f Sofia Papagiannaki
367 5ce3ce4f Sofia Papagiannaki
    astakos_groups = models.ManyToManyField(
368 5ce3ce4f Sofia Papagiannaki
        AstakosGroup, verbose_name=_('agroups'), blank=True,
369 ae497612 Olga Brani
        help_text=_(astakos_messages.ASTAKOSUSER_GROUPS_HELP),
370 8e45d6fd Sofia Papagiannaki
        through='Membership')
371 5ce3ce4f Sofia Papagiannaki
372 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
373 9a06d96f Olga Brani
    disturbed_quota = models.BooleanField('Needs quotaholder syncing',
374 9a06d96f Olga Brani
                                           default=False, db_index=True)
375 d2633501 Kostas Papadimitriou
376 d2633501 Kostas Papadimitriou
    objects = AstakosUserManager()
377 fbaa4f3c Kostas Papadimitriou
378 5ce3ce4f Sofia Papagiannaki
    owner = models.ManyToManyField(
379 5ce3ce4f Sofia Papagiannaki
        AstakosGroup, related_name='owner', null=True)
380 5ce3ce4f Sofia Papagiannaki
381 74b273d8 Sofia Papagiannaki
    class Meta:
382 74b273d8 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
383 d2633501 Kostas Papadimitriou
384 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
385 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
386 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
387 a3637508 Sofia Papagiannaki
        if not self.id:
388 18ffbee1 Sofia Papagiannaki
            self.is_active = False
389 5ce3ce4f Sofia Papagiannaki
390 0905ccd2 Sofia Papagiannaki
    @property
391 0905ccd2 Sofia Papagiannaki
    def realname(self):
392 5ce3ce4f Sofia Papagiannaki
        return '%s %s' % (self.first_name, self.last_name)
393 6c736ed7 Kostas Papadimitriou
394 0905ccd2 Sofia Papagiannaki
    @realname.setter
395 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
396 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
397 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
398 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
399 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
400 0905ccd2 Sofia Papagiannaki
        else:
401 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
402 6c736ed7 Kostas Papadimitriou
403 9a06d96f Olga Brani
    def add_permission(self, pname):
404 9a06d96f Olga Brani
        if self.has_perm(pname):
405 9a06d96f Olga Brani
            return
406 9a06d96f Olga Brani
        p, created = Permission.objects.get_or_create(codename=pname,
407 9a06d96f Olga Brani
                                                      name=pname.capitalize(),
408 9a06d96f Olga Brani
                                                      content_type=content_type)
409 9a06d96f Olga Brani
        self.user_permissions.add(p)
410 9a06d96f Olga Brani
411 9a06d96f Olga Brani
    def remove_permission(self, pname):
412 9a06d96f Olga Brani
        if self.has_perm(pname):
413 9a06d96f Olga Brani
            return
414 9a06d96f Olga Brani
        p = Permission.objects.get(codename=pname,
415 9a06d96f Olga Brani
                                   content_type=content_type)
416 9a06d96f Olga Brani
        self.user_permissions.remove(p)
417 9a06d96f Olga Brani
418 64cd4730 Antony Chazapis
    @property
419 64cd4730 Antony Chazapis
    def invitation(self):
420 64cd4730 Antony Chazapis
        try:
421 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
422 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
423 64cd4730 Antony Chazapis
            return None
424 6c736ed7 Kostas Papadimitriou
425 9a06d96f Olga Brani
    def invite(self, email, realname):
426 9a06d96f Olga Brani
        inv = Invitation(inviter=self, username=email, realname=realname)
427 9a06d96f Olga Brani
        inv.save()
428 9a06d96f Olga Brani
        send_invitation(inv)
429 9a06d96f Olga Brani
        self.invitations = max(0, self.invitations - 1)
430 9a06d96f Olga Brani
        self.save()
431 9a06d96f Olga Brani
432 ffb1e7a8 Sofia Papagiannaki
    @property
433 ffb1e7a8 Sofia Papagiannaki
    def quota(self):
434 9a06d96f Olga Brani
        """Returns a dict with the sum of quota limits per resource"""
435 ffb1e7a8 Sofia Papagiannaki
        d = defaultdict(int)
436 9a06d96f Olga Brani
        for q in self.policies:
437 9ee0c6a2 Sofia Papagiannaki
            d[q.resource] += q.uplimit or inf
438 9a06d96f Olga Brani
        for m in self.extended_groups:
439 fc1e2f02 Sofia Papagiannaki
            if not m.is_approved:
440 fc1e2f02 Sofia Papagiannaki
                continue
441 fc1e2f02 Sofia Papagiannaki
            g = m.group
442 ffb1e7a8 Sofia Papagiannaki
            if not g.is_enabled:
443 ffb1e7a8 Sofia Papagiannaki
                continue
444 670de92a Sofia Papagiannaki
            for r, uplimit in g.quota.iteritems():
445 9ee0c6a2 Sofia Papagiannaki
                d[r] += uplimit or inf
446 ffb1e7a8 Sofia Papagiannaki
        # TODO set default for remaining
447 ffb1e7a8 Sofia Papagiannaki
        return d
448 5ce3ce4f Sofia Papagiannaki
449 9a06d96f Olga Brani
    @property
450 9a06d96f Olga Brani
    def policies(self):
451 9a06d96f Olga Brani
        return self.astakosuserquota_set.select_related().all()
452 9a06d96f Olga Brani
453 9a06d96f Olga Brani
    @policies.setter
454 9a06d96f Olga Brani
    def policies(self, policies):
455 9a06d96f Olga Brani
        for p in policies:
456 9a06d96f Olga Brani
            service = policies.get('service', None)
457 9a06d96f Olga Brani
            resource = policies.get('resource', None)
458 9a06d96f Olga Brani
            uplimit = policies.get('uplimit', 0)
459 9a06d96f Olga Brani
            update = policies.get('update', True)
460 9a06d96f Olga Brani
            self.add_policy(service, resource, uplimit, update)
461 9a06d96f Olga Brani
462 9a06d96f Olga Brani
    def add_policy(self, service, resource, uplimit, update=True):
463 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
464 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
465 9a06d96f Olga Brani
        if update:
466 9a06d96f Olga Brani
            AstakosUserQuota.objects.update_or_create(user=self,
467 9a06d96f Olga Brani
                                                      resource=resource,
468 9a06d96f Olga Brani
                                                      defaults={'uplimit': uplimit})
469 9a06d96f Olga Brani
        else:
470 9a06d96f Olga Brani
            q = self.astakosuserquota_set
471 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
472 9a06d96f Olga Brani
473 9a06d96f Olga Brani
    def remove_policy(self, service, resource):
474 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
475 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
476 9a06d96f Olga Brani
        q = self.policies.get(resource=resource).delete()
477 9a06d96f Olga Brani
478 9a06d96f Olga Brani
    @property
479 9a06d96f Olga Brani
    def extended_groups(self):
480 9a06d96f Olga Brani
        return self.membership_set.select_related().all()
481 9a06d96f Olga Brani
482 9a06d96f Olga Brani
    @extended_groups.setter
483 9a06d96f Olga Brani
    def extended_groups(self, groups):
484 9a06d96f Olga Brani
        #TODO exceptions
485 40a0cd8b Sofia Papagiannaki
        for name in (groups or ()):
486 9a06d96f Olga Brani
            group = AstakosGroup.objects.get(name=name)
487 9a06d96f Olga Brani
            self.membership_set.create(group=group)
488 9a06d96f Olga Brani
489 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
490 64cd4730 Antony Chazapis
        if update_timestamps:
491 64cd4730 Antony Chazapis
            if not self.id:
492 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
493 64cd4730 Antony Chazapis
            self.updated = datetime.now()
494 5ce3ce4f Sofia Papagiannaki
495 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
496 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
497 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
498 5ce3ce4f Sofia Papagiannaki
499 9c01d9d1 Sofia Papagiannaki
        if not self.id:
500 9c01d9d1 Sofia Papagiannaki
            # set username
501 a706ae94 Sofia Papagiannaki
            self.username = self.email
502 fbaa4f3c Kostas Papadimitriou
503 591d0505 Sofia Papagiannaki
        self.validate_unique_email_isactive()
504 751d24cf Sofia Papagiannaki
        if self.is_active and self.activation_sent:
505 751d24cf Sofia Papagiannaki
            # reset the activation sent
506 751d24cf Sofia Papagiannaki
            self.activation_sent = None
507 5ce3ce4f Sofia Papagiannaki
508 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
509 2e90e3ec Kostas Papadimitriou
510 bf0c6de5 Sofia Papagiannaki
    def renew_token(self, flush_sessions=False, current_key=None):
511 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
512 8f8c43b2 Sofia Papagiannaki
        md5.update(settings.SECRET_KEY)
513 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
514 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
515 64cd4730 Antony Chazapis
        md5.update(asctime())
516 2e90e3ec Kostas Papadimitriou
517 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
518 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
519 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
520 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
521 bf0c6de5 Sofia Papagiannaki
        if flush_sessions:
522 bf0c6de5 Sofia Papagiannaki
            self.flush_sessions(current_key)
523 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
524 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
525 6c736ed7 Kostas Papadimitriou
526 bf0c6de5 Sofia Papagiannaki
    def flush_sessions(self, current_key=None):
527 bf0c6de5 Sofia Papagiannaki
        q = self.sessions
528 bf0c6de5 Sofia Papagiannaki
        if current_key:
529 bf0c6de5 Sofia Papagiannaki
            q = q.exclude(session_key=current_key)
530 2e90e3ec Kostas Papadimitriou
531 bf0c6de5 Sofia Papagiannaki
        keys = q.values_list('session_key', flat=True)
532 bf0c6de5 Sofia Papagiannaki
        if keys:
533 bf0c6de5 Sofia Papagiannaki
            msg = 'Flushing sessions: %s' % ','.join(keys)
534 c0b26605 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, msg, [])
535 bf0c6de5 Sofia Papagiannaki
        engine = import_module(settings.SESSION_ENGINE)
536 bf0c6de5 Sofia Papagiannaki
        for k in keys:
537 bf0c6de5 Sofia Papagiannaki
            s = engine.SessionStore(k)
538 bf0c6de5 Sofia Papagiannaki
            s.flush()
539 bf0c6de5 Sofia Papagiannaki
540 64cd4730 Antony Chazapis
    def __unicode__(self):
541 3abf6c78 Sofia Papagiannaki
        return '%s (%s)' % (self.realname, self.email)
542 5ce3ce4f Sofia Papagiannaki
543 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
544 5ce3ce4f Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username=self.username)
545 789a5951 Sofia Papagiannaki
        q = q.filter(email__iexact=self.email)
546 0a569195 Sofia Papagiannaki
        if q.count() != 0:
547 0a569195 Sofia Papagiannaki
            return True
548 0a569195 Sofia Papagiannaki
        return False
549 5ce3ce4f Sofia Papagiannaki
550 591d0505 Sofia Papagiannaki
    def validate_unique_email_isactive(self):
551 0a569195 Sofia Papagiannaki
        """
552 0a569195 Sofia Papagiannaki
        Implements a unique_together constraint for email and is_active fields.
553 0a569195 Sofia Papagiannaki
        """
554 e6759494 Sofia Papagiannaki
        q = AstakosUser.objects.all()
555 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
556 0a569195 Sofia Papagiannaki
        q = q.filter(is_active = self.is_active)
557 e6759494 Sofia Papagiannaki
        if self.id:
558 e6759494 Sofia Papagiannaki
            q = q.filter(~Q(id = self.id))
559 0a569195 Sofia Papagiannaki
        if q.count() != 0:
560 ae497612 Olga Brani
            raise ValidationError({'__all__': [_(astakos_messages.UNIQUE_EMAIL_IS_ACTIVE_CONSTRAIN_ERR)]})
561 5ce3ce4f Sofia Papagiannaki
562 fcf90160 Sofia Papagiannaki
    @property
563 09e7393c Sofia Papagiannaki
    def signed_terms(self):
564 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
565 09e7393c Sofia Papagiannaki
        if not term:
566 09e7393c Sofia Papagiannaki
            return True
567 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
568 09e7393c Sofia Papagiannaki
            return False
569 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
570 09e7393c Sofia Papagiannaki
            return False
571 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
572 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
573 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
574 09e7393c Sofia Papagiannaki
            self.save()
575 09e7393c Sofia Papagiannaki
            return False
576 09e7393c Sofia Papagiannaki
        return True
577 09e7393c Sofia Papagiannaki
578 d2633501 Kostas Papadimitriou
    def set_invitations_level(self):
579 d2633501 Kostas Papadimitriou
        """
580 d2633501 Kostas Papadimitriou
        Update user invitation level
581 d2633501 Kostas Papadimitriou
        """
582 d2633501 Kostas Papadimitriou
        level = self.invitation.inviter.level + 1
583 d2633501 Kostas Papadimitriou
        self.level = level
584 d2633501 Kostas Papadimitriou
        self.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
585 d2633501 Kostas Papadimitriou
586 d2633501 Kostas Papadimitriou
    def can_login_with_auth_provider(self, provider):
587 d2633501 Kostas Papadimitriou
        if not self.has_auth_provider(provider):
588 d2633501 Kostas Papadimitriou
            return False
589 d2633501 Kostas Papadimitriou
        else:
590 d2633501 Kostas Papadimitriou
            return auth_providers.get_provider(provider).is_available_for_login()
591 d2633501 Kostas Papadimitriou
592 f432088a Kostas Papadimitriou
    def can_add_auth_provider(self, provider, **kwargs):
593 d2633501 Kostas Papadimitriou
        provider_settings = auth_providers.get_provider(provider)
594 d2633501 Kostas Papadimitriou
        if not provider_settings.is_available_for_login():
595 d2633501 Kostas Papadimitriou
            return False
596 f432088a Kostas Papadimitriou
597 d2633501 Kostas Papadimitriou
        if self.has_auth_provider(provider) and \
598 d2633501 Kostas Papadimitriou
           provider_settings.one_per_user:
599 d2633501 Kostas Papadimitriou
            return False
600 f432088a Kostas Papadimitriou
601 f432088a Kostas Papadimitriou
        if 'identifier' in kwargs:
602 f432088a Kostas Papadimitriou
            try:
603 f432088a Kostas Papadimitriou
                # provider with specified params already exist
604 f432088a Kostas Papadimitriou
                existing_user = AstakosUser.objects.get_auth_provider_user(provider,
605 f432088a Kostas Papadimitriou
                                                                   **kwargs)
606 f432088a Kostas Papadimitriou
            except AstakosUser.DoesNotExist:
607 f432088a Kostas Papadimitriou
                return True
608 f432088a Kostas Papadimitriou
            else:
609 f432088a Kostas Papadimitriou
                return False
610 f432088a Kostas Papadimitriou
611 d2633501 Kostas Papadimitriou
        return True
612 d2633501 Kostas Papadimitriou
613 d2633501 Kostas Papadimitriou
    def can_remove_auth_provider(self, provider):
614 d2633501 Kostas Papadimitriou
        if len(self.get_active_auth_providers()) <= 1:
615 d2633501 Kostas Papadimitriou
            return False
616 d2633501 Kostas Papadimitriou
        return True
617 d2633501 Kostas Papadimitriou
618 d2633501 Kostas Papadimitriou
    def can_change_password(self):
619 d2633501 Kostas Papadimitriou
        return self.has_auth_provider('local', auth_backend='astakos')
620 d2633501 Kostas Papadimitriou
621 d2633501 Kostas Papadimitriou
    def has_auth_provider(self, provider, **kwargs):
622 d2633501 Kostas Papadimitriou
        return bool(self.auth_providers.filter(module=provider,
623 d2633501 Kostas Papadimitriou
                                               **kwargs).count())
624 d2633501 Kostas Papadimitriou
625 d2633501 Kostas Papadimitriou
    def add_auth_provider(self, provider, **kwargs):
626 f432088a Kostas Papadimitriou
        if self.can_add_auth_provider(provider, **kwargs):
627 f432088a Kostas Papadimitriou
            self.auth_providers.create(module=provider, active=True, **kwargs)
628 f432088a Kostas Papadimitriou
        else:
629 f432088a Kostas Papadimitriou
            raise Exception('Cannot add provider')
630 d2633501 Kostas Papadimitriou
631 d2633501 Kostas Papadimitriou
    def add_pending_auth_provider(self, pending):
632 d2633501 Kostas Papadimitriou
        """
633 d2633501 Kostas Papadimitriou
        Convert PendingThirdPartyUser object to AstakosUserAuthProvider entry for
634 d2633501 Kostas Papadimitriou
        the current user.
635 d2633501 Kostas Papadimitriou
        """
636 d2633501 Kostas Papadimitriou
        if not isinstance(pending, PendingThirdPartyUser):
637 d2633501 Kostas Papadimitriou
            pending = PendingThirdPartyUser.objects.get(token=pending)
638 d2633501 Kostas Papadimitriou
639 d2633501 Kostas Papadimitriou
        provider = self.add_auth_provider(pending.provider,
640 d2633501 Kostas Papadimitriou
                               identifier=pending.third_party_identifier)
641 d2633501 Kostas Papadimitriou
642 fbaa4f3c Kostas Papadimitriou
        if email_re.match(pending.email or '') and pending.email != self.email:
643 d2633501 Kostas Papadimitriou
            self.additionalmail_set.get_or_create(email=pending.email)
644 d2633501 Kostas Papadimitriou
645 d2633501 Kostas Papadimitriou
        pending.delete()
646 d2633501 Kostas Papadimitriou
        return provider
647 d2633501 Kostas Papadimitriou
648 d2633501 Kostas Papadimitriou
    def remove_auth_provider(self, provider, **kwargs):
649 d2633501 Kostas Papadimitriou
        self.auth_providers.get(module=provider, **kwargs).delete()
650 d2633501 Kostas Papadimitriou
651 d2633501 Kostas Papadimitriou
    # user urls
652 d2633501 Kostas Papadimitriou
    def get_resend_activation_url(self):
653 d2633501 Kostas Papadimitriou
        return reverse('send_activation', {'user_id': self.pk})
654 d2633501 Kostas Papadimitriou
655 d2633501 Kostas Papadimitriou
    def get_activation_url(self, nxt=False):
656 d2633501 Kostas Papadimitriou
        url = "%s?auth=%s" % (reverse('astakos.im.views.activate'),
657 d2633501 Kostas Papadimitriou
                                 quote(self.auth_token))
658 d2633501 Kostas Papadimitriou
        if nxt:
659 d2633501 Kostas Papadimitriou
            url += "&next=%s" % quote(nxt)
660 d2633501 Kostas Papadimitriou
        return url
661 d2633501 Kostas Papadimitriou
662 d2633501 Kostas Papadimitriou
    def get_password_reset_url(self, token_generator=default_token_generator):
663 d2633501 Kostas Papadimitriou
        return reverse('django.contrib.auth.views.password_reset_confirm',
664 d2633501 Kostas Papadimitriou
                          kwargs={'uidb36':int_to_base36(self.id),
665 d2633501 Kostas Papadimitriou
                                  'token':token_generator.make_token(self)})
666 d2633501 Kostas Papadimitriou
667 d2633501 Kostas Papadimitriou
    def get_auth_providers(self):
668 d2633501 Kostas Papadimitriou
        return self.auth_providers.all()
669 d2633501 Kostas Papadimitriou
670 d2633501 Kostas Papadimitriou
    def get_available_auth_providers(self):
671 d2633501 Kostas Papadimitriou
        """
672 d2633501 Kostas Papadimitriou
        Returns a list of providers available for user to connect to.
673 d2633501 Kostas Papadimitriou
        """
674 d2633501 Kostas Papadimitriou
        providers = []
675 d2633501 Kostas Papadimitriou
        for module, provider_settings in auth_providers.PROVIDERS.iteritems():
676 f432088a Kostas Papadimitriou
            if self.can_add_auth_provider(module):
677 d2633501 Kostas Papadimitriou
                providers.append(provider_settings(self))
678 d2633501 Kostas Papadimitriou
679 d2633501 Kostas Papadimitriou
        return providers
680 d2633501 Kostas Papadimitriou
681 d2633501 Kostas Papadimitriou
    def get_active_auth_providers(self):
682 d2633501 Kostas Papadimitriou
        providers = []
683 d2633501 Kostas Papadimitriou
        for provider in self.auth_providers.active():
684 d2633501 Kostas Papadimitriou
            if auth_providers.get_provider(provider.module).is_available_for_login():
685 d2633501 Kostas Papadimitriou
                providers.append(provider)
686 d2633501 Kostas Papadimitriou
        return providers
687 d2633501 Kostas Papadimitriou
688 b778b6fa Kostas Papadimitriou
    @property
689 b778b6fa Kostas Papadimitriou
    def auth_providers_display(self):
690 b778b6fa Kostas Papadimitriou
        return ",".join(map(lambda x:unicode(x), self.auth_providers.active()))
691 b778b6fa Kostas Papadimitriou
692 d2633501 Kostas Papadimitriou
693 d2633501 Kostas Papadimitriou
class AstakosUserAuthProviderManager(models.Manager):
694 d2633501 Kostas Papadimitriou
695 d2633501 Kostas Papadimitriou
    def active(self):
696 d2633501 Kostas Papadimitriou
        return self.filter(active=True)
697 d2633501 Kostas Papadimitriou
698 d2633501 Kostas Papadimitriou
699 d2633501 Kostas Papadimitriou
class AstakosUserAuthProvider(models.Model):
700 d2633501 Kostas Papadimitriou
    """
701 d2633501 Kostas Papadimitriou
    Available user authentication methods.
702 d2633501 Kostas Papadimitriou
    """
703 d2633501 Kostas Papadimitriou
    affiliation = models.CharField('Affiliation', max_length=255, blank=True,
704 d2633501 Kostas Papadimitriou
                                   null=True, default=None)
705 d2633501 Kostas Papadimitriou
    user = models.ForeignKey(AstakosUser, related_name='auth_providers')
706 d2633501 Kostas Papadimitriou
    module = models.CharField('Provider', max_length=255, blank=False,
707 d2633501 Kostas Papadimitriou
                                default='local')
708 d2633501 Kostas Papadimitriou
    identifier = models.CharField('Third-party identifier',
709 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
710 d2633501 Kostas Papadimitriou
                                              blank=True)
711 d2633501 Kostas Papadimitriou
    active = models.BooleanField(default=True)
712 d2633501 Kostas Papadimitriou
    auth_backend = models.CharField('Backend', max_length=255, blank=False,
713 d2633501 Kostas Papadimitriou
                                   default='astakos')
714 d2633501 Kostas Papadimitriou
715 d2633501 Kostas Papadimitriou
    objects = AstakosUserAuthProviderManager()
716 d2633501 Kostas Papadimitriou
717 d2633501 Kostas Papadimitriou
    class Meta:
718 d2633501 Kostas Papadimitriou
        unique_together = (('identifier', 'module', 'user'), )
719 d2633501 Kostas Papadimitriou
720 d2633501 Kostas Papadimitriou
    @property
721 d2633501 Kostas Papadimitriou
    def settings(self):
722 d2633501 Kostas Papadimitriou
        return auth_providers.get_provider(self.module)
723 d2633501 Kostas Papadimitriou
724 d2633501 Kostas Papadimitriou
    @property
725 d2633501 Kostas Papadimitriou
    def details_display(self):
726 d2633501 Kostas Papadimitriou
        return self.settings.details_tpl % self.__dict__
727 d2633501 Kostas Papadimitriou
728 d2633501 Kostas Papadimitriou
    def can_remove(self):
729 d2633501 Kostas Papadimitriou
        return self.user.can_remove_auth_provider(self.module)
730 d2633501 Kostas Papadimitriou
731 d2633501 Kostas Papadimitriou
    def delete(self, *args, **kwargs):
732 d2633501 Kostas Papadimitriou
        ret = super(AstakosUserAuthProvider, self).delete(*args, **kwargs)
733 5156e663 Kostas Papadimitriou
        if self.module == 'local':
734 5156e663 Kostas Papadimitriou
            self.user.set_unusable_password()
735 5156e663 Kostas Papadimitriou
            self.user.save()
736 d2633501 Kostas Papadimitriou
        return ret
737 d2633501 Kostas Papadimitriou
738 f432088a Kostas Papadimitriou
    def __repr__(self):
739 f432088a Kostas Papadimitriou
        return '<AstakosUserAuthProvider %s:%s>' % (self.module, self.identifier)
740 f432088a Kostas Papadimitriou
741 b778b6fa Kostas Papadimitriou
    def __unicode__(self):
742 b778b6fa Kostas Papadimitriou
        if self.identifier:
743 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.identifier)
744 b778b6fa Kostas Papadimitriou
        if self.auth_backend:
745 b778b6fa Kostas Papadimitriou
            return "%s:%s" % (self.module, self.auth_backend)
746 b778b6fa Kostas Papadimitriou
        return self.module
747 b778b6fa Kostas Papadimitriou
748 b778b6fa Kostas Papadimitriou
749 d2633501 Kostas Papadimitriou
750 8e45d6fd Sofia Papagiannaki
class Membership(models.Model):
751 8e45d6fd Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
752 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup)
753 01ac12d5 Sofia Papagiannaki
    date_requested = models.DateField(default=datetime.now(), blank=True)
754 01ac12d5 Sofia Papagiannaki
    date_joined = models.DateField(null=True, db_index=True, blank=True)
755 5ce3ce4f Sofia Papagiannaki
756 8e45d6fd Sofia Papagiannaki
    class Meta:
757 8e45d6fd Sofia Papagiannaki
        unique_together = ("person", "group")
758 5ce3ce4f Sofia Papagiannaki
759 ffb1e7a8 Sofia Papagiannaki
    def save(self, *args, **kwargs):
760 28252c7f Sofia Papagiannaki
        if not self.id:
761 28252c7f Sofia Papagiannaki
            if not self.group.moderation_enabled:
762 28252c7f Sofia Papagiannaki
                self.date_joined = datetime.now()
763 ffb1e7a8 Sofia Papagiannaki
        super(Membership, self).save(*args, **kwargs)
764 5ce3ce4f Sofia Papagiannaki
765 8e45d6fd Sofia Papagiannaki
    @property
766 8e45d6fd Sofia Papagiannaki
    def is_approved(self):
767 8e45d6fd Sofia Papagiannaki
        if self.date_joined:
768 8e45d6fd Sofia Papagiannaki
            return True
769 8e45d6fd Sofia Papagiannaki
        return False
770 5ce3ce4f Sofia Papagiannaki
771 01ac12d5 Sofia Papagiannaki
    def approve(self):
772 c0b26605 Sofia Papagiannaki
        if self.is_approved:
773 c0b26605 Sofia Papagiannaki
            return
774 ae497612 Olga Brani
        if self.group.max_participants:
775 a4075f5a root
            assert len(self.group.approved_members) + 1 <= self.group.max_participants, \
776 c0b26605 Sofia Papagiannaki
            'Maximum participant number has been reached.'
777 01ac12d5 Sofia Papagiannaki
        self.date_joined = datetime.now()
778 01ac12d5 Sofia Papagiannaki
        self.save()
779 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=(self.person,))
780 5ce3ce4f Sofia Papagiannaki
781 01ac12d5 Sofia Papagiannaki
    def disapprove(self):
782 01ac12d5 Sofia Papagiannaki
        self.delete()
783 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=(self.person,))
784 8e45d6fd Sofia Papagiannaki
785 9a06d96f Olga Brani
class AstakosQuotaManager(models.Manager):
786 9a06d96f Olga Brani
    def _update_or_create(self, **kwargs):
787 9a06d96f Olga Brani
        assert kwargs, \
788 9a06d96f Olga Brani
            'update_or_create() must be passed at least one keyword argument'
789 9a06d96f Olga Brani
        obj, created = self.get_or_create(**kwargs)
790 9a06d96f Olga Brani
        defaults = kwargs.pop('defaults', {})
791 9a06d96f Olga Brani
        if created:
792 9a06d96f Olga Brani
            return obj, True, False
793 9a06d96f Olga Brani
        else:
794 9a06d96f Olga Brani
            try:
795 9a06d96f Olga Brani
                params = dict(
796 9a06d96f Olga Brani
                    [(k, v) for k, v in kwargs.items() if '__' not in k])
797 9a06d96f Olga Brani
                params.update(defaults)
798 9a06d96f Olga Brani
                for attr, val in params.items():
799 9a06d96f Olga Brani
                    if hasattr(obj, attr):
800 9a06d96f Olga Brani
                        setattr(obj, attr, val)
801 9a06d96f Olga Brani
                sid = transaction.savepoint()
802 9a06d96f Olga Brani
                obj.save(force_update=True)
803 9a06d96f Olga Brani
                transaction.savepoint_commit(sid)
804 9a06d96f Olga Brani
                return obj, False, True
805 9a06d96f Olga Brani
            except IntegrityError, e:
806 9a06d96f Olga Brani
                transaction.savepoint_rollback(sid)
807 9a06d96f Olga Brani
                try:
808 9a06d96f Olga Brani
                    return self.get(**kwargs), False, False
809 9a06d96f Olga Brani
                except self.model.DoesNotExist:
810 9a06d96f Olga Brani
                    raise e
811 9a06d96f Olga Brani
812 9a06d96f Olga Brani
    update_or_create = _update_or_create
813 5ce3ce4f Sofia Papagiannaki
814 8e45d6fd Sofia Papagiannaki
class AstakosGroupQuota(models.Model):
815 9a06d96f Olga Brani
    objects = AstakosQuotaManager()
816 7507ea03 root
    limit = models.PositiveIntegerField('Limit', null=True)    # obsolete field
817 670de92a Sofia Papagiannaki
    uplimit = models.BigIntegerField('Up limit', null=True)
818 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
819 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup, blank=True)
820 5ce3ce4f Sofia Papagiannaki
821 8e45d6fd Sofia Papagiannaki
    class Meta:
822 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "group")
823 8e45d6fd Sofia Papagiannaki
824 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
825 9a06d96f Olga Brani
    objects = AstakosQuotaManager()
826 7507ea03 root
    limit = models.PositiveIntegerField('Limit', null=True)    # obsolete field
827 670de92a Sofia Papagiannaki
    uplimit = models.BigIntegerField('Up limit', null=True)
828 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
829 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
830 5ce3ce4f Sofia Papagiannaki
831 8e45d6fd Sofia Papagiannaki
    class Meta:
832 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
833 09e7393c Sofia Papagiannaki
834 5ce3ce4f Sofia Papagiannaki
835 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
836 270dd48d Sofia Papagiannaki
    """
837 270dd48d Sofia Papagiannaki
    Model for approval terms
838 270dd48d Sofia Papagiannaki
    """
839 6c736ed7 Kostas Papadimitriou
840 5ce3ce4f Sofia Papagiannaki
    date = models.DateTimeField(
841 5ce3ce4f Sofia Papagiannaki
        'Issue date', db_index=True, default=datetime.now())
842 270dd48d Sofia Papagiannaki
    location = models.CharField('Terms location', max_length=255)
843 270dd48d Sofia Papagiannaki
844 5ce3ce4f Sofia Papagiannaki
845 64cd4730 Antony Chazapis
class Invitation(models.Model):
846 890b0eaf Sofia Papagiannaki
    """
847 890b0eaf Sofia Papagiannaki
    Model for registring invitations
848 890b0eaf Sofia Papagiannaki
    """
849 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
850 64cd4730 Antony Chazapis
                                null=True)
851 64cd4730 Antony Chazapis
    realname = models.CharField('Real name', max_length=255)
852 ebd369d0 Sofia Papagiannaki
    username = models.CharField('Unique ID', max_length=255, unique=True)
853 64cd4730 Antony Chazapis
    code = models.BigIntegerField('Invitation code', db_index=True)
854 64cd4730 Antony Chazapis
    is_consumed = models.BooleanField('Consumed?', default=False)
855 64cd4730 Antony Chazapis
    created = models.DateTimeField('Creation date', auto_now_add=True)
856 64cd4730 Antony Chazapis
    consumed = models.DateTimeField('Consumption date', null=True, blank=True)
857 5ce3ce4f Sofia Papagiannaki
858 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
859 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
860 8f5a3a06 Sofia Papagiannaki
        if not self.id:
861 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
862 5ce3ce4f Sofia Papagiannaki
863 64cd4730 Antony Chazapis
    def consume(self):
864 64cd4730 Antony Chazapis
        self.is_consumed = True
865 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
866 64cd4730 Antony Chazapis
        self.save()
867 6c736ed7 Kostas Papadimitriou
868 64cd4730 Antony Chazapis
    def __unicode__(self):
869 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
870 9c01d9d1 Sofia Papagiannaki
871 49790d9d Sofia Papagiannaki
872 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
873 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
874 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
875 49790d9d Sofia Papagiannaki
        """
876 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
877 49790d9d Sofia Papagiannaki
        ``User`` if valid.
878 49790d9d Sofia Papagiannaki

879 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
880 49790d9d Sofia Papagiannaki
        after activating.
881 49790d9d Sofia Papagiannaki

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

884 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
885 49790d9d Sofia Papagiannaki
        return ``None``.
886 49790d9d Sofia Papagiannaki

887 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
888 49790d9d Sofia Papagiannaki

889 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
890 49790d9d Sofia Papagiannaki
        """
891 49790d9d Sofia Papagiannaki
        try:
892 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
893 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
894 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
895 49790d9d Sofia Papagiannaki
                email_change.delete()
896 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
897 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
898 49790d9d Sofia Papagiannaki
            try:
899 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
900 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
901 49790d9d Sofia Papagiannaki
                pass
902 49790d9d Sofia Papagiannaki
            else:
903 ae497612 Olga Brani
                raise ValueError(_(astakos_messages.NEW_EMAIL_ADDR_RESERVED))
904 49790d9d Sofia Papagiannaki
            # update user
905 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
906 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
907 49790d9d Sofia Papagiannaki
            user.save()
908 49790d9d Sofia Papagiannaki
            email_change.delete()
909 49790d9d Sofia Papagiannaki
            return user
910 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
911 ae497612 Olga Brani
            raise ValueError(_(astakos_messages.INVALID_ACTIVATION_KEY))
912 49790d9d Sofia Papagiannaki
913 49790d9d Sofia Papagiannaki
914 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
915 9a06d96f Olga Brani
    new_email_address = models.EmailField(_(u'new e-mail address'),
916 ae497612 Olga Brani
                                          help_text=_(astakos_messages.EMAIL_CHANGE_NEW_ADDR_HELP))
917 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
918 5ce3ce4f Sofia Papagiannaki
        AstakosUser, unique=True, related_name='emailchange_user')
919 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
920 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
921 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
922 49790d9d Sofia Papagiannaki
923 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
924 49790d9d Sofia Papagiannaki
925 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
926 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
927 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
928 ff9290ec Sofia Papagiannaki
929 6b03a847 Sofia Papagiannaki
930 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
931 ca828a10 Sofia Papagiannaki
    """
932 ca828a10 Sofia Papagiannaki
    Model for registring invitations
933 ca828a10 Sofia Papagiannaki
    """
934 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
935 1eec103a Sofia Papagiannaki
    email = models.EmailField()
936 ca828a10 Sofia Papagiannaki
937 5ce3ce4f Sofia Papagiannaki
938 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
939 fc1e2f02 Sofia Papagiannaki
    while True:
940 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
941 fc1e2f02 Sofia Papagiannaki
        try:
942 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
943 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
944 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
945 fc1e2f02 Sofia Papagiannaki
            return code
946 fc1e2f02 Sofia Papagiannaki
947 5ce3ce4f Sofia Papagiannaki
948 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
949 fc1e2f02 Sofia Papagiannaki
    try:
950 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
951 fc1e2f02 Sofia Papagiannaki
        return term
952 fc1e2f02 Sofia Papagiannaki
    except IndexError:
953 fc1e2f02 Sofia Papagiannaki
        pass
954 fc1e2f02 Sofia Papagiannaki
    return None
955 fc1e2f02 Sofia Papagiannaki
956 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
957 ef20ea07 Sofia Papagiannaki
    """
958 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
959 ef20ea07 Sofia Papagiannaki
    """
960 ef20ea07 Sofia Papagiannaki
    third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
961 ef20ea07 Sofia Papagiannaki
    provider = models.CharField('Provider', max_length=255, blank=True)
962 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
963 ef20ea07 Sofia Papagiannaki
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
964 ef20ea07 Sofia Papagiannaki
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
965 ef20ea07 Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
966 ef20ea07 Sofia Papagiannaki
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
967 d2633501 Kostas Papadimitriou
    token = models.CharField('Token', max_length=255, null=True, blank=True)
968 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
969 d2633501 Kostas Papadimitriou
970 678b2236 Sofia Papagiannaki
    class Meta:
971 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
972 ef20ea07 Sofia Papagiannaki
973 ef20ea07 Sofia Papagiannaki
    @property
974 ef20ea07 Sofia Papagiannaki
    def realname(self):
975 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
976 ef20ea07 Sofia Papagiannaki
977 ef20ea07 Sofia Papagiannaki
    @realname.setter
978 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
979 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
980 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
981 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
982 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
983 ef20ea07 Sofia Papagiannaki
        else:
984 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
985 2e90e3ec Kostas Papadimitriou
986 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
987 ef20ea07 Sofia Papagiannaki
        if not self.id:
988 ef20ea07 Sofia Papagiannaki
            # set username
989 ef20ea07 Sofia Papagiannaki
            while not self.username:
990 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
991 ef20ea07 Sofia Papagiannaki
                try:
992 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
993 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
994 ef20ea07 Sofia Papagiannaki
                    self.username = username
995 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
996 ef20ea07 Sofia Papagiannaki
997 d2633501 Kostas Papadimitriou
    def generate_token(self):
998 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
999 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
1000 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
1001 d2633501 Kostas Papadimitriou
1002 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
1003 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1004 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1005 bf0c6de5 Sofia Papagiannaki
1006 5ce3ce4f Sofia Papagiannaki
1007 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
1008 ff9290ec Sofia Papagiannaki
    try:
1009 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
1010 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1011 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
1012 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
1013 ff9290ec Sofia Papagiannaki
        extended_user.save()
1014 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
1015 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
1016 ff9290ec Sofia Papagiannaki
1017 5ce3ce4f Sofia Papagiannaki
1018 fc1e2f02 Sofia Papagiannaki
def fix_superusers(sender, **kwargs):
1019 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
1020 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
1021 ff9290ec Sofia Papagiannaki
    for u in admins:
1022 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
1023 d44f3f89 Kostas Papadimitriou
        if not u.has_auth_provider('local'):
1024 d44f3f89 Kostas Papadimitriou
            u.add_auth_provider('local')
1025 ff9290ec Sofia Papagiannaki
1026 ff9290ec Sofia Papagiannaki
1027 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
1028 aa4109d4 Sofia Papagiannaki
    if not created:
1029 aa4109d4 Sofia Papagiannaki
        return
1030 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
1031 fc1e2f02 Sofia Papagiannaki
1032 5ce3ce4f Sofia Papagiannaki
1033 fc1e2f02 Sofia Papagiannaki
def set_default_group(user):
1034 aa4109d4 Sofia Papagiannaki
    try:
1035 5ce3ce4f Sofia Papagiannaki
        default = AstakosGroup.objects.get(name='default')
1036 5ce3ce4f Sofia Papagiannaki
        Membership(
1037 5ce3ce4f Sofia Papagiannaki
            group=default, person=user, date_joined=datetime.now()).save()
1038 aa4109d4 Sofia Papagiannaki
    except AstakosGroup.DoesNotExist, e:
1039 aa4109d4 Sofia Papagiannaki
        logger.exception(e)
1040 ff9290ec Sofia Papagiannaki
1041 bf0c6de5 Sofia Papagiannaki
1042 fc1e2f02 Sofia Papagiannaki
def astakosuser_pre_save(sender, instance, **kwargs):
1043 fc1e2f02 Sofia Papagiannaki
    instance.aquarium_report = False
1044 fc1e2f02 Sofia Papagiannaki
    instance.new = False
1045 fc1e2f02 Sofia Papagiannaki
    try:
1046 5ce3ce4f Sofia Papagiannaki
        db_instance = AstakosUser.objects.get(id=instance.id)
1047 fc1e2f02 Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1048 fc1e2f02 Sofia Papagiannaki
        # create event
1049 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True
1050 fc1e2f02 Sofia Papagiannaki
        instance.new = True
1051 fc1e2f02 Sofia Papagiannaki
    else:
1052 fc1e2f02 Sofia Papagiannaki
        get = AstakosUser.__getattribute__
1053 fc1e2f02 Sofia Papagiannaki
        l = filter(lambda f: get(db_instance, f) != get(instance, f),
1054 9a06d96f Olga Brani
                   BILLING_FIELDS)
1055 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True if l else False
1056 fc1e2f02 Sofia Papagiannaki
1057 5ce3ce4f Sofia Papagiannaki
1058 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
1059 fc1e2f02 Sofia Papagiannaki
    if instance.aquarium_report:
1060 fc1e2f02 Sofia Papagiannaki
        report_user_event(instance, create=instance.new)
1061 fc1e2f02 Sofia Papagiannaki
    if not created:
1062 fc1e2f02 Sofia Papagiannaki
        return
1063 fc1e2f02 Sofia Papagiannaki
    set_default_group(instance)
1064 fc1e2f02 Sofia Papagiannaki
    # TODO handle socket.error & IOError
1065 fc1e2f02 Sofia Papagiannaki
    register_users((instance,))
1066 fc1e2f02 Sofia Papagiannaki
1067 5ce3ce4f Sofia Papagiannaki
1068 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
1069 bd4f356c Sofia Papagiannaki
    if not created:
1070 bd4f356c Sofia Papagiannaki
        return
1071 bd4f356c Sofia Papagiannaki
    register_resources((instance,))
1072 bd4f356c Sofia Papagiannaki
1073 bd4f356c Sofia Papagiannaki
1074 fc1e2f02 Sofia Papagiannaki
def send_quota_disturbed(sender, instance, **kwargs):
1075 fc1e2f02 Sofia Papagiannaki
    users = []
1076 fc1e2f02 Sofia Papagiannaki
    extend = users.extend
1077 fc1e2f02 Sofia Papagiannaki
    if sender == Membership:
1078 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1079 fc1e2f02 Sofia Papagiannaki
            return
1080 fc1e2f02 Sofia Papagiannaki
        extend([instance.person])
1081 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosUserQuota:
1082 fc1e2f02 Sofia Papagiannaki
        extend([instance.user])
1083 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroupQuota:
1084 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1085 fc1e2f02 Sofia Papagiannaki
            return
1086 fc1e2f02 Sofia Papagiannaki
        extend(instance.group.astakosuser_set.all())
1087 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroup:
1088 fc1e2f02 Sofia Papagiannaki
        if not instance.is_enabled:
1089 fc1e2f02 Sofia Papagiannaki
            return
1090 fc1e2f02 Sofia Papagiannaki
    quota_disturbed.send(sender=sender, users=users)
1091 fc1e2f02 Sofia Papagiannaki
1092 5ce3ce4f Sofia Papagiannaki
1093 fc1e2f02 Sofia Papagiannaki
def on_quota_disturbed(sender, users, **kwargs):
1094 c0b26605 Sofia Papagiannaki
#     print '>>>', locals()
1095 fc1e2f02 Sofia Papagiannaki
    if not users:
1096 fc1e2f02 Sofia Papagiannaki
        return
1097 fc1e2f02 Sofia Papagiannaki
    send_quota(users)
1098 bf0c6de5 Sofia Papagiannaki
1099 bf0c6de5 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
1100 f9aea9c8 Sofia Papagiannaki
    if not instance.auth_token:
1101 bf0c6de5 Sofia Papagiannaki
        instance.renew_token()
1102 bf0c6de5 Sofia Papagiannaki
1103 fc1e2f02 Sofia Papagiannaki
post_syncdb.connect(fix_superusers)
1104 fc1e2f02 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
1105 fc1e2f02 Sofia Papagiannaki
pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
1106 fc1e2f02 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
1107 bd4f356c Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
1108 fc1e2f02 Sofia Papagiannaki
1109 fc1e2f02 Sofia Papagiannaki
quota_disturbed = Signal(providing_args=["users"])
1110 fc1e2f02 Sofia Papagiannaki
quota_disturbed.connect(on_quota_disturbed)
1111 fc1e2f02 Sofia Papagiannaki
1112 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
1113 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=Membership)
1114 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
1115 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosUserQuota)
1116 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1117 a4075f5a root
post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1118 c0b26605 Sofia Papagiannaki
1119 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
1120 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)