mirror of
https://github.com/ansible/awx.git
synced 2026-05-11 11:27:36 -02:30
AC-448 Add support for mapping LDAP users to teams based on LDAP group membership.
This commit is contained in:
@@ -13,6 +13,7 @@ class LDAPSettings(BaseLDAPSettings):
|
|||||||
|
|
||||||
defaults = dict(BaseLDAPSettings.defaults.items() + {
|
defaults = dict(BaseLDAPSettings.defaults.items() + {
|
||||||
'ORGANIZATION_MAP': {},
|
'ORGANIZATION_MAP': {},
|
||||||
|
'TEAM_MAP': {},
|
||||||
}.items())
|
}.items())
|
||||||
|
|
||||||
class LDAPBackend(BaseLDAPBackend):
|
class LDAPBackend(BaseLDAPBackend):
|
||||||
@@ -83,10 +84,10 @@ def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=False):
|
|||||||
@receiver(populate_user)
|
@receiver(populate_user)
|
||||||
def on_populate_user(sender, **kwargs):
|
def on_populate_user(sender, **kwargs):
|
||||||
'''
|
'''
|
||||||
Handle signal from LDAP backend to populate the user object. Update user's
|
Handle signal from LDAP backend to populate the user object. Update user
|
||||||
organization membership according to their LDAP groups.
|
organization/team memberships according to their LDAP groups.
|
||||||
'''
|
'''
|
||||||
from awx.main.models import Organization
|
from awx.main.models import Organization, Team
|
||||||
user = kwargs['user']
|
user = kwargs['user']
|
||||||
ldap_user = kwargs['ldap_user']
|
ldap_user = kwargs['ldap_user']
|
||||||
backend = ldap_user.backend
|
backend = ldap_user.backend
|
||||||
@@ -105,6 +106,18 @@ def on_populate_user(sender, **kwargs):
|
|||||||
_update_m2m_from_groups(user, ldap_user, org.users, users_opts,
|
_update_m2m_from_groups(user, ldap_user, org.users, users_opts,
|
||||||
remove_users)
|
remove_users)
|
||||||
|
|
||||||
|
# Update team membership based on group memberships.
|
||||||
|
team_map = getattr(backend.settings, 'TEAM_MAP', {})
|
||||||
|
for team_name, team_opts in team_map.items():
|
||||||
|
if 'organization' not in team_opts:
|
||||||
|
continue
|
||||||
|
org, created = Organization.objects.get_or_create(name=team_opts['organization'])
|
||||||
|
team, created = Team.objects.get_or_create(name=team_name, organization=org)
|
||||||
|
users_opts = team_opts.get('users', None)
|
||||||
|
remove = bool(team_opts.get('remove', False))
|
||||||
|
_update_m2m_from_groups(user, ldap_user, team.users, users_opts,
|
||||||
|
remove)
|
||||||
|
|
||||||
# Update user profile to store LDAP DN.
|
# Update user profile to store LDAP DN.
|
||||||
profile = user.profile
|
profile = user.profile
|
||||||
if profile.ldap_dn != ldap_user.dn:
|
if profile.ldap_dn != ldap_user.dn:
|
||||||
|
|||||||
@@ -854,6 +854,39 @@ class LdapTest(BaseTest):
|
|||||||
else:
|
else:
|
||||||
self.assertFalse(user in org.users.all())
|
self.assertFalse(user in org.users.all())
|
||||||
|
|
||||||
|
def test_ldap_team_mapping(self):
|
||||||
|
for name in ('USER_SEARCH', 'ALWAYS_UPDATE_USER', 'USER_ATTR_MAP',
|
||||||
|
'GROUP_SEARCH', 'GROUP_TYPE', 'USER_FLAGS_BY_GROUP'):
|
||||||
|
self.use_test_setting(name)
|
||||||
|
self.assertEqual(User.objects.filter(username=self.ldap_username).count(), 0)
|
||||||
|
self.use_test_setting('TEAM_MAP', {})
|
||||||
|
self.use_test_setting('TEAM_MAP_RESULT', {})
|
||||||
|
for team_name, team_opts in settings.AUTH_LDAP_TEAM_MAP.items():
|
||||||
|
self.assertEqual(Team.objects.filter(name=team_name).count(), 0)
|
||||||
|
self.assertEqual(Organization.objects.filter(name=team_opts['organization']).count(), 0)
|
||||||
|
user = self.check_login()
|
||||||
|
for team_name, team_opts in settings.AUTH_LDAP_TEAM_MAP.items():
|
||||||
|
self.assertEqual(Team.objects.filter(name=team_name, organization__name=team_opts['organization']).count(), 1)
|
||||||
|
for team_name, team_result in settings.AUTH_LDAP_TEAM_MAP_RESULT.items():
|
||||||
|
team = Team.objects.get(name=team_name)
|
||||||
|
if team_result.get('users', False):
|
||||||
|
self.assertTrue(user in team.users.all())
|
||||||
|
else:
|
||||||
|
self.assertFalse(user in team.users.all())
|
||||||
|
# Try again with different test mapping.
|
||||||
|
self.use_test_setting('TEAM_MAP', {}, from_name='TEAM_MAP_2')
|
||||||
|
self.use_test_setting('TEAM_MAP_RESULT', {},
|
||||||
|
from_name='TEAM_MAP_2_RESULT')
|
||||||
|
user = self.check_login()
|
||||||
|
for team_name, team_opts in settings.AUTH_LDAP_TEAM_MAP.items():
|
||||||
|
self.assertEqual(Team.objects.filter(name=team_name, organization__name=team_opts['organization']).count(), 1)
|
||||||
|
for team_name, team_result in settings.AUTH_LDAP_TEAM_MAP_RESULT.items():
|
||||||
|
team = Team.objects.get(name=team_name)
|
||||||
|
if team_result.get('users', False):
|
||||||
|
self.assertTrue(user in team.users.all())
|
||||||
|
else:
|
||||||
|
self.assertFalse(user in team.users.all())
|
||||||
|
|
||||||
def test_prevent_changing_ldap_user_fields(self):
|
def test_prevent_changing_ldap_user_fields(self):
|
||||||
for name in ('USER_SEARCH', 'ALWAYS_UPDATE_USER', 'USER_ATTR_MAP',
|
for name in ('USER_SEARCH', 'ALWAYS_UPDATE_USER', 'USER_ATTR_MAP',
|
||||||
'GROUP_SEARCH', 'GROUP_TYPE', 'USER_FLAGS_BY_GROUP'):
|
'GROUP_SEARCH', 'GROUP_TYPE', 'USER_FLAGS_BY_GROUP'):
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
|||||||
# If True/False, all LDAP users will be added/removed as admins.
|
# If True/False, all LDAP users will be added/removed as admins.
|
||||||
# If a string or list of strings, specifies the group DN(s). User will be
|
# If a string or list of strings, specifies the group DN(s). User will be
|
||||||
# added as an org admin if the user is a member of ANY of these groups.
|
# added as an org admin if the user is a member of ANY of these groups.
|
||||||
# - remove_admins: True/False. Defaults to False. If True, a user who is not an
|
# - remove_admins: True/False. Defaults to False. If True, a user who is not a
|
||||||
# member of the given groups will be removed from the organization's admins.
|
# member of the given groups will be removed from the organization's admins.
|
||||||
# - users: None, True/False, string or list/tuple of strings. Same rules apply
|
# - users: None, True/False, string or list/tuple of strings. Same rules apply
|
||||||
# as for admins.
|
# as for admins.
|
||||||
@@ -251,6 +251,33 @@ AUTH_LDAP_ORGANIZATION_MAP = {
|
|||||||
#},
|
#},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Mapping between team members (users) and LDAP groups. Keys are team names
|
||||||
|
# (will be created if not present). Values are dictionaries of options for
|
||||||
|
# each team's membership, where each can contain the following parameters:
|
||||||
|
# - organization: string. The name of the organization to which the team
|
||||||
|
# belongs. The team will be created if the combination of organization and
|
||||||
|
# team name does not exist. The organization will first be created if it
|
||||||
|
# does not exist.
|
||||||
|
# - users: None, True/False, string or list/tuple of strings.
|
||||||
|
# If None, team members will not be updated.
|
||||||
|
# If True/False, all LDAP users will be added/removed as team members.
|
||||||
|
# If a string or list of strings, specifies the group DN(s). User will be
|
||||||
|
# added as a team member if the user is a member of ANY of these groups.
|
||||||
|
# - remove: True/False. Defaults to False. If True, a user who is not a member
|
||||||
|
# of the given groups will be removed from the team.
|
||||||
|
AUTH_LDAP_TEAM_MAP = {
|
||||||
|
'My Team': {
|
||||||
|
'organization': 'Test Org',
|
||||||
|
'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'],
|
||||||
|
'remove': True,
|
||||||
|
},
|
||||||
|
'Other Team': {
|
||||||
|
'organization': 'Test Org 2',
|
||||||
|
'users': 'CN=Other Users,CN=Users,DC=example,DC=com',
|
||||||
|
'remove': False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# SCM TEST SETTINGS
|
# SCM TEST SETTINGS
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -387,6 +414,57 @@ TEST_AUTH_LDAP_ORGANIZATION_MAP_2_RESULT = {
|
|||||||
'Test Org 2': {'admins': True, 'users': False},
|
'Test Org 2': {'admins': True, 'users': False},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Test mapping between team users and LDAP groups.
|
||||||
|
TEST_AUTH_LDAP_TEAM_MAP = {
|
||||||
|
'Domain Users Team': {
|
||||||
|
'organization': 'Test Org',
|
||||||
|
'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'],
|
||||||
|
'remove': False,
|
||||||
|
},
|
||||||
|
'Admins Team': {
|
||||||
|
'organization': 'Admins Org',
|
||||||
|
'users': 'CN=Domain Admins,CN=Users,DC=example,DC=com',
|
||||||
|
'remove': True,
|
||||||
|
},
|
||||||
|
'Everyone Team': {
|
||||||
|
'organization': 'Test Org 2',
|
||||||
|
'users': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
# Expected results from team mapping. After login, should user be a member of
|
||||||
|
# the given team?
|
||||||
|
TEST_AUTH_LDAP_TEAM_MAP_RESULT = {
|
||||||
|
'Domain Users Team': {'users': False},
|
||||||
|
'Admins Team': {'users': True},
|
||||||
|
'Everyone Team': {'users': True},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Second test mapping for teams to remove user.
|
||||||
|
TEST_AUTH_LDAP_TEAM_MAP_2 = {
|
||||||
|
'Domain Users Team': {
|
||||||
|
'organization': 'Test Org',
|
||||||
|
'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'],
|
||||||
|
'remove': False,
|
||||||
|
},
|
||||||
|
'Admins Team': {
|
||||||
|
'organization': 'Admins Org',
|
||||||
|
'users': 'CN=Administrators,CN=Builtin,DC=example,DC=com',
|
||||||
|
'remove': True,
|
||||||
|
},
|
||||||
|
'Everyone Team': {
|
||||||
|
'organization': 'Test Org 2',
|
||||||
|
'users': False,
|
||||||
|
'remove': False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
# Expected results from second team mapping. After login, should user be a
|
||||||
|
# member of the given team?
|
||||||
|
TEST_AUTH_LDAP_TEAM_MAP_2_RESULT = {
|
||||||
|
'Domain Users Team': {'users': False},
|
||||||
|
'Admins Team': {'users': False},
|
||||||
|
'Everyone Team': {'users': True},
|
||||||
|
}
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# INVENTORY IMPORT TEST SETTINGS
|
# INVENTORY IMPORT TEST SETTINGS
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|||||||
Reference in New Issue
Block a user