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