Changeset - 72d37f849053
[Not reviewed]
default
0 4 2
Branko Majic (branko) - 11 years ago 2013-07-17 21:35:51
branko@majic.rs
CONNT-5: Implemented views for updating and removing interfaces. Includes tests.
6 files changed with 319 insertions and 5 deletions:
0 comments (0 inline, 0 general)
conntrackt/templates/conntrackt/entity_detail.html
Show inline comments
 
@@ -54,11 +54,13 @@
 
    <div class="well">
 
      <table class="table table-striped">
 
        <tr>
 
          <th style="width:99%">Interfaces</th>
 
          <th colspan="3">Interfaces</th>
 
        </tr>
 
        {% for interface in interfaces %}
 
          <tr>
 
            <td>{{interface.name}} ({{interface.address}}/{{interface.netmask}})</td>
 
            <td style="width:99%">{{interface.name}} ({{interface.address}}/{{interface.netmask}})</td>
 
            <td>{% html_link '<i class="icon-edit"></i>' 'interface_update' interface.id class="btn btn-link"  %}</td>
 
            <td>{% html_link '<i class="icon-remove"></i>' 'interface_delete' interface.id class="btn btn-link"  %}</td>
 
          </tr>
 
        {% endfor %}
 
      </table>
conntrackt/templates/conntrackt/interface_confirm_delete.html
Show inline comments
 
new file 100644
 
{% extends "conntrackt/base.html" %}
 

	
 
{# For html_link #}
 
{% load conntrackt_tags %}
 
{# For Bootstrapped forms #}
 
{% load crispy_forms_tags %}
 

	
 
{% block content %}
 
<div class="row">
 
  <h1 class="span12">Remove interface {{interface.name}}</h1>
 
</div>
 
<div class="row">
 
  <div class="span12">
 
    <form action="" method="post">
 
      <div class="controls controls-row">
 
        {% csrf_token %}
 
        {{ form }}
 
        Are you sure you want to remove this interface?
 
      </div>
 
      <hr>
 
      <div class="controls">
 
        <button type="submit" class="btn btn-primary">Remove</button>
 
      </div>
 
    </form>
 
  </div>
 
</div>
 
{% endblock content %}
conntrackt/templates/conntrackt/interface_update_form.html
Show inline comments
 
new file 100644
 
{% extends "conntrackt/base.html" %}
 

	
 
{# For html_link #}
 
{% load conntrackt_tags %}
 
{# For Bootstrapped forms #}
 
{% load crispy_forms_tags %}
 

	
 
{% block content %}
 
<div class="row">
 
  <h1 class="span12">Edit interface {{interface.name}}</h1>
 
</div>
 
<div class="row">
 
  <div class="span6">
 
    <form action="" method="post">
 
      <div class="controls controls-row">
 
      {% csrf_token %}
 
      {{ form | crispy }}
 
      </div>
 
      <div class="controls">
 
        <button type="submit" class="btn btn-primary">Update</button>
 
      </div>
 
    </form>
 
  </div>
 
</div>
 
{% endblock content %}
conntrackt/tests/test_views.py
Show inline comments
 
@@ -10,8 +10,9 @@ from django.test.client import Client
 
from django.contrib.auth.models import User, Permission
 

	
 
# Application imports
 
from conntrackt.models import Project, Location, Entity
 
from conntrackt.views import EntityCreateView, InterfaceCreateView
 
from conntrackt.models import Project, Location, Entity, Interface
 
from conntrackt.views import EntityCreateView
 
from conntrackt.views import InterfaceCreateView, InterfaceUpdateView
 

	
 

	
 
class ViewTest(TestCase):
 
@@ -1064,3 +1065,175 @@ class InterfaceCreateViewTest(TestCase):
 

	
 
        self.assertDictContainsSubset({"entity": "1"}, initial)
 

	
 

	
 
class InterfaceUpdateViewTest(TestCase):
 

	
 
    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="change_interface"))
 
        self.user["noperms"] = User.objects.create_user("noperms", "noperms@example.com", "noperms")
 

	
 
    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("interface_update", 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("interface_update", args=(1,)))
 

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
    def test_content(self):
 
        """
 
        Tests if the form comes pre-populated with proper content.
 
        """
 

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

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

	
 
        self.assertContains(response, ">Edit interface eth0<")
 
        self.assertContains(response, 'value="eth0"')
 
        self.assertContains(response, "Main network interface.")
 

	
 
    def test_form_entity_limit(self):
 
        """
 
        Tests if the queryset is properly limitted to specific project's
 
        entities.
 
        """
 

	
 
        # Set-up the view.
 
        view = InterfaceUpdateView()
 
        view.request = RequestFactory().get("/fake-path/1")
 
        view.object = Interface.objects.get(pk=1)
 

	
 
        # Get the form.
 
        form = view.get_form(view.get_form_class())
 

	
 
        expected_entities = ["<Entity: Test Entity 1 (Test Project 1 - Test Location 1)>",
 
                             "<Entity: Test Entity 2 (Test Project 1 - Test Location 1)>",
 
                             "<Entity: Test Entity 3 (Test Project 1 - Test Location 2)>",
 
                             "<Entity: Test Subnet (Test Project 1 - Test Location 2)>"]
 

	
 
        self.assertQuerysetEqual(form.fields["entity"].queryset, expected_entities)
 

	
 
    def test_success_url(self):
 
        """
 
        Validate that the success URL is set properly after update.
 
        """
 

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

	
 
        interface = Interface.objects.get(pk=1)
 
        
 
        response = self.client.get(reverse("interface_update", args=(1,)))
 

	
 
        response = self.client.post(reverse("interface_update", args=(1,)),
 
                                    {'csrfmiddlewaretoken': response.context['request'].META['CSRF_COOKIE'],
 
                                     "name": interface.name,
 
                                     "description": interface.name,
 
                                     "entity": "1",
 
                                     "address": "192.168.1.1",
 
                                     "netmask": "255.255.255.255"},
 
                                    follow=True)
 

	
 
        self.assertEqual(response.context["request"].META["PATH_INFO"], reverse("entity", args=(1,)))
 

	
 

	
 
class InterfaceDeleteViewTest(TestCase):
 

	
 
    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="delete_interface"))
 
        self.user["fullperms"].user_permissions.add(Permission.objects.get(codename="view"))
 
        self.user["noperms"] = User.objects.create_user("noperms", "noperms@example.com", "noperms")
 

	
 
    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("interface_delete", 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("interface_delete", args=(1,)))
 

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
    def test_content(self):
 
        """
 
        Tests if the form comes pre-populated with proper content.
 
        """
 

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

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

	
 
        self.assertContains(response, ">Remove interface eth0<")
 
        self.assertContains(response, "Are you sure you want to remove this interface?")
 

	
 
    def test_message(self):
 
        """
 
        Tests if the message gets added when the interface is deleted.
 
        """
 

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

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

	
 
        response = self.client.post(reverse("interface_delete", args=(1,)),
 
                                    {'csrfmiddlewaretoken': response.context['request'].META['CSRF_COOKIE']},
 
                                    follow=True)
 

	
 
        self.assertContains(response, "Interface eth0 has been removed.")
 

	
 
    def test_success_url(self):
 
        """
 
        Validate that the success URL is set properly after delete.
 
        """
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 
        
 
        response = self.client.get(reverse("interface_delete", args=(1,)))
 

	
 
        response = self.client.post(reverse("interface_delete", args=(1,)),
 
                                    {'csrfmiddlewaretoken': response.context['request'].META['CSRF_COOKIE']},
 
                                    follow=True)
 

	
 
        self.assertEqual(response.context["request"].META["PATH_INFO"], reverse("entity", args=(1,)))
conntrackt/urls.py
Show inline comments
 
@@ -7,7 +7,7 @@ from .views import IndexView, EntityView
 
from .views import ProjectView, ProjectCreateView, ProjectUpdateView, ProjectDeleteView
 
from .views import LocationCreateView, LocationUpdateView, LocationDeleteView
 
from .views import EntityCreateView, EntityUpdateView, EntityDeleteView
 
from .views import InterfaceCreateView
 
from .views import InterfaceCreateView, InterfaceUpdateView, InterfaceDeleteView
 

	
 

	
 
urlpatterns = patterns(
 
@@ -45,6 +45,10 @@ urlpatterns = patterns(
 

	
 
    # View for creating a new interface.
 
    url(r'^interface/add/$', InterfaceCreateView.as_view(), name="interface_create"),
 
    # View for updating an existing interface.
 
    url(r'^interface/(?P<pk>\d+)/edit/$', InterfaceUpdateView.as_view(), name="interface_update"),
 
    # View for deleting an interface.
 
    url(r'^interface/(?P<pk>\d+)/remove/$', InterfaceDeleteView.as_view(), name="interface_delete"),
 

	
 
    # View for rendering iptables rules for a specific entity.
 
    url(r'^entity/(?P<pk>\d+)/iptables/$', entity_iptables, name="entity_iptables"),
conntrackt/views.py
Show inline comments
 
@@ -598,3 +598,86 @@ class InterfaceCreateView(MultiplePermis
 
        """
 

	
 
        return reverse("entity", args=(self.object.entity.pk,))
 

	
 

	
 
class InterfaceUpdateView(MultiplePermissionsRequiredMixin, UpdateView):
 
    """
 
    View for updating an existing interface.
 
    """
 

	
 
    model = Interface
 
    form_class = InterfaceForm
 
    template_name_suffix = "_update_form"
 

	
 
    # Required permissions.
 
    permissions = {
 
        "all": ("conntrackt.change_interface",),
 
        }
 

	
 
    # Raise authorisation denied exception for unmet permissions.
 
    raise_exception = True
 

	
 
    def get_form(self, form_class):
 
        """
 
        Returns an instance of form that can be used by the view.
 

	
 
        The method will limit the entities that can be selected for the
 
        interface to the ones that belong to the same project as the currently
 
        set entity.
 
        """
 

	
 
        form = super(InterfaceUpdateView, self).get_form(form_class)
 

	
 
        # Limit the entities to same project.
 
        form.fields["entity"].queryset = Entity.objects.filter(project=self.object.entity.project)
 

	
 
        return form
 

	
 
    def get_success_url(self):
 
        """
 
        Returns the URL to which the user should be redirected after an
 
        interface has been updated.
 

	
 
        The URL in this case will be set to entity's details page.
 
        """
 

	
 
        return reverse("entity", args=(self.object.entity.pk,))
 

	
 

	
 
class InterfaceDeleteView(MultiplePermissionsRequiredMixin, DeleteView):
 
    """
 
    View for deleting an interface.
 
    """
 

	
 
    model = Interface
 

	
 
    # Required permissions.
 
    permissions = {
 
        "all": ("conntrackt.delete_interface",),
 
        }
 

	
 
    # Raise authorisation denied exception for unmet permissions.
 
    raise_exception = True
 

	
 
    def post(self, *args, **kwargs):
 
        """
 
        Add a success message that will be displayed to the user to confirm the
 
        interface deletion.
 
        """
 

	
 
        messages.success(self.request, "Interface %s has been removed." % self.get_object().name, extra_tags="alert alert-success")
 

	
 
        return super(InterfaceDeleteView, self).post(*args, **kwargs)
 

	
 
    def delete(self, *args, **kwargs):
 
        """
 
        Deletes the object. This method is overridden in order to obtain the
 
        entity ID for success URL.
 

	
 
        @TODO: Fix this once Django 1.6 comes out with fix from ticket 19044.
 
        """
 

	
 
        self.success_url = reverse("entity", args=(self.get_object().entity.id,))
 

	
 
        return super(InterfaceDeleteView, self).delete(*args, **kwargs)
 

	
0 comments (0 inline, 0 general)