Replaced our 'Resource' table with a GenericForeignKey in RolePermission

This commit is contained in:
Akita Noek
2016-03-09 10:12:05 -05:00
parent e9c3d98a44
commit 9aae2979d9
10 changed files with 239 additions and 347 deletions

View File

@@ -1,6 +1,8 @@
# Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved.
import traceback
# Django
from django.db import connection
from django.db.models.signals import (
@@ -23,10 +25,10 @@ from django.db.transaction import TransactionManagementError
# AWX
from awx.main.models.rbac import Resource, RolePermission, Role
from awx.main.models.rbac import RolePermission, Role
__all__ = ['AutoOneToOneField', 'ImplicitResourceField', 'ImplicitRoleField']
__all__ = ['AutoOneToOneField', 'ImplicitRoleField']
# Based on AutoOneToOneField from django-annoying:
@@ -59,53 +61,6 @@ class AutoOneToOneField(models.OneToOneField):
class ResourceFieldDescriptor(ReverseSingleRelatedObjectDescriptor):
"""Descriptor for access to the object from its related class."""
def __init__(self, *args, **kwargs):
super(ResourceFieldDescriptor, self).__init__(*args, **kwargs)
def __get__(self, instance, instance_type=None):
resource = super(ResourceFieldDescriptor, self).__get__(instance, instance_type)
if resource:
return resource
if connection.needs_rollback:
raise TransactionManagementError('Current transaction has failed, cannot create implicit resource')
resource = Resource.objects.create(content_object=instance)
setattr(instance, self.field.name, resource)
if instance.pk:
instance.save(update_fields=[self.field.name,])
return resource
class ImplicitResourceField(models.ForeignKey):
"""Creates an associated resource object if one doesn't already exist"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('to', 'Resource')
kwargs.setdefault('related_name', '+')
kwargs.setdefault('null', 'True')
super(ImplicitResourceField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
super(ImplicitResourceField, self).contribute_to_class(cls, name)
setattr(cls, self.name, ResourceFieldDescriptor(self))
post_save.connect(self._post_save, cls, True)
post_delete.connect(self._post_delete, cls, True)
def _post_save(self, instance, *args, **kwargs):
# Ensures our resource object exists and that it's content_object
# points back to our hosting instance.
this_resource = getattr(instance, self.name)
if not this_resource.object_id:
this_resource.content_object = instance
this_resource.save()
def _post_delete(self, instance, *args, **kwargs):
getattr(instance, self.name).delete()
def resolve_role_field(obj, field):
ret = []
@@ -153,9 +108,13 @@ class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor):
if connection.needs_rollback:
raise TransactionManagementError('Current transaction has failed, cannot create implicit role')
role = Role.objects.create(name=self.role_name, description=self.role_description, content_object=instance)
if self.parent_role:
role = Role.objects.create(name=self.role_name, description=self.role_description, content_object=instance)
setattr(instance, self.field.name, role)
if instance.pk:
instance.save(update_fields=[self.field.name,])
if self.parent_role:
# Add all non-null parent roles as parents
paths = self.parent_role if type(self.parent_role) is list else [self.parent_role]
for path in paths:
@@ -165,14 +124,11 @@ class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor):
parents = resolve_role_field(instance, path)
for parent in parents:
role.parents.add(parent)
setattr(instance, self.field.name, role)
if instance.pk:
instance.save(update_fields=[self.field.name,])
if self.permissions is not None:
permissions = RolePermission(
role=role,
resource=instance.resource
resource=instance
)
if 'all' in self.permissions and self.permissions['all']:
@@ -289,48 +245,29 @@ class ImplicitRoleField(models.ForeignKey):
def _post_init(self, instance, *args, **kwargs):
if not self.parent_role:
return
#if not hasattr(instance, self.name):
# getattr(instance, self.name)
if not instance.pk:
return
self._calc_original_parents(instance)
def _calc_original_parents(self, instance):
if not hasattr(self, '__original_parent_roles'):
setattr(self, '__original_parent_roles', []) # do not just self.__original_parent_roles=[], it's not the same here
paths = self.parent_role if type(self.parent_role) is list else [self.parent_role]
all_parents = set()
original_parent_roles = set()
for path in paths:
if path.startswith("singleton:"):
parents = [Role.singleton(path[10:])]
else:
parents = resolve_role_field(instance, path)
for parent in parents:
all_parents.add(parent)
#role.parents.add(parent)
self.__original_parent_roles = all_parents
original_parent_roles.add(parent)
setattr(self, '__original_parent_roles', original_parent_roles)
'''
field_names = self.parent_role
if type(field_names) is not list:
field_names = [field_names]
self.__original_values = {}
for field_name in field_names:
if field_name.startswith('singleton:'):
continue
first_field_name = field_name.split('.')[0]
self.__original_values[first_field_name] = getattr(instance, first_field_name)
'''
else:
print('WE DO NEED THIS')
pass
def _post_save(self, instance, *args, **kwargs):
def _post_save(self, instance, created, *args, **kwargs):
# Ensure that our field gets initialized after our first save
this_role = getattr(instance, self.name)
if not this_role.object_id:
# Ensure our ref back to our instance is set. This will not be set the
# first time the object is saved because we create the role in our _post_init
# but that happens before an id for the instance has been set (because it
# hasn't been saved yet!). Now that everything has an id, we patch things
# so the role references the instance.
this_role.content_object = instance
this_role.save()
# As object relations change, the role hierarchy might also change if the relations
# that changed were referenced in our magic parent_role field. This code synchronizes
@@ -338,8 +275,12 @@ class ImplicitRoleField(models.ForeignKey):
if not self.parent_role:
return
if created:
self._calc_original_parents(instance)
return
paths = self.parent_role if type(self.parent_role) is list else [self.parent_role]
original_parents = self.__original_parent_roles
original_parents = getattr(self, '__original_parent_roles')
new_parents = set()
for path in paths:
if path.startswith("singleton:"):
@@ -356,7 +297,7 @@ class ImplicitRoleField(models.ForeignKey):
this_role.parents.add(role)
Role.unpause_role_ancestor_rebuilding()
self.__original_parent_roles = new_parents
setattr(self, '__original_parent_roles', new_parents)
def _post_delete(self, instance, *args, **kwargs):
this_role = getattr(instance, self.name)