Monkey patched User model. Poller js is templated. Plus minor changes
authorLeonidas Poulopoulos <leopoul@noc.grnet.gr>
Sun, 4 Dec 2011 21:18:44 +0000 (23:18 +0200)
committerLeonidas Poulopoulos <leopoul@noc.grnet.gr>
Sun, 4 Dec 2011 21:18:44 +0000 (23:18 +0200)
27 files changed:
apache/django.wsgi [new file with mode: 0644]
djangobackends/__init__.py [new file with mode: 0644]
djangobackends/shibauthBackend.py [new file with mode: 0644]
flowspec/admin.py
flowspec/forms.py
flowspec/models.py
flowspec/sql/then_action.sql [new file with mode: 0644]
flowspec/tasks.py
flowspec/views.py
monkey_patch/__init__.py [new file with mode: 0644]
monkey_patch/models.py [new file with mode: 0644]
peers/sql/peer_range.sql [new file with mode: 0644]
poller/urls.py
poller/views.py
run_poller.py
static/css/base.css
templates/add_port.html [new file with mode: 0644]
templates/add_rate_limit.html [new file with mode: 0644]
templates/apply.html
templates/base.html
templates/error.html [new file with mode: 0644]
templates/poller.js [new file with mode: 0644]
templates/profile.html
templates/user_routes.html
urls.py
utils/decorators.py [new file with mode: 0644]
utils/randomizer.py [new file with mode: 0644]

diff --git a/apache/django.wsgi b/apache/django.wsgi
new file mode 100644 (file)
index 0000000..f50d7d1
--- /dev/null
@@ -0,0 +1,13 @@
+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()
diff --git a/djangobackends/__init__.py b/djangobackends/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/djangobackends/shibauthBackend.py b/djangobackends/shibauthBackend.py
new file mode 100644 (file)
index 0000000..ff948ba
--- /dev/null
@@ -0,0 +1,49 @@
+# -*- 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
index 19193f0..a9c502b 100644 (file)
@@ -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')
 
index bdd78a6..4daee84 100644 (file)
@@ -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')
index c695ad7..d35f8eb 100644 (file)
@@ -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()
diff --git a/flowspec/sql/then_action.sql b/flowspec/sql/then_action.sql
new file mode 100644 (file)
index 0000000..bd87708
--- /dev/null
@@ -0,0 +1,3 @@
+INSERT INTO `then_action` (`id`, `action`, `action_value`) VALUES
+(1, 'accept', ''),
+(2, 'discard', '');
\ No newline at end of file
index ec2671c..8dd34cb 100644 (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)
index c8756a1..a2cb6a8 100644 (file)
@@ -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:
+            error = "Your idP should release the HTTP_EPPN, HTTP_SHIB_HOMEORGANIZATION attributes towards this service" 
+            return render_to_response('error.html', {'error': error,},
+                                  context_instance=RequestContext(request))
+        user = authenticate(username=username, firstname=firstname, lastname=lastname, mail=mail, organization=organization, affiliation=None)
+        if user is not None:
+            login(request, user)
+            return HttpResponseRedirect(reverse("group-routes"))
+                # Redirect to a success page.
+                # Return a 'disabled account' error message
+        else:
+            html = "<html><body>Invalid User</body></html>"
+            return HttpResponse(html)
+    except Exception as e:
+        html = "<html><body>Invalid Login Procedure %s </body></html>" %e
+        return HttpResponse(html)
+        # Return an 'invalid login' error message.
+#    return HttpResponseRedirect(reverse("user-routes"))
+
+@login_required
+def add_rate_limit(request):
+    if request.method == "GET":
+        form = ThenPlainForm()
+        return render_to_response('add_rate_limit.html', {'form': form,},
+                                  context_instance=RequestContext(request))
+
+    else:
+        form = ThenPlainForm(request.POST)
+        if form.is_valid():
+            then=form.save(commit=False)
+            then.action_value = "%sk"%then.action_value
+            then.save()
+            response_data = {}
+            response_data['pk'] = "%s" %then.pk
+            response_data['value'] = "%s:%s" %(then.action, then.action_value)
+            return HttpResponse(simplejson.dumps(response_data), mimetype='application/json')
+        else:
+            return render_to_response('add_rate_limit.html', {'form': form,},
+                                      context_instance=RequestContext(request))
+
+@login_required
+def add_port(request):
+    if request.method == "GET":
+        form = PortPlainForm()
+        return render_to_response('add_port.html', {'form': form,},
+                                  context_instance=RequestContext(request))
+
+    else:
+        form = PortPlainForm(request.POST)
+        if form.is_valid():
+            port=form.save()
+            response_data = {}
+            response_data['value'] = "%s" %port.pk
+            response_data['text'] = "%s" %port.port
+            return HttpResponse(simplejson.dumps(response_data), mimetype='application/json')
+        else:
+            return render_to_response('add_port.html', {'form': form,},
+                                      context_instance=RequestContext(request))
+
+@login_required
+def user_logout(request):
+    return HttpResponseRedirect(settings.SHIB_LOGOUT_URL)
+    
+    
+def load_jscript(request, file):
+    return render_to_response('%s.js' % file, context_instance=RequestContext(request), mimetype="text/javascript")
diff --git a/monkey_patch/__init__.py b/monkey_patch/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/monkey_patch/models.py b/monkey_patch/models.py
new file mode 100644 (file)
index 0000000..a39d2a5
--- /dev/null
@@ -0,0 +1,13 @@
+from django.contrib.auth.models import User
+from django.core.validators import MaxLengthValidator
+
+NEW_USERNAME_LENGTH = 255
+
+def monkey_patch_username():
+    username = User._meta.get_field("username")
+    username.max_length = NEW_USERNAME_LENGTH
+    for v in username.validators:
+        if isinstance(v, MaxLengthValidator):
+            v.limit_value = NEW_USERNAME_LENGTH
+
+monkey_patch_username()
diff --git a/peers/sql/peer_range.sql b/peers/sql/peer_range.sql
new file mode 100644 (file)
index 0000000..e69de29
index 57b1e28..ec4f682 100644 (file)
@@ -3,9 +3,9 @@ from django.conf import settings
 
 urlpatterns = patterns('flowspy.poller.views',
                        ('^$', 'main'),
-                       ('^a/message/existing$', 'message_existing'),
-                       ('^a/message/new$', 'message_new'),
-                       ('^a/message/updates$', 'message_updates'))
+                       url('^a/message/existing$', 'message_existing', name='fetch-existing'),
+                       url('^a/message/new$', 'message_new',name='fetch-new'),
+                       url('^a/message/updates$', 'message_updates', name='fetch-updates'))
 
 urlpatterns += patterns('',
     (r'^static/(?P<path>.*)', 'django.views.static.serve',\
index b990354..469f2b8 100644 (file)
@@ -12,6 +12,9 @@ from django.http import HttpResponse
 from gevent.event import Event
 from django.conf import settings
 from django.views.decorators.csrf import csrf_exempt
+from django.http import HttpResponseRedirect
+from django.core.urlresolvers import reverse
+
 
 from flowspy.utils import beanstalkc
 
@@ -37,6 +40,7 @@ class Msgs(object):
     cache_size = 200
 
     def __init__(self):
+        self.user = None
         self.user_cache = {}
         self.user_cursor = {}
         self.cache = []
@@ -47,22 +51,28 @@ class Msgs(object):
         if self.user_cache:
             request.session['cursor'] = self.user_cache[-1]['id']
         return render_to_response('poll.html', {'messages': self.user_cache})
-    
+
     @csrf_exempt
     def message_existing(self, request):
-        
-        try:
-            user = request.user.username
-        except:
-            user = None
-        self.new_message_user_event[user] = Event()
-        try:
-            if self.user_cache[user]:
-                self.user_cursor[user] = self.user_cache[user][-1]['id']
-        except:
-            self.user_cache[user] = []
-            self.user_cursor[user] = ''
-        return json_response({'messages': self.user_cache[user]})
+        if request.is_ajax():
+            try:
+                user = request.user.get_profile().peer.domain_name
+            except:
+                user = None
+                return False
+            try:
+                assert(self.new_message_user_event[user])
+            except:
+                self.new_message_user_event[user] = Event()
+    #        self.new_message_user_event[user] = Event()
+            try:
+                if self.user_cache[user]:
+                    self.user_cursor[user] = self.user_cache[user][-1]['id']
+            except:
+                self.user_cache[user] = []
+                self.user_cursor[user] = ''
+            return json_response({'messages': self.user_cache[user]})
+        return HttpResponseRedirect(reverse('login'))
     
     @csrf_exempt
     def message_new(self, mesg=None):
@@ -89,40 +99,42 @@ class Msgs(object):
     
     @csrf_exempt
     def message_updates(self, request):
-        cursor = {}
-        try:
-            user = request.user.username
-        except:
-            user = None
-
-        cursor[user] = self.user_cursor[user]
-            
-        try:
-            if not isinstance(self.user_cache[user], list):
+        if request.is_ajax():
+            cursor = {}
+            try:
+    #            user = request.user.username
+                user = request.user.get_profile().peer.domain_name
+            except:
+                user = None
+                return False
+            cursor[user] = self.user_cursor[user]
+                
+            try:
+                if not isinstance(self.user_cache[user], list):
+                    self.user_cache[user] = []
+            except:
                 self.user_cache[user] = []
-        except:
-            self.user_cache[user] = []
-        if not self.user_cache[user] or cursor[user] == self.user_cache[user][-1]['id']:
-            self.new_message_user_event[user].wait()
-#            self.new_message_event.wait()
-#        assert cursor[user] != self.user_cache[user][-1]['id'], cursor[user]
-        try:
-            for index, m in enumerate(self.user_cache[user]):
-                if m['id'] == cursor[user]:
-                    return json_response({'messages': self.user_cache[user][index + 1:]})
-            return json_response({'messages': self.user_cache[user]})
-        finally:
-            if self.user_cache[user]:
-                self.user_cursor[user] = self.user_cache[user][-1]['id']
-#            else:
-#                request.session.pop('cursor', None)
+            if not self.user_cache[user] or cursor[user] == self.user_cache[user][-1]['id']:
+                self.new_message_user_event[user].wait()
+    #            self.new_message_event.wait()
+    #        assert cursor[user] != self.user_cache[user][-1]['id'], cursor[user]
+            try:
+                for index, m in enumerate(self.user_cache[user]):
+                    if m['id'] == cursor[user]:
+                        return json_response({'messages': self.user_cache[user][index + 1:]})
+                return json_response({'messages': self.user_cache[user]})
+            finally:
+                if self.user_cache[user]:
+                    self.user_cursor[user] = self.user_cache[user][-1]['id']
+        return HttpResponseRedirect(reverse('login'))
+    #            else:
+    #                request.session.pop('cursor', None)
 
     def monitor_polls(self, polls=None):
         b = beanstalkc.Connection()
         b.watch(settings.POLLS_TUBE)
         while True:
             job = b.reserve()
-            print job.body
             msg = json.loads(job.body)
             job.bury()
             self.message_new(msg)
index 769dfd2..cde0e85 100644 (file)
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 from gevent.wsgi import WSGIServer
 from poller.application import application
-print 'Serving on 8000...'
-WSGIServer(('netdev.grnet.gr', 9090), application).serve_forever()
+server="localhost"
+port=8081
+print 'Serving on port %s...' % port
+WSGIServer((server,port ), application).serve_forever()
index 1f9567d..58e30a5 100644 (file)
@@ -455,7 +455,9 @@ margin: 1px;
 #cluster_overview_table_wrapper{
        width: 100%;
 }
-
+/*
+ * jQuery UI specific styling
+ */
 
 .paging_two_button .ui-button {
        float: left;
@@ -518,30 +520,39 @@ table.display thead th div.DataTables_sort_wrapper span {
 }
 
 
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Everything below this line is the same as demo_table.css. This file is
+ * required for 'cleanliness' of the markup
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables features
+ */
+
 .dataTables_wrapper {
        position: relative;
        min-height: 302px;
-       float: left;
-       clear: both;
        _height: 302px;
-       width: 100%;
-       zoom: 1; /* Feeling sorry for IE */
+       clear: both;
 }
 
 .dataTables_processing {
        position: absolute;
-       top: 50%;
+       top: 0px;
        left: 50%;
        width: 250px;
-       height: 30px;
        margin-left: -125px;
-       margin-top: -15px;
-       padding: 14px 0 2px 0;
        border: 1px solid #ddd;
        text-align: center;
        color: #999;
-       font-size: 14px;
-       background-color: white;
+       font-size: 11px;
+       padding: 2px 0;
 }
 
 .dataTables_length {
@@ -556,13 +567,11 @@ table.display thead th div.DataTables_sort_wrapper span {
 }
 
 .dataTables_info {
-       width: 60%;
+       width: 50%;
        float: left;
 }
 
 .dataTables_paginate {
-       width: 44px;
-       * width: 50px;
        float: right;
        text-align: right;
 }
@@ -593,26 +602,20 @@ table.display thead th div.DataTables_sort_wrapper span {
 
 
 
-
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables display
+ */
 table.display {
        margin: 0 auto;
-       clear: both;
        width: 100%;
+       clear: both;
        border-collapse: collapse;
-       
-}
-
-table.display thead th {
-       padding: 3px 18px 3px 10px;
-       font-weight: normal;
-       cursor: pointer;
-       * cursor: hand;
 }
 
 table.display tfoot th {
-       padding: 3px 18px 3px 10px;
-       border-top: 1px solid black;
+       padding: 3px 0px 3px 10px;
        font-weight: bold;
+       font-weight: normal;
 }
 
 table.display tr.heading2 td {
@@ -623,31 +626,15 @@ table.display td {
        padding: 3px 10px;
 }
 
-table.display td a {
-       font-weight: normal;
-}
-
 table.display td.center {
        text-align: center;
 }
 
-table.display tfoot th {
-       padding: 3px 0px 3px 10px;
-       font-weight: bold;
-       font-weight: normal;
-}
 
-table.display tr.heading2 td {
-       border-bottom: 1px solid #aaa;
-}
-
-table.display td {
-       padding: 3px 10px;
-}
 
-table.display td.center {
-       text-align: center;
-}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables sorting
+ */
 
 .sorting_asc {
        background: url('../images/sort_asc.png') no-repeat center right;
@@ -669,12 +656,29 @@ table.display td.center {
        background: url('../images/sort_desc_disabled.png') no-repeat center right;
 }
 
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
 table.display tr.odd.gradeA {
-       background-color: #EEEEEE;
+       background-color: #ddffdd;
 }
 
 table.display tr.even.gradeA {
-       background-color: #F6F6F6;
+       background-color: #eeffee;
+}
+
+
+
+
+table.display tr.odd.gradeA {
+       background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+       background-color: #eeffee;
 }
 
 table.display tr.odd.gradeC {
@@ -710,14 +714,17 @@ tr.even {
        background-color: white;
 }
 
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Misc
+ */
 .dataTables_scroll {
        clear: both;
 }
 
-.dataTables_scrollBody {
-       *margin-top: -1px;
-}
-
 .top, .bottom {
        padding: 15px;
        background-color: #F5F5F5;
@@ -762,12 +769,6 @@ td.details {
        width: 40%;
 }
 
-.paging_full_numbers {
-       width: 400px;
-       height: 22px;
-       line-height: 22px;
-}
-
 .paging_full_numbers span.paginate_button,
        .paging_full_numbers span.paginate_active {
        border: 1px solid #aaa;
@@ -799,6 +800,11 @@ table.display tr.odd.row_selected td {
        background-color: #9FAFD1;
 }
 
+
+/*
+ * Sorting classes for columns
+ */
+/* For the standard odd/even */
 tr.odd td.sorting_1 {
        background-color: #D3D6FF;
 }
@@ -823,28 +829,39 @@ tr.even td.sorting_3 {
        background-color: #F9F9FF;
 }
 
+
+/* For the Conditional-CSS grading rows */
+/*
+       Colour calculations (based off the main row colours)
+  Level 1:
+               dd > c4
+               ee > d5
+       Level 2:
+         dd > d1
+         ee > e2
+ */
 tr.odd.gradeA td.sorting_1 {
-       background-color: #E1E1E1;
+       background-color: #c4ffc4;
 }
 
 tr.odd.gradeA td.sorting_2 {
-       background-color: #E8E8E8;
+       background-color: #d1ffd1;
 }
 
 tr.odd.gradeA td.sorting_3 {
-       background-color: #E8E8E8;
+       background-color: #d1ffd1;
 }
 
 tr.even.gradeA td.sorting_1 {
-       background-color: #EAEAEA;
+       background-color: #d5ffd5;
 }
 
 tr.even.gradeA td.sorting_2 {
-       background-color: #F0F0F0;
+       background-color: #e2ffe2;
 }
 
 tr.even.gradeA td.sorting_3 {
-       background-color: #F0F0F0;
+       background-color: #e2ffe2;
 }
 
 tr.odd.gradeC td.sorting_1 {
@@ -919,6 +936,10 @@ tr.even.gradeU td.sorting_3 {
        background-color: #e2e2e2;
 }
 
+
+/*
+ * Row highlighting example
+ */
 .ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
        background-color: #ECFFB3;
 }
@@ -927,62 +948,6 @@ tr.even.gradeU td.sorting_3 {
        background-color: #E6FF99;
 }
 
-.ex_highlight_row #example tr.even:hover {
-       background-color: #ECFFB3;
-}
-
-.ex_highlight_row #example tr.even:hover td.sorting_1 {
-       background-color: #DDFF75;
-}
-
-.ex_highlight_row #example tr.even:hover td.sorting_2 {
-       background-color: #E7FF9E;
-}
-
-.ex_highlight_row #example tr.even:hover td.sorting_3 {
-       background-color: #E2FF89;
-}
-
-.ex_highlight_row #example tr.odd:hover {
-       background-color: #E6FF99;
-}
-
-.ex_highlight_row #example tr.odd:hover td.sorting_1 {
-       background-color: #D6FF5C;
-}
-
-.ex_highlight_row #example tr.odd:hover td.sorting_2 {
-       background-color: #E0FF84;
-}
-
-.ex_highlight_row #example tr.odd:hover td.sorting_3 {
-       background-color: #DBFF70;
-}
-
-table.KeyTable td {
-       border: 3px solid transparent;
-}
-
-table.KeyTable td.focus {
-       border: 3px solid #3366FF;
-}
-
-table.display tr.gradeA {
-       background-color: #eeffee;
-}
-
-table.display tr.gradeC {
-       background-color: #ddddff;
-}
-
-table.display tr.gradeX {
-       background-color: #ffdddd;
-}
-
-table.display tr.gradeU {
-       background-color: #ddd;
-}
-
 div.box {
        height: 100px;
        padding: 10px;
diff --git a/templates/add_port.html b/templates/add_port.html
new file mode 100644 (file)
index 0000000..6e8a066
--- /dev/null
@@ -0,0 +1,6 @@
+<form id="add_port_form" method="POST">
+       {% csrf_token %}
+<table>
+<tr><th>Port</th><td>{{ form.port }}<span class="error" id='rl_error'>{{ form.port.errors|join:", " }}</span></td></tr>
+</table>
+</form>
\ No newline at end of file
diff --git a/templates/add_rate_limit.html b/templates/add_rate_limit.html
new file mode 100644 (file)
index 0000000..6364dbc
--- /dev/null
@@ -0,0 +1,8 @@
+<form id="add_rl_form" method="POST">
+       {% csrf_token %}
+<table>
+<input type="hidden" id="id_action" name="action" value="rate-limit"/>
+<tr><th>Value</th><td>{{ form.action_value }}Kbps<span class="error" id='rl_error'>{{ form.action_value.errors|join:", " }}</span></td></tr>
+<tr class="help"><td></td><td>Value should be >50Kbps</td></tr>
+</table>
+</form>
\ No newline at end of file
index c239ef8..b42f744 100644 (file)
@@ -1,6 +1,6 @@
 {% extends "base.html" %}
 {% load i18n %}
-       
+
 {% block title %}
        {% if edit %}
                {% trans "Edit Route" %} {{form.data.name}}
        {% trans "Create route" %}
        {% endif %}
                {% endblock %}
-
+{% block extrahead %}
+<script>
+       $(document).ready( function(){
+               
+               $("#id_sourceport").css('width', '100px').attr('size', '5');
+               $("#id_port").css('width', '100px').attr('size', '5');
+               $("#id_destinationport").css('width', '100px').attr('size', '5');
+               $('#id_then').attr("multiple", "");
+               $('#then_diag').dialog({
+                       height: 220,
+            width: 340,
+                       modal: true,
+                       autoOpen: false,
+                       buttons: {
+               'Add': function() {
+                       console.log($("#add_rl_form").serialize());
+                       $.ajax({
+                       url:"{% url add-rate-limit %}", 
+                       data:$("#add_rl_form").serialize(),
+                       type: "POST",
+                       cache: false,
+                       success:function(data){
+                                       try {
+                                               value = data.pk;
+                                               text = data.value;
+                                               $('#id_then').append($("<option></option>").attr("value",value).text(text));
+                                               $('#then_diag').dialog('close');
+                                       }
+                                       catch (exception) {
+                                               $('#then_diag').html(data);
+                                       }                                       
+                               }
+                               });
+               },
+               Cancel: function() {
+                       $('#then_diag').dialog('close');
+               }
+       }
+               });
+               
+               $('#port_diag').dialog({
+                       height: 220,
+            width: 340,
+                       modal: true,
+                       autoOpen: false,
+                       buttons: {
+               'Add': function() {
+                       console.log($("#add_port_form").serialize());
+                       $.ajax({
+                       url:"{% url add-port %}", 
+                       data:$("#add_port_form").serialize(),
+                       type: "POST",
+                       cache: false,
+                       success:function(data){
+                                       try {
+                                               value = data.value;
+                                               text = data.text;
+                                               $('#id_port').append($("<option></option>").attr("value",value).text(text));
+                                               $('#id_destinationport').append($("<option></option>").attr("value",value).text(text));
+                                               $('#id_sourceport').append($("<option></option>").attr("value",value).text(text));
+                                               $('#port_diag').dialog('close');
+                                       }
+                                       catch (exception) {
+                                               $('#port_diag').html(data);
+                                       }                                       
+                               }
+                               });
+               },
+               Cancel: function() {
+                       $('#port_diag').dialog('close');
+               }
+       }
+               });
+               
+               
+               $("#new_then_actions").button({
+            icons: {
+                primary: "ui-icon-plusthick"
+            },
+                       })
+                       .click(function(){
+                               $.ajax({
+                                       url: "{% url add-rate-limit %}",
+                                       cache: false,
+                                       success: function(data){
+                                               $("#then_diag").html(data);
+                                       }
+                               });
+                               $('#then_diag').dialog('open');
+                               return false;
+                       });
+                       
+                       
+                       $(".new_port").button({
+            icons: {
+                primary: "ui-icon-plusthick"
+            },
+                       })
+                       .click(function(){
+                               $.ajax({
+                                       url: "{% url add-port %}",
+                                       cache: false,
+                                       success: function(data){
+                                               $("#port_diag").html(data);
+                                       }
+                               });
+                               $('#port_diag').dialog('open');
+                               return false;
+                       });
+               });
+               
+</script>
+{% endblock %}
 {% block content %}
 <style type="text/css">
 th {
@@ -30,7 +142,6 @@ th {
 
 }
 </style>
-
 <div align="center">
        {% if edit %}
        <h3>{% trans "Edit route" %}: {{form.data.name}}</h3>
@@ -47,30 +158,30 @@ th {
        <legend>{% trans "Route Basic Info" %}</legend>
 <table>
 <tr><th>{{ form.name.label_tag }}</th><td>{{ form.name }}<span class="error">{{ form.name.errors|join:", " }}</span></td></tr>
-<tr class="help"><td></td><td>{{ form.name.help_text }}</td></tr>
+<tr class="help"><td></td><td>A unique identifier will be added as a name_suffix</td></tr>
 </table>
 </fieldset>
 
 <fieldset>
 <legend>{% trans "Route Match Conditions" %}</legend>
 <table>
+<input type="hidden" id="id_applier" name="applier" value="{{applier}}"/>
 <tr><th>{{ form.source.label_tag }}</th><td>{{ form.source }}<span class="error">{{ form.source.errors|join:", " }}</span></td></tr>
 <tr class="help"><td></td><td>{{ form.source.help_text }}</td></tr>
-<tr><th>{{ form.sourceport.label_tag }}</th><td>{{ form.sourceport }}<span class="error">{{ form.sourceport.errors|join:", " }}</span></td></tr>
+<tr><th>{{ form.sourceport.label_tag }}</th><td>{{ form.sourceport }}&nbsp;&nbsp;<button class="new_port">Port</button><span class="error">{{ form.sourceport.errors|join:", " }}</span></td></tr>
 <tr class="help"><td></td><td>{{ form.sourceport.help_text }}</td></tr>
 <tr><th>{{ form.destination.label_tag }}</th><td>{{ form.destination }}<span class="error">{{ form.destination.errors|join:", " }}</span></td></tr>
 <tr class="help"><td></td><td>{{ form.destination.help_text }}</td></tr>
-<tr><th>{{ form.destinationport.label_tag }}</th><td>{{ form.destinationport }}<span class="error">{{ form.destinationport.errors|join:", " }}</span></td></tr>
+<tr><th>{{ form.destinationport.label_tag }}</th><td>{{ form.destinationport }}&nbsp;&nbsp;<button class="new_port">Port</button><span class="error">{{ form.destinationport.errors|join:", " }}</span></td></tr>
 <tr class="help"><td></td><td>{{ form.destinationport.help_text }}</td></tr>
-<tr><th>{{ form.port.label_tag }}</th><td>{{ form.port }}<span class="error">{{ form.port.errors|join:", " }}</span></td></tr>
+<tr><th>{{ form.port.label_tag }}</th><td>{{ form.port }}&nbsp;&nbsp;<button class="new_port">Port</button><span class="error">{{ form.port.errors|join:", " }}</span></td></tr>
 <tr class="help"><td></td><td>{{ form.port.help_text }}</td></tr>
 </table>
 </fieldset>
 <fieldset>
 <legend>{% trans "Route Actions" %}</legend>
 <table>
-<tr><th>{{ form.then.label_tag }}</th><td>{{ form.then }}<span class="error">{{ form.then.errors|join:", " }}</span></td></tr>
-<tr class="help"><td></td><td>{{ form.then.help_text }}</td></tr>
+<tr><th>{{ form.then.label_tag }}</th><td>{{ form.then }}&nbsp;&nbsp;<button id="new_then_actions">Rate-limit</button><span class="error">{{ form.then.errors|join:", " }}</span></td></tr>
 </table>
 </fieldset>
 <fieldset>
@@ -87,4 +198,10 @@ th {
 </form>
 </div>
 
+<div id="then_diag" title="Add new rate-limit value">
+</div>
+
+<div id="port_diag" title="Add new port">
+</div>
+
 {% endblock %}
index 8a0942f..197bdca 100644 (file)
@@ -8,7 +8,7 @@
 <link rel="stylesheet" type="text/css" href="/static/css/base.css">
 <link rel="stylesheet" type="text/css" href="/static/css/smoothness/jquery-ui-1.8.13.custom.css">
 <script type="text/javascript" src="/static/js/jquery-ui-1.8.12.custom.min.js"></script>
-<script type="text/javascript" src="/static/js/poller.js"></script>
+<script type="text/javascript" src="{% url load-js 'poller' %}"></script>
 <script type="text/javascript">
 
        function setlang(lang){
@@ -42,7 +42,7 @@
 <body>
        <div id="main">
                        <div id="header">
-                               <a class="grnetlogo" href="/">
+                               <a class="grnetlogo" href="{% url group-routes %}">
                                        <img class="logoimg" src="/static/grnet_logo.png">
                                </a>
                                <div id="title">
@@ -53,6 +53,7 @@
                                                {% if user.is_authenticated %}
                                                <span class="topmenuuser">{% trans "Welcome" %} <strong>{{user}}</strong></span>
                                                {% endif %}
+                                               {% comment %}
                                                <span {% if user.is_authenticated %}class="topmenuaction"{% else %}class="topmenuuser"{% endif %} >
                                                        <form action="{% url django.views.i18n.set_language %}" method="post" style="display:inline;" id="langform">
                                                                {% csrf_token %}
                                                                {% endfor %}
                                                        </form>
                                                </span>
+                                               {% endcomment %}
                                                {% if user.is_authenticated %}
-                                               <span class="topmenuaction"><a href="">{% trans "My profile" %}</a></span>
-{% if user.is_superuser %}
+                                               <span class="topmenuaction"><a href="{% url user-profile %}">{% trans "My profile" %}</a></span>
+                                               {% if user.is_superuser %}
                                                <span class="topmenuaction"><a href="{% url admin:index %}">{% trans "Admin" %}</a></span>
-{% endif %}
+                                               {% endif %}
                                                <span class="topmenuaction"><a href="{% url logout %}">{% trans "Logout" %}</a></span>
                                                {% else %}
                                                <span class="topmenuaction"><a href="{% url login %}">{% trans "Login" %}</a></span>
@@ -77,7 +79,7 @@
                        </div>
                        <div id="content">
                                {% block brcrmb_container %}
-                               <div class="info_content_title">{% if user.is_authenticated %}<a href="/">{% trans "My routes" %}</a>{% endif %}
+                               <div class="info_content_title">{% if user.is_authenticated %}<a href="{% url group-routes %}">{% trans "My routes" %}</a>{% endif %}
                                {% block breadcrumbs %}{% endblock %}
                                </div>
                                
diff --git a/templates/error.html b/templates/error.html
new file mode 100644 (file)
index 0000000..b3037a0
--- /dev/null
@@ -0,0 +1,81 @@
+{% load i18n %}
+<html>
+<head>
+<title>Error</title>
+<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
+<META HTTP-EQUIV="Expires" CONTENT="-1">
+<script src="/static/js/jquery.min.js" type="text/javascript"></script>
+<link rel="stylesheet" type="text/css" href="/static/css/base.css">
+<link rel="stylesheet" type="text/css" href="/static/css/smoothness/jquery-ui-1.8.13.custom.css">
+<script type="text/javascript" src="/static/js/jquery-ui-1.8.12.custom.min.js"></script>
+
+</head>
+
+
+
+<body>
+       <div id="main">
+                       <div id="header">
+                               <a class="grnetlogo" href="{% url group-routes %}">
+                                       <img class="logoimg" src="/static/grnet_logo.png">
+                               </a>
+                               <div id="title">
+                                       <a href="/">Firewall on Demand</a>
+                               </div>
+                               <div class="loginform">
+                                       <div id="formcontent">
+                                               {% if user.is_authenticated %}
+                                               <span class="topmenuuser">{% trans "Welcome" %} <strong>{{user}}</strong></span>
+                                               {% endif %}
+                                               <span {% if user.is_authenticated %}class="topmenuaction"{% else %}class="topmenuuser"{% endif %} >
+                                                       <form action="{% url django.views.i18n.set_language %}" method="post" style="display:inline;" id="langform">
+                                                               {% csrf_token %}
+                                                               <input name="next" type="hidden" value="{{ next }}" />
+                                                               <input id="langsel" name="language" type="hidden" value="" />
+                                                               {% for lang in LANGUAGES %}<a class="select_lang" href="#" onclick="setlang('{{ lang.0 }}'); return false;" title="{{lang.1}}">{% ifnotequal LANGUAGE_CODE lang.0 %}<font style="font-weight:normal;">{% endifnotequal %}{% trans lang.1 %}{% ifnotequal LANGUAGE_CODE lang.0 %}</font>{% endifnotequal %}</a>{% if not forloop.last %} -{% endif %}
+                                                               {% endfor %}
+                                                       </form>
+                                               </span>
+                                               {% if user.is_authenticated %}
+                                               <span class="topmenuaction"><a href="{% url user-profile %}">{% trans "My profile" %}</a></span>
+{% if user.is_superuser %}
+                                               <span class="topmenuaction"><a href="{% url admin:index %}">{% trans "Admin" %}</a></span>
+{% endif %}
+                                               <span class="topmenuaction"><a href="{% url logout %}">{% trans "Logout" %}</a></span>
+                                               {% else %}
+                                               <span class="topmenuaction"><a href="{% url login %}">{% trans "Login" %}</a></span>
+                                               {% endif %}
+                                       </div>
+                                       
+                               </div>
+                       </div>
+                       <div id="content">
+                               {% block brcrmb_container %}
+                               <div class="info_content_title">{% if user.is_authenticated %}<a href="{% url group-routes %}">{% trans "My routes" %}</a>{% endif %}
+                               {% block breadcrumbs %}{% endblock %}
+                               </div>
+                               
+                               {% endblock %}
+                               {% block content %}
+                               A required shibboleth attribute was not released towards this service<br>
+                               Required shibboleth attributes:<br>
+                               <strong>HTTP_EPPN</strong><br>
+                               <strong>HTTP_SHIB_HOMEORGANIZATION</strong>
+                               <br><br>
+                               Optional Attributes:<br>
+                               <strong>HTTP_SHIB_INETORGPERSON_GIVENNAME</strong><br>
+                               <strong>HTTP_SHIB_PERSON_SURNAME</strong><br>
+                               <strong>HTTP_SHIB_INETORGPERSON_MAIL</strong><br>
+
+                               {% endblock %}
+
+                       </div>
+                       <div id="footer">
+                       {% if user.is_authenticated %}
+                       {% trans "If you have any questions or need help, contact GRNET Helpdesk at <a href='mailto:helpdesk@grnet.gr'>helpdesk@grnet.gr</a> or 800-11-47638." %}<br />
+                       {% endif %}
+                       <a href="http://www.grnet.gr">{% trans "GRNET" %} NOC</a> | <a href="/about/info/">{% trans "Info" %}</a> | <a href="/about/terms-of-service">{% trans "Service Terms" %}</a>
+                       </div>
+       </div>
+</body>
+</html>
diff --git a/templates/poller.js b/templates/poller.js
new file mode 100644 (file)
index 0000000..d109faf
--- /dev/null
@@ -0,0 +1,177 @@
+// Copyright 2009 FriendFeed
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+$(document).ready(function() {
+    if (!window.console) window.console = {};
+    if (!window.console.log) window.console.log = function() {};
+
+    $("#messageform").live("submit", function() {
+       newMessage($(this));
+       return false;
+    });
+    $("#messageform").live("keypress", function(e) {
+       if (e.keyCode == 13) {
+           newMessage($(this));
+           return false;
+       }
+    });
+    $("#message").select();
+    updater.start();
+    updater.poll();
+});
+
+function newMessage(form) {
+    var message = form.formToDict();
+    var disabled = form.find("input[type=submit]");
+    disabled.disable();
+    $.postJSON("{% url fetch-new %}", message, function(response) {
+       updater.showMessage(response);
+       if (message.id) {
+           form.parent().remove();
+       } else {
+           form.find("input[type=text]").val("").select();
+           disabled.enable();
+       }
+    });
+}
+
+function getCookie(name) {
+    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
+    return r ? r[1] : undefined;
+}
+
+jQuery.postJSON = function(url, args, callback) {
+    args._xsrf = getCookie("_xsrf");
+    $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
+           success: function(response) {
+       if (callback) callback(eval("(" + response + ")"));
+    }, error: function(response) {
+       console.log("ERROR:", response)
+    }});
+};
+
+jQuery.fn.formToDict = function() {
+    var fields = this.serializeArray();
+    var json = {}
+    for (var i = 0; i < fields.length; i++) {
+       json[fields[i].name] = fields[i].value;
+    }
+    if (json.next) delete json.next;
+    return json;
+};
+
+jQuery.fn.disable = function() {
+    this.enable(false);
+    return this;
+};
+
+jQuery.fn.enable = function(opt_enable) {
+    if (arguments.length && !opt_enable) {
+        this.attr("disabled", "disabled");
+    } else {
+        this.removeAttr("disabled");
+    }
+    return this;
+};
+
+var updater = {
+    errorSleepTime: 500,
+    cursor: null,
+    
+    start: function() {
+       var args = {"_xsrf": getCookie("_xsrf")};
+       if (updater.cursor) args.cursor = updater.cursor;
+       $.ajax({url: "{% url fetch-existing %}", type: "POST", dataType: "text",
+               data: $.param(args), success: updater.onFetchExisting,
+               error: updater.onError});
+        },
+    
+    poll: function() {
+       var args = {"_xsrf": getCookie("_xsrf")};
+       if (updater.cursor) args.cursor = updater.cursor;
+       $.ajax({url: "{% url fetch-updates %}", type: "POST", dataType: "text",
+               data: $.param(args), success: updater.onSuccess,
+               error: updater.onError});
+    },
+
+    onSuccess: function(response) {
+       try {
+           updater.newMessages(eval("(" + response + ")"));
+       } catch (e) {
+           updater.onError();
+           return;
+       }
+       updater.errorSleepTime = 500;
+       window.setTimeout(updater.poll, 0);
+    },
+
+    onFetchExisting: function(response) {
+       try {
+           updater.existingMessages(eval("(" + response + ")"));
+       } catch (e) {
+//         updater.onError();
+           return;
+       }
+        },
+     
+    onError: function(response) {
+       updater.errorSleepTime *= 2;
+       console.log("Poll error; sleeping for", updater.errorSleepTime, "ms");
+       window.setTimeout(updater.poll, updater.errorSleepTime);
+    },
+
+    newMessages: function(response) {
+       if (!response.messages) return;
+       updater.cursor = response.cursor;
+       var messages = response.messages;
+       updater.cursor = messages[messages.length - 1].id;
+//     console.log(messages.length, "new messages, cursor:", updater.cursor);
+       
+       for (var i = 0; i < messages.length; i++) {
+           updater.showMessage(messages[i]);
+       }
+       if (($('#console').dialog('isOpen')) == false){
+               blink("#consolebutton");
+       }
+    },
+
+    existingMessages: function(response) {
+       if (!response.messages) return;
+       updater.cursor = response.cursor;
+       var messages = response.messages;
+       updater.cursor = messages[messages.length - 1].id;
+       for (var i = 0; i < messages.length; i++) {
+           updater.showMessage(messages[i]);
+       }
+        },
+   
+    showMessage: function(message) {
+       var existing = $("#m" + message.id);
+       if (existing.length > 0) return;
+       var node = $(message.html);
+       node.hide();
+//      $('#inbox').val($('#inbox').val()+message.text); 
+       $("#inbox").append(node);
+       node.slideDown();
+    }
+};
+
+function blink(selector){
+       $(selector).animate({ color: "red" }, 500, function(){
+       $(this).animate({ color: "#555555" }, 500, function(){
+       blink(this);
+       });
+       });
+}
+
index 692d71e..c0a747c 100644 (file)
@@ -1,42 +1,21 @@
 {% extends "base.html" %}
 {% load i18n %}
-{% block extrahead %}
-       <script>
-$(document).ready(function () {
-    $('#accordion').accordion({
-               active: false,
-               collapsible: true,
-        autoHeight: false,
-        create: function () {
-//                     load password change form
-            $('#pass_change_placeholder').load("{% url pass_change %}");
-//                     load ssh keys manager
-                       $('#manage_sshkeys_placeholder').load("{% url user-keys %}");
-//                     change user mail
-                       $('#change_email_placeholder').load("{% url mail-change %}");
-//                     change user full name
-                       $('#change_name_placeholder').load("{% url name-change %}");
-                       
-        }
-    });
-});
 
-       </script>
-
-{% endblock %}
 {% block title %}{% trans "My profile" %}{% endblock %}
 {% block content %}
 <h3>{% trans "My profile" %}</h3>
 
-<div id="accordion">
-       <h3><a href="#" style="color: #4183C4;">{% trans "Change Name" %}</a></h3>
-       <div id="change_name_placeholder"></div>
-       <h3><a href="#" style="color: #4183C4;">{% trans "Change Password" %}</a></h3>
-       <div id="pass_change_placeholder"></div>
-       <h3><a href="#" style="color: #4183C4;">{% trans "Change Email" %}</a></h3>
-       <div id="change_email_placeholder"></div>
-       <h3><a href="#" style="color: #4183C4;">{% trans "Manage SSH Keys" %}</a></h3>
-       <div id="manage_sshkeys_placeholder"></div>
+<div id="profile">
+       My data:<br>
+       <strong>First name:</strong> {{user.first_name}} <br>
+       <strong>Last name:</strong> {{user.last_name}} <br>
+       <strong>Email:</strong> {{user.email}}<br>
+       <strong>Organization:</strong> {{peer}} <br>
+       <strong>Admin Networks:</strong> <br>
+       {% for network in peer.networks.all %}
+       {{network}}<br>
+       {% endfor %}
+
 </div>
 
 
index 46ff25f..61a5aa3 100644 (file)
@@ -13,9 +13,7 @@
                'Delete': function() {
                route = $('#route_to_delete').text();
                route_url_id = '#del_route_'+route;
-               console.log(route_url_id);
                url = $(route_url_id).attr('href');
-               console.log(route, url);
                        $.ajax({
                        url: url ,
                        cache: false,
@@ -23,7 +21,6 @@
                                $('#dialog').dialog('close');
                          }
                });
-//                             $('#dialog').dialog('close');
                },
                Cancel: function() {
                        $('#dialog').dialog('close');
@@ -32,7 +29,7 @@
                });
                $('#console').dialog({
                        height: 250,
-            width: 500,
+            width: 800,
                        modal: true,
                        autoOpen: false
                        });
@@ -115,30 +112,34 @@ function delete_route(route){
        <th style="text-align: center;">{% trans "Applier" %}</th>
        <th style="text-align: center;">{% trans "Expires" %}</th>
        <th style="text-align: center;">{% trans "Response" %}</th>
-       <th style="text-align: center; width:120px;">{% trans "Actions" %}</th>
+       <th style="text-align: center; width:140px;">{% trans "Actions" %}</th>
 </tr>
 </thead>
 
 <tbody>
 {% for route in routes %}
 
-<tr {% if route.is_online %} class="GradeA" {% else %} class="GradeX" {% endif %}>
+<tr {% ifequal route.status 'ACTIVE' %} class="GradeA" {% else %} class="GradeX" {% endifequal %}>
        <td>{{ route.name }}</td>
        <td>{{ route.get_match|safe|escape }}</td>
        <td style="text-align: center;">{{route.get_then|safe|escape}}</td>
-       <td style="text-align: center;">{% if route.is_online %}Online{% else %}Offline{% endif %}</td>
+       <td style="text-align: center;">{{route.status}}</td>
        {% comment %}<td style="text-align: center;">{{ route.response }}</td>{% endcomment %}
        <td style="text-align: center;">{{ route.applier }}</td>
        <td style="text-align: center;">{{ route.expires }}</td>
        <td style="text-align: center;">{{ route.response }}</td>
        <td style="text-align: center;">
-               {% if route.is_active %}
+               {% ifequal route.status 'ACTIVE' %}
                <a href="{% url edit-route route.name %}" class="edit_button" id="edit_button_{{route.pk}}">Edit</a> 
                <button class="del_button" id="{{route.name}}" onclick="javascript:delete_route(this.id)">Del</button>
                <a href="{% url delete-route route.name %}" style="display:none" id="del_route_{{route.name}}"></a>
                {% else %}
+               {% ifequal route.status 'INACTIVE' %}
+               <a href="{% url edit-route route.name %}" class="edit_button" id="edit_button_{{route.pk}}">Reactivate</a>
+               {% else %}
                -
-               {% endif %}</td>
+               {% endifequal %}
+               {% endifequal %}</td>
 </tr>
 
 {% endfor %}
diff --git a/urls.py b/urls.py
index 2c7f60c..12724a9 100644 (file)
--- a/urls.py
+++ b/urls.py
@@ -6,26 +6,30 @@ admin.autodiscover()
 
 urlpatterns = patterns('',
     # Example:
-    # (r'^flowspy/', include('flowspy.foo.urls')),
-    (r'^poll/', include('flowspy.poller.urls')),
-    url(r'^/?$', 'flowspy.flowspec.views.group_routes', name="group-routes"),
-    url(r'^add/?$', 'flowspy.flowspec.views.add_route', name="add-route"),
-    url(r'^edit/(?P<route_slug>\w+)/$', 'flowspy.flowspec.views.edit_route', name="edit-route"),
-    url(r'^delete/(?P<route_slug>\w+)/$', 'flowspy.flowspec.views.delete_route', name="delete-route"),
-    url(r'^user/login/?', 'django.contrib.auth.views.login', {'template_name': 'login.html'}, name="login"),
-    url(r'^user/logout/?', 'django.contrib.auth.views.logout', {'next_page': '/'}, name="logout"),
-    (r'^setlang/?$', 'django.views.i18n.set_language'),
+    # (r'^fod/flowspy/', include('flowspy.foo.urls')),
+    (r'^fod/poll/', include('flowspy.poller.urls')),
+#    url(r'^fod/?$', 'flowspy.flowspec.views.user_routes', name="user-routes"),
+    url(r'^fod/?$', 'flowspy.flowspec.views.group_routes', name="group-routes"),
+    url(r'^fod/profile/?$', 'flowspy.flowspec.views.user_profile', name="user-profile"),
+    url(r'^fod/add/?$', 'flowspy.flowspec.views.add_route', name="add-route"),
+    url(r'^fod/addrl/?$', 'flowspy.flowspec.views.add_rate_limit', name="add-rate-limit"),
+    url(r'^fod/addport/?$', 'flowspy.flowspec.views.add_port', name="add-port"),
+    url(r'^fod/edit/(?P<route_slug>\w+)/$', 'flowspy.flowspec.views.edit_route', name="edit-route"),
+    url(r'^fod/delete/(?P<route_slug>\w+)/$', 'flowspy.flowspec.views.delete_route', name="delete-route"),
+    url(r'^fod/login/?', 'flowspy.flowspec.views.user_login', name="login"),
+    url(r'^fod/logout/?', 'flowspy.flowspec.views.user_logout', name="logout"),
+    (r'^fod/setlang/?$', 'django.views.i18n.set_language'),
     # Uncomment the admin/doc line below to enable admin documentation:
-    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
-
+    (r'^fod/admin/doc/', include('django.contrib.admindocs.urls')),
+    url(r'^fod/load_js/(?P<file>[\w\s\d_-]+)/$', 'flowspy.flowspec.views.load_jscript', name="load-js"), 
 
     # Uncomment the next line to enable the admin:
-    (r'^admin/', include(admin.site.urls)),
+    (r'^fod/admin/', include(admin.site.urls)),
 )
 
 
 if settings.DEBUG:
     urlpatterns += patterns('',
-        (r'^static/(?P<path>.*)', 'django.views.static.serve',\
+        (r'^fod/static/(?P<path>.*)', 'django.views.static.serve',\
             {'document_root':  settings.STATIC_URL}),
     )
\ No newline at end of file
diff --git a/utils/decorators.py b/utils/decorators.py
new file mode 100644 (file)
index 0000000..c1930f3
--- /dev/null
@@ -0,0 +1,13 @@
+from django.http import HttpResponseRedirect
+from django.core.urlresolvers import reverse
+
+
+def shib_required(f):
+    def wrap(request, *args, **kwargs):
+        if 'HTTP_SHIB_SESSION_ID' not in request.META or not request.META['HTTP_SHIB_SESSION_ID']:
+            return HttpResponseRedirect(reverse('login'))
+        return f(request, *args, **kwargs)
+
+    wrap.__doc__=f.__doc__
+    wrap.__name__=f.__name__
+    return wrap
diff --git a/utils/randomizer.py b/utils/randomizer.py
new file mode 100644 (file)
index 0000000..bfb0854
--- /dev/null
@@ -0,0 +1,5 @@
+import string
+import random
+
+def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
+    return ''.join(random.choice(chars) for x in range(size))