CORD-1570: Re-implementation of XOS Security via xproto at the API boundary

Change-Id: I9cb6380b0798a5f4af2f0459c5decd0b9edbb317
diff --git a/lib/xos-genx/xosgenx/jinja2_extensions/fol2.py b/lib/xos-genx/xosgenx/jinja2_extensions/fol2.py
index 0c8513a..4552d59 100644
--- a/lib/xos-genx/xosgenx/jinja2_extensions/fol2.py
+++ b/lib/xos-genx/xosgenx/jinja2_extensions/fol2.py
@@ -422,7 +422,7 @@
         if not tag:
             tag = gen_random_string()
 
-        policy_function_name_template = 'policy_%s_' + '%(random_string)s' % {'random_string': tag}
+        policy_function_name_template = '%s_' + '%(random_string)s' % {'random_string': tag}
         policy_function_name = policy_function_name_template % policy_name
 
         self.verdict_next()
@@ -636,7 +636,7 @@
     if fol_reduced in ['True','False'] and fol != fol_reduced:
         raise TrivialPolicy("Policy %(name)s trivially reduces to %(reduced)s. If this is what you want, replace its contents with %(reduced)s"%{'name':policy, 'reduced':fol_reduced})
 
-    a = f2p.gen_test_function(fol_reduced, policy, tag='enforcer')
+    a = f2p.gen_test_function(fol_reduced, policy, tag='security_check')
 
     return astunparse.unparse(a)
 
diff --git a/lib/xos-genx/xosgenx/targets/django-security.xtarget b/lib/xos-genx/xosgenx/targets/django-security.xtarget
new file mode 100644
index 0000000..d970cea
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/django-security.xtarget
@@ -0,0 +1,9 @@
+from privilege import Privilege
+from django.db.models import Q
+
+{% for m in proto.messages %}
+{% if m.policy %}
+{{ xproto_fol_to_python_test(m.policy, proto.policies[m.policy], m) }}
+{% endif %}
+
+{% endfor %}
diff --git a/lib/xos-genx/xosgenx/targets/django-split.xtarget b/lib/xos-genx/xosgenx/targets/django-split.xtarget
index 5260e85..60cf9de 100644
--- a/lib/xos-genx/xosgenx/targets/django-split.xtarget
+++ b/lib/xos-genx/xosgenx/targets/django-split.xtarget
@@ -4,13 +4,17 @@
 {%- for l in m.links %}
 
 {% if l.peer.name != m.name %}
-from core.models.{{ l.peer.name | lower }} import {{ l.peer.name }} 
+from {{ l.peer.name | lower }} import {{ l.peer.name }} 
 {% endif %}
 
 {%- endfor %}
+{% if m.name!='XOSBase' and 'Mixin' not in m.name %}
+import security
+from privilege import Privilege
+{% endif %}
 {% for b in m.bases %}
 {% if b.name!='XOSBase' and 'Mixin' not in b.name %}
-from core.models.{{b.name | lower}} import {{ b.name }}
+from {{b.name | lower}} import {{ b.name }}
 {% endif %}
 {% endfor %}
 
@@ -40,9 +44,9 @@
       unique_together = {{ xproto_tuplify(uniques) }}
   {%- endif %}
   {% if file_exists(m.name|lower + '_model.py') -%}{{ include_file(m.name|lower + '_model.py') | indent(width=2)}}{%- endif %}
+  pass
 
   {% if m.name!='XOSBase' and 'Mixin' not in m.name %}
-
   # Generated methods
   def save(self, *args, **kwds):
       if not self.leaf_model_name:
@@ -58,7 +62,17 @@
       {% endfor %}
       super({{ m.name }}, self).save(*args, **kwds)
 
+  def can_access(self, ctx):
+      {% if m.policy %}
+      verdict = security.{{m.policy}}_security_check(self, ctx)
+      return verdict,"{{ m.policy }}"
+      {% else %}
+      verdict = XOS_GLOBAL_DEFAULT_SECURITY_POLICY
+      return verdict,"xos_default_policy"
+      {% endif %}
+      
   {% endif %}
+    
 {% if file_exists(xproto_base_name(m.name)|lower+'_bottom.py') -%}{{ include_file(xproto_base_name(m.name)|lower+'_bottom.py') }}{% endif %}
 +++ {{m.name|lower}}.py
 {% endif %}{% endfor %}
diff --git a/lib/xos-genx/xosgenx/targets/django.xtarget b/lib/xos-genx/xosgenx/targets/django.xtarget
index e9a56ec..1c8ce93 100644
--- a/lib/xos-genx/xosgenx/targets/django.xtarget
+++ b/lib/xos-genx/xosgenx/targets/django.xtarget
@@ -4,13 +4,19 @@
 {%- for l in m.links %}
 
 {% if l.peer.name != m.name %}
-from core.models.{{ l.peer.name | lower }} import {{ l.peer.name }} 
+from {{ l.peer.name | lower }} import {{ l.peer.name }} 
 {% endif %}
 
 {%- endfor %}
+{% if m.name!='XOSBase' and 'Mixin' not in m.name %}
+import security
+{% if m.name!='Privilege' %}
+from privilege import Privilege
+{% endif %}
+{% endif %}
 {% for b in m.bases %}
 {% if b.name!='XOSBase' and 'Mixin' not in b.name %}
-from core.models.{{b.name | lower}} import {{ b.name }}
+from {{b.name | lower}} import {{ b.name }}
 {% endif %}
 {% endfor %}
 
@@ -57,6 +63,16 @@
       policy_{{policy}}_validator(self, None)
       {% endfor %}
       super({{ m.name }}, self).save(*args, **kwds)
+
+  def can_access(self, ctx):
+      {% if m.policy %}
+      verdict = security.{{m.policy}}_security_check(self, ctx)
+      return verdict,"{{ m.policy }}"
+      {% else %}
+      verdict = XOS_GLOBAL_DEFAULT_SECURITY_POLICY
+      return verdict,"xos_default_policy"
+      {% endif %}
+      
   {% endif %}
     
 {% if file_exists(xproto_base_name(m.name)|lower+'_bottom.py') -%}{{ include_file(xproto_base_name(m.name)|lower+'_bottom.py') }}{% endif %}
diff --git a/lib/xos-genx/xosgenx/targets/grpc_api.xtarget b/lib/xos-genx/xosgenx/targets/grpc_api.xtarget
index a0373a3..4d05870 100644
--- a/lib/xos-genx/xosgenx/targets/grpc_api.xtarget
+++ b/lib/xos-genx/xosgenx/targets/grpc_api.xtarget
@@ -21,19 +21,19 @@
     def List{{ object.name }}(self, request, context):
       user=self.authenticate(context)
       model=self.get_model("{{ object.name }}")
-      return self.querysetToProto(model, model.objects.all())
+      return self.list(model, user)
 
     @translate_exceptions
     def Filter{{ object.name }}(self, request, context):
       user=self.authenticate(context)
       model=self.get_model("{{ object.name }}")
-      return self.filter(model, request)
+      return self.filter(model, user, request)
 
     @translate_exceptions
     def Get{{ object.name }}(self, request, context):
       user=self.authenticate(context)
       model=self.get_model("{{ object.name }}")
-      return self.get(model, request.id)
+      return self.get(model, user, request.id)
 
     @translate_exceptions
     def Create{{ object.name }}(self, request, context):
diff --git a/lib/xos-genx/xosgenx/targets/service.xtarget b/lib/xos-genx/xosgenx/targets/service.xtarget
index 5336f82..bf14b83 100644
--- a/lib/xos-genx/xosgenx/targets/service.xtarget
+++ b/lib/xos-genx/xosgenx/targets/service.xtarget
@@ -73,6 +73,15 @@
       policy_{{policy}}_validator(self, None)
       {% endfor %}
       super({{ m.name }}{{ legacy_tag }}, self).save(*args, **kwds)
+  
+  def can_access(self, ctx):
+      {% if m.policy %}
+      verdict = security.{{m.policy}}_security_check(self, ctx)
+      return verdict,"{{ m.policy }}"
+      {% else %}
+      verdict = True
+      return verdict,"xos_default_policy"
+      {% endif %}
 
 {% if file_exists(m.name|lower+'_bottom.py') -%}{{ include_file(m.name|lower+'_bottom.py') }}{% endif %} 
 {% endfor %}
diff --git a/xos/api/xosapi_helpers.py b/xos/api/xosapi_helpers.py
index 90163e6..ae27d82 100644
--- a/xos/api/xosapi_helpers.py
+++ b/xos/api/xosapi_helpers.py
@@ -65,8 +65,6 @@
             user = self.context['request'].user
             if user.__class__.__name__=="AnonymousUser":
                 raise XOSPermissionDenied()
-            if not instance.can_update(user):
-                raise XOSPermissionDenied()
 
         for k in validated_data:
             if k in property_fields:
@@ -142,8 +140,6 @@
                 user = self.request.user
                 if user.__class__.__name__=="AnonymousUser":
                     raise XOSPermissionDenied()
-                if not obj.can_update(user):
-                    raise XOSPermissionDenied()
 
         return obj
 
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 152ef1c..72d56dc 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -144,16 +144,16 @@
 
         obj.caller = request.user
         # update openstack connection to use this site/tenant
-        obj.save_by_user(request.user)
+        obj.save()
 
     def delete_model(self, request, obj):
-        obj.delete_by_user(request.user)
+        obj.delete()
 
     def save_formset(self, request, form, formset, change):
         instances = formset.save(commit=False)
         for instance in instances:
             instance.caller = request.user
-            instance.save_by_user(request.user)
+            instance.save()
 
         # BUG in django 1.7? Objects are not deleted by formset.save if
         # commit is False. So let's delete them ourselves.
@@ -830,10 +830,10 @@
 
     def save_model(self, request, obj, form, change):
             # update openstack connection to use this site/tenant
-        obj.save_by_user(request.user)
+        obj.save()
 
     def delete_model(self, request, obj):
-        obj.delete_by_user(request.user)
+        obj.delete()
 
     def queryset(self, request):
         return Controller.select_by_user(request.user)
@@ -1047,10 +1047,10 @@
 
     def save_model(self, request, obj, form, change):
         # update openstack connection to use this site/tenant
-        obj.save_by_user(request.user)
+        obj.save()
 
     def delete_model(self, request, obj):
-        obj.delete_by_user(request.user)
+        obj.delete()
 
 
 class SitePrivilegeAdmin(XOSBaseAdmin):
diff --git a/xos/core/models/attic/controller_model.py b/xos/core/models/attic/controller_model.py
index 9e76572..8b13789 100644
--- a/xos/core/models/attic/controller_model.py
+++ b/xos/core/models/attic/controller_model.py
@@ -1,9 +1 @@
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = Controller.objects.all()
-    else:
-        from core.models.privilege import Privilege
-        deployments = [dp.deployment for dp in Privilege.objects.filter(accessor_id=user_id, accessor_type='User', permission__in=['role:Admin', 'role:admin'])]
-        qs = Controller.objects.filter(deployment__in=deployments)
-    return qs
+
diff --git a/xos/core/models/attic/controllernetwork_model.py b/xos/core/models/attic/controllernetwork_model.py
index 43bec3d..5879050 100644
--- a/xos/core/models/attic/controllernetwork_model.py
+++ b/xos/core/models/attic/controllernetwork_model.py
@@ -6,15 +6,3 @@
     except:
         pass
     return d
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = ControllerNetwork.objects.all()
-    else:
-        from core.models.slice import Slice
-        slices = Slice.select_by_user(user)
-        networks = Network.objects.filter(owner__in=slices)
-        qs = ControllerNetwork.objects.filter(network__in=networks)
-    return qs
-
diff --git a/xos/core/models/attic/controllersiteprivilege_model.py b/xos/core/models/attic/controllersiteprivilege_model.py
deleted file mode 100644
index 11ba22d..0000000
--- a/xos/core/models/attic/controllersiteprivilege_model.py
+++ /dev/null
@@ -1,20 +0,0 @@
-def can_update(self, user):
-    if user.is_readonly:
-        return False
-    if user.is_admin:
-        return True
-
-    cprivs = ControllerPrivilege.objects.filter(privilege__accessor_id=user.id, privilege__object_type='Site')
-    for cpriv in cprivs:
-        if cpriv.privilege.permission in ['role:admin', 'role:Admin']:
-            return True
-    return False
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = ControllerPrivilege.objects.filter(privilege__object_type='Site')
-    else:
-        cpriv_ids = [cp.id for cp in ControllerPrivilege.objects.filter(privilege__accessor_id=user.id, privilege__object_type='Site')]
-        qs = ControllerPrivilege.objects.filter(id__in=cpriv_ids, privilege__object_type='Site')
-    return qs
diff --git a/xos/core/models/attic/controllerslice_model.py b/xos/core/models/attic/controllerslice_model.py
index d8f4b4d..9e4a3d0 100644
--- a/xos/core/models/attic/controllerslice_model.py
+++ b/xos/core/models/attic/controllerslice_model.py
@@ -6,13 +6,3 @@
     except:
         pass
     return d
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = ControllerSlice.objects.all()
-    else:
-        slices = Slice.select_by_user(user)
-        qs = ControllerSlice.objects.filter(slice__in=slices)
-    return qs    
-
diff --git a/xos/core/models/attic/controllersliceprivilege_model.py b/xos/core/models/attic/controllersliceprivilege_model.py
deleted file mode 100644
index ab64569..0000000
--- a/xos/core/models/attic/controllersliceprivilege_model.py
+++ /dev/null
@@ -1,19 +0,0 @@
-def can_update(self, user):
-    if user.is_readonly:
-        return False
-    if user.is_admin:
-        return True
-    cprivs = ControllerPrivilege.objects.filter(privilege__accessor_id=user.id, privilege__object_type='Slice')
-    for cpriv in cprivs:
-        if cpriv.privilege.permission in ['role:admin', 'role:Admin']:
-            return True
-    return False
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = ControllerPrivilege.objects.filter(privilege__object_type='Slice')
-    else:
-        cpriv_ids = [cp.id for cp in ControllerPrivilege.objects.filter(privilege__accessor_id=user.id, privilege__object_type='Slice')]
-        qs = ControllerPrivilege.objects.filter(id__in=cpriv_ids, privilege__object_type='Slice')
-    return qs
diff --git a/xos/core/models/attic/controlleruser_model.py b/xos/core/models/attic/controlleruser_model.py
deleted file mode 100644
index ae2f3ac..0000000
--- a/xos/core/models/attic/controlleruser_model.py
+++ /dev/null
@@ -1,12 +0,0 @@
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = ControllerUser.objects.all()
-    else:
-        users = User.select_by_user(user)
-        qs = ControllerUser.objects.filter(user__in=users)
-    return qs
-
-def can_update(self, user):
-    return user.can_update_root()    
-
diff --git a/xos/core/models/attic/deployment_model.py b/xos/core/models/attic/deployment_model.py
index d9da903..0448f02 100644
--- a/xos/core/models/attic/deployment_model.py
+++ b/xos/core/models/attic/deployment_model.py
@@ -29,7 +29,3 @@
             ids.append(deployment.id)
 
     return Deployment.objects.filter(id__in=ids)
-
-def can_update(self, user):
-    return user.can_update_deployment(self)
-
diff --git a/xos/core/models/attic/deploymentprivilege_model.py b/xos/core/models/attic/deploymentprivilege_model.py
deleted file mode 100644
index 8fc40d4..0000000
--- a/xos/core/models/attic/deploymentprivilege_model.py
+++ /dev/null
@@ -1,12 +0,0 @@
-def can_update(self, user):
-    return user.can_update_deployment(self)
-
-@staticmethod
-def select_by_user(user):
-    from core.models.deploymentprivilege import DeploymentPrivilege
-    if user.is_admin:
-        qs = DeploymentPrivilege.objects.all()
-    else:
-        dpriv_ids = [dp.id for dp in DeploymentPrivilege.objects.filter(user=user)]
-        qs = DeploymentPrivilege.objects.filter(id__in=dpriv_ids)
-    return qs
diff --git a/xos/core/models/attic/header.py b/xos/core/models/attic/header.py
index a03046a..b6b8d31 100644
--- a/xos/core/models/attic/header.py
+++ b/xos/core/models/attic/header.py
@@ -5,7 +5,6 @@
 import operator
 from operator import attrgetter
 from core.models.xosbase import *
-from core.models.privilege import *
 from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
 from django.contrib.contenttypes.models import ContentType
 from django.utils.timezone import now
diff --git a/xos/core/models/attic/imagedeployments_model.py b/xos/core/models/attic/imagedeployments_model.py
deleted file mode 100644
index ad51017..0000000
--- a/xos/core/models/attic/imagedeployments_model.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def can_update(self, user):
-    return user.can_update_deployment(self.deployment)
diff --git a/xos/core/models/attic/instance_model.py b/xos/core/models/attic/instance_model.py
index 3ea46fb..2ce52f7 100644
--- a/xos/core/models/attic/instance_model.py
+++ b/xos/core/models/attic/instance_model.py
@@ -16,9 +16,6 @@
     if not self.creator and hasattr(self, 'caller'):
         self.creator = self.caller
 
-def can_update(self, user):
-    return user.can_update_slice(self.slice)
-
 def all_ips(self):
     ips={}
     for ns in self.ports.all():
@@ -63,15 +60,6 @@
     # if all else fails, look for nat-net (for OpenCloud?)
     return self.get_network_ip("nat")
 
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = Instance.objects.all()
-    else:
-        slices = Slice.select_by_user(user)
-        qs = Instance.objects.filter(slice__in=slices)
-    return qs
-
 def get_ssh_command(self):
     if (not self.instance_id) or (not self.node) or (not self.instance_name):
         return None
diff --git a/xos/core/models/attic/network_model.py b/xos/core/models/attic/network_model.py
deleted file mode 100644
index c58639a..0000000
--- a/xos/core/models/attic/network_model.py
+++ /dev/null
@@ -1,14 +0,0 @@
-def can_update(self, user):
-    return user.can_update_slice(self.owner)
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = Network.objects.all()
-    else:
-        slices = Slice.select_by_user(user)
-        #slice_ids = [s.id for s in Slice.select_by_user(user)]
-        qs = Network.objects.filter(owner__in=slices)
-    return qs
-
-
diff --git a/xos/core/models/attic/networkslice_model.py b/xos/core/models/attic/networkslice_model.py
deleted file mode 100644
index 1f33390..0000000
--- a/xos/core/models/attic/networkslice_model.py
+++ /dev/null
@@ -1,13 +0,0 @@
-def can_update(self, user):
-    return user.can_update_slice(self.slice)
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = NetworkSlice.objects.all()
-    else:
-        slice_ids = [s.id for s in Slice.select_by_user(user)]
-        network_ids = [network.id for network in Network.select_by_user(user)]
-        qs = NetworkSlice.objects.filter(Q(slice__in=slice_ids) | Q(network__in=network_ids))
-    return qs
-
diff --git a/xos/core/models/attic/node_model.py b/xos/core/models/attic/node_model.py
deleted file mode 100644
index 763f480..0000000
--- a/xos/core/models/attic/node_model.py
+++ /dev/null
@@ -1,3 +0,0 @@
-def can_update(self, user):
-    return user.can_update_site(self.site_deployment.site, allow=['tech'])
-
diff --git a/xos/core/models/attic/port_model.py b/xos/core/models/attic/port_model.py
deleted file mode 100644
index 57617c6..0000000
--- a/xos/core/models/attic/port_model.py
+++ /dev/null
@@ -1,19 +0,0 @@
-def can_update(self, user):
-    if self.instance:
-        return user.can_update_slice(self.instance.slice)
-    if self.network:
-        return user.can_update_slice(self.network.owner)
-    return False
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = Port.objects.all()
-    else:
-        instances = Instance.select_by_user(user)
-        instance_ids = [instance.id for instance in instances]
-        networks = Network.select_by_user(user)
-        network_ids = [network.id for network in networks]
-        qs = Port.objects.filter(Q(instance__in=instance_ids) | Q(network__in=network_ids))
-    return qs
-
diff --git a/xos/core/models/attic/service_model.py b/xos/core/models/attic/service_model.py
index 9cb2d4d..5b305cd 100644
--- a/xos/core/models/attic/service_model.py
+++ b/xos/core/models/attic/service_model.py
@@ -5,16 +5,6 @@
     self._meta.get_field("kind").default = self.KIND
     super(Service, self).__init__(*args, **kwargs)
 
-@classmethod
-def select_by_user(cls, user):
-    if user.is_admin:
-        return cls.objects.all()
-    else:
-        from core.models.privilege import Privilege
-        service_ids = [
-            sp.object_id for sp in Privilege.objects.filter(accessor_id=user.id, accessor_type='User', object_type='Service')]
-        return cls.objects.filter(id__in=service_ids)
-
 @property
 def serviceattribute_dict(self):
     attrs = {}
@@ -22,9 +12,6 @@
         attrs[attr.name] = attr.value
     return attrs
 
-def can_update(self, user):
-    return user.can_update_service(self, allow=['admin'])
-
 def get_scalable_nodes(self, slice, max_per_node=None, exclusive_slices=[]):
     """
          Get a list of nodes that can be used to scale up a slice.
diff --git a/xos/core/models/attic/serviceprivilege_model.py b/xos/core/models/attic/serviceprivilege_model.py
deleted file mode 100644
index bac83cc..0000000
--- a/xos/core/models/attic/serviceprivilege_model.py
+++ /dev/null
@@ -1,7 +0,0 @@
-@classmethod
-def select_by_user(cls, user):
-    if user.is_admin:
-        qs = cls.objects.all()
-    else:
-        qs = cls.objects.filter(user=user)
-    return qs
diff --git a/xos/core/models/attic/site_model.py b/xos/core/models/attic/site_model.py
deleted file mode 100644
index f16b1a8..0000000
--- a/xos/core/models/attic/site_model.py
+++ /dev/null
@@ -1,4 +0,0 @@
-def can_update(self, user):
-    return user.can_update_site(self, allow=['pi'])
-
-
diff --git a/xos/core/models/attic/siteprivilege_model.py b/xos/core/models/attic/siteprivilege_model.py
deleted file mode 100644
index 0a05b5f..0000000
--- a/xos/core/models/attic/siteprivilege_model.py
+++ /dev/null
@@ -1,12 +0,0 @@
-def can_update(self, user):
-    return user.can_update_site(self, allow=['pi'])
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = SitePrivilege.objects.all()
-    else:
-        sp_ids = [sp.id for sp in SitePrivilege.objects.filter(user=user)]
-        qs = SitePrivilege.objects.filter(id__in=sp_ids)
-    return qs
-
diff --git a/xos/core/models/attic/slice_model.py b/xos/core/models/attic/slice_model.py
index 37ec321..eaa5b9d 100644
--- a/xos/core/models/attic/slice_model.py
+++ b/xos/core/models/attic/slice_model.py
@@ -26,26 +26,3 @@
         # "Private Only" was the default from the old Tenant View
         self.network=None
     self.enforce_choices(self.network, self.NETWORK_CHOICES)
-
-def can_update(self, user):
-    return user.can_update_slice(self)
-
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = Slice.objects.all()
-    else:
-        from core.models.privilege import Privilege 
-        # users can see slices they belong to 
-        slice_ids = [sp.object_id for sp in Privilege.objects.filter(accessor_id=user.id, accessor_type='User', object_type='Slice')]
-        # pis and admins can see slices at their sites
-        site_ids = [sp.object_id for sp in Privilege.objects.filter(accessor_id=user.id, accessor_type='User', object_type='Site')\
-                    if (sp.permission in ['role:pi', 'role:admin'])]
-        sites = [Site.objects.get(pk = site_id) for site_id in site_ids]
-
-        slice_ids.extend([s.id for s in Slice.objects.filter(site__in=sites)])
-        qs = Slice.objects.filter(id__in=slice_ids)
-    return qs
-
-
diff --git a/xos/core/models/attic/sliceprivilege_model.py b/xos/core/models/attic/sliceprivilege_model.py
deleted file mode 100644
index e2599a3..0000000
--- a/xos/core/models/attic/sliceprivilege_model.py
+++ /dev/null
@@ -1,23 +0,0 @@
-def can_update(self, user):
-    return user.can_update_slice(self.slice)
-
-@staticmethod
-def select_by_user(user):
-    if user.is_admin:
-        qs = SlicePrivilege.objects.all()
-    else:
-        # You can see your own SlicePrivileges
-        sp_ids = [sp.id for sp in SlicePrivilege.objects.filter(user=user)]
-
-        from core.models.siteprivilege import SitePrivilege
-        # A site pi or site admin can see the SlicePrivileges for all slices in his Site
-        for priv in SitePrivilege.objects.filter(user=user):
-            if priv.role.role in ['pi', 'admin']:
-                sp_ids.extend( [sp.id for sp in SlicePrivilege.objects.filter(slice__site = priv.site)] )
-
-        # A slice admin can see the SlicePrivileges for his Slice
-        for priv in SlicePrivilege.objects.filter(user=user, role__role="admin"):
-            sp_ids.extend( [sp.id for sp in SlicePrivilege.objects.filter(slice=priv.slice)] )
-
-        qs = SlicePrivilege.objects.filter(id__in=sp_ids)
-    return qs
diff --git a/xos/core/models/attic/tag_model.py b/xos/core/models/attic/tag_model.py
deleted file mode 100644
index 364baa2..0000000
--- a/xos/core/models/attic/tag_model.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def can_update(self, user):
-    return user.can_update_root()
-
-@staticmethod
-def select_by_user(user):
-    return Tag.objects.all()
diff --git a/xos/core/models/attic/tenantroot_model.py b/xos/core/models/attic/tenantroot_model.py
index 86208cd..a31a3cc 100644
--- a/xos/core/models/attic/tenantroot_model.py
+++ b/xos/core/models/attic/tenantroot_model.py
@@ -5,9 +5,6 @@
     self._meta.get_field("kind").default = self.KIND
     super(TenantRoot, self).__init__(*args, **kwargs)
 
-def can_update(self, user):
-    return user.can_update_tenant_root(self, allow=['admin'])
-
 def get_subscribed_tenants(self, tenant_class):
     ids = self.subscribed_tenants.filter(kind=tenant_class.KIND)
     return tenant_class.objects.filter(id__in=ids)
@@ -18,16 +15,6 @@
         return None
     return sorted(st, key=attrgetter('id'))[0]
 
-@classmethod
-def select_by_user(cls, user):
-    if user.is_admin:
-        return cls.objects.all()
-    else:
-        from core.models.privilege import Privilege
-        tr_ids = [
-            trp.object_id for trp in Privilege.objects.filter(accessor_id=user.id, accessor_type='User', object_type='TenantRoot')]
-        return cls.objects.filter(id__in=tr_ids)
-
 # helper function to be used in subclasses that want to ensure
 # service_specific_id is unique
 def validate_unique_service_specific_id(self, none_okay=False):
diff --git a/xos/core/models/attic/xosbase_header.py b/xos/core/models/attic/xosbase_header.py
index 5848900..93df83d 100644
--- a/xos/core/models/attic/xosbase_header.py
+++ b/xos/core/models/attic/xosbase_header.py
@@ -19,6 +19,8 @@
 import redis
 from redis import ConnectionError
 
+XOS_GLOBAL_DEFAULT_SECURITY_POLICY = True
+
 def date_handler(obj):
     if isinstance(obj, pytz.tzfile.DstTzInfo):
         # json can't serialize DstTzInfo
diff --git a/xos/core/models/attic/xosbase_model.py b/xos/core/models/attic/xosbase_model.py
index 98746c6..dc7e5ed 100644
--- a/xos/core/models/attic/xosbase_model.py
+++ b/xos/core/models/attic/xosbase_model.py
@@ -15,9 +15,6 @@
 def get_controller(self):
     return self.controller
 
-def can_update(self, user):
-    return user.can_update_root()
-
 def delete(self, *args, **kwds):
     # so we have something to give the observer
     purge = kwds.get('purge',False)
@@ -131,26 +128,6 @@
 
     self._initial = self._dict
 
-def save_by_user(self, user, *args, **kwds):
-    if not self.can_update(user):
-        if getattr(self, "_cant_update_fieldName", None) is not None:
-            raise PermissionDenied("You do not have permission to update field %s on object %s" % (self._cant_update_fieldName, self.__class__.__name__))
-        else:
-            raise PermissionDenied("You do not have permission to update %s objects" % self.__class__.__name__)
-
-    self.save(*args, **kwds)
-
-def delete_by_user(self, user, *args, **kwds):
-    if not self.can_update(user):
-        raise PermissionDenied("You do not have permission to delete %s objects" % self.__class__.__name__)
-    self.delete(*args, **kwds)
-
-@classmethod
-def select_by_user(cls, user):
-    # This should be overridden by descendant classes that want to perform
-    # filtering of visible objects by user.
-    return cls.objects.all()
-
 def tologdict(self):
     try:
         d = {'model_name':self.__class__.__name__, 'pk': self.pk}
diff --git a/xos/core/models/core.xproto b/xos/core/models/core.xproto
index 524a62c..b0c6a31 100644
--- a/xos/core/models/core.xproto
+++ b/xos/core/models/core.xproto
@@ -20,7 +20,18 @@
      required string leaf_model_name = 15 [null = False, max_length = 1024, help_text = "The most specialized model in this chain of inheritance, often defined by a service developer"];
 }
 
-message User (AbstractBaseUser,PlModelMixIn) {
+// The calling user represents the user being accessed, or is a site admin.
+policy user_policy <
+         ctx.user.is_admin
+         | ctx.user.id = obj.id
+         | (exists Privilege:
+             Privilege.accessor_id = ctx.user.id
+             & Privilege.accessor_type = "User"
+             & Privilege.permission = "role:admin"
+             & Privilege.object_type = "Site"
+             & Privilege.object_id = ctx.user.site.id) >
+
+message User::user_policy (AbstractBaseUser,PlModelMixIn) {
      option skip_django = True;
      option tosca_description = "An XOS User";
 
@@ -58,14 +69,23 @@
      optional string policy_status = 32 [default = "0 - Policy in process", max_length = 1024];
 }
 
-message Privilege (XOSBase) {
+// A user may give a permission that he has to another user
+policy grant_policy < ctx.user.is_admin
+                      | exists Privilege:Privilege.object_type = obj.object_type
+                        & Privilege.object_id = obj.object_id
+                        & Privilege.accessor_type = "User"
+                        & Privilege.accessor_id = ctx.user.id 
+                        & Privilege.permission = "role:admin" >
+    
+message Privilege::grant_policy (XOSBase) {
      required int32 accessor_id = 1 [null = False];
      required string accessor_type = 2 [null = False, max_length=1024];
-     required int32 object_id = 3 [null = False];  
-     required string object_type = 4 [null = False, max_length=1024];
-     required string permission = 5 [null = False, default = "all", max_length=1024];
-     required string granted = 6 [content_type = "date", auto_now_add = True, max_length=1024];
-     required string expires = 7 [content_type = "date", null = True, max_length=1024];
+     required int32 controller_id = 3 [null = True];
+     required int32 object_id = 4 [null = False];  
+     required string object_type = 5 [null = False, max_length=1024];
+     required string permission = 6 [null = False, default = "all", max_length=1024];
+     required string granted = 7 [content_type = "date", auto_now_add = True, max_length=1024];
+     required string expires = 8 [content_type = "date", null = True, max_length=1024];
 }
 
 message AddressPool (XOSBase) {
@@ -78,8 +98,16 @@
      optional manytoone service->Service:addresspools = 7 [db_index = True, null = True, blank = True];
 }
 
+// Admins at a deployment have access to controllers at those deployments
+policy controller_policy 
+       < ctx.user.is_admin 
+           | exists Privilege: 
+                  Privilege.accessor_id = ctx.user.id 
+                  & Privilege.object_type = "Deployment" 
+                  & Privilege.permission = "role:admin" 
+                  & Privilege.object_id = obj.id >
 
-message Controller (XOSBase) {
+message Controller::controller_policy (XOSBase) {
      required string name = 1 [max_length = 200, content_type = "stripped", blank = False, help_text = "Name of the Controller", null = False, db_index = False];
      required string backend_type = 2 [max_length = 200, content_type = "stripped", blank = False, help_text = "Type of compute controller, e.g. EC2, OpenStack, or OpenStack version", null = False, db_index = False];
      required string version = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Controller version", null = False, db_index = False];
@@ -109,8 +137,47 @@
      optional string glance_image_id = 3 [max_length = 200, content_type = "stripped", blank = True, help_text = "Glance image id", null = True, db_index = False];
 }
 
+// Everyone has read access
+// For write access, you have to be a site_admin
 
-message ControllerNetwork (XOSBase) {
+policy site_policy <
+         ctx.user.is_admin
+         | (ctx.write_access -> exists Privilege: Privilege.object_type = "Site" & Privilege.object_id = obj.id & Privilege.accessor_id = ctx.user.id & Privilege.permission = "role:admin") >
+
+// If you can access (read or write) the site, you can also access its slices
+// Otherwise, you need an explicit privilege on the Slice (admin for write access)
+// or admin privilege on the associated site.
+policy slice_policy <
+         ctx.user.is_admin
+         | (*site_policy(site)
+         & (ctx.user.id = obj.creator.id  
+               | (exists Privilege:
+                     Privilege.accessor_id = ctx.user.id
+                     & Privilege.accessor_type = "User"
+                     & Privilege.object_type = "Slice"
+                     & Privilege.object_id = obj.id
+                     & (ctx.write_access -> Privilege.permission = "role:admin"))
+             )
+             |
+              (exists Privilege:
+                 Privilege.accessor_id = ctx.user.id
+                 & Privilege.accessor_type = "User"
+                 & Privilege.object_type = "Slice"
+                 & Privilege.object_id = obj.id)
+             | (exists Privilege:
+                 Privilege.accessor_id = ctx.user.id
+                 & Privilege.accessor_type = "User"
+                 & Privilege.object_type = "Site"
+                 & Privilege.object_id = obj.site.id
+                 & Privilege.permission = "role:admin")
+                ) >
+
+policy controller_network_policy <
+         ctx.user.is_admin
+         | *slice_policy(network.owner) >
+
+
+message ControllerNetwork::controller_network_policy (XOSBase) {
      required manytoone network->Network:controllernetworks = 1 [db_index = True, null = False, blank = False, unique_with = "controller"];
      required manytoone controller->Controller:controllernetworks = 2 [db_index = True, null = False, blank = False];
      required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
@@ -147,8 +214,11 @@
      optional string role_id = 3 [max_length = 200, content_type = "stripped", blank = True, help_text = "Keystone id", null = True, db_index = True];
 }
 
+policy controller_slice_policy <
+         ctx.user.is_admin
+         | *slice_policy(slice) >
 
-message ControllerSlice (XOSBase) {
+message ControllerSlice::controller_slice_policy (XOSBase) {
      required manytoone controller->Controller:controllerslices = 1 [db_index = True, null = False, blank = False, unique_with = "slice"];
      required manytoone slice->Slice:controllerslices = 2 [db_index = True, null = False, blank = False];
      optional string tenant_id = 3 [max_length = 200, content_type = "stripped", blank = True, help_text = "Keystone tenant id", null = True, db_index = False];
@@ -161,8 +231,11 @@
      optional string role_id = 3 [max_length = 200, content_type = "stripped", blank = True, help_text = "Keystone id", null = True, db_index = True];
 }
 
+policy controller_user_policy <
+         ctx.user.is_admin
+         | (ctx.read_access & *user_policy(user)) >
 
-message ControllerUser (XOSBase) {
+message ControllerUser::controller_user_policy (XOSBase) {
      required manytoone user->User:controllerusers = 1 [db_index = True, null = False, blank = False];
      required manytoone controller->Controller:controllersusers = 2 [db_index = True, null = False, blank = False, unique_with = "user"];
      optional string kuser_id = 3 [max_length = 200, content_type = "stripped", blank = True, help_text = "Keystone user id", null = True, db_index = False];
@@ -179,8 +252,13 @@
      required manytomany deployments->Deployment/DashboardView_deployments:dashboardviews = 7 [help_text = "Deployments that should be included in this view", null = False, db_index = False, blank = True];
 }
 
+// Everyone has read access
+// For write access you need admin privileges at that deployment
+policy deployment_policy <
+         ctx.user.is_admin
+         | (ctx.write_access -> exists Privilege: Privilege.object_type = "Deployment" & Privilege.object_id = obj.id & Privilege.accessor_id = ctx.user.id & Privilege.permission = "role:admin") >
 
-message Deployment (XOSBase) {
+message Deployment::deployment_policy (XOSBase) {
      required string name = 1 [max_length = 200, content_type = "stripped", blank = False, help_text = "Name of the Deployment", null = False, db_index = False];
      required string accessControl = 2 [default = "allow all", max_length = 200, blank = False, help_text = "Access control list that specifies which sites/users may use nodes in this deployment", null = False, db_index = False, varchar = True];
 }
@@ -219,6 +297,9 @@
      optional string tag = 6 [max_length = 256, content_type = "stripped", blank = True, help_text = "For Docker Images, tag of image", null = True, db_index = False];
 }
 
+policy image_deployment_policy <
+        *deployment_policy(deployment) 
+>
 
 message ImageDeployments (XOSBase) {
      required manytoone image->Image:imagedeployments = 1 [db_index = True, null = False, blank = False, unique_with = "deployment"];
@@ -232,9 +313,10 @@
 policy instance_isolation_vm < (obj.isolation = "vm") -> (obj.image.kind = "vm") >
 policy instance_creator_privilege < not (obj.slice.creator = obj.creator) -> exists Privilege:Privilege.object_id = obj.slice.id & Privilege.accessor_id = obj.creator.id & Privilege.object_type = "Slice" >
 
-message Instance (XOSBase) {
-     option validators = "instance_creator:Instance has no creator, instance_isolation: Container instance { obj.name } must use container image, instance_isolation_container_vm_parent:Container-vm instance {obj.name} must have a parent, instance_parent_isolation_container_vm:Parent field can only be set on Container-vm instances ({ obj.name }), instance_isolation_vm: VM Instance { obj.name } must use VM image, instance_creator_privilege: instance creator has no privileges on slice";
-    
+policy instance_policy < *slice_policy(slice) >
+
+message Instance::instance_policy (XOSBase) {
+     option validators = "instance_creator:Instance has no creator, instance_isolation: Container instance {obj.name} must use container image, instance_isolation_container_vm_parent:Container-vm instance {obj.name} must have a parent, instance_parent_isolation_container_vm:Parent field can only be set on Container-vm instances ({obj.name}), instance_isolation_vm: VM Instance {obj.name} must use VM image, instance_creator_privilege: instance creator has no privileges on slice";
      optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
      optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
      required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
@@ -252,7 +334,11 @@
      optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
      optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
 }
-message Network (XOSBase) {
+
+
+policy network_policy < *slice_policy(owner) >
+
+message Network::network_policy (XOSBase) {
      required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
      required manytoone template->NetworkTemplate:network = 2 [db_index = True, null = False, blank = False];
      required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
@@ -283,8 +369,9 @@
 }
 
 policy network_slice_validator < (obj.slice in obj.network.permitted_slices.all()) | (obj.slice = obj.network.owner) | obj.network.permit_all_slices >
+policy network_slice_policy < *slice_policy(slice) & *network_policy(network) >
 
-message NetworkSlice (XOSBase) {
+message NetworkSlice::network_slice_policy (XOSBase) {
      option validators = "network_slice_validator:Slice { obj.slice.name } is not allowed to connect to networks { obj.network }";
      required manytoone network->Network:networkslices = 1 [db_index = True, null = False, blank = False, unique_with = "slice"];
      required manytoone slice->Slice:networkslices = 2 [db_index = True, null = False, blank = False];
@@ -302,7 +389,10 @@
      optional string controller_kind = 10 [blank = True, max_length = 30, null = True, db_index = False, choices = "((None, 'None'), ('onos', 'ONOS'), ('custom', 'Custom'))"];
      optional string vtn_kind = 11 [default = "PRIVATE", choices = "(('PRIVATE', 'Private'), ('PUBLIC', 'Public'), ('MANAGEMENT_LOCAL', 'Management Local'), ('MANAGEMENT_HOST', 'Management Host'), ('VSG', 'VSG'), ('ACCESS_AGENT', 'Access Agent'))", max_length = 30, blank = True, null = True, db_index = False];
 }
-message Node (XOSBase) {
+
+policy node_policy < *site_policy(site_deployment.site) >
+
+message Node::node_policy (XOSBase) {
      required string name = 1 [max_length = 200, content_type = "stripped", blank = False, help_text = "Name of the Node", null = False, db_index = False];
      required manytoone site_deployment->SiteDeployment:nodes = 2 [db_index = True, null = False, blank = False];
 }
@@ -312,8 +402,9 @@
 }
 
 policy port_validator < (obj.instance.slice in obj.network.permitted_slices.all()) | (obj.instance.slice = obj.network.owner) | obj.network.permit_all_slices >
+policy port_policy < *instance_policy(instance) & *network_policy(network) >
 
-message Port (XOSBase) {
+message Port::port_policy (XOSBase) {
      option validators = "port_validator:Slice is not allowed to connect to network";
      required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False, unique_with = "instance"];
      optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
@@ -330,6 +421,7 @@
      required string description = 3 [db_index = False, max_length = 120, null = False, content_type = "stripped", blank = False];
 }
 
+policy service_policy <ctx.user.is_admin | exists Privilege: Privilege.accessor_id = ctx.user.id & Privilege.accessor_type = "User" & Privilege.object_type = "Service" & Privilege.object_id = obj.id >
 
 message Service (XOSBase,AttributeMixin) {
      optional string description = 1 [help_text = "Description of Service", max_length = 254, null = True, db_index = False, blank = True, varchar = True];
@@ -380,7 +472,8 @@
 }
 
 
-message Site (XOSBase) {
+
+message Site::site_policy (XOSBase) {
      required string name = 1 [max_length = 200, content_type = "stripped", blank = False, help_text = "Name for this Site", null = False, db_index = False];
      optional string site_url = 2 [max_length = 512, content_type = "url", blank = True, help_text = "Site's Home URL Page", null = True, db_index = False];
      required bool enabled = 3 [help_text = "Status for this Site", default = True, null = False, db_index = False, blank = True];
@@ -418,8 +511,10 @@
 policy slice_name_length_and_no_spaces < {{ len(obj.site.login_base) + 1 < len(obj.name) and ' ' not in obj.name }} >
 policy slice_has_creator < obj.creator >
 
-message Slice (XOSBase) {
-     option validators = "slice_name:Slice name ({ obj.name}) must begin with site login_base ({ obj.site.login_base}), slice_name_length_and_no_spaces:Slice name too short or contains spaces, slice_has_creator:Slice has no creator";
+
+            
+message Slice::slice_policy (XOSBase) {
+     option validators = "slice_name:Slice name ({obj.name}) must begin with site login_base ({ obj.site.login_base}), slice_name_length_and_no_spaces:Slice name too short or contains spaces, slice_has_creator:Slice has no creator";
      option plural = "Slices";
 
      required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
@@ -451,8 +546,9 @@
      required string role = 1 [choices = "(('admin', 'Admin'), ('default', 'Default'))", max_length = 30, content_type = "stripped", blank = False, null = False, db_index = False];
 }
 
+policy tag_policy < ctx.user.is_admin >
 
-message Tag (XOSBase) {
+message Tag::tag_policy (XOSBase) {
      required manytoone service->Service:tags = 1 [help_text = "The Service this Tag is associated with", null = False, db_index = True, blank = False];
      required string name = 2 [help_text = "The name of this tag", max_length = 128, null = False, db_index = True, blank = False];
      required string value = 3 [max_length = 1024, content_type = "stripped", blank = False, help_text = "The value of this tag", null = False, db_index = False];
diff --git a/xos/core/models/user.py b/xos/core/models/user.py
index 3add183..dd7ae84 100644
--- a/xos/core/models/user.py
+++ b/xos/core/models/user.py
@@ -7,7 +7,8 @@
 from operator import attrgetter, itemgetter
 
 from core.middleware import get_request
-from core.models import DashboardView, XOSBase, PlModelMixIn, Site, ModelLink
+from xosbase import XOSBase,PlModelMixIn
+import dashboardview
 from core.models.xosbase import StrippedCharField, XOSCollector
 from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
 from django.core.exceptions import PermissionDenied
@@ -21,6 +22,10 @@
 from timezones.fields import TimeZoneField
 from django.contrib.contenttypes.models import ContentType
 
+# The following manual import is needed because we do not
+# currently generate the User models.
+import security
+
 import redis
 from redis import ConnectionError
 
@@ -114,7 +119,7 @@
     phone = StrippedCharField(null=True, blank=True,
                               help_text="phone number contact", max_length=100)
     user_url = models.URLField(null=True, blank=True)
-    site = models.ForeignKey(Site, related_name='users',
+    site = models.ForeignKey('Site', related_name='users',
                              help_text="Site this user will be homed too")
     public_key = models.TextField(
         null=True, blank=True, max_length=1024, help_text="Public key string")
@@ -143,8 +148,6 @@
     no_sync = models.BooleanField(default=False)     # prevent object sync
     no_policy = models.BooleanField(default=False)   # prevent model_policy run
 
-    #xos_links = [ModelLink(Site,via='site')]
-
     timezone = TimeZoneField()
 
     dashboards = models.ManyToManyField(
@@ -207,7 +210,7 @@
 
         if (not dashboards) and (not self.is_appuser):
             for dashboardName in DEFAULT_DASHBOARDS:
-                dbv = DashboardView.objects.filter(name=dashboardName)
+                dbv = dashboardview.DashboardView.objects.filter(name=dashboardName)
                 if dbv:
                     dashboards.append(dbv[0])
 
@@ -321,150 +324,6 @@
         msg.attach_alternative(html_content, "text/html")
         msg.send()
 
-    def can_update(self, user):
-        from core.models.privilege import Privilege
-        _cant_update_fieldName = None
-        if user.can_update_root():
-            return True
-
-        # site pis can update
-        site_privs = Privilege.objects.filter(accessor_id=user.id, object_id=self.site.id, object_type='Site', accessor_type='User')
-        for site_priv in site_privs:
-            if site_priv.permission == 'role:admin':
-                return True
-
-            if site_priv.permission == 'role:pi':
-                for fieldName in self.diff.keys():
-                    if fieldName in self.PI_FORBIDDEN_FIELDS:
-                        _cant_update_fieldName = fieldName
-                        return False
-                return True
-        if (user.id == self.id):
-            for fieldName in self.diff.keys():
-                if fieldName in self.USER_FORBIDDEN_FIELDS:
-                    _cant_update_fieldName = fieldName
-                    return False
-            return True
-
-        return False
-
-    def can_update_root(self):
-        """
-        Return True if user has root (global) write access.
-        """
-        if self.is_readonly:
-            return False
-        if self.is_admin:
-            return True
-
-        return False
-
-    def can_update_deployment(self, deployment):
-        from core.models.privilege import Privilege
-        if self.can_update_root():
-            return True
-
-        if Privilege.objects.filter(
-                object_id=deployment.id,
-                accessor_id=self.id,
-                permission__in=['role:admin', 'role:Admin']):
-            return True
-        return False
-
-    def can_update_site(self, site, allow=[]):
-        from core.models.privilege import Privilege
-        if self.can_update_root():
-            return True
-        if Privilege.objects.filter(
-                object_id=site.id, accessor_id=self.id, accessor_type='User', permission__in=['role:admin', 'role:Admin'] + ['role:'+opt for opt in allow]):
-            return True
-        return False
-
-    def can_update_slice(self, slice):
-        from core.models.privilege import Privilege
-        if self.can_update_root():
-            return True
-        if self == slice.creator:
-            return True
-        if self.can_update_site(slice.site, allow=['pi']):
-            return True
-
-        if Privilege.objects.filter(
-                object_id=slice.id, accessor_id=self.id, permission__in=['role:admin', 'role:Admin'], accessor_type='User', object_type='Slice'):
-            return True
-        return False
-
-    def can_update_service(self, service, allow=[]):
-        from core.models.privilege import Privilege
-        if self.can_update_root():
-            return True
-        if Privilege.objects.filter(
-                object_id=service.id, accessor_id=self.id, accessor_type='User', permission__in=['role:admin', 'role:Admin'] + ['role:'+opt for opt in allow]):
-            return True
-        return False
-
-    def can_update_tenant_root(self, tenant_root, allow=[]):
-        from core.models.tenantroot import TenantRoot
-        from core.models.privilege import Privilege
-        if self.can_update_root():
-            return True
-        if Privilege.objects.filter(
-                object_id=tenant_root.id, accessor_type='User',accessor_id=self.id, permission__in=['role:admin', 'role:Admin'] + ['role:'+opt for opt in allow]):
-            return True
-        return False
-
-    def can_update_tenant(self, tenant, allow=[]):
-        from core.models.tenant import Tenant
-        from core.models.privilege import Privilege
-        if self.can_update_root():
-            return True
-        if Privilege.objects.filter(
-                object_id=tenant.id, accessor_type='User',accessor_id=self.id, permission__in=['role:admin', 'role:Admin'] + ['role:'+opt for opt in 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)
-
-    @staticmethod
-    def select_by_user(user):
-        if user.is_admin:
-            qs = User.objects.all()
-        else:
-            # can see all users at any site where this user has pi role
-            from core.models.privilege import Privilege
-            site_privs = Privilege.objects.filter(accessor_type='User', accessor_id=user.id, object_type='Site')
-            site_ids = [sp.object_id for sp in site_privs if sp.permission in [
-                'role:Admin', 'role:admin', 'role:pi']]
-            sites = [Site.objects.get(pk=sid) for sid in site_ids]
-
-            # get site privs of users at these sites
-            site_privs = Privilege.objects.filter(object_id__in=site_ids, object_type='Site', accessor_type='User')
-
-            user_ids = [sp.accessor_id for sp in site_privs] + [user.id]
-            qs = User.objects.filter(Q(site__in=sites) | Q(id__in=user_ids))
-        return qs
-
-    def save_by_user(self, user, *args, **kwds):
-        if not self.can_update(user):
-            if getattr(self, "_cant_update_fieldName", None) is not None:
-                raise PermissionDenied("You do not have permission to update field %s on object %s" % (
-                    self._cant_update_fieldName, self.__class__.__name__))
-            else:
-                raise PermissionDenied(
-                    "You do not have permission to update %s objects" % self.__class__.__name__)
-
-        self.save(*args, **kwds)
-
-    def delete_by_user(self, user, *args, **kwds):
-        if not self.can_update(user):
-            raise PermissionDenied(
-                "You do not have permission to delete %s objects" % self.__class__.__name__)
-        self.delete(*args, **kwds)
-
     def apply_profile(self, profile):
         if profile == "regular":
             self.is_appuser = False
@@ -491,11 +350,14 @@
         cls = ct.model_class()
         return cls.objects.get(id=object_id)
 
+    ''' This function is hardcoded here because we do not yet
+    generate the User class'''
+    def can_access(self, ctx):
+        return security.user_policy_security_check(self, ctx), "user_policy"
 
 class UserDashboardView(XOSBase):
     user = models.ForeignKey(User, related_name='userdashboardviews')
     dashboardView = models.ForeignKey(
-        DashboardView, related_name='userdashboardviews')
+        dashboardview.DashboardView, related_name='userdashboardviews')
     order = models.IntegerField(default=0)
 
-    xos_links = [ModelLink(User, via='user'), ModelLink(DashboardView,via='userdashboardviews')]
diff --git a/xos/coreapi/apihelper.py b/xos/coreapi/apihelper.py
index de7ee19..5d21c74 100644
--- a/xos/coreapi/apihelper.py
+++ b/xos/coreapi/apihelper.py
@@ -15,6 +15,12 @@
 
 from importlib import import_module
 from django.conf import settings
+
+class XOSDefaultSecurityContext(object):
+    grant_access = True
+    write_access = True
+    read_access = True
+
 SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
 
 def translate_exceptions(function):
@@ -261,24 +267,64 @@
             obj = djangoClass.objects.get(id=id)
         return obj
 
-    def get(self, djangoClass, id):
+    def xos_security_gate(self, obj, user, **access_types):
+        sec_ctx = XOSDefaultSecurityContext()
+        sec_ctx.user = user
+
+        for k,v in access_types.items():
+            setattr(sec_ctx, k, v)
+
+        obj_ctx = obj
+
+        verdict, policy_name = obj.can_access(ctx = sec_ctx)
+
+        # FIXME: This is the central point of enforcement for security policies
+        #        Implement Auditing here.
+        #        logging.info( ... )
+
+        if not verdict:
+            #    logging.critical( ... )
+            if object_id:
+                object_descriptor = 'object %d'%object_id
+            else:
+                object_descriptor = 'new object'
+
+            raise XOSPermissionDenied("User %(user_email)s cannot access %(django_class_name)s %(descriptor)s due to policy %(policy_name)s"%{'user_email':user.email, 'django_class_name':obj.__class__.__name__, 'policy_name': policy_name, 'descriptor': object_descriptor})
+
+    def xos_security_check(self, obj, user, **access_types):
+        sec_ctx = XOSDefaultSecurityContext()
+        sec_ctx.user = user
+        for k,v in access_types.items():
+            setattr(sec_ctx, k, v)
+
+        obj_ctx = obj
+
+        verdict, _ = obj.can_access(ctx = sec_ctx)
+        return verdict
+
+    def get(self, djangoClass, user, id):
         obj = self.get_live_or_deleted_object(djangoClass, id)
+
+        self.xos_security_gate(obj, user, read_access=True)
+
         return self.objToProto(obj)
 
     def create(self, djangoClass, user, request):
         args = self.protoToArgs(djangoClass, request)
         new_obj = djangoClass(**args)
         new_obj.caller = user
-        if (not user) or (not new_obj.can_update(user)):
-            raise XOSPermissionDenied()
+
+        self.xos_security_gate(new_obj, user, write_access=True)
+
         new_obj.save()
         return self.objToProto(new_obj)
 
     def update(self, djangoClass, user, id, message, context):
         obj = self.get_live_or_deleted_object(djangoClass, id)
         obj.caller = user
-        if (not user) or (not obj.can_update(user)):
-            raise XOSPermissionDenied()
+
+        self.xos_security_gate(obj, user, write_access=True)
+
         args = self.protoToArgs(djangoClass, message)
         for (k,v) in args.iteritems():
             setattr(obj, k, v)
@@ -297,8 +343,9 @@
 
     def delete(self, djangoClass, user, id):
       obj = djangoClass.objects.get(id=id)
-      if (not user) or (not obj.can_update(user)):
-          raise XOSPermissionDenied()
+
+      self.xos_security_gate(obj, user, write_access=True)
+
       obj.delete()
       return Empty()
 
@@ -329,7 +376,16 @@
 
         return q
 
-    def filter(self, djangoClass, request):
+    def list(self, djangoClass, user):
+        queryset = djangoClass.objects.all()
+        filtered_queryset = (elt for elt in queryset if self.xos_security_check(elt, user, read_access=True))
+
+        # FIXME: Implement auditing here
+        # logging.info("User requested x objects, y objects were filtered out by policy z")
+
+        return self.querysetToProto(djangoClass, filtered_queryset)
+
+    def filter(self, djangoClass, user, request):
         query = None
         if request.kind == request.DEFAULT:
             for element in request.elements:
@@ -352,7 +408,12 @@
         elif request.kind == request.ALL:
             queryset = djangoClass.objects.all()
 
-        return self.querysetToProto(djangoClass, queryset)
+        filtered_queryset = (elt for elt in queryset if self.xos_security_check(elt, user, read_access=True))
+
+        # FIXME: Implement auditing here
+        # logging.info("User requested x objects, y objects were filtered out by policy z")
+
+        return self.querysetToProto(djangoClass, filtered_queryset)
 
     def authenticate(self, context, required=False):
         for (k, v) in context.invocation_metadata():
diff --git a/xos/tests/permissiontest.py b/xos/tests/permissiontest.py
index 191eae6..0b3c88f 100644
--- a/xos/tests/permissiontest.py
+++ b/xos/tests/permissiontest.py
@@ -112,7 +112,7 @@
     def test_deployment(self):
         for user in [self.user_admin, self.user_deployment_admin]:
             self.assertEqual(
-                self.deployment.save_by_user(user), None)
+                self.deployment.save(), None)
         for user in [self.user_read_only, self.user_default, self.user_site_admin,
                      self.user_site_pi, self.user_site_tech, self.user_slice_admin,
                      self.user_slice_access]:
diff --git a/xos/tools/corebuilder/corebuilder.py b/xos/tools/corebuilder/corebuilder.py
index 845e7c7..26820da 100644
--- a/xos/tools/corebuilder/corebuilder.py
+++ b/xos/tools/corebuilder/corebuilder.py
@@ -316,6 +316,16 @@
 
                     XOSGenerator.generate(args)
 
+                    # Generate security checks
+                    class SecurityArgs:
+                        output = build_dest_fn
+                        target = 'django-security.xtarget'
+                        dest_file = 'security.py'
+                        write_to_file = 'single'
+                        files = file_list
+
+                    XOSGenerator.generate(SecurityArgs())
+
                     # Generate __init__.py
                     if service_name == "core":
                         class InitArgs:
@@ -324,6 +334,7 @@
                             dest_file = '__init__.py'
                             write_to_file = 'single'
                             files = file_list
+
                         XOSGenerator.generate(InitArgs())
 
                 except Exception, e:
diff --git a/xos/xos/apibase.py b/xos/xos/apibase.py
index 8d80f38..e22128b 100644
--- a/xos/xos/apibase.py
+++ b/xos/xos/apibase.py
@@ -32,8 +32,6 @@
         for attr, value in serializer.validated_data.items():
             setattr(obj, attr, value)
         obj.caller = request.user
-        if not obj.can_update(request.user):
-            raise XOSPermissionDenied()
 
         self.perform_update(serializer)
 
@@ -42,11 +40,8 @@
     def destroy(self, request, *args, **kwargs):
         obj = self.get_object()
         obj.caller = request.user
-        if obj.can_update(request.user):
-            self.perform_destroy(obj)
-            return Response(status=status.HTTP_204_NO_CONTENT)
-        else:
-            return Response(status=status.HTTP_400_BAD_REQUEST)
+        self.perform_destroy(obj)
+        return Response(status=status.HTTP_204_NO_CONTENT)
 
     def handle_exception(self, exc):
         # REST API drops the string attached to Django's PermissionDenied
@@ -70,9 +65,6 @@
         # now do XOS can_update permission checking
         obj = serializer.Meta.model(**serializer.validated_data)
         obj.caller = request.user
-        if not obj.can_update(request.user):
-            raise XOSPermissionDenied()
-
         self.perform_create(serializer)
 
         headers = self.get_success_headers(serializer.data)