Statistics
| Branch: | Tag: | Revision:

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

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.qh 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(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

    
148
        if not u.has_auth_provider('local'):
149
            u.add_auth_provider('local')
150

    
151
        return self._list(AstakosUser, filter=(u.id,))
152

    
153
    @safe
154
    def add_policies(self, user_id, update=False, policies=()):
155
        user = self._lookup_user(user_id)
156
        rejected = []
157
        append = rejected.append
158
        for p in policies:
159
            service = p.get('service')
160
            resource = p.get('resource')
161
            uplimit = p.get('uplimit')
162
            try:
163
                user.add_policy(service, resource, uplimit, update)
164
            except (ObjectDoesNotExist, IntegrityError), e:
165
                append((service, resource, e))
166
        return rejected
167

    
168
    @safe
169
    def remove_policies(self, user_id, policies=()):
170
        user = self._lookup_user(user_id)
171
        if not user:
172
            return user_id
173
        rejected = []
174
        append = rejected.append
175
        for p in policies:
176
            service = p.get('service')
177
            resource = p.get('resource')
178
            try:
179
                user.delete_policy(service, resource)
180
            except ObjectDoesNotExist, e:
181
                append((service, resource, e))
182
        return rejected
183
    @safe
184
    def add_permissions(self, user_id, permissions=()):
185
        user = self._lookup_user(user_id)
186
        rejected = []
187
        append = rejected.append
188
        for p in permissions:
189
            try:
190
                user.add_permission(p)
191
            except IntegrityError, e:
192
                append((p, e))
193
        return rejected
194

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

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

    
221
    @safe
222
    def list_users(self, filter=()):
223
        return self._list(AstakosUser, filter=filter)
224

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

    
246
    @safe
247
    def list_resources(self, filter=()):
248
        return self._list(Resource, filter=filter)
249

    
250
    @safe
251
    def create_service(self, **kwargs):
252
        resources = kwargs.pop('resources', ())
253
        s = self._create_object(Service, **kwargs)
254
        s.resources = resources
255
        return self._list(Service, filter=(s.id,))
256

    
257
    @safe
258
    def remove_services(self, ids=()):
259
        # TODO return information for unknown ids
260
        q = Service.objects.filter(id__in=ids)
261
        q.delete()
262

    
263
    @safe
264
    def update_service(self, service_id, renew_token=False, **kwargs):
265
        s = self._update_object(Service, service_id, save=False, **kwargs)
266
        if renew_token:
267
            s.renew_token()
268

    
269
        if kwargs or renew_token:
270
            s.save()
271

    
272
    @safe
273
    def add_resources(self, service_id, update=False, resources=()):
274
        s = self._lookup_service(service_id)
275
        rejected = []
276
        append = rejected.append
277
        for r in resources:
278
            try:
279
                rr = r.copy()
280
                resource_id = rr.pop('id', None)
281
                if update:
282
                    if not resource_id:
283
                        raise MissingIdentifier
284
                    resource = self._update_object(Resource, resource_id, **rr)
285
                else:
286
                    resource = self._create_object(Resource, service=s, **rr)
287
            except Exception, e:
288
                append((r, e))
289
        return rejected
290

    
291
    @safe
292
    def remove_resources(self, service_id, ids=()):
293
        # TODO return information for unknown ids
294
        q = Resource.objects.filter(service__id=service_id,
295
                                id__in=ids)
296
        q.delete()
297

    
298
    @safe
299
    def create_group(self, **kwargs):
300
        policies = kwargs.pop('policies', ())
301
        permissions = kwargs.pop('permissions', ())
302
        members = kwargs.pop('members', ())
303
        owners = kwargs.pop('owners', ())
304

    
305
        g = self._create_object(AstakosGroup, **kwargs)
306

    
307
        g.permissions = permissions
308
        g.policies = policies
309
#        g.members = members
310
        g.owners = owners
311
        return self._list(AstakosGroup, filter=(g.id,))