Changeset - 26c0c45a8480
[Not reviewed]
default
0 3 0
Branko Majic (branko) - 12 years ago 2013-07-09 23:33:28
branko@majic.rs
CONNT-7: Modified the index view to show listing of projects and entities in two columns. Updated the index test accordingly.
3 files changed with 78 insertions and 17 deletions:
0 comments (0 inline, 0 general)
conntrackt/templates/conntrackt/index.html
Show inline comments
 
{% extends "conntrackt/base.html" %}
 

	
 
{# For html_link #}
 
{% load conntrackt_tags %}
 

	
 
{% block content %}
 

	
 
<div class="row">
 
  <h1 class="span12">Welcome to Conntrackt</h1>
 
</div>
 

	
 
<div class="row">
 
    <div class="span12">Below you may find the list of projects that are available to you. Clicking on the project will take you to the summary page for that particular project. Clicking on an entity inside of a project will take you to the summary page of an entity.</div>
 
</div>
 

	
 
<hr>
 

	
 
<div class="row">
 
  <div class="span12">
 
    {% html_link "Add project" "project_create" class="btn btn-primary" %}
 
  </div>
 
</div>
 

	
 
<hr>
 

	
 
<div class="row">
 
{% if projects %}
 
  {% for project in projects %}
 
  <div class="span4">
 
    <div class="well">{% include "conntrackt/project_widget.html" %}</div>
 

	
 
  <div class="span6">
 
    <h2>Projects</h2>
 
    <div class="well">
 
    {% if projects %}
 
      <table class="table table-striped">
 
        {% for project in projects %}
 
          <tr>
 
            <td style="width:99%">{% html_link project.name "project" project.id class="btn btn-link" %}</td>
 
            <td>{% html_link '<i class="icon-list"></i>' "project_iptables" project.id class="btn btn-link" %}</td>
 
            <td>{% html_link '<i class="icon-edit"></i>' "project_update" project.id class="btn btn-link" %}</td>
 
            <td>{% html_link '<i class="icon-remove"></i>' "project_delete" project.id class="btn btn-link" %}</td>
 
          </tr>
 
        {% endfor %}
 
      </table>
 
    {% else %}
 
      There are no projects defined.
 
    {% endif %}
 
    </div>     
 
  </div>
 
  {% endfor %}
 
{% else %}
 
  <div class="span12">Currently there are no projects defined in the database. Use the administration pages in order to add a new project.</div>
 
{% endif %}
 

	
 
  <div class="span6">
 
    <h2>Locations</h2>
 
    <div class="well">
 
      {% if locations %}
 
        <table class="table table-striped">
 
          {% for location in locations %}
 
            <tr>
 
              <td style="width:99%">{{location.name}}</td>
 
            </tr>
 
          {% endfor %}
 
        </table>
 
      {% else %}
 
        There are no locations defined.
 
      {% endif %}
 
    </div>
 
  </div>
 

	
 
</div>
 

	
 
{% endblock %}
 

	
conntrackt/tests/test_views.py
Show inline comments
 
# Standard library imports.
 
from StringIO import StringIO
 
from zipfile import ZipFile, ZIP_DEFLATED
 

	
 
# Django imports.
 
from django.core.urlresolvers import reverse
 
from django.test import TestCase
 
from django.test.client import Client
 
from django.contrib.auth.models import User, Permission
 

	
 
# Application imports
 
from conntrackt.models import Project
 
from conntrackt.models import Project, Location
 

	
 

	
 
class ViewTest(TestCase):
 
    """
 
    Abstract test class that initalises the fixtures, sets-up a client, and
 
    sets-up a test user.
 
    """
 

	
 
    fixtures = ['test-data.json']
 

	
 
    def setUp(self):
 
        # Set-up web client.
 
        self.client = Client()
 

	
 
        # Set-up users with different view permissions.
 
        self.user = {}
 
        self.user["fullperms"] = User.objects.create_user("fullperms", "fullperms@example.com", "fullperms")
 
        self.user["fullperms"].user_permissions.add(Permission.objects.get(codename="view"))
 
        self.user["noperms"] = User.objects.create_user("noperms", "noperms@example.com", "noperms")
 

	
 

	
 
class IndexViewTest(ViewTest):
 

	
 
    def test_permission_denied(self):
 
        """
 
        Tests if permission will be denied for client without sufficient privileges.
 
        """
 

	
 
        self.client.login(username="noperms", password="noperms")
 

	
 
        response = self.client.get(reverse("index"))
 

	
 
        self.assertContains(response, "You have insufficient privileges to access this resource. Please contact your local system administrator if you believe you should have been granted access.", status_code=403)
 

	
 
    def test_permission_granted(self):
 
        """
 
        Tests if permission will be granted for user with correct privileges.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("index"))
 

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
    def test_no_projects(self):
 
        """
 
        Tests the index view when no projects are defined.
 
        """
 

	
 
        Project.objects.all().delete()
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 
        response = self.client.get(reverse("index"))
 

	
 
        self.assertContains(response, "Currently there are no projects defined in the database. Use the administration pages in order to add a new project.")
 
        self.assertContains(response, "There are no projects defined.")
 

	
 
    def test_no_locations(self):
 
        """
 
        Tests the index view when no locations are defined.
 
        """
 

	
 
        Location.objects.all().delete()
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 
        response = self.client.get(reverse("index"))
 

	
 
        self.assertContains(response, "There are no locations defined.")
 

	
 
    def test_projects_available(self):
 
        """
 
        Tests if projects are shown or not.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("index"))
 

	
 
        self.assertQuerysetEqual(response.context["projects"], ["<Project: Test Project 1>", "<Project: Test Project 2>"])
 
        self.assertContains(response, "Test Project 1")
 
        self.assertContains(response, "Test Project 2")
 

	
 
    def test_locations_available(self):
 
        """
 
        Tests if locations are show or not.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("index"))
 

	
 
        self.assertQuerysetEqual(response.context["locations"], ["<Location: Test Location 1>", "<Location: Test Location 2>"])
 
        self.assertContains(response, "Test Location 1")
 
        self.assertContains(response, "Test Location 2")
 

	
 

	
 
class ProjectViewTest(ViewTest):
 

	
 
    def test_permission_denied(self):
 
        """
 
        Tests if permission will be denied for client without sufficient privileges.
 
        """
 

	
 
        self.client.login(username="noperms", password="noperms")
 

	
 
        response = self.client.get(reverse("project", args=(1,)))
 

	
 
        self.assertContains(response, "You have insufficient privileges to access this resource. Please contact your local system administrator if you believe you should have been granted access.", status_code=403)
 

	
 
    def test_permission_granted(self):
 
        """
 
        Tests if permission will be granted for user with correct privileges.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project", args=(1,)))
 

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
    def test_project_show(self):
 
        """
 
        Tests if the project information is shown properly.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("project", args=(1,)))
 

	
 
        location, entities = response.context["location_entities"][0]
 
        self.assertEqual(location.name, "Test Location 1")
 
        self.assertQuerysetEqual(entities, ["<Entity: Test Entity 1 (Test Project 1 - Test Location 1)>",
 
                                            "<Entity: Test Entity 2 (Test Project 1 - Test Location 1)>"])
 

	
 
        location, entities = response.context["location_entities"][1]
 
        self.assertEqual(location.name, "Test Location 2")
 
        self.assertQuerysetEqual(entities, ["<Entity: Test Entity 3 (Test Project 1 - Test Location 2)>",
 
                                            "<Entity: Test Subnet (Test Project 1 - Test Location 2)>"])
 

	
 
        self.assertEqual(str(response.context["project"]), "Test Project 1")
 
        self.assertContains(response, "Test Project 1")
 
        self.assertContains(response, "Test Location 1")
 
        self.assertContains(response, "Test Location 2")
conntrackt/views.py
Show inline comments
 
# Standard library imports.
 
from StringIO import StringIO
 
from zipfile import ZipFile, ZIP_DEFLATED
 

	
 
# Django imports.
 
from django.contrib.auth.decorators import permission_required
 
from django.contrib import messages
 
from django.core.urlresolvers import reverse_lazy
 
from django.http import HttpResponse
 
from django.shortcuts import render_to_response, get_object_or_404
 
from django.views.generic import TemplateView, DetailView, CreateView, UpdateView, DeleteView
 

	
 
# 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(MultiplePermissionsRequiredMixin, TemplateView):
 
    """
 
    Custom view used for rendering the index page.
 
    """
 

	
 
    template_name = 'conntrackt/index.html'
 

	
 
    # Required permissions.
 
    permissions = {
 
        "all": ("conntrackt.view", "conntrackt.view",),
 
        "all": ("conntrackt.view",),
 
        }
 
    # 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
 
        template.
 

	
 
        Adds the 'projects' context object containing all of the projects
 
        available sorted aplhabetically by name.
 
        """
 

	
 
        # Set the context using the parent class.
 
        context = super(IndexView, self).get_context_data(**kwargs)
 

	
 
        # 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')
 
        # Store information about all projcts in context.
 
        context['projects'] = Project.objects.all().order_by('name')
 

	
 
        # Store information about all locations in context.
 
        context['locations'] = Location.objects.all().order_by('name')
 

	
 
        return context
 

	
 

	
 
class ProjectView(MultiplePermissionsRequiredMixin, DetailView):
 
    """
 
    Custom view for presenting the project information.
 
    """
 

	
 
    model = Project
 

	
 
    permissions = {
 
        "all": ("conntrackt.view", "conntrackt.view",)
 
        "all": ("conntrackt.view",),
 
        }
 
    # 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
 
        template.
 

	
 
        Adds the 'location_entities' context object that contains tuples of the
 
        form '(location, entities)', where location is an instance of a
 
        Location, and entities is a query set of entities that belong to that
 
        particular location, and to related project.
 
        """
 

	
 
        # Set the context using the parent class.
 
        context = super(ProjectView, self).get_context_data(**kwargs)
 

	
 
        # Set-up an array that will contaion (location, entities) tuples.
 
        location_entities = []
 

	
 
        # Add the (location, entities) tuple for each location that has entities
 
        # belonging to the related project.
 
        for location in Location.objects.filter(entity__project=self.object).distinct():
 
            entities = Entity.objects.filter(project=self.object, location=location)
 
            location_entities.append((location, entities))
 

	
 
        # Add the (location, entities) tuples to context.
 
        context['location_entities'] = location_entities
 

	
 
        # Finally return the context.
 
        return context
 

	
 

	
 
class EntityView(MultiplePermissionsRequiredMixin, DetailView):
 
    """
 
    Custom view for presenting entity information.
 
    """
 

	
 
    # 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",)
 
        "all": ("conntrackt.view",),
 
        }
 
    # 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
 
        template.
 

	
 
        Adds the 'entity_iptables' context object that contains full iptables
 
        rules generated for the entity.
 
        """
 

	
 
        # Call the parent class method.
 
        context = super(DetailView, self).get_context_data(**kwargs)
 

	
 
        # Add the rendered iptables rules to the context.
 
        context['entity_iptables'] = generate_entity_iptables(self.object)
 

	
 
        return context
 

	
 

	
 
@permission_required("conntrackt.view", raise_exception=True)
 
def entity_iptables(request, pk):
 
    """
 
    Custom view that returns response containing iptables rules generated for an
 
    entity.
 

	
 
    Makes sure to set the Content-Disposition of a response in order to
 
    signal the browser it should start download of this view's response
 
    immediately. Also sets the suggested filename for it.
 

	
 
    Arguments:
 

	
 
        pk - Primary key of the Entity object for which the rules should be
 
        generated.
 

	
 
    Returns:
 

	
 
        Response object that contains the iptables rules for specified entity.
 
    """
 

	
 
    # Fetch the entity, and construct the response with iptables rules as
 
    # content.
 
    entity = get_object_or_404(Entity, pk=pk)
 
    content = generate_entity_iptables(entity)
 
    response = HttpResponse(content, mimetype='text/plain')
 

	
0 comments (0 inline, 0 general)