Commit 97e42c7d authored by Leonidas Poulopoulos's avatar Leonidas Poulopoulos

Monkey patched User model. Poller js is templated. Plus minor changes

parent 6ee21ffd
import os
import sys
sys.path.append('/home/leopoul/projects/')
sys.path.append('/home/leopoul/projects/flowspy')
os.environ['DJANGO_SETTINGS_MODULE'] = 'flowspy.settings'
from gevent import monkey; monkey.patch_all()
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
# -*- coding: utf-8 -*- vim:encoding=utf-8:
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
from django.contrib.auth.models import User, UserManager, Permission, Group
from django.conf import settings
from flowspy.peers.models import *
from flowspy.accounts.models import *
class shibauthBackend:
def authenticate(self, **kwargs):
username = kwargs.get('username')
firstname = kwargs.get('firstname')
lastname = kwargs.get('lastname')
mail = kwargs.get('mail')
affiliation = kwargs.get('affiliation')
organization = kwargs.get('organization')
user = self._auth_user(username, firstname, lastname, mail, affiliation, organization)
if not user:
return None
return user
def _auth_user(self, username, firstname, lastname, mail, affiliation, organization):
try:
user = User.objects.get(username__exact=username)
# The user did not exist. Create one with no privileges
except:
user = User.objects.create_user(username, mail, None)
user.first_name = firstname
user.last_name = lastname
user.is_staff = False
user.is_superuser = False
# if organization == settings.SHIB_ADMIN_DOMAIN:
# user.is_staff = True
# user.is_superuser = True
user.is_active = True
try:
peer = Peer.objects.get(domain_name=organization)
up = UserProfile.objects.get_or_create(user=user,peer=peer)
except:
pass
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
......@@ -3,30 +3,30 @@ from flowspy.flowspec.models import *
from flowspy.accounts.models import *
from utils import proxy as PR
class RouteAdmin(admin.ModelAdmin):
actions = ['deactivate']
def deactivate(self, request, queryset):
applier = PR.Applier(route_objects=queryset)
commit, response = applier.apply(configuration=applier.delete_routes())
if commit:
rows = queryset.update(is_online=False, is_active=False)
queryset.update(response="Successfully removed route from network")
self.message_user(request, "Successfully removed %s routes from network" % rows)
else:
self.message_user(request, "Could not remove routes from network")
deactivate.short_description = "Deactivate selected routes from network"
list_display = ('name', 'is_online', 'applier', 'get_match', 'get_then', 'response')
fieldsets = [
(None, {'fields': ['name','applier']}),
("Match", {'fields': ['source', 'sourceport', 'destination', 'destinationport', 'port']}),
('Advanced Match Statements', {'fields': ['dscp', 'fragmenttype', 'icmpcode', 'icmptype', 'packetlength', 'protocol', 'tcpflag'], 'classes': ['collapse']}),
("Then", {'fields': ['then' ]}),
(None, {'fields': ['comments',]}),
]
#class RouteAdmin(admin.ModelAdmin):
#
# actions = ['deactivate']
#
# def deactivate(self, request, queryset):
# applier = PR.Applier(route_objects=queryset)
# commit, response = applier.apply(configuration=applier.delete_routes())
# if commit:
# rows = queryset.update(is_online=False, is_active=False)
# queryset.update(response="Successfully removed route from network")
# self.message_user(request, "Successfully removed %s routes from network" % rows)
# else:
# self.message_user(request, "Could not remove routes from network")
# deactivate.short_description = "Deactivate selected routes from network"
#
# list_display = ('name', 'is_online', 'applier', 'get_match', 'get_then', 'response')
# fieldsets = [
# (None, {'fields': ['name','applier']}),
# ("Match", {'fields': ['source', 'sourceport', 'destination', 'destinationport', 'port']}),
# ('Advanced Match Statements', {'fields': ['dscp', 'fragmenttype', 'icmpcode', 'icmptype', 'packetlength', 'protocol', 'tcpflag'], 'classes': ['collapse']}),
# ("Then", {'fields': ['then' ]}),
# (None, {'fields': ['comments',]}),
#
# ]
# fields = ('name', 'applier', 'expires')
#def formfield_for_dbfield(self, db_field, **kwargs):
......@@ -47,7 +47,7 @@ admin.site.register(UserProfile)
admin.site.register(ThenAction)
#admin.site.register(ThenStatement)
#admin.site.register(MatchStatement)
admin.site.register(Route, RouteAdmin)
admin.site.register(Route)
admin.site.disable_action('delete_selected')
......
......@@ -3,9 +3,9 @@ from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from django.template.defaultfilters import filesizeformat
from flowspy.flowspec.models import *
from ipaddr import *
from django.contrib.auth.models import User
class RouteForm(forms.ModelForm):
# name = forms.CharField(help_text=ugettext_lazy("A unique route name,"
......@@ -44,6 +44,16 @@ class RouteForm(forms.ModelForm):
ports = self.cleaned_data.get('port', None)
destination = self.cleaned_data.get('destination', None)
destinationports = self.cleaned_data.get('destinationport', None)
user = self.cleaned_data.get('applier', None)
networks = user.get_profile().peer.networks.all()
mynetwork = False
if destination:
for network in networks:
net = IPNetwork(network.network)
if IPNetwork(destination) in net:
mynetwork = True
if not mynetwork:
raise forms.ValidationError('Destination address/network should belong to your administrative address space. Check My Profile to review your networks')
if (sourceports and ports):
raise forms.ValidationError('Cannot create rule for source ports and ports at the same time. Select either ports or source ports')
if (destinationports and ports):
......@@ -54,4 +64,45 @@ class RouteForm(forms.ModelForm):
raise forms.ValidationError('Once destination port is matched, destination has to be filled as well. Either deselect destination port or fill destination address')
if not (source or sourceports or ports or destination or destinationports):
raise forms.ValidationError('Fill at least a Route Match Condition')
return self.cleaned_data
\ No newline at end of file
return self.cleaned_data
class ThenPlainForm(forms.ModelForm):
# action = forms.CharField(initial='rate-limit')
class Meta:
model = ThenAction
def clean_action_value(self):
action_value = self.cleaned_data['action_value']
if action_value:
try:
assert(int(action_value))
return "%s" %self.cleaned_data["action_value"]
except:
raise forms.ValidationError('Rate-limiting should be an integer')
if int(action_value) < 50:
raise forms.ValidationError('Rate-limiting cannot be < 50kbps')
else:
raise forms.ValidationError('Cannot be empty')
def clean_action(self):
action = self.cleaned_data['action']
if action != 'rate-limit':
raise forms.ValidationError('Cannot select something other than rate-limit')
else:
return self.cleaned_data["action"]
class PortPlainForm(forms.ModelForm):
# action = forms.CharField(initial='rate-limit')
class Meta:
model = MatchPort
def clean_port(self):
port = self.cleaned_data['port']
if port:
try:
assert(int(port))
return "%s" %self.cleaned_data["port"]
except:
raise forms.ValidationError('Port should be an integer')
else:
raise forms.ValidationError('Cannot be empty')
......@@ -12,6 +12,7 @@ from flowspec.tasks import *
from time import sleep
from flowspy.utils import beanstalkc
from flowspy.utils.randomizer import id_generator as id_gen
FORMAT = '%(asctime)s %(levelname)s: %(message)s'
......@@ -38,11 +39,20 @@ THEN_CHOICES = (
("sample", "Sample")
)
ROUTE_STATES = (
("ACTIVE", "ACTIVE"),
("ERROR", "ERROR"),
("EXPIRED", "EXPIRED"),
("PENDING", "PENDING"),
("OUTOFSYNC", "OUTOFSYNC"),
("INACTIVE", "INACTIVE"),
)
def days_offset(): return datetime.now() + timedelta(days = settings.EXPIRATION_DAYS_OFFSET)
class MatchPort(models.Model):
port = models.CharField(max_length=24)
port = models.CharField(max_length=24, unique=True)
def __unicode__(self):
return self.port
class Meta:
......@@ -60,7 +70,8 @@ class ThenAction(models.Model):
action = models.CharField(max_length=60, choices=THEN_CHOICES, verbose_name="Action")
action_value = models.CharField(max_length=255, blank=True, null=True, verbose_name="Action Value")
def __unicode__(self):
return "%s: %s" %(self.action, self.action_value)
ret = "%s:%s" %(self.action, self.action_value)
return ret.rstrip(":")
class Meta:
db_table = u'then_action'
......@@ -82,8 +93,9 @@ class Route(models.Model):
then = models.ManyToManyField(ThenAction, verbose_name="Then")
filed = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
is_online = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
status = models.CharField(max_length=20, choices=ROUTE_STATES, blank=True, null=True, verbose_name="Status", default="PENDING")
# is_online = models.BooleanField(default=False)
# is_active = models.BooleanField(default=False)
expires = models.DateField(default=days_offset, blank=True, null=True,)
response = models.CharField(max_length=512, blank=True, null=True)
comments = models.TextField(null=True, blank=True, verbose_name="Comments")
......@@ -93,9 +105,15 @@ class Route(models.Model):
return self.name
class Meta:
unique_together = (("name", "is_active"),)
db_table = u'route'
def save(self, *args, **kwargs):
if not self.pk:
hash = id_gen()
self.name = "%s_%s" %(self.name, hash)
super(Route, self).save(*args, **kwargs) # Call the "real" save() method.
def clean(self, *args, **kwargs):
from django.core.exceptions import ValidationError
if self.destination:
......@@ -122,40 +140,42 @@ class Route(models.Model):
# logger.info("Got save job id: %s" %response)
def commit_add(self, *args, **kwargs):
send_message("Adding route %s. Please wait..." %self.name, self.applier)
peer = self.applier.get_profile().peer.domain_name
send_message("[%s] Adding route %s. Please wait..." %(self.applier.username, self.name), peer)
response = add.delay(self)
logger.info("Got save job id: %s" %response)
def deactivate(self):
self.is_online = False
self.is_active = False
self.status = "INACTIVE"
self.save()
# def delete(self, *args, **kwargs):
# response = delete.delay(self)
# logger.info("Got delete job id: %s" %response)
def commit_edit(self, *args, **kwargs):
send_message("Editing route %s. Please wait..." %self.name, self.applier)
peer = self.applier.get_profile().peer.domain_name
send_message("[%s] Editing route %s. Please wait..." %(self.applier.username, self.name), peer)
response = edit.delay(self)
logger.info("Got edit job id: %s" %response)
def commit_delete(self, *args, **kwargs):
send_message("Removing route %s. Please wait..." %self.name, self.applier)
peer = self.applier.get_profile().peer.domain_name
send_message("[%s] Removing route %s. Please wait..." %(self.applier.username, self.name), peer)
response = delete.delay(self)
logger.info("Got edit job id: %s" %response)
#
# def delete(self, *args, **kwargs):
# response = delete.delay(self)
# logger.info("Got delete job id: %s" %response)
def is_synced(self):
def is_synced(self):
found = False
get_device = PR.Retriever()
device = get_device.fetch_device()
try:
routes = device.routing_options[0].routes
except Exception as e:
self.is_online = False
self.status = "EXPIRED"
self.save()
logger.error("No routing options on device. Exception: %s" %e)
return False
......@@ -230,7 +250,7 @@ class Route(models.Model):
logger.info('Protocol fields do not match')
except:
pass
if found and not self.is_online:
if found and self.status != "ACTIVE":
logger.error('Rule is applied on device but appears as offline')
found = False
......@@ -252,7 +272,7 @@ class Route(models.Model):
def get_match(self):
ret = ''
if self.destination:
ret = '%s Destination Address:<strong>%s</strong><br/>' %(ret, self.destination)
ret = '%s Dst Addr:<strong>%s</strong><br/>' %(ret, self.destination)
if self.fragmenttype:
ret = "%s Fragment Type:<strong>%s</strong><br/>" %(ret, self.fragmenttype)
if self.icmpcode:
......@@ -264,7 +284,7 @@ class Route(models.Model):
if self.protocol:
ret = "%s Protocol:<strong>%s</strong><br/>" %(ret, self.protocol)
if self.source:
ret = "%s Source Address:<strong>%s</strong><br/>" %(ret, self.source)
ret = "%s Src Addr:<strong>%s</strong><br/>" %(ret, self.source)
if self.tcpflag:
ret = "%s TCP flag:<strong>%s</strong><br/>" %(ret, self.tcpflag)
if self.port:
......@@ -272,10 +292,10 @@ class Route(models.Model):
ret = ret + "Port:<strong>%s</strong><br/>" %(port)
if self.destinationport:
for port in self.destinationport.all():
ret = ret + "Destination Port:<strong>%s</strong><br/>" %(port)
ret = ret + "Dst Port:<strong>%s</strong><br/>" %(port)
if self.sourceport:
for port in self.sourceport.all():
ret = ret +"Source Port:<strong>%s</strong><br/>" %(port)
ret = ret +"Src Port:<strong>%s</strong><br/>" %(port)
if self.dscp:
for dscp in self.dscp.all():
ret = ret + "%s Port:<strong>%s</strong><br/>" %(ret, dscp)
......@@ -285,9 +305,10 @@ class Route(models.Model):
get_match.allow_tags = True
def send_message(msg, user):
username = user.username
# username = user.username
peer = user
b = beanstalkc.Connection()
b.use(settings.POLLS_TUBE)
tube_message = json.dumps({'message': str(msg), 'username':username})
tube_message = json.dumps({'message': str(msg), 'username':peer})
b.put(tube_message)
b.close()
INSERT INTO `then_action` (`id`, `action`, `action_value`) VALUES
(1, 'accept', ''),
(2, 'discard', '');
\ No newline at end of file
......@@ -18,15 +18,12 @@ def add(route, callback=None):
applier = PR.Applier(route_object=route)
commit, response = applier.apply()
if commit:
is_online = True
is_active = True
status = "ACTIVE"
else:
is_online = False
is_active = True
route.is_online = is_online
route.is_active = is_active
status = "ERROR"
route.status = status
route.response = response
subtask(announce).delay("Route add: %s - Result: %s" %(route.name, response), route.applier)
subtask(announce).delay("[%s] Route add: %s - Result: %s" %(route.applier, route.name, response), route.applier)
route.save()
@task
......@@ -34,14 +31,13 @@ def edit(route, callback=None):
applier = PR.Applier(route_object=route)
commit, response = applier.apply(operation="replace")
if commit:
is_online = True
status = "ACTIVE"
else:
is_online = False
route.is_active = True
route.is_online = is_online
status = "ERROR"
route.status = status
route.response = response
route.save()
subtask(announce).delay("Route edit: %s - Result: %s" %(route.name, response), route.applier)
subtask(announce).delay("[%s] Route edit: %s - Result: %s"%(route.applier, route.name, response), route.applier)
......@@ -50,30 +46,40 @@ def delete(route, callback=None):
applier = PR.Applier(route_object=route)
commit, response = applier.apply(operation="delete")
if commit:
is_online = False
is_active = False
status = "INACTIVE"
else:
is_online = route.is_online
is_active = route.is_active
route.is_online = is_online
route.is_active = is_active
status = "ERROR"
route.status = status
route.response = response
route.save()
subtask(announce).delay("Route delete: %s - Result %s" %(route.name, response), route.applier)
subtask(announce).delay("[%s] Route delete: %s - Result %s" %(route.applier, route.name, response), route.applier)
@task
def announce(messg, user):
messg = str(messg)
username = user.username
# username = user.username
username = user.get_profile().peer.domain_name
b = beanstalkc.Connection()
b.use(settings.POLLS_TUBE)
tube_message = json.dumps({'message': messg, 'username':username})
b.put(tube_message)
b.close()
@task
def check_sync(route_name=None, selected_routes = []):
if not selected_routes:
routes = Route.objects.all()
else:
routes = selected_routes
if route_name:
routes = routes.filter(name=route_name)
for route in roures:
if route.is_synced():
logger.info("Route %s is synced" %route.name)
else:
logger.warn("Route %s is out of sync" %route.name)
#def delete(route):
#
# applier = PR.Applier(route_object=route)
......
......@@ -2,9 +2,11 @@
import urllib2
import re
import socket
import json
from django import forms
from django.views.decorators.csrf import csrf_exempt
from django.core import urlresolvers
from django.core import serializers
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect, HttpResponseForbidden, HttpResponse
from django.shortcuts import get_object_or_404, render_to_response
......@@ -16,27 +18,27 @@ from django.core.urlresolvers import reverse
from django.contrib import messages
from flowspy.accounts.models import *
from django.contrib.auth import authenticate, login
from django.forms.models import model_to_dict
from flowspy.flowspec.forms import *
from flowspy.flowspec.models import *
from copy import deepcopy
from flowspy.utils.decorators import shib_required
def days_offset(): return datetime.now() + timedelta(days = settings.EXPIRATION_DAYS_OFFSET)
@login_required
def user_routes(request):
if request.user.is_anonymous():
return HttpResponseRedirect(reverse('login'))
user_routes = Route.objects.filter(applier=request.user)
return render_to_response('user_routes.html', {'routes': user_routes},
context_instance=RequestContext(request))
@login_required
def group_routes(request):
if request.user.is_anonymous():
return HttpResponseRedirect(reverse('login'))
group_routes = []
peer = request.user.get_profile().peer
if peer:
peer_members = UserProfile.objects.filter(peer=peer)
......@@ -48,9 +50,10 @@ def group_routes(request):
@login_required
def add_route(request):
applier = request.user.pk
if request.method == "GET":
form = RouteForm()
return render_to_response('apply.html', {'form': form},
return render_to_response('apply.html', {'form': form, 'applier': applier},
context_instance=RequestContext(request))
else:
......@@ -59,15 +62,40 @@ def add_route(request):
route=form.save(commit=False)
route.applier = request.user
route.expires = days_offset()
route.status = "PENDING"
route.save()
form.save_m2m()
route.commit_add()
return HttpResponseRedirect(urlresolvers.reverse("user-routes"))
return HttpResponseRedirect(reverse("group-routes"))
else:
return render_to_response('apply.html', {'form': form},
return render_to_response('apply.html', {'form': form, 'applier':applier},
context_instance=RequestContext(request))
@login_required
def add_then(request):
applier = request.user.pk
if request.method == "GET":
form = RouteForm()
return render_to_response('apply.html', {'form': form, 'applier': applier},
context_instance=RequestContext(request))
else:
form = RouteForm(request.POST)
if form.is_valid():
route=form.save(commit=False)
route.applier = request.user
route.expires = days_offset()
route.save()
form.save_m2m()
route.commit_add()
return HttpResponseRedirect(reverse("group-routes"))
else:
return render_to_response('apply.html', {'form': form, 'applier':applier},
context_instance=RequestContext(request))
@login_required
def edit_route(request, route_slug):
applier = request.user.pk
route_edit = get_object_or_404(Route, name=route_slug)
route_original = deepcopy(route_edit)
if request.POST:
......@@ -75,27 +103,123 @@ def edit_route(request, route_slug):
if form.is_valid():
route=form.save(commit=False)
route.name = route_original.name
route.applier = route_original.applier
route.applier = request.user
route.expires = route_original.expires
route.is_active = route_original.is_active
route.status = "PENDING"
route.save()
form.save_m2m()
route.commit_edit()
return HttpResponseRedirect(urlresolvers.reverse("user-routes"))
return HttpResponseRedirect(reverse("group-routes"))
else:
return render_to_response('apply.html', {'form': form, 'edit':True},
return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
context_instance=RequestContext(request))
else:
dictionary = model_to_dict(route_edit, fields=[], exclude=[])
#form = RouteForm(instance=route_edit)
form = RouteForm(dictionary)
return render_to_response('apply.html', {'form': form, 'edit':True},
return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
context_instance=RequestContext(request))
@login_required
def delete_route(request, route_slug):
if request.is_ajax():
route = get_object_or_404(Route, name=route_slug)
if route.applier == request.user:
applier_peer = route.applier.get_profile().peer
requester_peer = request.user.get_profile().peer
if applier_peer == requester_peer:
route.deactivate()
route.commit_delete()
return HttpResponseRedirect(urlresolvers.reverse("user-routes"))
html = "<html><body>Done</body></html>"
return HttpResponse(html)
else:
return HttpResponseRedirect(reverse("group-routes"))
@login_required
def user_profile(request):
user = request.user
peer = request.user.get_profile().peer
return render_to_response('profile.html', {'user': user, 'peer':peer},
context_instance=RequestContext(request))
def user_login(request):
try:
error_username = None
error_orgname = None
username = request.META['HTTP_EPPN']
if not username:
error_username = True
firstname = request.META['HTTP_SHIB_INETORGPERSON_GIVENNAME']
lastname = request.META['HTTP_SHIB_PERSON_SURNAME']
mail = request.META['HTTP_SHIB_INETORGPERSON_MAIL']
organization = request.META['HTTP_SHIB_HOMEORGANIZATION']
if not organization:
error_orgname = True
if error_orgname or error_username: