Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ f432088a

History | View | Annotate | Download (37.8 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 fc1e2f02 Sofia Papagiannaki
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 5ce3ce4f Sofia Papagiannaki
        propagate_groupmembers_quota.apply_async(
227 5ce3ce4f Sofia Papagiannaki
            args=[self], eta=self.issue_date)
228 5ce3ce4f Sofia Papagiannaki
        propagate_groupmembers_quota.apply_async(
229 5ce3ce4f Sofia Papagiannaki
            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 d2633501 Kostas Papadimitriou
class AstakosUserManager(models.Manager):
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 0905ccd2 Sofia Papagiannaki
    # Use UserManager to get the create_user method, etc.
326 0905ccd2 Sofia Papagiannaki
    objects = UserManager()
327 6c736ed7 Kostas Papadimitriou
328 d2633501 Kostas Papadimitriou
    affiliation = models.CharField('Affiliation', max_length=255, blank=True,
329 d2633501 Kostas Papadimitriou
                                   null=True)
330 d2633501 Kostas Papadimitriou
331 d2633501 Kostas Papadimitriou
    # DEPRECATED FIELDS: provider, third_party_identifier moved in
332 d2633501 Kostas Papadimitriou
    #                    AstakosUserProvider model.
333 d2633501 Kostas Papadimitriou
    provider = models.CharField('Provider', max_length=255, blank=True,
334 d2633501 Kostas Papadimitriou
                                null=True)
335 d2633501 Kostas Papadimitriou
    # ex. screen_name for twitter, eppn for shibboleth
336 d2633501 Kostas Papadimitriou
    third_party_identifier = models.CharField('Third-party identifier',
337 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
338 d2633501 Kostas Papadimitriou
                                              blank=True)
339 d2633501 Kostas Papadimitriou
340 6c736ed7 Kostas Papadimitriou
341 64cd4730 Antony Chazapis
    #for invitations
342 92defad4 Sofia Papagiannaki
    user_level = DEFAULT_USER_LEVEL
343 a196eb7e Sofia Papagiannaki
    level = models.IntegerField('Inviter level', default=user_level)
344 5ce3ce4f Sofia Papagiannaki
    invitations = models.IntegerField(
345 5ce3ce4f Sofia Papagiannaki
        'Invitations left', default=INVITATIONS_PER_LEVEL.get(user_level, 0))
346 6c736ed7 Kostas Papadimitriou
347 64cd4730 Antony Chazapis
    auth_token = models.CharField('Authentication Token', max_length=32,
348 0905ccd2 Sofia Papagiannaki
                                  null=True, blank=True)
349 0905ccd2 Sofia Papagiannaki
    auth_token_created = models.DateTimeField('Token creation date', null=True)
350 5ce3ce4f Sofia Papagiannaki
    auth_token_expires = models.DateTimeField(
351 5ce3ce4f Sofia Papagiannaki
        'Token expiration date', null=True)
352 6c736ed7 Kostas Papadimitriou
353 64cd4730 Antony Chazapis
    updated = models.DateTimeField('Update date')
354 890b0eaf Sofia Papagiannaki
    is_verified = models.BooleanField('Is verified?', default=False)
355 6c736ed7 Kostas Papadimitriou
356 ebd369d0 Sofia Papagiannaki
    email_verified = models.BooleanField('Email verified?', default=False)
357 6c736ed7 Kostas Papadimitriou
358 59f598f1 Sofia Papagiannaki
    has_credits = models.BooleanField('Has credits?', default=False)
359 5ce3ce4f Sofia Papagiannaki
    has_signed_terms = models.BooleanField(
360 9a06d96f Olga Brani
        'I agree with the terms', default=False)
361 5ce3ce4f Sofia Papagiannaki
    date_signed_terms = models.DateTimeField(
362 5ce3ce4f Sofia Papagiannaki
        'Signed terms date', null=True, blank=True)
363 5ce3ce4f Sofia Papagiannaki
364 5ce3ce4f Sofia Papagiannaki
    activation_sent = models.DateTimeField(
365 5ce3ce4f Sofia Papagiannaki
        'Activation sent data', null=True, blank=True)
366 5ce3ce4f Sofia Papagiannaki
367 5ce3ce4f Sofia Papagiannaki
    policy = models.ManyToManyField(
368 5ce3ce4f Sofia Papagiannaki
        Resource, null=True, through='AstakosUserQuota')
369 5ce3ce4f Sofia Papagiannaki
370 5ce3ce4f Sofia Papagiannaki
    astakos_groups = models.ManyToManyField(
371 5ce3ce4f Sofia Papagiannaki
        AstakosGroup, verbose_name=_('agroups'), blank=True,
372 ae497612 Olga Brani
        help_text=_(astakos_messages.ASTAKOSUSER_GROUPS_HELP),
373 8e45d6fd Sofia Papagiannaki
        through='Membership')
374 5ce3ce4f Sofia Papagiannaki
375 18ffbee1 Sofia Papagiannaki
    __has_signed_terms = False
376 9a06d96f Olga Brani
    disturbed_quota = models.BooleanField('Needs quotaholder syncing',
377 9a06d96f Olga Brani
                                           default=False, db_index=True)
378 d2633501 Kostas Papadimitriou
379 d2633501 Kostas Papadimitriou
    objects = AstakosUserManager()
380 5ce3ce4f Sofia Papagiannaki
    owner = models.ManyToManyField(
381 5ce3ce4f Sofia Papagiannaki
        AstakosGroup, related_name='owner', null=True)
382 5ce3ce4f Sofia Papagiannaki
383 74b273d8 Sofia Papagiannaki
    class Meta:
384 74b273d8 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
385 d2633501 Kostas Papadimitriou
386 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
387 18ffbee1 Sofia Papagiannaki
        super(AstakosUser, self).__init__(*args, **kwargs)
388 18ffbee1 Sofia Papagiannaki
        self.__has_signed_terms = self.has_signed_terms
389 a3637508 Sofia Papagiannaki
        if not self.id:
390 18ffbee1 Sofia Papagiannaki
            self.is_active = False
391 5ce3ce4f Sofia Papagiannaki
392 0905ccd2 Sofia Papagiannaki
    @property
393 0905ccd2 Sofia Papagiannaki
    def realname(self):
394 5ce3ce4f Sofia Papagiannaki
        return '%s %s' % (self.first_name, self.last_name)
395 6c736ed7 Kostas Papadimitriou
396 0905ccd2 Sofia Papagiannaki
    @realname.setter
397 0905ccd2 Sofia Papagiannaki
    def realname(self, value):
398 0905ccd2 Sofia Papagiannaki
        parts = value.split(' ')
399 0905ccd2 Sofia Papagiannaki
        if len(parts) == 2:
400 0905ccd2 Sofia Papagiannaki
            self.first_name = parts[0]
401 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[1]
402 0905ccd2 Sofia Papagiannaki
        else:
403 0905ccd2 Sofia Papagiannaki
            self.last_name = parts[0]
404 6c736ed7 Kostas Papadimitriou
405 9a06d96f Olga Brani
    def add_permission(self, pname):
406 9a06d96f Olga Brani
        if self.has_perm(pname):
407 9a06d96f Olga Brani
            return
408 9a06d96f Olga Brani
        p, created = Permission.objects.get_or_create(codename=pname,
409 9a06d96f Olga Brani
                                                      name=pname.capitalize(),
410 9a06d96f Olga Brani
                                                      content_type=content_type)
411 9a06d96f Olga Brani
        self.user_permissions.add(p)
412 9a06d96f Olga Brani
413 9a06d96f Olga Brani
    def remove_permission(self, pname):
414 9a06d96f Olga Brani
        if self.has_perm(pname):
415 9a06d96f Olga Brani
            return
416 9a06d96f Olga Brani
        p = Permission.objects.get(codename=pname,
417 9a06d96f Olga Brani
                                   content_type=content_type)
418 9a06d96f Olga Brani
        self.user_permissions.remove(p)
419 9a06d96f Olga Brani
420 64cd4730 Antony Chazapis
    @property
421 64cd4730 Antony Chazapis
    def invitation(self):
422 64cd4730 Antony Chazapis
        try:
423 9fb8e808 Sofia Papagiannaki
            return Invitation.objects.get(username=self.email)
424 64cd4730 Antony Chazapis
        except Invitation.DoesNotExist:
425 64cd4730 Antony Chazapis
            return None
426 6c736ed7 Kostas Papadimitriou
427 9a06d96f Olga Brani
    def invite(self, email, realname):
428 9a06d96f Olga Brani
        inv = Invitation(inviter=self, username=email, realname=realname)
429 9a06d96f Olga Brani
        inv.save()
430 9a06d96f Olga Brani
        send_invitation(inv)
431 9a06d96f Olga Brani
        self.invitations = max(0, self.invitations - 1)
432 9a06d96f Olga Brani
        self.save()
433 9a06d96f Olga Brani
434 ffb1e7a8 Sofia Papagiannaki
    @property
435 ffb1e7a8 Sofia Papagiannaki
    def quota(self):
436 9a06d96f Olga Brani
        """Returns a dict with the sum of quota limits per resource"""
437 ffb1e7a8 Sofia Papagiannaki
        d = defaultdict(int)
438 9a06d96f Olga Brani
        for q in self.policies:
439 9ee0c6a2 Sofia Papagiannaki
            d[q.resource] += q.uplimit or inf
440 9a06d96f Olga Brani
        for m in self.extended_groups:
441 fc1e2f02 Sofia Papagiannaki
            if not m.is_approved:
442 fc1e2f02 Sofia Papagiannaki
                continue
443 fc1e2f02 Sofia Papagiannaki
            g = m.group
444 ffb1e7a8 Sofia Papagiannaki
            if not g.is_enabled:
445 ffb1e7a8 Sofia Papagiannaki
                continue
446 670de92a Sofia Papagiannaki
            for r, uplimit in g.quota.iteritems():
447 9ee0c6a2 Sofia Papagiannaki
                d[r] += uplimit or inf
448 ffb1e7a8 Sofia Papagiannaki
        # TODO set default for remaining
449 ffb1e7a8 Sofia Papagiannaki
        return d
450 5ce3ce4f Sofia Papagiannaki
451 9a06d96f Olga Brani
    @property
452 9a06d96f Olga Brani
    def policies(self):
453 9a06d96f Olga Brani
        return self.astakosuserquota_set.select_related().all()
454 9a06d96f Olga Brani
455 9a06d96f Olga Brani
    @policies.setter
456 9a06d96f Olga Brani
    def policies(self, policies):
457 9a06d96f Olga Brani
        for p in policies:
458 9a06d96f Olga Brani
            service = policies.get('service', None)
459 9a06d96f Olga Brani
            resource = policies.get('resource', None)
460 9a06d96f Olga Brani
            uplimit = policies.get('uplimit', 0)
461 9a06d96f Olga Brani
            update = policies.get('update', True)
462 9a06d96f Olga Brani
            self.add_policy(service, resource, uplimit, update)
463 9a06d96f Olga Brani
464 9a06d96f Olga Brani
    def add_policy(self, service, resource, uplimit, update=True):
465 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
466 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
467 9a06d96f Olga Brani
        if update:
468 9a06d96f Olga Brani
            AstakosUserQuota.objects.update_or_create(user=self,
469 9a06d96f Olga Brani
                                                      resource=resource,
470 9a06d96f Olga Brani
                                                      defaults={'uplimit': uplimit})
471 9a06d96f Olga Brani
        else:
472 9a06d96f Olga Brani
            q = self.astakosuserquota_set
473 9a06d96f Olga Brani
            q.create(resource=resource, uplimit=uplimit)
474 9a06d96f Olga Brani
475 9a06d96f Olga Brani
    def remove_policy(self, service, resource):
476 9a06d96f Olga Brani
        """Raises ObjectDoesNotExist, IntegrityError"""
477 9a06d96f Olga Brani
        resource = Resource.objects.get(service__name=service, name=resource)
478 9a06d96f Olga Brani
        q = self.policies.get(resource=resource).delete()
479 9a06d96f Olga Brani
480 9a06d96f Olga Brani
    @property
481 9a06d96f Olga Brani
    def extended_groups(self):
482 9a06d96f Olga Brani
        return self.membership_set.select_related().all()
483 9a06d96f Olga Brani
484 9a06d96f Olga Brani
    @extended_groups.setter
485 9a06d96f Olga Brani
    def extended_groups(self, groups):
486 9a06d96f Olga Brani
        #TODO exceptions
487 40a0cd8b Sofia Papagiannaki
        for name in (groups or ()):
488 9a06d96f Olga Brani
            group = AstakosGroup.objects.get(name=name)
489 9a06d96f Olga Brani
            self.membership_set.create(group=group)
490 9a06d96f Olga Brani
491 64cd4730 Antony Chazapis
    def save(self, update_timestamps=True, **kwargs):
492 64cd4730 Antony Chazapis
        if update_timestamps:
493 64cd4730 Antony Chazapis
            if not self.id:
494 0905ccd2 Sofia Papagiannaki
                self.date_joined = datetime.now()
495 64cd4730 Antony Chazapis
            self.updated = datetime.now()
496 5ce3ce4f Sofia Papagiannaki
497 18ffbee1 Sofia Papagiannaki
        # update date_signed_terms if necessary
498 18ffbee1 Sofia Papagiannaki
        if self.__has_signed_terms != self.has_signed_terms:
499 18ffbee1 Sofia Papagiannaki
            self.date_signed_terms = datetime.now()
500 5ce3ce4f Sofia Papagiannaki
501 9c01d9d1 Sofia Papagiannaki
        if not self.id:
502 9c01d9d1 Sofia Papagiannaki
            # set username
503 9c01d9d1 Sofia Papagiannaki
            while not self.username:
504 d2633501 Kostas Papadimitriou
                username =  self.email
505 9c01d9d1 Sofia Papagiannaki
                try:
506 5ce3ce4f Sofia Papagiannaki
                    AstakosUser.objects.get(username=username)
507 aab4d540 Sofia Papagiannaki
                except AstakosUser.DoesNotExist:
508 9c01d9d1 Sofia Papagiannaki
                    self.username = username
509 d2633501 Kostas Papadimitriou
510 591d0505 Sofia Papagiannaki
        self.validate_unique_email_isactive()
511 751d24cf Sofia Papagiannaki
        if self.is_active and self.activation_sent:
512 751d24cf Sofia Papagiannaki
            # reset the activation sent
513 751d24cf Sofia Papagiannaki
            self.activation_sent = None
514 5ce3ce4f Sofia Papagiannaki
515 0905ccd2 Sofia Papagiannaki
        super(AstakosUser, self).save(**kwargs)
516 2e90e3ec Kostas Papadimitriou
517 bf0c6de5 Sofia Papagiannaki
    def renew_token(self, flush_sessions=False, current_key=None):
518 64cd4730 Antony Chazapis
        md5 = hashlib.md5()
519 8f8c43b2 Sofia Papagiannaki
        md5.update(settings.SECRET_KEY)
520 0905ccd2 Sofia Papagiannaki
        md5.update(self.username)
521 64cd4730 Antony Chazapis
        md5.update(self.realname.encode('ascii', 'ignore'))
522 64cd4730 Antony Chazapis
        md5.update(asctime())
523 2e90e3ec Kostas Papadimitriou
524 64cd4730 Antony Chazapis
        self.auth_token = b64encode(md5.digest())
525 64cd4730 Antony Chazapis
        self.auth_token_created = datetime.now()
526 64cd4730 Antony Chazapis
        self.auth_token_expires = self.auth_token_created + \
527 92defad4 Sofia Papagiannaki
                                  timedelta(hours=AUTH_TOKEN_DURATION)
528 bf0c6de5 Sofia Papagiannaki
        if flush_sessions:
529 bf0c6de5 Sofia Papagiannaki
            self.flush_sessions(current_key)
530 111f3da6 Sofia Papagiannaki
        msg = 'Token renewed for %s' % self.email
531 aab4d540 Sofia Papagiannaki
        logger.log(LOGGING_LEVEL, msg)
532 6c736ed7 Kostas Papadimitriou
533 bf0c6de5 Sofia Papagiannaki
    def flush_sessions(self, current_key=None):
534 bf0c6de5 Sofia Papagiannaki
        q = self.sessions
535 bf0c6de5 Sofia Papagiannaki
        if current_key:
536 bf0c6de5 Sofia Papagiannaki
            q = q.exclude(session_key=current_key)
537 2e90e3ec Kostas Papadimitriou
538 bf0c6de5 Sofia Papagiannaki
        keys = q.values_list('session_key', flat=True)
539 bf0c6de5 Sofia Papagiannaki
        if keys:
540 bf0c6de5 Sofia Papagiannaki
            msg = 'Flushing sessions: %s' % ','.join(keys)
541 c0b26605 Sofia Papagiannaki
            logger.log(LOGGING_LEVEL, msg, [])
542 bf0c6de5 Sofia Papagiannaki
        engine = import_module(settings.SESSION_ENGINE)
543 bf0c6de5 Sofia Papagiannaki
        for k in keys:
544 bf0c6de5 Sofia Papagiannaki
            s = engine.SessionStore(k)
545 bf0c6de5 Sofia Papagiannaki
            s.flush()
546 bf0c6de5 Sofia Papagiannaki
547 64cd4730 Antony Chazapis
    def __unicode__(self):
548 3abf6c78 Sofia Papagiannaki
        return '%s (%s)' % (self.realname, self.email)
549 5ce3ce4f Sofia Papagiannaki
550 0a569195 Sofia Papagiannaki
    def conflicting_email(self):
551 5ce3ce4f Sofia Papagiannaki
        q = AstakosUser.objects.exclude(username=self.username)
552 789a5951 Sofia Papagiannaki
        q = q.filter(email__iexact=self.email)
553 0a569195 Sofia Papagiannaki
        if q.count() != 0:
554 0a569195 Sofia Papagiannaki
            return True
555 0a569195 Sofia Papagiannaki
        return False
556 5ce3ce4f Sofia Papagiannaki
557 591d0505 Sofia Papagiannaki
    def validate_unique_email_isactive(self):
558 0a569195 Sofia Papagiannaki
        """
559 0a569195 Sofia Papagiannaki
        Implements a unique_together constraint for email and is_active fields.
560 0a569195 Sofia Papagiannaki
        """
561 e6759494 Sofia Papagiannaki
        q = AstakosUser.objects.all()
562 0a569195 Sofia Papagiannaki
        q = q.filter(email = self.email)
563 0a569195 Sofia Papagiannaki
        q = q.filter(is_active = self.is_active)
564 e6759494 Sofia Papagiannaki
        if self.id:
565 e6759494 Sofia Papagiannaki
            q = q.filter(~Q(id = self.id))
566 0a569195 Sofia Papagiannaki
        if q.count() != 0:
567 ae497612 Olga Brani
            raise ValidationError({'__all__': [_(astakos_messages.UNIQUE_EMAIL_IS_ACTIVE_CONSTRAIN_ERR)]})
568 5ce3ce4f Sofia Papagiannaki
569 fcf90160 Sofia Papagiannaki
    @property
570 09e7393c Sofia Papagiannaki
    def signed_terms(self):
571 09e7393c Sofia Papagiannaki
        term = get_latest_terms()
572 09e7393c Sofia Papagiannaki
        if not term:
573 09e7393c Sofia Papagiannaki
            return True
574 09e7393c Sofia Papagiannaki
        if not self.has_signed_terms:
575 09e7393c Sofia Papagiannaki
            return False
576 09e7393c Sofia Papagiannaki
        if not self.date_signed_terms:
577 09e7393c Sofia Papagiannaki
            return False
578 09e7393c Sofia Papagiannaki
        if self.date_signed_terms < term.date:
579 09e7393c Sofia Papagiannaki
            self.has_signed_terms = False
580 f0f92965 Sofia Papagiannaki
            self.date_signed_terms = None
581 09e7393c Sofia Papagiannaki
            self.save()
582 09e7393c Sofia Papagiannaki
            return False
583 09e7393c Sofia Papagiannaki
        return True
584 09e7393c Sofia Papagiannaki
585 d2633501 Kostas Papadimitriou
    def set_invitations_level(self):
586 d2633501 Kostas Papadimitriou
        """
587 d2633501 Kostas Papadimitriou
        Update user invitation level
588 d2633501 Kostas Papadimitriou
        """
589 d2633501 Kostas Papadimitriou
        level = self.invitation.inviter.level + 1
590 d2633501 Kostas Papadimitriou
        self.level = level
591 d2633501 Kostas Papadimitriou
        self.invitations = INVITATIONS_PER_LEVEL.get(level, 0)
592 d2633501 Kostas Papadimitriou
593 d2633501 Kostas Papadimitriou
    def can_login_with_auth_provider(self, provider):
594 d2633501 Kostas Papadimitriou
        if not self.has_auth_provider(provider):
595 d2633501 Kostas Papadimitriou
            return False
596 d2633501 Kostas Papadimitriou
        else:
597 d2633501 Kostas Papadimitriou
            return auth_providers.get_provider(provider).is_available_for_login()
598 d2633501 Kostas Papadimitriou
599 f432088a Kostas Papadimitriou
    def can_add_auth_provider(self, provider, **kwargs):
600 d2633501 Kostas Papadimitriou
        provider_settings = auth_providers.get_provider(provider)
601 d2633501 Kostas Papadimitriou
        if not provider_settings.is_available_for_login():
602 d2633501 Kostas Papadimitriou
            return False
603 f432088a Kostas Papadimitriou
604 d2633501 Kostas Papadimitriou
        if self.has_auth_provider(provider) and \
605 d2633501 Kostas Papadimitriou
           provider_settings.one_per_user:
606 d2633501 Kostas Papadimitriou
            return False
607 f432088a Kostas Papadimitriou
608 f432088a Kostas Papadimitriou
        if 'identifier' in kwargs:
609 f432088a Kostas Papadimitriou
            try:
610 f432088a Kostas Papadimitriou
                # provider with specified params already exist
611 f432088a Kostas Papadimitriou
                existing_user = AstakosUser.objects.get_auth_provider_user(provider,
612 f432088a Kostas Papadimitriou
                                                                   **kwargs)
613 f432088a Kostas Papadimitriou
            except AstakosUser.DoesNotExist:
614 f432088a Kostas Papadimitriou
                return True
615 f432088a Kostas Papadimitriou
            else:
616 f432088a Kostas Papadimitriou
                return False
617 f432088a Kostas Papadimitriou
618 d2633501 Kostas Papadimitriou
        return True
619 d2633501 Kostas Papadimitriou
620 d2633501 Kostas Papadimitriou
    def can_remove_auth_provider(self, provider):
621 d2633501 Kostas Papadimitriou
        if len(self.get_active_auth_providers()) <= 1:
622 d2633501 Kostas Papadimitriou
            return False
623 d2633501 Kostas Papadimitriou
        return True
624 d2633501 Kostas Papadimitriou
625 d2633501 Kostas Papadimitriou
    def can_change_password(self):
626 d2633501 Kostas Papadimitriou
        return self.has_auth_provider('local', auth_backend='astakos')
627 d2633501 Kostas Papadimitriou
628 d2633501 Kostas Papadimitriou
    def has_auth_provider(self, provider, **kwargs):
629 d2633501 Kostas Papadimitriou
        return bool(self.auth_providers.filter(module=provider,
630 d2633501 Kostas Papadimitriou
                                               **kwargs).count())
631 d2633501 Kostas Papadimitriou
632 d2633501 Kostas Papadimitriou
    def add_auth_provider(self, provider, **kwargs):
633 f432088a Kostas Papadimitriou
        if self.can_add_auth_provider(provider, **kwargs):
634 f432088a Kostas Papadimitriou
            self.auth_providers.create(module=provider, active=True, **kwargs)
635 f432088a Kostas Papadimitriou
        else:
636 f432088a Kostas Papadimitriou
            raise Exception('Cannot add provider')
637 d2633501 Kostas Papadimitriou
638 d2633501 Kostas Papadimitriou
    def add_pending_auth_provider(self, pending):
639 d2633501 Kostas Papadimitriou
        """
640 d2633501 Kostas Papadimitriou
        Convert PendingThirdPartyUser object to AstakosUserAuthProvider entry for
641 d2633501 Kostas Papadimitriou
        the current user.
642 d2633501 Kostas Papadimitriou
        """
643 d2633501 Kostas Papadimitriou
        if not isinstance(pending, PendingThirdPartyUser):
644 d2633501 Kostas Papadimitriou
            pending = PendingThirdPartyUser.objects.get(token=pending)
645 d2633501 Kostas Papadimitriou
646 d2633501 Kostas Papadimitriou
        provider = self.add_auth_provider(pending.provider,
647 d2633501 Kostas Papadimitriou
                               identifier=pending.third_party_identifier)
648 d2633501 Kostas Papadimitriou
649 d2633501 Kostas Papadimitriou
        if email_re.match(pending.email) and pending.email != self.email:
650 d2633501 Kostas Papadimitriou
            self.additionalmail_set.get_or_create(email=pending.email)
651 d2633501 Kostas Papadimitriou
652 d2633501 Kostas Papadimitriou
        pending.delete()
653 d2633501 Kostas Papadimitriou
        return provider
654 d2633501 Kostas Papadimitriou
655 d2633501 Kostas Papadimitriou
    def remove_auth_provider(self, provider, **kwargs):
656 d2633501 Kostas Papadimitriou
        self.auth_providers.get(module=provider, **kwargs).delete()
657 d2633501 Kostas Papadimitriou
658 d2633501 Kostas Papadimitriou
    # user urls
659 d2633501 Kostas Papadimitriou
    def get_resend_activation_url(self):
660 d2633501 Kostas Papadimitriou
        return reverse('send_activation', {'user_id': self.pk})
661 d2633501 Kostas Papadimitriou
662 d2633501 Kostas Papadimitriou
    def get_activation_url(self, nxt=False):
663 d2633501 Kostas Papadimitriou
        url = "%s?auth=%s" % (reverse('astakos.im.views.activate'),
664 d2633501 Kostas Papadimitriou
                                 quote(self.auth_token))
665 d2633501 Kostas Papadimitriou
        if nxt:
666 d2633501 Kostas Papadimitriou
            url += "&next=%s" % quote(nxt)
667 d2633501 Kostas Papadimitriou
        return url
668 d2633501 Kostas Papadimitriou
669 d2633501 Kostas Papadimitriou
    def get_password_reset_url(self, token_generator=default_token_generator):
670 d2633501 Kostas Papadimitriou
        return reverse('django.contrib.auth.views.password_reset_confirm',
671 d2633501 Kostas Papadimitriou
                          kwargs={'uidb36':int_to_base36(self.id),
672 d2633501 Kostas Papadimitriou
                                  'token':token_generator.make_token(self)})
673 d2633501 Kostas Papadimitriou
674 d2633501 Kostas Papadimitriou
    def get_auth_providers(self):
675 d2633501 Kostas Papadimitriou
        return self.auth_providers.all()
676 d2633501 Kostas Papadimitriou
677 d2633501 Kostas Papadimitriou
    def get_available_auth_providers(self):
678 d2633501 Kostas Papadimitriou
        """
679 d2633501 Kostas Papadimitriou
        Returns a list of providers available for user to connect to.
680 d2633501 Kostas Papadimitriou
        """
681 d2633501 Kostas Papadimitriou
        providers = []
682 d2633501 Kostas Papadimitriou
        for module, provider_settings in auth_providers.PROVIDERS.iteritems():
683 f432088a Kostas Papadimitriou
            if self.can_add_auth_provider(module):
684 d2633501 Kostas Papadimitriou
                providers.append(provider_settings(self))
685 d2633501 Kostas Papadimitriou
686 d2633501 Kostas Papadimitriou
        return providers
687 d2633501 Kostas Papadimitriou
688 d2633501 Kostas Papadimitriou
    def get_active_auth_providers(self):
689 d2633501 Kostas Papadimitriou
        providers = []
690 d2633501 Kostas Papadimitriou
        for provider in self.auth_providers.active():
691 d2633501 Kostas Papadimitriou
            if auth_providers.get_provider(provider.module).is_available_for_login():
692 d2633501 Kostas Papadimitriou
                providers.append(provider)
693 d2633501 Kostas Papadimitriou
        return providers
694 d2633501 Kostas Papadimitriou
695 d2633501 Kostas Papadimitriou
696 d2633501 Kostas Papadimitriou
class AstakosUserAuthProviderManager(models.Manager):
697 d2633501 Kostas Papadimitriou
698 d2633501 Kostas Papadimitriou
    def active(self):
699 d2633501 Kostas Papadimitriou
        return self.filter(active=True)
700 d2633501 Kostas Papadimitriou
701 d2633501 Kostas Papadimitriou
702 d2633501 Kostas Papadimitriou
class AstakosUserAuthProvider(models.Model):
703 d2633501 Kostas Papadimitriou
    """
704 d2633501 Kostas Papadimitriou
    Available user authentication methods.
705 d2633501 Kostas Papadimitriou
    """
706 d2633501 Kostas Papadimitriou
    affiliation = models.CharField('Affiliation', max_length=255, blank=True,
707 d2633501 Kostas Papadimitriou
                                   null=True, default=None)
708 d2633501 Kostas Papadimitriou
    user = models.ForeignKey(AstakosUser, related_name='auth_providers')
709 d2633501 Kostas Papadimitriou
    module = models.CharField('Provider', max_length=255, blank=False,
710 d2633501 Kostas Papadimitriou
                                default='local')
711 d2633501 Kostas Papadimitriou
    identifier = models.CharField('Third-party identifier',
712 d2633501 Kostas Papadimitriou
                                              max_length=255, null=True,
713 d2633501 Kostas Papadimitriou
                                              blank=True)
714 d2633501 Kostas Papadimitriou
    active = models.BooleanField(default=True)
715 d2633501 Kostas Papadimitriou
    auth_backend = models.CharField('Backend', max_length=255, blank=False,
716 d2633501 Kostas Papadimitriou
                                   default='astakos')
717 d2633501 Kostas Papadimitriou
718 d2633501 Kostas Papadimitriou
    objects = AstakosUserAuthProviderManager()
719 d2633501 Kostas Papadimitriou
720 d2633501 Kostas Papadimitriou
    class Meta:
721 d2633501 Kostas Papadimitriou
        unique_together = (('identifier', 'module', 'user'), )
722 d2633501 Kostas Papadimitriou
723 d2633501 Kostas Papadimitriou
    @property
724 d2633501 Kostas Papadimitriou
    def settings(self):
725 d2633501 Kostas Papadimitriou
        return auth_providers.get_provider(self.module)
726 d2633501 Kostas Papadimitriou
727 d2633501 Kostas Papadimitriou
    @property
728 d2633501 Kostas Papadimitriou
    def details_display(self):
729 d2633501 Kostas Papadimitriou
        return self.settings.details_tpl % self.__dict__
730 d2633501 Kostas Papadimitriou
731 d2633501 Kostas Papadimitriou
    def can_remove(self):
732 d2633501 Kostas Papadimitriou
        return self.user.can_remove_auth_provider(self.module)
733 d2633501 Kostas Papadimitriou
734 d2633501 Kostas Papadimitriou
    def delete(self, *args, **kwargs):
735 d2633501 Kostas Papadimitriou
        ret = super(AstakosUserAuthProvider, self).delete(*args, **kwargs)
736 d2633501 Kostas Papadimitriou
        self.user.set_unusable_password()
737 d2633501 Kostas Papadimitriou
        self.user.save()
738 d2633501 Kostas Papadimitriou
        return ret
739 d2633501 Kostas Papadimitriou
740 f432088a Kostas Papadimitriou
    def __repr__(self):
741 f432088a Kostas Papadimitriou
        return '<AstakosUserAuthProvider %s:%s>' % (self.module, self.identifier)
742 f432088a Kostas Papadimitriou
743 d2633501 Kostas Papadimitriou
744 8e45d6fd Sofia Papagiannaki
class Membership(models.Model):
745 8e45d6fd Sofia Papagiannaki
    person = models.ForeignKey(AstakosUser)
746 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup)
747 01ac12d5 Sofia Papagiannaki
    date_requested = models.DateField(default=datetime.now(), blank=True)
748 01ac12d5 Sofia Papagiannaki
    date_joined = models.DateField(null=True, db_index=True, blank=True)
749 5ce3ce4f Sofia Papagiannaki
750 8e45d6fd Sofia Papagiannaki
    class Meta:
751 8e45d6fd Sofia Papagiannaki
        unique_together = ("person", "group")
752 5ce3ce4f Sofia Papagiannaki
753 ffb1e7a8 Sofia Papagiannaki
    def save(self, *args, **kwargs):
754 28252c7f Sofia Papagiannaki
        if not self.id:
755 28252c7f Sofia Papagiannaki
            if not self.group.moderation_enabled:
756 28252c7f Sofia Papagiannaki
                self.date_joined = datetime.now()
757 ffb1e7a8 Sofia Papagiannaki
        super(Membership, self).save(*args, **kwargs)
758 5ce3ce4f Sofia Papagiannaki
759 8e45d6fd Sofia Papagiannaki
    @property
760 8e45d6fd Sofia Papagiannaki
    def is_approved(self):
761 8e45d6fd Sofia Papagiannaki
        if self.date_joined:
762 8e45d6fd Sofia Papagiannaki
            return True
763 8e45d6fd Sofia Papagiannaki
        return False
764 5ce3ce4f Sofia Papagiannaki
765 01ac12d5 Sofia Papagiannaki
    def approve(self):
766 c0b26605 Sofia Papagiannaki
        if self.is_approved:
767 c0b26605 Sofia Papagiannaki
            return
768 ae497612 Olga Brani
        if self.group.max_participants:
769 a4075f5a root
            assert len(self.group.approved_members) + 1 <= self.group.max_participants, \
770 c0b26605 Sofia Papagiannaki
            'Maximum participant number has been reached.'
771 01ac12d5 Sofia Papagiannaki
        self.date_joined = datetime.now()
772 01ac12d5 Sofia Papagiannaki
        self.save()
773 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=(self.person,))
774 5ce3ce4f Sofia Papagiannaki
775 01ac12d5 Sofia Papagiannaki
    def disapprove(self):
776 01ac12d5 Sofia Papagiannaki
        self.delete()
777 fc1e2f02 Sofia Papagiannaki
        quota_disturbed.send(sender=self, users=(self.person,))
778 8e45d6fd Sofia Papagiannaki
779 9a06d96f Olga Brani
class AstakosQuotaManager(models.Manager):
780 9a06d96f Olga Brani
    def _update_or_create(self, **kwargs):
781 9a06d96f Olga Brani
        assert kwargs, \
782 9a06d96f Olga Brani
            'update_or_create() must be passed at least one keyword argument'
783 9a06d96f Olga Brani
        obj, created = self.get_or_create(**kwargs)
784 9a06d96f Olga Brani
        defaults = kwargs.pop('defaults', {})
785 9a06d96f Olga Brani
        if created:
786 9a06d96f Olga Brani
            return obj, True, False
787 9a06d96f Olga Brani
        else:
788 9a06d96f Olga Brani
            try:
789 9a06d96f Olga Brani
                params = dict(
790 9a06d96f Olga Brani
                    [(k, v) for k, v in kwargs.items() if '__' not in k])
791 9a06d96f Olga Brani
                params.update(defaults)
792 9a06d96f Olga Brani
                for attr, val in params.items():
793 9a06d96f Olga Brani
                    if hasattr(obj, attr):
794 9a06d96f Olga Brani
                        setattr(obj, attr, val)
795 9a06d96f Olga Brani
                sid = transaction.savepoint()
796 9a06d96f Olga Brani
                obj.save(force_update=True)
797 9a06d96f Olga Brani
                transaction.savepoint_commit(sid)
798 9a06d96f Olga Brani
                return obj, False, True
799 9a06d96f Olga Brani
            except IntegrityError, e:
800 9a06d96f Olga Brani
                transaction.savepoint_rollback(sid)
801 9a06d96f Olga Brani
                try:
802 9a06d96f Olga Brani
                    return self.get(**kwargs), False, False
803 9a06d96f Olga Brani
                except self.model.DoesNotExist:
804 9a06d96f Olga Brani
                    raise e
805 9a06d96f Olga Brani
806 9a06d96f Olga Brani
    update_or_create = _update_or_create
807 5ce3ce4f Sofia Papagiannaki
808 8e45d6fd Sofia Papagiannaki
class AstakosGroupQuota(models.Model):
809 9a06d96f Olga Brani
    objects = AstakosQuotaManager()
810 7507ea03 root
    limit = models.PositiveIntegerField('Limit', null=True)    # obsolete field
811 670de92a Sofia Papagiannaki
    uplimit = models.BigIntegerField('Up limit', null=True)
812 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
813 8e45d6fd Sofia Papagiannaki
    group = models.ForeignKey(AstakosGroup, blank=True)
814 5ce3ce4f Sofia Papagiannaki
815 8e45d6fd Sofia Papagiannaki
    class Meta:
816 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "group")
817 8e45d6fd Sofia Papagiannaki
818 8e45d6fd Sofia Papagiannaki
class AstakosUserQuota(models.Model):
819 9a06d96f Olga Brani
    objects = AstakosQuotaManager()
820 7507ea03 root
    limit = models.PositiveIntegerField('Limit', null=True)    # obsolete field
821 670de92a Sofia Papagiannaki
    uplimit = models.BigIntegerField('Up limit', null=True)
822 8e45d6fd Sofia Papagiannaki
    resource = models.ForeignKey(Resource)
823 8e45d6fd Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser)
824 5ce3ce4f Sofia Papagiannaki
825 8e45d6fd Sofia Papagiannaki
    class Meta:
826 8e45d6fd Sofia Papagiannaki
        unique_together = ("resource", "user")
827 09e7393c Sofia Papagiannaki
828 5ce3ce4f Sofia Papagiannaki
829 270dd48d Sofia Papagiannaki
class ApprovalTerms(models.Model):
830 270dd48d Sofia Papagiannaki
    """
831 270dd48d Sofia Papagiannaki
    Model for approval terms
832 270dd48d Sofia Papagiannaki
    """
833 6c736ed7 Kostas Papadimitriou
834 5ce3ce4f Sofia Papagiannaki
    date = models.DateTimeField(
835 5ce3ce4f Sofia Papagiannaki
        'Issue date', db_index=True, default=datetime.now())
836 270dd48d Sofia Papagiannaki
    location = models.CharField('Terms location', max_length=255)
837 270dd48d Sofia Papagiannaki
838 5ce3ce4f Sofia Papagiannaki
839 64cd4730 Antony Chazapis
class Invitation(models.Model):
840 890b0eaf Sofia Papagiannaki
    """
841 890b0eaf Sofia Papagiannaki
    Model for registring invitations
842 890b0eaf Sofia Papagiannaki
    """
843 0905ccd2 Sofia Papagiannaki
    inviter = models.ForeignKey(AstakosUser, related_name='invitations_sent',
844 64cd4730 Antony Chazapis
                                null=True)
845 64cd4730 Antony Chazapis
    realname = models.CharField('Real name', max_length=255)
846 ebd369d0 Sofia Papagiannaki
    username = models.CharField('Unique ID', max_length=255, unique=True)
847 64cd4730 Antony Chazapis
    code = models.BigIntegerField('Invitation code', db_index=True)
848 64cd4730 Antony Chazapis
    is_consumed = models.BooleanField('Consumed?', default=False)
849 64cd4730 Antony Chazapis
    created = models.DateTimeField('Creation date', auto_now_add=True)
850 64cd4730 Antony Chazapis
    consumed = models.DateTimeField('Consumption date', null=True, blank=True)
851 5ce3ce4f Sofia Papagiannaki
852 18ffbee1 Sofia Papagiannaki
    def __init__(self, *args, **kwargs):
853 18ffbee1 Sofia Papagiannaki
        super(Invitation, self).__init__(*args, **kwargs)
854 8f5a3a06 Sofia Papagiannaki
        if not self.id:
855 8f5a3a06 Sofia Papagiannaki
            self.code = _generate_invitation_code()
856 5ce3ce4f Sofia Papagiannaki
857 64cd4730 Antony Chazapis
    def consume(self):
858 64cd4730 Antony Chazapis
        self.is_consumed = True
859 64cd4730 Antony Chazapis
        self.consumed = datetime.now()
860 64cd4730 Antony Chazapis
        self.save()
861 6c736ed7 Kostas Papadimitriou
862 64cd4730 Antony Chazapis
    def __unicode__(self):
863 0905ccd2 Sofia Papagiannaki
        return '%s -> %s [%d]' % (self.inviter, self.username, self.code)
864 9c01d9d1 Sofia Papagiannaki
865 49790d9d Sofia Papagiannaki
866 49790d9d Sofia Papagiannaki
class EmailChangeManager(models.Manager):
867 49790d9d Sofia Papagiannaki
    @transaction.commit_on_success
868 49790d9d Sofia Papagiannaki
    def change_email(self, activation_key):
869 49790d9d Sofia Papagiannaki
        """
870 49790d9d Sofia Papagiannaki
        Validate an activation key and change the corresponding
871 49790d9d Sofia Papagiannaki
        ``User`` if valid.
872 49790d9d Sofia Papagiannaki

873 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
874 49790d9d Sofia Papagiannaki
        after activating.
875 49790d9d Sofia Papagiannaki

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

878 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
879 49790d9d Sofia Papagiannaki
        return ``None``.
880 49790d9d Sofia Papagiannaki

881 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
882 49790d9d Sofia Papagiannaki

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