diff --git a/conntrackt/models.py b/conntrackt/models.py --- a/conntrackt/models.py +++ b/conntrackt/models.py @@ -1,57 +1,227 @@ +# Basic classes needed for creating models. from django.db import models + +# from django.core.exceptions import ValidationError # Create your models here. class Project(models.Model): + """ + Implements a model with information about a project. A project has some + basic settings, and mainly serves the purpose of grouping entities for + easier handling and administration. + + Fields: + + name - String denoting the project name. + description - Free-form description of the project. + """ + name = models.CharField(max_length = 100) description = models.TextField(blank = True) def __unicode__(self): + """ + Returns: + String representation of a project. + """ + return self.name class Location(models.Model): + """ + Implements a model with information about location. Locations can further be + assigned to entities, letting the user group different servers and equipment + based on location. + + Locations are not tied to specific project, and they do not have to be + actual physical locations. Such generic locations are therefore reusable + accross multiple projects. + + For example, locations can be: + + - Main site + - Backup site + - Disaster recovery site + - Belgrade + - Stockholm + + Fields: + + name - String denoting the location name. + description - Free-form description of a location. + """ + name = models.CharField(max_length = 100) description = models.TextField(blank = True) def __unicode__(self): + """ + Returns: + String representation of a location. + """ + return self.name class Entity(models.Model): + """ + Models an entity in a project. An entity can be a server, router, or any + other piece of networking equipment that has its own IP address. + + Entities can also be used for representing subnets etc. This is useful when + the communication restrictions need to be applied across a subnet. + + Entities are tied to specific projects and locations. + + Fields: + + name - String denoting the entity name. + description - Free-form description of an entity. + project - Foreign key pointing to the project to which the entity + belongs. + location - Foreign key pointing to the location at which the entity is + located. + """ + name = models.CharField(max_length = 100) description = models.TextField(blank = True) project = models.ForeignKey(Project) location = models.ForeignKey(Location) class Meta: + """ + Overrides some of the default parameters used by Django for this model. + + Properties: + verbose_name_plural - Changes the way Django Admin displays the model + name in plural. + """ + verbose_name_plural = 'entities' def __unicode__(self): + """ + Returns: + String representation of an entity. This identifier contains name of + entity, its project name, and location name. + """ + return "%s (%s - %s)" % (self.name, self.project, self.location) class Interface(models.Model): - name = models.CharField(max_length = 100) - description = models.TextField(blank = True) + """ + Models a representation of an interface on an entity. It can be used for + representing the subnets as well. + + Each interface is coupled with a specific Entity. + + Fields: + name - String denoting the interface name. For example 'eth0', 'eth1' + etc. + description - Free-form description of an interface. + entity - Foreign key pointing to the entity to which the interface + belongs. + address - IP address of an interface. It's possible to store network + address in it as well. + netmask - Netmask of the interface. By default this is /32 + (255.255.255.255), but in case of subnet entities this can be used for + denoting the network netmask. + """ + + name = models.CharField(max_length = 100, default = 'eth0') + description = models.TextField(blank = True, default = 'Main network interface.') entity = models.ForeignKey(Entity) address = models.IPAddressField() - netmask = models.IPAddressField(default='255.255.255.255') + netmask = models.IPAddressField(default = '255.255.255.255') def __unicode__(self): - return "%s (%s = %s/%s)" % (self.entity.name, self.name, self.address, self.netmask) + """ + Returns: + String representation of an interface. In case of single IP this will + simply be the interface name and IP address. In case of subnet it will + include the netmask as well. + """ + + if self.netmask == '255.255.255.255': + return '%s (%s)' % (self.entity.name, self.address) + else: + return '%s (%s/%s)' % (self.entity.name, self.address, self.netmask) class Communication(models.Model): + """ + Models a representation of allowed network communication. This lets the user + display the possible network connections that should be allowed. Information + from the communication instances is also used for generating the iptables + rules for the entities. + + Communication instances allow the user to specify one of the three possible + protocols and related information: + + - TCP, along with the TCP port. + - UDP, along with the UDP port. + - ICMP, along with the ICMP type. + + Allowed communication is always represented as combination of source + interface, destination interface, protocol, and port/ICMP type. + + Fields: + source - Foreign key to the source (originating) interface. The + communication is expected to come _from_ the source. + destination - Foreign key to the destination interface. The destination + interface is expected to be able to accept incoming connections + (i.e. entity's servers are listening on those). + protocol - Textual field denoting the protocol that is used for + communication. This can be 'TCP', 'UDP', or 'ICMP'. + port - Port number used by the protocol. In case of ICMP, this is an ICMP + type (in numeric form). + description - Free-form text that can be used to describe the + communication. This is also used when generating the iptables rules for + documenting the rules. + """ + + PROTOCOL_CHOICES = ( + ('TCP', 'TCP'), + ('UDP', 'UDP'), + ('ICMP', 'ICMP'), + ) + source = models.ForeignKey(Interface, related_name = 'source_set') destination = models.ForeignKey(Interface, related_name = 'destination_set') - protocol = models.CharField(max_length = 10) + protocol = models.CharField(max_length = 10, choices = PROTOCOL_CHOICES) port = models.IntegerField(default = 0) description = models.TextField(blank = True) def __unicode__(self): - return "%s (%s/%s) -> %s (%s/%s)" % (self.source.entity.name, self.source.address, self.source.netmask, - self.destination.entity.name, self.destination.address, self.destination.netmask) + """ + Returns: + String representation of an interface. This involves showing the + source and destination _entity_ name, protocol, and port. + """ + + return "%s -> %s (%s:%s)" % (self.source.entity.name, self.destination.entity.name, self.protocol, self.port) def clean(self): - if self.source == self.destination: - raise ValidationError('Source and destination must differ.') - if self.protocol.lower() not in ('udp', 'tcp', 'icmp'): + """ + Performs additional validation checks on the submitted data. It will + verify the following: + + - That source and destination interface belongs to distinct entities. + - That the specified protocol is supported. + """ + + if self.source.entity == self.destination.entity: + raise ValidationError('Source and destination entities must differ.') + if (self.protocol.upper(), self.protocol.upper()) not in self.PROTOCOL_CHOICES: raise ValidationError('%s is not a supported protocol.' % self.protocol) + def edit_link(self): + """ + This method is used for providing an additional 'Edit' link in the admin + site for the communication instances (for the display_list). + + This provides ability to let all of the other fields of a communication + instance to be editable. + """ + + return "Edit" +