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 *
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
21 from django.shortcuts import get_object_or_404
22 from copy import deepcopy
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')
30 logger = logging.getLogger(__name__)
31 logger.setLevel(logging.DEBUG)
32 handler = logging.FileHandler(LOG_FILENAME)
33 handler.setFormatter(formatter)
34 logger.addHandler(handler)
38 GET all: curl --user username:APIKEY --dump-header - -H "Content-Type: application/json" -X GET https://example.com/fod/api/v1/rule/?format=json
39 GET rule_by_pk: curl --user username:APIKEY --dump-header - -H "Content-Type: application/json" -X GET https://example.com/fod/api/v1/rule/{rule_pk}?format=json
40 UPDATE RULE (PUT) : curl --user username:APIKEY --dump-header - -H "Content-Type: application/json" -X PUT --data '{"comments": "", "destination": "", "destinationport": [{"port": "9090"}], "expires": "2012-02-21", "id": "11", "port": [], "source": "5.6.7.8/32", "sourceport": [{"port": "9090"}], "status": "ACTIVE", "then": [{"action": "discard", "action_value": ""}]}' https://example.com/fod/api/v1/rule/11/?format=json
41 DELETE RULE: curl --user username:APIKEY --dump-header - -H "Content-Type: application/json" -X DELETE https://example.com/fod/api/v1/rule/11/?format=json
42 CREATE RULE (POST): curl --user username:APIKEY --dump-header - -H "Content-Type: application/json" -X POST --data '{"name":"", "comments": "", "destination": "", "destinationport": [{"port": "9090"}], "expires": "2012-02-21", "port": [], "source": "5.6.7.8/32", "sourceport": [{"port": "9090"}], "status": "ACTIVE", "then": [{"action": "discard", "action_value": ""}]}' https://example.com/fod/api/v1/rule/?format=json
44 class SourcePortRes(ModelResource):
46 queryset = MatchPort.objects.all()
47 resource_name = 'sourceport'
49 allowed_methods = ['get']
51 class DestPortRes(ModelResource):
53 queryset = MatchPort.objects.all()
54 resource_name = 'destinationport'
56 allowed_methods = ['get']
58 class PortRes(ModelResource):
60 queryset = MatchPort.objects.all()
61 resource_name = 'port'
63 allowed_methods = ['get']
65 class ThenResource(ModelResource):
67 queryset = ThenAction.objects.all()
68 resource_name = 'then'
69 allowed_methods = ['get']
70 fields = ['action', 'action_value']
72 class RuleResource(ModelResource):
73 name = fields.CharField(attribute='name', help_text="A slug field (no spaces)")
74 sourceport = fields.ManyToManyField(SourcePortRes, 'sourceport', full=True, null=True)
75 destinationport = fields.ManyToManyField(DestPortRes, 'destinationport', full=True, null=True)
76 then = fields.ManyToManyField(ThenResource, 'then', full=True, null=True)
77 port = fields.ManyToManyField(PortRes, 'port', full=True, null=True)
79 def get_object_list(self, request):
80 logger.info('Got REST GET list request %s' %request)
83 peer = user.get_profile().peer
85 peer_members = UserProfile.objects.filter(peer=peer)
86 users = [prof.user for prof in peer_members]
87 return super(RuleResource, self).get_object_list(request).filter(applier__in=users)
89 def obj_create(self, bundle, request=None, **kwargs):
90 logger.info('Got REST POST request %s' %kwargs)
95 bundle.obj = self._meta.object_class()
97 for key, value in kwargs.items():
98 setattr(bundle.obj, key, value)
100 bundle = self.full_hydrate(bundle)
101 m2m_bundle = self.hydrate_m2m(bundle)
103 then_action_dict = m2m_bundle.data['then'][0].obj.__dict__
104 then_action_pk = ThenAction.objects.get(action=then_action_dict['action'], action_value=then_action_dict['action_value']).pk
105 then_list.append(then_action_pk)
107 sourceport_dict = m2m_bundle.data['sourceport']
109 sourceport_dict = None
111 for sp in sourceport_dict:
112 sourceport_pk = get_port_pk_or_create(sp)
113 sourceport_list.append(sourceport_pk)
116 destport_dict = m2m_bundle.data['destinationport']
120 for dp in destport_dict:
121 destport_pk = get_port_pk_or_create(dp)
122 destport_list.append(destport_pk)
125 port_dict = m2m_bundle.data['port']
129 for prt in port_dict:
130 port_pk = get_port_pk_or_create(prt)
131 port_list.append(port_pk)
133 datadict = bundle.obj.__dict__
134 datadict['applier'] = request.user.pk
135 datadict['comments'] = "%s: %s" %("Submitted via REST API", datadict['comments'])
136 datadict['then'] = then_list
137 datadict['sourceport'] = sourceport_list
138 datadict['destinationport'] = destport_list
139 datadict['port'] = port_list
140 form = RouteForm(data=datadict)
143 route=form.save(commit=False)
144 route.applier = request.user
145 route.status = "PENDING"
146 route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
147 route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
151 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
152 mail_body = render_to_string("rule_add_mail.txt",
153 {"route": route, "address": requesters_address})
154 user_mail = "%s" %route.applier.email
155 user_mail = user_mail.split(';')
156 new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s creation request submitted by %s via REST api" %(route.name, route.applier.username),
157 mail_body, settings.SERVER_EMAIL, user_mail,
158 get_peer_techcs_contact(route.applier))
159 d = { 'clientip' : "REST_API: %s" %requesters_address, 'user' : route.applier.username }
160 logger.info(mail_body, extra=d)
162 raise ApiFieldError("ERRORS %s %s" %(form.errors, form.__dict__))
166 def obj_delete(self, request=None, **kwargs):
167 logger.info('Got REST DELETE request %s' %kwargs)
168 obj = kwargs.pop('pk', None)
171 route = Route.objects.get(pk=obj)
173 raise NotFound("A model instance matching the provided arguments could not be found.")
174 applier_peer = route.applier.get_profile().peer
175 requester_peer = request.user.get_profile().peer
176 if applier_peer == requester_peer:
177 route.status = "PENDING"
178 route.expires = datetime.date.today()
180 route.commit_delete()
181 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
182 mail_body = render_to_string("rule_delete_mail.txt",
183 {"route": route, "address": requesters_address})
184 user_mail = "%s" %route.applier.email
185 user_mail = user_mail.split(';')
186 new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s removal request submitted by %s" %(route.name, route.applier.username),
187 mail_body, settings.SERVER_EMAIL, user_mail,
188 get_peer_techcs_contact(route.applier))
189 d = { 'clientip' : "REST_API: %s" %requesters_address, 'user' : route.applier.username }
190 logger.info(mail_body, extra=d)
192 raise NotFound("A model instance matching the provided arguments could not be found.")
194 raise NotFound("A model instance matching the provided arguments could not be found.")
196 def delete_list(self, request, **kwargs):
197 return http.HttpForbidden()
199 def obj_update(self, bundle, request=None, **kwargs):
200 logger.info('Got REST PUT request %s' %kwargs)
205 applier = request.user.pk
206 applier_peer = request.user.get_profile().peer
207 route_edit = get_object_or_404(Route, pk=kwargs['pk'])
208 route_edit_applier_peer = route_edit.applier.get_profile().peer
209 if applier_peer != route_edit_applier_peer:
210 return http.HttpForbidden()
212 route_original = deepcopy(route_edit)
213 if not bundle.obj or not bundle.obj.pk:
216 bundle.obj = self.get_object_list(request).model()
217 bundle.data.update(kwargs)
218 bundle = self.full_hydrate(bundle)
219 lookup_kwargs = kwargs.copy()
221 for key in kwargs.keys():
224 elif getattr(bundle.obj, key, NOT_AVAILABLE) is not NOT_AVAILABLE:
225 lookup_kwargs[key] = getattr(bundle.obj, key)
227 del lookup_kwargs[key]
229 # if there is trouble hydrating the data, fall back to just
230 # using kwargs by itself (usually it only contains a "pk" key
231 # and this will work fine.
232 lookup_kwargs = kwargs
235 bundle.obj = self.obj_get(request, **lookup_kwargs)
236 except ObjectDoesNotExist:
237 raise NotFound("A model instance matching the provided arguments could not be found.")
239 bundle = self.full_hydrate(bundle)
240 m2m_bundle = self.hydrate_m2m(bundle)
242 then_action_dict = m2m_bundle.data['then'][0].obj.__dict__
243 then_action_pk = ThenAction.objects.get(action=then_action_dict['action'], action_value=then_action_dict['action_value']).pk
244 then_list.append(then_action_pk)
246 then_list.extend([x.pk for x in route_edit.then.all() if x])
249 sourceport_dict = m2m_bundle.data['sourceport']
251 sourceport_dict = None
253 for sp in sourceport_dict:
254 sourceport_pk = get_port_pk_or_create(sp)
255 sourceport_list.append(sourceport_pk)
257 sourceport_list.extend([x.pk for x in route_edit.sourceport.all() if x])
260 destport_dict = m2m_bundle.data['destinationport']
264 for dp in destport_dict:
265 destport_pk = get_port_pk_or_create(dp)
266 destport_list.append(destport_pk)
268 destport_list.extend([x.pk for x in route_edit.destinationport.all() if x])
270 port_dict = m2m_bundle.data['port']
274 for prt in port_dict:
275 port_pk = get_port_pk_or_create(prt)
276 port_list.append(port_pk)
278 port_list.extend([x.pk for x in route_edit.port.all() if x])
281 datadict = bundle.obj.__dict__
282 datadict['applier'] = request.user.pk
283 comment_text = "Submitted via REST API"
284 if comment_text not in datadict['comments']:
285 datadict['comments'] = "%s: %s" %("Submitted via REST API", datadict['comments'])
286 datadict['then'] = then_list
287 datadict['sourceport'] = sourceport_list
288 datadict['destinationport'] = destport_list
289 datadict['port'] = port_list
290 datadict['name'] = route_edit.name
291 datadict['expires'] = datetime.date.today() + datetime.timedelta(days = settings.EXPIRATION_DAYS_OFFSET)
292 form = RouteForm(data=datadict, instance = route_edit)
295 route=form.save(commit=False)
296 route.name = route_original.name
297 route.applier = request.user
298 route.status = "PENDING"
299 route.source = IPNetwork("%s/%s" %(IPNetwork(route.source).network.compressed, IPNetwork(route.source).prefixlen)).compressed
300 route.destination = IPNetwork("%s/%s" %(IPNetwork(route.destination).network.compressed, IPNetwork(route.destination).prefixlen)).compressed
304 requesters_address = request.META['HTTP_X_FORWARDED_FOR']
305 mail_body = render_to_string("rule_edit_mail.txt",
306 {"route": route, "address": requesters_address})
307 user_mail = "%s" %route.applier.email
308 user_mail = user_mail.split(';')
309 new_mail(settings.EMAIL_SUBJECT_PREFIX + "Rule %s edit request submitted by %s via REST api" %(route.name, route.applier.username),
310 mail_body, settings.SERVER_EMAIL, user_mail,
311 get_peer_techcs_contact(route.applier))
312 d = { 'clientip' : "REST_API: %s" %requesters_address, 'user' : route.applier.username }
313 logger.info(mail_body, extra=d)
315 raise ApiFieldError("ERRORS %s %s" %(form.errors, form.__dict__))
320 queryset = Route.objects.all()
321 default_format = "application/json"
322 resource_name = 'rule'
323 allowed_methods = ['get', 'post', 'delete', 'put']
324 fields = ['comments', 'source', 'destination', 'expires', 'status', 'id', 'sourceport', 'destinationport', 'then']
325 authentication = BasicAuthentication(restauthBackend())
326 authorization= Authorization()
328 def get_port_pk_or_create(prt):
330 port_pk = MatchPort.objects.get(port=prt.obj.__dict__['port']).pk
333 assert (int(prt.obj.__dict__['port']))
334 mp = MatchPort(port=int(int(prt.obj.__dict__['port'])))
338 raise ApiFieldError("Port should be an integer")