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 |
Also available in: Unified diff