Jeremy Mowery | 0ee0bfc | 2016-04-15 16:13:45 -0700 | [diff] [blame] | 1 | from django import forms |
| 2 | from django.contrib import admin |
| 3 | |
Jeremy Mowery | b31bd9e | 2016-03-14 23:59:11 -0700 | [diff] [blame] | 4 | from core.admin import ReadOnlyAwareAdmin, SliceInline, TenantPrivilegeInline |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 5 | from core.middleware import get_request |
Jeremy Mowery | fba5a84 | 2016-04-13 15:10:23 -0700 | [diff] [blame] | 6 | from core.models import User |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 7 | from services.openvpn.models import OPENVPN_KIND, OpenVPNService, OpenVPNTenant |
Jeremy Mowery | b3c735b | 2016-04-02 10:16:21 -0700 | [diff] [blame] | 8 | from xos.exceptions import XOSValidationError |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 9 | |
| 10 | |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 11 | class OpenVPNServiceForm(forms.ModelForm): |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 12 | |
Jeremy Mowery | 9ddca29 | 2016-03-27 20:32:04 -0700 | [diff] [blame] | 13 | exposed_ports = forms.CharField(required=True) |
| 14 | |
Jeremy Mowery | 10be54f | 2016-03-27 21:35:58 -0700 | [diff] [blame] | 15 | def __init__(self, *args, **kwargs): |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 16 | super(OpenVPNServiceForm, self).__init__(*args, **kwargs) |
Jeremy Mowery | 10be54f | 2016-03-27 21:35:58 -0700 | [diff] [blame] | 17 | |
| 18 | if self.instance: |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 19 | self.fields['exposed_ports'].initial = ( |
| 20 | self.instance.exposed_ports_str) |
Jeremy Mowery | 10be54f | 2016-03-27 21:35:58 -0700 | [diff] [blame] | 21 | |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 22 | def save(self, commit=True): |
Jeremy Mowery | 9ddca29 | 2016-03-27 20:32:04 -0700 | [diff] [blame] | 23 | self.instance.exposed_ports = self.cleaned_data['exposed_ports'] |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 24 | return super(OpenVPNServiceForm, self).save(commit=commit) |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 25 | |
Jeremy Mowery | 9ddca29 | 2016-03-27 20:32:04 -0700 | [diff] [blame] | 26 | def clean_exposed_ports(self): |
| 27 | exposed_ports = self.cleaned_data['exposed_ports'] |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 28 | self.instance.exposed_ports_str = exposed_ports |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 29 | port_mapping = {"udp": [], "tcp": []} |
| 30 | parts = exposed_ports.split(",") |
| 31 | for part in parts: |
| 32 | part = part.strip() |
| 33 | if "/" in part: |
| 34 | (protocol, ports) = part.split("/", 1) |
| 35 | elif " " in part: |
| 36 | (protocol, ports) = part.split(None, 1) |
| 37 | else: |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 38 | raise XOSValidationError( |
| 39 | 'malformed port specifier %s, format example: ' + |
| 40 | '"tcp 123, tcp 201:206, udp 333"' % part) |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 41 | |
| 42 | protocol = protocol.strip() |
| 43 | ports = ports.strip() |
| 44 | |
| 45 | if not (protocol in ["udp", "tcp"]): |
| 46 | raise XOSValidationError('unknown protocol %s' % protocol) |
| 47 | |
| 48 | if "-" in ports: |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 49 | port_mapping[protocol].extend( |
| 50 | self.parse_port_range(ports, "-")) |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 51 | elif ":" in ports: |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 52 | port_mapping[protocol].extend( |
| 53 | self.parse_port_range(ports, ":")) |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 54 | else: |
| 55 | port_mapping[protocol].append(int(ports)) |
| 56 | |
| 57 | return port_mapping |
| 58 | |
| 59 | def parse_port_range(self, port_str, split_str): |
| 60 | (first, last) = port_str.split(split_str) |
| 61 | first = int(first.strip()) |
| 62 | last = int(last.strip()) |
| 63 | return list(range(first, last)) |
| 64 | |
| 65 | class Meta: |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 66 | model = OpenVPNService |
Jeremy Mowery | 1f3f9f6 | 2016-03-21 17:56:25 -0700 | [diff] [blame] | 67 | |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 68 | |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 69 | class OpenVPNServiceAdmin(ReadOnlyAwareAdmin): |
| 70 | """Defines the admin for the OpenVPNService.""" |
| 71 | model = OpenVPNService |
| 72 | form = OpenVPNServiceForm |
| 73 | verbose_name = "OpenVPN Service" |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 74 | |
| 75 | list_display = ("backend_status_icon", "name", "enabled") |
| 76 | |
| 77 | list_display_links = ('backend_status_icon', 'name', ) |
| 78 | |
| 79 | fieldsets = [(None, {'fields': ['backend_status_text', 'name', 'enabled', |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 80 | 'versionNumber', 'description', "view_url", |
| 81 | 'exposed_ports'], |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 82 | 'classes':['suit-tab suit-tab-general']})] |
| 83 | |
| 84 | readonly_fields = ('backend_status_text', ) |
| 85 | |
| 86 | inlines = [SliceInline] |
| 87 | |
| 88 | extracontext_registered_admins = True |
| 89 | |
| 90 | user_readonly_fields = ["name", "enabled", "versionNumber", "description"] |
| 91 | |
| 92 | suit_form_tabs = (('general', 'VPN Service Details'), |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 93 | ('slices', 'Slices'),) |
| 94 | |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 95 | def queryset(self, request): |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 96 | return OpenVPNService.get_service_objects_by_user(request.user) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 97 | |
| 98 | |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 99 | class OpenVPNTenantForm(forms.ModelForm): |
| 100 | """The form used to create and edit a OpenVPNTenant. |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 101 | |
| 102 | Attributes: |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 103 | creator (forms.ModelChoiceField): The XOS user that created this |
| 104 | tenant. |
Jeremy Mowery | 98e97d7 | 2016-04-15 00:05:27 -0700 | [diff] [blame] | 105 | server_network (forms.GenericIPAddressField): The IP address of the VPN network. |
| 106 | vpn_subnet (forms.GenericIPAddressField): The subnet used by the VPN network. |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 107 | is_persistent (forms.BooleanField): Determines if this Tenant keeps |
| 108 | this connection alive through failures. |
Jeremy Mowery | 98e97d7 | 2016-04-15 00:05:27 -0700 | [diff] [blame] | 109 | clients_can_see_each_other (forms.BooleanField): Determines if the clients on the VPN can |
| 110 | communicate with each other. |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 111 | failover_servers (forms.ModelMultipleChoiceField): The other OpenVPNTenants to use as failover |
Jeremy Mowery | 98e97d7 | 2016-04-15 00:05:27 -0700 | [diff] [blame] | 112 | servers. |
| 113 | protocol (forms.ChoiceField): The protocol to use. |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 114 | use_ca_from (forms.ModelChoiceField): Another OpenVPNTenant to use the CA of, this is a very |
Jeremy Mowery | 98e97d7 | 2016-04-15 00:05:27 -0700 | [diff] [blame] | 115 | hacky way to let VPNs have the same clients. |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 116 | """ |
| 117 | creator = forms.ModelChoiceField(queryset=User.objects.all()) |
| 118 | server_network = forms.GenericIPAddressField( |
| 119 | protocol="IPv4", required=True) |
| 120 | vpn_subnet = forms.GenericIPAddressField(protocol="IPv4", required=True) |
| 121 | is_persistent = forms.BooleanField(required=False) |
| 122 | clients_can_see_each_other = forms.BooleanField(required=False) |
Jeremy Mowery | 86aafe3 | 2016-04-10 21:04:08 -0700 | [diff] [blame] | 123 | failover_servers = forms.ModelMultipleChoiceField( |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 124 | required=False, queryset=OpenVPNTenant.get_tenant_objects()) |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 125 | protocol = forms.ChoiceField(required=True, choices=[ |
Jeremy Mowery | 5d06a23 | 2016-04-04 22:30:44 -0700 | [diff] [blame] | 126 | ("tcp", "tcp"), ("udp", "udp")]) |
Jeremy Mowery | 36fb9e5 | 2016-04-05 23:32:10 -0700 | [diff] [blame] | 127 | use_ca_from = forms.ModelChoiceField( |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 128 | queryset=OpenVPNTenant.get_tenant_objects(), required=False) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 129 | |
| 130 | def __init__(self, *args, **kwargs): |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 131 | super(OpenVPNTenantForm, self).__init__(*args, **kwargs) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 132 | self.fields['kind'].widget.attrs['readonly'] = True |
Jeremy Mowery | 18d054e | 2016-04-13 22:18:50 -0700 | [diff] [blame] | 133 | self.fields['failover_servers'].widget.attrs['rows'] = 300 |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 134 | self.fields[ |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 135 | 'provider_service'].queryset = ( |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 136 | OpenVPNService.get_service_objects().all()) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 137 | |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 138 | self.fields['kind'].initial = OPENVPN_KIND |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 139 | |
| 140 | if self.instance: |
| 141 | self.fields['creator'].initial = self.instance.creator |
| 142 | self.fields['vpn_subnet'].initial = self.instance.vpn_subnet |
| 143 | self.fields[ |
| 144 | 'server_network'].initial = self.instance.server_network |
| 145 | self.fields[ |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 146 | 'clients_can_see_each_other'].initial = ( |
| 147 | self.instance.clients_can_see_each_other) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 148 | self.fields['is_persistent'].initial = self.instance.is_persistent |
Jeremy Mowery | 26eb2fe | 2016-03-29 23:52:46 -0700 | [diff] [blame] | 149 | self.initial['protocol'] = self.instance.protocol |
Jeremy Mowery | 5d06a23 | 2016-04-04 22:30:44 -0700 | [diff] [blame] | 150 | self.fields['failover_servers'].queryset = ( |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 151 | OpenVPNTenant.get_tenant_objects().exclude(pk=self.instance.pk)) |
| 152 | self.initial['failover_servers'] = OpenVPNTenant.get_tenant_objects().filter( |
Jeremy Mowery | 18d054e | 2016-04-13 22:18:50 -0700 | [diff] [blame] | 153 | pk__in=self.instance.failover_server_ids) |
Jeremy Mowery | 36fb9e5 | 2016-04-05 23:32:10 -0700 | [diff] [blame] | 154 | self.fields['use_ca_from'].queryset = ( |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 155 | OpenVPNTenant.get_tenant_objects().exclude(pk=self.instance.pk)) |
Jeremy Mowery | 0fce723 | 2016-04-12 22:03:55 -0700 | [diff] [blame] | 156 | if (self.instance.use_ca_from_id): |
Jeremy Mowery | 18d054e | 2016-04-13 22:18:50 -0700 | [diff] [blame] | 157 | self.initial['use_ca_from'] = ( |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 158 | OpenVPNTenant.get_tenant_objects().filter(pk=self.instance.use_ca_from_id)[0]) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 159 | |
| 160 | if (not self.instance) or (not self.instance.pk): |
| 161 | self.fields['creator'].initial = get_request().user |
| 162 | self.fields['vpn_subnet'].initial = "255.255.255.0" |
| 163 | self.fields['server_network'].initial = "10.66.77.0" |
| 164 | self.fields['clients_can_see_each_other'].initial = True |
| 165 | self.fields['is_persistent'].initial = True |
Jeremy Mowery | 5d06a23 | 2016-04-04 22:30:44 -0700 | [diff] [blame] | 166 | self.fields['failover_servers'].queryset = ( |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 167 | OpenVPNTenant.get_tenant_objects()) |
| 168 | if OpenVPNService.get_service_objects().exists(): |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 169 | self.fields["provider_service"].initial = ( |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 170 | OpenVPNService.get_service_objects().all()[0]) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 171 | |
| 172 | def save(self, commit=True): |
| 173 | self.instance.creator = self.cleaned_data.get("creator") |
| 174 | self.instance.is_persistent = self.cleaned_data.get('is_persistent') |
| 175 | self.instance.vpn_subnet = self.cleaned_data.get("vpn_subnet") |
| 176 | self.instance.server_network = self.cleaned_data.get('server_network') |
| 177 | self.instance.clients_can_see_each_other = self.cleaned_data.get( |
| 178 | 'clients_can_see_each_other') |
Jeremy Mowery | 5d06a23 | 2016-04-04 22:30:44 -0700 | [diff] [blame] | 179 | |
Jeremy Mowery | 18d054e | 2016-04-13 22:18:50 -0700 | [diff] [blame] | 180 | self.instance.failover_server_ids = [ |
| 181 | tenant.id for tenant in self.cleaned_data.get('failover_servers')] |
Jeremy Mowery | 324caf5 | 2016-03-16 11:22:02 -0700 | [diff] [blame] | 182 | |
Jeremy Mowery | 9c6b9f3 | 2016-04-10 22:10:24 -0700 | [diff] [blame] | 183 | # Do not aquire a new port number if the protocol hasn't changed |
| 184 | if ((not self.instance.protocol) or |
| 185 | (self.instance.protocol != self.cleaned_data.get("protocol"))): |
| 186 | self.instance.protocol = self.cleaned_data.get("protocol") |
| 187 | self.instance.port_number = ( |
| 188 | self.instance.provider_service.get_next_available_port( |
| 189 | self.instance.protocol)) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 190 | |
Jeremy Mowery | 3d8f11f | 2016-04-12 22:54:26 -0700 | [diff] [blame] | 191 | if (self.cleaned_data.get('use_ca_from')): |
Jeremy Mowery | 5acf7b9 | 2016-04-12 23:31:51 -0700 | [diff] [blame] | 192 | self.instance.use_ca_from_id = self.cleaned_data.get( |
| 193 | 'use_ca_from').id |
Jeremy Mowery | 18d054e | 2016-04-13 22:18:50 -0700 | [diff] [blame] | 194 | else: |
| 195 | self.instance.use_ca_from_id = None |
Jeremy Mowery | 3d8f11f | 2016-04-12 22:54:26 -0700 | [diff] [blame] | 196 | |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 197 | return super(OpenVPNTenantForm, self).save(commit=commit) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 198 | |
| 199 | class Meta: |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 200 | model = OpenVPNTenant |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 201 | |
| 202 | |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 203 | class OpenVPNTenantAdmin(ReadOnlyAwareAdmin): |
| 204 | verbose_name = "OpenVPN Tenant Admin" |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 205 | list_display = ('id', 'backend_status_icon', 'instance', |
| 206 | 'server_network', 'vpn_subnet') |
| 207 | list_display_links = ('id', 'backend_status_icon', |
| 208 | 'instance', 'server_network', 'vpn_subnet') |
| 209 | fieldsets = [(None, {'fields': ['backend_status_text', 'kind', |
| 210 | 'provider_service', 'instance', 'creator', |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 211 | 'server_network', 'vpn_subnet', |
Jeremy Mowery | 36fb9e5 | 2016-04-05 23:32:10 -0700 | [diff] [blame] | 212 | 'is_persistent', 'use_ca_from', |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 213 | 'clients_can_see_each_other', |
| 214 | 'failover_servers', "protocol"], |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 215 | 'classes': ['suit-tab suit-tab-general']})] |
| 216 | readonly_fields = ('backend_status_text', 'instance') |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 217 | form = OpenVPNTenantForm |
Jeremy Mowery | b31bd9e | 2016-03-14 23:59:11 -0700 | [diff] [blame] | 218 | inlines = [TenantPrivilegeInline] |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 219 | |
Jeremy Mowery | ee71fce | 2016-04-01 21:54:29 -0700 | [diff] [blame] | 220 | suit_form_tabs = (('general', 'Details'), |
| 221 | ('tenantprivileges', 'Privileges')) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 222 | |
| 223 | def queryset(self, request): |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 224 | return OpenVPNTenant.get_tenant_objects_by_user(request.user) |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 225 | |
Jeremy Mowery | b31bd9e | 2016-03-14 23:59:11 -0700 | [diff] [blame] | 226 | |
Jeremy Mowery | 3c5277f | 2016-02-29 21:09:04 -0700 | [diff] [blame] | 227 | # Associate the admin forms with the models. |
Jeremy Mowery | 81e84cc | 2016-04-19 17:01:48 -0700 | [diff] [blame] | 228 | admin.site.register(OpenVPNService, OpenVPNServiceAdmin) |
| 229 | admin.site.register(OpenVPNTenant, OpenVPNTenantAdmin) |