Hopefully make the VPN work with certificates, automatically pick ports, and have user privleges
diff --git a/xos/services/vpn/models.py b/xos/services/vpn/models.py
index 1c7af1b..6b7872c 100644
--- a/xos/services/vpn/models.py
+++ b/xos/services/vpn/models.py
@@ -1,5 +1,6 @@
 from core.models import Service, TenantWithContainer
 from django.db import transaction
+from xos.exceptions import XOSConfigurationError, XOSValidationError
 
 VPN_KIND = "vpn"
 
@@ -14,6 +15,28 @@
         app_label = "vpn"
         verbose_name = "VPN Service"
 
+    default_attributes = {'exposed_ports': None}
+
+    @property
+    def exposed_ports(self):
+        return self.get_attribute("exposed_ports",
+                                    self.default_attributes["exposed_ports"])
+
+    @exposed_ports.setter
+    def exposed_ports(self, value):
+        self.set_attribute("exposed_ports", value)
+
+    def get_next_available_port(self, protocol):
+        if protocol != "udp" and protocol != "tcp":
+            raise XOSConfigurationError("Port protocol must be udp or tcp")
+        if not self.ports[protocol]:
+            raise XOSValidationError("No availble ports for protocol: " + protocol)
+        tenants = [tenant for tenant in VPNTenant.get_tenant_objects.all() if tenant.protocol == protocol]
+        port_numbers = self.exposed_ports[protocol]
+        for port_number in port_numbers:
+            if [tenant for tenant in tenants if tenant.port_number == port_number].count() == 0:
+                return port_number
+
 
 class VPNTenant(TenantWithContainer):
     """Defines the Tenant for creating VPN servers."""
@@ -33,7 +56,8 @@
                           'ca_crt': None,
                           'port': None,
                           'script_text': None,
-                          'failover_servers': []}
+                          'failover_servers': [],
+                          'protocol': None}
 
     def __init__(self, *args, **kwargs):
         vpn_services = VPNService.get_service_objects().all()
@@ -51,6 +75,14 @@
         super(VPNTenant, self).delete(*args, **kwargs)
 
     @property
+    def protocol(self):
+        return self.get_attribute("protocol", self.default_attributes["protocol"])
+
+    @protocol.setter
+    def protocol(self, value):
+        self.set_attribute("protocol", value)
+
+    @property
     def addresses(self):
         """Mapping[str, str]: The ip, mac address, and subnet of the NAT network of this Tenant."""
         if (not self.id) or (not self.instance):
@@ -155,50 +187,50 @@
     def script_text(self, value):
         self.set_attribute("script_text", value)
 
-    def create_client_script(self, client_certificate):
+    def create_client_script(self, client_name):
         script = ""
         # write the configuration portion
         script += ("printf \"%b\" \"")
-        script += self.generate_client_conf(client_certificate)
+        script += self.generate_client_conf(client_name)
         script += ("\" > client.conf\n")
         script += ("printf \"%b\" \"")
         for line in self.ca_crt:
             script += (line.rstrip() + r"\n")
         script += ("\" > ca.crt\n")
         script += ("printf \"%b\" \"")
-        for line in self.generate_client_cert(client_certificate):
+        for line in self.generate_client_cert(client_name):
             script += (line.rstrip() + r"\n")
-        script += ("\" > " + client_certificate + ".crt\n")
-        for line in self.generate_client_key(client_certificate):
+        script += ("\" > " + client_name + ".crt\n")
+        for line in self.generate_client_key(client_name):
             script += (line.rstrip() + r"\n")
-        script += ("\" > " + client_certificate + ".key\n")
+        script += ("\" > " + client_name + ".key\n")
         # make sure openvpn is installed
         script += ("apt-get update\n")
         script += ("apt-get install openvpn\n")
         script += ("openvpn client.conf &\n")
         # close the script
-        return script;
+        return script
 
-    def generate_client_cert(self, client_certificate):
-        return open("/opt/openvpn/easyrsa3/pki/issued/" + client_certificate + ".crt").readlines()
+    def generate_client_cert(self, client_name):
+        return open("/opt/openvpn/easyrsa3/pki/issued/" + client_name + ".crt").readlines()
 
-    def generate_client_key(self, client_certificate):
-        return open("/opt/openvpn/easyrsa3/pki/private/" + client_certificate + ".key").readlines()
+    def generate_client_key(self, client_name):
+        return open("/opt/openvpn/easyrsa3/pki/private/" + client_name + ".key").readlines()
 
-    def generate_client_conf(self, client_certificate):
+    def generate_client_conf(self, client_name):
         """str: Generates the client configuration to use to connect to this VPN server.
         """
         conf = ("client\n" +
-            "dev tun\n" +
-            "proto udp\n" +
-            "remote " + str(self.nat_ip) + " " + str(self.port_number) + "\n" +
-            "resolv-retry infinite\n" +
-            "nobind\n" +
-            "ca ca.crt\n" +
-            "cert " + client_certificate + ".crt\n" +
-            "key " + client_certificate + ".key\n" +
-            "comp-lzo\n" +
-            "verb 3\n")
+                "dev tun\n" +
+                "proto " + self.protocol + "\n" +
+                "remote " + str(self.nat_ip) + " " + str(self.port_number) + "\n" +
+                "resolv-retry infinite\n" +
+                "nobind\n" +
+                "ca ca.crt\n" +
+                "cert " + client_name + ".crt\n" +
+                "key " + client_name + ".key\n" +
+                "comp-lzo\n" +
+                "verb 3\n")
 
         if self.is_persistent:
             conf += "persist-tun\n"
@@ -207,7 +239,6 @@
         return conf
 
 
-
 def model_policy_vpn_tenant(pk):
     """Manages the contain for the VPN Tenant."""
     # This section of code is atomic to prevent race conditions