diff options
-rw-r--r-- | awx/main/backend.py | 36 | ||||
-rw-r--r-- | awx/main/tests/__init__.py | 2 | ||||
-rw-r--r-- | awx/main/tests/users.py | 71 | ||||
-rw-r--r-- | awx/settings/defaults.py | 11 | ||||
-rw-r--r-- | awx/settings/local_settings.py.example | 60 | ||||
-rw-r--r-- | config/deb/settings.py | 28 | ||||
-rw-r--r-- | config/rpm/settings.py | 28 |
7 files changed, 235 insertions, 1 deletions
diff --git a/awx/main/backend.py b/awx/main/backend.py new file mode 100644 index 0000000000..86a06ae051 --- /dev/null +++ b/awx/main/backend.py @@ -0,0 +1,36 @@ +# Copyright (c) 2013 AnsibleWorks, Inc. +# All Rights Reserved. + +# django-auth-ldap +from django_auth_ldap.backend import LDAPBackend as BaseLDAPBackend + +class LDAPBackend(BaseLDAPBackend): + ''' + Custom LDAP backend for AWX. + ''' + + settings_prefix = 'AUTH_LDAP_' + + def authenticate(self, username, password): + if not self.settings.SERVER_URI: + return None + return super(LDAPBackend, self).authenticate(username, password) + + def get_user(self, user_id): + if not self.settings.SERVER_URI: + return None + return super(LDAPBackend, self).get_user(user_id) + + # Disable any LDAP based authorization / permissions checking. + + def has_perm(self, user, perm, obj=None): + return False + + def has_module_perms(self, user, app_label): + return False + + def get_all_permissions(self, user, obj=None): + return set() + + def get_group_permissions(self, user, obj=None): + return set() diff --git a/awx/main/tests/__init__.py b/awx/main/tests/__init__.py index 985bda16e1..bd12cbff90 100644 --- a/awx/main/tests/__init__.py +++ b/awx/main/tests/__init__.py @@ -2,7 +2,7 @@ # All Rights Reserved. from awx.main.tests.organizations import OrganizationsTest -from awx.main.tests.users import UsersTest +from awx.main.tests.users import * from awx.main.tests.inventory import InventoryTest from awx.main.tests.projects import ProjectsTest, ProjectUpdatesTest from awx.main.tests.commands import * diff --git a/awx/main/tests/users.py b/awx/main/tests/users.py index 2c05ad3a2c..1bad7f4591 100644 --- a/awx/main/tests/users.py +++ b/awx/main/tests/users.py @@ -7,15 +7,21 @@ import json import urllib # Django +from django.conf import settings, UserSettingsHolder from django.contrib.auth.models import User import django.test from django.test.client import Client from django.core.urlresolvers import reverse +# Django-Auth-LDAP +from django_auth_ldap.backend import LDAPSettings + # AWX from awx.main.models import * from awx.main.tests.base import BaseTest +__all__ = ['UsersTest', 'LdapTest'] + class UsersTest(BaseTest): def collection(self): @@ -621,3 +627,68 @@ class UsersTest(BaseTest): self.check_get_list(url, self.super_django_user, qs) url = u'%s?user\u2605name=normal' % base_url self.check_get_list(url, self.super_django_user, base_qs, expect=400) + +class LdapTest(BaseTest): + + def use_test_setting(self, name, default=None): + setattr(settings, 'AUTH_LDAP_%s' % name, + getattr(settings, 'TEST_AUTH_LDAP_%s' % name, default)) + + def setUp(self): + super(LdapTest, self).setUp() + # Skip tests if basic LDAP test settings aren't defined. + if not getattr(settings, 'TEST_AUTH_LDAP_SERVER_URI', None): + self.skipTest('no test LDAP auth server defined') + self.ldap_username = getattr(settings, 'TEST_AUTH_LDAP_USERNAME', None) + if not self.ldap_username: + self.skipTest('no test LDAP username defined') + self.ldap_password = getattr(settings, 'TEST_AUTH_LDAP_PASSWORD', None) + if not self.ldap_password: + self.skipTest('no test LDAP password defined') + # Wrap settings so we can redfine them for each test. + self._wrapped = settings._wrapped + settings._wrapped = UserSettingsHolder(settings._wrapped) + # Reset all AUTH_LDAP_* settings to defaults. + for name, value in LDAPSettings.defaults.items(): + setattr(settings, 'AUTH_LDAP_%s' % name, value) + # Set test LDAP settings that are always needed. + for name in ('SERVER_URI', 'BIND_DN', 'BIND_PASSWORD', 'USE_TLS'): + self.use_test_setting(name) + + def tearDown(self): + super(LdapTest, self).tearDown() + settings._wrapped = self._wrapped + + def check_login(self, username=None, password=None, should_fail=False): + 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) + if not should_fail: + return User.objects.get(username=username) + + def test_ldap_auth(self): + self.use_test_setting('USER_SEARCH') + 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() + self.assertTrue(user.is_active) + self.assertFalse(user.has_usable_password()) + self.assertFalse(user.is_superuser) + self.assertFalse(user.first_name) + self.assertFalse(user.last_name) + self.assertFalse(user.email) + # Test logging in with bad username or password. + self.check_login(username='not a valid user', should_fail=True) + self.check_login(password='not a valid pass', should_fail=True) + # Test using a flat DN instead of user search. + self.use_test_setting('USER_DN_TEMPLATE', None) + if settings.AUTH_LDAP_USER_DN_TEMPLATE: + user = self.check_login() + del settings.AUTH_LDAP_USER_DN_TEMPLATE + # Test user attributes assigned from LDAP. + self.use_test_setting('USER_ATTR_MAP', {}) + if settings.AUTH_LDAP_USER_ATTR_MAP: + user = self.check_login() + for attr in settings.AUTH_LDAP_USER_ATTR_MAP.keys(): + self.assertTrue(getattr(user, attr)) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 6f0b8320a7..15ce104881 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -165,6 +165,14 @@ REST_FRAMEWORK = { ), } +AUTHENTICATION_BACKENDS = ( + 'awx.main.backend.LDAPBackend', + 'django.contrib.auth.backends.ModelBackend', +) + +# LDAP server (default to None to skip using LDAP authentication). +AUTH_LDAP_SERVER_URI = None + # Seconds before auth tokens expire. AUTH_TOKEN_EXPIRATION = 1800 @@ -345,5 +353,8 @@ LOGGING = { 'handlers': ['null'], 'propagate': False, }, + 'django_auth_ldap': { + 'handlers': ['null'], + }, } } diff --git a/awx/settings/local_settings.py.example b/awx/settings/local_settings.py.example index 17ed53e53b..9a4fabd889 100644 --- a/awx/settings/local_settings.py.example +++ b/awx/settings/local_settings.py.example @@ -60,6 +60,33 @@ 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 + +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 address that error messages come from. SERVER_EMAIL = 'root@localhost' @@ -111,6 +138,10 @@ LOGGING['handlers']['syslog'] = { #LOGGING['loggers']['awx.main.signals']['propagate'] = True #LOGGING['loggers']['awx.main.permissions']['propagate'] = True +# Enable the following lines to turn on LDAP auth logging. +#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' @@ -141,3 +172,32 @@ TEST_SVN_USERNAME = '' TEST_SVN_PASSWORD = '' TEST_SVN_PUBLIC_HTTPS = 'https://projects.ninemoreminutes.com/svn/django-site-utils/trunk/' TEST_SVN_PRIVATE_HTTPS = '' + +# 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 +TEST_AUTH_LDAP_SERVER_URI = '' +TEST_AUTH_LDAP_BIND_DN = '' +TEST_AUTH_LDAP_BIND_PASSWORD = '' +TEST_AUTH_LDAP_START_TLS = False + +# LDAP username/password for testing authentication. +TEST_AUTH_LDAP_USERNAME = '' +TEST_AUTH_LDAP_PASSWORD = '' + +# LDAP search query to find users. +TEST_AUTH_LDAP_USER_SEARCH = LDAPSearch( + 'OU=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' + +# Mapping of LDAP attributes to user attributes. +TEST_AUTH_LDAP_USER_ATTR_MAP = { + 'first_name': 'givenName', + 'last_name': 'sn', + 'email': 'mail', +} diff --git a/config/deb/settings.py b/config/deb/settings.py index 50ac050710..015d074de3 100644 --- a/config/deb/settings.py +++ b/config/deb/settings.py @@ -54,3 +54,31 @@ EMAIL_PORT = 25 EMAIL_HOST_USER = '' EMAIL_HOST_PASSWORD = '' EMAIL_USE_TLS = False + +# 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 + +#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', +#} diff --git a/config/rpm/settings.py b/config/rpm/settings.py index 50ac050710..015d074de3 100644 --- a/config/rpm/settings.py +++ b/config/rpm/settings.py @@ -54,3 +54,31 @@ EMAIL_PORT = 25 EMAIL_HOST_USER = '' EMAIL_HOST_PASSWORD = '' EMAIL_USE_TLS = False + +# 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 + +#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', +#} |