mirror of
https://github.com/ansible/awx.git
synced 2026-01-09 23:12:08 -03:30
feat: AAP-48498 RADIUS authenticator migrator (#7013)
* feat: AAP-48498 Radius authenticator migrator Issue: AAP-48498 * fix: Namingm Style and tests * enabled by default * test: SECRET is now ignored unless --force is set
This commit is contained in:
parent
ab9bde3698
commit
abc4692231
@ -7,6 +7,7 @@ from awx.sso.utils.github_migrator import GitHubMigrator
|
||||
from awx.sso.utils.ldap_migrator import LDAPMigrator
|
||||
from awx.sso.utils.oidc_migrator import OIDCMigrator
|
||||
from awx.sso.utils.saml_migrator import SAMLMigrator
|
||||
from awx.sso.utils.radius_migrator import RADIUSMigrator
|
||||
from awx.main.utils.gateway_client import GatewayClient, GatewayAPIError
|
||||
|
||||
|
||||
@ -18,6 +19,7 @@ class Command(BaseCommand):
|
||||
parser.add_argument('--skip-ldap', action='store_true', help='Skip importing LDAP authenticators')
|
||||
parser.add_argument('--skip-ad', action='store_true', help='Skip importing Azure AD authenticator')
|
||||
parser.add_argument('--skip-saml', action='store_true', help='Skip importing SAML authenticator')
|
||||
parser.add_argument('--skip-radius', action='store_true', help='Skip importing RADIUS authenticator')
|
||||
parser.add_argument('--force', action='store_true', help='Force migration even if configurations already exist')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
@ -31,6 +33,7 @@ class Command(BaseCommand):
|
||||
skip_ldap = options['skip_ldap']
|
||||
skip_ad = options['skip_ad']
|
||||
skip_saml = options['skip_saml']
|
||||
skip_radius = options['skip_radius']
|
||||
force = options['force']
|
||||
|
||||
# If the management command isn't called with all parameters needed to talk to Gateway, consider
|
||||
@ -71,6 +74,9 @@ class Command(BaseCommand):
|
||||
if not skip_ldap:
|
||||
migrators.append(LDAPMigrator(gateway_client, self, force=force))
|
||||
|
||||
if not skip_radius:
|
||||
migrators.append(RADIUSMigrator(gateway_client, self, force=force))
|
||||
|
||||
# Run migrations
|
||||
total_results = {
|
||||
'created': 0,
|
||||
|
||||
@ -247,12 +247,12 @@ class TestAuthenticatorConfigComparison:
|
||||
match, differences = self.migrator._authenticator_configs_match(existing_auth, new_config, ignore_keys)
|
||||
|
||||
assert match is False
|
||||
assert len(differences) == 3 # KEY, SECRET, NEW_FIELD
|
||||
assert len(differences) == 2 # KEY, NEW_FIELD (SECRET shows up only if --force is used)
|
||||
|
||||
# Check that all expected differences are captured
|
||||
difference_text = ' '.join(differences)
|
||||
assert 'KEY:' in difference_text
|
||||
assert 'SECRET:' in difference_text
|
||||
# assert 'SECRET:' in difference_text # SECRET shows up only if --force is used
|
||||
assert 'NEW_FIELD:' in difference_text
|
||||
assert 'CALLBACK_URL' not in difference_text # Should be ignored
|
||||
|
||||
|
||||
@ -34,6 +34,13 @@ def existing_tacacsplus_user():
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_radius_config(settings):
|
||||
settings.RADIUS_SERVER = '127.0.0.1'
|
||||
settings.RADIUS_PORT = 1812
|
||||
settings.RADIUS_SECRET = 'secret'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_saml_config(settings):
|
||||
settings.SAML_SECURITY_CONFIG = {
|
||||
|
||||
17
awx/sso/tests/unit/test_radius_migrator.py
Normal file
17
awx/sso/tests/unit/test_radius_migrator.py
Normal file
@ -0,0 +1,17 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
from awx.sso.utils.radius_migrator import RADIUSMigrator
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_controller_config(test_radius_config):
|
||||
gateway_client = MagicMock()
|
||||
command_obj = MagicMock()
|
||||
obj = RADIUSMigrator(gateway_client, command_obj)
|
||||
|
||||
result = obj.get_controller_config()
|
||||
config = result[0]['settings']['configuration']
|
||||
assert config['SERVER'] == '127.0.0.1'
|
||||
assert config['PORT'] == 1812
|
||||
assert config['SECRET'] == 'secret'
|
||||
assert len(config) == 3
|
||||
86
awx/sso/utils/radius_migrator.py
Normal file
86
awx/sso/utils/radius_migrator.py
Normal file
@ -0,0 +1,86 @@
|
||||
"""
|
||||
RADIUS authenticator migrator.
|
||||
|
||||
This module handles the migration of RADIUS authenticators from AWX to Gateway.
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from awx.sso.utils.base_migrator import BaseAuthenticatorMigrator
|
||||
|
||||
|
||||
class RADIUSMigrator(BaseAuthenticatorMigrator):
|
||||
"""
|
||||
Handles the migration of RADIUS authenticators from AWX to Gateway.
|
||||
"""
|
||||
|
||||
CATEGORY = "RADIUS"
|
||||
AUTH_TYPE = "ansible_base.authentication.authenticator_plugins.radius"
|
||||
|
||||
def get_authenticator_type(self):
|
||||
"""Get the human-readable authenticator type name."""
|
||||
return "RADIUS"
|
||||
|
||||
def get_controller_config(self):
|
||||
"""
|
||||
Export RADIUS authenticators. A RADIUS authenticator is only exported if
|
||||
required configuration is present.
|
||||
|
||||
Returns:
|
||||
list: List of configured RADIUS authentication providers with their settings
|
||||
"""
|
||||
server = getattr(settings, "RADIUS_SERVER", None)
|
||||
if not server:
|
||||
return []
|
||||
|
||||
port = getattr(settings, "RADIUS_PORT", 1812)
|
||||
secret = getattr(settings, "RADIUS_SECRET", "")
|
||||
|
||||
config_data = {
|
||||
"name": "default",
|
||||
"type": self.AUTH_TYPE,
|
||||
"enabled": True,
|
||||
"create_objects": True,
|
||||
"remove_users": False,
|
||||
"configuration": {
|
||||
"SERVER": server,
|
||||
"PORT": port,
|
||||
"SECRET": secret,
|
||||
},
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
"category": self.CATEGORY,
|
||||
"settings": config_data,
|
||||
}
|
||||
]
|
||||
|
||||
def create_gateway_authenticator(self, config):
|
||||
"""Create a RADIUS authenticator in Gateway."""
|
||||
category = config["category"]
|
||||
config_settings = config["settings"]
|
||||
name = config_settings["name"]
|
||||
|
||||
# Generate authenticator name and slug
|
||||
authenticator_name = f"AWX-{category.replace('-', '_').title()}-{name}"
|
||||
authenticator_slug = self._generate_authenticator_slug("radius", category)
|
||||
|
||||
self._write_output(f"\n--- Processing {category} authenticator ---")
|
||||
self._write_output(f"Name: {authenticator_name}")
|
||||
self._write_output(f"Slug: {authenticator_slug}")
|
||||
self._write_output(f"Type: {config_settings['type']}")
|
||||
|
||||
# Build Gateway authenticator configuration
|
||||
gateway_config = {
|
||||
"name": authenticator_name,
|
||||
"slug": authenticator_slug,
|
||||
"type": config_settings["type"],
|
||||
"enabled": config_settings["enabled"],
|
||||
"create_objects": config_settings["create_objects"],
|
||||
"remove_users": config_settings["remove_users"],
|
||||
"configuration": config_settings["configuration"],
|
||||
}
|
||||
|
||||
# Submit the authenticator (create or update as needed)
|
||||
return self.submit_authenticator(gateway_config, config=config)
|
||||
Loading…
x
Reference in New Issue
Block a user