Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / api / backends / lib / django / __init__.py~ @ 50dce93a

History | View | Annotate | Download (10.3 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

    
37
from functools import wraps
38
from smtplib import SMTPException
39

    
40
from astakos.im.models import (
41
    AstakosUser, AstakosGroup, GroupKind, Resource, Service, RESOURCE_SEPARATOR
42
)
43
from astakos.im.api.backends.base import BaseBackend, SuccessResult, FailureResult
44
from astakos.im.api.backends.errors import (
45
    ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist
46
)
47
from astakos.im.util import reserved_email, model_to_dict
48
from astakos.im.endpoints.quotaholder import get_quota
49

    
50
import logging
51

    
52
logger = logging.getLogger(__name__)
53

    
54
DEFAULT_CONTENT_TYPE = None
55

    
56

    
57
def safe(func):
58
    """Decorator function for views that implement an API method."""
59
    @transaction.commit_manually
60
    @wraps(func)
61
    def wrapper(self, *args, **kwargs):
62
        logger.debug('%s %s %s' % (func, args, kwargs))
63
        try:
64
            data = func(self, *args, **kwargs) or ()
65
        except Exception, e:
66
            logger.exception(e)
67
            transaction.rollback()
68
            return FailureResult(getattr(e, 'message', e))
69
        else:
70
            transaction.commit()
71
            return SuccessResult(data)
72
    return wrapper
73

    
74

    
75
class DjangoBackend(BaseBackend):
76
    def _lookup_object(self, model, **kwargs):
77
        """
78
        Returns an object of the specific model matching the given lookup
79
        parameters.
80
        """
81
        if not kwargs:
82
            raise MissingIdentifier
83
        try:
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)
89

    
90
    def _lookup_user(self, id):
91
        """
92
        Returns an AstakosUser having this id.
93
        """
94
        if not isinstance(id, int):
95
            raise TypeError('User id should be of type int')
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
        if not isinstance(id, int):
103
            raise TypeError('Service id should be of type int')
104
        return self._lookup_object(Service, id=id)
105

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

    
112
    def _create_object(self, model, **kwargs):
113
        o = model.objects.create(**kwargs)
114
        o.save()
115
        return o
116

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

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

    
133
    @safe
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)
139

    
140
        u = self._create_object(AstakosUser, **kwargs)
141

    
142
        if password:
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,))
148

    
149
    @safe
150
    def add_policies(self, user_id, update=False, policies=()):
151
        user = self._lookup_user(user_id)
152
        rejected = []
153
        append = rejected.append
154
        for p in policies:
155
            service = p.get('service')
156
            resource = p.get('resource')
157
            uplimit = p.get('uplimit')
158
            try:
159
                user.add_policy(service, resource, uplimit, update)
160
            except (ObjectDoesNotExist, IntegrityError), e:
161
                append((service, resource, e))
162
        return rejected
163
    
164
    @safe
165
    def remove_policies(self, user_id, policies=()):
166
        user = self._lookup_user(user_id)
167
        if not user:
168
            return user_id
169
        rejected = []
170
        append = rejected.append
171
        for p in policies:
172
            service = p.get('service')
173
            resource = p.get('resource')
174
            try:
175
                user.delete_policy(service, resource)
176
            except ObjectDoesNotExist, e:
177
                append((service, resource, e))
178
        return rejected
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
        return rejected
190
    
191
    @safe
192
    def remove_permissions(self, user_id, permissions=()):
193
        user = self._lookup_user(user_id)
194
        rejected = []
195
        append = rejected.append
196
        for p in permissions:
197
            try:
198
                user.remove_permission(p)
199
            except (ObjectDoesNotExist, IntegrityError), e:
200
                append((p, e))
201
        return rejected
202
    
203
    @safe
204
    def invite_users(self, senderid, recipients=()):
205
        user = self._lookup_user(senderid)
206
        rejected = []
207
        append = rejected.append
208
        for r in recipients:
209
            try:
210
                user.invite(r.get('email'), r.get('realname'))
211
            except (IntegrityError, SMTPException), e:
212
                append((email, e))
213
        return rejected
214
    
215
    @safe
216
    def list_users(self, filter=()):
217
        return self._list(AstakosUser, filter=filter)
218

    
219
    @safe
220
    def get_resource_usage(self, user_id):
221
        user = self._lookup_user(user_id)
222
        c, data = get_quota((user,))
223
        resources = []
224
        append = resources.append
225
        for t in data:
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)
232
            d = dict(name=name,
233
                     description=resource.desc,
234
                     unit=resource.unit or '',
235
                     maxValue=quantity + capacity,
236
                     currValue=quantity + imported - released - exported + returned)
237
            append(d)
238
        return resources
239

    
240
    @safe
241
    def list_resources(self, filter=()):
242
        return self._list(Resource, filter=filter)
243

    
244
    @safe
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,))
250

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

    
263
        if kwargs or renew_token:
264
            s.save()
265

    
266
    @safe
267
    def add_resources(self, service_id, update=False, resources=()):
268
        s = self._lookup_service(service_id)
269
        rejected = []
270
        append = rejected.append
271
        for r in resources:
272
            try:
273
                rr = r.copy()
274
                resource_id = rr.pop('id', None)
275
                if update:
276
                    if not resource_id:
277
                        raise MissingIdentifier
278
                    resource = self._update_object(Resource, resource_id, **rr)
279
                else:
280
                    resource = self._create_object(Resource, service=s, **rr)
281
            except Exception, e:
282
                append((r, e))
283
        return rejected
284
    
285
    @safe
286
    def remove_resources(self, service_id, ids=()):
287
        # TODO return information for unknown ids
288
        q = Resource.objects.filter(service__id=service_id,
289
                                id__in=ids)
290
        q.delete()
291
    
292
    @safe
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')
300
        )
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
308
        return self._list(AstakosGroup, filter=(g.id,))