mirror of
https://github.com/ansible/awx.git
synced 2026-01-09 15:02:07 -03:30
replace certain terms with more inclusive language
see: https://www.redhat.com/en/blog/making-open-source-more-inclusive-eradicating-problematic-language
This commit is contained in:
parent
5b9c19df8f
commit
78229f5871
@ -146,7 +146,7 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
|
|
||||||
# A list of fields that we know can be filtered on without the possiblity
|
# A list of fields that we know can be filtered on without the possiblity
|
||||||
# of introducing duplicates
|
# of introducing duplicates
|
||||||
NO_DUPLICATES_WHITELIST = (CharField, IntegerField, BooleanField)
|
NO_DUPLICATES_ALLOW_LIST = (CharField, IntegerField, BooleanField)
|
||||||
|
|
||||||
def get_fields_from_lookup(self, model, lookup):
|
def get_fields_from_lookup(self, model, lookup):
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
field_list, new_lookup = self.get_fields_from_lookup(model, lookup)
|
field_list, new_lookup = self.get_fields_from_lookup(model, lookup)
|
||||||
field = field_list[-1]
|
field = field_list[-1]
|
||||||
|
|
||||||
needs_distinct = (not all(isinstance(f, self.NO_DUPLICATES_WHITELIST) for f in field_list))
|
needs_distinct = (not all(isinstance(f, self.NO_DUPLICATES_ALLOW_LIST) for f in field_list))
|
||||||
|
|
||||||
# Type names are stored without underscores internally, but are presented and
|
# Type names are stored without underscores internally, but are presented and
|
||||||
# and serialized over the API containing underscores so we remove `_`
|
# and serialized over the API containing underscores so we remove `_`
|
||||||
|
|||||||
@ -159,11 +159,11 @@ class APIView(views.APIView):
|
|||||||
self.queries_before = len(connection.queries)
|
self.queries_before = len(connection.queries)
|
||||||
|
|
||||||
# If there are any custom headers in REMOTE_HOST_HEADERS, make sure
|
# If there are any custom headers in REMOTE_HOST_HEADERS, make sure
|
||||||
# they respect the proxy whitelist
|
# they respect the allowed proxy list
|
||||||
if all([
|
if all([
|
||||||
settings.PROXY_IP_WHITELIST,
|
settings.PROXY_IP_ALLOWED_LIST,
|
||||||
request.environ.get('REMOTE_ADDR') not in settings.PROXY_IP_WHITELIST,
|
request.environ.get('REMOTE_ADDR') not in settings.PROXY_IP_ALLOWED_LIST,
|
||||||
request.environ.get('REMOTE_HOST') not in settings.PROXY_IP_WHITELIST
|
request.environ.get('REMOTE_HOST') not in settings.PROXY_IP_ALLOWED_LIST
|
||||||
]):
|
]):
|
||||||
for custom_header in settings.REMOTE_HOST_HEADERS:
|
for custom_header in settings.REMOTE_HOST_HEADERS:
|
||||||
if custom_header.startswith('HTTP_'):
|
if custom_header.startswith('HTTP_'):
|
||||||
@ -837,7 +837,7 @@ class CopyAPIView(GenericAPIView):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _decrypt_model_field_if_needed(obj, field_name, field_val):
|
def _decrypt_model_field_if_needed(obj, field_name, field_val):
|
||||||
if field_name in getattr(type(obj), 'REENCRYPTION_BLACKLIST_AT_COPY', []):
|
if field_name in getattr(type(obj), 'REENCRYPTION_BLOCKLIST_AT_COPY', []):
|
||||||
return field_val
|
return field_val
|
||||||
if isinstance(obj, Credential) and field_name == 'inputs':
|
if isinstance(obj, Credential) and field_name == 'inputs':
|
||||||
for secret in obj.credential_type.secret_fields:
|
for secret in obj.credential_type.secret_fields:
|
||||||
@ -883,7 +883,7 @@ class CopyAPIView(GenericAPIView):
|
|||||||
field_val = getattr(obj, field.name)
|
field_val = getattr(obj, field.name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
continue
|
continue
|
||||||
# Adjust copy blacklist fields here.
|
# Adjust copy blocked fields here.
|
||||||
if field.name in fields_to_discard or field.name in [
|
if field.name in fields_to_discard or field.name in [
|
||||||
'id', 'pk', 'polymorphic_ctype', 'unifiedjobtemplate_ptr', 'created_by', 'modified_by'
|
'id', 'pk', 'polymorphic_ctype', 'unifiedjobtemplate_ptr', 'created_by', 'modified_by'
|
||||||
] or field.name.endswith('_role'):
|
] or field.name.endswith('_role'):
|
||||||
|
|||||||
@ -1936,7 +1936,7 @@ class InventorySourceOptionsSerializer(BaseSerializer):
|
|||||||
def validate_source_vars(self, value):
|
def validate_source_vars(self, value):
|
||||||
ret = vars_validate_or_raise(value)
|
ret = vars_validate_or_raise(value)
|
||||||
for env_k in parse_yaml_or_json(value):
|
for env_k in parse_yaml_or_json(value):
|
||||||
if env_k in settings.INV_ENV_VARIABLE_BLACKLIST:
|
if env_k in settings.INV_ENV_VARIABLE_BLOCKED:
|
||||||
raise serializers.ValidationError(_("`{}` is a prohibited environment variable".format(env_k)))
|
raise serializers.ValidationError(_("`{}` is a prohibited environment variable".format(env_k)))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|||||||
19
awx/conf/migrations/0007_v380_rename_more_settings.py
Normal file
19
awx/conf/migrations/0007_v380_rename_more_settings.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from django.db import migrations
|
||||||
|
from awx.conf.migrations import _rename_setting
|
||||||
|
|
||||||
|
|
||||||
|
def copy_allowed_ips(apps, schema_editor):
|
||||||
|
_rename_setting.rename_setting(apps, schema_editor, old_key='PROXY_IP_WHITELIST', new_key='PROXY_IP_ALLOWED_LIST')
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('conf', '0006_v331_ldap_group_type'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(copy_allowed_ips),
|
||||||
|
]
|
||||||
@ -1513,8 +1513,7 @@ class JobTemplateAccess(NotificationAttachMixin, BaseAccess):
|
|||||||
thus can be made by a job template administrator which may not have access
|
thus can be made by a job template administrator which may not have access
|
||||||
to the any inventory, project, or credentials associated with the template.
|
to the any inventory, project, or credentials associated with the template.
|
||||||
'''
|
'''
|
||||||
# We are white listing fields that can
|
allowed_fields = [
|
||||||
field_whitelist = [
|
|
||||||
'name', 'description', 'forks', 'limit', 'verbosity', 'extra_vars',
|
'name', 'description', 'forks', 'limit', 'verbosity', 'extra_vars',
|
||||||
'job_tags', 'force_handlers', 'skip_tags', 'ask_variables_on_launch',
|
'job_tags', 'force_handlers', 'skip_tags', 'ask_variables_on_launch',
|
||||||
'ask_tags_on_launch', 'ask_job_type_on_launch', 'ask_skip_tags_on_launch',
|
'ask_tags_on_launch', 'ask_job_type_on_launch', 'ask_skip_tags_on_launch',
|
||||||
@ -1529,7 +1528,7 @@ class JobTemplateAccess(NotificationAttachMixin, BaseAccess):
|
|||||||
if k not in [x.name for x in obj._meta.concrete_fields]:
|
if k not in [x.name for x in obj._meta.concrete_fields]:
|
||||||
continue
|
continue
|
||||||
if hasattr(obj, k) and getattr(obj, k) != v:
|
if hasattr(obj, k) and getattr(obj, k) != v:
|
||||||
if k not in field_whitelist and v != getattr(obj, '%s_id' % k, None) \
|
if k not in allowed_fields and v != getattr(obj, '%s_id' % k, None) \
|
||||||
and not (hasattr(obj, '%s_id' % k) and getattr(obj, '%s_id' % k) is None and v == ''): # Equate '' to None in the case of foreign keys
|
and not (hasattr(obj, '%s_id' % k) and getattr(obj, '%s_id' % k) is None and v == ''): # Equate '' to None in the case of foreign keys
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|||||||
@ -80,11 +80,11 @@ register(
|
|||||||
)
|
)
|
||||||
|
|
||||||
register(
|
register(
|
||||||
'PROXY_IP_WHITELIST',
|
'PROXY_IP_ALLOWED_LIST',
|
||||||
field_class=fields.StringListField,
|
field_class=fields.StringListField,
|
||||||
label=_('Proxy IP Whitelist'),
|
label=_('Proxy IP Allowed List'),
|
||||||
help_text=_("If Tower is behind a reverse proxy/load balancer, use this setting "
|
help_text=_("If Tower is behind a reverse proxy/load balancer, use this setting "
|
||||||
"to whitelist the proxy IP addresses from which Tower should trust "
|
"to configure the proxy IP addresses from which Tower should trust "
|
||||||
"custom REMOTE_HOST_HEADERS header values. "
|
"custom REMOTE_HOST_HEADERS header values. "
|
||||||
"If this setting is an empty list (the default), the headers specified by "
|
"If this setting is an empty list (the default), the headers specified by "
|
||||||
"REMOTE_HOST_HEADERS will be trusted unconditionally')"),
|
"REMOTE_HOST_HEADERS will be trusted unconditionally')"),
|
||||||
@ -241,7 +241,7 @@ register(
|
|||||||
field_class=fields.StringListField,
|
field_class=fields.StringListField,
|
||||||
required=False,
|
required=False,
|
||||||
label=_('Paths to expose to isolated jobs'),
|
label=_('Paths to expose to isolated jobs'),
|
||||||
help_text=_('Whitelist of paths that would otherwise be hidden to expose to isolated jobs. Enter one path per line.'),
|
help_text=_('List of paths that would otherwise be hidden to expose to isolated jobs. Enter one path per line.'),
|
||||||
category=_('Jobs'),
|
category=_('Jobs'),
|
||||||
category_slug='jobs',
|
category_slug='jobs',
|
||||||
)
|
)
|
||||||
|
|||||||
@ -31,7 +31,7 @@ STANDARD_INVENTORY_UPDATE_ENV = {
|
|||||||
CAN_CANCEL = ('new', 'pending', 'waiting', 'running')
|
CAN_CANCEL = ('new', 'pending', 'waiting', 'running')
|
||||||
ACTIVE_STATES = CAN_CANCEL
|
ACTIVE_STATES = CAN_CANCEL
|
||||||
CENSOR_VALUE = '************'
|
CENSOR_VALUE = '************'
|
||||||
ENV_BLACKLIST = frozenset((
|
ENV_BLOCKLIST = frozenset((
|
||||||
'VIRTUAL_ENV', 'PATH', 'PYTHONPATH', 'PROOT_TMP_DIR', 'JOB_ID',
|
'VIRTUAL_ENV', 'PATH', 'PYTHONPATH', 'PROOT_TMP_DIR', 'JOB_ID',
|
||||||
'INVENTORY_ID', 'INVENTORY_SOURCE_ID', 'INVENTORY_UPDATE_ID',
|
'INVENTORY_ID', 'INVENTORY_SOURCE_ID', 'INVENTORY_UPDATE_ID',
|
||||||
'AD_HOC_COMMAND_ID', 'REST_API_URL', 'REST_API_TOKEN', 'MAX_EVENT_RES',
|
'AD_HOC_COMMAND_ID', 'REST_API_URL', 'REST_API_TOKEN', 'MAX_EVENT_RES',
|
||||||
@ -41,7 +41,7 @@ ENV_BLACKLIST = frozenset((
|
|||||||
))
|
))
|
||||||
|
|
||||||
# loggers that may be called in process of emitting a log
|
# loggers that may be called in process of emitting a log
|
||||||
LOGGER_BLACKLIST = (
|
LOGGER_BLOCKLIST = (
|
||||||
'awx.main.utils.handlers',
|
'awx.main.utils.handlers',
|
||||||
'awx.main.utils.formatters',
|
'awx.main.utils.formatters',
|
||||||
'awx.main.utils.filters',
|
'awx.main.utils.filters',
|
||||||
|
|||||||
@ -50,7 +50,7 @@ from awx.main.models.rbac import (
|
|||||||
batch_role_ancestor_rebuilding, Role,
|
batch_role_ancestor_rebuilding, Role,
|
||||||
ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_AUDITOR
|
ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_AUDITOR
|
||||||
)
|
)
|
||||||
from awx.main.constants import ENV_BLACKLIST
|
from awx.main.constants import ENV_BLOCKLIST
|
||||||
from awx.main import utils
|
from awx.main import utils
|
||||||
|
|
||||||
|
|
||||||
@ -870,9 +870,9 @@ class CredentialTypeInjectorField(JSONSchemaField):
|
|||||||
'use is not allowed in credentials.').format(env_var),
|
'use is not allowed in credentials.').format(env_var),
|
||||||
code='invalid', params={'value': env_var},
|
code='invalid', params={'value': env_var},
|
||||||
)
|
)
|
||||||
if env_var in ENV_BLACKLIST:
|
if env_var in ENV_BLOCKLIST:
|
||||||
raise django_exceptions.ValidationError(
|
raise django_exceptions.ValidationError(
|
||||||
_('Environment variable {} is blacklisted from use in credentials.').format(env_var),
|
_('Environment variable {} is not allowed to be used in credentials.').format(env_var),
|
||||||
code='invalid', params={'value': env_var},
|
code='invalid', params={'value': env_var},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -407,7 +407,7 @@ def prevent_search(relation):
|
|||||||
sensitive_data = prevent_search(models.CharField(...))
|
sensitive_data = prevent_search(models.CharField(...))
|
||||||
|
|
||||||
The flag set by this function is used by
|
The flag set by this function is used by
|
||||||
`awx.api.filters.FieldLookupBackend` to blacklist fields and relations that
|
`awx.api.filters.FieldLookupBackend` to block fields and relations that
|
||||||
should not be searchable/filterable via search query params
|
should not be searchable/filterable via search query params
|
||||||
"""
|
"""
|
||||||
setattr(relation, '__prevent_search__', True)
|
setattr(relation, '__prevent_search__', True)
|
||||||
|
|||||||
@ -1910,7 +1910,7 @@ class ec2(PluginFileInjector):
|
|||||||
# Compatibility content
|
# Compatibility content
|
||||||
legacy_regex = {
|
legacy_regex = {
|
||||||
True: r"[^A-Za-z0-9\_]",
|
True: r"[^A-Za-z0-9\_]",
|
||||||
False: r"[^A-Za-z0-9\_\-]" # do not replace dash, dash is whitelisted
|
False: r"[^A-Za-z0-9\_\-]" # do not replace dash, dash is allowed
|
||||||
}[replace_dash]
|
}[replace_dash]
|
||||||
list_replacer = 'map("regex_replace", "{rx}", "_") | list'.format(rx=legacy_regex)
|
list_replacer = 'map("regex_replace", "{rx}", "_") | list'.format(rx=legacy_regex)
|
||||||
# this option, a plugin option, will allow dashes, but not unicode
|
# this option, a plugin option, will allow dashes, but not unicode
|
||||||
@ -1943,7 +1943,7 @@ class ec2(PluginFileInjector):
|
|||||||
ret['boto_profile'] = source_vars['boto_profile']
|
ret['boto_profile'] = source_vars['boto_profile']
|
||||||
|
|
||||||
elif not replace_dash:
|
elif not replace_dash:
|
||||||
# Using the plugin, but still want dashes whitelisted
|
# Using the plugin, but still want dashes allowed
|
||||||
ret['use_contrib_script_compatible_sanitization'] = True
|
ret['use_contrib_script_compatible_sanitization'] = True
|
||||||
|
|
||||||
if source_vars.get('nested_groups') is False:
|
if source_vars.get('nested_groups') is False:
|
||||||
|
|||||||
@ -262,25 +262,25 @@ class JobNotificationMixin(object):
|
|||||||
'running': 'started',
|
'running': 'started',
|
||||||
'failed': 'error'}
|
'failed': 'error'}
|
||||||
# Tree of fields that can be safely referenced in a notification message
|
# Tree of fields that can be safely referenced in a notification message
|
||||||
JOB_FIELDS_WHITELIST = ['id', 'type', 'url', 'created', 'modified', 'name', 'description', 'job_type', 'playbook',
|
JOB_FIELDS_ALLOWED_LIST = ['id', 'type', 'url', 'created', 'modified', 'name', 'description', 'job_type', 'playbook',
|
||||||
'forks', 'limit', 'verbosity', 'job_tags', 'force_handlers', 'skip_tags', 'start_at_task',
|
'forks', 'limit', 'verbosity', 'job_tags', 'force_handlers', 'skip_tags', 'start_at_task',
|
||||||
'timeout', 'use_fact_cache', 'launch_type', 'status', 'failed', 'started', 'finished',
|
'timeout', 'use_fact_cache', 'launch_type', 'status', 'failed', 'started', 'finished',
|
||||||
'elapsed', 'job_explanation', 'execution_node', 'controller_node', 'allow_simultaneous',
|
'elapsed', 'job_explanation', 'execution_node', 'controller_node', 'allow_simultaneous',
|
||||||
'scm_revision', 'diff_mode', 'job_slice_number', 'job_slice_count', 'custom_virtualenv',
|
'scm_revision', 'diff_mode', 'job_slice_number', 'job_slice_count', 'custom_virtualenv',
|
||||||
'approval_status', 'approval_node_name', 'workflow_url', 'scm_branch',
|
'approval_status', 'approval_node_name', 'workflow_url', 'scm_branch',
|
||||||
{'host_status_counts': ['skipped', 'ok', 'changed', 'failed', 'failures', 'dark'
|
{'host_status_counts': ['skipped', 'ok', 'changed', 'failed', 'failures', 'dark'
|
||||||
'processed', 'rescued', 'ignored']},
|
'processed', 'rescued', 'ignored']},
|
||||||
{'summary_fields': [{'inventory': ['id', 'name', 'description', 'has_active_failures',
|
{'summary_fields': [{'inventory': ['id', 'name', 'description', 'has_active_failures',
|
||||||
'total_hosts', 'hosts_with_active_failures', 'total_groups',
|
'total_hosts', 'hosts_with_active_failures', 'total_groups',
|
||||||
'has_inventory_sources',
|
'has_inventory_sources',
|
||||||
'total_inventory_sources', 'inventory_sources_with_failures',
|
'total_inventory_sources', 'inventory_sources_with_failures',
|
||||||
'organization_id', 'kind']},
|
'organization_id', 'kind']},
|
||||||
{'project': ['id', 'name', 'description', 'status', 'scm_type']},
|
{'project': ['id', 'name', 'description', 'status', 'scm_type']},
|
||||||
{'job_template': ['id', 'name', 'description']},
|
{'job_template': ['id', 'name', 'description']},
|
||||||
{'unified_job_template': ['id', 'name', 'description', 'unified_job_type']},
|
{'unified_job_template': ['id', 'name', 'description', 'unified_job_type']},
|
||||||
{'instance_group': ['name', 'id']},
|
{'instance_group': ['name', 'id']},
|
||||||
{'created_by': ['id', 'username', 'first_name', 'last_name']},
|
{'created_by': ['id', 'username', 'first_name', 'last_name']},
|
||||||
{'labels': ['count', 'results']}]}]
|
{'labels': ['count', 'results']}]}]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def context_stub(cls):
|
def context_stub(cls):
|
||||||
@ -377,8 +377,8 @@ class JobNotificationMixin(object):
|
|||||||
|
|
||||||
def context(self, serialized_job):
|
def context(self, serialized_job):
|
||||||
"""Returns a dictionary that can be used for rendering notification messages.
|
"""Returns a dictionary that can be used for rendering notification messages.
|
||||||
The context will contain whitelisted content retrieved from a serialized job object
|
The context will contain allowed content retrieved from a serialized job object
|
||||||
(see JobNotificationMixin.JOB_FIELDS_WHITELIST), the job's friendly name,
|
(see JobNotificationMixin.JOB_FIELDS_ALLOWED_LIST the job's friendly name,
|
||||||
and a url to the job run."""
|
and a url to the job run."""
|
||||||
job_context = {'host_status_counts': {}}
|
job_context = {'host_status_counts': {}}
|
||||||
summary = None
|
summary = None
|
||||||
@ -395,22 +395,22 @@ class JobNotificationMixin(object):
|
|||||||
'job_metadata': json.dumps(self.notification_data(), indent=4)
|
'job_metadata': json.dumps(self.notification_data(), indent=4)
|
||||||
}
|
}
|
||||||
|
|
||||||
def build_context(node, fields, whitelisted_fields):
|
def build_context(node, fields, allowed_fields):
|
||||||
for safe_field in whitelisted_fields:
|
for safe_field in allowed_fields:
|
||||||
if type(safe_field) is dict:
|
if type(safe_field) is dict:
|
||||||
field, whitelist_subnode = safe_field.copy().popitem()
|
field, allowed_subnode = safe_field.copy().popitem()
|
||||||
# ensure content present in job serialization
|
# ensure content present in job serialization
|
||||||
if field not in fields:
|
if field not in fields:
|
||||||
continue
|
continue
|
||||||
subnode = fields[field]
|
subnode = fields[field]
|
||||||
node[field] = {}
|
node[field] = {}
|
||||||
build_context(node[field], subnode, whitelist_subnode)
|
build_context(node[field], subnode, allowed_subnode)
|
||||||
else:
|
else:
|
||||||
# ensure content present in job serialization
|
# ensure content present in job serialization
|
||||||
if safe_field not in fields:
|
if safe_field not in fields:
|
||||||
continue
|
continue
|
||||||
node[safe_field] = fields[safe_field]
|
node[safe_field] = fields[safe_field]
|
||||||
build_context(context['job'], serialized_job, self.JOB_FIELDS_WHITELIST)
|
build_context(context['job'], serialized_job, self.JOB_FIELDS_ALLOWED_LIST)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|||||||
@ -139,7 +139,7 @@ class WorkflowJobTemplateNode(WorkflowNodeBase):
|
|||||||
'always_nodes', 'credentials', 'inventory', 'extra_data', 'survey_passwords',
|
'always_nodes', 'credentials', 'inventory', 'extra_data', 'survey_passwords',
|
||||||
'char_prompts', 'all_parents_must_converge', 'identifier'
|
'char_prompts', 'all_parents_must_converge', 'identifier'
|
||||||
]
|
]
|
||||||
REENCRYPTION_BLACKLIST_AT_COPY = ['extra_data', 'survey_passwords']
|
REENCRYPTION_BLOCKLIST_AT_COPY = ['extra_data', 'survey_passwords']
|
||||||
|
|
||||||
workflow_job_template = models.ForeignKey(
|
workflow_job_template = models.ForeignKey(
|
||||||
'WorkflowJobTemplate',
|
'WorkflowJobTemplate',
|
||||||
|
|||||||
@ -1802,7 +1802,7 @@ class RunJob(BaseTask):
|
|||||||
|
|
||||||
# By default, all extra vars disallow Jinja2 template usage for
|
# By default, all extra vars disallow Jinja2 template usage for
|
||||||
# security reasons; top level key-values defined in JT.extra_vars, however,
|
# security reasons; top level key-values defined in JT.extra_vars, however,
|
||||||
# are whitelisted as "safe" (because they can only be set by users with
|
# are allowed as "safe" (because they can only be set by users with
|
||||||
# higher levels of privilege - those that have the ability create and
|
# higher levels of privilege - those that have the ability create and
|
||||||
# edit Job Templates)
|
# edit Job Templates)
|
||||||
safe_dict = {}
|
safe_dict = {}
|
||||||
@ -2472,7 +2472,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
|
|
||||||
if inventory_update.source in ['scm', 'custom']:
|
if inventory_update.source in ['scm', 'custom']:
|
||||||
for env_k in inventory_update.source_vars_dict:
|
for env_k in inventory_update.source_vars_dict:
|
||||||
if str(env_k) not in env and str(env_k) not in settings.INV_ENV_VARIABLE_BLACKLIST:
|
if str(env_k) not in env and str(env_k) not in settings.INV_ENV_VARIABLE_BLOCKED:
|
||||||
env[str(env_k)] = str(inventory_update.source_vars_dict[env_k])
|
env[str(env_k)] = str(inventory_update.source_vars_dict[env_k])
|
||||||
elif inventory_update.source == 'file':
|
elif inventory_update.source == 'file':
|
||||||
raise NotImplementedError('Cannot update file sources through the task system.')
|
raise NotImplementedError('Cannot update file sources through the task system.')
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from awx.api.versioning import reverse
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_proxy_ip_whitelist(get, patch, admin):
|
def test_proxy_ip_allowed(get, patch, admin):
|
||||||
url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'system'})
|
url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'system'})
|
||||||
patch(url, user=admin, data={
|
patch(url, user=admin, data={
|
||||||
'REMOTE_HOST_HEADERS': [
|
'REMOTE_HOST_HEADERS': [
|
||||||
@ -23,37 +23,37 @@ def test_proxy_ip_whitelist(get, patch, admin):
|
|||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
self.environ = request.environ
|
self.environ = request.environ
|
||||||
|
|
||||||
# By default, `PROXY_IP_WHITELIST` is disabled, so custom `REMOTE_HOST_HEADERS`
|
# By default, `PROXY_IP_ALLOWED_LIST` is disabled, so custom `REMOTE_HOST_HEADERS`
|
||||||
# should just pass through
|
# should just pass through
|
||||||
middleware = HeaderTrackingMiddleware()
|
middleware = HeaderTrackingMiddleware()
|
||||||
get(url, user=admin, middleware=middleware,
|
get(url, user=admin, middleware=middleware,
|
||||||
HTTP_X_FROM_THE_LOAD_BALANCER='some-actual-ip')
|
HTTP_X_FROM_THE_LOAD_BALANCER='some-actual-ip')
|
||||||
assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip'
|
assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip'
|
||||||
|
|
||||||
# If `PROXY_IP_WHITELIST` is restricted to 10.0.1.100 and we make a request
|
# If `PROXY_IP_ALLOWED_LIST` is restricted to 10.0.1.100 and we make a request
|
||||||
# from 8.9.10.11, the custom `HTTP_X_FROM_THE_LOAD_BALANCER` header should
|
# from 8.9.10.11, the custom `HTTP_X_FROM_THE_LOAD_BALANCER` header should
|
||||||
# be stripped
|
# be stripped
|
||||||
patch(url, user=admin, data={
|
patch(url, user=admin, data={
|
||||||
'PROXY_IP_WHITELIST': ['10.0.1.100']
|
'PROXY_IP_ALLOWED_LIST': ['10.0.1.100']
|
||||||
})
|
})
|
||||||
middleware = HeaderTrackingMiddleware()
|
middleware = HeaderTrackingMiddleware()
|
||||||
get(url, user=admin, middleware=middleware, REMOTE_ADDR='8.9.10.11',
|
get(url, user=admin, middleware=middleware, REMOTE_ADDR='8.9.10.11',
|
||||||
HTTP_X_FROM_THE_LOAD_BALANCER='some-actual-ip')
|
HTTP_X_FROM_THE_LOAD_BALANCER='some-actual-ip')
|
||||||
assert 'HTTP_X_FROM_THE_LOAD_BALANCER' not in middleware.environ
|
assert 'HTTP_X_FROM_THE_LOAD_BALANCER' not in middleware.environ
|
||||||
|
|
||||||
# If 8.9.10.11 is added to `PROXY_IP_WHITELIST` the
|
# If 8.9.10.11 is added to `PROXY_IP_ALLOWED_LIST` the
|
||||||
# `HTTP_X_FROM_THE_LOAD_BALANCER` header should be passed through again
|
# `HTTP_X_FROM_THE_LOAD_BALANCER` header should be passed through again
|
||||||
patch(url, user=admin, data={
|
patch(url, user=admin, data={
|
||||||
'PROXY_IP_WHITELIST': ['10.0.1.100', '8.9.10.11']
|
'PROXY_IP_ALLOWED_LIST': ['10.0.1.100', '8.9.10.11']
|
||||||
})
|
})
|
||||||
middleware = HeaderTrackingMiddleware()
|
middleware = HeaderTrackingMiddleware()
|
||||||
get(url, user=admin, middleware=middleware, REMOTE_ADDR='8.9.10.11',
|
get(url, user=admin, middleware=middleware, REMOTE_ADDR='8.9.10.11',
|
||||||
HTTP_X_FROM_THE_LOAD_BALANCER='some-actual-ip')
|
HTTP_X_FROM_THE_LOAD_BALANCER='some-actual-ip')
|
||||||
assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip'
|
assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip'
|
||||||
|
|
||||||
# Allow whitelisting of proxy hostnames in addition to IP addresses
|
# Allow allowed list of proxy hostnames in addition to IP addresses
|
||||||
patch(url, user=admin, data={
|
patch(url, user=admin, data={
|
||||||
'PROXY_IP_WHITELIST': ['my.proxy.example.org']
|
'PROXY_IP_ALLOWED_LIST': ['my.proxy.example.org']
|
||||||
})
|
})
|
||||||
middleware = HeaderTrackingMiddleware()
|
middleware = HeaderTrackingMiddleware()
|
||||||
get(url, user=admin, middleware=middleware, REMOTE_ADDR='8.9.10.11',
|
get(url, user=admin, middleware=middleware, REMOTE_ADDR='8.9.10.11',
|
||||||
|
|||||||
@ -413,7 +413,7 @@ def test_inventory_update_access_called(post, inventory_source, alice, mock_acce
|
|||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_inventory_source_vars_prohibition(post, inventory, admin_user):
|
def test_inventory_source_vars_prohibition(post, inventory, admin_user):
|
||||||
with mock.patch('awx.api.serializers.settings') as mock_settings:
|
with mock.patch('awx.api.serializers.settings') as mock_settings:
|
||||||
mock_settings.INV_ENV_VARIABLE_BLACKLIST = ('FOOBAR',)
|
mock_settings.INV_ENV_VARIABLE_BLOCKED = ('FOOBAR',)
|
||||||
r = post(reverse('api:inventory_source_list'),
|
r = post(reverse('api:inventory_source_list'),
|
||||||
{'name': 'new inv src', 'source_vars': '{\"FOOBAR\": \"val\"}', 'inventory': inventory.pk},
|
{'name': 'new inv src', 'source_vars': '{\"FOOBAR\": \"val\"}', 'inventory': inventory.pk},
|
||||||
admin_user, expect=400)
|
admin_user, expect=400)
|
||||||
|
|||||||
@ -347,7 +347,7 @@ class TestExtraVarSanitation(TestJobExecution):
|
|||||||
assert extra_vars['msg'] == {'a': [self.UNSAFE]}
|
assert extra_vars['msg'] == {'a': [self.UNSAFE]}
|
||||||
assert hasattr(extra_vars['msg']['a'][0], '__UNSAFE__')
|
assert hasattr(extra_vars['msg']['a'][0], '__UNSAFE__')
|
||||||
|
|
||||||
def test_whitelisted_jt_extra_vars(self, job, private_data_dir):
|
def test_allowed_jt_extra_vars(self, job, private_data_dir):
|
||||||
job.job_template.extra_vars = job.extra_vars = json.dumps({'msg': self.UNSAFE})
|
job.job_template.extra_vars = job.extra_vars = json.dumps({'msg': self.UNSAFE})
|
||||||
task = tasks.RunJob()
|
task = tasks.RunJob()
|
||||||
|
|
||||||
@ -358,7 +358,7 @@ class TestExtraVarSanitation(TestJobExecution):
|
|||||||
assert extra_vars['msg'] == self.UNSAFE
|
assert extra_vars['msg'] == self.UNSAFE
|
||||||
assert not hasattr(extra_vars['msg'], '__UNSAFE__')
|
assert not hasattr(extra_vars['msg'], '__UNSAFE__')
|
||||||
|
|
||||||
def test_nested_whitelisted_vars(self, job, private_data_dir):
|
def test_nested_allowed_vars(self, job, private_data_dir):
|
||||||
job.extra_vars = json.dumps({'msg': {'a': {'b': [self.UNSAFE]}}})
|
job.extra_vars = json.dumps({'msg': {'a': {'b': [self.UNSAFE]}}})
|
||||||
job.job_template.extra_vars = job.extra_vars
|
job.job_template.extra_vars = job.extra_vars
|
||||||
task = tasks.RunJob()
|
task = tasks.RunJob()
|
||||||
|
|||||||
@ -367,13 +367,13 @@ def get_allowed_fields(obj, serializer_mapping):
|
|||||||
'oauth2application': ['client_secret']
|
'oauth2application': ['client_secret']
|
||||||
}
|
}
|
||||||
model_name = obj._meta.model_name
|
model_name = obj._meta.model_name
|
||||||
field_blacklist = ACTIVITY_STREAM_FIELD_EXCLUSIONS.get(model_name, [])
|
fields_excluded = ACTIVITY_STREAM_FIELD_EXCLUSIONS.get(model_name, [])
|
||||||
# see definition of from_db for CredentialType
|
# see definition of from_db for CredentialType
|
||||||
# injection logic of any managed types are incompatible with activity stream
|
# injection logic of any managed types are incompatible with activity stream
|
||||||
if model_name == 'credentialtype' and obj.managed_by_tower and obj.namespace:
|
if model_name == 'credentialtype' and obj.managed_by_tower and obj.namespace:
|
||||||
field_blacklist.extend(['inputs', 'injectors'])
|
fields_excluded.extend(['inputs', 'injectors'])
|
||||||
if field_blacklist:
|
if fields_excluded:
|
||||||
allowed_fields = [f for f in allowed_fields if f not in field_blacklist]
|
allowed_fields = [f for f in allowed_fields if f not in fields_excluded]
|
||||||
return allowed_fields
|
return allowed_fields
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ from django.apps import apps
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from awx.main.constants import LOGGER_BLACKLIST
|
from awx.main.constants import LOGGER_BLOCKLIST
|
||||||
from awx.main.utils.common import get_search_fields
|
from awx.main.utils.common import get_search_fields
|
||||||
|
|
||||||
__all__ = ['SmartFilter', 'ExternalLoggerEnabled', 'DynamicLevelFilter']
|
__all__ = ['SmartFilter', 'ExternalLoggerEnabled', 'DynamicLevelFilter']
|
||||||
@ -48,11 +48,11 @@ class FieldFromSettings(object):
|
|||||||
instance.settings_override[self.setting_name] = value
|
instance.settings_override[self.setting_name] = value
|
||||||
|
|
||||||
|
|
||||||
def record_is_blacklisted(record):
|
def record_is_blocked(record):
|
||||||
"""Given a log record, return True if it is considered to be in
|
"""Given a log record, return True if it is considered to be
|
||||||
the logging blacklist, return False if not
|
blocked, return False if not
|
||||||
"""
|
"""
|
||||||
for logger_name in LOGGER_BLACKLIST:
|
for logger_name in LOGGER_BLOCKLIST:
|
||||||
if record.name.startswith(logger_name):
|
if record.name.startswith(logger_name):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@ -81,7 +81,7 @@ class ExternalLoggerEnabled(Filter):
|
|||||||
True - should be logged
|
True - should be logged
|
||||||
"""
|
"""
|
||||||
# Do not send exceptions to external logger
|
# Do not send exceptions to external logger
|
||||||
if record_is_blacklisted(record):
|
if record_is_blocked(record):
|
||||||
return False
|
return False
|
||||||
# General enablement
|
# General enablement
|
||||||
if not self.enabled_flag:
|
if not self.enabled_flag:
|
||||||
@ -108,8 +108,8 @@ class DynamicLevelFilter(Filter):
|
|||||||
"""Filters out logs that have a level below the threshold defined
|
"""Filters out logs that have a level below the threshold defined
|
||||||
by the databse setting LOG_AGGREGATOR_LEVEL
|
by the databse setting LOG_AGGREGATOR_LEVEL
|
||||||
"""
|
"""
|
||||||
if record_is_blacklisted(record):
|
if record_is_blocked(record):
|
||||||
# Fine to write blacklisted loggers to file, apply default filtering level
|
# Fine to write denied loggers to file, apply default filtering level
|
||||||
cutoff_level = logging.WARNING
|
cutoff_level = logging.WARNING
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@ -179,7 +179,7 @@ class SmartFilter(object):
|
|||||||
pyparsing do the heavy lifting.
|
pyparsing do the heavy lifting.
|
||||||
TODO: separate django filter requests from our custom json filter
|
TODO: separate django filter requests from our custom json filter
|
||||||
request so we don't process the key any. This could be
|
request so we don't process the key any. This could be
|
||||||
accomplished using a whitelist or introspecting the
|
accomplished using an allowed list or introspecting the
|
||||||
relationship refered to to see if it's a jsonb type.
|
relationship refered to to see if it's a jsonb type.
|
||||||
'''
|
'''
|
||||||
def _json_path_to_contains(self, k, v):
|
def _json_path_to_contains(self, k, v):
|
||||||
|
|||||||
@ -36,7 +36,7 @@ def safe_dump(x, safe_dict=None):
|
|||||||
_unless_ they've been deemed "trusted" (meaning, they likely were set/added
|
_unless_ they've been deemed "trusted" (meaning, they likely were set/added
|
||||||
by a user with a high level of privilege).
|
by a user with a high level of privilege).
|
||||||
|
|
||||||
This function allows you to pass in a trusted `safe_dict` to whitelist
|
This function allows you to pass in a trusted `safe_dict` to allow
|
||||||
certain extra vars so that they are _not_ marked as `!unsafe` in the
|
certain extra vars so that they are _not_ marked as `!unsafe` in the
|
||||||
resulting YAML. Anything _not_ in this dict will automatically be
|
resulting YAML. Anything _not_ in this dict will automatically be
|
||||||
`!unsafe`.
|
`!unsafe`.
|
||||||
|
|||||||
@ -163,13 +163,13 @@ ALLOWED_HOSTS = []
|
|||||||
REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
|
REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
|
||||||
|
|
||||||
# If Tower is behind a reverse proxy/load balancer, use this setting to
|
# If Tower is behind a reverse proxy/load balancer, use this setting to
|
||||||
# whitelist the proxy IP addresses from which Tower should trust custom
|
# allow the proxy IP addresses from which Tower should trust custom
|
||||||
# REMOTE_HOST_HEADERS header values
|
# REMOTE_HOST_HEADERS header values
|
||||||
# REMOTE_HOST_HEADERS = ['HTTP_X_FORWARDED_FOR', ''REMOTE_ADDR', 'REMOTE_HOST']
|
# REMOTE_HOST_HEADERS = ['HTTP_X_FORWARDED_FOR', ''REMOTE_ADDR', 'REMOTE_HOST']
|
||||||
# PROXY_IP_WHITELIST = ['10.0.1.100', '10.0.1.101']
|
# PROXY_IP_ALLOWED_LIST = ['10.0.1.100', '10.0.1.101']
|
||||||
# If this setting is an empty list (the default), the headers specified by
|
# If this setting is an empty list (the default), the headers specified by
|
||||||
# REMOTE_HOST_HEADERS will be trusted unconditionally')
|
# REMOTE_HOST_HEADERS will be trusted unconditionally')
|
||||||
PROXY_IP_WHITELIST = []
|
PROXY_IP_ALLOWED_LIST = []
|
||||||
|
|
||||||
CUSTOM_VENV_PATHS = []
|
CUSTOM_VENV_PATHS = []
|
||||||
|
|
||||||
@ -666,7 +666,7 @@ AD_HOC_COMMANDS = [
|
|||||||
'win_user',
|
'win_user',
|
||||||
]
|
]
|
||||||
|
|
||||||
INV_ENV_VARIABLE_BLACKLIST = ("HOME", "USER", "_", "TERM")
|
INV_ENV_VARIABLE_BLOCKED = ("HOME", "USER", "_", "TERM")
|
||||||
|
|
||||||
# ----------------
|
# ----------------
|
||||||
# -- Amazon EC2 --
|
# -- Amazon EC2 --
|
||||||
@ -694,11 +694,6 @@ EC2_REGION_NAMES = {
|
|||||||
'cn-north-1': _('China (Beijing)'),
|
'cn-north-1': _('China (Beijing)'),
|
||||||
}
|
}
|
||||||
|
|
||||||
EC2_REGIONS_BLACKLIST = [
|
|
||||||
'us-gov-west-1',
|
|
||||||
'cn-north-1',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Inventory variable name/values for determining if host is active/enabled.
|
# Inventory variable name/values for determining if host is active/enabled.
|
||||||
EC2_ENABLED_VAR = 'ec2_state'
|
EC2_ENABLED_VAR = 'ec2_state'
|
||||||
EC2_ENABLED_VALUE = 'running'
|
EC2_ENABLED_VALUE = 'running'
|
||||||
@ -715,8 +710,6 @@ EC2_EXCLUDE_EMPTY_GROUPS = True
|
|||||||
# ------------
|
# ------------
|
||||||
# -- VMware --
|
# -- VMware --
|
||||||
# ------------
|
# ------------
|
||||||
VMWARE_REGIONS_BLACKLIST = []
|
|
||||||
|
|
||||||
# Inventory variable name/values for determining whether a host is
|
# Inventory variable name/values for determining whether a host is
|
||||||
# active in vSphere.
|
# active in vSphere.
|
||||||
VMWARE_ENABLED_VAR = 'guest.gueststate'
|
VMWARE_ENABLED_VAR = 'guest.gueststate'
|
||||||
@ -771,8 +764,6 @@ GCE_REGION_CHOICES = [
|
|||||||
('australia-southeast1-b', _('Australia Southeast (B)')),
|
('australia-southeast1-b', _('Australia Southeast (B)')),
|
||||||
('australia-southeast1-c', _('Australia Southeast (C)')),
|
('australia-southeast1-c', _('Australia Southeast (C)')),
|
||||||
]
|
]
|
||||||
GCE_REGIONS_BLACKLIST = []
|
|
||||||
|
|
||||||
# Inventory variable name/value for determining whether a host is active
|
# Inventory variable name/value for determining whether a host is active
|
||||||
# in Google Compute Engine.
|
# in Google Compute Engine.
|
||||||
GCE_ENABLED_VAR = 'status'
|
GCE_ENABLED_VAR = 'status'
|
||||||
@ -817,8 +808,6 @@ AZURE_RM_REGION_CHOICES = [
|
|||||||
('koreacentral', _('Korea Central')),
|
('koreacentral', _('Korea Central')),
|
||||||
('koreasouth', _('Korea South')),
|
('koreasouth', _('Korea South')),
|
||||||
]
|
]
|
||||||
AZURE_RM_REGIONS_BLACKLIST = []
|
|
||||||
|
|
||||||
AZURE_RM_GROUP_FILTER = r'^.+$'
|
AZURE_RM_GROUP_FILTER = r'^.+$'
|
||||||
AZURE_RM_HOST_FILTER = r'^.+$'
|
AZURE_RM_HOST_FILTER = r'^.+$'
|
||||||
AZURE_RM_ENABLED_VAR = 'powerstate'
|
AZURE_RM_ENABLED_VAR = 'powerstate'
|
||||||
|
|||||||
@ -183,5 +183,4 @@ else:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
WEBSOCKET_ORIGIN_WHITELIST = ['https://localhost:8043', 'https://localhost:3000']
|
|
||||||
AWX_CALLBACK_PROFILE = True
|
AWX_CALLBACK_PROFILE = True
|
||||||
|
|||||||
@ -11,7 +11,7 @@ This IP address can be found by making a GET to any page on the browsable API an
|
|||||||
awx_1 | 14:42:08 uwsgi.1 | 172.18.0.1 GET /api/v2/tokens/ - HTTP/1.1 200
|
awx_1 | 14:42:08 uwsgi.1 | 172.18.0.1 GET /api/v2/tokens/ - HTTP/1.1 200
|
||||||
```
|
```
|
||||||
|
|
||||||
Whitelist this IP address by adding it to the `INTERNAL_IPS` variable in `local_settings`, then navigate to the API and you should see DDT on the
|
Allow this IP address by adding it to the `INTERNAL_IPS` variable in `local_settings`, then navigate to the API and you should see DDT on the
|
||||||
right side. If you don't see it, make sure to set `DEBUG=True`.
|
right side. If you don't see it, make sure to set `DEBUG=True`.
|
||||||
> Note that enabling DDT is detrimental to the performance of AWX and adds overhead to every API request. It is
|
> Note that enabling DDT is detrimental to the performance of AWX and adds overhead to every API request. It is
|
||||||
recommended to keep this turned off when you are not using it.
|
recommended to keep this turned off when you are not using it.
|
||||||
|
|||||||
@ -32,7 +32,7 @@ The standard pattern applies to the following fields:
|
|||||||
- `ask_inventory_on_launch` allows use of `inventory`
|
- `ask_inventory_on_launch` allows use of `inventory`
|
||||||
|
|
||||||
Surveys are a special-case of prompting for variables - applying a survey to
|
Surveys are a special-case of prompting for variables - applying a survey to
|
||||||
a template whitelists variable names in the survey spec (requires the survey
|
a template allows variable names in the survey spec (requires the survey
|
||||||
spec to exist and `survey_enabled` to be true). On the other hand,
|
spec to exist and `survey_enabled` to be true). On the other hand,
|
||||||
if `ask_variables_on_launch` is true, users can provide any variables in
|
if `ask_variables_on_launch` is true, users can provide any variables in
|
||||||
`extra_vars`.
|
`extra_vars`.
|
||||||
|
|||||||
@ -132,7 +132,7 @@ user performing the copy, and `new_objs`, a list of all sub objects of the creat
|
|||||||
`user`'s permission against these new sub objects and unlink related objects or send
|
`user`'s permission against these new sub objects and unlink related objects or send
|
||||||
warning logs as necessary. `deep_copy_permission_check_func` should not return anything.
|
warning logs as necessary. `deep_copy_permission_check_func` should not return anything.
|
||||||
|
|
||||||
Lastly, macro `REENCRYPTION_BLACKLIST_AT_COPY` is available as part of a model definition. It is a
|
Lastly, macro `REENCRYPTION_BLOCKLIST_AT_COPY` is available as part of a model definition. It is a
|
||||||
list of field names which will escape re-encryption during copy. For example, the `extra_data` field
|
list of field names which will escape re-encryption during copy. For example, the `extra_data` field
|
||||||
of workflow job template nodes.
|
of workflow job template nodes.
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user