mirror of
https://github.com/ansible/awx.git
synced 2026-03-10 14:09:28 -02:30
make user_capabilities validation front-loaded, try to run tests
This commit is contained in:
@@ -209,6 +209,8 @@ class ProjectUpdatePermission(ModelAccessPermission):
|
|||||||
|
|
||||||
class UserPermission(ModelAccessPermission):
|
class UserPermission(ModelAccessPermission):
|
||||||
def check_post_permissions(self, request, view, obj=None):
|
def check_post_permissions(self, request, view, obj=None):
|
||||||
if request.user.is_superuser:
|
if not request.data:
|
||||||
|
return request.user.admin_of_organizations.exists()
|
||||||
|
elif request.user.is_superuser:
|
||||||
return True
|
return True
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ class BaseSerializer(serializers.ModelSerializer):
|
|||||||
if hasattr(self, 'show_capabilities'):
|
if hasattr(self, 'show_capabilities'):
|
||||||
view = self.context.get('view', None)
|
view = self.context.get('view', None)
|
||||||
parent_obj = None
|
parent_obj = None
|
||||||
if hasattr(view, 'parent_model'):
|
if view and hasattr(view, 'parent_model'):
|
||||||
parent_obj = view.get_parent_object()
|
parent_obj = view.get_parent_object()
|
||||||
if view and view.request and view.request.user:
|
if view and view.request and view.request.user:
|
||||||
user_capabilities = get_user_capabilities(
|
user_capabilities = get_user_capabilities(
|
||||||
|
|||||||
@@ -220,6 +220,8 @@ class BaseAccess(object):
|
|||||||
raise LicenseForbids("Features not found in active license.")
|
raise LicenseForbids("Features not found in active license.")
|
||||||
|
|
||||||
def get_user_capabilities(self, obj, method_list=[], parent_obj=None):
|
def get_user_capabilities(self, obj, method_list=[], parent_obj=None):
|
||||||
|
if obj is None:
|
||||||
|
return {}
|
||||||
user_capabilities = {}
|
user_capabilities = {}
|
||||||
|
|
||||||
# Custom ordering to loop through methods so we can reuse earlier calcs
|
# Custom ordering to loop through methods so we can reuse earlier calcs
|
||||||
@@ -227,6 +229,17 @@ class BaseAccess(object):
|
|||||||
if display_method not in method_list:
|
if display_method not in method_list:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Validation consistency checks
|
||||||
|
if display_method == 'copy' and isinstance(obj, JobTemplate):
|
||||||
|
validation_errors, resources_needed_to_start = obj.resource_validation_data()
|
||||||
|
if validation_errors:
|
||||||
|
user_capabilities[display_method] = False
|
||||||
|
continue
|
||||||
|
elif display_method == 'start' and isinstance(obj, Group):
|
||||||
|
if obj.inventory_source and not obj.inventory_source._can_update():
|
||||||
|
user_capabilities[display_method] = False
|
||||||
|
continue
|
||||||
|
|
||||||
# Grab the answer from the cache, if available
|
# Grab the answer from the cache, if available
|
||||||
if hasattr(obj, 'capabilities_cache') and display_method in obj.capabilities_cache:
|
if hasattr(obj, 'capabilities_cache') and display_method in obj.capabilities_cache:
|
||||||
user_capabilities[display_method] = obj.capabilities_cache[display_method]
|
user_capabilities[display_method] = obj.capabilities_cache[display_method]
|
||||||
@@ -243,22 +256,21 @@ class BaseAccess(object):
|
|||||||
method = display_method
|
method = display_method
|
||||||
|
|
||||||
# Shortcuts in certain cases by deferring to earlier property
|
# Shortcuts in certain cases by deferring to earlier property
|
||||||
if display_method == 'schedule' and 'edit' in user_capabilities:
|
if display_method == 'schedule':
|
||||||
user_capabilities['schedule'] = user_capabilities['edit']
|
user_capabilities['schedule'] = user_capabilities['edit']
|
||||||
continue
|
continue
|
||||||
elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob)):
|
elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob)):
|
||||||
user_capabilities['delete'] = user_capabilities['edit']
|
user_capabilities['delete'] = user_capabilities['edit']
|
||||||
continue
|
continue
|
||||||
if display_method == 'copy' and isinstance(obj, JobTemplate):
|
elif display_method == 'copy' and isinstance(obj, (Group, Host)):
|
||||||
validation_errors, resources_needed_to_start = obj.resource_validation_data()
|
user_capabilities['copy'] = user_capabilities['edit']
|
||||||
if validation_errors:
|
continue
|
||||||
user_capabilities['copy'] = False
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Preprocessing before the access method is called
|
# Preprocessing before the access method is called
|
||||||
data = {}
|
data = {}
|
||||||
if method == 'add' and isinstance(obj, JobTemplate):
|
if method == 'add':
|
||||||
data['reference_obj'] = obj
|
if isinstance(obj, JobTemplate):
|
||||||
|
data['reference_obj'] = obj
|
||||||
|
|
||||||
# Compute permission
|
# Compute permission
|
||||||
access_method = getattr(self, "can_%s" % method)
|
access_method = getattr(self, "can_%s" % method)
|
||||||
@@ -599,7 +611,7 @@ class GroupAccess(BaseAccess):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def can_start(self, obj):
|
def can_start(self, obj):
|
||||||
# Used as another alias to inventory_source start access
|
# Used as another alias to inventory_source start access for user_capabilities
|
||||||
if obj and obj.inventory_source:
|
if obj and obj.inventory_source:
|
||||||
return self.user.can_access(InventorySource, 'start', obj.inventory_source)
|
return self.user.can_access(InventorySource, 'start', obj.inventory_source)
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ def mk_instance(persisted=True):
|
|||||||
if not persisted:
|
if not persisted:
|
||||||
raise RuntimeError('creating an Instance requires persisted=True')
|
raise RuntimeError('creating an Instance requires persisted=True')
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
return Instance.objects.get_or_create(uuid=settings.SYSTEM_UUID, primary=True, hostname="instance.example.org")
|
return Instance.objects.get_or_create(uuid=settings.SYSTEM_UUID, hostname="instance.example.org")
|
||||||
|
|
||||||
|
|
||||||
def mk_organization(name, description=None, persisted=True):
|
def mk_organization(name, description=None, persisted=True):
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ from django.test.client import RequestFactory
|
|||||||
|
|
||||||
from awx.main.models.jobs import JobTemplate
|
from awx.main.models.jobs import JobTemplate
|
||||||
from awx.main.models import Role, Group
|
from awx.main.models import Role, Group
|
||||||
from awx.main.access import access_registry
|
from awx.main.access import (
|
||||||
|
access_registry,
|
||||||
|
get_user_capabilities
|
||||||
|
)
|
||||||
from awx.main.utils import cache_list_capabilities
|
from awx.main.utils import cache_list_capabilities
|
||||||
from awx.api.serializers import JobTemplateSerializer
|
from awx.api.serializers import JobTemplateSerializer
|
||||||
|
|
||||||
@@ -300,3 +303,21 @@ def test_prefetch_jt_copy_capability(job_template, project, inventory, machine_c
|
|||||||
]}], JobTemplate, rando)
|
]}], JobTemplate, rando)
|
||||||
assert qs[0].capabilities_cache == {'copy': True}
|
assert qs[0].capabilities_cache == {'copy': True}
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_group_update_capabilities_possible(group, inventory_source, admin_user):
|
||||||
|
group.inventory_source = inventory_source
|
||||||
|
group.save()
|
||||||
|
|
||||||
|
capabilities = get_user_capabilities(admin_user, group, method_list=['start'])
|
||||||
|
assert capabilities['start']
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_group_update_capabilities_impossible(group, inventory_source, admin_user):
|
||||||
|
inventory_source.source = ""
|
||||||
|
inventory_source.save()
|
||||||
|
group.inventory_source = inventory_source
|
||||||
|
group.save()
|
||||||
|
|
||||||
|
capabilities = get_user_capabilities(admin_user, group, method_list=['start'])
|
||||||
|
assert not capabilities['start']
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ def user_project(user):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def instance(settings):
|
def instance(settings):
|
||||||
return Instance.objects.create(uuid=settings.SYSTEM_UUID, primary=True, hostname="instance.example.org")
|
return Instance.objects.create(uuid=settings.SYSTEM_UUID, hostname="instance.example.org")
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def organization(instance):
|
def organization(instance):
|
||||||
|
|||||||
Reference in New Issue
Block a user