Skip to content
Snippets Groups Projects
Commit ad5a7bf3 authored by David Schmitz's avatar David Schmitz Committed by root
Browse files

REST API: fix DELETE so that active routes are deactiveatd first and only...

REST API: fix DELETE so that active routes are deactiveatd first and only admin can DELETE ; allow access to all routes of one's peers by REST API
parent 301b190c
No related branches found
No related tags found
No related merge requests found
...@@ -28,9 +28,11 @@ from flowspec.tasks import * ...@@ -28,9 +28,11 @@ from flowspec.tasks import *
from flowspec.helpers import send_new_mail, get_peer_techc_mails from flowspec.helpers import send_new_mail, get_peer_techc_mails
from utils import proxy as PR from utils import proxy as PR
from ipaddress import * from ipaddress import *
from ipaddress import ip_network
import datetime import datetime
import logging import logging
import json import json
from peers.models import PeerRange, Peer
from flowspec.junos import create_junos_name from flowspec.junos import create_junos_name
...@@ -84,6 +86,7 @@ ROUTE_STATES = ( ...@@ -84,6 +86,7 @@ ROUTE_STATES = (
("PENDING", "PENDING"), ("PENDING", "PENDING"),
("OUTOFSYNC", "OUTOFSYNC"), ("OUTOFSYNC", "OUTOFSYNC"),
("INACTIVE", "INACTIVE"), ("INACTIVE", "INACTIVE"),
("INACTIVE_TODELETE", "INACTIVE_TODELETE"),
("ADMININACTIVE", "ADMININACTIVE"), ("ADMININACTIVE", "ADMININACTIVE"),
) )
...@@ -557,7 +560,7 @@ class Route(models.Model): ...@@ -557,7 +560,7 @@ class Route(models.Model):
self.status = "ACTIVE" self.status = "ACTIVE"
self.save() self.save()
found = True found = True
if self.status == "ADMININACTIVE" or self.status == "INACTIVE" or self.status == "EXPIRED": if self.status == "ADMININACTIVE" or self.status == "INACTIVE" or self.status == "INACTIVE_TODELETE" or self.status == "EXPIRED":
found = True found = True
return found return found
...@@ -627,9 +630,31 @@ class Route(models.Model): ...@@ -627,9 +630,31 @@ class Route(models.Model):
applier_peers = None applier_peers = None
return applier_peers return applier_peers
# perf has to be checked:
@property
def containing_peer_ranges(self):
try:
destination_network = ip_network(self.destination)
logger.info("containing_peer_ranges(): destination_network="+str(destination_network))
#containing_peer_ranges = PeerRange.objects.filter(network__contains(destination_network))
containing_peer_ranges = [obj for obj in PeerRange.objects.all() if ip_network(obj.network).__contains__(destination_network)]
logger.info("containing_peer_ranges(): containing_peer_ranges="+str(containing_peer_ranges))
except Exception as e:
logger.info("containing_peer_ranges(): exception occured: "+str(e))
#containing_peer_ranges = None
containing_peer_ranges = []
return containing_peer_ranges
# perf has to be checked:
def containing_peers(self):
containing_peer_ranges2 = set(self.containing_peer_ranges)
logger.info("containing_peers(): containing_peer_ranges="+str(containing_peer_ranges2))
#return [obj.peer for obj in containing_peer_ranges2]
return [obj for obj in Peer.objects.all() if len(set(obj.networks.all()).intersection(containing_peer_ranges2))>0]
@property @property
def days_to_expire(self): def days_to_expire(self):
if self.status not in ['EXPIRED', 'ADMININACTIVE', 'ERROR', 'INACTIVE']: if self.status not in ['EXPIRED', 'ADMININACTIVE', 'ERROR', 'INACTIVE', 'INACTIVE_TODELETE']:
expiration_days = (self.expires - datetime.date.today()).days expiration_days = (self.expires - datetime.date.today()).days
if expiration_days < settings.EXPIRATION_NOTIFY_DAYS: if expiration_days < settings.EXPIRATION_NOTIFY_DAYS:
return "%s" %expiration_days return "%s" %expiration_days
......
...@@ -121,10 +121,17 @@ def edit(routepk, callback=None): ...@@ -121,10 +121,17 @@ def edit(routepk, callback=None):
def delete(routepk, **kwargs): def delete(routepk, **kwargs):
from flowspec.models import Route from flowspec.models import Route
route = Route.objects.get(pk=routepk) route = Route.objects.get(pk=routepk)
initial_status = route.status
try: try:
applier = PR.Applier(route_object=route) applier = PR.Applier(route_object=route)
commit, response = applier.apply(operation="delete") commit, response = applier.apply(operation="delete")
reason_text = '' reason_text = ''
logger.info("tasks::delete(): route="+str(route)+" initial_status="+str(initial_status))
if commit and initial_status == "INACTIVE_TODELETE": # special new case for fully deleting a rule via REST API
route.delete()
msg1 = "[%s] Fully deleted route : %s%s- Result %s" % (route.applier, route.name, reason_text, response)
logger.info("tasks::delete(): DELETED msg="+msg1)
announce(msg1, route.applier, route)
if commit: if commit:
status = "INACTIVE" status = "INACTIVE"
if "reason" in kwargs and kwargs['reason'] == 'EXPIRED': if "reason" in kwargs and kwargs['reason'] == 'EXPIRED':
...@@ -150,7 +157,8 @@ def delete(routepk, **kwargs): ...@@ -150,7 +157,8 @@ def delete(routepk, **kwargs):
route.response = "Task timeout" route.response = "Task timeout"
route.save() route.save()
announce("[%s] Suspending rule : %s - Result: %s" % (route.applier_username_nice, route.name, route.response), route.applier, route) announce("[%s] Suspending rule : %s - Result: %s" % (route.applier_username_nice, route.name, route.response), route.applier, route)
except Exception: except Exception as e:
logger.error("edit(): route="+str(route)+", got unexpected exception="+str(e))
route.status = "ERROR" route.status = "ERROR"
route.response = "Error" route.response = "Error"
route.save() route.save()
...@@ -222,7 +230,7 @@ def check_sync(route_name=None, selected_routes=[]): ...@@ -222,7 +230,7 @@ def check_sync(route_name=None, selected_routes=[]):
if route_name: if route_name:
routes = routes.filter(name=route_name) routes = routes.filter(name=route_name)
for route in routes: for route in routes:
if route.has_expired() and (route.status != 'EXPIRED' and route.status != 'ADMININACTIVE' and route.status != 'INACTIVE'): if route.has_expired() and (route.status != 'EXPIRED' and route.status != 'ADMININACTIVE' and route.status != 'INACTIVE' and route.status != 'INACTIVE_TODELETE'):
if route.status != 'ERROR': if route.status != 'ERROR':
logger.info('Expiring %s route %s' %(route.status, route.name)) logger.info('Expiring %s route %s' %(route.status, route.name))
subtask(delete).delay(route, reason="EXPIRED") subtask(delete).delay(route, reason="EXPIRED")
...@@ -238,7 +246,7 @@ def notify_expired(): ...@@ -238,7 +246,7 @@ def notify_expired():
logger.info('Initializing expiration notification') logger.info('Initializing expiration notification')
routes = Route.objects.all() routes = Route.objects.all()
for route in routes: for route in routes:
if route.status not in ['EXPIRED', 'ADMININACTIVE', 'INACTIVE', 'ERROR']: if route.status not in ['EXPIRED', 'ADMININACTIVE', 'INACTIVE', 'INACTIVE_TODELETE', 'ERROR']:
expiration_days = (route.expires - datetime.date.today()).days expiration_days = (route.expires - datetime.date.today()).days
if expiration_days < settings.EXPIRATION_NOTIFY_DAYS: if expiration_days < settings.EXPIRATION_NOTIFY_DAYS:
try: try:
......
...@@ -52,7 +52,7 @@ def clean_status(status): ...@@ -52,7 +52,7 @@ def clean_status(status):
:rtype: str :rtype: str
""" """
allowed_states = ['ACTIVE', 'INACTIVE'] allowed_states = ['ACTIVE', 'INACTIVE', 'INACTIVE_TODELETE']
if status not in allowed_states: if status not in allowed_states:
return _('Invalid status value. You are allowed to use "{}".'.format( return _('Invalid status value. You are allowed to use "{}".'.format(
......
...@@ -39,7 +39,9 @@ class RouteViewSet(viewsets.ModelViewSet): ...@@ -39,7 +39,9 @@ class RouteViewSet(viewsets.ModelViewSet):
if self.request.user.is_anonymous: if self.request.user.is_anonymous:
return Route.objects.all() return Route.objects.all()
elif self.request.user.is_authenticated: elif self.request.user.is_authenticated:
return Route.objects.filter(applier=self.request.user) #return Route.objects.filter(applier=self.request.user)
return convert_container_to_queryset(self.get_users_routes_all(), Route)
else: else:
raise PermissionDenied('User is not Authenticated') raise PermissionDenied('User is not Authenticated')
...@@ -47,7 +49,19 @@ class RouteViewSet(viewsets.ModelViewSet): ...@@ -47,7 +49,19 @@ class RouteViewSet(viewsets.ModelViewSet):
return Route.objects.all() return Route.objects.all()
elif (self.request.user.is_authenticated and not elif (self.request.user.is_authenticated and not
self.request.user.is_anonymous): self.request.user.is_anonymous):
return Route.objects.filter(applier=self.request.user) #return Route.objects.filter(applier=self.request.user)
return convert_container_to_queryset(self.get_users_routes_all(), Route)
def get_users_routes_all(self):
return global__get_users_routes_all(self.request.user)
def get_users_routes_by_its_peers(self):
return global__get_users_routes_by_its_peers(self.request.user)
def get_users_routes_by_applier_only(self):
return global__get_users_routes_by_applier_only(self.request.user)
def list(self, request): def list(self, request):
serializer = RouteSerializer( serializer = RouteSerializer(
...@@ -228,12 +242,22 @@ class RouteViewSet(viewsets.ModelViewSet): ...@@ -228,12 +242,22 @@ class RouteViewSet(viewsets.ModelViewSet):
def destroy(self, request, pk=None): def destroy(self, request, pk=None):
obj = get_object_or_404(self.queryset, pk=pk) obj = get_object_or_404(self.queryset, pk=pk)
logger.info("destroy(): pk="+str(pk)+" obj="+str(obj)) logger.info("destroy(): pk="+str(pk)+" obj="+str(obj))
if False or not self.request.user.is_superuser:
raise PermissionDenied('Permission Denied')
logger.info("destroy(): pre commit_delete") logger.info("destroy(): pre commit_delete")
obj.commit_delete() if obj.status == 'ACTIVE':
serializer = RouteSerializer(obj, context={'request': request}) obj.status = "PENDING"
return Response(serializer.data) obj.response = "N/A"
obj.save()
obj.commit_delete()
serializer = RouteSerializer(obj, context={'request': request})
return Response(serializer.data)
else:
try:
if not settings.ALLOW_ADMIN__FULL_RULEDEL or not self.request.user.is_superuser:
raise PermissionDenied('Permission Denied')
except Exception as e:
raise PermissionDenied('Permission Denied')
# this will delete the rule from DB
return super(RouteViewSet, self).destroy(self, request, pk=pk)
class ThenActionViewSet(viewsets.ModelViewSet): class ThenActionViewSet(viewsets.ModelViewSet):
queryset = ThenAction.objects.all() queryset = ThenAction.objects.all()
...@@ -266,3 +290,41 @@ class StatsRoutesViewSet(viewsets.ViewSet): ...@@ -266,3 +290,41 @@ class StatsRoutesViewSet(viewsets.ViewSet):
route = get_object_or_404(queryset, name=pk) route = get_object_or_404(queryset, name=pk)
return routestats(request, route.name) return routestats(request, route.name)
#############################################################################
#############################################################################
# global helpers
# class1's attribute 'id' should be existing and be the primary key, e.g., be a Django model class
def convert_container_to_queryset(list1, class1):
#temp1_ids = [obj.id for obj in list1]
temp1_ids = [obj.id for obj in list1 if obj != None]
temp2_ids = set(temp1_ids)
return class1.objects.filter(id__in=temp2_ids)
#############################################################################
#############################################################################
def global__get_users_routes_all(user):
routes1=global__get_users_routes_by_its_peers(user)
routes2=global__get_users_routes_by_applier_only(user)
routes_all=list(routes1)+list(routes2)
routes_all=list(set(routes_all)) # make uniques list
return routes_all
# all these following functions return normal containers, not particular query sets
# if needed convert them back to query sets by convert_container_to_queryset
def global__get_users_routes_by_its_peers(user):
users_peers_set = set(user.userprofile.peers.all())
routes_all = list(Route.objects.filter())
#routes_all = list(Route.objects)
#temp1 = [obj for obj in routes_all]
temp1 = [obj for obj in routes_all if len(set(obj.containing_peers()).intersection(users_peers_set))>0]
return temp1
def global__get_users_routes_by_applier_only(user):
#return list(Rule.objects.filter(applier=user))
return Route.objects.filter(applier=user)
#############################################################################
#############################################################################
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment