Merge pull request #363 from AlanCoding/org_smart_inv

backend of org-scoped smart inventories
This commit is contained in:
Alan Rominger
2017-08-29 20:51:03 -04:00
committed by GitHub
5 changed files with 45 additions and 3 deletions

View File

@@ -602,9 +602,20 @@ class InventoryAccess(BaseAccess):
@check_superuser @check_superuser
def can_admin(self, obj, data): def can_admin(self, obj, data):
# Host filter may only be modified by org admin level
org_admin_mandatory = False
new_host_filter = data.get('host_filter', None) if data else None
if new_host_filter == '': # Provision for field 'blank' behavior
new_host_filter = None
if new_host_filter != obj.host_filter:
org_admin_mandatory = True
# Verify that the user has access to the new organization if moving an # Verify that the user has access to the new organization if moving an
# inventory to a new organization. Otherwise, just check for admin permission. # inventory to a new organization. Otherwise, just check for admin permission.
return self.check_related('organization', Organization, data, obj=obj) and self.user in obj.admin_role return (
self.check_related('organization', Organization, data, obj=obj,
mandatory=org_admin_mandatory) and
self.user in obj.admin_role
)
@check_superuser @check_superuser
def can_update(self, obj): def can_update(self, obj):

View File

@@ -37,6 +37,8 @@ class HostManager(models.Manager):
hasattr(self.instance, 'kind')): hasattr(self.instance, 'kind')):
if self.instance.kind == 'smart' and self.instance.host_filter is not None: if self.instance.kind == 'smart' and self.instance.host_filter is not None:
q = SmartFilter.query_from_string(self.instance.host_filter) q = SmartFilter.query_from_string(self.instance.host_filter)
if self.instance.organization_id:
q = q.filter(inventory__organization=self.instance.organization_id)
# If we are using host_filters, disable the core_filters, this allows # If we are using host_filters, disable the core_filters, this allows
# us to access all of the available Host entries, not just the ones associated # us to access all of the available Host entries, not just the ones associated
# with a specific FK/relation. # with a specific FK/relation.

View File

@@ -10,6 +10,7 @@ from awx.main.models import (
InventorySource, InventorySource,
InventoryUpdate, InventoryUpdate,
) )
from awx.main.utils.filters import SmartFilter
@pytest.mark.django_db @pytest.mark.django_db
@@ -109,3 +110,17 @@ class TestHostManager:
organization=organization, organization=organization,
host_filter='inventory_sources__source=ec2') host_filter='inventory_sources__source=ec2')
assert len(smart_inventory.hosts.all()) == 0 assert len(smart_inventory.hosts.all()) == 0
def test_host_distinctness(self, setup_inventory_groups, organization):
"""
two criteria would both yield the same host, check that we only get 1 copy here
"""
assert (
list(SmartFilter.query_from_string('name=single_host or name__startswith=single_')) ==
[Host.objects.get(name='single_host')]
)
# Things we can not easily test due to SQLite backend:
# 2 organizations with host of same name only has 1 entry in smart inventory
# smart inventory in 1 organization does not include host from another
# smart inventory correctly returns hosts in filter in same organization

View File

@@ -174,3 +174,17 @@ def test_inventory_source_org_admin_schedule_access(org_admin, inventory_source)
assert access.get_queryset() assert access.get_queryset()
assert access.can_read(schedule) assert access.can_read(schedule)
assert access.can_change(schedule, {'rrule': 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2'}) assert access.can_change(schedule, {'rrule': 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2'})
@pytest.fixture
def smart_inventory(organization):
return organization.inventories.create(name="smart-inv", kind="smart")
@pytest.mark.django_db
class TestSmartInventory:
def test_host_filter_edit(self, smart_inventory, rando, org_admin):
assert InventoryAccess(org_admin).can_admin(smart_inventory, {'host_filter': 'search=foo'})
smart_inventory.admin_role.members.add(rando)
assert not InventoryAccess(rando).can_admin(smart_inventory, {'host_filter': 'search=foo'})

View File

@@ -220,8 +220,8 @@ class TestHostInsights():
class TestInventoryHostsList(object): class TestInventoryHostsList(object):
def test_host_list_smart_inventory(self, mocker): def test_host_list_smart_inventory(self, mocker):
Inventory = namedtuple('Inventory', ['kind', 'host_filter', 'hosts']) Inventory = namedtuple('Inventory', ['kind', 'host_filter', 'hosts', 'organization_id'])
obj = Inventory(kind='smart', host_filter='localhost', hosts=HostManager()) obj = Inventory(kind='smart', host_filter='localhost', hosts=HostManager(), organization_id=None)
obj.hosts.instance = obj obj.hosts.instance = obj
with mock.patch.object(InventoryHostsList, 'get_parent_object', return_value=obj): with mock.patch.object(InventoryHostsList, 'get_parent_object', return_value=obj):