CORD-1316: Implement new credentials system
Change-Id: Iaf869cc275fe78a3dd871dc45ef5bba55cde2027
diff --git a/xos/core/models/attic/controller_model.py b/xos/core/models/attic/controller_model.py
index 1e07807..9e76572 100644
--- a/xos/core/models/attic/controller_model.py
+++ b/xos/core/models/attic/controller_model.py
@@ -1,10 +1,9 @@
@staticmethod
def select_by_user(user):
-
if user.is_admin:
qs = Controller.objects.all()
else:
- from core.models.deploymentprivilege import DeploymentPrivilege
- deployments = [dp.deployment for dp in DeploymentPrivilege.objects.filter(user=user, role__role__in=['Admin', 'admin'])]
+ 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/controllersiteprivilege_model.py b/xos/core/models/attic/controllersiteprivilege_model.py
index b9d8af4..11ba22d 100644
--- a/xos/core/models/attic/controllersiteprivilege_model.py
+++ b/xos/core/models/attic/controllersiteprivilege_model.py
@@ -3,17 +3,18 @@
return False
if user.is_admin:
return True
- cprivs = ControllerSitePrivilege.objects.filter(site_privilege__user=user)
- for cpriv in dprivs:
- if cpriv.site_privilege.role.role == ['admin', 'Admin']:
+
+ 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 = ControllerSitePrivilege.objects.all()
+ qs = ControllerPrivilege.objects.filter(privilege__object_type='Site')
else:
- cpriv_ids = [cp.id for cp in ControllerSitePrivilege.objects.filter(site_privilege__user=user)]
- qs = ControllerSitePrivilege.objects.filter(id__in=cpriv_ids)
+ 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/controllersliceprivilege_model.py b/xos/core/models/attic/controllersliceprivilege_model.py
index aaf6053..ab64569 100644
--- a/xos/core/models/attic/controllersliceprivilege_model.py
+++ b/xos/core/models/attic/controllersliceprivilege_model.py
@@ -3,17 +3,17 @@
return False
if user.is_admin:
return True
- cprivs = ControllerSlicePrivilege.objects.filter(slice_privilege__user=user)
- for cpriv in dprivs:
- if cpriv.role.role == ['admin', 'Admin']:
+ 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 = ControllerSlicePrivilege.objects.all()
+ qs = ControllerPrivilege.objects.filter(privilege__object_type='Slice')
else:
- cpriv_ids = [cp.id for cp in ControllerSlicePrivilege.objects.filter(slice_privilege__user=user)]
- qs = ControllerSlicePrivilege.objects.filter(id__in=cpriv_ids)
+ 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/instance_model.py b/xos/core/models/attic/instance_model.py
index d2e7b89..a86e53d 100644
--- a/xos/core/models/attic/instance_model.py
+++ b/xos/core/models/attic/instance_model.py
@@ -32,10 +32,10 @@
raise ValidationError("Parent field can only be set on Container-vm instances")
if (self.slice.creator != self.creator):
- from core.models.sliceprivilege import SlicePrivilege
+ from core.models.privilege import Privilege
# Check to make sure there's a slice_privilege for the user. If there
# isn't, then keystone will throw an exception inside the observer.
- slice_privs = SlicePrivilege.objects.filter(slice=self.slice, user=self.creator)
+ slice_privs = Privilege.objects.filter(object_id=self.slice.id, accessor_id=self.creator.id, object_type='Slice')
if not slice_privs:
raise ValidationError('instance creator has no privileges on slice')
@@ -108,9 +108,10 @@
return 'ssh -o "ProxyCommand ssh -q %s@%s" ubuntu@%s' % (self.instance_id, self.node.name, self.instance_name)
def get_public_keys(self):
- from core.models.sliceprivilege import SlicePrivilege
- slice_memberships = SlicePrivilege.objects.filter(slice=self.slice)
- pubkeys = set([sm.user.public_key for sm in slice_memberships if sm.user.public_key])
+ from core.models.sliceprivilege import Privilege
+ slice_privileges = Privilege.objects.filter(object_id=self.slice.id, object_type='Slice', accessor_type='User')
+ slice_users = [User.objects.get(pk = priv.accessor_id) for priv in slice_privileges]
+ pubkeys = set([u.public_key for u in slice_users if u.public_key])
if self.creator.public_key:
pubkeys.add(self.creator.public_key)
diff --git a/xos/core/models/attic/service_model.py b/xos/core/models/attic/service_model.py
index d7175b6..9cb2d4d 100644
--- a/xos/core/models/attic/service_model.py
+++ b/xos/core/models/attic/service_model.py
@@ -10,9 +10,9 @@
if user.is_admin:
return cls.objects.all()
else:
- from core.models.serviceprivilege import ServicePrivilege
+ from core.models.privilege import Privilege
service_ids = [
- sp.slice.id for sp in ServicePrivilege.objects.filter(user=user)]
+ 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
diff --git a/xos/core/models/attic/slice_model.py b/xos/core/models/attic/slice_model.py
index db53e11..7154c73 100644
--- a/xos/core/models/attic/slice_model.py
+++ b/xos/core/models/attic/slice_model.py
@@ -51,13 +51,14 @@
if user.is_admin:
qs = Slice.objects.all()
else:
- from core.models.sliceprivilege import SlicePrivilege
- from core.models.siteprivilege import SitePrivilege
+ from core.models.privilege import Privilege
# users can see slices they belong to
- slice_ids = [sp.slice.id for sp in SlicePrivilege.objects.filter(user=user)]
+ 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
- sites = [sp.site for sp in SitePrivilege.objects.filter(user=user)\
- if (sp.role.role == 'pi') or (sp.role.role == 'admin')]
+ 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/tenantroot_model.py b/xos/core/models/attic/tenantroot_model.py
index bb09b1e..86208cd 100644
--- a/xos/core/models/attic/tenantroot_model.py
+++ b/xos/core/models/attic/tenantroot_model.py
@@ -23,9 +23,9 @@
if user.is_admin:
return cls.objects.all()
else:
- from core.models.tenantrootprivilege import TenantRootPrivilege
+ from core.models.privilege import Privilege
tr_ids = [
- trp.tenant_root.id for trp in TenantRootPrivilege.objects.filter(user=user)]
+ 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
diff --git a/xos/core/models/core.xproto b/xos/core/models/core.xproto
index 587769d..adb5842 100644
--- a/xos/core/models/core.xproto
+++ b/xos/core/models/core.xproto
@@ -57,6 +57,16 @@
optional string policy_status = 32 [default = "0 - Policy in process", max_length = 1024];
}
+message Privilege (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];
+}
+
message AddressPool (XOSBase) {
required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
optional string addresses = 2 [db_index = False, null = True, blank = True, varchar = True];
@@ -124,6 +134,11 @@
optional string tenant_id = 3 [max_length = 200, content_type = "stripped", blank = True, help_text = "Keystone tenant id", null = True, db_index = True];
}
+message ControllerPrivilege (XOSBase) {
+ required manytoone controller->Controller:controllerprivileges = 1 [db_index = True, null = False, blank = False];
+ required manytoone privilege->Privilege:controllerprivileges = 2 [db_index = True, null = False, blank = False];
+ optional string role_id = 3 [max_length = 200, content_type = "stripped", blank = True, help_text = "Keystone id", null = True, db_index = True];
+}
message ControllerSitePrivilege (XOSBase) {
required manytoone controller->Controller:controllersiteprivileges = 1 [db_index = True, null = False, blank = False, unique_with = "site_privilege"];
diff --git a/xos/core/models/user.py b/xos/core/models/user.py
index 0d721e2..3add183 100644
--- a/xos/core/models/user.py
+++ b/xos/core/models/user.py
@@ -322,17 +322,18 @@
msg.send()
def can_update(self, user):
- from core.models.siteprivilege import SitePrivilege
+ from core.models.privilege import Privilege
_cant_update_fieldName = None
if user.can_update_root():
return True
# site pis can update
- site_privs = SitePrivilege.objects.filter(user=user, site=self.site)
+ 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.role.role == 'admin':
+ if site_priv.permission == 'role:admin':
return True
- if site_priv.role.role == 'pi':
+
+ if site_priv.permission == 'role:pi':
for fieldName in self.diff.keys():
if fieldName in self.PI_FORBIDDEN_FIELDS:
_cant_update_fieldName = fieldName
@@ -359,28 +360,28 @@
return False
def can_update_deployment(self, deployment):
- from core.models.deploymentprivilege import DeploymentPrivilege
+ from core.models.privilege import Privilege
if self.can_update_root():
return True
- if DeploymentPrivilege.objects.filter(
- deployment=deployment,
- user=self,
- role__role__in=['admin', 'Admin']):
+ 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.siteprivilege import SitePrivilege
+ from core.models.privilege import Privilege
if self.can_update_root():
return True
- if SitePrivilege.objects.filter(
- site=site, user=self, role__role__in=['admin', 'Admin'] + allow):
+ 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.sliceprivilege import SlicePrivilege
+ from core.models.privilege import Privilege
if self.can_update_root():
return True
if self == slice.creator:
@@ -388,37 +389,37 @@
if self.can_update_site(slice.site, allow=['pi']):
return True
- if SlicePrivilege.objects.filter(
- slice=slice, user=self, role__role__in=['admin', 'Admin']):
+ 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.serviceprivilege import ServicePrivilege
+ from core.models.privilege import Privilege
if self.can_update_root():
return True
- if ServicePrivilege.objects.filter(
- service=service, user=self, role__role__in=['admin', 'Admin'] + allow):
+ 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.tenantrootprivilege import TenantRootPrivilege
+ from core.models.privilege import Privilege
if self.can_update_root():
return True
- if TenantRootPrivilege.objects.filter(
- tenant_root=tenant_root, user=self, role__role__in=['admin', 'Admin'] + allow):
+ 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.tenantprivilege import TenantPrivilege
+ from core.models.privilege import Privilege
if self.can_update_root():
return True
- if TenantPrivilege.objects.filter(
- tenant=tenant, user=self, role__role__in=['admin', 'Admin'] + allow):
+ 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
@@ -428,132 +429,22 @@
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
- models = []
- if filter_by and isinstance(filter_by, list):
- models = [m for m in filter_by if issubclass(m, PlModelMixIn)]
- if not models:
- models = [Deployment, Network, Site,
- Slice, SliceTag, Instance, Tag, User]
- readable_objects = []
- for model in models:
- readable_objects.extend(model.select_by_user(self))
- 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.
- Permissions will be either 'r' or 'rw'.
-
- e.g.
- [{'object': django_object_instance, 'permissions': 'rw'}, ...]
-
- Returns:
- list of dicts
-
- """
- 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 = []
- if filter_by and isinstance(filter_by, list):
- models = [m for m in filter_by if issubclass(m, PlModelMixIn)]
-
- deployment_priv_objs = [Image, NetworkTemplate, Flavor]
- site_priv_objs = [Node, Slice, User]
- slice_priv_objs = [Instance, Network]
-
- # maps the set of objects a paticular role has write access
- write_map = {
- DeploymentPrivilege: {
- 'admin': deployment_priv_objects,
- },
- SitePrivilege: {
- 'admin': site_priv_objs,
- 'pi': [Slice, User],
- 'tech': [Node],
- },
- SlicePrivilege: {
- 'admin': slice_priv_objs,
- },
- }
-
- privilege_map = {
- DeploymentPrivilege: (Deployment, deployment_priv_objs),
- SitePrivilege: (Site, site_priv_objs),
- SlicePrivilege: (Slice, slice_priv_objs)
- }
- permissions = []
- permission_dict = lambda x, y: {'object': x, 'permission': y}
- for privilege_model, (model, affected_models) in privileg_map.items():
- if models and model not in models:
- continue
-
- # 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))
-
- if self.is_admin:
- # assume admin users have read/write access to all objects
- for affected_object in affected_objects:
- permissions.append(permission_dict(
- affected_object, READWRITE))
- else:
- # create a dict of the user's per object privileges
- # ex: {princeton_tmack : ['admin']
- privileges = privilege_model.objects.filter(user=self)
- for privilege in privileges:
- object_roles = defaultdict(list)
- obj = None
- roles = []
- for field in dir(privilege):
- if field == model.__name__.lower():
- 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:
- if affected_object not in objects_roles:
- permissions.append(
- permission_dict(affected_object, READ))
- else:
- has_write_permission = False
- for write_role, models in write_dict.items():
- if affected_object._meta.model in models and \
- write_role in object_roles[affected_object]:
- has_write_permission = True
- break
- if has_write_permission:
- permissions.append(
- permission_dict(affected_object, WRITE))
- else:
- permissions.append(
- permission_dict(affected_object, READ))
-
- 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:
qs = User.objects.all()
else:
# can see all users at any site where this user has pi role
- from core.models.siteprivilege import SitePrivilege
- site_privs = SitePrivilege.objects.filter(user=user)
- sites = [sp.site for sp in site_privs if sp.role.role in [
- 'Admin', 'admin', 'pi']]
+ 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 = SitePrivilege.objects.filter(site__in=sites)
- user_ids = [sp.user.id for sp in site_privs] + [user.id]
+ 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
diff --git a/xos/core/xoslib/objects/sliceplus.py b/xos/core/xoslib/objects/sliceplus.py
index 9d2868f..5086f79 100644
--- a/xos/core/xoslib/objects/sliceplus.py
+++ b/xos/core/xoslib/objects/sliceplus.py
@@ -1,4 +1,4 @@
-from core.models import Slice, SlicePrivilege, SliceRole, Instance, Site, Node, User
+from core.models import Slice, Privilege, SliceRole, Instance, Site, Node, User
from plus import PlusObjectMixin
from operator import itemgetter, attrgetter
from rest_framework.exceptions import APIException
@@ -38,10 +38,11 @@
ready_sites[site.name] = ready_sites.get(site.name, 0) + 1
users = {}
- for priv in SlicePrivilege.objects.filter(slice=self):
- if not (priv.user.id in users.keys()):
- users[priv.user.id] = {"name": priv.user.email, "id": priv.user.id, "roles": []}
- users[priv.user.id]["roles"].append(priv.role.role)
+ for priv in Privilege.objects.filter(object_id=self.id, object_type='Slice', accessor_type='User'):
+ if not (priv.accessor_id in users.keys()):
+ user = User.objects.get(pk=priv.accessor_id)
+ users[priv.accessor_id] = {"name": user.email, "id": user.id, "roles": []}
+ users[priv.accessor_id]["roles"].append(priv.permission)
# XXX this assumes there is only one network that can have ports bound
# to it for a given slice. This is intended for the tenant view, which
@@ -118,7 +119,7 @@
if user.is_admin:
qs = SlicePlus.objects.all()
else:
- slice_ids = [sp.slice.id for sp in SlicePrivilege.objects.filter(user=user)]
+ slice_ids = [sp.slice.id for sp in Privilege.objects.filter(accessor_type='User',accessor_id=user.id, object_type='Slice')]
qs = SlicePlus.objects.filter(id__in=slice_ids)
return qs
@@ -231,12 +232,12 @@
except:
default_role = SliceRole.objects.get(role="default")
- slice_privs = self.sliceprivileges.all()
- slice_user_ids = [priv.user.id for priv in slice_privs]
+ slice_privs = Privilege.objects.filter(object_id=self.id, object_type='Slice', accessor_type='User')
+ slice_user_ids = [priv.accessor_id for priv in slice_privs]
for user_id in new_users:
if (user_id not in slice_user_ids):
- priv = SlicePrivilege(slice=self, user=User.objects.get(id=user_id), role=default_role)
+ priv = Privilege(object_id=self.id, accessor_id=user_id, permission='role:'+default_role, accessor_type='User', object_type='Slice')
priv.caller = self.caller
if (not noAct):
priv.save()