Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / models.py @ 5e992f29

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

957 49790d9d Sofia Papagiannaki
        If the key is valid and has not expired, return the ``User``
958 49790d9d Sofia Papagiannaki
        after activating.
959 49790d9d Sofia Papagiannaki

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

962 49790d9d Sofia Papagiannaki
        If the key is valid but the ``User`` is already active,
963 49790d9d Sofia Papagiannaki
        return ``None``.
964 49790d9d Sofia Papagiannaki

965 49790d9d Sofia Papagiannaki
        After successful email change the activation record is deleted.
966 49790d9d Sofia Papagiannaki

967 49790d9d Sofia Papagiannaki
        Throws ValueError if there is already
968 49790d9d Sofia Papagiannaki
        """
969 49790d9d Sofia Papagiannaki
        try:
970 5ce3ce4f Sofia Papagiannaki
            email_change = self.model.objects.get(
971 5ce3ce4f Sofia Papagiannaki
                activation_key=activation_key)
972 49790d9d Sofia Papagiannaki
            if email_change.activation_key_expired():
973 49790d9d Sofia Papagiannaki
                email_change.delete()
974 49790d9d Sofia Papagiannaki
                raise EmailChange.DoesNotExist
975 49790d9d Sofia Papagiannaki
            # is there an active user with this address?
976 49790d9d Sofia Papagiannaki
            try:
977 789a5951 Sofia Papagiannaki
                AstakosUser.objects.get(email__iexact=email_change.new_email_address)
978 49790d9d Sofia Papagiannaki
            except AstakosUser.DoesNotExist:
979 49790d9d Sofia Papagiannaki
                pass
980 49790d9d Sofia Papagiannaki
            else:
981 ae497612 Olga Brani
                raise ValueError(_(astakos_messages.NEW_EMAIL_ADDR_RESERVED))
982 49790d9d Sofia Papagiannaki
            # update user
983 49790d9d Sofia Papagiannaki
            user = AstakosUser.objects.get(pk=email_change.user_id)
984 49790d9d Sofia Papagiannaki
            user.email = email_change.new_email_address
985 49790d9d Sofia Papagiannaki
            user.save()
986 49790d9d Sofia Papagiannaki
            email_change.delete()
987 49790d9d Sofia Papagiannaki
            return user
988 49790d9d Sofia Papagiannaki
        except EmailChange.DoesNotExist:
989 ae497612 Olga Brani
            raise ValueError(_(astakos_messages.INVALID_ACTIVATION_KEY))
990 49790d9d Sofia Papagiannaki
991 49790d9d Sofia Papagiannaki
992 49790d9d Sofia Papagiannaki
class EmailChange(models.Model):
993 9a06d96f Olga Brani
    new_email_address = models.EmailField(_(u'new e-mail address'),
994 ae497612 Olga Brani
                                          help_text=_(astakos_messages.EMAIL_CHANGE_NEW_ADDR_HELP))
995 5ce3ce4f Sofia Papagiannaki
    user = models.ForeignKey(
996 5ce3ce4f Sofia Papagiannaki
        AstakosUser, unique=True, related_name='emailchange_user')
997 49790d9d Sofia Papagiannaki
    requested_at = models.DateTimeField(default=datetime.now())
998 5ce3ce4f Sofia Papagiannaki
    activation_key = models.CharField(
999 5ce3ce4f Sofia Papagiannaki
        max_length=40, unique=True, db_index=True)
1000 49790d9d Sofia Papagiannaki
1001 49790d9d Sofia Papagiannaki
    objects = EmailChangeManager()
1002 49790d9d Sofia Papagiannaki
1003 49790d9d Sofia Papagiannaki
    def activation_key_expired(self):
1004 49790d9d Sofia Papagiannaki
        expiration_date = timedelta(days=EMAILCHANGE_ACTIVATION_DAYS)
1005 ff9290ec Sofia Papagiannaki
        return self.requested_at + expiration_date < datetime.now()
1006 ff9290ec Sofia Papagiannaki
1007 6b03a847 Sofia Papagiannaki
1008 ca828a10 Sofia Papagiannaki
class AdditionalMail(models.Model):
1009 ca828a10 Sofia Papagiannaki
    """
1010 ca828a10 Sofia Papagiannaki
    Model for registring invitations
1011 ca828a10 Sofia Papagiannaki
    """
1012 ca828a10 Sofia Papagiannaki
    owner = models.ForeignKey(AstakosUser)
1013 1eec103a Sofia Papagiannaki
    email = models.EmailField()
1014 ca828a10 Sofia Papagiannaki
1015 5ce3ce4f Sofia Papagiannaki
1016 fc1e2f02 Sofia Papagiannaki
def _generate_invitation_code():
1017 fc1e2f02 Sofia Papagiannaki
    while True:
1018 5ce3ce4f Sofia Papagiannaki
        code = randint(1, 2L ** 63 - 1)
1019 fc1e2f02 Sofia Papagiannaki
        try:
1020 fc1e2f02 Sofia Papagiannaki
            Invitation.objects.get(code=code)
1021 fc1e2f02 Sofia Papagiannaki
            # An invitation with this code already exists, try again
1022 fc1e2f02 Sofia Papagiannaki
        except Invitation.DoesNotExist:
1023 fc1e2f02 Sofia Papagiannaki
            return code
1024 fc1e2f02 Sofia Papagiannaki
1025 5ce3ce4f Sofia Papagiannaki
1026 fc1e2f02 Sofia Papagiannaki
def get_latest_terms():
1027 fc1e2f02 Sofia Papagiannaki
    try:
1028 fc1e2f02 Sofia Papagiannaki
        term = ApprovalTerms.objects.order_by('-id')[0]
1029 fc1e2f02 Sofia Papagiannaki
        return term
1030 fc1e2f02 Sofia Papagiannaki
    except IndexError:
1031 fc1e2f02 Sofia Papagiannaki
        pass
1032 fc1e2f02 Sofia Papagiannaki
    return None
1033 fc1e2f02 Sofia Papagiannaki
1034 ef20ea07 Sofia Papagiannaki
class PendingThirdPartyUser(models.Model):
1035 ef20ea07 Sofia Papagiannaki
    """
1036 ef20ea07 Sofia Papagiannaki
    Model for registring successful third party user authentications
1037 ef20ea07 Sofia Papagiannaki
    """
1038 ef20ea07 Sofia Papagiannaki
    third_party_identifier = models.CharField('Third-party identifier', max_length=255, null=True, blank=True)
1039 ef20ea07 Sofia Papagiannaki
    provider = models.CharField('Provider', max_length=255, blank=True)
1040 678b2236 Sofia Papagiannaki
    email = models.EmailField(_('e-mail address'), blank=True, null=True)
1041 ef20ea07 Sofia Papagiannaki
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
1042 ef20ea07 Sofia Papagiannaki
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
1043 ef20ea07 Sofia Papagiannaki
    affiliation = models.CharField('Affiliation', max_length=255, blank=True)
1044 ef20ea07 Sofia Papagiannaki
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
1045 d2633501 Kostas Papadimitriou
    token = models.CharField('Token', max_length=255, null=True, blank=True)
1046 d2633501 Kostas Papadimitriou
    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
1047 c630fee6 Kostas Papadimitriou
    info = models.TextField(default="", null=True, blank=True)
1048 d2633501 Kostas Papadimitriou
1049 678b2236 Sofia Papagiannaki
    class Meta:
1050 678b2236 Sofia Papagiannaki
        unique_together = ("provider", "third_party_identifier")
1051 ef20ea07 Sofia Papagiannaki
1052 c630fee6 Kostas Papadimitriou
    def get_user_instance(self):
1053 c630fee6 Kostas Papadimitriou
        d = self.__dict__
1054 c630fee6 Kostas Papadimitriou
        d.pop('_state', None)
1055 c630fee6 Kostas Papadimitriou
        d.pop('id', None)
1056 c630fee6 Kostas Papadimitriou
        d.pop('token', None)
1057 c630fee6 Kostas Papadimitriou
        d.pop('created', None)
1058 c630fee6 Kostas Papadimitriou
        d.pop('info', None)
1059 c630fee6 Kostas Papadimitriou
        user = AstakosUser(**d)
1060 c630fee6 Kostas Papadimitriou
1061 c630fee6 Kostas Papadimitriou
        return user
1062 c630fee6 Kostas Papadimitriou
1063 ef20ea07 Sofia Papagiannaki
    @property
1064 ef20ea07 Sofia Papagiannaki
    def realname(self):
1065 ef20ea07 Sofia Papagiannaki
        return '%s %s' %(self.first_name, self.last_name)
1066 ef20ea07 Sofia Papagiannaki
1067 ef20ea07 Sofia Papagiannaki
    @realname.setter
1068 ef20ea07 Sofia Papagiannaki
    def realname(self, value):
1069 ef20ea07 Sofia Papagiannaki
        parts = value.split(' ')
1070 ef20ea07 Sofia Papagiannaki
        if len(parts) == 2:
1071 ef20ea07 Sofia Papagiannaki
            self.first_name = parts[0]
1072 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[1]
1073 ef20ea07 Sofia Papagiannaki
        else:
1074 ef20ea07 Sofia Papagiannaki
            self.last_name = parts[0]
1075 2e90e3ec Kostas Papadimitriou
1076 ef20ea07 Sofia Papagiannaki
    def save(self, **kwargs):
1077 ef20ea07 Sofia Papagiannaki
        if not self.id:
1078 ef20ea07 Sofia Papagiannaki
            # set username
1079 ef20ea07 Sofia Papagiannaki
            while not self.username:
1080 ef20ea07 Sofia Papagiannaki
                username =  uuid.uuid4().hex[:30]
1081 ef20ea07 Sofia Papagiannaki
                try:
1082 ef20ea07 Sofia Papagiannaki
                    AstakosUser.objects.get(username = username)
1083 ef20ea07 Sofia Papagiannaki
                except AstakosUser.DoesNotExist, e:
1084 ef20ea07 Sofia Papagiannaki
                    self.username = username
1085 ef20ea07 Sofia Papagiannaki
        super(PendingThirdPartyUser, self).save(**kwargs)
1086 ef20ea07 Sofia Papagiannaki
1087 d2633501 Kostas Papadimitriou
    def generate_token(self):
1088 d2633501 Kostas Papadimitriou
        self.password = self.third_party_identifier
1089 d2633501 Kostas Papadimitriou
        self.last_login = datetime.now()
1090 d2633501 Kostas Papadimitriou
        self.token = default_token_generator.make_token(self)
1091 d2633501 Kostas Papadimitriou
1092 bf0c6de5 Sofia Papagiannaki
class SessionCatalog(models.Model):
1093 bf0c6de5 Sofia Papagiannaki
    session_key = models.CharField(_('session key'), max_length=40)
1094 bf0c6de5 Sofia Papagiannaki
    user = models.ForeignKey(AstakosUser, related_name='sessions', null=True)
1095 bf0c6de5 Sofia Papagiannaki
1096 5ce3ce4f Sofia Papagiannaki
1097 ff9290ec Sofia Papagiannaki
def create_astakos_user(u):
1098 ff9290ec Sofia Papagiannaki
    try:
1099 ff9290ec Sofia Papagiannaki
        AstakosUser.objects.get(user_ptr=u.pk)
1100 ff9290ec Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1101 ff9290ec Sofia Papagiannaki
        extended_user = AstakosUser(user_ptr_id=u.pk)
1102 ff9290ec Sofia Papagiannaki
        extended_user.__dict__.update(u.__dict__)
1103 ff9290ec Sofia Papagiannaki
        extended_user.save()
1104 67be1883 Olga Brani
        if not extended_user.has_auth_provider('local'):
1105 67be1883 Olga Brani
            extended_user.add_auth_provider('local')
1106 fc1e2f02 Sofia Papagiannaki
    except BaseException, e:
1107 fc1e2f02 Sofia Papagiannaki
        logger.exception(e)
1108 ff9290ec Sofia Papagiannaki
1109 5ce3ce4f Sofia Papagiannaki
1110 fc1e2f02 Sofia Papagiannaki
def fix_superusers(sender, **kwargs):
1111 fc1e2f02 Sofia Papagiannaki
    # Associate superusers with AstakosUser
1112 ff9290ec Sofia Papagiannaki
    admins = User.objects.filter(is_superuser=True)
1113 ff9290ec Sofia Papagiannaki
    for u in admins:
1114 ff9290ec Sofia Papagiannaki
        create_astakos_user(u)
1115 ff9290ec Sofia Papagiannaki
1116 ff9290ec Sofia Papagiannaki
1117 fc1e2f02 Sofia Papagiannaki
def user_post_save(sender, instance, created, **kwargs):
1118 aa4109d4 Sofia Papagiannaki
    if not created:
1119 aa4109d4 Sofia Papagiannaki
        return
1120 fc1e2f02 Sofia Papagiannaki
    create_astakos_user(instance)
1121 fc1e2f02 Sofia Papagiannaki
1122 5ce3ce4f Sofia Papagiannaki
1123 fc1e2f02 Sofia Papagiannaki
def set_default_group(user):
1124 aa4109d4 Sofia Papagiannaki
    try:
1125 5ce3ce4f Sofia Papagiannaki
        default = AstakosGroup.objects.get(name='default')
1126 5ce3ce4f Sofia Papagiannaki
        Membership(
1127 5ce3ce4f Sofia Papagiannaki
            group=default, person=user, date_joined=datetime.now()).save()
1128 aa4109d4 Sofia Papagiannaki
    except AstakosGroup.DoesNotExist, e:
1129 aa4109d4 Sofia Papagiannaki
        logger.exception(e)
1130 ff9290ec Sofia Papagiannaki
1131 bf0c6de5 Sofia Papagiannaki
1132 fc1e2f02 Sofia Papagiannaki
def astakosuser_pre_save(sender, instance, **kwargs):
1133 fc1e2f02 Sofia Papagiannaki
    instance.aquarium_report = False
1134 fc1e2f02 Sofia Papagiannaki
    instance.new = False
1135 fc1e2f02 Sofia Papagiannaki
    try:
1136 5ce3ce4f Sofia Papagiannaki
        db_instance = AstakosUser.objects.get(id=instance.id)
1137 fc1e2f02 Sofia Papagiannaki
    except AstakosUser.DoesNotExist:
1138 fc1e2f02 Sofia Papagiannaki
        # create event
1139 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True
1140 fc1e2f02 Sofia Papagiannaki
        instance.new = True
1141 fc1e2f02 Sofia Papagiannaki
    else:
1142 fc1e2f02 Sofia Papagiannaki
        get = AstakosUser.__getattribute__
1143 fc1e2f02 Sofia Papagiannaki
        l = filter(lambda f: get(db_instance, f) != get(instance, f),
1144 9a06d96f Olga Brani
                   BILLING_FIELDS)
1145 fc1e2f02 Sofia Papagiannaki
        instance.aquarium_report = True if l else False
1146 fc1e2f02 Sofia Papagiannaki
1147 5ce3ce4f Sofia Papagiannaki
1148 fc1e2f02 Sofia Papagiannaki
def astakosuser_post_save(sender, instance, created, **kwargs):
1149 fc1e2f02 Sofia Papagiannaki
    if instance.aquarium_report:
1150 fc1e2f02 Sofia Papagiannaki
        report_user_event(instance, create=instance.new)
1151 fc1e2f02 Sofia Papagiannaki
    if not created:
1152 fc1e2f02 Sofia Papagiannaki
        return
1153 fc1e2f02 Sofia Papagiannaki
    set_default_group(instance)
1154 fc1e2f02 Sofia Papagiannaki
    # TODO handle socket.error & IOError
1155 ab7e3a9e Kostas Papadimitriou
    #register_users((instance,))
1156 fc1e2f02 Sofia Papagiannaki
1157 5ce3ce4f Sofia Papagiannaki
1158 bd4f356c Sofia Papagiannaki
def resource_post_save(sender, instance, created, **kwargs):
1159 bd4f356c Sofia Papagiannaki
    if not created:
1160 bd4f356c Sofia Papagiannaki
        return
1161 bd4f356c Sofia Papagiannaki
    register_resources((instance,))
1162 bd4f356c Sofia Papagiannaki
1163 bd4f356c Sofia Papagiannaki
1164 fc1e2f02 Sofia Papagiannaki
def send_quota_disturbed(sender, instance, **kwargs):
1165 fc1e2f02 Sofia Papagiannaki
    users = []
1166 fc1e2f02 Sofia Papagiannaki
    extend = users.extend
1167 fc1e2f02 Sofia Papagiannaki
    if sender == Membership:
1168 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1169 fc1e2f02 Sofia Papagiannaki
            return
1170 fc1e2f02 Sofia Papagiannaki
        extend([instance.person])
1171 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosUserQuota:
1172 fc1e2f02 Sofia Papagiannaki
        extend([instance.user])
1173 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroupQuota:
1174 fc1e2f02 Sofia Papagiannaki
        if not instance.group.is_enabled:
1175 fc1e2f02 Sofia Papagiannaki
            return
1176 fc1e2f02 Sofia Papagiannaki
        extend(instance.group.astakosuser_set.all())
1177 fc1e2f02 Sofia Papagiannaki
    elif sender == AstakosGroup:
1178 fc1e2f02 Sofia Papagiannaki
        if not instance.is_enabled:
1179 fc1e2f02 Sofia Papagiannaki
            return
1180 fc1e2f02 Sofia Papagiannaki
    quota_disturbed.send(sender=sender, users=users)
1181 fc1e2f02 Sofia Papagiannaki
1182 5ce3ce4f Sofia Papagiannaki
1183 fc1e2f02 Sofia Papagiannaki
def on_quota_disturbed(sender, users, **kwargs):
1184 c0b26605 Sofia Papagiannaki
#     print '>>>', locals()
1185 fc1e2f02 Sofia Papagiannaki
    if not users:
1186 fc1e2f02 Sofia Papagiannaki
        return
1187 fc1e2f02 Sofia Papagiannaki
    send_quota(users)
1188 bf0c6de5 Sofia Papagiannaki
1189 bf0c6de5 Sofia Papagiannaki
def renew_token(sender, instance, **kwargs):
1190 f9aea9c8 Sofia Papagiannaki
    if not instance.auth_token:
1191 bf0c6de5 Sofia Papagiannaki
        instance.renew_token()
1192 bf0c6de5 Sofia Papagiannaki
1193 fc1e2f02 Sofia Papagiannaki
post_syncdb.connect(fix_superusers)
1194 fc1e2f02 Sofia Papagiannaki
post_save.connect(user_post_save, sender=User)
1195 fc1e2f02 Sofia Papagiannaki
pre_save.connect(astakosuser_pre_save, sender=AstakosUser)
1196 fc1e2f02 Sofia Papagiannaki
post_save.connect(astakosuser_post_save, sender=AstakosUser)
1197 bd4f356c Sofia Papagiannaki
post_save.connect(resource_post_save, sender=Resource)
1198 fc1e2f02 Sofia Papagiannaki
1199 fc1e2f02 Sofia Papagiannaki
quota_disturbed = Signal(providing_args=["users"])
1200 fc1e2f02 Sofia Papagiannaki
quota_disturbed.connect(on_quota_disturbed)
1201 fc1e2f02 Sofia Papagiannaki
1202 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosGroup)
1203 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=Membership)
1204 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosUserQuota)
1205 fc1e2f02 Sofia Papagiannaki
post_delete.connect(send_quota_disturbed, sender=AstakosUserQuota)
1206 fc1e2f02 Sofia Papagiannaki
post_save.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1207 a4075f5a root
post_delete.connect(send_quota_disturbed, sender=AstakosGroupQuota)
1208 c0b26605 Sofia Papagiannaki
1209 bf0c6de5 Sofia Papagiannaki
pre_save.connect(renew_token, sender=AstakosUser)
1210 2e90e3ec Kostas Papadimitriou
pre_save.connect(renew_token, sender=Service)