Hopefully fix everything that needs to be fixed
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 5d2343f..8679cbd 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -906,19 +906,6 @@
def queryset(self, request):
return TenantPrivilege.select_by_user(request.user)
-class TenantAdmin(XOSBaseAdmin):
- model = Tenant
- list_display = ('backend_status_icon', 'name', 'kind')
- list_display_links = ('backend_status_icon', 'name')
- fieldList = ('backend_status_text', 'name', 'kind', )
- fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
- inlines = (TenantPrivilegeInline)
- readonly_fields = ('backend_status_text', )
-
- suit_form_tabs =(('general', 'Tenant Details'),
- ('tenantprivileges','Privileges')
- )
-
class ProviderTenantInline(XOSTabularInline):
model = CoarseTenant
fields = ['provider_service', 'subscriber_service', 'connect_method']
@@ -2176,7 +2163,6 @@
admin.site.register(Flavor, FlavorAdmin)
admin.site.register(TenantRoot, TenantRootAdmin)
admin.site.register(TenantRootRole, TenantRootRoleAdmin)
- admin.site.register(Tenant, TenantAdmin)
- admin.site.register(TenantRole, TenantRootRoleAdmin)
+ admin.site.register(TenantRole, TenantRoleAdmin)
admin.site.register(TenantAttribute, TenantAttributeAdmin)
# admin.site.register(Container, ContainerAdmin)
diff --git a/xos/core/migrations/0001_initial.py b/xos/core/migrations/0001_initial.py
index db7dad0..33af6a6 100644
--- a/xos/core/migrations/0001_initial.py
+++ b/xos/core/migrations/0001_initial.py
@@ -1,15 +1,15 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from django.db import models, migrations
-import timezones.fields
import core.models.instance
import core.models.network
-import geoposition.fields
-import encrypted_fields.fields
import core.models.serviceclass
import django.utils.timezone
+import encrypted_fields.fields
+import geoposition.fields
+import timezones.fields
from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -1145,6 +1145,39 @@
},
bases=(models.Model,),
),
+ migrations.CreateModel(
+ name='TenantPrivilege',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('created', models.DateTimeField(default=django.utils.timezone.now, auto_now_add=True)),
+ ('updated', models.DateTimeField(default=django.utils.timezone.now, auto_now=True)),
+ ('enacted', models.DateTimeField(default=None, null=True, blank=True)),
+ ('policed', models.DateTimeField(default=None, null=True, blank=True)),
+ ('backend_status', models.CharField(default=b'Provisioning in progress', max_length=140)),
+ ('deleted', models.BooleanField(default=False)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='TenantRole',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('created', models.DateTimeField(default=django.utils.timezone.now, auto_now_add=True)),
+ ('updated', models.DateTimeField(default=django.utils.timezone.now, auto_now=True)),
+ ('enacted', models.DateTimeField(default=None, null=True, blank=True)),
+ ('policed', models.DateTimeField(default=None, null=True, blank=True)),
+ ('backend_status', models.CharField(default=b'Provisioning in progress', max_length=140)),
+ ('deleted', models.BooleanField(default=False)),
+ ('role', models.CharField(unique=True, max_length=30, choices=[(b'admin', b'Admin'), (b'access', b'Access')])),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
migrations.AddField(
model_name='sliceprivilege',
name='role',
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 015564a..86612f8 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -808,9 +808,7 @@
class TenantRole(PlCoreBase):
ROLE_CHOICES = (('admin','Admin'), ('access','Access'))
-
role = StrippedCharField(choices=ROLE_CHOICES, unique=True, max_length=30)
-
def __unicode__(self): return u'%s' % (self.role)
class TenantPrivilege(PlCoreBase):
diff --git a/xos/core/models/user.py b/xos/core/models/user.py
index a8ed571..43a4e2b 100644
--- a/xos/core/models/user.py
+++ b/xos/core/models/user.py
@@ -1,21 +1,22 @@
-import os
import datetime
-import sys
import hashlib
+import os
+import sys
from collections import defaultdict
-from django.forms.models import model_to_dict
+from operator import attrgetter, itemgetter
+
+import synchronizers.model_policy
+from core.middleware import get_request
+from core.models import DashboardView, PlCoreBase, PlModelMixIn, Site
+from core.models.plcorebase import StrippedCharField
+from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
+from django.core.exceptions import PermissionDenied
+from django.core.mail import EmailMultiAlternatives
from django.db import models
from django.db.models import F, Q
+from django.forms.models import model_to_dict
from django.utils import timezone
-from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
-from django.core.mail import EmailMultiAlternatives
-from django.core.exceptions import PermissionDenied
-from core.models import PlCoreBase,Site, DashboardView, PlModelMixIn
-from core.models.plcorebase import StrippedCharField
from timezones.fields import TimeZoneField
-from operator import itemgetter, attrgetter
-from core.middleware import get_request
-import synchronizers.model_policy
# ------ from plcorebase.py ------
try:
diff --git a/xos/services/vpn/admin.py b/xos/services/vpn/admin.py
index 77a411b..9f1ceda 100644
--- a/xos/services/vpn/admin.py
+++ b/xos/services/vpn/admin.py
@@ -1,4 +1,5 @@
import os
+import shutil
from core.admin import ReadOnlyAwareAdmin, SliceInline, TenantPrivilegeInline
from core.middleware import get_request
@@ -123,9 +124,11 @@
vpn_subnet = forms.GenericIPAddressField(protocol="IPv4", required=True)
is_persistent = forms.BooleanField(required=False)
clients_can_see_each_other = forms.BooleanField(required=False)
- failover_servers = forms.ModelMultipleChoiceField(required=False)
+ failover_servers = forms.ModelMultipleChoiceField(required=False, queryset=VPNTenant.get_tenant_objects())
protocol = forms.ChoiceField(required=True, choices=[
("tcp", "tcp"), ("udp", "udp")])
+ use_ca_from = forms.ModelChoiceField(
+ queryset=VPNTenant.get_tenant_objects(), required=False)
def __init__(self, *args, **kwargs):
super(VPNTenantForm, self).__init__(*args, **kwargs)
@@ -151,6 +154,11 @@
self.initial['failover_servers'] = self.instance.failover_servers
self.fields['failover_servers'].queryset = (
VPNTenant.get_tenant_objects().exclude(pk=self.instance.pk))
+ self.fields['use_ca_from'].queryset = (
+ VPNTenant.get_tenant_objects().exclude(pk=self.instance.pk))
+ if (self.instance.use_ca_from):
+ self.fields['use_ca_from'].initial = (
+ self.instance.use_ca_from[0])
if (not self.instance) or (not self.instance.pk):
self.fields['creator'].initial = get_request().user
@@ -165,6 +173,7 @@
VPNService.get_service_objects().all()[0])
def save(self, commit=True):
+ result = super(VPNTenantForm, self).save(commit=commit)
self.instance.creator = self.cleaned_data.get("creator")
self.instance.is_persistent = self.cleaned_data.get('is_persistent')
self.instance.vpn_subnet = self.cleaned_data.get("vpn_subnet")
@@ -172,6 +181,7 @@
self.instance.clients_can_see_each_other = self.cleaned_data.get(
'clients_can_see_each_other')
+ self.instance.failover_servers.clear()
for tenant in self.cleaned_data['failover_servers']:
self.instance.failover_servers.add(tenant)
@@ -180,20 +190,35 @@
self.instance.provider_service.get_next_available_port(
self.instance.protocol))
- result = super(VPNTenantForm, self).save(commit=commit)
- result.save()
- pki_dir = VPNService.OPENVPN_PREFIX + "server-" + str(result.id)
- if (not os.path.isdir(pki_dir)):
- VPNService.execute_easyrsa_command(pki_dir, "init-pki")
+ self.instance.use_ca_from.clear()
+ self.instance.use_ca_from.append(self.cleaned_data.get('use_ca_from'))
+ result.save() # Need to do this so that we know the ID
+
+ self.instance.pki_dir = (
+ VPNService.OPENVPN_PREFIX + "server-" + str(result.id))
+
+ if (not os.path.isdir(self.instance.pki_dir)):
VPNService.execute_easyrsa_command(
- pki_dir, "--req-cn=XOS build-ca nopass")
- result.ca_crt = self.generate_ca_crt(pki_dir)
- result.save()
+ self.instance.pki_dir, "init-pki")
+ if (self.instance.use_ca_from):
+ shutil.copy2(
+ self.instance.use_ca_from.pki_dir + "/ca.crt",
+ self.instance.pki_dir)
+ else:
+ VPNService.execute_easyrsa_command(
+ self.instance.pki_dir, "--req-cn=XOS build-ca nopass")
+ elif (self.instance.use_ca_from):
+ shutil.copy2(
+ self.instance.use_ca_from.pki_dir + "/ca.crt",
+ self.instance.pki_dir)
+
+ result.ca_crt = self.generate_ca_crt()
+
return result
- def generate_ca_crt(self, pki_dir):
+ def generate_ca_crt(self):
"""str: Generates the ca cert by reading from the ca file"""
- with open(pki_dir + "/ca.crt") as crt:
+ with open(self.instance.pki_dir + "/ca.crt") as crt:
return crt.readlines()
class Meta:
@@ -209,7 +234,7 @@
fieldsets = [(None, {'fields': ['backend_status_text', 'kind',
'provider_service', 'instance', 'creator',
'server_network', 'vpn_subnet',
- 'is_persistent',
+ 'is_persistent', 'use_ca_from',
'clients_can_see_each_other',
'failover_servers', "protocol"],
'classes': ['suit-tab suit-tab-general']})]
@@ -235,10 +260,8 @@
# certificate
if type(obj) is TenantPrivilege:
certificate = self.certificate_name(obj)
- pki_dir = (
- VPNService.OPENVPN_PREFIX + "server-" + str(obj.id))
VPNService.execute_easyrsa_command(
- pki_dir, "revoke " + certificate)
+ obj.tenant.pki_dir, "revoke " + certificate)
# TODO(jermowery): determine if this is necessary.
# if type(obj) is VPNTenant:
# if the tenant was deleted revoke all certs assoicated
@@ -248,10 +271,9 @@
# If there were any new TenantPrivlege objects then create certs
if type(obj) is TenantPrivilege:
certificate = self.certificate_name(obj)
- pki_dir = (
- VPNService.OPENVPN_PREFIX + "server-" + str(obj.id))
VPNService.execute_easyrsa_command(
- pki_dir, "build-client-full " + certificate + " nopass")
+ obj.tenant.pki_dir,
+ "build-client-full " + certificate + " nopass")
# Associate the admin forms with the models.
admin.site.register(VPNService, VPNServiceAdmin)
diff --git a/xos/services/vpn/models.py b/xos/services/vpn/models.py
index dfae1e8..b4ff12f 100644
--- a/xos/services/vpn/models.py
+++ b/xos/services/vpn/models.py
@@ -92,6 +92,8 @@
'ca_crt': None,
'port': None,
'script_text': None,
+ 'pki_dir': None,
+ 'use_ca_from': list(),
'failover_servers': set(),
'protocol': None}
@@ -120,6 +122,24 @@
self.set_attribute("protocol", value)
@property
+ def use_ca_from(self):
+ return self.get_attribute(
+ "use_ca_from", self.default_attributes["use_ca_from"])
+
+ @use_ca_from.setter
+ def use_ca_from(self, value):
+ self.set_attribute("use_ca_from", value)
+
+ @property
+ def pki_dir(self):
+ return self.get_attribute(
+ "pki_dir", self.default_attributes["pki_dir"])
+
+ @pki_dir.setter
+ def pki_dir(self, value):
+ self.set_attribute("pki_dir", value)
+
+ @property
def addresses(self):
"""Mapping[str, str]: The ip, mac address, and subnet of the NAT
network of this Tenant."""
@@ -253,14 +273,12 @@
return script
def get_client_cert(self, client_name):
- with open(VPNService.OPENVPN_PREFIX + "server-" + str(self.id) +
- "/issued/" + client_name + ".crt", 'r') as f:
- return f.readlines()
+ with open(self.pki_dir + "/issued/" + client_name + ".crt", 'r') as f:
+ return f.readlines()
def get_client_key(self, client_name):
- with open(VPNService.OPENVPN_PREFIX + "server-" + str(self.id) +
- "/private/" + client_name + ".key", 'r') as f:
- return f.readlines()
+ with open(self.pki_dir + "/private/" + client_name + ".key", 'r') as f:
+ return f.readlines()
def generate_client_conf(self, client_name):
"""str: Generates the client configuration to use to connect to this
diff --git a/xos/synchronizers/vpn/steps/sync_vpntenant.py b/xos/synchronizers/vpn/steps/sync_vpntenant.py
index d066237..58a9287 100644
--- a/xos/synchronizers/vpn/steps/sync_vpntenant.py
+++ b/xos/synchronizers/vpn/steps/sync_vpntenant.py
@@ -39,15 +39,13 @@
tenant.clients_can_see_each_other),
"port_number": tenant.port_number,
"protocol": tenant.protocol,
- "pki_dir": (
- VPNService.OPENVPN_PREFIX + "server-" + str(tenant.id))
+ "pki_dir": tenant.pki_dir
}
def run_playbook(self, o, fields):
# Generate the server files
- pki_dir = VPNService.OPENVPN_PREFIX + "server-" + str(o.id)
- if (not os.path.isfile(pki_dir + "/issued/server.crt")):
+ if (not os.path.isfile(o.pki_dir + "/issued/server.crt")):
VPNService.execute_easyrsa_command(
- pki_dir, "build-server-full server nopass")
- VPNService.execute_easyrsa_command(pki_dir, "gen-crl")
+ o.pki_dir, "build-server-full server nopass")
+ VPNService.execute_easyrsa_command(o.pki_dir, "gen-crl")
super(SyncVPNTenant, self).run_playbook(o, fields)