Statistics
| Branch: | Tag: | Revision:

root / rapi / api.py @ 534c4a37

History | View | Annotate | Download (13.8 kB)

1
from tastypie.resources import ModelResource
2
from flowspy.flowspec.models import *
3
from tastypie import fields
4
from django.contrib.auth.models import User
5
from flowspy.accounts.models import *
6
from tastypie.authentication import BasicAuthentication
7
from tastypie.authorization import Authorization
8
from flowspy.djangobackends.restauthBackend import restauthBackend
9
from django.template.loader import render_to_string
10
from django import forms
11
from flowspy.flowspec.forms import *
12
from ipaddr import * 
13
from flowspy.flowspec.views import send_new_mail as new_mail
14
from flowspy.flowspec.views import get_peer_techc_mails as get_peer_techcs_contact
15
from django.conf import settings
16
from django.core.exceptions import ObjectDoesNotExist
17
from tastypie.exceptions import *
18
from tastypie import http
19
import logging
20
import datetime
21
from django.shortcuts import get_object_or_404
22
from copy import deepcopy
23

    
24

    
25
LOG_FILENAME = os.path.join(settings.LOG_FILE_LOCATION, 'celery_jobs.log')
26
#FORMAT = '%(asctime)s %(levelname)s: %(message)s'
27
#logging.basicConfig(format=FORMAT)
28
formatter = logging.Formatter('%(asctime)s %(levelname)s %(clientip)s %(user)s: %(message)s')
29

    
30
logger = logging.getLogger(__name__)
31
logger.setLevel(logging.DEBUG)
32
handler = logging.FileHandler(LOG_FILENAME)
33
handler.setFormatter(formatter)
34
logger.addHandler(handler)
35

    
36
class SourcePortRes(ModelResource):
37
    class Meta:
38
        queryset = MatchPort.objects.all()
39
        resource_name = 'sourceport'
40
        fields = ['port']
41
        allowed_methods = ['get']
42

    
43
class DestPortRes(ModelResource):
44
    class Meta:
45
        queryset = MatchPort.objects.all()
46
        resource_name = 'destinationport'
47
        fields = ['port']
48
        allowed_methods = ['get']
49
        
50
class PortRes(ModelResource):
51
    class Meta:
52
        queryset = MatchPort.objects.all()
53
        resource_name = 'port'
54
        fields = ['port']
55
        allowed_methods = ['get']        
56

    
57
class ThenResource(ModelResource):
58
    class Meta:
59
        queryset = ThenAction.objects.all()
60
        resource_name = 'then'
61
        allowed_methods = ['get']
62
        fields = ['action', 'action_value']
63

    
64
class RuleResource(ModelResource):
65
    name = fields.CharField(attribute='name')
66
    sourceport = fields.ManyToManyField(SourcePortRes, 'sourceport', full=True, null=True)
67
    destinationport = fields.ManyToManyField(DestPortRes, 'destinationport', full=True, null=True)
68
    then = fields.ManyToManyField(ThenResource, 'then', full=True, null=True)
69
    port = fields.ManyToManyField(PortRes, 'port', full=True, null=True)
70
       
71
    def get_object_list(self, request):
72
        group_routes = []
73
        user = request.user
74
        peer = user.get_profile().peer
75
        if peer:
76
            peer_members = UserProfile.objects.filter(peer=peer)
77
            users = [prof.user for prof in peer_members]
78
        return super(RuleResource, self).get_object_list(request).filter(applier__in=users)
79
    
80
    def obj_create(self, bundle, request=None, **kwargs):
81
        then_list = []
82
        sourceport_list = []
83
        destport_list = []
84
        port_list = []
85
        bundle.obj = self._meta.object_class()
86

    
87
        for key, value in kwargs.items():
88
            setattr(bundle.obj, key, value)
89

    
90
        bundle = self.full_hydrate(bundle)
91
        m2m_bundle = self.hydrate_m2m(bundle)
92
        
93
        then_action_dict = m2m_bundle.data['then'][0].obj.__dict__
94
        then_action_pk = ThenAction.objects.get(action=then_action_dict['action'], action_value=then_action_dict['action_value']).pk
95
        then_list.append(then_action_pk)
96
        try:
97
            sourceport_dict = m2m_bundle.data['sourceport']
98
        except:
99
            sourceport_dict = None
100
        if sourceport_dict:
101
            for sp in sourceport_dict:
102
                sourceport_pk = get_port_pk_or_create(sp)
103
                sourceport_list.append(sourceport_pk)
104

    
105
        try:
106
            destport_dict = m2m_bundle.data['destinationport']
107
        except:
108
            destport_dict = None
109
        if destport_dict:
110
            for dp in destport_dict:
111
                destport_pk = get_port_pk_or_create(dp)
112
                destport_list.append(destport_pk)
113

    
114
        try:
115
            port_dict = m2m_bundle.data['port']
116
        except:
117
            port_dict = None
118
        if port_dict:
119
            for prt in port_dict:
120
                port_pk = get_port_pk_or_create(prt) 
121
                port_list.append(port_pk)
122

    
123
        datadict = bundle.obj.__dict__
124
        datadict['applier'] =  request.user.pk
125
        datadict['comments'] =  "%s: %s" %("Submitted via REST API", datadict['comments'])
126
        datadict['then'] =  then_list
127
        datadict['sourceport'] =  sourceport_list
128
        datadict['destinationport'] =  destport_list
129
        datadict['port'] =  port_list
130
        form = RouteForm(data=datadict)
131

    
132
        if form.is_valid():
133
            route=form.save(commit=False)
134
            route.applier = request.user
135
            route.status = "PENDING"
136
            route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
137
            route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
138
            route.save()
139
            form.save_m2m()
140
            route.commit_add()
141
            requesters_address = request.META['HTTP_X_FORWARDED_FOR']
142
            mail_body = render_to_string("rule_add_mail.txt",
143
                                             {"route": route, "address": requesters_address})
144
            user_mail = "%s" %route.applier.email
145
            user_mail = user_mail.split(';')
146
            new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s creation request submitted by %s via REST api" %(route.name, route.applier.username),
147
                              mail_body, settings.SERVER_EMAIL, user_mail,
148
                              get_peer_techcs_contact(route.applier))
149
            d = { 'clientip' : "REST_API: %s" %requesters_address, 'user' : route.applier.username }
150
            logger.info(mail_body, extra=d)
151
        else:
152
            raise ApiFieldError("ERRORS %s %s" %(form.errors, form.__dict__))
153

    
154
        return bundle
155
    
156
    def obj_delete(self, request=None, **kwargs):
157
        obj = kwargs.pop('pk', None)
158
        if obj:
159
            try:
160
                route = Route.objects.get(pk=obj)
161
            except:
162
                raise NotFound("A model instance matching the provided arguments could not be found.")
163
            applier_peer = route.applier.get_profile().peer
164
            requester_peer = request.user.get_profile().peer
165
            if applier_peer == requester_peer:
166
                route.status = "PENDING"
167
                route.expires = datetime.date.today()
168
                route.save()
169
                route.commit_delete()
170
                requesters_address = request.META['HTTP_X_FORWARDED_FOR']
171
                mail_body = render_to_string("rule_delete_mail.txt",
172
                                                 {"route": route, "address": requesters_address})
173
                user_mail = "%s" %route.applier.email
174
                user_mail = user_mail.split(';')
175
                new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s removal request submitted by %s" %(route.name, route.applier.username), 
176
                                  mail_body, settings.SERVER_EMAIL, user_mail,
177
                                 get_peer_techcs_contact(route.applier))
178
                d = { 'clientip' : "REST_API: %s" %requesters_address, 'user' : route.applier.username }
179
                logger.info(mail_body, extra=d)
180
            else:
181
                raise NotFound("A model instance matching the provided arguments could not be found.")
182
        else:
183
            raise NotFound("A model instance matching the provided arguments could not be found.")
184
        
185
    def delete_list(self, request, **kwargs):
186
        return http.HttpForbidden()
187

    
188
    def obj_update(self, bundle, request=None, **kwargs):
189
        then_list = []
190
        sourceport_list = []
191
        destport_list = []
192
        port_list = []
193
        applier = request.user.pk
194
        applier_peer = request.user.get_profile().peer
195
        route_edit = get_object_or_404(Route, pk=kwargs['pk'])
196
        route_edit_applier_peer = route_edit.applier.get_profile().peer
197
        if applier_peer != route_edit_applier_peer:
198
            return http.HttpForbidden()
199

    
200
        route_original = deepcopy(route_edit)
201
        if not bundle.obj or not bundle.obj.pk:
202

    
203
            try:
204
                bundle.obj = self.get_object_list(request).model()
205
                bundle.data.update(kwargs)
206
                bundle = self.full_hydrate(bundle)
207
                lookup_kwargs = kwargs.copy()
208

    
209
                for key in kwargs.keys():
210
                    if key == 'pk':
211
                        continue
212
                    elif getattr(bundle.obj, key, NOT_AVAILABLE) is not NOT_AVAILABLE:
213
                        lookup_kwargs[key] = getattr(bundle.obj, key)
214
                    else:
215
                        del lookup_kwargs[key]
216
            except:
217
                # if there is trouble hydrating the data, fall back to just
218
                # using kwargs by itself (usually it only contains a "pk" key
219
                # and this will work fine.
220
                lookup_kwargs = kwargs
221

    
222
            try:
223
                bundle.obj = self.obj_get(request, **lookup_kwargs)
224
            except ObjectDoesNotExist:
225
                raise NotFound("A model instance matching the provided arguments could not be found.")
226

    
227
        bundle = self.full_hydrate(bundle)
228
        m2m_bundle = self.hydrate_m2m(bundle)
229
        try:
230
            then_action_dict = m2m_bundle.data['then'][0].obj.__dict__
231
            then_action_pk = ThenAction.objects.get(action=then_action_dict['action'], action_value=then_action_dict['action_value']).pk
232
            then_list.append(then_action_pk)
233
        except:
234
            then_list.extend([x.pk for x in route_edit.then.all() if x])
235

    
236
        try:
237
            sourceport_dict = m2m_bundle.data['sourceport']
238
        except:
239
            sourceport_dict = None
240
        if sourceport_dict:
241
            for sp in sourceport_dict:
242
                sourceport_pk = get_port_pk_or_create(sp)
243
                sourceport_list.append(sourceport_pk)
244
        else:
245
             sourceport_list.extend([x.pk for x in route_edit.sourceport.all() if x])
246

    
247
        try:
248
            destport_dict = m2m_bundle.data['destinationport']
249
        except:
250
            destport_dict = None
251
        if destport_dict:
252
            for dp in destport_dict:
253
                destport_pk = get_port_pk_or_create(dp)
254
                destport_list.append(destport_pk)
255
        else:
256
             destport_list.extend([x.pk for x in route_edit.destinationport.all() if x])
257
        try:
258
            port_dict = m2m_bundle.data['port']
259
        except:
260
            port_dict = None
261
        if port_dict:
262
            for prt in port_dict:
263
                port_pk = get_port_pk_or_create(prt)
264
                port_list.append(port_pk)
265
        else:
266
            port_list.extend([x.pk for x in route_edit.port.all() if x])
267
        
268
    
269
        datadict = bundle.obj.__dict__
270
        datadict['applier'] =  request.user.pk
271
        comment_text = "Submitted via REST API"
272
        if comment_text not in datadict['comments']:
273
            datadict['comments'] =  "%s: %s" %("Submitted via REST API", datadict['comments'])
274
        datadict['then'] =  then_list
275
        datadict['sourceport'] =  sourceport_list
276
        datadict['destinationport'] =  destport_list
277
        datadict['port'] =  port_list
278
        datadict['name'] =  route_edit.name
279
        datadict['expires'] = datetime.date.today() + datetime.timedelta(days = settings.EXPIRATION_DAYS_OFFSET)
280
        form = RouteForm(data=datadict, instance = route_edit)
281

    
282
        if form.is_valid():
283
            route=form.save(commit=False)
284
            route.name = route_original.name
285
            route.applier = request.user
286
            route.status = "PENDING"
287
            route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
288
            route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
289
            route.save()
290
            form.save_m2m()
291
            route.commit_edit()
292
            requesters_address = request.META['HTTP_X_FORWARDED_FOR']
293
            mail_body = render_to_string("rule_edit_mail.txt",
294
                                             {"route": route, "address": requesters_address})
295
            user_mail = "%s" %route.applier.email
296
            user_mail = user_mail.split(';')
297
            new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s edit request submitted by %s via REST api" %(route.name, route.applier.username),
298
                              mail_body, settings.SERVER_EMAIL, user_mail,
299
                              get_peer_techcs_contact(route.applier))
300
            d = { 'clientip' : "REST_API: %s" %requesters_address, 'user' : route.applier.username }
301
            logger.info(mail_body, extra=d)
302
        else:
303
            raise ApiFieldError("ERRORS %s %s" %(form.errors, form.__dict__))
304
        return bundle
305
    
306
    
307
    class Meta:
308
        queryset = Route.objects.all()
309
        default_format = "application/json"
310
        resource_name = 'rule'
311
        allowed_methods = ['get', 'post', 'delete', 'put']
312
        fields = ['comments', 'source', 'destination', 'expires', 'status', 'id', 'sourceport', 'destinationport', 'then']
313
        authentication = BasicAuthentication(restauthBackend())
314
        authorization= Authorization()
315
        
316
def get_port_pk_or_create(prt):
317
    try:
318
        port_pk = MatchPort.objects.get(port=prt.obj.__dict__['port']).pk
319
    except:
320
        try:
321
            assert (int(prt.obj.__dict__['port']))
322
            mp = MatchPort(port=int(int(prt.obj.__dict__['port'])))
323
            mp.save()
324
            port_pk=mp.pk
325
        except:
326
            raise ApiFieldError("Port should be an integer")
327
    return port_pk