mirror of
https://github.com/ansible/awx.git
synced 2026-05-14 12:57:40 -02:30
AC-905 Work in progress on better filtering of inactive/deleted objects.
This commit is contained in:
@@ -14,7 +14,7 @@ __all__ = ['__version__']
|
|||||||
try:
|
try:
|
||||||
import awx.devonly
|
import awx.devonly
|
||||||
MODE = 'development'
|
MODE = 'development'
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
MODE = 'production'
|
MODE = 'production'
|
||||||
|
|
||||||
def find_commands(management_dir):
|
def find_commands(management_dir):
|
||||||
@@ -27,7 +27,7 @@ def find_commands(management_dir):
|
|||||||
continue
|
continue
|
||||||
elif f.endswith('.py') and f[:-3] not in commands:
|
elif f.endswith('.py') and f[:-3] not in commands:
|
||||||
commands.append(f[:-3])
|
commands.append(f[:-3])
|
||||||
elif f.endswith('.pyc') and f[:-4] not in commands:
|
elif f.endswith('.pyc') and f[:-4] not in commands: # pragma: no cover
|
||||||
commands.append(f[:-4])
|
commands.append(f[:-4])
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
@@ -43,7 +43,7 @@ def prepare_env():
|
|||||||
# Hide DeprecationWarnings when running in production. Need to first load
|
# Hide DeprecationWarnings when running in production. Need to first load
|
||||||
# settings to apply our filter after Django's own warnings filter.
|
# settings to apply our filter after Django's own warnings filter.
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
if not settings.DEBUG:
|
if not settings.DEBUG: # pragma: no cover
|
||||||
warnings.simplefilter('ignore', DeprecationWarning)
|
warnings.simplefilter('ignore', DeprecationWarning)
|
||||||
# Monkeypatch Django find_commands to also work with .pyc files.
|
# Monkeypatch Django find_commands to also work with .pyc files.
|
||||||
import django.core.management
|
import django.core.management
|
||||||
@@ -53,7 +53,7 @@ def prepare_env():
|
|||||||
import django.utils
|
import django.utils
|
||||||
try:
|
try:
|
||||||
import django.utils.six
|
import django.utils.six
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
import six
|
import six
|
||||||
sys.modules['django.utils.six'] = sys.modules['six']
|
sys.modules['django.utils.six'] = sys.modules['six']
|
||||||
django.utils.six = sys.modules['django.utils.six']
|
django.utils.six = sys.modules['django.utils.six']
|
||||||
@@ -61,7 +61,7 @@ def prepare_env():
|
|||||||
# Use the AWX_TEST_DATABASE_* environment variables to specify the test
|
# Use the AWX_TEST_DATABASE_* environment variables to specify the test
|
||||||
# database settings to use when management command is run as an external
|
# database settings to use when management command is run as an external
|
||||||
# program via unit tests.
|
# program via unit tests.
|
||||||
for opt in ('ENGINE', 'NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'):
|
for opt in ('ENGINE', 'NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'): # pragma: no cover
|
||||||
if os.environ.get('AWX_TEST_DATABASE_%s' % opt, None):
|
if os.environ.get('AWX_TEST_DATABASE_%s' % opt, None):
|
||||||
settings.DATABASES['default'][opt] = os.environ['AWX_TEST_DATABASE_%s' % opt]
|
settings.DATABASES['default'][opt] = os.environ['AWX_TEST_DATABASE_%s' % opt]
|
||||||
# Disable capturing all SQL queries in memory when in DEBUG mode.
|
# Disable capturing all SQL queries in memory when in DEBUG mode.
|
||||||
@@ -75,7 +75,7 @@ def manage():
|
|||||||
prepare_env()
|
prepare_env()
|
||||||
# Now run the command (or display the version).
|
# Now run the command (or display the version).
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
if len(sys.argv) >= 2 and sys.argv[1] in ('version', '--version'):
|
if len(sys.argv) >= 2 and sys.argv[1] in ('version', '--version'): # pragma: no cover
|
||||||
sys.stdout.write('%s\n' % __version__)
|
sys.stdout.write('%s\n' % __version__)
|
||||||
else:
|
else:
|
||||||
execute_from_command_line(sys.argv)
|
execute_from_command_line(sys.argv)
|
||||||
|
|||||||
@@ -143,8 +143,10 @@ class BaseSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = SortedDict()
|
res = SortedDict()
|
||||||
if getattr(obj, 'created_by', None):
|
if getattr(obj, 'created_by', None) and obj.created_by.is_active:
|
||||||
res['created_by'] = reverse('api:user_detail', args=(obj.created_by.pk,))
|
res['created_by'] = reverse('api:user_detail', args=(obj.created_by.pk,))
|
||||||
|
if getattr(obj, 'modified_by', None) and obj.modified_by.is_active:
|
||||||
|
res['modified_by'] = reverse('api:user_detail', args=(obj.modified_by.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def get_summary_fields(self, obj):
|
def get_summary_fields(self, obj):
|
||||||
@@ -154,12 +156,17 @@ class BaseSerializer(serializers.ModelSerializer):
|
|||||||
for fk, related_fields in SUMMARIZABLE_FK_FIELDS.items():
|
for fk, related_fields in SUMMARIZABLE_FK_FIELDS.items():
|
||||||
try:
|
try:
|
||||||
fkval = getattr(obj, fk, None)
|
fkval = getattr(obj, fk, None)
|
||||||
if fkval is not None:
|
if fkval is None:
|
||||||
summary_fields[fk] = SortedDict()
|
continue
|
||||||
for field in related_fields:
|
if hasattr(fkval, 'active') and not fkval.active:
|
||||||
fval = getattr(fkval, field, None)
|
continue
|
||||||
if fval is not None:
|
if hasattr(fkval, 'is_active') and not fkval.is_active:
|
||||||
summary_fields[fk][field] = fval
|
continue
|
||||||
|
summary_fields[fk] = SortedDict()
|
||||||
|
for field in related_fields:
|
||||||
|
fval = getattr(fkval, field, None)
|
||||||
|
if fval is not None:
|
||||||
|
summary_fields[fk][field] = fval
|
||||||
# Can be raised by the reverse accessor for a OneToOneField.
|
# Can be raised by the reverse accessor for a OneToOneField.
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
pass
|
pass
|
||||||
@@ -337,7 +344,7 @@ class ProjectSerializer(BaseSerializer):
|
|||||||
project_updates = reverse('api:project_updates_list', args=(obj.pk,)),
|
project_updates = reverse('api:project_updates_list', args=(obj.pk,)),
|
||||||
activity_stream = reverse('api:project_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:project_activity_stream_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
if obj.credential:
|
if obj.credential and obj.credential.active:
|
||||||
res['credential'] = reverse('api:credential_detail',
|
res['credential'] = reverse('api:credential_detail',
|
||||||
args=(obj.credential.pk,))
|
args=(obj.credential.pk,))
|
||||||
if obj.current_update:
|
if obj.current_update:
|
||||||
@@ -364,6 +371,12 @@ class ProjectSerializer(BaseSerializer):
|
|||||||
raise serializers.ValidationError('Invalid path choice')
|
raise serializers.ValidationError('Invalid path choice')
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(ProjectSerializer, self).to_native(obj)
|
||||||
|
if 'credential' in ret and (not obj.credential or not obj.credential.active):
|
||||||
|
ret['credential'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class ProjectPlaybooksSerializer(ProjectSerializer):
|
class ProjectPlaybooksSerializer(ProjectSerializer):
|
||||||
|
|
||||||
@@ -433,12 +446,19 @@ class InventorySerializer(BaseSerializerWithVariables):
|
|||||||
variable_data = reverse('api:inventory_variable_data', args=(obj.pk,)),
|
variable_data = reverse('api:inventory_variable_data', args=(obj.pk,)),
|
||||||
script = reverse('api:inventory_script_view', args=(obj.pk,)),
|
script = reverse('api:inventory_script_view', args=(obj.pk,)),
|
||||||
tree = reverse('api:inventory_tree_view', args=(obj.pk,)),
|
tree = reverse('api:inventory_tree_view', args=(obj.pk,)),
|
||||||
organization = reverse('api:organization_detail', args=(obj.organization.pk,)),
|
|
||||||
inventory_sources = reverse('api:inventory_inventory_sources_list', args=(obj.pk,)),
|
inventory_sources = reverse('api:inventory_inventory_sources_list', args=(obj.pk,)),
|
||||||
activity_stream = reverse('api:inventory_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:inventory_activity_stream_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
|
if obj.organization and obj.organization.active:
|
||||||
|
res['organization'] = reverse('api:organization_detail', args=(obj.organization.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(InventorySerializer, self).to_native(obj)
|
||||||
|
if 'organization' in ret and (not obj.organization or not obj.organization.active):
|
||||||
|
ret['organization'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class HostSerializer(BaseSerializerWithVariables):
|
class HostSerializer(BaseSerializerWithVariables):
|
||||||
|
|
||||||
@@ -458,7 +478,6 @@ class HostSerializer(BaseSerializerWithVariables):
|
|||||||
res = super(HostSerializer, self).get_related(obj)
|
res = super(HostSerializer, self).get_related(obj)
|
||||||
res.update(dict(
|
res.update(dict(
|
||||||
variable_data = reverse('api:host_variable_data', args=(obj.pk,)),
|
variable_data = reverse('api:host_variable_data', args=(obj.pk,)),
|
||||||
inventory = reverse('api:inventory_detail', args=(obj.inventory.pk,)),
|
|
||||||
groups = reverse('api:host_groups_list', args=(obj.pk,)),
|
groups = reverse('api:host_groups_list', args=(obj.pk,)),
|
||||||
all_groups = reverse('api:host_all_groups_list', args=(obj.pk,)),
|
all_groups = reverse('api:host_all_groups_list', args=(obj.pk,)),
|
||||||
job_events = reverse('api:host_job_events_list', args=(obj.pk,)),
|
job_events = reverse('api:host_job_events_list', args=(obj.pk,)),
|
||||||
@@ -466,9 +485,11 @@ class HostSerializer(BaseSerializerWithVariables):
|
|||||||
activity_stream = reverse('api:host_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:host_activity_stream_list', args=(obj.pk,)),
|
||||||
#inventory_sources = reverse('api:host_inventory_sources_list', args=(obj.pk,)),
|
#inventory_sources = reverse('api:host_inventory_sources_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
if obj.last_job:
|
if obj.inventory and obj.inventory.active:
|
||||||
|
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
||||||
|
if obj.last_job and obj.last_job.active:
|
||||||
res['last_job'] = reverse('api:job_detail', args=(obj.last_job.pk,))
|
res['last_job'] = reverse('api:job_detail', args=(obj.last_job.pk,))
|
||||||
if obj.last_job_host_summary:
|
if obj.last_job_host_summary and obj.last_job_host_summary.job.active:
|
||||||
res['last_job_host_summary'] = reverse('api:job_host_summary_detail', args=(obj.last_job_host_summary.pk,))
|
res['last_job_host_summary'] = reverse('api:job_host_summary_detail', args=(obj.last_job_host_summary.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -543,6 +564,16 @@ class HostSerializer(BaseSerializerWithVariables):
|
|||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(HostSerializer, self).to_native(obj)
|
||||||
|
if 'inventory' in ret and (not obj.inventory or not obj.inventory.active):
|
||||||
|
ret['inventory'] = None
|
||||||
|
if 'last_job' in ret and (not obj.last_job or not obj.last_job.active):
|
||||||
|
ret['last_job'] = None
|
||||||
|
if 'last_job_host_summary' in ret and (not obj.last_job_host_summary or not obj.last_job_host_summary.job.active):
|
||||||
|
ret['last_job_host_summary'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class GroupSerializer(BaseSerializerWithVariables):
|
class GroupSerializer(BaseSerializerWithVariables):
|
||||||
|
|
||||||
@@ -563,13 +594,15 @@ class GroupSerializer(BaseSerializerWithVariables):
|
|||||||
potential_children = reverse('api:group_potential_children_list', args=(obj.pk,)),
|
potential_children = reverse('api:group_potential_children_list', args=(obj.pk,)),
|
||||||
children = reverse('api:group_children_list', args=(obj.pk,)),
|
children = reverse('api:group_children_list', args=(obj.pk,)),
|
||||||
all_hosts = reverse('api:group_all_hosts_list', args=(obj.pk,)),
|
all_hosts = reverse('api:group_all_hosts_list', args=(obj.pk,)),
|
||||||
inventory = reverse('api:inventory_detail', args=(obj.inventory.pk,)),
|
|
||||||
job_events = reverse('api:group_job_events_list', args=(obj.pk,)),
|
job_events = reverse('api:group_job_events_list', args=(obj.pk,)),
|
||||||
job_host_summaries = reverse('api:group_job_host_summaries_list', args=(obj.pk,)),
|
job_host_summaries = reverse('api:group_job_host_summaries_list', args=(obj.pk,)),
|
||||||
inventory_source = reverse('api:inventory_source_detail', args=(obj.inventory_source.pk,)),
|
|
||||||
activity_stream = reverse('api:group_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:group_activity_stream_list', args=(obj.pk,)),
|
||||||
#inventory_sources = reverse('api:group_inventory_sources_list', args=(obj.pk,)),
|
#inventory_sources = reverse('api:group_inventory_sources_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
|
if obj.inventory and obj.inventory.active:
|
||||||
|
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
||||||
|
if obj.inventory_source:
|
||||||
|
res['inventory_source'] = reverse('api:inventory_source_detail', args=(obj.inventory_source.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def validate_name(self, attrs, source):
|
def validate_name(self, attrs, source):
|
||||||
@@ -578,6 +611,12 @@ class GroupSerializer(BaseSerializerWithVariables):
|
|||||||
raise serializers.ValidationError('Invalid group name')
|
raise serializers.ValidationError('Invalid group name')
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(GroupSerializer, self).to_native(obj)
|
||||||
|
if 'inventory' in ret and (not obj.inventory or not obj.inventory.active):
|
||||||
|
ret['inventory'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class GroupTreeSerializer(GroupSerializer):
|
class GroupTreeSerializer(GroupSerializer):
|
||||||
|
|
||||||
@@ -659,11 +698,11 @@ class InventorySourceSerializer(BaseSerializer):
|
|||||||
#hosts = reverse('api:inventory_source_hosts_list', args=(obj.pk,)),
|
#hosts = reverse('api:inventory_source_hosts_list', args=(obj.pk,)),
|
||||||
#groups = reverse('api:inventory_source_groups_list', args=(obj.pk,)),
|
#groups = reverse('api:inventory_source_groups_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
if obj.inventory:
|
if obj.inventory and obj.inventory.active:
|
||||||
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
||||||
if obj.group:
|
if obj.group and obj.group.active:
|
||||||
res['group'] = reverse('api:group_detail', args=(obj.group.pk,))
|
res['group'] = reverse('api:group_detail', args=(obj.group.pk,))
|
||||||
if obj.credential:
|
if obj.credential and obj.credential.active:
|
||||||
res['credential'] = reverse('api:credential_detail',
|
res['credential'] = reverse('api:credential_detail',
|
||||||
args=(obj.credential.pk,))
|
args=(obj.credential.pk,))
|
||||||
if obj.current_update:
|
if obj.current_update:
|
||||||
@@ -712,6 +751,16 @@ class InventorySourceSerializer(BaseSerializer):
|
|||||||
field_opts['rax_region_choices'] = self.opts.model.get_rax_region_choices()
|
field_opts['rax_region_choices'] = self.opts.model.get_rax_region_choices()
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(InventorySourceSerializer, self).to_native(obj)
|
||||||
|
if 'inventory' in ret and (not obj.inventory or not obj.inventory.active):
|
||||||
|
ret['inventory'] = None
|
||||||
|
if 'group' in ret and (not obj.group or not obj.group.active):
|
||||||
|
ret['group'] = None
|
||||||
|
if 'credential' in ret and (not obj.credential or not obj.credential.active):
|
||||||
|
ret['credential'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class InventoryUpdateSerializer(BaseTaskSerializer):
|
class InventoryUpdateSerializer(BaseTaskSerializer):
|
||||||
|
|
||||||
@@ -749,12 +798,19 @@ class TeamSerializer(BaseSerializer):
|
|||||||
projects = reverse('api:team_projects_list', args=(obj.pk,)),
|
projects = reverse('api:team_projects_list', args=(obj.pk,)),
|
||||||
users = reverse('api:team_users_list', args=(obj.pk,)),
|
users = reverse('api:team_users_list', args=(obj.pk,)),
|
||||||
credentials = reverse('api:team_credentials_list', args=(obj.pk,)),
|
credentials = reverse('api:team_credentials_list', args=(obj.pk,)),
|
||||||
organization = reverse('api:organization_detail', args=(obj.organization.pk,)),
|
|
||||||
permissions = reverse('api:team_permissions_list', args=(obj.pk,)),
|
permissions = reverse('api:team_permissions_list', args=(obj.pk,)),
|
||||||
activity_stream = reverse('api:team_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:team_activity_stream_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
|
if obj.organization:
|
||||||
|
res['organization'] = reverse('api:organization_detail', args=(obj.organization.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(TeamSerializer, self).to_native(obj)
|
||||||
|
if 'organization' in ret and (not obj.organization or not obj.organization.active):
|
||||||
|
ret['organization'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class PermissionSerializer(BaseSerializer):
|
class PermissionSerializer(BaseSerializer):
|
||||||
|
|
||||||
@@ -792,6 +848,18 @@ class PermissionSerializer(BaseSerializer):
|
|||||||
'assigning deployment permissions')
|
'assigning deployment permissions')
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(PermissionSerializer, self).to_native(obj)
|
||||||
|
if 'user' in ret and (not obj.user or not obj.user.is_active):
|
||||||
|
ret['user'] = None
|
||||||
|
if 'team' in ret and (not obj.team or not obj.team.active):
|
||||||
|
ret['team'] = None
|
||||||
|
if 'project' in ret and (not obj.project or not obj.project.active):
|
||||||
|
ret['project'] = None
|
||||||
|
if 'inventory' in ret and (not obj.inventory or not obj.inventory.active):
|
||||||
|
ret['inventory'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class CredentialSerializer(BaseSerializer):
|
class CredentialSerializer(BaseSerializer):
|
||||||
|
|
||||||
@@ -810,6 +878,10 @@ class CredentialSerializer(BaseSerializer):
|
|||||||
|
|
||||||
def to_native(self, obj):
|
def to_native(self, obj):
|
||||||
ret = super(CredentialSerializer, self).to_native(obj)
|
ret = super(CredentialSerializer, self).to_native(obj)
|
||||||
|
if 'user' in ret and (not obj.user or not obj.user.is_active):
|
||||||
|
ret['user'] = None
|
||||||
|
if 'team' in ret and (not obj.team or not obj.team.active):
|
||||||
|
ret['team'] = None
|
||||||
# Replace the actual encrypted value with the string $encrypted$.
|
# Replace the actual encrypted value with the string $encrypted$.
|
||||||
for field in Credential.PASSWORD_FIELDS:
|
for field in Credential.PASSWORD_FIELDS:
|
||||||
if field in ret and unicode(ret[field]).startswith('$encrypted$'):
|
if field in ret and unicode(ret[field]).startswith('$encrypted$'):
|
||||||
@@ -852,20 +924,36 @@ class JobTemplateSerializer(BaseSerializer):
|
|||||||
return {}
|
return {}
|
||||||
res = super(JobTemplateSerializer, self).get_related(obj)
|
res = super(JobTemplateSerializer, self).get_related(obj)
|
||||||
res.update(dict(
|
res.update(dict(
|
||||||
inventory = reverse('api:inventory_detail', args=(obj.inventory.pk,)),
|
jobs = reverse('api:job_template_jobs_list', args=(obj.pk,)),
|
||||||
project = reverse('api:project_detail', args=(obj.project.pk,)),
|
|
||||||
jobs = reverse('api:job_template_jobs_list', args=(obj.pk,)),
|
|
||||||
activity_stream = reverse('api:job_template_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:job_template_activity_stream_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
if obj.credential:
|
if obj.inventory and obj.inventory.active:
|
||||||
|
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
||||||
|
if obj.project and obj.project.active:
|
||||||
|
res['project'] = reverse('api:project_detail', args=(obj.project.pk,)),
|
||||||
|
if obj.credential and obj.credential.active:
|
||||||
res['credential'] = reverse('api:credential_detail', args=(obj.credential.pk,))
|
res['credential'] = reverse('api:credential_detail', args=(obj.credential.pk,))
|
||||||
if obj.cloud_credential:
|
if obj.cloud_credential and obj.cloud_credential.active:
|
||||||
res['cloud_credential'] = reverse('api:credential_detail',
|
res['cloud_credential'] = reverse('api:credential_detail',
|
||||||
args=(obj.cloud_credential.pk,))
|
args=(obj.cloud_credential.pk,))
|
||||||
if obj.host_config_key:
|
if obj.host_config_key:
|
||||||
res['callback'] = reverse('api:job_template_callback', args=(obj.pk,))
|
res['callback'] = reverse('api:job_template_callback', args=(obj.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(JobTemplateSerializer, self).to_native(obj)
|
||||||
|
if 'inventory' in ret and (not obj.inventory or not obj.inventory.active):
|
||||||
|
ret['inventory'] = None
|
||||||
|
if 'project' in ret and (not obj.project or not obj.project.active):
|
||||||
|
ret['project'] = None
|
||||||
|
if 'playbook' in ret:
|
||||||
|
ret['playbook'] = ''
|
||||||
|
if 'credential' in ret and (not obj.credential or not obj.credential.active):
|
||||||
|
ret['credential'] = None
|
||||||
|
if 'cloud_credential' in ret and (not obj.cloud_credential or not obj.cloud_credential.active):
|
||||||
|
ret['cloud_credential'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
def validate_playbook(self, attrs, source):
|
def validate_playbook(self, attrs, source):
|
||||||
project = attrs.get('project', None)
|
project = attrs.get('project', None)
|
||||||
playbook = attrs.get('playbook', '')
|
playbook = attrs.get('playbook', '')
|
||||||
@@ -894,16 +982,23 @@ class JobSerializer(BaseTaskSerializer):
|
|||||||
return {}
|
return {}
|
||||||
res = super(JobSerializer, self).get_related(obj)
|
res = super(JobSerializer, self).get_related(obj)
|
||||||
res.update(dict(
|
res.update(dict(
|
||||||
inventory = reverse('api:inventory_detail', args=(obj.inventory.pk,)),
|
|
||||||
project = reverse('api:project_detail', args=(obj.project.pk,)),
|
|
||||||
credential = reverse('api:credential_detail', args=(obj.credential.pk,)),
|
|
||||||
job_events = reverse('api:job_job_events_list', args=(obj.pk,)),
|
job_events = reverse('api:job_job_events_list', args=(obj.pk,)),
|
||||||
job_host_summaries = reverse('api:job_job_host_summaries_list', args=(obj.pk,)),
|
job_host_summaries = reverse('api:job_job_host_summaries_list', args=(obj.pk,)),
|
||||||
activity_stream = reverse('api:job_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:job_activity_stream_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
if obj.job_template:
|
if obj.job_template and obj.job_template.active:
|
||||||
res['job_template'] = reverse('api:job_template_detail', args=(obj.job_template.pk,))
|
res['job_template'] = reverse('api:job_template_detail',
|
||||||
if obj.cloud_credential:
|
args=(obj.job_template.pk,))
|
||||||
|
if obj.inventory and obj.inventory.active:
|
||||||
|
res['inventory'] = reverse('api:inventory_detail',
|
||||||
|
args=(obj.inventory.pk,))
|
||||||
|
if obj.project and obj.project.active:
|
||||||
|
res['project'] = reverse('api:project_detail',
|
||||||
|
args=(obj.project.pk,))
|
||||||
|
if obj.credential and obj.credential.active:
|
||||||
|
res['credential'] = reverse('api:credential_detail',
|
||||||
|
args=(obj.credential.pk,))
|
||||||
|
if obj.cloud_credential and obj.cloud_credential.active:
|
||||||
res['cloud_credential'] = reverse('api:credential_detail',
|
res['cloud_credential'] = reverse('api:credential_detail',
|
||||||
args=(obj.cloud_credential.pk,))
|
args=(obj.cloud_credential.pk,))
|
||||||
if obj.can_start or True:
|
if obj.can_start or True:
|
||||||
@@ -923,9 +1018,11 @@ class JobSerializer(BaseTaskSerializer):
|
|||||||
return
|
return
|
||||||
# Don't auto-populate name or description.
|
# Don't auto-populate name or description.
|
||||||
data.setdefault('job_type', job_template.job_type)
|
data.setdefault('job_type', job_template.job_type)
|
||||||
data.setdefault('inventory', job_template.inventory.pk)
|
if job_template.inventory:
|
||||||
data.setdefault('project', job_template.project.pk)
|
data.setdefault('inventory', job_template.inventory.pk)
|
||||||
data.setdefault('playbook', job_template.playbook)
|
if job_template.project:
|
||||||
|
data.setdefault('project', job_template.project.pk)
|
||||||
|
data.setdefault('playbook', job_template.playbook)
|
||||||
if job_template.credential:
|
if job_template.credential:
|
||||||
data.setdefault('credential', job_template.credential.pk)
|
data.setdefault('credential', job_template.credential.pk)
|
||||||
if job_template.cloud_credential:
|
if job_template.cloud_credential:
|
||||||
@@ -937,6 +1034,22 @@ class JobSerializer(BaseTaskSerializer):
|
|||||||
data.setdefault('job_tags', job_template.job_tags)
|
data.setdefault('job_tags', job_template.job_tags)
|
||||||
return super(JobSerializer, self).from_native(data, files)
|
return super(JobSerializer, self).from_native(data, files)
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(JobSerializer, self).to_native(obj)
|
||||||
|
if 'job_template' in ret and (not obj.job_template or not obj.job_template.active):
|
||||||
|
ret['job_template'] = None
|
||||||
|
if 'inventory' in ret and (not obj.inventory or not obj.inventory.active):
|
||||||
|
ret['inventory'] = None
|
||||||
|
if 'project' in ret and (not obj.project or not obj.project.active):
|
||||||
|
ret['project'] = None
|
||||||
|
if 'playbook' in ret:
|
||||||
|
ret['playbook'] = ''
|
||||||
|
if 'credential' in ret and (not obj.credential or not obj.credential.active):
|
||||||
|
ret['credential'] = None
|
||||||
|
if 'cloud_credential' in ret and (not obj.cloud_credential or not obj.cloud_credential.active):
|
||||||
|
ret['cloud_credential'] = None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class JobHostSummarySerializer(BaseSerializer):
|
class JobHostSummarySerializer(BaseSerializer):
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ class BaseAccess(object):
|
|||||||
return self.model.objects.none()
|
return self.model.objects.none()
|
||||||
|
|
||||||
def can_read(self, obj):
|
def can_read(self, obj):
|
||||||
return bool(obj and self.get_queryset().filter(pk=obj.pk).count())
|
return bool(obj and self.get_queryset().filter(pk=obj.pk).exists())
|
||||||
|
|
||||||
def can_add(self, data):
|
def can_add(self, data):
|
||||||
return self.user.is_superuser
|
return self.user.is_superuser
|
||||||
@@ -172,18 +172,18 @@ class UserAccess(BaseAccess):
|
|||||||
qs = self.model.objects.filter(is_active=True).distinct()
|
qs = self.model.objects.filter(is_active=True).distinct()
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
if self.user.admin_of_organizations.count():
|
if self.user.admin_of_organizations.filter(active=True).exists():
|
||||||
return qs
|
return qs
|
||||||
return qs.filter(
|
return qs.filter(
|
||||||
Q(pk=self.user.pk) |
|
Q(pk=self.user.pk) |
|
||||||
Q(organizations__in=self.user.admin_of_organizations.all()) |
|
Q(organizations__in=self.user.admin_of_organizations.filter(active=True)) |
|
||||||
Q(organizations__in=self.user.organizations.all()) |
|
Q(organizations__in=self.user.organizations.filter(active=True)) |
|
||||||
Q(teams__in=self.user.teams.all())
|
Q(teams__in=self.user.teams.filter(active=True))
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
def can_add(self, data):
|
def can_add(self, data):
|
||||||
return bool(self.user.is_superuser or
|
return bool(self.user.is_superuser or
|
||||||
self.user.admin_of_organizations.count())
|
self.user.admin_of_organizations.filter(active=True).exists())
|
||||||
|
|
||||||
def can_change(self, obj, data):
|
def can_change(self, obj, data):
|
||||||
# A user can be changed if they are themselves, or by org admins or
|
# A user can be changed if they are themselves, or by org admins or
|
||||||
@@ -195,7 +195,8 @@ class UserAccess(BaseAccess):
|
|||||||
# Admin implies changing all user fields.
|
# Admin implies changing all user fields.
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return True
|
return True
|
||||||
return bool(obj.organizations.filter(admins__in=[self.user]).count())
|
#return bool(obj.organizations.filter(active=True, admins__in=[self.user]).exists()) TODO: replace line below
|
||||||
|
return bool(obj.organizations.filter(admins__in=[self.user]).exists())
|
||||||
|
|
||||||
def can_delete(self, obj):
|
def can_delete(self, obj):
|
||||||
if obj == self.user:
|
if obj == self.user:
|
||||||
@@ -206,7 +207,8 @@ class UserAccess(BaseAccess):
|
|||||||
# cannot delete the last active superuser
|
# cannot delete the last active superuser
|
||||||
return False
|
return False
|
||||||
return bool(self.user.is_superuser or
|
return bool(self.user.is_superuser or
|
||||||
obj.organizations.filter(admins__in=[self.user]).count())
|
#obj.organizations.filter(active=True, admins__in=[self.user]).exists()) TODO: replace line below
|
||||||
|
obj.organizations.filter(admins__in=[self.user]).exists())
|
||||||
|
|
||||||
class OrganizationAccess(BaseAccess):
|
class OrganizationAccess(BaseAccess):
|
||||||
'''
|
'''
|
||||||
@@ -258,21 +260,24 @@ class InventoryAccess(BaseAccess):
|
|||||||
qs = qs.select_related('created_by', 'organization')
|
qs = qs.select_related('created_by', 'organization')
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
admin_of = qs.filter(organization__admins__in=[self.user]).distinct()
|
admin_of = qs.filter(organization__admins__in=[self.user],
|
||||||
|
#organization__active=True).distinct() TODO: enable this line
|
||||||
|
).distinct()
|
||||||
has_user_perms = qs.filter(
|
has_user_perms = qs.filter(
|
||||||
permissions__user__in=[self.user],
|
permissions__user__in=[self.user],
|
||||||
permissions__permission_type__in=allowed,
|
permissions__permission_type__in=allowed,
|
||||||
permissions__active=True,
|
permissions__active=True, # TODO: add test for this case
|
||||||
).distinct()
|
).distinct()
|
||||||
has_team_perms = qs.filter(
|
has_team_perms = qs.filter(
|
||||||
permissions__team__users__in=[self.user],
|
permissions__team__users__in=[self.user],
|
||||||
|
#permissions__team__active=True, TODO: enable this line
|
||||||
permissions__permission_type__in=allowed,
|
permissions__permission_type__in=allowed,
|
||||||
permissions__active=True,
|
permissions__active=True, # TODO: add test for this case
|
||||||
).distinct()
|
).distinct()
|
||||||
return admin_of | has_user_perms | has_team_perms
|
return admin_of | has_user_perms | has_team_perms
|
||||||
|
|
||||||
def has_permission_types(self, obj, allowed):
|
def has_permission_types(self, obj, allowed):
|
||||||
return bool(obj and self.get_queryset(allowed).filter(pk=obj.pk).count())
|
return bool(obj and self.get_queryset(allowed).filter(pk=obj.pk).exists())
|
||||||
|
|
||||||
def can_read(self, obj):
|
def can_read(self, obj):
|
||||||
return self.has_permission_types(obj, PERMISSION_TYPES_ALLOWING_INVENTORY_READ)
|
return self.has_permission_types(obj, PERMISSION_TYPES_ALLOWING_INVENTORY_READ)
|
||||||
@@ -280,7 +285,9 @@ class InventoryAccess(BaseAccess):
|
|||||||
def can_add(self, data):
|
def can_add(self, data):
|
||||||
# If no data is specified, just checking for generic add permission?
|
# If no data is specified, just checking for generic add permission?
|
||||||
if not data:
|
if not data:
|
||||||
return bool(self.user.is_superuser or self.user.admin_of_organizations.count())
|
return bool(self.user.is_superuser or
|
||||||
|
#self.user.admin_of_organizations.filter(active=True).exists()) TODO: replace line below
|
||||||
|
self.user.admin_of_organizations.all().exists())
|
||||||
# Otherwise, verify that the user has access to change the parent
|
# Otherwise, verify that the user has access to change the parent
|
||||||
# organization of this inventory.
|
# organization of this inventory.
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
@@ -360,7 +367,6 @@ class HostAccess(BaseAccess):
|
|||||||
raise PermissionDenied("license has expired")
|
raise PermissionDenied("license has expired")
|
||||||
|
|
||||||
if validation_info.get('free_instances', 0) > 0:
|
if validation_info.get('free_instances', 0) > 0:
|
||||||
# BOOKMARK
|
|
||||||
return True
|
return True
|
||||||
instances = validation_info.get('available_instances', 0)
|
instances = validation_info.get('available_instances', 0)
|
||||||
raise PermissionDenied("license range of %s instances has been exceeded" % instances)
|
raise PermissionDenied("license range of %s instances has been exceeded" % instances)
|
||||||
@@ -433,7 +439,6 @@ class GroupAccess(BaseAccess):
|
|||||||
parent_pks.add(obj.pk)
|
parent_pks.add(obj.pk)
|
||||||
child_pks = set(sub_obj.all_children.values_list('pk', flat=True))
|
child_pks = set(sub_obj.all_children.values_list('pk', flat=True))
|
||||||
child_pks.add(sub_obj.pk)
|
child_pks.add(sub_obj.pk)
|
||||||
#print parent_pks, child_pks
|
|
||||||
if parent_pks & child_pks:
|
if parent_pks & child_pks:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -518,11 +523,14 @@ class CredentialAccess(BaseAccess):
|
|||||||
qs = qs.select_related('created_by', 'user', 'team')
|
qs = qs.select_related('created_by', 'user', 'team')
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
|
#orgs_as_admin = self.user.admin_of_organizations.filter(active=True) TODO: replace line below
|
||||||
orgs_as_admin = self.user.admin_of_organizations.all()
|
orgs_as_admin = self.user.admin_of_organizations.all()
|
||||||
return qs.filter(
|
return qs.filter(
|
||||||
Q(user=self.user) |
|
Q(user=self.user) |
|
||||||
Q(user__organizations__in=orgs_as_admin) |
|
Q(user__organizations__in=orgs_as_admin) |
|
||||||
Q(user__admin_of_organizations__in=orgs_as_admin) |
|
Q(user__admin_of_organizations__in=orgs_as_admin) |
|
||||||
|
#Q(team__organization__in=orgs_as_admin, team__active=True) | TODO: replace both lines below
|
||||||
|
#Q(team__users__in=[self.user], team__active=True)
|
||||||
Q(team__organization__in=orgs_as_admin) |
|
Q(team__organization__in=orgs_as_admin) |
|
||||||
Q(team__users__in=[self.user])
|
Q(team__users__in=[self.user])
|
||||||
)
|
)
|
||||||
@@ -550,11 +558,14 @@ class CredentialAccess(BaseAccess):
|
|||||||
if obj.user:
|
if obj.user:
|
||||||
if self.user == obj.user:
|
if self.user == obj.user:
|
||||||
return True
|
return True
|
||||||
if obj.user.organizations.filter(admins__in=[self.user]).count():
|
#if obj.user.organizations.filter(active=True, admins__in=[self.user]).exists(): TODO: replace line below
|
||||||
|
if obj.user.organizations.filter(admins__in=[self.user]).exists():
|
||||||
return True
|
return True
|
||||||
if obj.user.admin_of_organizations.filter(admins__in=[self.user]).count():
|
#if obj.user.admin_of_organizations.filter(active=True, admins__in=[self.user]).exists(): TODO: replace line below
|
||||||
|
if obj.user.admin_of_organizations.filter(admins__in=[self.user]).exists():
|
||||||
return True
|
return True
|
||||||
if obj.team:
|
if obj.team:
|
||||||
|
#if self.user in obj.team.organization.admins.filter(active=True): TODO: replace line below
|
||||||
if self.user in obj.team.organization.admins.all():
|
if self.user in obj.team.organization.admins.all():
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -585,6 +596,7 @@ class TeamAccess(BaseAccess):
|
|||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
return qs.filter(
|
return qs.filter(
|
||||||
|
#Q(organization__admins__in=[self.user], organization__active=True) | TODO: replace line below
|
||||||
Q(organization__admins__in=[self.user]) |
|
Q(organization__admins__in=[self.user]) |
|
||||||
Q(users__in=[self.user])
|
Q(users__in=[self.user])
|
||||||
)
|
)
|
||||||
@@ -639,17 +651,21 @@ class ProjectAccess(BaseAccess):
|
|||||||
allowed = [PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
allowed = [PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
||||||
return qs.filter(
|
return qs.filter(
|
||||||
Q(created_by=self.user) |
|
Q(created_by=self.user) |
|
||||||
|
#Q(organizations__admins__in=[self.user], organizations__active=True) | TODO: replace 3 lines below
|
||||||
|
#Q(organizations__users__in=[self.user], organizations__active=True) |
|
||||||
|
#Q(teams__users__in=[self.user], teams__active=True) |
|
||||||
Q(organizations__admins__in=[self.user]) |
|
Q(organizations__admins__in=[self.user]) |
|
||||||
Q(organizations__users__in=[self.user]) |
|
Q(organizations__users__in=[self.user]) |
|
||||||
Q(teams__users__in=[self.user]) |
|
Q(teams__users__in=[self.user]) |
|
||||||
Q(permissions__user=self.user, permissions__permission_type__in=allowed, permissions__active=True) |
|
Q(permissions__user=self.user, permissions__permission_type__in=allowed, permissions__active=True) | # TODO: add tests for these 2 lines?
|
||||||
Q(permissions__team__users__in=[self.user], permissions__permission_type__in=allowed, permissions__active=True)
|
Q(permissions__team__users__in=[self.user], permissions__permission_type__in=allowed, permissions__active=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_add(self, data):
|
def can_add(self, data):
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return True
|
return True
|
||||||
if self.user.admin_of_organizations.count():
|
#if self.user.admin_of_organizations.filter(active=True).exists(): TODO: replace line below
|
||||||
|
if self.user.admin_of_organizations.all().exists():
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -658,7 +674,8 @@ class ProjectAccess(BaseAccess):
|
|||||||
return True
|
return True
|
||||||
if obj.created_by == self.user:
|
if obj.created_by == self.user:
|
||||||
return True
|
return True
|
||||||
if obj.organizations.filter(admins__in=[self.user]).count():
|
#if obj.organizations.filter(active=True, admins__in=[self.user]).exists(): TODO: replace line below
|
||||||
|
if obj.organizations.filter(admins__in=[self.user]).exists():
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -707,12 +724,15 @@ class PermissionAccess(BaseAccess):
|
|||||||
'project')
|
'project')
|
||||||
if self.user.is_superuser:
|
if self.user.is_superuser:
|
||||||
return qs
|
return qs
|
||||||
|
#orgs_as_admin = self.user.admin_of_organizations.filter(active=True) TODO: replace line below
|
||||||
orgs_as_admin = self.user.admin_of_organizations.all()
|
orgs_as_admin = self.user.admin_of_organizations.all()
|
||||||
return qs.filter(
|
return qs.filter(
|
||||||
Q(user__organizations__in=orgs_as_admin) |
|
Q(user__organizations__in=orgs_as_admin) |
|
||||||
Q(user__admin_of_organizations__in=orgs_as_admin) |
|
Q(user__admin_of_organizations__in=orgs_as_admin) |
|
||||||
|
#Q(team__organization__in=orgs_as_admin, team__active=True) | TODO: replace line below
|
||||||
Q(team__organization__in=orgs_as_admin) |
|
Q(team__organization__in=orgs_as_admin) |
|
||||||
Q(user=self.user) |
|
Q(user=self.user) |
|
||||||
|
#Q(team__users__in=[self.user], team__active=True) TODO: replace line below
|
||||||
Q(team__users__in=[self.user])
|
Q(team__users__in=[self.user])
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -802,7 +822,9 @@ class JobTemplateAccess(BaseAccess):
|
|||||||
credential_qs = self.user.get_queryset(Credential)
|
credential_qs = self.user.get_queryset(Credential)
|
||||||
base_qs = qs.filter(
|
base_qs = qs.filter(
|
||||||
Q(credential__in=credential_qs) | Q(credential__isnull=True),
|
Q(credential__in=credential_qs) | Q(credential__isnull=True),
|
||||||
|
Q(cloud_credential__in=credential_qs) | Q(cloud_credential__isnull=True),
|
||||||
)
|
)
|
||||||
|
# FIXME: Check active status on related objects!
|
||||||
org_admin_qs = base_qs.filter(
|
org_admin_qs = base_qs.filter(
|
||||||
project__organizations__admins__in=[self.user]
|
project__organizations__admins__in=[self.user]
|
||||||
)
|
)
|
||||||
@@ -821,13 +843,17 @@ class JobTemplateAccess(BaseAccess):
|
|||||||
|
|
||||||
def can_read(self, obj):
|
def can_read(self, obj):
|
||||||
# you can only see the job templates that you have permission to launch.
|
# you can only see the job templates that you have permission to launch.
|
||||||
data = dict(
|
data = {
|
||||||
inventory = obj.inventory.pk,
|
'job_type': obj.job_type,
|
||||||
project = obj.project.pk,
|
}
|
||||||
job_type = obj.job_type,
|
if obj.inventory and obj.inventory.pk:
|
||||||
)
|
data['inventory'] = obj.inventory.pk
|
||||||
|
if obj.project and obj.project.pk:
|
||||||
|
data['project'] = obj.project.pk
|
||||||
if obj.credential:
|
if obj.credential:
|
||||||
data['credential'] = obj.credential.pk
|
data['credential'] = obj.credential.pk
|
||||||
|
if obj.cloud_credential:
|
||||||
|
data['cloud_credential'] = obj.cloud_credential.pk
|
||||||
return self.can_add(data)
|
return self.can_add(data)
|
||||||
|
|
||||||
def can_add(self, data):
|
def can_add(self, data):
|
||||||
@@ -850,6 +876,14 @@ class JobTemplateAccess(BaseAccess):
|
|||||||
if not self.user.can_access(Credential, 'read', credential):
|
if not self.user.can_access(Credential, 'read', credential):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# If a cloud credential is provided, the user should have read access.
|
||||||
|
cloud_credential_pk = get_pk_from_dict(data, 'cloud_credential')
|
||||||
|
if cloud_credential_pk:
|
||||||
|
cloud_credential = get_object_or_400(Credential,
|
||||||
|
pk=cloud_credential_pk)
|
||||||
|
if not self.user.can_access(Credential, 'read', cloud_credential):
|
||||||
|
return False
|
||||||
|
|
||||||
# Check that the given inventory ID is valid.
|
# Check that the given inventory ID is valid.
|
||||||
inventory_pk = get_pk_from_dict(data, 'inventory')
|
inventory_pk = get_pk_from_dict(data, 'inventory')
|
||||||
inventory = get_object_or_400(Inventory, pk=inventory_pk)
|
inventory = get_object_or_400(Inventory, pk=inventory_pk)
|
||||||
@@ -1004,7 +1038,7 @@ class JobEventAccess(BaseAccess):
|
|||||||
'host', 'parent')
|
'host', 'parent')
|
||||||
qs = qs.prefetch_related('hosts', 'children')
|
qs = qs.prefetch_related('hosts', 'children')
|
||||||
|
|
||||||
# Filter certain "internal" events generating by async polling.
|
# Filter certain "internal" events generated by async polling.
|
||||||
qs = qs.exclude(event__in=('runner_on_ok', 'runner_on_failed'),
|
qs = qs.exclude(event__in=('runner_on_ok', 'runner_on_failed'),
|
||||||
event_data__icontains='"ansible_job_id": "',
|
event_data__icontains='"ansible_job_id": "',
|
||||||
event_data__contains='"module_name": "async_status"')
|
event_data__contains='"module_name": "async_status"')
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ class UsersTest(BaseTest):
|
|||||||
self.post(url, expect=400, data=new_user, auth=self.get_super_credentials())
|
self.post(url, expect=400, data=new_user, auth=self.get_super_credentials())
|
||||||
self.post(url, expect=201, data=new_user2, auth=self.get_normal_credentials())
|
self.post(url, expect=201, data=new_user2, auth=self.get_normal_credentials())
|
||||||
self.post(url, expect=400, data=new_user2, auth=self.get_normal_credentials())
|
self.post(url, expect=400, data=new_user2, auth=self.get_normal_credentials())
|
||||||
|
# Normal user cannot add users after his org is marked inactive.
|
||||||
|
self.organizations[0].mark_inactive()
|
||||||
|
new_user3 = dict(username='blippy3')
|
||||||
|
self.post(url, expect=403, data=new_user3, auth=self.get_normal_credentials())
|
||||||
|
|
||||||
def test_auth_token_login(self):
|
def test_auth_token_login(self):
|
||||||
auth_token_url = reverse('api:auth_token_view')
|
auth_token_url = reverse('api:auth_token_view')
|
||||||
@@ -230,8 +234,15 @@ class UsersTest(BaseTest):
|
|||||||
# Normal user is an org admin, can see all users.
|
# Normal user is an org admin, can see all users.
|
||||||
data2 = self.get(url, expect=200, auth=self.get_normal_credentials())
|
data2 = self.get(url, expect=200, auth=self.get_normal_credentials())
|
||||||
self.assertEquals(data2['count'], 4)
|
self.assertEquals(data2['count'], 4)
|
||||||
|
# Other use can only see users in his org.
|
||||||
data1 = self.get(url, expect=200, auth=self.get_other_credentials())
|
data1 = self.get(url, expect=200, auth=self.get_other_credentials())
|
||||||
self.assertEquals(data1['count'], 2)
|
self.assertEquals(data1['count'], 2)
|
||||||
|
# Normal user can no longer see all users after the organization he
|
||||||
|
# admins is marked inactive, nor can he see any other users that were
|
||||||
|
# in that org, so he only sees himself.
|
||||||
|
self.organizations[0].mark_inactive()
|
||||||
|
data3 = self.get(url, expect=200, auth=self.get_normal_credentials())
|
||||||
|
self.assertEquals(data3['count'], 1)
|
||||||
|
|
||||||
def test_super_user_can_delete_a_user_but_only_marked_inactive(self):
|
def test_super_user_can_delete_a_user_but_only_marked_inactive(self):
|
||||||
user_pk = self.normal_django_user.pk
|
user_pk = self.normal_django_user.pk
|
||||||
|
|||||||
Reference in New Issue
Block a user