mirror of
https://github.com/ansible/awx.git
synced 2026-05-23 16:47:45 -02:30
Aap 49570 (#7022)
* consider global org and team maps for github authenticator * consider global org and team maps for saml authenticator
This commit is contained in:
@@ -3,7 +3,7 @@ Unit tests for base authenticator migrator functionality.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock, patch
|
||||||
from awx.sso.utils.base_migrator import BaseAuthenticatorMigrator
|
from awx.sso.utils.base_migrator import BaseAuthenticatorMigrator
|
||||||
|
|
||||||
|
|
||||||
@@ -684,3 +684,188 @@ def test_mappers_match_structurally_edge_cases(mapper1, mapper2, expected):
|
|||||||
|
|
||||||
result = migrator._mappers_match_structurally(mapper1, mapper2)
|
result = migrator._mappers_match_structurally(mapper1, mapper2)
|
||||||
assert result == expected
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
class TestSocialAuthMapFunctions:
|
||||||
|
"""Test cases for social auth map functions."""
|
||||||
|
|
||||||
|
def setup_method(self):
|
||||||
|
"""Set up test fixtures."""
|
||||||
|
self.gateway_client = Mock()
|
||||||
|
self.command_obj = Mock()
|
||||||
|
self.migrator = BaseAuthenticatorMigrator(self.gateway_client, self.command_obj)
|
||||||
|
|
||||||
|
@patch('awx.sso.utils.base_migrator.settings')
|
||||||
|
def test_get_social_org_map_with_authenticator_specific_setting(self, mock_settings):
|
||||||
|
"""Test get_social_org_map returns authenticator-specific setting when available."""
|
||||||
|
# Set up mock settings
|
||||||
|
authenticator_map = {'org1': ['team1', 'team2']}
|
||||||
|
global_map = {'global_org': ['global_team']}
|
||||||
|
|
||||||
|
mock_settings.SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP = authenticator_map
|
||||||
|
mock_settings.SOCIAL_AUTH_ORGANIZATION_MAP = global_map
|
||||||
|
|
||||||
|
# Mock getattr to return the specific setting
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {
|
||||||
|
'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP': authenticator_map,
|
||||||
|
'SOCIAL_AUTH_ORGANIZATION_MAP': global_map,
|
||||||
|
}.get(name, default)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_org_map('SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP')
|
||||||
|
|
||||||
|
assert result == authenticator_map
|
||||||
|
# Verify it was called with the authenticator-specific setting first
|
||||||
|
mock_getattr.assert_any_call(mock_settings, 'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP', None)
|
||||||
|
|
||||||
|
@patch('awx.sso.utils.base_migrator.settings')
|
||||||
|
def test_get_social_org_map_fallback_to_global(self, mock_settings):
|
||||||
|
"""Test get_social_org_map falls back to global setting when authenticator-specific is empty."""
|
||||||
|
# Set up mock settings
|
||||||
|
global_map = {'global_org': ['global_team']}
|
||||||
|
|
||||||
|
# Mock getattr to return None for authenticator-specific, global for fallback
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {
|
||||||
|
'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP': None,
|
||||||
|
'SOCIAL_AUTH_ORGANIZATION_MAP': global_map,
|
||||||
|
}.get(name, default)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_org_map('SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP')
|
||||||
|
|
||||||
|
assert result == global_map
|
||||||
|
# Verify both calls were made
|
||||||
|
mock_getattr.assert_any_call(mock_settings, 'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP', None)
|
||||||
|
mock_getattr.assert_any_call(mock_settings, 'SOCIAL_AUTH_ORGANIZATION_MAP', {})
|
||||||
|
|
||||||
|
@patch('awx.sso.utils.base_migrator.settings')
|
||||||
|
def test_get_social_org_map_empty_dict_fallback(self, mock_settings):
|
||||||
|
"""Test get_social_org_map returns empty dict when neither setting exists."""
|
||||||
|
# Mock getattr to return None for both settings
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP': None, 'SOCIAL_AUTH_ORGANIZATION_MAP': {}}.get(
|
||||||
|
name, default
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_org_map('SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP')
|
||||||
|
|
||||||
|
assert result == {}
|
||||||
|
|
||||||
|
@patch('awx.sso.utils.base_migrator.settings')
|
||||||
|
def test_get_social_team_map_with_authenticator_specific_setting(self, mock_settings):
|
||||||
|
"""Test get_social_team_map returns authenticator-specific setting when available."""
|
||||||
|
# Set up mock settings
|
||||||
|
authenticator_map = {'team1': {'organization': 'org1'}}
|
||||||
|
global_map = {'global_team': {'organization': 'global_org'}}
|
||||||
|
|
||||||
|
# Mock getattr to return the specific setting
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {
|
||||||
|
'SOCIAL_AUTH_GITHUB_TEAM_MAP': authenticator_map,
|
||||||
|
'SOCIAL_AUTH_TEAM_MAP': global_map,
|
||||||
|
}.get(name, default)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_team_map('SOCIAL_AUTH_GITHUB_TEAM_MAP')
|
||||||
|
|
||||||
|
assert result == authenticator_map
|
||||||
|
# Verify it was called with the authenticator-specific setting first
|
||||||
|
mock_getattr.assert_any_call(mock_settings, 'SOCIAL_AUTH_GITHUB_TEAM_MAP', None)
|
||||||
|
|
||||||
|
@patch('awx.sso.utils.base_migrator.settings')
|
||||||
|
def test_get_social_team_map_fallback_to_global(self, mock_settings):
|
||||||
|
"""Test get_social_team_map falls back to global setting when authenticator-specific is empty."""
|
||||||
|
# Set up mock settings
|
||||||
|
global_map = {'global_team': {'organization': 'global_org'}}
|
||||||
|
|
||||||
|
# Mock getattr to return None for authenticator-specific, global for fallback
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {'SOCIAL_AUTH_GITHUB_TEAM_MAP': None, 'SOCIAL_AUTH_TEAM_MAP': global_map}.get(
|
||||||
|
name, default
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_team_map('SOCIAL_AUTH_GITHUB_TEAM_MAP')
|
||||||
|
|
||||||
|
assert result == global_map
|
||||||
|
# Verify both calls were made
|
||||||
|
mock_getattr.assert_any_call(mock_settings, 'SOCIAL_AUTH_GITHUB_TEAM_MAP', None)
|
||||||
|
mock_getattr.assert_any_call(mock_settings, 'SOCIAL_AUTH_TEAM_MAP', {})
|
||||||
|
|
||||||
|
@patch('awx.sso.utils.base_migrator.settings')
|
||||||
|
def test_get_social_team_map_empty_dict_fallback(self, mock_settings):
|
||||||
|
"""Test get_social_team_map returns empty dict when neither setting exists."""
|
||||||
|
# Mock getattr to return None for both settings
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {'SOCIAL_AUTH_GITHUB_TEAM_MAP': None, 'SOCIAL_AUTH_TEAM_MAP': {}}.get(name, default)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_team_map('SOCIAL_AUTH_GITHUB_TEAM_MAP')
|
||||||
|
|
||||||
|
assert result == {}
|
||||||
|
|
||||||
|
@patch('awx.sso.utils.base_migrator.settings')
|
||||||
|
def test_get_social_org_map_with_empty_string_fallback(self, mock_settings):
|
||||||
|
"""Test get_social_org_map falls back to global when authenticator-specific is empty string."""
|
||||||
|
# Set up mock settings
|
||||||
|
global_map = {'global_org': ['global_team']}
|
||||||
|
|
||||||
|
# Mock getattr to return empty string for authenticator-specific
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {
|
||||||
|
'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP': '',
|
||||||
|
'SOCIAL_AUTH_ORGANIZATION_MAP': global_map,
|
||||||
|
}.get(name, default)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_org_map('SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP')
|
||||||
|
|
||||||
|
assert result == global_map
|
||||||
|
|
||||||
|
@patch('awx.sso.utils.base_migrator.settings')
|
||||||
|
def test_get_social_team_map_with_empty_dict_fallback(self, mock_settings):
|
||||||
|
"""Test get_social_team_map falls back to global when authenticator-specific is empty dict."""
|
||||||
|
# Set up mock settings
|
||||||
|
global_map = {'global_team': {'organization': 'global_org'}}
|
||||||
|
|
||||||
|
# Mock getattr to return empty dict for authenticator-specific
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {'SOCIAL_AUTH_GITHUB_TEAM_MAP': {}, 'SOCIAL_AUTH_TEAM_MAP': global_map}.get(
|
||||||
|
name, default
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_team_map('SOCIAL_AUTH_GITHUB_TEAM_MAP')
|
||||||
|
|
||||||
|
# Empty dict is falsy, so it should fall back to global
|
||||||
|
assert result == global_map
|
||||||
|
|
||||||
|
def test_get_social_org_map_different_authenticators(self):
|
||||||
|
"""Test get_social_org_map works with different authenticator setting names."""
|
||||||
|
test_cases = [
|
||||||
|
'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP',
|
||||||
|
'SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP',
|
||||||
|
'SOCIAL_AUTH_SAML_ORGANIZATION_MAP',
|
||||||
|
'SOCIAL_AUTH_OIDC_ORGANIZATION_MAP',
|
||||||
|
]
|
||||||
|
|
||||||
|
for setting_name in test_cases:
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {
|
||||||
|
setting_name: {'test_org': ['test_team']},
|
||||||
|
'SOCIAL_AUTH_ORGANIZATION_MAP': {'fallback_org': ['fallback_team']},
|
||||||
|
}.get(name, default)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_org_map(setting_name)
|
||||||
|
|
||||||
|
assert result == {'test_org': ['test_team']}
|
||||||
|
|
||||||
|
def test_get_social_team_map_different_authenticators(self):
|
||||||
|
"""Test get_social_team_map works with different authenticator setting names."""
|
||||||
|
test_cases = ['SOCIAL_AUTH_GITHUB_TEAM_MAP', 'SOCIAL_AUTH_AZUREAD_OAUTH2_TEAM_MAP', 'SOCIAL_AUTH_SAML_TEAM_MAP', 'SOCIAL_AUTH_OIDC_TEAM_MAP']
|
||||||
|
|
||||||
|
for setting_name in test_cases:
|
||||||
|
with patch('awx.sso.utils.base_migrator.getattr') as mock_getattr:
|
||||||
|
mock_getattr.side_effect = lambda obj, name, default=None: {
|
||||||
|
setting_name: {'test_team': {'organization': 'test_org'}},
|
||||||
|
'SOCIAL_AUTH_TEAM_MAP': {'fallback_team': {'organization': 'fallback_org'}},
|
||||||
|
}.get(name, default)
|
||||||
|
|
||||||
|
result = self.migrator.get_social_team_map(setting_name)
|
||||||
|
|
||||||
|
assert result == {'test_team': {'organization': 'test_org'}}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Base authenticator migrator class.
|
|||||||
This module defines the contract that all specific authenticator migrators must follow.
|
This module defines the contract that all specific authenticator migrators must follow.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from awx.main.utils.gateway_client import GatewayAPIError
|
from awx.main.utils.gateway_client import GatewayAPIError
|
||||||
|
|
||||||
|
|
||||||
@@ -512,6 +513,46 @@ class BaseAuthenticatorMigrator:
|
|||||||
self._write_output(f' ✗ Unexpected error creating {mapper_type} mapper "{mapper_name}": {str(e)}', 'error')
|
self._write_output(f' ✗ Unexpected error creating {mapper_type} mapper "{mapper_name}": {str(e)}', 'error')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_social_org_map(self, authenticator_setting_name):
|
||||||
|
"""
|
||||||
|
Get social auth organization map with fallback to global setting.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
authenticator_setting_name: Name of the authenticator-specific organization map setting
|
||||||
|
(e.g., 'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Organization mapping configuration, with fallback to global setting
|
||||||
|
"""
|
||||||
|
# Try authenticator-specific setting first
|
||||||
|
authenticator_map = getattr(settings, authenticator_setting_name, None)
|
||||||
|
if authenticator_map:
|
||||||
|
return authenticator_map
|
||||||
|
|
||||||
|
# Fall back to global setting
|
||||||
|
global_map = getattr(settings, 'SOCIAL_AUTH_ORGANIZATION_MAP', {})
|
||||||
|
return global_map
|
||||||
|
|
||||||
|
def get_social_team_map(self, authenticator_setting_name):
|
||||||
|
"""
|
||||||
|
Get social auth team map with fallback to global setting.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
authenticator_setting_name: Name of the authenticator-specific team map setting
|
||||||
|
(e.g., 'SOCIAL_AUTH_GITHUB_TEAM_MAP')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Team mapping configuration, with fallback to global setting
|
||||||
|
"""
|
||||||
|
# Try authenticator-specific setting first
|
||||||
|
authenticator_map = getattr(settings, authenticator_setting_name, None)
|
||||||
|
if authenticator_map:
|
||||||
|
return authenticator_map
|
||||||
|
|
||||||
|
# Fall back to global setting
|
||||||
|
global_map = getattr(settings, 'SOCIAL_AUTH_TEAM_MAP', {})
|
||||||
|
return global_map
|
||||||
|
|
||||||
def _write_output(self, message, style=None):
|
def _write_output(self, message, style=None):
|
||||||
"""Write output message if command is available."""
|
"""Write output message if command is available."""
|
||||||
if self.command:
|
if self.command:
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ class GitHubMigrator(BaseAuthenticatorMigrator):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# If we have both key and secret, collect all settings
|
# If we have both key and secret, collect all settings
|
||||||
org_map_value = None
|
org_map_setting_name = None
|
||||||
team_map_value = None
|
team_map_setting_name = None
|
||||||
|
|
||||||
for setting_name in category_settings:
|
for setting_name in category_settings:
|
||||||
# Skip if setting_name is not a string (e.g., regex pattern)
|
# Skip if setting_name is not a string (e.g., regex pattern)
|
||||||
@@ -76,11 +76,15 @@ class GitHubMigrator(BaseAuthenticatorMigrator):
|
|||||||
value = getattr(settings, setting_name, None)
|
value = getattr(settings, setting_name, None)
|
||||||
config_data[setting_name] = value
|
config_data[setting_name] = value
|
||||||
|
|
||||||
# Capture org and team map values for special processing
|
# Capture org and team map setting names for special processing
|
||||||
if setting_name.endswith('_ORGANIZATION_MAP'):
|
if setting_name.endswith('_ORGANIZATION_MAP'):
|
||||||
org_map_value = value
|
org_map_setting_name = setting_name
|
||||||
elif setting_name.endswith('_TEAM_MAP'):
|
elif setting_name.endswith('_TEAM_MAP'):
|
||||||
team_map_value = value
|
team_map_setting_name = setting_name
|
||||||
|
|
||||||
|
# Get org and team mappings using the new fallback functions
|
||||||
|
org_map_value = self.get_social_org_map(org_map_setting_name) if org_map_setting_name else {}
|
||||||
|
team_map_value = self.get_social_team_map(team_map_setting_name) if team_map_setting_name else {}
|
||||||
|
|
||||||
# Convert GitHub org and team mappings from AWX to the Gateway format
|
# Convert GitHub org and team mappings from AWX to the Gateway format
|
||||||
# Start with order 1 and maintain sequence across both org and team mappers
|
# Start with order 1 and maintain sequence across both org and team mappers
|
||||||
|
|||||||
@@ -49,8 +49,9 @@ class SAMLMigrator(BaseAuthenticatorMigrator):
|
|||||||
idps = getattr(settings, "SOCIAL_AUTH_SAML_ENABLED_IDPS", {})
|
idps = getattr(settings, "SOCIAL_AUTH_SAML_ENABLED_IDPS", {})
|
||||||
security_config = getattr(settings, "SOCIAL_AUTH_SAML_SECURITY_CONFIG", {})
|
security_config = getattr(settings, "SOCIAL_AUTH_SAML_SECURITY_CONFIG", {})
|
||||||
|
|
||||||
org_map_value = getattr(settings, "SOCIAL_AUTH_SAML_ORGANIZATION_MAP", None)
|
# Get org and team mappings using the new fallback functions
|
||||||
team_map_value = getattr(settings, "SOCIAL_AUTH_SAML_TEAM_MAP", None)
|
org_map_value = self.get_social_org_map("SOCIAL_AUTH_SAML_ORGANIZATION_MAP")
|
||||||
|
team_map_value = self.get_social_team_map("SOCIAL_AUTH_SAML_TEAM_MAP")
|
||||||
extra_data = getattr(settings, "SOCIAL_AUTH_SAML_EXTRA_DATA", None)
|
extra_data = getattr(settings, "SOCIAL_AUTH_SAML_EXTRA_DATA", None)
|
||||||
support_contact = getattr(settings, "SOCIAL_AUTH_SAML_SUPPORT_CONTACT", {})
|
support_contact = getattr(settings, "SOCIAL_AUTH_SAML_SUPPORT_CONTACT", {})
|
||||||
technical_contact = getattr(settings, "SOCIAL_AUTH_SAML_TECHNICAL_CONTACT", {})
|
technical_contact = getattr(settings, "SOCIAL_AUTH_SAML_TECHNICAL_CONTACT", {})
|
||||||
|
|||||||
Reference in New Issue
Block a user