Revision 9a06d96f

b/.gitignore
6 6
.project
7 7
.pydevproject
8 8
snf-astakos-app/astakos/version.py
9
snf-astakos-app/distribute-0.6.10-py2.6.egg
10
snf-astakos-app/distribute-0.6.10.tar.gz
b/snf-astakos-app/README
85 85
ASTAKOS_GREETING_EMAIL_SUBJECT              'Welcome to %s alpha2 testing' % SITENAME                                       Welcome email subject
86 86
ASTAKOS_FEEDBACK_EMAIL_SUBJECT              'Feedback from %s alpha2 testing' % SITENAME                                    Feedback email subject
87 87
ASTAKOS_VERIFICATION_EMAIL_SUBJECT          '%s alpha2 testing account activation is needed' % SITENAME                     Account activation email subject
88
ASTAKOS_ADMIN_NOTIFICATION_EMAIL_SUBJECT    '%s alpha2 testing account created (%%(user)s)' % SITENAME                      Account creation admin notification email subject
88
ASTAKOS_ACCOUNT_CREATION_SUBJECT            '%s alpha2 testing account created (%%(user)s)' % SITENAME                      Account creation email subject
89
ASTAKOS_GROUP_CREATION_SUBJECT              '%s alpha2 testing group created (%%(group)s)' % SITENAME                       Group creation email subject
89 90
ASTAKOS_HELPDESK_NOTIFICATION_EMAIL_SUBJECT '%s alpha2 testing account activated (%%(user)s)' % SITENAME                    Account activation helpdesk notification email subject
90 91
ASTAKOS_EMAIL_CHANGE_EMAIL_SUBJECT          'Email change on %s alpha2 testing' % SITENAME                                  Email change subject               
91 92
ASTAKOS_PASSWORD_RESET_EMAIL_SUBJECT        'Password reset on %s alpha2 testing' % SITENAME                                Password change email subject
93

  
92 94
ASTAKOS_QUOTA_HOLDER_URL                    ''                                                                              The quota holder URI
93
                                                                                                                    e.g. ``http://localhost:8080/api/quotaholder/v``
94
ASTAKOS_SERVICES                            {'cyclades': {'url':'https://node1.example.com/ui/', 'quota': {'vm': 2}},       Cloud service default url and quota      
95
                                            'pithos+':  {'url':'https://node2.example.com/ui/', 'quota': {                  
96
                                            'diskspace': 50 * 1024 * 1024 * 1024}}})                                        
95
                                                                                                                            e.g. ``http://localhost:8080/api/quotaholder/v``
96
ASTAKOS_SERVICES                            {'cyclades': {'resources': [{'desc': 'Number of virtual machines',              Default cloud service information
97
                                            'group': 'storage',
98
                                            'name': 'vm',
99
                                            'uplimit': 2},
100
                                            {'desc': 'Virtual machine disk size',
101
                                            'group': 'storage',
102
                                            'name': 'disksize',
103
                                            'unit': 'GB',
104
                                            'uplimit': 5},
105
                                            {'desc': 'Number of virtual machine processors',
106
                                            'group': 'storage',
107
                                            'name': 'cpu',
108
                                            'uplimit': 1},
109
                                            {'desc': 'Virtual machines',
110
                                            'group': 'storage',
111
                                            'name': 'ram',
112
                                            'unit': 'MB',
113
                                            'uplimit': 1024}],
114
                                            'url': 'https://node1.example.com/ui/'},
115
                                            'pithos+': {'resources': [{'desc': 'Pithos account diskspace',
116
                                            'group': 'compute',
117
                                            'name': 'diskspace',
118
                                            'unit': 'bytes',
119
                                            'uplimit': 5368709120}],
120
                                            'url': 'https://node2.example.com/ui/'}}                                                                               
97 121
ASTAKOS_AQUARIUM_URL                        ''                                                                              The billing (aquarium) URI
98
                                                                                                                    e.g. ``http://localhost:8888/user``
122
                                                                                                                            e.g. ``http://localhost:8888/user``
99 123
ASTAKOS_PAGINATE_BY                         10                                                                              Number of object to be displayed per page
124

  
125
ASTAKOS_NEWPASSWD_INVALIDATE_TOKEN          True                                                                            Enforce token renewal on password change/reset. If set to False, user can optionally decide
126
                                                                                                                            whether to renew the token or not.
100 127
=========================================== =============================================================================   ===========================================================================================
101 128

  
102 129
Administrator functions
b/snf-astakos-app/astakos/im/activation_backends.py
38 38
from astakos.im.models import AstakosUser
39 39
from astakos.im.forms import LocalUserCreationForm, ShibbolethUserCreationForm
40 40
from astakos.im.util import get_invitation
41
from astakos.im.functions import send_verification, send_activation, \
42
    send_admin_notification, activate
41
from astakos.im.functions import (send_verification, send_activation,
42
                                  send_account_creation_notification,
43
                                  send_group_creation_notification, activate)
43 44
from astakos.im.settings import INVITATIONS_ENABLED, MODERATION_ENABLED, SITENAME, RE_USER_EMAIL_PATTERNS
44 45

  
45 46
import logging
......
129 130
                    send_activation(user, activation_template_name)
130 131
                    return VerificationSent()
131 132
            else:
132
                send_admin_notification(
133
                send_account_creation_notification(
133 134
                    template_name=admin_email_template_name,
134
                    dictionary={'user': user, 'group_creation': True},
135
                    subject='%s alpha2 testing account notification' % SITENAME
135
                    dictionary={'user': user, 'group_creation': True}
136 136
                )
137 137
                return NotificationSent()
138 138
        except BaseException, e:
b/snf-astakos-app/astakos/im/api/__init__.py
173 173
        item = MenuItem
174 174
        item.current_path = absolute(request, request.path)
175 175
        append(item(
176
                url=absolute(request, reverse('index')),
177
                name=user.email))
176
               url=absolute(request, reverse('index')),
177
               name=user.email))
178 178
        append(item(url=absolute(request, reverse('edit_profile')),
179 179
               name="My account"))
180 180
        if with_extra_links:
181 181
            if user.has_usable_password() and user.provider in ('local', ''):
182 182
                append(item(
183
                        url=absolute(request, reverse('password_change')),
184
                        name="Change password"))
183
                       url=absolute(request, reverse('password_change')),
184
                       name="Change password"))
185 185
            if EMAILCHANGE_ENABLED:
186 186
                append(item(
187
                        url=absolute(request, reverse('email_change')),
188
                        name="Change email"))
187
                       url=absolute(request, reverse('email_change')),
188
                       name="Change email"))
189 189
            if INVITATIONS_ENABLED:
190 190
                append(item(
191
                        url=absolute(request, reverse('invite')),
192
                        name="Invitations"))
191
                       url=absolute(request, reverse('invite')),
192
                       name="Invitations"))
193 193
            append(item(
194
                    url=absolute(request, reverse('feedback')),
195
                    name="Feedback"))
194
                   url=absolute(request, reverse('feedback')),
195
                   name="Feedback"))
196 196
            append(item(
197
                    url=absolute(request, reverse('group_list')),
198
                    name="Groups",
199
                    submenu=(item(
200
                                url=absolute(request,
201
                                             reverse('group_list')),
202
                                name="Overview"),
197
                   url=absolute(request, reverse('group_list')),
198
                   name="Groups",
199
                   submenu=(item(
200
                            url=absolute(request,
201
                                         reverse('group_list')),
202
                            name="Overview"),
203 203
                            item(
204 204
                                url=absolute(request,
205 205
                                             reverse('group_create_list')),
......
209 209
                                             reverse('group_search')),
210 210
                                name="Join"),)))
211 211
            append(item(
212
                    url=absolute(request, reverse('resource_list')),
213
                    name="Resources"))
212
                   url=absolute(request, reverse('resource_list')),
213
                   name="Resources"))
214 214
            append(item(
215
                    url=absolute(request, reverse('billing')),
216
                    name="Billing"))
215
                   url=absolute(request, reverse('billing')),
216
                   name="Billing"))
217 217
            append(item(
218
                    url=absolute(request, reverse('timeline')),
219
                    name="Timeline"))
218
                   url=absolute(request, reverse('timeline')),
219
                   name="Timeline"))
220 220
        if with_signout:
221 221
            append(item(
222
                    url=absolute(request, reverse('logout')),
223
                    name="Sign out"))
222
                   url=absolute(request, reverse('logout')),
223
                   name="Sign out"))
224 224

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

  
34
from astakos.im.api.backends.lib.django import DjangoBackend
35

  
36

  
37
def get_backend():
38
    return DjangoBackend()
b/snf-astakos-app/astakos/im/api/backends/base.py
1
class ItemNotExists(NameError):
2
    pass
3

  
4

  
5
class ItemExists(NameError):
6
    pass
7

  
8

  
9
class MissingIdentifier(IOError):
10
    pass
11

  
12

  
13
class BaseBackend(object):
14
    def update_user():
15
        pass
16

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

  
34
from django.db import IntegrityError, transaction
35
from django.core.exceptions import ObjectDoesNotExist
36

  
37
from functools import wraps
38
from smtplib import SMTPException
39

  
40
from astakos.im.models import AstakosUser, Resource, Service, RESOURCE_SEPARATOR
41
from astakos.im.api.backends.base import (BaseBackend, ItemNotExists,
42
                                          ItemExists, MissingIdentifier)
43
from astakos.im.util import reserved_email, model_to_dict
44
from astakos.im.endpoints.quotaholder import get_quota
45

  
46
import logging
47

  
48
logger = logging.getLogger(__name__)
49

  
50
DEFAULT_CONTENT_TYPE = None
51

  
52

  
53
def safe(propagate_exceptions=False):
54
    """Decorator function for views that implement an API method."""
55

  
56
    def decorator(func):
57
        @transaction.commit_manually
58
        @wraps(func)
59
        def wrapper(self, *args, **kwargs):
60
            logger.debug('%s %s %s' % (func, args, kwargs))
61
            try:
62
                r = func(self, *args, **kwargs) or ()
63
            except Exception, e:
64
                logger.exception(e)
65
                transaction.rollback()
66
                if propagate_exceptions:
67
                    raise e
68
                else:
69
                    args = list(args)
70
                    args.append(e)
71
                    r = args
72
            else:
73
                transaction.commit()
74
            r = filter(bool, r) # filter out None elements
75
            return list(r)
76
        return wrapper
77
    return decorator
78

  
79

  
80
class DjangoBackend(BaseBackend):
81
    def _lookup_object(self, model, **kwargs):
82
        """
83
        Returns an object of the specific model having this id.
84
        """
85
        if not kwargs:
86
            raise MissingIdentifier
87
        try:
88
            return model.objects.get(**kwargs)
89
        except model.DoesNotExist:
90
            raise ItemNotExists()
91

  
92
    def _lookup_user(self, id):
93
        """
94
        Returns an AstakosUser having this id.
95
        """
96
        return self._lookup_object(AstakosUser, id=id)
97

  
98
    def _lookup_service(self, id):
99
        """
100
        Returns an Service having this id.
101
        """
102
        return self._lookup_object(Service, id=id)
103

  
104
    def _list(self, model, filter=()):
105
        q = model.objects.all()
106
        if filter:
107
            q = q.filter(id__in=filter)
108
        return map(lambda o: model_to_dict(o, exclude=[]), q)
109

  
110
    def _create_object(self, model, **kwargs):
111
        o = model(**kwargs)
112
        o.save()
113
        return o
114

  
115
    def _update_object(self, model, id, save=True, **kwargs):
116
        o = self._lookup_object(model, id=id)
117
        if kwargs:
118
            o.__dict__.update(kwargs)
119
        if save:
120
            o.save()
121
        return o
122

  
123
    @safe()
124
    def update_user(self, user_id, renew_token=False, **kwargs):
125
        user = self._update_object(AstakosUser, user_id, save=False, **kwargs)
126
        if renew_token:
127
            user.renew_token()
128
        if kwargs or renew_token:
129
            user.save()
130

  
131
    @safe()
132
    def create_user(self, **kwargs):
133
        policies = kwargs.pop('policies', ())
134
        permissions = kwargs.pop('permissions', ())
135
        groups = kwargs.pop('groups', ())
136
        password = kwargs.pop('password', None)
137

  
138
        u = self._create_object(AstakosUser, **kwargs)
139

  
140
        if password:
141
            u.set_password(password)
142
        u.permissions = permissions
143
        u.policies = policies
144
        u.extended_groups = groups
145

  
146
    @safe()
147
    def add_policies(self, user_id, update=False, policies=()):
148
        user = self._lookup_user(user_id)
149
        rejected = []
150
        append = rejected.append
151
        for p in policies:
152
            service = p.get('service')
153
            resource = p.get('resource')
154
            uplimit = p.get('uplimit')
155
            try:
156
                user.add_policy(service, resource, uplimit, update)
157
            except (ObjectDoesNotExist, IntegrityError), e:
158
                append((service, resource, e))
159
        if rejected:
160
            raise Exception(rejected)
161

  
162
    @safe()
163
    def remove_policies(self, user_id, policies=()):
164
        user = self._lookup_user(user_id)
165
        if not user:
166
            return user_id
167
        rejected = []
168
        append = rejected.append
169
        for p in policies:
170
            service = p.get('service')
171
            resource = p.get('resource')
172
            try:
173
                user.delete_policy(service, resource)
174
            except ObjectDoesNotExist, e:
175
                append((service, resource, e))
176
        if rejected:
177
            raise Exception(rejected)
178

  
179
    @safe()
180
    def add_permissions(self, user_id, permissions=()):
181
        user = self._lookup_user(user_id)
182
        rejected = []
183
        append = rejected.append
184
        for p in permissions:
185
            try:
186
                user.add_permission(p)
187
            except IntegrityError, e:
188
                append((p, e))
189
        if rejected:
190
            raise Exception(rejected)
191

  
192
    @safe()
193
    def remove_permissions(self, user_id, permissions=()):
194
        user = self._lookup_user(user_id)
195
        rejected = []
196
        append = rejected.append
197
        for p in permissions:
198
            try:
199
                user.remove_permission(p)
200
            except (ObjectDoesNotExist, IntegrityError), e:
201
                append((p, e))
202
        if rejected:
203
            raise Exception(rejected)
204

  
205
    @safe()
206
    def invite_users(self, senderid, recipients=()):
207
        user = self._lookup_user(senderid)
208
        rejected = []
209
        append = rejected.append
210
        for r in recipients:
211
            try:
212
                user.invite(r.get('email'), r.get('realname'))
213
            except (IntegrityError, SMTPException), e:
214
                append((email, e))
215
        if rejected:
216
            raise Exception(rejected)
217

  
218
    @safe(propagate_exceptions=True)
219
    def list_users(self, filter=()):
220
        return self._list(AstakosUser, filter=filter)
221

  
222
    @safe(propagate_exceptions=True)
223
    def get_resource_usage(self, user_id):
224
        user = self._lookup_user(user_id)
225
        c, data = get_quota((user,))
226
        resources = []
227
        append = resources.append
228
        for t in data:
229
            t = (i if i else 0 for i in t)
230
            (entity, name, quantity, capacity, importLimit, exportLimit,
231
             imported, exported, returned, released, flags) = t
232
            service, sep, resource = name.partition(RESOURCE_SEPARATOR)
233
            resource = Resource.objects.select_related().get(
234
                service__name=service, name=resource)
235
            d = dict(name=name,
236
                     description=resource.desc,
237
                     unit=resource.unit or '',
238
                     maxValue=quantity + capacity,
239
                     currValue=quantity + imported - released - exported + returned)
240
            append(d)
241
        return resources
242

  
243
    @safe(propagate_exceptions=True)
244
    def list_resources(self, filter=()):
245
        return self._list(Resource, filter=filter)
246

  
247
    @safe()
248
    def create_service(self, **kwargs):
249
        resources = kwargs.pop('resources', ())
250
        s = self._create_object(Service, **kwargs)
251
        s.resources = resources
252

  
253
    @safe()
254
    def remove_services(self, ids=()):
255
        # TODO return information for unknown ids
256
        q = Service.objects.filter(id__in=ids)
257
        q.delete()
258
    
259
    @safe()
260
    def update_service(self, service_id, renew_token=False, **kwargs):
261
        s = self._update_object(Service, service_id, save=False, **kwargs)
262
        if renew_token:
263
            s.renew_token()
264

  
265
        if kwargs or renew_token:
266
            s.save()
267

  
268
    @safe()
269
    def add_resources(self, service_id, update=False, resources=()):
270
        s = self._lookup_service(service_id)
271
        rejected = []
272
        append = rejected.append
273
        for r in resources:
274
            try:
275
                rr = r.copy()
276
                resource_id = rr.pop('id', None)
277
                if update:
278
                    if not resource_id:
279
                        raise MissingIdentifier
280
                    resource = self._update_object(Resource, resource_id, **rr)
281
                else:
282
                    resource = self._create_object(Resource, service=s, **rr)
283
            except Exception, e:
284
                append((r, e))
285
        if rejected:
286
            raise Exception(rejected)
287
    
288
    @safe()
289
    def remove_resources(self, service_id, ids=()):
290
        # TODO return information for unknown ids
291
        q = Resource.objects.filter(service__id=service_id,
292
                                id__in=ids)
293
        q.delete()
294
    
295
    @safe()
296
    def create_group(self, **kwargs):
297
        policies = kwargs.pop('policies', ())
298
        permissions = kwargs.pop('permissions', ())
299
        members = kwargs.pop('members', ())
300
        owners = kwargs.pop('owners', ())
301

  
302
        g = self._create_object(AstakosGroup, **kwargs)
303

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

  
34
from astakos.im.api.spec import AstakosAPI
35
from backends import get_backend
36

  
37
from commissioning import (Callpoint,
38
                           #                             CommissionException,
39
                           #                             CorruptedError, InvalidDataError,
40
                           #                             InvalidKeyError, NoEntityError,
41
                           #                             NoQuantityError, NoCapacityError,
42
                           #                             ExportLimitError, ImportLimitError
43
                           )
44

  
45

  
46
# from commissioning.utils.newname import newname
47
# from django.db.models import Model, BigIntegerField, CharField, ForeignKey, Q
48
# from django.db import transaction, IntegrityError
49
# from .models import (Holder, Entity, Policy, Holding,
50
#                      Commission, Provision, ProvisionLog, now)
51

  
52
class AstakosDjangoDBCallpoint():
53

  
54
    api_spec = AstakosAPI()
55

  
56
#     http_exc_lookup = {
57
#         CorruptedError:   550,
58
#         InvalidDataError: 400,
59
#         InvalidKeyError:  401,
60
#         NoEntityError:    404,
61
#         NoQuantityError:  413,
62
#         NoCapacityError:  413,
63
#     }
64

  
65
    def init_connection(self, connection):
66
        if connection is not None:
67
            raise ValueError("Cannot specify connection args with %s" %
68
                             type(self).__name__)
69
        pass
70

  
71
    def commit(self):
72
        transaction.commit()
73

  
74
    def rollback(self):
75
        transaction.rollback()
76

  
77
    def do_make_call(self, call_name, data):
78
        call_fn = getattr(self, call_name, None)
79
        if not call_fn:
80
            m = "cannot find call '%s'" % (call_name,)
81
            raise CorruptedError(m)
82

  
83
        return call_fn(**data)
84

  
85
    def create_users(self, users=()):
86
        b = get_backend()
87
        rejected = (b.create_user(**u) for u in users)
88
        return rejected
89

  
90
    def update_users(self, users=()):
91
        b = get_backend()
92
        rejected = (b.update_user(**u) for u in users)
93
        return rejected
94

  
95
    def add_user_policies(self, user_id, update=False, policies=()):
96
        b = get_backend()
97
        rejected = b.add_policies(user_id, update, policies)
98
        return rejected
99

  
100
    def remove_user_policies(self, user_id, policies=()):
101
        b = get_backend()
102
        rejected = b.remove_policies(user_id, policies)
103
        return rejected
104

  
105
    def add_user_permissions(self, user_id, permissions=()):
106
        b = get_backend()
107
        rejected = b.add_permissions(user_id, permissions)
108
        return rejected
109

  
110
    def remove_user_permissions(self, user_id, permissions=()):
111
        b = get_backend()
112
        rejected = b.remove_permissions(user_id, permissions)
113
        return rejected
114

  
115
    def invite_users(self, sender_id, recipients=()):
116
        b = get_backend()
117
        rejected = b.invite_users(sender_id, recipients)
118
        return rejected
119

  
120
    def list_users(self, filter=()):
121
        b = get_backend()
122
        return b.list_users(filter)
123

  
124
    def get_user_status(self, user_id):
125
        b = get_backend()
126
        return b.get_resource_usage(user_id)
127

  
128
    def list_resources(self, filter=()):
129
        b = get_backend()
130
        return b.list_resources(filter)
131

  
132
    def add_services(self, services=()):
133
        b = get_backend()
134
        rejected = (b.create_service(**s) for s in services)
135
        return rejected
136

  
137
    def update_services(self, services=()):
138
        b = get_backend()
139
        rejected = (b.update_service(**s) for s in services)
140
        return rejected
141

  
142
    def remove_services(self, ids=()):
143
        b = get_backend()
144
        rejected = b.remove_services(ids)
145
        return rejected
146

  
147
    def add_resources(self, service_id, update=False, resources=()):
148
        b = get_backend()
149
        rejected = b.add_resources(service_id, update, resources)
150
        return rejected
151
    
152
    def remove_resources(self, service_id, ids=()):
153
        b = get_backend()
154
        rejected = b.remove_resources(service_id, ids)
155
        return rejected
156
    
157
    def create_groups(self, groups=()):
158
        b = get_backend()
159
        rejected = (b.create_group(**g) for g in groups)
160
        return rejected
161

  
162
API_Callpoint = AstakosDjangoDBCallpoint
b/snf-astakos-app/astakos/im/api/client.py
1
#!/usr/bin/env python
2
from commissioning.clients.http import main, HTTP_API_Client
3
from astakos.im.api.spec import AstakosAPI
4

  
5

  
6
class AstakosHTTP(HTTP_API_Client):
7
    api_spec = AstakosAPI()
8

  
9

  
10
if __name__ == '__main__':
11
    main(callpoint=AstakosHTTP())
b/snf-astakos-app/astakos/im/api/spec.py
1
from commissioning.api.specificator import (
2
    CanonifyException, SpecifyException,
3
    Specificator, Null, Integer, Text,
4
    Tuple, ListOf, Dict, Args)
5

  
6

  
7
class Name(Text):
8
    def init(self):
9
        self.opts.update({'regex': "[\w.:]+", 'maxlen': 512})
10
Name = Name()
11

  
12

  
13
class Email(Text):
14
    def init(self):
15
        pattern = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
16
        self.opts.update({'regex': pattern, 'maxlen': 512})
17
Email = Email()
18

  
19

  
20
class Url(Text):
21
    def init(self):
22
        pattern = "(((f|ht){1}tp://)[-a-zA-Z0-9@:%_\+.~#?&//=]+)"
23
        self.opts.update({'regex': pattern, 'maxlen': 512})
24
Url = Url()
25

  
26

  
27
class Filepath(Text):
28
    def init(self):
29
        self.opts.update({'regex': "", 'maxlen': 512})
30
Filepath = Filepath()
31

  
32

  
33
class Nonnegative(Integer):
34
    def init(self):
35
        self.opts.update({'minimum': 0})
36
Nonnegative = Nonnegative()
37

  
38

  
39
class Boolean(Integer):
40
    def init(self):
41
        self.opts.update({'minimum': 0, 'maximum': 1})
42
Boolean = Boolean()
43

  
44

  
45
class GroupKind(Integer):
46
    def init(self):
47
        self.opts.update({'minimum': 1, 'maximum': 5})
48
GroupKind = GroupKind()
49

  
50
Timepoint = Text(classname='Timepoint', maxlen=24)
51

  
52

  
53
class AstakosAPI(Specificator):
54
    def create_users(
55
        self,
56
        users=ListOf(
57
            email=Email,
58
            first_name=Name,
59
            last_name=Name,
60
            is_active=Boolean,
61
            is_superuser=Boolean,
62
            affiliation=Name,
63
            password=Name,
64
            provider=Name,
65
            level=Nonnegative,
66
            invitations=Nonnegative,
67
            is_verified=Boolean,
68
            third_party_identifier=Name,
69
            email_verified=Boolean),
70
        policies=ListOf(resource=Name, supimit=Nonnegative),
71
        groups=ListOf(Name),
72
        permissions=ListOf(Name)
73
    ):
74
        rejected = ListOf(user=Email, reason=Text())
75
        return rejected
76

  
77
    def update_users(
78
        self,
79
        users=ListOf(
80
            pk=Nonnegative,
81
            renew_token=Boolean,
82
            data=ListOf(
83
                first_name=Name,
84
                last_name=Name,
85
                is_active=Boolean,
86
                is_superuser=Boolean,
87
                affiliation=Name,
88
                password=Name,
89
                provider=Name,
90
                level=Nonnegative,
91
                invitations=Nonnegative,
92
                is_verified=Boolean,
93
                third_party_identifier=Name,
94
                email_verified=Boolean
95
            )
96
        )
97
    ):
98
        rejected = ListOf(user_id=Nonnegative, reason=Text())
99
        return rejected
100

  
101
    def add_user_policies(
102
        self,
103
        pk=Nonnegative,
104
        update=Boolean,
105
        policies=ListOf(service=Name, resource=Name, upimit=Nonnegative)
106
    ):
107
        rejected = ListOf(resource=Name, reason=Text())
108
        return rejected
109

  
110
    def remove_user_policies(
111
        self,
112
        pk=Nonnegative,
113
        policies=ListOf(service=Name, resource=Name)
114
    ):
115
        rejected = ListOf(service=Name, resource=Name)
116
        return rejected
117

  
118
    def add_user_permissions(
119
        self,
120
        pk=Nonnegative,
121
        permissions=ListOf(permission=Name)
122
    ):
123
        rejected = ListOf(permission=Name)
124
        return rejected
125

  
126
    def remove_user_permissions(
127
        self,
128
        pk=Nonnegative,
129
        permissions=ListOf(permission=Name)
130
    ):
131
        rejected = ListOf(permission=Name)
132
        return rejected
133

  
134
    def invite_users(
135
        self,
136
        sender=Email,
137
        data=ListOf(email=Email, realname=Name)
138
    ):
139
        rejected = ListOf(receiver=Email)
140
        return rejected
141

  
142
    def list_users(
143
        self,
144
        filter=ListOf(id=Nonnegative)
145
    ):
146
        return ListOf(
147
            activation_sent=Timepoint,
148
            affiliation=Name,
149
            auth_token=Name,
150
            auth_token_created=Timepoint,
151
            auth_token_expires=Timepoint,
152
            date_joined=Timepoint,
153
            date_signed_terms=Timepoint,
154
            email=Email,
155
            email_verified=Boolean,
156
            first_name=Name,
157
            has_credits=Boolean,
158
            has_signed_terms=Boolean,
159
            id=Nonnegative,
160
            invitations=Nonnegative,
161
            invitations_sent=ListOf(
162
                code=Name,
163
                consumed=Boolean,
164
                created=Timepoint,
165
                id=Nonnegative,
166
                realname=Name,
167
                username=Email
168
            ),
169
            is_active=Boolean,
170
            is_staff=Boolean,
171
            is_superuser=Boolean,
172
            is_verified=Boolean,
173
            last_login=Timepoint,
174
            last_name=Name,
175
            level=Nonnegative,
176
            password=Name,
177
            provider=Name,
178
            third_party_identifier=Name,
179
            updated=Timepoint,
180
            user_permissions=ListOf(
181
                codename=Name,
182
                id=Nonnegative,
183
                name=Name
184
            ),
185
            username=Name,
186
            astakos_groups=ListOf(
187
                approval_date=Timepoint,
188
                creation_date=Timepoint,
189
                desc=Text(),
190
                max_participants=Nonnegative,
191
                expiration_date=Timepoint,
192
                group_ptr=Url,
193
                homepage=Url,
194
                id=Nonnegative,
195
                issue_date=Timepoint,
196
                kind=Name,
197
                moderation_enabled=Boolean,
198
                name=Name,
199
                #permissions=ListOf(),
200
                policy=ListOf(id=Nonnegative, name=Name)
201
            )
202
        )
203

  
204
    def get_user_status(
205
        self,
206
        user_id=Nonnegative
207
    ):
208
        return ListOf(
209
            name=Name,
210
            description=Text(),
211
            unit=Name,
212
            maxValue=Integer(),
213
            currValue=Integer()
214
        )
215

  
216
    def list_resources(self, filter=ListOf(id=Nonnegative)):
217
        return ListOf(
218
            desc=Text(),
219
            group=Name,
220
            id=Nonnegative,
221
            meta=ListOf(key=Name, value=Name),
222
            name=Name,
223
            service=Name,
224
            unit=Name
225
        )
226

  
227
    def add_services(
228
        self,
229
        services=ListOf(
230
            name=Name,
231
            url=Url,
232
            icon=Filepath,
233
            resources=ListOf(
234
                name=Name,
235
                desc=Text(),
236
                unit=Name,
237
                group=Name
238
            )
239
        )
240
    ):
241
        rejected = ListOf(service=Name)
242
        return rejected
243

  
244
    def update_services(
245
        self,
246
        services=ListOf(id=Nonnegative, url=Url, icon=Filepath)
247
    ):
248
        rejected = ListOf(service=Name)
249
        return rejected
250

  
251
    def remove_services(self, ids=ListOf(Nonnegative)):
252
        rejected = ListOf(service=Name)
253
        return rejected
254

  
255
    def add_resources(
256
        self,
257
        service_id=Nonnegative,
258
        update=Boolean,
259
        resources=ListOf(
260
            name=Name,
261
            resources=ListOf(
262
                name=Name,
263
                desc=Text(),
264
                unit=Name,
265
                group=Name)
266
        )
267
    ):
268
        rejected = ListOf(service=Name)
269
        return rejected
270

  
271
    def remove_resources(
272
        self,
273
        service_id=Nonnegative,
274
        ids=ListOf(Nonnegative)
275
    ):
276
        rejected = ListOf(Name)
277
        return rejected
278

  
279
    def create_groups(
280
        self,
281
        groups=ListOf(
282
            name=Name,
283
            kind=GroupKind,
284
            homepage=Url,
285
            desc=Text(),
286
            policies=ListOf(resource=Name, upimit=Nonnegative),
287
            issue_date=Timepoint,
288
            expiration_date=Timepoint,
289
            moderation_enabled=Boolean,
290
            participants=Nonnegative,
291
            permissions=ListOf(permission=Name),
292
            members=ListOf(user=Email, is_approved=Boolean),
293
            owners=ListOf(user=Email)
294
        )
295
    ):
296
        rejected = ListOf(group=Name)
297
        return rejected
298

  
299
    def enable_groups(self, data=ListOf(group=Name)):
300
        rejected = ListOf(group=Name)
301
        return rejected
302

  
303
    def search_groups(self, key=Name):
304
        return ListOf(
305
            group=Name,
306
            kind=GroupKind,
307
            homepage=Url,
308
            desc=Text(),
309
            creation_date=Timepoint,
310
            issue_date=Timepoint,
311
            expiration_date=Timepoint,
312
            moderation_enabled=Boolean,
313
            participants=Nonnegative,
314
            owner=ListOf(user=Email),
315
            policies=ListOf(resource=Name, upimit=Nonnegative),
316
            members=ListOf(user=Email, is_approved=Boolean)
317
        )
318

  
319
    def list_groups(self):
320
        return ListOf(
321
            group=Name,
322
            kind=GroupKind,
323
            homepage=Url,
324
            desc=Text(),
325
            creation_date=Timepoint,
326
            issue_date=Timepoint,
327
            expiration_date=Timepoint,
328
            moderation_enabled=Boolean,
329
            participants=Nonnegative,
330
            owners=ListOf(user=Email),
331
            policies=ListOf(resource=Name, upimit=Nonnegative),
332
            members=ListOf(user=Email, is_approved=Boolean)
333
        )
334

  
335
    def add_owners(
336
        self,
337
        data=ListOf(group=Name, owners=ListOf(user=Email))
338
    ):
339
        rejected = ListOf(user=Email)
340
        return rejected
341

  
342
    def remove_owners(
343
        self,
344
        data=ListOf(group=Name, owners=ListOf(user=Email))
345
    ):
346
        rejected = ListOf(user=Email)
347
        return rejected
348

  
349
    def add_members(
350
        self,
351
        data=ListOf(group=Name, members=ListOf(user=Email))
352
    ):
353
        rejected = ListOf(user=Email)
354
        return rejected
355

  
356
    def remove_members(
357
        self,
358
        data=ListOf(group=Name, members=ListOf(user=Email))
359
    ):
360
        rejected = ListOf(user=Email)
361
        return rejected
362

  
363
    def add_policies(
364
        self,
365
        data=ListOf(group=Name, resource=Name, upimit=Nonnegative)
366
    ):
367
        rejected = ListOf(group=Name, resource=Name)
368
        return rejected
369

  
370
    def remove_group_policies(
371
        self,
372
        data=ListOf(group=Name, resource=Name, upimit=Nonnegative)
373
    ):
374
        rejected = ListOf(group=Name, resource=Name)
375
        return rejected
376

  
377
    def update_group_policies(
378
        self, data=ListOf(group=Name, resource=Name, upimit=Nonnegative)
379
    ):
380
        rejected = ListOf(group=Name, resource=Name)
381
        return rejected
382

  
383
    def approve_members(
384
        self,
385
        data=ListOf(group=Name, members=ListOf(user=Email))
386
    ):
387
        rejected = ListOf(user=Email)
388
        return rejected
389

  
390
    def disapprove_members(
391
        self,
392
        data=ListOf(group=Name, members=ListOf(user=Email))
393
    ):
394
        rejected = ListOf(user=Email)
395
        return rejected
396

  
397
    def add_group_permissions(
398
        self,
399
        data=ListOf(group=Name, permission=Name)
400
    ):
401
        rejected = ListOf(group=Name, permission=Name)
402
        return rejected
403

  
404
    def delete_group_permissions(
405
        self,
406
        data=ListOf(group=Name, permission=Name)
407
    ):
408
        rejected = ListOf(group=Name, permission=Name)
409
        return rejected
410

  
411
    def list_resource_units(self):
412
        return ListOf(Name)
413

  
414
    def get_approval_terms(term=Nonnegative):
415
        return Text()
416

  
417
    def add_approval_terms(location=Filepath):
418
        return Nonnegative
419

  
420
#     def change_emails():
421
#         pass
b/snf-astakos-app/astakos/im/context_processors.py
74 74
    if type(PROFILE_MESSAGES) == dict:
75 75
        PROFILE_MESSAGES = PROFILE_MESSAGES.items()
76 76

  
77
    EXTRA_MESSAGES_SET = bool(GLOBAL_MESSAGES or SIGNUP_MESSAGES or \
78
            LOGIN_MESSAGES or PROFILE_MESSAGES)
77
    EXTRA_MESSAGES_SET = bool(GLOBAL_MESSAGES or SIGNUP_MESSAGES or
78
                              LOGIN_MESSAGES or PROFILE_MESSAGES)
79 79

  
80 80
    return {
81 81
        'GLOBAL_MESSAGES': GLOBAL_MESSAGES,
b/snf-astakos-app/astakos/im/endpoints/aquarium/consumer.py
34 34
import logging
35 35

  
36 36
logging.basicConfig(format='%(asctime)s [%(levelname)s] %(name)s %(message)s',
37
        datefmt='%Y-%m-%d %H:%M:%S'
38
)
37
                    datefmt='%Y-%m-%d %H:%M:%S'
38
                    )
39 39
logger = logging.getLogger('endpoint.aquarium')
40 40

  
41 41
from astakos.im.models import AstakosUser
b/snf-astakos-app/astakos/im/endpoints/quotaholder.py
57 57
        def wrapper(entities=(), client=None, **kwargs):
58 58
            if not entities:
59 59
                return ()
60
        
60

  
61 61
            if not QUOTA_HOLDER_URL:
62 62
                return ()
63
        
63

  
64 64
            c = client or QuotaholderHTTP(QUOTA_HOLDER_URL)
65 65
            func = c.__dict__.get(func_name)
66 66
            if not func:
67 67
                return c,
68
            
68

  
69 69
            data = payload_func(entities, client, **kwargs)
70 70
            if not data:
71
                return c,
72
            
71
                return c, data
72

  
73 73
            funcname = func.__name__
74 74
            kwargs = {'context': {}, funcname: data}
75 75
            rejected = func(**kwargs)
......
79 79
        return wrapper
80 80
    return decorator
81 81

  
82

  
82 83
@call('set_quota')
83 84
def send_quota(users, client=None):
84 85
    data = []
......
91 92
            import_limit = None
92 93
            export_limit = None
93 94
            flags = 0
94
            args = (user.email, resource, key, quantity, capacity, import_limit,
95
                    export_limit, flags)
95
            args = (
96
                user.email, resource, key, quantity, capacity, import_limit,
97
                export_limit, flags)
96 98
            append(args)
97 99
    return data
98 100

  
......
172 174

  
173 175
SECOND_RESOLUTION = 1
174 176

  
177

  
175 178
def total_seconds(timedelta_object):
176 179
    return timedelta_object.seconds + timedelta_object.days * 86400
177 180

  
181

  
178 182
def iter_timeline(timeline, before):
179 183
    if not timeline:
180 184
        return
......
186 190
    t['issue_time'] = before
187 191
    yield t
188 192

  
193

  
189 194
def _usage_units(timeline, after, before, details=0):
190 195

  
191 196
    t_total = 0
......
226 231
            'total',
227 232
            point['resource'],
228 233
            issue_time,
229
            uu_total/t_total,
234
            uu_total / t_total,
230 235
            uu_total)
231 236

  
237

  
232 238
def usage_units(timeline, after, before, details=0):
233 239
    return list(_usage_units(timeline, after, before, details=details))
234 240

  
241

  
235 242
def traffic_units(timeline, after, before, details=0):
236 243
    tu_total = 0
237 244
    target = None
......
263 270
            'total',
264 271
            point['resource'],
265 272
            issue_time,
266
            tu_total//len(timeline),
273
            tu_total // len(timeline),
267 274
            tu_total)
268 275

  
276

  
269 277
def timeline_charge(entity, resource, after, before, details, charge_type):
270 278
    key = '1'
271 279
    if charge_type == 'charge_usage':
......
278 286

  
279 287
    quotaholder = QuotaholderHTTP(QUOTA_HOLDER_URL)
280 288
    timeline = quotaholder.get_timeline(
281
                            context         =   {},
282
                            after           =   after,
283
                            before          =   before,
284
                            get_timeline    =   [[entity, resource, key]])
289
        context={},
290
        after=after,
291
        before=before,
292
        get_timeline=[[entity, resource, key]])
285 293
    cu = charge_units(timeline, after, before, details=details)
286 294
    return cu
287

  
b/snf-astakos-app/astakos/im/forms.py
35 35
from django import forms
36 36
from django.utils.translation import ugettext as _
37 37
from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
38
                                       PasswordResetForm, PasswordChangeForm
39
                                       )
38
                                       PasswordResetForm, PasswordChangeForm,
39
                                       SetPasswordForm)
40 40
from django.core.mail import send_mail
41 41
from django.contrib.auth.tokens import default_token_generator
42 42
from django.template import Context, loader
......
49 49

  
50 50
from astakos.im.models import (AstakosUser, EmailChange, AstakosGroup,
51 51
                               Invitation, Membership, GroupKind, Resource,
52
                               get_latest_terms)
52
                               get_latest_terms, RESOURCE_SEPARATOR)
53 53
from astakos.im.settings import (INVITATIONS_PER_LEVEL, BASEURL, SITENAME,
54 54
                                 RECAPTCHA_PRIVATE_KEY, RECAPTCHA_ENABLED,
55 55
                                 DEFAULT_CONTACT_EMAIL, LOGGING_LEVEL,
56
                                 PASSWORD_RESET_EMAIL_SUBJECT)
57

  
56
                                 PASSWORD_RESET_EMAIL_SUBJECT,
57
                                 NEWPASSWD_INVALIDATE_TOKEN)
58 58
from astakos.im.widgets import DummyWidget, RecaptchaWidget
59 59
from astakos.im.functions import send_change_email
60 60

  
......
176 176
        ro = ('email', 'username',)
177 177
        for f in ro:
178 178
            self.fields[f].widget.attrs['readonly'] = True
179
    
179

  
180 180
    def save(self, commit=True):
181 181
        user = super(InvitedLocalUserCreationForm, self).save(commit=False)
182 182
        level = user.invitation.inviter.level + 1
......
193 193
        model = AstakosUser
194 194
        fields = ("email", "first_name", "last_name",
195 195
                  "third_party_identifier", "has_signed_terms")
196
    
196

  
197 197
    def __init__(self, *args, **kwargs):
198 198
        """
199 199
        Changes the order of fields, and removes the username field.
......
218 218
                % (reverse('latest_terms'), _("the terms"))
219 219
            self.fields['has_signed_terms'].label = \
220 220
                mark_safe("I agree with %s" % terms_link_html)
221
    
221

  
222 222
    def clean_email(self):
223 223
        email = self.cleaned_data['email']
224 224
        if not email:
......
273 273
class ShibbolethUserCreationForm(ThirdPartyUserCreationForm):
274 274
    additional_email = forms.CharField(
275 275
        widget=forms.HiddenInput(), label='', required=False)
276
    
276

  
277 277
    def __init__(self, *args, **kwargs):
278 278
        super(ShibbolethUserCreationForm, self).__init__(*args, **kwargs)
279 279
        self.fields.keyOrder.append('additional_email')
......
282 282
        field = self.fields[name]
283 283
        self.initial['additional_email'] = self.initial.get(name,
284 284
                                                            field.initial)
285
    
285

  
286 286
    def clean_email(self):
287 287
        email = self.cleaned_data['email']
288 288
        for user in AstakosUser.objects.filter(email=email):
......
295 295
        return email
296 296

  
297 297

  
298
class InvitedShibbolethUserCreationForm(ShibbolethUserCreationForm, InvitedThirdPartyUserCreationForm):
298
class InvitedShibbolethUserCreationForm(ShibbolethUserCreationForm,
299
                                        InvitedThirdPartyUserCreationForm):
299 300
    pass
300 301

  
301 302

  
......
304 305
    recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
305 306
    recaptcha_response_field = forms.CharField(
306 307
        widget=RecaptchaWidget, label='')
307
    
308

  
308 309
    def __init__(self, *args, **kwargs):
309 310
        was_limited = kwargs.get('was_limited', False)
310 311
        request = kwargs.get('request', None)
......
322 323
        if was_limited and RECAPTCHA_ENABLED:
323 324
            self.fields.keyOrder.extend(['recaptcha_challenge_field',
324 325
                                         'recaptcha_response_field', ])
325
    
326

  
327
    def clean_username(self):
328
        if 'username' in self.cleaned_data:
329
            return self.cleaned_data['username'].lower()
330

  
326 331
    def clean_recaptcha_response_field(self):
327 332
        if 'recaptcha_challenge_field' in self.cleaned_data:
328 333
            self.validate_captcha()
......
340 345
        if not check.is_valid:
341 346
            raise forms.ValidationError(
342 347
                _('You have not entered the correct words'))
343
    
348

  
344 349
    def clean(self):
345 350
        super(LoginForm, self).clean()
346 351
        if self.user_cache and self.user_cache.provider not in ('local', ''):
......
351 356
class ProfileForm(forms.ModelForm):
352 357
    """
353 358
    Subclass of ``ModelForm`` for permiting user to edit his/her profile.
354
    Most of the fields are readonly since the user is not allowed to change them.
359
    Most of the fields are readonly since the user is not allowed to change
360
    them.
355 361

  
356
    The class defines a save method which sets ``is_verified`` to True so as the user
357
    during the next login will not to be redirected to profile page.
362
    The class defines a save method which sets ``is_verified`` to True so as the
363
    user during the next login will not to be redirected to profile page.
358 364
    """
359 365
    renew = forms.BooleanField(label='Renew token', required=False)
360 366

  
......
509 515
    Extends PasswordChangeForm by enabling user
510 516
    to optionally renew also the token.
511 517
    """
512
    renew = forms.BooleanField(label='Renew token', required=False)
518
    if not NEWPASSWD_INVALIDATE_TOKEN:
519
        renew = forms.BooleanField(label='Renew token', required=False,
520
                                   initial=True,
521
                                   help_text='Unsetting this may result in security risk.')
513 522

  
514 523
    def __init__(self, user, *args, **kwargs):
515 524
        super(ExtendedPasswordChangeForm, self).__init__(user, *args, **kwargs)
516 525

  
517 526
    def save(self, commit=True):
518
        user = super(ExtendedPasswordChangeForm, self).save(commit=False)
519
        if self.cleaned_data.get('renew'):
520
            user.renew_token()
521
        if commit:
522
            user.save()
523
        return user
527
        if NEWPASSWD_INVALIDATE_TOKEN or self.cleaned_data.get('renew'):
528
            self.user.renew_token()
529
        return super(ExtendedPasswordChangeForm, self).save(commit=commit)
524 530

  
525 531

  
526 532
class AstakosGroupCreationForm(forms.ModelForm):
......
532 538
    name = forms.URLField()
533 539
    moderation_enabled = forms.BooleanField(
534 540
        help_text="Check if you want to approve members participation manually",
535
        required=False   
541
        required=False
536 542
    )
537
    
543
    max_participants = forms.IntegerField(
544
        widget=forms.HiddenInput(), label='', required=False
545
    )
546

  
538 547
    class Meta:
539 548
        model = AstakosGroup
540 549

  
541 550
    def __init__(self, *args, **kwargs):
542
        try:
543
            resources = kwargs.pop('resources')
544
        except KeyError:
545
            resources = {}
551
        #update QueryDict
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff