Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / api / backends / lib / django / __init__.py @ fcc1e93f

History | View | Annotate | Download (10 kB)

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
from django.conf import settings
37

    
38
from functools import wraps
39
from smtplib import SMTPException
40

    
41
from astakos.im.models import (
42
    AstakosUser,
43
    Resource, Service, RESOURCE_SEPARATOR,
44
    Project, ProjectApplication, ProjectMembership)
45
from astakos.im.api.backends.base import (
46
    BaseBackend, SuccessResult, FailureResult)
47
from astakos.im.api.backends.errors import (
48
    ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist)
49

    
50
from astakos.im.util import reserved_email, model_to_dict
51
from astakos.im.endpoints.qh import get_quota
52
try:
53
    from astakos.im.messages import astakos_messages
54
except:
55
    pass
56

    
57
import logging
58

    
59
logger = logging.getLogger(__name__)
60

    
61
DEFAULT_CONTENT_TYPE = None
62

    
63

    
64
def safe(func):
65
    """Decorator function for views that implement an API method."""
66
    @wraps(func)
67
    def wrapper(self, *args, **kwargs):
68
        logger.debug('%s %s %s' % (func, args, kwargs))
69
        try:
70
            data = func(self, *args, **kwargs) or ()
71
        except Exception, e:
72
            logger.exception(e)
73
            return FailureResult(e)
74
        else:
75
            return SuccessResult(data)
76
    return wrapper
77

    
78

    
79
class DjangoBackend(BaseBackend):
80
    def _lookup_object(self, model, **kwargs):
81
        """
82
        Returns an object of the specific model matching the given lookup
83
        parameters.
84
        """
85
        if not kwargs:
86
            raise MissingIdentifier
87
        try:
88
            return model.objects.get(**kwargs)
89
        except model.DoesNotExist:
90
            raise ItemNotExists(model._meta.verbose_name, **kwargs)
91
        except model.MultipleObjectsReturned:
92
            raise MultipleItemsExist(model._meta.verbose_name, **kwargs)
93

    
94
    def _lookup_user(self, id):
95
        """
96
        Returns an AstakosUser having this id.
97
        """
98
        if not isinstance(id, int):
99
            raise TypeError('User id should be of type int')
100
        return self._lookup_object(AstakosUser, id=id)
101

    
102
    def _lookup_service(self, id):
103
        """
104
        Returns an Service having this id.
105
        """
106
        if not isinstance(id, int):
107
            raise TypeError('Service id should be of type int')
108
        return self._lookup_object(Service, id=id)
109

    
110
    def _list(self, model, filter=()):
111
        q = model.objects.all()
112
        if filter:
113
            q = q.filter(id__in=filter)
114
        return map(lambda o: self._details(o), q)
115

    
116
    def _details(self, obj):
117
        return model_to_dict(obj, exclude=[])
118

    
119
    def _create_object(self, model, **kwargs):
120
        o = model.objects.create(**kwargs)
121
        o.save()
122
        return o
123

    
124
    def _update_object(self, model, id, save=True, **kwargs):
125
        o = self._lookup_object(model, id=id)
126
        if kwargs:
127
            o.__dict__.update(kwargs)
128
        if save:
129
            o.save()
130
        return o
131

    
132
    @safe
133
    def update_user(self, user_id, renew_token=False, **kwargs):
134
        user = self._update_object(AstakosUser, user_id, save=False, **kwargs)
135
        if renew_token:
136
            user.renew_token()
137
        if kwargs or renew_token:
138
            user.save()
139

    
140
    @safe
141
    def create_user(self, **kwargs):
142
        policies = kwargs.pop('policies', ())
143
        permissions = kwargs.pop('permissions', ())
144
        groups = kwargs.pop('groups', ())
145
        password = kwargs.pop('password', None)
146
        provider = kwargs.pop('provider', 'local')
147

    
148
        u = self._create_object(AstakosUser, **kwargs)
149

    
150
        if password:
151
            u.set_password(password)
152
        u.permissions = permissions
153
        u.policies = policies
154
        u.extended_groups = groups
155

    
156
        if not u.has_auth_provider(provider):
157
            u.add_auth_provider(provider)
158

    
159
        return self._details(u)
160

    
161
    @safe
162
    def add_policies(self, user_id, update=False, policies=()):
163
        user = self._lookup_user(user_id)
164
        rejected = []
165
        append = rejected.append
166
        for p in policies:
167
            service = p.get('service')
168
            resource = p.get('resource')
169
            uplimit = p.get('uplimit')
170
            try:
171
                user.add_policy(service, resource, uplimit, update)
172
            except (ObjectDoesNotExist, IntegrityError), e:
173
                append((service, resource, e))
174
        return rejected
175

    
176
    @safe
177
    def remove_policies(self, user_id, policies=()):
178
        user = self._lookup_user(user_id)
179
        if not user:
180
            return user_id
181
        rejected = []
182
        append = rejected.append
183
        for p in policies:
184
            service = p.get('service')
185
            resource = p.get('resource')
186
            try:
187
                user.delete_policy(service, resource)
188
            except ObjectDoesNotExist, e:
189
                append((service, resource, e))
190
        return rejected
191
    
192
    @safe
193
    def add_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.add_permission(p)
200
            except IntegrityError, e:
201
                append((p, e))
202
        return rejected
203

    
204
    @safe
205
    def remove_permissions(self, user_id, permissions=()):
206
        user = self._lookup_user(user_id)
207
        rejected = []
208
        append = rejected.append
209
        for p in permissions:
210
            try:
211
                user.remove_permission(p)
212
            except (ObjectDoesNotExist, IntegrityError), e:
213
                append((p, e))
214
        return rejected
215

    
216
    @safe
217
    def invite_users(self, senderid, recipients=()):
218
        user = self._lookup_user(senderid)
219
        rejected = []
220
        append = rejected.append
221
        for r in recipients:
222
            email = r.get('email')
223
            realname = r.get('realname')
224
            try:
225
                user.invite(email, realname)
226
            except (IntegrityError, SMTPException), e:
227
                append((email, e))
228
        return rejected
229

    
230
    @safe
231
    def list_users(self, filter=()):
232
        return self._list(AstakosUser, filter=filter)
233

    
234
    @safe
235
    def get_resource_usage(self, user_id):
236
        user = self._lookup_user(user_id)
237
        data = get_quota((user,))
238
        resources = []
239
        append = resources.append
240
        for t in data:
241
            t = (i if i else 0 for i in t)
242
            (entity, name, quantity, capacity, importLimit, exportLimit,
243
             imported, exported, returned, released, flags) = t
244
            service, sep, resource = name.partition(RESOURCE_SEPARATOR)
245
            resource = Resource.objects.select_related().get(
246
                service__name=service, name=resource)
247
            d = dict(name=name,
248
                     description=resource.desc,
249
                     unit=resource.unit or '',
250
                     maxValue=quantity + capacity,
251
                     currValue=quantity + imported - released - exported + returned)
252
            append(d)
253
        return resources
254

    
255
    @safe
256
    def list_resources(self, filter=()):
257
        return self._list(Resource, filter=filter)
258

    
259
    @safe
260
    def create_service(self, **kwargs):
261
        resources = kwargs.pop('resources', ())
262
        s = self._create_object(Service, **kwargs)
263
        s.resources = resources
264
        return self._details(s)
265

    
266
    @safe
267
    def remove_services(self, ids=()):
268
        # TODO return information for unknown ids
269
        q = Service.objects.filter(id__in=ids)
270
        q.delete()
271

    
272
    @safe
273
    def update_service(self, service_id, renew_token=False, **kwargs):
274
        s = self._update_object(Service, service_id, save=False, **kwargs)
275
        if renew_token:
276
            s.renew_token()
277

    
278
        if kwargs or renew_token:
279
            s.save()
280

    
281
    @safe
282
    def add_resources(self, service_id, update=False, resources=()):
283
        s = self._lookup_service(service_id)
284
        rejected = []
285
        append = rejected.append
286
        for r in resources:
287
            try:
288
                rr = r.copy()
289
                resource_id = rr.pop('id', None)
290
                if update:
291
                    if not resource_id:
292
                        raise MissingIdentifier
293
                    resource = self._update_object(Resource, resource_id, **rr)
294
                else:
295
                    resource = self._create_object(Resource, service=s, **rr)
296
            except Exception, e:
297
                append((r, e))
298
        return rejected
299

    
300
    @safe
301
    def remove_resources(self, service_id, ids=()):
302
        # TODO return information for unknown ids
303
        q = Resource.objects.filter(service__id=service_id,
304
                                id__in=ids)
305
        q.delete()