Add privileges for tenants
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 6448ed4..8ca8ff8 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -879,6 +879,16 @@
     def queryset(self, request):
         return TenantRootPrivilege.select_by_user(request.user)
 
+class TenantPrivilegeInline(XOSTabularInline):
+    model = TenantPrivilege
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-tenantprivileges'
+    fields = ['backend_status_icon', 'user', 'role', 'tenant']
+    readonly_fields = ('backend_status_icon', )
+
+    def queryset(self, request):
+        return TenantPrivilege.select_by_user(request.user)
+
 class TenantRootAdmin(XOSBaseAdmin):
     model = TenantRoot
     list_display = ('backend_status_icon', 'name', 'kind')
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index 2ee6b94..f203ba3 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -1,7 +1,7 @@
 from .plcorebase import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager,PlModelMixIn
 from .project import Project
 from .singletonmodel import SingletonModel
-from .service import Service, Tenant, TenantWithContainer, CoarseTenant, ServicePrivilege, TenantRoot, TenantRootPrivilege, TenantRootRole, Subscriber, Provider
+from .service import Service, Tenant, TenantWithContainer, CoarseTenant, ServicePrivilege, TenantRoot, TenantRootPrivilege, TenantRootRole, TenantPrivilege, TenantRole, Subscriber, Provider
 from .service import ServiceAttribute, TenantAttribute, ServiceRole
 from .tag import Tag
 from .role import Role
@@ -29,4 +29,3 @@
 from .network import Network, NetworkParameterType, NetworkParameter, Port, NetworkTemplate, Router, NetworkSlice, ControllerNetwork, AddressPool
 from .billing import Account, Invoice, Charge, UsableObject, Payment
 from .program import Program
-
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index ee28cf6..97fa890 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -644,14 +644,14 @@
         for slice in slices:
             if slice.instances.all().count() > 0:
                 for instance in slice.instances.all():
-                     #Pick the first instance that has lesser than 5 tenants 
+                     #Pick the first instance that has lesser than 5 tenants
                      if self.count_of_tenants_of_an_instance(instance) < 5:
                          return instance
         return None
 
-    #TODO: Ideally the tenant count for an instance should be maintained using a 
-    #many-to-one relationship attribute, however this model being proxy, it does 
-    #not permit any new attributes to be defined. Find if any better solutions 
+    #TODO: Ideally the tenant count for an instance should be maintained using a
+    #many-to-one relationship attribute, however this model being proxy, it does
+    #not permit any new attributes to be defined. Find if any better solutions
     def count_of_tenants_of_an_instance(self, instance):
         tenant_count = 0
         for tenant in self.get_tenant_objects().all():
@@ -805,4 +805,41 @@
 
             return cls.objects.filter(id__in=trp_ids)
 
+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):
+    user = models.ForeignKey('User', related_name="tenant_privileges")
+    tenant = models.ForeignKey('Tenant', related_name="tenant_privileges")
+    role = models.ForeignKey('TenantRole', related_name="tenant_privileges")
+
+    class Meta:
+        unique_together = ('user', 'tenant', 'role')
+
+    def __unicode__(self):  return u'%s %s %s' % (self.tenant, self.user, self.role)
+
+    def save(self, *args, **kwds):
+        if not self.user.is_active:
+            raise PermissionDenied, "Cannot modify role(s) of a disabled user"
+        super(TenantPrivilege, self).save(*args, **kwds)
+
+    def can_update(self, user):
+        return user.can_update_tenant_privilege(self)
+
+    @classmethod
+    def select_by_user(cls, user):
+        if user.is_admin:
+            return cls.objects.all()
+        else:
+            # User can see his own privilege
+            trp_ids = [trp.id for trp in cls.objects.filter(user=user)]
+
+            # A tenant admin can see the TenantPrivileges for their Tenants
+            for priv in cls.objects.filter(user=user, role__role="admin"):
+                trp_ids.extend( [trp.id for trp in cls.objects.filter(tenant=priv.tenant)] )
+
+            return cls.objects.filter(id__in=trp_ids)
diff --git a/xos/core/models/user.py b/xos/core/models/user.py
index 0b8e3af..a8ed571 100644
--- a/xos/core/models/user.py
+++ b/xos/core/models/user.py
@@ -165,7 +165,7 @@
             purge = purge or observer_disabled
         except NameError:
             pass
-            
+
         if (purge):
             super(User, self).delete(*args, **kwds)
         else:
@@ -219,7 +219,7 @@
 #            roles[site_privilege.role.role_type].append(site_privilege.site.login_base)
 #        for slice_membership in slice_memberships:
 #            roles[slice_membership.role.role_type].append(slice_membership.slice.name)
-#        return roles   
+#        return roles
 
     def save(self, *args, **kwds):
         if not self.id:
@@ -254,7 +254,7 @@
         site_privs = SitePrivilege.objects.filter(user=user, site=self.site)
         for site_priv in site_privs:
             if site_priv.role.role == 'admin':
-                return True 
+                return True
             if site_priv.role.role == 'pi':
                 for fieldName in self.diff.keys():
                     if fieldName in self.PI_FORBIDDEN_FIELDS:
@@ -272,26 +272,26 @@
 
     def can_update_root(self):
         """
-        Return True if user has root (global) write access. 
+        Return True if user has root (global) write access.
         """
         if self.is_readonly:
             return False
         if self.is_admin:
             return True
 
-        return False 
+        return False
 
     def can_update_deployment(self, deployment):
         from core.models.site import DeploymentPrivilege
         if self.can_update_root():
-            return True    
-                          
+            return True
+
         if DeploymentPrivilege.objects.filter(
             deployment=deployment,
             user=self,
             role__role__in=['admin', 'Admin']):
             return True
-        return False    
+        return False
 
     def can_update_site(self, site, allow=[]):
         from core.models.site import SitePrivilege
@@ -301,7 +301,7 @@
             site=site, user=self, role__role__in=['admin', 'Admin']+allow):
             return True
         return False
-    
+
     def can_update_slice(self, slice):
         from core.models.slice import SlicePrivilege
         if self.can_update_root():
@@ -310,7 +310,7 @@
             return True
         if self.can_update_site(slice.site, allow=['pi']):
             return True
-                     
+
         if SlicePrivilege.objects.filter(
             slice=slice, user=self, role__role__in=['admin', 'Admin']):
             return True
@@ -334,9 +334,21 @@
             return True
         return False
 
+    def can_update_tenant(self, tenant, allow=[]):
+        from core.models.service import Tenant, TenantPrivilege
+        if self.can_update_root():
+            return True
+        if TenantPrivilege.objects.filter(
+            tenant=tenant, user=self, role__role__in=['admin', 'Admin']+allow):
+            return True
+        return False
+
     def can_update_tenant_root_privilege(self, tenant_root_privilege, allow=[]):
         return self.can_update_tenant_root(tenant_root_privilege.tenant_root, allow)
 
+    def can_update_tenant_privilege(self, tenant_privilege, allow=[]):
+        return self.can_update_tenant(tenant_privilege.tenant, allow)
+
     def get_readable_objects(self, filter_by=None):
        """ Returns a list of objects that the user is allowed to read. """
        from core.models import Deployment, Flavor, Image, Network, NetworkTemplate, Node, PlModelMixIn, Site, Slice, SliceTag, Instance, Tag, User, DeploymentPrivilege, SitePrivilege, SlicePrivilege
@@ -351,18 +363,18 @@
        return readable_objects
 
     def get_permissions(self, filter_by=None):
-        """ Return a list of objects for which the user has read or read/write 
-        access. The object will be an instance of a django model object. 
+        """ Return a list of objects for which the user has read or read/write
+        access. The object will be an instance of a django model object.
         Permissions will be either 'r' or 'rw'.
-         
+
         e.g.
         [{'object': django_object_instance, 'permissions': 'rw'}, ...]
 
         Returns:
-          list of dicts  
-       
+          list of dicts
+
         """
-        from core.models import Deployment, Flavor, Image, Network, NetworkTemplate, Node, PlModelMixIn, Site, Slice, SliceTag, Instance, Tag, User, DeploymentPrivilege, SitePrivilege, SlicePrivilege   
+        from core.models import Deployment, Flavor, Image, Network, NetworkTemplate, Node, PlModelMixIn, Site, Slice, SliceTag, Instance, Tag, User, DeploymentPrivilege, SitePrivilege, SlicePrivilege
         READ = 'r'
         READWRITE = 'rw'
         models = []
@@ -371,8 +383,8 @@
 
         deployment_priv_objs = [Image, NetworkTemplate, Flavor]
         site_priv_objs = [Node, Slice, User]
-        slice_priv_objs = [Instance, Network] 
-        
+        slice_priv_objs = [Instance, Network]
+
         # maps the set of objects a paticular role has write access
         write_map = {
             DeploymentPrivilege : {
@@ -382,12 +394,12 @@
                 'admin' : site_priv_objs,
                 'pi' : [Slice, User],
                 'tech': [Node],
-            },     
+            },
             SlicePrivilege : {
-                'admin': slice_priv_objs, 
-            }, 
+                'admin': slice_priv_objs,
+            },
         }
-            
+
         privilege_map = {
             DeploymentPrivilege : (Deployment, deployment_priv_objs),
             SitePrivilege : (Site, site_priv_objs),
@@ -399,7 +411,7 @@
             if models and model not in models:
                 continue
 
-            # get the objects affected by this privilege model   
+            # get the objects affected by this privilege model
             affected_objects = []
             for affected_model in affected_models:
                 affected_objects.extend(affected_model.select_by_user(self))
@@ -410,7 +422,7 @@
                     permissions.append(permission_dict(affected_object, READWRITE))
             else:
                 # create a dict of the user's per object privileges
-                # ex:  {princeton_tmack : ['admin']  
+                # ex:  {princeton_tmack : ['admin']
                 privileges = privilege_model.objects.filter(user=self)
                 for privilege in privileges:
                     object_roles = defaultdict(list)
@@ -421,7 +433,7 @@
                             obj = getattr(privilege, field)
                     if obj:
                         object_roles[obj].append(privilege.role.role)
-                        
+
                 # loop through all objects the user has access to and determine
                 # if they also have write access
                 for affected_object in affected_objects:
@@ -438,15 +450,15 @@
                             permissions.append(permission_dict(affected_object, WRITE))
                         else:
                             permissions.append(permission_dict(affected_object, READ))
-                                
-        return permissions                          
-                     
+
+        return permissions
+
 
     def get_tenant_permissions(self):
         from core.models import Site, Slice
         return self.get_object_permissions(filter_by=[Site,Slice])
 
-    
+
     @staticmethod
     def select_by_user(user):
         if user.is_admin:
diff --git a/xos/core/xoslib/methods/vpnview.py b/xos/core/xoslib/methods/vpnview.py
index 7a101f5..0e2c376 100644
--- a/xos/core/xoslib/methods/vpnview.py
+++ b/xos/core/xoslib/methods/vpnview.py
@@ -62,6 +62,7 @@
 
     def get_queryset(self):
         queryset = VPNTenant.get_tenant_objects().all()
+        queryset = [ tenant for tenant in queryset if self.request.user.can_update_tenant(tenant, ['access','Access'])]
         for tenant in queryset:
             tenant.script_text = tenant.create_client_script()
         return queryset
diff --git a/xos/services/vpn/admin.py b/xos/services/vpn/admin.py
index 35e1174..0e2362f 100644
--- a/xos/services/vpn/admin.py
+++ b/xos/services/vpn/admin.py
@@ -1,6 +1,6 @@
 import time
 
-from core.admin import ReadOnlyAwareAdmin, SliceInline
+from core.admin import ReadOnlyAwareAdmin, SliceInline, TenantPrivilegeInline
 from core.middleware import get_request
 from core.models import User
 from django import forms
@@ -126,12 +126,39 @@
                          'classes': ['suit-tab suit-tab-general']})]
     readonly_fields = ('backend_status_text', 'instance')
     form = VPNTenantForm
+    inlines = [TenantPrivilegeInline]
 
-    suit_form_tabs = (('general', 'Details'),)
+    suit_form_tabs = (('general', 'Details'),
+        ('tenantprivileges','Privileges')
+    )
 
     def queryset(self, request):
         return VPNTenant.get_tenant_objects_by_user(request.user)
 
+    def certificate_name(self, tenant_privilege):
+        return str(tenant_privilege.user.email) + "-" + str(tenant_privilege.tenant.id)
+
+    def save_formset(self, request, form, formset, change):
+        super(VPNTenantAdmin, self).save_formset(request, form, formset, change)
+        for obj in formset.deleted_objects:
+            # If anything deleated was a TenantPrivilege then revoke the certificate
+            if type(obj) is TenantPrivilege:
+                certificate = self.certificate_name(obj)
+                # revoke the cert
+                pass
+            # TODO(jermowery): determine if this is necessary.
+            # if type(obj) is VPNTenant:
+                # if the tenant was deleted revoke all certs assoicated
+                # pass
+
+        for obj in formset.new_objects:
+            # If there were any new TenantPrivlege objects then create certs
+            if type(obj) is TenantPrivilege:
+                certificate = self.certificate_name(obj)
+                # create the cert
+                pass
+
+
 # Associate the admin forms with the models.
 admin.site.register(VPNService, VPNServiceAdmin)
 admin.site.register(VPNTenant, VPNTenantAdmin)