Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.2 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.functions 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, policies=()):
163
        user = self._lookup_user(user_id)
164
        rejected = []
165
        append = rejected.append
166
        for p in policies:
167
            try:
168
                user.add_resource_policy(**p)
169
            except (ObjectDoesNotExist, IntegrityError), e:
170
                append((service, resource, e))
171
        return rejected
172

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

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

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

    
227
    @safe
228
    def list_users(self, filter=()):
229
        return self._list(AstakosUser, filter=filter)
230

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

    
261
    @safe
262
    def list_resources(self, filter=()):
263
        return self._list(Resource, filter=filter)
264

    
265
    @safe
266
    def create_service(self, **kwargs):
267
        resources = kwargs.pop('resources', ())
268
        s = self._create_object(Service, **kwargs)
269
        s.resources = resources
270
        return self._details(s)
271

    
272
    @safe
273
    def remove_services(self, ids=()):
274
        # TODO return information for unknown ids
275
        q = Service.objects.filter(id__in=ids)
276
        q.delete()
277

    
278
    @safe
279
    def update_service(self, service_id, renew_token=False, **kwargs):
280
        s = self._update_object(Service, service_id, save=False, **kwargs)
281
        if renew_token:
282
            s.renew_token()
283

    
284
        if kwargs or renew_token:
285
            s.save()
286

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

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