From cad3612a8ff2a41f1ebeaac4f9123a36b2fdb002 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Mon, 9 Sep 2013 17:20:43 -0400 Subject: [PATCH] AC-156. Implement LDAP organization mapping, update settings files and comments on LDAP configuration. --- awx/main/backend.py | 69 ++++++++ awx/main/tests/base.py | 4 +- awx/main/tests/users.py | 72 +++++++- awx/settings/local_settings.py.example | 220 +++++++++++++++++++++---- config/deb/settings.py | 134 ++++++++++++--- config/rpm/settings.py | 134 ++++++++++++--- 6 files changed, 543 insertions(+), 90 deletions(-) diff --git a/awx/main/backend.py b/awx/main/backend.py index 86a06ae051..04bea552a3 100644 --- a/awx/main/backend.py +++ b/awx/main/backend.py @@ -1,8 +1,19 @@ # Copyright (c) 2013 AnsibleWorks, Inc. # All Rights Reserved. +# Django +from django.dispatch import receiver + # django-auth-ldap +from django_auth_ldap.backend import LDAPSettings as BaseLDAPSettings from django_auth_ldap.backend import LDAPBackend as BaseLDAPBackend +from django_auth_ldap.backend import populate_user + +class LDAPSettings(BaseLDAPSettings): + + defaults = dict(BaseLDAPSettings.defaults.items() + { + 'ORGANIZATION_MAP': {}, + }.items()) class LDAPBackend(BaseLDAPBackend): ''' @@ -11,6 +22,16 @@ class LDAPBackend(BaseLDAPBackend): settings_prefix = 'AUTH_LDAP_' + def _get_settings(self): + if self._settings is None: + self._settings = LDAPSettings(self.settings_prefix) + return self._settings + + def _set_settings(self, settings): + self._settings = settings + + settings = property(_get_settings, _set_settings) + def authenticate(self, username, password): if not self.settings.SERVER_URI: return None @@ -34,3 +55,51 @@ class LDAPBackend(BaseLDAPBackend): def get_group_permissions(self, user, obj=None): return set() + +def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=False): + ''' + Hepler function to update m2m relationship based on LDAP group membership. + ''' + should_add = False + if opts is None: + return + elif not opts: + pass + elif opts is True: + should_add = True + else: + if isinstance(opts, basestring): + opts = [opts] + for group_dn in opts: + if not isinstance(group_dn, basestring): + continue + if ldap_user._get_groups().is_member_of(group_dn): + should_add = True + if should_add: + rel.add(user) + elif remove: + rel.remove(user) + +@receiver(populate_user) +def on_populate_user(sender, **kwargs): + ''' + Handle signal from LDAP backend to populate the user object. Update user's + organization membership according to their LDAP groups. + ''' + from awx.main.models import Organization + user = kwargs['user'] + ldap_user = kwargs['ldap_user'] + backend = ldap_user.backend + + org_map = getattr(backend.settings, 'ORGANIZATION_MAP', {}) + for org_name, org_opts in org_map.items(): + org, created = Organization.objects.get_or_create(name=org_name) + remove = bool(org_opts.get('remove', False)) + admins_opts = org_opts.get('admins', None) + remove_admins = bool(org_opts.get('remove_admins', remove)) + _update_m2m_from_groups(user, ldap_user, org.admins, admins_opts, + remove_admins) + users_opts = org_opts.get('users', None) + remove_users = bool(org_opts.get('remove_users', remove)) + _update_m2m_from_groups(user, ldap_user, org.users, users_opts, + remove_users) diff --git a/awx/main/tests/base.py b/awx/main/tests/base.py index 257bf15d27..05894664f4 100644 --- a/awx/main/tests/base.py +++ b/awx/main/tests/base.py @@ -18,11 +18,9 @@ from django.contrib.auth.models import User import django.test from django.test.client import Client -# Django-Auth-LDAP -from django_auth_ldap.backend import LDAPSettings - # AWX from awx.main.models import * +from awx.main.backend import LDAPSettings class BaseTestMixin(object): ''' diff --git a/awx/main/tests/users.py b/awx/main/tests/users.py index 0e90db09c3..7fc5324947 100644 --- a/awx/main/tests/users.py +++ b/awx/main/tests/users.py @@ -8,7 +8,7 @@ import urllib # Django from django.conf import settings -from django.contrib.auth.models import User +from django.contrib.auth.models import User, Group import django.test from django.test.client import Client from django.core.urlresolvers import reverse @@ -627,9 +627,10 @@ class UsersTest(BaseTest): class LdapTest(BaseTest): - def use_test_setting(self, name, default=None): + def use_test_setting(self, name, default=None, from_name=None): + from_name = from_name or name setattr(settings, 'AUTH_LDAP_%s' % name, - getattr(settings, 'TEST_AUTH_LDAP_%s' % name, default)) + getattr(settings, 'TEST_AUTH_LDAP_%s' % from_name, default)) def setUp(self): super(LdapTest, self).setUp() @@ -647,15 +648,18 @@ class LdapTest(BaseTest): self.use_test_setting(name) def check_login(self, username=None, password=None, should_fail=False): + self.assertEqual(Group.objects.count(), 0) username = username or self.ldap_username password = password or self.ldap_password result = self.client.login(username=username, password=password) self.assertNotEqual(result, should_fail) + self.assertEqual(Group.objects.count(), 0) if not should_fail: return User.objects.get(username=username) def test_ldap_auth(self): self.use_test_setting('USER_SEARCH') + self.use_test_setting('ALWAYS_UPDATE_USER') self.assertEqual(User.objects.filter(username=self.ldap_username).count(), 0) # Test logging in, user should be created with no flags or fields set. user = self.check_login() @@ -679,3 +683,65 @@ class LdapTest(BaseTest): user = self.check_login() for attr in settings.AUTH_LDAP_USER_ATTR_MAP.keys(): self.assertTrue(getattr(user, attr)) + # Turn on group search fields. + for name in ('GROUP_SEARCH', 'GROUP_TYPE'): + self.use_test_setting(name) + # Test that user must be in required group. + self.use_test_setting('REQUIRE_GROUP', from_name='REQUIRE_GROUP_FAIL') + if settings.AUTH_LDAP_REQUIRE_GROUP: + user = self.check_login(should_fail=True) + self.use_test_setting('REQUIRE_GROUP') + user = self.check_login() + # Test that user must not be in deny group. + self.use_test_setting('DENY_GROUP', from_name='DENY_GROUP_FAIL') + if settings.AUTH_LDAP_DENY_GROUP: + user = self.check_login(should_fail=True) + self.use_test_setting('DENY_GROUP') + user = self.check_login() + # Check that user flags are set from group membership. + self.use_test_setting('USER_FLAGS_BY_GROUP') + if settings.AUTH_LDAP_USER_FLAGS_BY_GROUP: + user = self.check_login() + for attr in settings.AUTH_LDAP_USER_FLAGS_BY_GROUP.keys(): + self.assertTrue(getattr(user, attr)) + + def test_ldap_organization_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('ORGANIZATION_MAP', {}) + self.use_test_setting('ORGANIZATION_MAP_RESULT', {}) + for org_name in settings.AUTH_LDAP_ORGANIZATION_MAP.keys(): + self.assertEqual(Organization.objects.filter(name=org_name).count(), 0) + user = self.check_login() + for org_name in settings.AUTH_LDAP_ORGANIZATION_MAP.keys(): + self.assertEqual(Organization.objects.filter(name=org_name).count(), 1) + for org_name, org_result in settings.AUTH_LDAP_ORGANIZATION_MAP_RESULT.items(): + org = Organization.objects.get(name=org_name) + if org_result.get('admins', False): + self.assertTrue(user in org.admins.all()) + else: + self.assertFalse(user in org.admins.all()) + if org_result.get('users', False): + self.assertTrue(user in org.users.all()) + else: + self.assertFalse(user in org.users.all()) + # Try again with different test mapping. + self.use_test_setting('ORGANIZATION_MAP', {}, + from_name='ORGANIZATION_MAP_2') + self.use_test_setting('ORGANIZATION_MAP_RESULT', {}, + from_name='ORGANIZATION_MAP_2_RESULT') + user = self.check_login() + for org_name in settings.AUTH_LDAP_ORGANIZATION_MAP.keys(): + self.assertEqual(Organization.objects.filter(name=org_name).count(), 1) + for org_name, org_result in settings.AUTH_LDAP_ORGANIZATION_MAP_RESULT.items(): + org = Organization.objects.get(name=org_name) + if org_result.get('admins', False): + self.assertTrue(user in org.admins.all()) + else: + self.assertFalse(user in org.admins.all()) + if org_result.get('users', False): + self.assertTrue(user in org.users.all()) + else: + self.assertFalse(user in org.users.all()) diff --git a/awx/settings/local_settings.py.example b/awx/settings/local_settings.py.example index c0884fc1bc..ab7a8885cb 100644 --- a/awx/settings/local_settings.py.example +++ b/awx/settings/local_settings.py.example @@ -4,6 +4,10 @@ # Local Django settings for AWX project. Rename to "local_settings.py" and # edit as needed for your development environment. +############################################################################### +# MISC PROJECT SETTINGS +############################################################################### + ADMINS = ( # ('Your Name', 'your_email@domain.com'), ) @@ -60,32 +64,13 @@ SECRET_KEY = 'p7z7g1ql4%6+(6nlebb6hdk7sd^&fnjpal308%n%+p^_e6vo1y' # reverse proxy. REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST'] -# LDAP connection and authentication settings. Refer to django-auth-ldap docs: -# http://pythonhosted.org/django-auth-ldap/authentication.html -AUTH_LDAP_SERVER_URI = '' -AUTH_LDAP_BIND_DN = '' -AUTH_LDAP_BIND_PASSWORD = '' -AUTH_LDAP_START_TLS = False +# Define additional environment variables to be passed to subprocess started by +# the celery task. +#AWX_TASK_ENV['FOO'] = 'BAR' -import ldap -from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion - -# LDAP search query to find users. -AUTH_LDAP_USER_SEARCH = LDAPSearch( - 'OU=Users,DC=example,DC=com', - ldap.SCOPE_SUBTREE, - '(sAMAccountName=%(user)s)', -) - -# Alternative to user search. -#AUTH_LDAP_USER_DN_TEMPLATE = 'sAMAccountName=%(user)s,OU=Users,DC=example,DC=com' - -# Mapping of LDAP attributes to user attributes. -AUTH_LDAP_USER_ATTR_MAP = { - 'first_name': 'givenName', - 'last_name': 'sn', - 'email': 'mail', -} +############################################################################### +# EMAIL SETTINGS +############################################################################### # Email address that error messages come from. SERVER_EMAIL = 'root@localhost' @@ -115,6 +100,10 @@ DEFAULT_FROM_EMAIL = 'webmaster@localhost' # or ...mail_managers. Make sure to include the trailing space. EMAIL_SUBJECT_PREFIX = '[AWX] ' +############################################################################### +# LOGGING SETTINGS +############################################################################### + # Enable logging to syslog. Setting level to ERROR captures 500 errors, # WARNING also logs 4xx responses. LOGGING['handlers']['syslog'] = { @@ -142,9 +131,107 @@ LOGGING['handlers']['syslog'] = { #LOGGING['loggers']['django_auth_ldap']['handlers'] = ['console'] #LOGGING['loggers']['django_auth_ldap']['level'] = 'DEBUG' -# Define additional environment variables to be passed to subprocess started by -# the celery task. -#AWX_TASK_ENV['FOO'] = 'BAR' +############################################################################### +# LDAP AUTHENTICATION SETTINGS +############################################################################### + +# Refer to django-auth-ldap docs for more details: +# http://pythonhosted.org/django-auth-ldap/authentication.html + +# LDAP server URI, such as "ldap://ldap.example.com:389" (non-SSL) or +# "ldaps://ldap.example.com:636" (SSL). LDAP authentication is disable if this +# parameter is empty. +AUTH_LDAP_SERVER_URI = '' + +# DN of user to bind for all search queries. Normally in the format +# "CN=Some User,OU=Users,DC=example,DC=com" but may also be specified as +# "DOMAIN\username" for Active Directory. +AUTH_LDAP_BIND_DN = '' + +# Password using to bind above user account. +AUTH_LDAP_BIND_PASSWORD = '' + +# Enable TLS when the connection is not using SSL. +AUTH_LDAP_START_TLS = False + +# Imports needed for remaining LDAP configuration. +import ldap +from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion +from django_auth_ldap.config import ActiveDirectoryGroupType + +# LDAP search query to find users. +AUTH_LDAP_USER_SEARCH = LDAPSearch( + 'OU=Users,DC=example,DC=com', # Base DN + ldap.SCOPE_SUBTREE, # SCOPE_BASE, SCOPE_ONELEVEL, SCOPE_SUBTREE + '(sAMAccountName=%(user)s)', # Query +) + +# Alternative to user search, if user DNs are all of the same format. +#AUTH_LDAP_USER_DN_TEMPLATE = 'uid=%(user)s,OU=Users,DC=example,DC=com' + +# Mapping of LDAP to user atrributes (key is user attribute name, value is LDAP +# attribute name). +AUTH_LDAP_USER_ATTR_MAP = { + 'first_name': 'givenName', + 'last_name': 'sn', + 'email': 'mail', +} + +# LDAP search query to find groups. Does not support LDAPSearchUnion. +AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + 'DC=example,DC=com', # Base DN + ldap.SCOPE_SUBTREE, # SCOPE_BASE, SCOPE_ONELEVEL, SCOPE_SUBTREE + '(objectClass=group)', # Query +) +# Type of group returned by the search above. Should be one of the types +# listed at: http://pythonhosted.org/django-auth-ldap/groups.html#types-of-groups +AUTH_LDAP_GROUP_TYPE = ActiveDirectoryGroupType() + +# Group DN required to login. If specified, user must be a member of this +# group to login via LDAP. +AUTH_LDAP_REQUIRE_GROUP = '' + +# Group DN denied from login. If specified, user will not be allowed to login +# if a member of this group. +AUTH_LDAP_DENY_GROUP = '' + +# User profile flags updated from group membership (key is user attribute name, +# value is group DN). +AUTH_LDAP_USER_FLAGS_BY_GROUP = { + #'is_superuser': 'CN=Domain Admins,CN=Users,DC=example,DC=com', +} + +# Mapping between organization admins/users and LDAP groups. Keys are +# organization names (will be created if not present). Values are dictionaries +# of options for each organization's membership, where each can contain the +# following parameters: +# - remove: True/False. Defaults to False. Specifies the default for +# remove_admins or remove_users if those parameters aren't explicitly set. +# - admins: None, True/False, string or list/tuple of strings. +# If None, organization admins will not be updated. +# 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 +# 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 +# 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 +# as for admins. +# - remove_users: 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 users. +AUTH_LDAP_ORGANIZATION_MAP = { + #'Test Org': { + # 'admins': 'CN=Domain Admins,CN=Users,DC=example,DC=com', + # 'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'], + #}, + #'Test Org 2': { + # 'admins': ['CN=Administrators,CN=Builtin,DC=example,DC=com'], + # 'users': True, + #}, +} + +############################################################################### +# SCM TEST SETTINGS +############################################################################### # Define these variables to enable more complete testing of project support for # SCM updates. @@ -173,9 +260,13 @@ TEST_SVN_PASSWORD = '' TEST_SVN_PUBLIC_HTTPS = 'https://github.com/ansible/ansible-examples' TEST_SVN_PRIVATE_HTTPS = 'https://github.com/ansible/ansible-doc' +############################################################################### +# LDAP TEST SETTINGS +############################################################################### + # LDAP connection and authentication settings for unit tests only. LDAP tests -# will be skipped if not configured. Refer to django-auth-ldap docs: -# http://pythonhosted.org/django-auth-ldap/authentication.html +# will be skipped if TEST_AUTH_LDAP_SERVER_URI is not configured. + TEST_AUTH_LDAP_SERVER_URI = '' TEST_AUTH_LDAP_BIND_DN = '' TEST_AUTH_LDAP_BIND_PASSWORD = '' @@ -187,13 +278,13 @@ TEST_AUTH_LDAP_PASSWORD = '' # LDAP search query to find users. TEST_AUTH_LDAP_USER_SEARCH = LDAPSearch( - 'OU=Users,DC=example,DC=com', + 'CN=Users,DC=example,DC=com', ldap.SCOPE_SUBTREE, '(sAMAccountName=%(user)s)', ) # Alternative to user search. -TEST_AUTH_LDAP_USER_DN_TEMPLATE = 'sAMAccountName=%(user)s,OU=Users,DC=example,DC=com' +#TEST_AUTH_LDAP_USER_DN_TEMPLATE = 'sAMAccountName=%(user)s,OU=Users,DC=example,DC=com' # Mapping of LDAP attributes to user attributes. TEST_AUTH_LDAP_USER_ATTR_MAP = { @@ -201,3 +292,68 @@ TEST_AUTH_LDAP_USER_ATTR_MAP = { 'last_name': 'sn', 'email': 'mail', } + +# LDAP search query for finding groups. +TEST_AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + 'DC=example,DC=com', + ldap.SCOPE_SUBTREE, + '(objectClass=group)', +) +# Type of group returned by the search above. +TEST_AUTH_LDAP_GROUP_TYPE = ActiveDirectoryGroupType() + +# Test DNs for a group required to login. User should be a member of the first +# group, but not a member of the second. +TEST_AUTH_LDAP_REQUIRE_GROUP = 'CN=Domain Admins,CN=Users,DC=example,DC=com' +TEST_AUTH_LDAP_REQUIRE_GROUP_FAIL = 'CN=Guest,CN=Users,DC=example,DC=com' + +# Test DNs for a group denied from login. User should not be a member of the +# first group, but should be a member of the second. +TEST_AUTH_LDAP_DENY_GROUP = 'CN=Guest,CN=Users,DC=example,DC=com' +TEST_AUTH_LDAP_DENY_GROUP_FAIL = 'CN=Domain Admins,CN=Users,DC=example,DC=com' + +# User profile flags updated from group membership. Test user should be a +# member of the group. +TEST_AUTH_LDAP_USER_FLAGS_BY_GROUP = { + 'is_superuser': 'CN=Domain Admins,CN=Users,DC=example,DC=com', +} + +# Test mapping between organization admins/users and LDAP groups. +TEST_AUTH_LDAP_ORGANIZATION_MAP = { + 'Test Org': { + 'admins': 'CN=Domain Admins,CN=Users,DC=example,DC=com', + 'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'], + }, + 'Test Org 2': { + 'admins': ['CN=Administrators,CN=Builtin,DC=example,DC=com'], + 'users': True, + }, +} +# Expected results from organization mapping. After login, should user be an +# admin/user in the given organization? +TEST_AUTH_LDAP_ORGANIZATION_MAP_RESULT = { + 'Test Org': {'admins': True, 'users': False}, + 'Test Org 2': {'admins': False, 'users': True}, +} + +# Second test mapping to test remove parameters. +TEST_AUTH_LDAP_ORGANIZATION_MAP_2 = { + 'Test Org': { + 'admins': 'CN=Domain Users,CN=Users,DC=example,DC=com', + 'users': True, + 'remove_admins': True, + 'remove_users': False, + }, + 'Test Org 2': { + 'admins': ['CN=Domain Admins,CN=Users,DC=example,DC=com', + 'CN=Administrators,CN=Builtin,DC=example,DC=com'], + 'users': False, + 'remove': True, + }, +} + +# Expected results from second organization mapping. +TEST_AUTH_LDAP_ORGANIZATION_MAP_2_RESULT = { + 'Test Org': {'admins': False, 'users': True}, + 'Test Org 2': {'admins': True, 'users': False}, +} diff --git a/config/deb/settings.py b/config/deb/settings.py index 015d074de3..106923af11 100644 --- a/config/deb/settings.py +++ b/config/deb/settings.py @@ -1,3 +1,7 @@ +############################################################################### +# MISC PROJECT SETTINGS +############################################################################### + ADMINS = ( #('Joe Admin', 'joeadmin@example.com'), ) @@ -32,19 +36,13 @@ SECRET_KEY = file('/etc/awx/SECRET_KEY', 'rb').read().strip() ALLOWED_HOSTS = ['*'] -LOGGING['handlers']['syslog'] = { - # ERROR captures 500 errors, WARNING also logs 4xx responses. - 'level': 'ERROR', - 'filters': ['require_debug_false'], - 'class': 'logging.handlers.SysLogHandler', - 'address': '/dev/log', - 'facility': 'local0', - 'formatter': 'simple', -} - AWX_TASK_ENV['HOME'] = '/var/lib/awx' AWX_TASK_ENV['USER'] = 'awx' +############################################################################### +# EMAIL SETTINGS +############################################################################### + SERVER_EMAIL = 'root@localhost' DEFAULT_FROM_EMAIL = 'webmaster@localhost' EMAIL_SUBJECT_PREFIX = '[AnsibleWorks] ' @@ -55,30 +53,114 @@ EMAIL_HOST_USER = '' EMAIL_HOST_PASSWORD = '' EMAIL_USE_TLS = False -# LDAP connection and authentication settings. Refer to django-auth-ldap docs: +############################################################################### +# LOGGING SETTINGS +############################################################################### + +LOGGING['handlers']['syslog'] = { + # ERROR captures 500 errors, WARNING also logs 4xx responses. + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'logging.handlers.SysLogHandler', + 'address': '/dev/log', + 'facility': 'local0', + 'formatter': 'simple', +} + +############################################################################### +# LDAP AUTHENTICATION SETTINGS +############################################################################### + +# Refer to django-auth-ldap docs for more details: # http://pythonhosted.org/django-auth-ldap/authentication.html +# LDAP server URI, such as "ldap://ldap.example.com:389" (non-SSL) or +# "ldaps://ldap.example.com:636" (SSL). LDAP authentication is disable if this +# parameter is empty. AUTH_LDAP_SERVER_URI = '' + +# DN of user to bind for all search queries. Normally in the format +# "CN=Some User,OU=Users,DC=example,DC=com" but may also be specified as +# "DOMAIN\username" for Active Directory. AUTH_LDAP_BIND_DN = '' + +# Password using to bind above user account. AUTH_LDAP_BIND_PASSWORD = '' + +# Enable TLS when the connection is not using SSL. AUTH_LDAP_START_TLS = False -#import ldap -#from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion +# Imports needed for remaining LDAP configuration. +import ldap +from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion +from django_auth_ldap.config import ActiveDirectoryGroupType # LDAP search query to find users. -#AUTH_LDAP_USER_SEARCH = LDAPSearch( -# 'OU=Users,DC=example,DC=com', -# ldap.SCOPE_SUBTREE, -# '(sAMAccountName=%(user)s)', -#) +AUTH_LDAP_USER_SEARCH = LDAPSearch( + 'OU=Users,DC=example,DC=com', # Base DN + ldap.SCOPE_SUBTREE, # SCOPE_BASE, SCOPE_ONELEVEL, SCOPE_SUBTREE + '(sAMAccountName=%(user)s)', # Query +) -# Alternative to user search. -#AUTH_LDAP_USER_DN_TEMPLATE = 'sAMAccountName=%(user)s,OU=Users,DC=example,DC=com' +# Alternative to user search, if user DNs are all of the same format. +#AUTH_LDAP_USER_DN_TEMPLATE = 'uid=%(user)s,OU=Users,DC=example,DC=com' -# Mapping of LDAP attributes to user attributes. -#AUTH_LDAP_USER_ATTR_MAP = { -# 'first_name': 'givenName', -# 'last_name': 'sn', -# 'email': 'mail', -#} +# Mapping of LDAP to user atrributes (key is user attribute name, value is LDAP +# attribute name). +AUTH_LDAP_USER_ATTR_MAP = { + 'first_name': 'givenName', + 'last_name': 'sn', + 'email': 'mail', +} + +# LDAP search query to find groups. Does not support LDAPSearchUnion. +AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + 'DC=example,DC=com', # Base DN + ldap.SCOPE_SUBTREE, # SCOPE_BASE, SCOPE_ONELEVEL, SCOPE_SUBTREE + '(objectClass=group)', # Query +) +# Type of group returned by the search above. Should be one of the types +# listed at: http://pythonhosted.org/django-auth-ldap/groups.html#types-of-groups +AUTH_LDAP_GROUP_TYPE = ActiveDirectoryGroupType() + +# Group DN required to login. If specified, user must be a member of this +# group to login via LDAP. +AUTH_LDAP_REQUIRE_GROUP = '' + +# Group DN denied from login. If specified, user will not be allowed to login +# if a member of this group. +AUTH_LDAP_DENY_GROUP = '' + +# User profile flags updated from group membership (key is user attribute name, +# value is group DN). +AUTH_LDAP_USER_FLAGS_BY_GROUP = { + #'is_superuser': 'CN=Domain Admins,CN=Users,DC=example,DC=com', +} + +# Mapping between organization admins/users and LDAP groups. Keys are +# organization names (will be created if not present). Values are dictionaries +# of options for each organization's membership, where each can contain the +# following parameters: +# - remove: True/False. Defaults to False. Specifies the default for +# remove_admins or remove_users if those parameters aren't explicitly set. +# - admins: None, True/False, string or list/tuple of strings. +# If None, organization admins will not be updated. +# 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 +# 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 +# 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 +# as for admins. +# - remove_users: 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 users. +AUTH_LDAP_ORGANIZATION_MAP = { + #'Test Org': { + # 'admins': 'CN=Domain Admins,CN=Users,DC=example,DC=com', + # 'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'], + #}, + #'Test Org 2': { + # 'admins': ['CN=Administrators,CN=Builtin,DC=example,DC=com'], + # 'users': True, + #}, +} diff --git a/config/rpm/settings.py b/config/rpm/settings.py index 015d074de3..106923af11 100644 --- a/config/rpm/settings.py +++ b/config/rpm/settings.py @@ -1,3 +1,7 @@ +############################################################################### +# MISC PROJECT SETTINGS +############################################################################### + ADMINS = ( #('Joe Admin', 'joeadmin@example.com'), ) @@ -32,19 +36,13 @@ SECRET_KEY = file('/etc/awx/SECRET_KEY', 'rb').read().strip() ALLOWED_HOSTS = ['*'] -LOGGING['handlers']['syslog'] = { - # ERROR captures 500 errors, WARNING also logs 4xx responses. - 'level': 'ERROR', - 'filters': ['require_debug_false'], - 'class': 'logging.handlers.SysLogHandler', - 'address': '/dev/log', - 'facility': 'local0', - 'formatter': 'simple', -} - AWX_TASK_ENV['HOME'] = '/var/lib/awx' AWX_TASK_ENV['USER'] = 'awx' +############################################################################### +# EMAIL SETTINGS +############################################################################### + SERVER_EMAIL = 'root@localhost' DEFAULT_FROM_EMAIL = 'webmaster@localhost' EMAIL_SUBJECT_PREFIX = '[AnsibleWorks] ' @@ -55,30 +53,114 @@ EMAIL_HOST_USER = '' EMAIL_HOST_PASSWORD = '' EMAIL_USE_TLS = False -# LDAP connection and authentication settings. Refer to django-auth-ldap docs: +############################################################################### +# LOGGING SETTINGS +############################################################################### + +LOGGING['handlers']['syslog'] = { + # ERROR captures 500 errors, WARNING also logs 4xx responses. + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'logging.handlers.SysLogHandler', + 'address': '/dev/log', + 'facility': 'local0', + 'formatter': 'simple', +} + +############################################################################### +# LDAP AUTHENTICATION SETTINGS +############################################################################### + +# Refer to django-auth-ldap docs for more details: # http://pythonhosted.org/django-auth-ldap/authentication.html +# LDAP server URI, such as "ldap://ldap.example.com:389" (non-SSL) or +# "ldaps://ldap.example.com:636" (SSL). LDAP authentication is disable if this +# parameter is empty. AUTH_LDAP_SERVER_URI = '' + +# DN of user to bind for all search queries. Normally in the format +# "CN=Some User,OU=Users,DC=example,DC=com" but may also be specified as +# "DOMAIN\username" for Active Directory. AUTH_LDAP_BIND_DN = '' + +# Password using to bind above user account. AUTH_LDAP_BIND_PASSWORD = '' + +# Enable TLS when the connection is not using SSL. AUTH_LDAP_START_TLS = False -#import ldap -#from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion +# Imports needed for remaining LDAP configuration. +import ldap +from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion +from django_auth_ldap.config import ActiveDirectoryGroupType # LDAP search query to find users. -#AUTH_LDAP_USER_SEARCH = LDAPSearch( -# 'OU=Users,DC=example,DC=com', -# ldap.SCOPE_SUBTREE, -# '(sAMAccountName=%(user)s)', -#) +AUTH_LDAP_USER_SEARCH = LDAPSearch( + 'OU=Users,DC=example,DC=com', # Base DN + ldap.SCOPE_SUBTREE, # SCOPE_BASE, SCOPE_ONELEVEL, SCOPE_SUBTREE + '(sAMAccountName=%(user)s)', # Query +) -# Alternative to user search. -#AUTH_LDAP_USER_DN_TEMPLATE = 'sAMAccountName=%(user)s,OU=Users,DC=example,DC=com' +# Alternative to user search, if user DNs are all of the same format. +#AUTH_LDAP_USER_DN_TEMPLATE = 'uid=%(user)s,OU=Users,DC=example,DC=com' -# Mapping of LDAP attributes to user attributes. -#AUTH_LDAP_USER_ATTR_MAP = { -# 'first_name': 'givenName', -# 'last_name': 'sn', -# 'email': 'mail', -#} +# Mapping of LDAP to user atrributes (key is user attribute name, value is LDAP +# attribute name). +AUTH_LDAP_USER_ATTR_MAP = { + 'first_name': 'givenName', + 'last_name': 'sn', + 'email': 'mail', +} + +# LDAP search query to find groups. Does not support LDAPSearchUnion. +AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + 'DC=example,DC=com', # Base DN + ldap.SCOPE_SUBTREE, # SCOPE_BASE, SCOPE_ONELEVEL, SCOPE_SUBTREE + '(objectClass=group)', # Query +) +# Type of group returned by the search above. Should be one of the types +# listed at: http://pythonhosted.org/django-auth-ldap/groups.html#types-of-groups +AUTH_LDAP_GROUP_TYPE = ActiveDirectoryGroupType() + +# Group DN required to login. If specified, user must be a member of this +# group to login via LDAP. +AUTH_LDAP_REQUIRE_GROUP = '' + +# Group DN denied from login. If specified, user will not be allowed to login +# if a member of this group. +AUTH_LDAP_DENY_GROUP = '' + +# User profile flags updated from group membership (key is user attribute name, +# value is group DN). +AUTH_LDAP_USER_FLAGS_BY_GROUP = { + #'is_superuser': 'CN=Domain Admins,CN=Users,DC=example,DC=com', +} + +# Mapping between organization admins/users and LDAP groups. Keys are +# organization names (will be created if not present). Values are dictionaries +# of options for each organization's membership, where each can contain the +# following parameters: +# - remove: True/False. Defaults to False. Specifies the default for +# remove_admins or remove_users if those parameters aren't explicitly set. +# - admins: None, True/False, string or list/tuple of strings. +# If None, organization admins will not be updated. +# 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 +# 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 +# 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 +# as for admins. +# - remove_users: 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 users. +AUTH_LDAP_ORGANIZATION_MAP = { + #'Test Org': { + # 'admins': 'CN=Domain Admins,CN=Users,DC=example,DC=com', + # 'users': ['CN=Domain Users,CN=Users,DC=example,DC=com'], + #}, + #'Test Org 2': { + # 'admins': ['CN=Administrators,CN=Builtin,DC=example,DC=com'], + # 'users': True, + #}, +}