mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
ensure manually modifying hosts adhears to license
* Super user or not, don't allow adding NOR editing (changing the name of) a host to exceed the host license count.
This commit is contained in:
parent
7c4b77284e
commit
e09a0fb886
@ -4,9 +4,6 @@
|
||||
# Python
|
||||
import logging
|
||||
|
||||
# Django
|
||||
from django.http import Http404
|
||||
|
||||
# Django REST Framework
|
||||
from rest_framework.exceptions import MethodNotAllowed, PermissionDenied
|
||||
from rest_framework import permissions
|
||||
@ -19,7 +16,8 @@ from awx.main.utils import get_object_or_400
|
||||
logger = logging.getLogger('awx.api.permissions')
|
||||
|
||||
__all__ = ['ModelAccessPermission', 'JobTemplateCallbackPermission',
|
||||
'TaskPermission', 'ProjectUpdatePermission', 'UserPermission']
|
||||
'TaskPermission', 'ProjectUpdatePermission', 'UserPermission',
|
||||
'HostPermission',]
|
||||
|
||||
|
||||
class ModelAccessPermission(permissions.BasePermission):
|
||||
@ -96,13 +94,6 @@ class ModelAccessPermission(permissions.BasePermission):
|
||||
method based on the request method.
|
||||
'''
|
||||
|
||||
# Check that obj (if given) is active, otherwise raise a 404.
|
||||
active = getattr(obj, 'active', getattr(obj, 'is_active', True))
|
||||
if callable(active):
|
||||
active = active()
|
||||
if not active:
|
||||
raise Http404()
|
||||
|
||||
# Don't allow anonymous users. 401, not 403, hence no raised exception.
|
||||
if not request.user or request.user.is_anonymous():
|
||||
return False
|
||||
@ -216,3 +207,25 @@ class UserPermission(ModelAccessPermission):
|
||||
elif request.user.is_superuser:
|
||||
return True
|
||||
raise PermissionDenied()
|
||||
|
||||
|
||||
class HostPermission(ModelAccessPermission):
|
||||
'''
|
||||
Allow super super for all operations that don't add or update data.
|
||||
Allow the request to flow through access.py so that even a super-user can't
|
||||
violate the license host count restriction.
|
||||
'''
|
||||
|
||||
def check_options_permissions(self, request, view, obj=None):
|
||||
view.always_allow_superuser = True
|
||||
return super(HostPermission, self).check_options_permissions(request, view, obj)
|
||||
|
||||
def check_head_permissions(self, request, view, obj=None):
|
||||
view.always_allow_superuser = True
|
||||
return super(HostPermission, self).check_head_permissions(request, view, obj)
|
||||
|
||||
def check_get_permissions(self, request, view, obj=None):
|
||||
view.always_allow_superuser = True
|
||||
return super(HostPermission, self).check_get_permissions(request, view, obj)
|
||||
|
||||
|
||||
|
||||
@ -1684,8 +1684,10 @@ class HostList(ListCreateAPIView):
|
||||
|
||||
class HostDetail(RetrieveUpdateDestroyAPIView):
|
||||
|
||||
always_allow_superuser = False
|
||||
model = Host
|
||||
serializer_class = HostSerializer
|
||||
permission_classes = (HostPermission,)
|
||||
|
||||
|
||||
class InventoryHostsList(SubListCreateAttachDetachAPIView):
|
||||
|
||||
@ -285,7 +285,7 @@ class BaseAccess(object):
|
||||
|
||||
return True # User has access to both, permission check passed
|
||||
|
||||
def check_license(self, add_host=False, feature=None, check_expiration=True):
|
||||
def check_license(self, add_host_name=None, feature=None, check_expiration=True):
|
||||
validation_info = TaskEnhancer().validate_enhancements()
|
||||
if ('test' in sys.argv or 'py.test' in sys.argv[0] or 'jenkins' in sys.argv) and not os.environ.get('SKIP_LICENSE_FIXUP_FOR_TEST', ''):
|
||||
validation_info['free_instances'] = 99999999
|
||||
@ -299,11 +299,14 @@ class BaseAccess(object):
|
||||
|
||||
free_instances = validation_info.get('free_instances', 0)
|
||||
available_instances = validation_info.get('available_instances', 0)
|
||||
if add_host and free_instances == 0:
|
||||
raise PermissionDenied(_("License count of %s instances has been reached.") % available_instances)
|
||||
elif add_host and free_instances < 0:
|
||||
raise PermissionDenied(_("License count of %s instances has been exceeded.") % available_instances)
|
||||
elif not add_host and free_instances < 0:
|
||||
|
||||
if add_host_name:
|
||||
host_exists = Host.objects.filter(name=add_host_name).exists()
|
||||
if not host_exists and free_instances == 0:
|
||||
raise PermissionDenied(_("License count of %s instances has been reached.") % available_instances)
|
||||
elif not host_exists and free_instances < 0:
|
||||
raise PermissionDenied(_("License count of %s instances has been exceeded.") % available_instances)
|
||||
elif not add_host_name and free_instances < 0:
|
||||
raise PermissionDenied(_("Host count exceeds available instances."))
|
||||
|
||||
if feature is not None:
|
||||
@ -611,7 +614,7 @@ class HostAccess(BaseAccess):
|
||||
return False
|
||||
|
||||
# Check to see if we have enough licenses
|
||||
self.check_license(add_host=True)
|
||||
self.check_license(add_host_name=data['name'])
|
||||
return True
|
||||
|
||||
def can_change(self, obj, data):
|
||||
@ -619,6 +622,11 @@ class HostAccess(BaseAccess):
|
||||
inventory_pk = get_pk_from_dict(data, 'inventory')
|
||||
if obj and inventory_pk and obj.inventory.pk != inventory_pk:
|
||||
raise PermissionDenied(_('Unable to change inventory on a host.'))
|
||||
|
||||
# Prevent renaming a host that might exceed license count
|
||||
if 'name' in data:
|
||||
self.check_license(add_host_name=data['name'])
|
||||
|
||||
# Checks for admin or change permission on inventory, controls whether
|
||||
# the user can edit variable data.
|
||||
return obj and self.user in obj.inventory.admin_role
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user