Skip to content

Commit

Permalink
Merge branch 'ansible:devel' into AAP-22242
Browse files Browse the repository at this point in the history
  • Loading branch information
chadmf authored May 14, 2024
2 parents c1899bb + da2bccf commit f7b41f2
Show file tree
Hide file tree
Showing 50 changed files with 492 additions and 346 deletions.
75 changes: 0 additions & 75 deletions .github/workflows/e2e_test.yml

This file was deleted.

2 changes: 1 addition & 1 deletion ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ If any of those items are missing your pull request will still get the `needs_tr
Currently you can expect awxbot to add common labels such as `state:needs_triage`, `type:bug`, `component:docs`, etc...
These labels are determined by the template data. Please use the template and fill it out as accurately as possible.

The `state:needs_triage` label will will remain on your pull request until a person has looked at it.
The `state:needs_triage` label will remain on your pull request until a person has looked at it.

You can also expect the bot to CC maintainers of specific areas of the code, this will notify them that there is a pull request by placing a comment on the pull request.
The comment will look something like `CC @matburt @wwitzel3 ...`.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ docker-clean:
-$(foreach image_id,$(shell docker images --filter=reference='*/*/*awx_devel*' --filter=reference='*/*awx_devel*' --filter=reference='*awx_devel*' -aq),docker rmi --force $(image_id);)

docker-clean-volumes: docker-compose-clean docker-compose-container-group-clean
docker volume rm -f tools_var_lib_awx tools_awx_db tools_awx_db_15 tools_vault_1 tools_ldap_1 tools_grafana_storage tools_prometheus_storage $(docker volume ls --filter name=tools_redis_socket_ -q)
docker volume rm -f tools_var_lib_awx tools_awx_db tools_awx_db_15 tools_vault_1 tools_ldap_1 tools_grafana_storage tools_prometheus_storage $(shell docker volume ls --filter name=tools_redis_socket_ -q)

docker-refresh: docker-clean docker-compose

Expand Down
7 changes: 6 additions & 1 deletion awx/api/generics.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ def post(self, request, *args, **kwargs):
ret = super(LoggedLoginView, self).post(request, *args, **kwargs)
if request.user.is_authenticated:
logger.info(smart_str(u"User {} logged in from {}".format(self.request.user.username, request.META.get('REMOTE_ADDR', None))))
ret.set_cookie('userLoggedIn', 'true', secure=getattr(settings, 'SESSION_COOKIE_SECURE', False))
ret.set_cookie(
'userLoggedIn', 'true', secure=getattr(settings, 'SESSION_COOKIE_SECURE', False), samesite=getattr(settings, 'USER_COOKIE_SAMESITE', 'Lax')
)
ret.setdefault('X-API-Session-Cookie-Name', getattr(settings, 'SESSION_COOKIE_NAME', 'awx_sessionid'))

return ret
Expand All @@ -107,6 +109,9 @@ def post(self, request, *args, **kwargs):


class LoggedLogoutView(auth_views.LogoutView):

success_url_allowed_hosts = settings.LOGOUT_ALLOWED_HOSTS

def dispatch(self, request, *args, **kwargs):
original_user = getattr(request, 'user', None)
ret = super(LoggedLogoutView, self).dispatch(request, *args, **kwargs)
Expand Down
6 changes: 6 additions & 0 deletions awx/conf/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ class StringListBooleanField(ListField):

def to_representation(self, value):
try:
if isinstance(value, str):
# https://github.com/encode/django-rest-framework/commit/a180bde0fd965915718b070932418cabc831cee1
# DRF changed truthy and falsy lists to be capitalized
value = value.lower()
if isinstance(value, (list, tuple)):
return super(StringListBooleanField, self).to_representation(value)
elif value in BooleanField.TRUE_VALUES:
Expand All @@ -78,6 +82,8 @@ def to_representation(self, value):

def to_internal_value(self, data):
try:
if isinstance(data, str):
data = data.lower()
if isinstance(data, (list, tuple)):
return super(StringListBooleanField, self).to_internal_value(data)
elif data in BooleanField.TRUE_VALUES:
Expand Down
25 changes: 25 additions & 0 deletions awx/main/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging

# Django
from django.core.checks import Error
from django.utils.translation import gettext_lazy as _

# Django REST Framework
Expand Down Expand Up @@ -954,3 +955,27 @@ def logging_validate(serializer, attrs):


register_validate('logging', logging_validate)


def csrf_trusted_origins_validate(serializer, attrs):
if not serializer.instance or not hasattr(serializer.instance, 'CSRF_TRUSTED_ORIGINS'):
return attrs
if 'CSRF_TRUSTED_ORIGINS' not in attrs:
return attrs
errors = []
for origin in attrs['CSRF_TRUSTED_ORIGINS']:
if "://" not in origin:
errors.append(
Error(
"As of Django 4.0, the values in the CSRF_TRUSTED_ORIGINS "
"setting must start with a scheme (usually http:// or "
"https://) but found %s. See the release notes for details." % origin,
)
)
if errors:
error_messages = [error.msg for error in errors]
raise serializers.ValidationError(_('\n'.join(error_messages)))
return attrs


register_validate('system', csrf_trusted_origins_validate)
44 changes: 30 additions & 14 deletions awx/main/management/commands/dump_auth_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
import os
import sys
import re

from typing import Any

from django.core.management.base import BaseCommand
from django.conf import settings

from awx.conf import settings_registry


Expand Down Expand Up @@ -40,6 +41,15 @@ class Command(BaseCommand):
"USER_SEARCH": False,
}

def is_enabled(self, settings, keys):
missing_fields = []
for key, required in keys.items():
if required and not settings.get(key):
missing_fields.append(key)
if missing_fields:
return False, missing_fields
return True, None

def get_awx_ldap_settings(self) -> dict[str, dict[str, Any]]:
awx_ldap_settings = {}

Expand All @@ -64,14 +74,16 @@ def get_awx_ldap_settings(self) -> dict[str, dict[str, Any]]:

if new_key == "SERVER_URI" and value:
value = value.split(", ")
grouped_settings[index][new_key] = value

return grouped_settings
if type(value).__name__ == "LDAPSearch":
data = []
data.append(value.base_dn)
data.append("SCOPE_SUBTREE")
data.append(value.filterstr)
grouped_settings[index][new_key] = data

def is_enabled(self, settings, keys):
for key, required in keys.items():
if required and not settings.get(key):
return False
return True
return grouped_settings

def get_awx_saml_settings(self) -> dict[str, Any]:
awx_saml_settings = {}
Expand All @@ -82,7 +94,7 @@ def get_awx_saml_settings(self) -> dict[str, Any]:

def format_config_data(self, enabled, awx_settings, type, keys, name):
config = {
"type": f"awx.authentication.authenticator_plugins.{type}",
"type": f"ansible_base.authentication.authenticator_plugins.{type}",
"name": name,
"enabled": enabled,
"create_objects": True,
Expand Down Expand Up @@ -130,7 +142,7 @@ def handle(self, *args, **options):

# dump SAML settings
awx_saml_settings = self.get_awx_saml_settings()
awx_saml_enabled = self.is_enabled(awx_saml_settings, self.DAB_SAML_AUTHENTICATOR_KEYS)
awx_saml_enabled, saml_missing_fields = self.is_enabled(awx_saml_settings, self.DAB_SAML_AUTHENTICATOR_KEYS)
if awx_saml_enabled:
awx_saml_name = awx_saml_settings["ENABLED_IDPS"]
data.append(
Expand All @@ -142,21 +154,25 @@ def handle(self, *args, **options):
awx_saml_name,
)
)
else:
data.append({"SAML_missing_fields": saml_missing_fields})

# dump LDAP settings
awx_ldap_group_settings = self.get_awx_ldap_settings()
for awx_ldap_name, awx_ldap_settings in enumerate(awx_ldap_group_settings.values()):
enabled = self.is_enabled(awx_ldap_settings, self.DAB_LDAP_AUTHENTICATOR_KEYS)
if enabled:
for awx_ldap_name, awx_ldap_settings in awx_ldap_group_settings.items():
awx_ldap_enabled, ldap_missing_fields = self.is_enabled(awx_ldap_settings, self.DAB_LDAP_AUTHENTICATOR_KEYS)
if awx_ldap_enabled:
data.append(
self.format_config_data(
enabled,
awx_ldap_enabled,
awx_ldap_settings,
"ldap",
self.DAB_LDAP_AUTHENTICATOR_KEYS,
str(awx_ldap_name),
f"LDAP_{awx_ldap_name}",
)
)
else:
data.append({f"LDAP_{awx_ldap_name}_missing_fields": ldap_missing_fields})

# write to file if requested
if options["output_file"]:
Expand Down
3 changes: 1 addition & 2 deletions awx/main/management/commands/run_wsrelay.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,10 @@ def handle(self, *arg, **options):
return

WebsocketsMetricsServer().start()
websocket_relay_manager = WebSocketRelayManager()

while True:
try:
asyncio.run(websocket_relay_manager.run())
asyncio.run(WebSocketRelayManager().run())
except KeyboardInterrupt:
logger.info('Shutting down Websocket Relayer')
break
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class Migration(migrations.Migration):

dependencies = [
('main', '0189_inbound_hop_nodes'),
]
Expand Down
20 changes: 18 additions & 2 deletions awx/main/migrations/_dab_rbac.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@ def get_permissions_for_role(role_field, children_map, apps):
return perm_list


def model_class(ct, apps):
"""
You can not use model methods in migrations, so this duplicates
what ContentType.model_class does, using current apps
"""
try:
return apps.get_model(ct.app_label, ct.model)
except LookupError:
return None


def migrate_to_new_rbac(apps, schema_editor):
"""
This method moves the assigned permissions from the old rbac.py models
Expand Down Expand Up @@ -197,7 +208,7 @@ def migrate_to_new_rbac(apps, schema_editor):
role_definition = managed_definitions[permissions]
else:
action = role.role_field.rsplit('_', 1)[0] # remove the _field ending of the name
role_definition_name = f'{role.content_type.model_class().__name__} {action.title()}'
role_definition_name = f'{model_class(role.content_type, apps).__name__} {action.title()}'

description = role_descriptions[role.role_field]
if type(description) == dict:
Expand Down Expand Up @@ -264,7 +275,12 @@ def setup_managed_role_definitions(apps, schema_editor):
"""
Idepotent method to create or sync the managed role definitions
"""
to_create = settings.ANSIBLE_BASE_ROLE_PRECREATE
to_create = {
'object_admin': '{cls.__name__} Admin',
'org_admin': 'Organization Admin',
'org_children': 'Organization {cls.__name__} Admin',
'special': '{cls.__name__} {action}',
}

ContentType = apps.get_model('contenttypes', 'ContentType')
Permission = apps.get_model('dab_rbac', 'DABPermission')
Expand Down
8 changes: 8 additions & 0 deletions awx/main/models/credential/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,14 @@ def create(self):
'multiline': True,
'help_text': gettext_noop('Terraform backend config as Hashicorp configuration language.'),
},
{
'id': 'gce_credentials',
'label': gettext_noop('Google Cloud Platform account credentials'),
'type': 'string',
'secret': True,
'multiline': True,
'help_text': gettext_noop('Google Cloud Platform account credentials in JSON format.'),
},
],
'required': ['configuration'],
},
Expand Down
7 changes: 7 additions & 0 deletions awx/main/models/credential/injectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,10 @@ def terraform(cred, env, private_data_dir):
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
f.write(cred.get_input('configuration'))
env['TF_BACKEND_CONFIG_FILE'] = to_container_path(path, private_data_dir)
# Handle env variables for GCP account credentials
if 'gce_credentials' in cred.inputs:
handle, path = tempfile.mkstemp(dir=os.path.join(private_data_dir, 'env'))
with os.fdopen(handle, 'w') as f:
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
f.write(cred.get_input('gce_credentials'))
env['GOOGLE_BACKEND_CREDENTIALS'] = to_container_path(path, private_data_dir)
Loading

0 comments on commit f7b41f2

Please sign in to comment.