diff --git a/conntrackt/models.py b/conntrackt/models.py
--- a/conntrackt/models.py
+++ b/conntrackt/models.py
@@ -3,6 +3,25 @@ from django.core.exceptions import Valid
from django.db import models
+class ViewPermissionModelMetaClass(models.base.ModelBase):
+ """
+ Implements a meta class that can be used for automatically adding the view
+ permission for a model.
+
+ Limitation is that syncdb must be called with --all if using South.
+
+ Original source found at:
+
+ http://stackoverflow.com/questions/725913/dynamic-meta-attributes-for-django-models
+ """
+
+ def __new__(cls, name, bases, attrs):
+ cl = super(ViewPermissionModelMetaClass, cls).__new__(cls, name, bases, attrs)
+ cl._meta.permissions.append(('view_' + cl._meta.module_name, u'Can view %s' % cl._meta.verbose_name))
+
+ return cl
+
+
class Project(models.Model):
"""
Implements a model with information about a project. A project has some
@@ -18,6 +37,8 @@ class Project(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
+ __metaclass__ = ViewPermissionModelMetaClass
+
def __unicode__(self):
"""
Returns:
@@ -54,6 +75,8 @@ class Location(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
+ __metaclass__ = ViewPermissionModelMetaClass
+
def __unicode__(self):
"""
Returns:
@@ -88,6 +111,8 @@ class Entity(models.Model):
project = models.ForeignKey(Project)
location = models.ForeignKey(Location)
+ __metaclass__ = ViewPermissionModelMetaClass
+
class Meta:
"""
Overrides some of the default parameters used by Django for this model.
diff --git a/conntrackt/templates/403.html b/conntrackt/templates/403.html
new file mode 100644
--- /dev/null
+++ b/conntrackt/templates/403.html
@@ -0,0 +1,8 @@
+{% extends "conntrackt/template.html" %}
+
+{% block content %}
+
Access denied
+
+You have insufficient privileges to access this resource. Please contact your local system administrator if you believe you should have been granted access.
+
+{% endblock content %}
diff --git a/conntrackt/views.py b/conntrackt/views.py
--- a/conntrackt/views.py
+++ b/conntrackt/views.py
@@ -3,22 +3,32 @@ from StringIO import StringIO
from zipfile import ZipFile, ZIP_DEFLATED
# Django imports.
+from django.contrib.auth.decorators import permission_required
from django.http import HttpResponse
from django.shortcuts import render_to_response, get_object_or_404
from django.views.generic import TemplateView, DetailView
+# Third-party application imports.
+from braces.views import MultiplePermissionsRequiredMixin
+
# Application imports.
from .models import Project, Entity, Location
from .utils import generate_entity_iptables
-
-class IndexView(TemplateView):
+class IndexView(MultiplePermissionsRequiredMixin, TemplateView):
"""
Custom view used for rendering the index page.
"""
template_name = 'conntrackt/index.html'
+ # Required permissions.
+ permissions = {
+ "all": ("conntrackt.view_project", "conntrackt.view_location",),
+ }
+ # Raise authorisation denied exception for unmet permissions.
+ raise_exception = True
+
def get_context_data(self, **kwargs):
"""
Returns the context data that should be used for rendering of the
@@ -31,18 +41,26 @@ class IndexView(TemplateView):
# Set the context using the parent class.
context = super(IndexView, self).get_context_data(**kwargs)
- context['projects'] = Project.objects.all().order_by('name')
+ # Store information about all projcts in context. Optimise database
+ # access for the view.
+ context['projects'] = Project.objects.all().prefetch_related('entity_set').order_by('name')
return context
-class ProjectView(DetailView):
+class ProjectView(MultiplePermissionsRequiredMixin, DetailView):
"""
Custom view for presenting the project information.
"""
model = Project
+ permissions = {
+ "all": ("conntrackt.view_project", "conntrackt.view_location",)
+ }
+ # Raise authorisation denied exception for unmet permissions.
+ raise_exception = True
+
def get_context_data(self, **kwargs):
"""
Returns the context data that should be used for rendering of the
@@ -73,11 +91,24 @@ class ProjectView(DetailView):
return context
-class EntityView(DetailView):
+class EntityView(MultiplePermissionsRequiredMixin, DetailView):
"""
Custom view for presenting entity information.
"""
- model = Entity
+
+ # Optimise the query to fetch the related data from reverse relationships.
+ queryset = Entity.objects.all()
+ queryset = queryset.prefetch_related('interface_set__destination_set__source__entity')
+ queryset = queryset.prefetch_related('interface_set__destination_set__destination__entity')
+ queryset = queryset.prefetch_related('interface_set__source_set__source__entity')
+ queryset = queryset.prefetch_related('interface_set__source_set__destination__entity')
+
+ # Required permissions.
+ permissions = {
+ "all": ("conntrackt.view_entity",)
+ }
+ # Raise authorisation denied exception for unmet permissions.
+ raise_exception = True
def get_context_data(self, **kwargs):
"""
@@ -97,6 +128,7 @@ class EntityView(DetailView):
return context
+@permission_required("conntrackt.view_entity", raise_exception = True)
def entity_iptables(request, pk):
"""
Custom view that returns response containing iptables rules generated for an
@@ -129,6 +161,7 @@ def entity_iptables(request, pk):
return response
+@permission_required("conntrackt.view_entity", raise_exception = True)
def project_iptables(request, project_id, location_id=None):
"""
Custom view for obtaining iptables for all entities of a project or project