1 # Copyright 2011-2012 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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.
34 from django.db import IntegrityError, transaction
35 from django.core.exceptions import ObjectDoesNotExist
37 from functools import wraps
38 from smtplib import SMTPException
40 from astakos.im.models import (
41 AstakosUser, AstakosGroup, GroupKind, Resource, Service, RESOURCE_SEPARATOR
43 from astakos.im.api.backends.base import BaseBackend, SuccessResult, FailureResult
44 from astakos.im.api.backends.errors import (
45 ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist
47 from astakos.im.util import reserved_email, model_to_dict
48 from astakos.im.endpoints.quotaholder import get_quota
52 logger = logging.getLogger(__name__)
54 DEFAULT_CONTENT_TYPE = None
58 """Decorator function for views that implement an API method."""
59 @transaction.commit_manually
61 def wrapper(self, *args, **kwargs):
62 logger.debug('%s %s %s' % (func, args, kwargs))
64 data = func(self, *args, **kwargs) or ()
67 transaction.rollback()
68 return FailureResult(getattr(e, 'message', e))
71 return SuccessResult(data)
75 class DjangoBackend(BaseBackend):
76 def _lookup_object(self, model, **kwargs):
78 Returns an object of the specific model matching the given lookup
82 raise MissingIdentifier
84 return model.objects.get(**kwargs)
85 except model.DoesNotExist:
86 raise ItemNotExists(model._meta.verbose_name, **kwargs)
87 except model.MultipleObjectsReturned:
88 raise MultipleItemsExist(model._meta.verbose_name, **kwargs)
90 def _lookup_user(self, id):
92 Returns an AstakosUser having this id.
94 if not isinstance(id, int):
95 raise TypeError('User id should be of type int')
96 return self._lookup_object(AstakosUser, id=id)
98 def _lookup_service(self, id):
100 Returns an Service having this id.
102 if not isinstance(id, int):
103 raise TypeError('Service id should be of type int')
104 return self._lookup_object(Service, id=id)
106 def _list(self, model, filter=()):
107 q = model.objects.all()
109 q = q.filter(id__in=filter)
110 return map(lambda o: model_to_dict(o, exclude=[]), q)
112 def _create_object(self, model, **kwargs):
113 o = model.objects.create(**kwargs)
117 def _update_object(self, model, id, save=True, **kwargs):
118 o = self._lookup_object(model, id=id)
120 o.__dict__.update(kwargs)
126 def update_user(self, user_id, renew_token=False, **kwargs):
127 user = self._update_object(AstakosUser, user_id, save=False, **kwargs)
130 if kwargs or renew_token:
134 def create_user(self, **kwargs):
135 policies = kwargs.pop('policies', ())
136 permissions = kwargs.pop('permissions', ())
137 groups = kwargs.pop('groups', ())
138 password = kwargs.pop('password', None)
140 u = self._create_object(AstakosUser, **kwargs)
143 u.set_password(password)
144 u.permissions = permissions
145 u.policies = policies
146 u.extended_groups = groups
147 return self._list(AstakosUser, filter=(u.id,))
150 def add_policies(self, user_id, update=False, policies=()):
151 user = self._lookup_user(user_id)
153 append = rejected.append
155 service = p.get('service')
156 resource = p.get('resource')
157 uplimit = p.get('uplimit')
159 user.add_policy(service, resource, uplimit, update)
160 except (ObjectDoesNotExist, IntegrityError), e:
161 append((service, resource, e))
165 def remove_policies(self, user_id, policies=()):
166 user = self._lookup_user(user_id)
170 append = rejected.append
172 service = p.get('service')
173 resource = p.get('resource')
175 user.delete_policy(service, resource)
176 except ObjectDoesNotExist, e:
177 append((service, resource, e))
180 def add_permissions(self, user_id, permissions=()):
181 user = self._lookup_user(user_id)
183 append = rejected.append
184 for p in permissions:
186 user.add_permission(p)
187 except IntegrityError, e:
192 def remove_permissions(self, user_id, permissions=()):
193 user = self._lookup_user(user_id)
195 append = rejected.append
196 for p in permissions:
198 user.remove_permission(p)
199 except (ObjectDoesNotExist, IntegrityError), e:
204 def invite_users(self, senderid, recipients=()):
205 user = self._lookup_user(senderid)
207 append = rejected.append
210 user.invite(r.get('email'), r.get('realname'))
211 except (IntegrityError, SMTPException), e:
216 def list_users(self, filter=()):
217 return self._list(AstakosUser, filter=filter)
220 def get_resource_usage(self, user_id):
221 user = self._lookup_user(user_id)
222 c, data = get_quota((user,))
224 append = resources.append
226 t = (i if i else 0 for i in t)
227 (entity, name, quantity, capacity, importLimit, exportLimit,
228 imported, exported, returned, released, flags) = t
229 service, sep, resource = name.partition(RESOURCE_SEPARATOR)
230 resource = Resource.objects.select_related().get(
231 service__name=service, name=resource)
233 description=resource.desc,
234 unit=resource.unit or '',
235 maxValue=quantity + capacity,
236 currValue=quantity + imported - released - exported + returned)
241 def list_resources(self, filter=()):
242 return self._list(Resource, filter=filter)
245 def create_service(self, **kwargs):
246 resources = kwargs.pop('resources', ())
247 s = self._create_object(Service, **kwargs)
248 s.resources = resources
249 return self._list(Service, filter=(s.id,))
252 def remove_services(self, ids=()):
253 # TODO return information for unknown ids
254 q = Service.objects.filter(id__in=ids)
258 def update_service(self, service_id, renew_token=False, **kwargs):
259 s = self._update_object(Service, service_id, save=False, **kwargs)
263 if kwargs or renew_token:
267 def add_resources(self, service_id, update=False, resources=()):
268 s = self._lookup_service(service_id)
270 append = rejected.append
274 resource_id = rr.pop('id', None)
277 raise MissingIdentifier
278 resource = self._update_object(Resource, resource_id, **rr)
280 resource = self._create_object(Resource, service=s, **rr)
286 def remove_resources(self, service_id, ids=()):
287 # TODO return information for unknown ids
288 q = Resource.objects.filter(service__id=service_id,
293 def create_group(self, **kwargs):
294 policies = kwargs.pop('policies', ())
295 permissions = kwargs.pop('permissions', ())
296 members = kwargs.pop('members', ())
297 owners = kwargs.pop('owners', ())
298 kwargs['kind'] = self._lookup_object(
299 GroupKind, name=kwargs.get('kind', 'course')
302 g = self._create_object(AstakosGroup, **kwargs)
304 g.permissions = permissions
305 g.policies = policies
306 # g.members = members
308 return self._list(AstakosGroup, filter=(g.id,))