Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.5 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
from astakos.im.functions import activate
50

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

    
58
import logging
59

    
60
logger = logging.getLogger(__name__)
61

    
62
DEFAULT_CONTENT_TYPE = None
63

    
64

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

    
79

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

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

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

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

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

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

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

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

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

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

    
152
        if password:
153
            u.set_password(password)
154
            u.save()
155

    
156
        u.permissions = permissions
157
        u.policies = policies
158
        u.extended_groups = groups
159

    
160
        if not u.has_auth_provider(provider):
161
            u.add_auth_provider(provider)
162

    
163
        if active:
164
            activate(u)
165

    
166
        return self._details(u)
167

    
168
    @safe
169
    def add_policies(self, user_id, policies=()):
170
        user = self._lookup_user(user_id)
171
        rejected = []
172
        append = rejected.append
173
        for p in policies:
174
            try:
175
                user.add_resource_policy(**p)
176
            except (ObjectDoesNotExist, IntegrityError), e:
177
                append((service, resource, e))
178
        return rejected
179

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

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

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

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

    
238
    @safe
239
    def get_resource_usage(self, user_id):
240
        user = self._lookup_user(user_id)
241
        data = get_quota([user])
242
        if not data:
243
            return ()
244
        resources = []
245
        append = resources.append
246
        for t in data:
247
            t = (i if i else 0 for i in t)
248
            (entity, name, quantity, capacity, importLimit, exportLimit,
249
             imported, exported, returned, released, flags) = t
250
            service, sep, resource = name.partition(RESOURCE_SEPARATOR)
251
            resource = Resource.objects.select_related().get(
252
                service__name=service, name=resource)
253
            currValue=quantity + imported - released - exported + returned
254
            d = dict(name=name,
255
                     description=resource.desc,
256
                     unit=resource.unit or '',
257
                     help_text=resource.help_text,
258
                     help_text_input_each=resource.help_text_input_each,
259
                     is_abbreviation=resource.is_abbreviation,
260
                     report_desc=resource.report_desc,
261
                     placeholder=resource.placeholder,
262
                     verbose_name=resource.verbose_name,
263
                     display_name=resource.display_name,
264
                     pluralized_display_name=resource.pluralized_display_name,
265
                     maxValue=quantity + capacity,
266
                     currValue=currValue)
267
            append(d)
268
        return resources
269

    
270
    @safe
271
    def list_resources(self, filter=()):
272
        return self._list(Resource, filter=filter)
273

    
274
    @safe
275
    def create_service(self, **kwargs):
276
        resources = kwargs.pop('resources', ())
277
        s = self._create_object(Service, **kwargs)
278
        s.resources = resources
279
        return self._details(s)
280

    
281
    @safe
282
    def remove_services(self, ids=()):
283
        # TODO return information for unknown ids
284
        q = Service.objects.filter(id__in=ids)
285
        q.delete()
286

    
287
    @safe
288
    def update_service(self, service_id, renew_token=False, **kwargs):
289
        s = self._update_object(Service, service_id, save=False, **kwargs)
290
        if renew_token:
291
            s.renew_token()
292

    
293
        if kwargs or renew_token:
294
            s.save()
295

    
296
    @safe
297
    def add_resources(self, service_id, update=False, resources=()):
298
        s = self._lookup_service(service_id)
299
        rejected = []
300
        append = rejected.append
301
        for r in resources:
302
            try:
303
                rr = r.copy()
304
                resource_id = rr.pop('id', None)
305
                if update:
306
                    if not resource_id:
307
                        raise MissingIdentifier
308
                    resource = self._update_object(Resource, resource_id, **rr)
309
                else:
310
                    resource = self._create_object(Resource, service=s, **rr)
311
            except Exception, e:
312
                append((r, e))
313
        return rejected
314

    
315
    @safe
316
    def remove_resources(self, service_id, ids=()):
317
        # TODO return information for unknown ids
318
        q = Resource.objects.filter(service__id=service_id,
319
                                id__in=ids)
320
        q.delete()