mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 01:21:21 +03:00
AC-905 Work in progress on better filtering of inactive/deleted objects.
This commit is contained in:
parent
f579dbd72d
commit
9807477944
@ -14,7 +14,7 @@ __all__ = ['__version__']
|
||||
try:
|
||||
import awx.devonly
|
||||
MODE = 'development'
|
||||
except ImportError:
|
||||
except ImportError: # pragma: no cover
|
||||
MODE = 'production'
|
||||
|
||||
def find_commands(management_dir):
|
||||
@ -27,7 +27,7 @@ def find_commands(management_dir):
|
||||
continue
|
||||
elif f.endswith('.py') and f[:-3] not in commands:
|
||||
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])
|
||||
except OSError:
|
||||
pass
|
||||
@ -43,7 +43,7 @@ def prepare_env():
|
||||
# Hide DeprecationWarnings when running in production. Need to first load
|
||||
# settings to apply our filter after Django's own warnings filter.
|
||||
from django.conf import settings
|
||||
if not settings.DEBUG:
|
||||
if not settings.DEBUG: # pragma: no cover
|
||||
warnings.simplefilter('ignore', DeprecationWarning)
|
||||
# Monkeypatch Django find_commands to also work with .pyc files.
|
||||
import django.core.management
|
||||
@ -53,7 +53,7 @@ def prepare_env():
|
||||
import django.utils
|
||||
try:
|
||||
import django.utils.six
|
||||
except ImportError:
|
||||
except ImportError: # pragma: no cover
|
||||
import six
|
||||
sys.modules['django.utils.six'] = sys.modules['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
|
||||
# database settings to use when management command is run as an external
|
||||
# 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):
|
||||
settings.DATABASES['default'][opt] = os.environ['AWX_TEST_DATABASE_%s' % opt]
|
||||
# Disable capturing all SQL queries in memory when in DEBUG mode.
|
||||
@ -75,7 +75,7 @@ def manage():
|
||||
prepare_env()
|
||||
# Now run the command (or display the version).
|
||||
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__)
|
||||
else:
|
||||
execute_from_command_line(sys.argv)
|
||||
|
@ -143,8 +143,10 @@ class BaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
def get_related(self, obj):
|
||||
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,))
|
||||
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
|
||||
|
||||
def get_summary_fields(self, obj):
|
||||
@ -154,12 +156,17 @@ class BaseSerializer(serializers.ModelSerializer):
|
||||
for fk, related_fields in SUMMARIZABLE_FK_FIELDS.items():
|
||||
try:
|
||||
fkval = getattr(obj, fk, None)
|
||||
if fkval is not None:
|
||||
summary_fields[fk] = SortedDict()
|
||||
for field in related_fields:
|
||||
fval = getattr(fkval, field, None)
|
||||
if fval is not None:
|
||||
summary_fields[fk][field] = fval
|
||||
if fkval is None:
|
||||
continue
|
||||
if hasattr(fkval, 'active') and not fkval.active:
|
||||
continue
|
||||
if hasattr(fkval, 'is_active') and not fkval.is_active:
|
||||
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.
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
@ -337,7 +344,7 @@ class ProjectSerializer(BaseSerializer):
|
||||
project_updates = reverse('api:project_updates_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',
|
||||
args=(obj.credential.pk,))
|
||||
if obj.current_update:
|
||||
@ -364,6 +371,12 @@ class ProjectSerializer(BaseSerializer):
|
||||
raise serializers.ValidationError('Invalid path choice')
|
||||
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):
|
||||
|
||||
@ -433,12 +446,19 @@ class InventorySerializer(BaseSerializerWithVariables):
|
||||
variable_data = reverse('api:inventory_variable_data', args=(obj.pk,)),
|
||||
script = reverse('api:inventory_script_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,)),
|
||||
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
|
||||
|
||||
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):
|
||||
|
||||
@ -458,7 +478,6 @@ class HostSerializer(BaseSerializerWithVariables):
|
||||
res = super(HostSerializer, self).get_related(obj)
|
||||
res.update(dict(
|
||||
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,)),
|
||||
all_groups = reverse('api:host_all_groups_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,)),
|
||||
#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,))
|
||||
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,))
|
||||
return res
|
||||
|
||||
@ -543,6 +564,16 @@ class HostSerializer(BaseSerializerWithVariables):
|
||||
|
||||
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):
|
||||
|
||||
@ -563,13 +594,15 @@ class GroupSerializer(BaseSerializerWithVariables):
|
||||
potential_children = reverse('api:group_potential_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,)),
|
||||
inventory = reverse('api:inventory_detail', args=(obj.inventory.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,)),
|
||||
inventory_source = reverse('api:inventory_source_detail', args=(obj.inventory_source.pk,)),
|
||||
activity_stream = reverse('api:group_activity_stream_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
|
||||
|
||||
def validate_name(self, attrs, source):
|
||||
@ -578,6 +611,12 @@ class GroupSerializer(BaseSerializerWithVariables):
|
||||
raise serializers.ValidationError('Invalid group name')
|
||||
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):
|
||||
|
||||
@ -659,11 +698,11 @@ class InventorySourceSerializer(BaseSerializer):
|
||||
#hosts = reverse('api:inventory_source_hosts_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,))
|
||||
if obj.group:
|
||||
if obj.group and obj.group.active:
|
||||
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',
|
||||
args=(obj.credential.pk,))
|
||||
if obj.current_update:
|
||||
@ -712,6 +751,16 @@ class InventorySourceSerializer(BaseSerializer):
|
||||
field_opts['rax_region_choices'] = self.opts.model.get_rax_region_choices()
|
||||
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):
|
||||
|
||||
@ -749,12 +798,19 @@ class TeamSerializer(BaseSerializer):
|
||||
projects = reverse('api:team_projects_list', args=(obj.pk,)),
|
||||
users = reverse('api:team_users_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,)),
|
||||
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
|
||||
|
||||
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):
|
||||
|
||||
@ -792,6 +848,18 @@ class PermissionSerializer(BaseSerializer):
|
||||
'assigning deployment permissions')
|
||||
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):
|
||||
|
||||
@ -810,6 +878,10 @@ class CredentialSerializer(BaseSerializer):
|
||||
|
||||
def to_native(self, 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$.
|
||||
for field in Credential.PASSWORD_FIELDS:
|
||||
if field in ret and unicode(ret[field]).startswith('$encrypted$'):
|
||||
@ -852,20 +924,36 @@ class JobTemplateSerializer(BaseSerializer):
|
||||
return {}
|
||||
res = super(JobTemplateSerializer, self).get_related(obj)
|
||||
res.update(dict(
|
||||
inventory = reverse('api:inventory_detail', args=(obj.inventory.pk,)),
|
||||
project = reverse('api:project_detail', args=(obj.project.pk,)),
|
||||
jobs = reverse('api:job_template_jobs_list', args=(obj.pk,)),
|
||||
jobs = reverse('api:job_template_jobs_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,))
|
||||
if obj.cloud_credential:
|
||||
if obj.cloud_credential and obj.cloud_credential.active:
|
||||
res['cloud_credential'] = reverse('api:credential_detail',
|
||||
args=(obj.cloud_credential.pk,))
|
||||
if obj.host_config_key:
|
||||
res['callback'] = reverse('api:job_template_callback', args=(obj.pk,))
|
||||
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):
|
||||
project = attrs.get('project', None)
|
||||
playbook = attrs.get('playbook', '')
|
||||
@ -894,16 +982,23 @@ class JobSerializer(BaseTaskSerializer):
|
||||
return {}
|
||||
res = super(JobSerializer, self).get_related(obj)
|
||||
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_host_summaries = reverse('api:job_job_host_summaries_list', args=(obj.pk,)),
|
||||
activity_stream = reverse('api:job_activity_stream_list', args=(obj.pk,)),
|
||||
))
|
||||
if obj.job_template:
|
||||
res['job_template'] = reverse('api:job_template_detail', args=(obj.job_template.pk,))
|
||||
if obj.cloud_credential:
|
||||
if obj.job_template and obj.job_template.active:
|
||||
res['job_template'] = reverse('api:job_template_detail',
|
||||
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',
|
||||
args=(obj.cloud_credential.pk,))
|
||||
if obj.can_start or True:
|
||||
@ -923,9 +1018,11 @@ class JobSerializer(BaseTaskSerializer):
|
||||
return
|
||||
# Don't auto-populate name or description.
|
||||
data.setdefault('job_type', job_template.job_type)
|
||||
data.setdefault('inventory', job_template.inventory.pk)
|
||||
data.setdefault('project', job_template.project.pk)
|
||||
data.setdefault('playbook', job_template.playbook)
|
||||
if job_template.inventory:
|
||||
data.setdefault('inventory', job_template.inventory.pk)
|
||||
if job_template.project:
|
||||
data.setdefault('project', job_template.project.pk)
|
||||
data.setdefault('playbook', job_template.playbook)
|
||||
if job_template.credential:
|
||||
data.setdefault('credential', job_template.credential.pk)
|
||||
if job_template.cloud_credential:
|
||||
@ -937,6 +1034,22 @@ class JobSerializer(BaseTaskSerializer):
|
||||
data.setdefault('job_tags', job_template.job_tags)
|
||||
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):
|
||||
|
||||
|
@ -120,7 +120,7 @@ class BaseAccess(object):
|
||||
return self.model.objects.none()
|
||||
|
||||
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):
|
||||
return self.user.is_superuser
|
||||
@ -172,18 +172,18 @@ class UserAccess(BaseAccess):
|
||||
qs = self.model.objects.filter(is_active=True).distinct()
|
||||
if self.user.is_superuser:
|
||||
return qs
|
||||
if self.user.admin_of_organizations.count():
|
||||
if self.user.admin_of_organizations.filter(active=True).exists():
|
||||
return qs
|
||||
return qs.filter(
|
||||
Q(pk=self.user.pk) |
|
||||
Q(organizations__in=self.user.admin_of_organizations.all()) |
|
||||
Q(organizations__in=self.user.organizations.all()) |
|
||||
Q(teams__in=self.user.teams.all())
|
||||
Q(organizations__in=self.user.admin_of_organizations.filter(active=True)) |
|
||||
Q(organizations__in=self.user.organizations.filter(active=True)) |
|
||||
Q(teams__in=self.user.teams.filter(active=True))
|
||||
).distinct()
|
||||
|
||||
def can_add(self, data):
|
||||
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):
|
||||
# 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.
|
||||
if self.user.is_superuser:
|
||||
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):
|
||||
if obj == self.user:
|
||||
@ -206,7 +207,8 @@ class UserAccess(BaseAccess):
|
||||
# cannot delete the last active superuser
|
||||
return False
|
||||
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):
|
||||
'''
|
||||
@ -258,21 +260,24 @@ class InventoryAccess(BaseAccess):
|
||||
qs = qs.select_related('created_by', 'organization')
|
||||
if self.user.is_superuser:
|
||||
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(
|
||||
permissions__user__in=[self.user],
|
||||
permissions__permission_type__in=allowed,
|
||||
permissions__active=True,
|
||||
permissions__active=True, # TODO: add test for this case
|
||||
).distinct()
|
||||
has_team_perms = qs.filter(
|
||||
permissions__team__users__in=[self.user],
|
||||
#permissions__team__active=True, TODO: enable this line
|
||||
permissions__permission_type__in=allowed,
|
||||
permissions__active=True,
|
||||
permissions__active=True, # TODO: add test for this case
|
||||
).distinct()
|
||||
return admin_of | has_user_perms | has_team_perms
|
||||
|
||||
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):
|
||||
return self.has_permission_types(obj, PERMISSION_TYPES_ALLOWING_INVENTORY_READ)
|
||||
@ -280,7 +285,9 @@ class InventoryAccess(BaseAccess):
|
||||
def can_add(self, data):
|
||||
# If no data is specified, just checking for generic add permission?
|
||||
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
|
||||
# organization of this inventory.
|
||||
if self.user.is_superuser:
|
||||
@ -360,7 +367,6 @@ class HostAccess(BaseAccess):
|
||||
raise PermissionDenied("license has expired")
|
||||
|
||||
if validation_info.get('free_instances', 0) > 0:
|
||||
# BOOKMARK
|
||||
return True
|
||||
instances = validation_info.get('available_instances', 0)
|
||||
raise PermissionDenied("license range of %s instances has been exceeded" % instances)
|
||||
@ -433,7 +439,6 @@ class GroupAccess(BaseAccess):
|
||||
parent_pks.add(obj.pk)
|
||||
child_pks = set(sub_obj.all_children.values_list('pk', flat=True))
|
||||
child_pks.add(sub_obj.pk)
|
||||
#print parent_pks, child_pks
|
||||
if parent_pks & child_pks:
|
||||
return False
|
||||
return True
|
||||
@ -518,11 +523,14 @@ class CredentialAccess(BaseAccess):
|
||||
qs = qs.select_related('created_by', 'user', 'team')
|
||||
if self.user.is_superuser:
|
||||
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()
|
||||
return qs.filter(
|
||||
Q(user=self.user) |
|
||||
Q(user__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__users__in=[self.user])
|
||||
)
|
||||
@ -550,11 +558,14 @@ class CredentialAccess(BaseAccess):
|
||||
if obj.user:
|
||||
if self.user == obj.user:
|
||||
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
|
||||
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
|
||||
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():
|
||||
return True
|
||||
return False
|
||||
@ -585,6 +596,7 @@ class TeamAccess(BaseAccess):
|
||||
if self.user.is_superuser:
|
||||
return qs
|
||||
return qs.filter(
|
||||
#Q(organization__admins__in=[self.user], organization__active=True) | TODO: replace line below
|
||||
Q(organization__admins__in=[self.user]) |
|
||||
Q(users__in=[self.user])
|
||||
)
|
||||
@ -639,17 +651,21 @@ class ProjectAccess(BaseAccess):
|
||||
allowed = [PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
||||
return qs.filter(
|
||||
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__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)
|
||||
)
|
||||
|
||||
def can_add(self, data):
|
||||
if self.user.is_superuser:
|
||||
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 False
|
||||
|
||||
@ -658,7 +674,8 @@ class ProjectAccess(BaseAccess):
|
||||
return True
|
||||
if obj.created_by == self.user:
|
||||
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 False
|
||||
|
||||
@ -707,12 +724,15 @@ class PermissionAccess(BaseAccess):
|
||||
'project')
|
||||
if self.user.is_superuser:
|
||||
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()
|
||||
return qs.filter(
|
||||
Q(user__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(user=self.user) |
|
||||
#Q(team__users__in=[self.user], team__active=True) TODO: replace line below
|
||||
Q(team__users__in=[self.user])
|
||||
)
|
||||
|
||||
@ -802,7 +822,9 @@ class JobTemplateAccess(BaseAccess):
|
||||
credential_qs = self.user.get_queryset(Credential)
|
||||
base_qs = qs.filter(
|
||||
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(
|
||||
project__organizations__admins__in=[self.user]
|
||||
)
|
||||
@ -821,13 +843,17 @@ class JobTemplateAccess(BaseAccess):
|
||||
|
||||
def can_read(self, obj):
|
||||
# you can only see the job templates that you have permission to launch.
|
||||
data = dict(
|
||||
inventory = obj.inventory.pk,
|
||||
project = obj.project.pk,
|
||||
job_type = obj.job_type,
|
||||
)
|
||||
data = {
|
||||
'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:
|
||||
data['credential'] = obj.credential.pk
|
||||
if obj.cloud_credential:
|
||||
data['cloud_credential'] = obj.cloud_credential.pk
|
||||
return self.can_add(data)
|
||||
|
||||
def can_add(self, data):
|
||||
@ -850,6 +876,14 @@ class JobTemplateAccess(BaseAccess):
|
||||
if not self.user.can_access(Credential, 'read', credential):
|
||||
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.
|
||||
inventory_pk = get_pk_from_dict(data, 'inventory')
|
||||
inventory = get_object_or_400(Inventory, pk=inventory_pk)
|
||||
@ -1004,7 +1038,7 @@ class JobEventAccess(BaseAccess):
|
||||
'host', 'parent')
|
||||
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'),
|
||||
event_data__icontains='"ansible_job_id": "',
|
||||
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=201, 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):
|
||||
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.
|
||||
data2 = self.get(url, expect=200, auth=self.get_normal_credentials())
|
||||
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())
|
||||
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):
|
||||
user_pk = self.normal_django_user.pk
|
||||
|
Loading…
Reference in New Issue
Block a user