Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.6 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, AstakosGroup, GroupKind, Resource, Service, RESOURCE_SEPARATOR,
43
    Project, ProjectApplication, ProjectMembership, filter_queryset_by_property
44
)
45
from astakos.im.api.backends.base import BaseBackend, SuccessResult, FailureResult
46
from astakos.im.api.backends.errors import (
47
    ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist
48
)
49
# from astakos.im.api.backends.lib.notifications import EmailNotification
50

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

    
59
import logging
60

    
61
logger = logging.getLogger(__name__)
62

    
63
DEFAULT_CONTENT_TYPE = None
64

    
65

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

    
80

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

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

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

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

    
118
    def _details(self, obj):
119
        return model_to_dict(obj, exclude=[])
120

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

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

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

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

    
150
        u = self._create_object(AstakosUser, **kwargs)
151

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

    
158
        if not u.has_auth_provider(provider):
159
            u.add_auth_provider(provider)
160

    
161
        return self._details(u)
162

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

    
178
    @safe
179
    def remove_policies(self, user_id, policies=()):
180
        user = self._lookup_user(user_id)
181
        if not user:
182
            return user_id
183
        rejected = []
184
        append = rejected.append
185
        for p in policies:
186
            service = p.get('service')
187
            resource = p.get('resource')
188
            try:
189
                user.delete_policy(service, resource)
190
            except ObjectDoesNotExist, e:
191
                append((service, resource, e))
192
        return rejected
193
    
194
    @safe
195
    def add_permissions(self, user_id, permissions=()):
196
        user = self._lookup_user(user_id)
197
        rejected = []
198
        append = rejected.append
199
        for p in permissions:
200
            try:
201
                user.add_permission(p)
202
            except IntegrityError, e:
203
                append((p, e))
204
        return rejected
205

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

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

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

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

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

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

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

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

    
280
        if kwargs or renew_token:
281
            s.save()
282

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

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

    
309
    @safe
310
    def create_group(self, **kwargs):
311
        policies = kwargs.pop('policies', ())
312
        permissions = kwargs.pop('permissions', ())
313
        members = kwargs.pop('members', ())
314
        owners = kwargs.pop('owners', ())
315

    
316
        g = self._create_object(AstakosGroup, **kwargs)
317

    
318
        g.permissions = permissions
319
        g.policies = policies
320
#        g.members = members
321
        g.owners = owners
322
        return self._details(g)