__init__.py 10.2 KB
Newer Older
Olga Brani's avatar
Olga Brani committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Copyright 2011-2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

from django.db import IntegrityError, transaction
from django.core.exceptions import ObjectDoesNotExist

from functools import wraps
from smtplib import SMTPException

40
41
42
43
44
45
46
from astakos.im.models import (
    AstakosUser, AstakosGroup, GroupKind, Resource, Service, RESOURCE_SEPARATOR
)
from astakos.im.api.backends.base import BaseBackend, SuccessResult, FailureResult
from astakos.im.api.backends.errors import (
    ItemNotExists, ItemExists, MissingIdentifier, MultipleItemsExist
)
Olga Brani's avatar
Olga Brani committed
47
from astakos.im.util import reserved_email, model_to_dict
48
from astakos.im.endpoints.qh import get_quota
Olga Brani's avatar
Olga Brani committed
49
50
51
52
53
54
55
56

import logging

logger = logging.getLogger(__name__)

DEFAULT_CONTENT_TYPE = None


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


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

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

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

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

    def _create_object(self, model, **kwargs):
113
        o = model.objects.create(**kwargs)
Olga Brani's avatar
Olga Brani committed
114
115
116
117
118
119
120
121
122
123
124
        o.save()
        return o

    def _update_object(self, model, id, save=True, **kwargs):
        o = self._lookup_object(model, id=id)
        if kwargs:
            o.__dict__.update(kwargs)
        if save:
            o.save()
        return o

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

133
    @safe
Olga Brani's avatar
Olga Brani committed
134
135
136
137
138
139
140
141
142
143
144
145
146
    def create_user(self, **kwargs):
        policies = kwargs.pop('policies', ())
        permissions = kwargs.pop('permissions', ())
        groups = kwargs.pop('groups', ())
        password = kwargs.pop('password', None)

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

        if password:
            u.set_password(password)
        u.permissions = permissions
        u.policies = policies
        u.extended_groups = groups
147
        return self._list(AstakosUser, filter=(u.id,))
Olga Brani's avatar
Olga Brani committed
148

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

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

240
    @safe
Olga Brani's avatar
Olga Brani committed
241
242
243
    def list_resources(self, filter=()):
        return self._list(Resource, filter=filter)

244
    @safe
Olga Brani's avatar
Olga Brani committed
245
246
247
248
    def create_service(self, **kwargs):
        resources = kwargs.pop('resources', ())
        s = self._create_object(Service, **kwargs)
        s.resources = resources
249
        return self._list(Service, filter=(s.id,))
Olga Brani's avatar
Olga Brani committed
250

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

        if kwargs or renew_token:
            s.save()

266
    @safe
Olga Brani's avatar
Olga Brani committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
    def add_resources(self, service_id, update=False, resources=()):
        s = self._lookup_service(service_id)
        rejected = []
        append = rejected.append
        for r in resources:
            try:
                rr = r.copy()
                resource_id = rr.pop('id', None)
                if update:
                    if not resource_id:
                        raise MissingIdentifier
                    resource = self._update_object(Resource, resource_id, **rr)
                else:
                    resource = self._create_object(Resource, service=s, **rr)
            except Exception, e:
                append((r, e))
283
        return rejected
Olga Brani's avatar
Olga Brani committed
284
    
285
    @safe
Olga Brani's avatar
Olga Brani committed
286
287
288
289
290
291
    def remove_resources(self, service_id, ids=()):
        # TODO return information for unknown ids
        q = Resource.objects.filter(service__id=service_id,
                                id__in=ids)
        q.delete()
    
292
    @safe
Olga Brani's avatar
Olga Brani committed
293
294
295
296
297
298
299
300
301
302
    def create_group(self, **kwargs):
        policies = kwargs.pop('policies', ())
        permissions = kwargs.pop('permissions', ())
        members = kwargs.pop('members', ())
        owners = kwargs.pop('owners', ())

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

        g.permissions = permissions
        g.policies = policies
303
304
#         g.members = members
        g.owners = owners
Olga Brani's avatar
Olga Brani committed
305
        return self._list(AstakosGroup, filter=(g.id,))