mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 23:07:42 -02:30
AC-156 Added code and tests to support LDAP authentication (no organization or team mapping yet).
This commit is contained in:
36
awx/main/backend.py
Normal file
36
awx/main/backend.py
Normal file
@@ -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()
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
|
||||||
from awx.main.tests.organizations import OrganizationsTest
|
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.inventory import InventoryTest
|
||||||
from awx.main.tests.projects import ProjectsTest, ProjectUpdatesTest
|
from awx.main.tests.projects import ProjectsTest, ProjectUpdatesTest
|
||||||
from awx.main.tests.commands import *
|
from awx.main.tests.commands import *
|
||||||
|
|||||||
@@ -7,15 +7,21 @@ import json
|
|||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
|
from django.conf import settings, UserSettingsHolder
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
import django.test
|
import django.test
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
# Django-Auth-LDAP
|
||||||
|
from django_auth_ldap.backend import LDAPSettings
|
||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.main.models import *
|
from awx.main.models import *
|
||||||
from awx.main.tests.base import BaseTest
|
from awx.main.tests.base import BaseTest
|
||||||
|
|
||||||
|
__all__ = ['UsersTest', 'LdapTest']
|
||||||
|
|
||||||
class UsersTest(BaseTest):
|
class UsersTest(BaseTest):
|
||||||
|
|
||||||
def collection(self):
|
def collection(self):
|
||||||
@@ -621,3 +627,68 @@ class UsersTest(BaseTest):
|
|||||||
self.check_get_list(url, self.super_django_user, qs)
|
self.check_get_list(url, self.super_django_user, qs)
|
||||||
url = u'%s?user\u2605name=normal' % base_url
|
url = u'%s?user\u2605name=normal' % base_url
|
||||||
self.check_get_list(url, self.super_django_user, base_qs, expect=400)
|
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))
|
||||||
|
|||||||
@@ -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.
|
# Seconds before auth tokens expire.
|
||||||
AUTH_TOKEN_EXPIRATION = 1800
|
AUTH_TOKEN_EXPIRATION = 1800
|
||||||
|
|
||||||
@@ -345,5 +353,8 @@ LOGGING = {
|
|||||||
'handlers': ['null'],
|
'handlers': ['null'],
|
||||||
'propagate': False,
|
'propagate': False,
|
||||||
},
|
},
|
||||||
|
'django_auth_ldap': {
|
||||||
|
'handlers': ['null'],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,33 @@ SECRET_KEY = 'p7z7g1ql4%6+(6nlebb6hdk7sd^&fnjpal308%n%+p^_e6vo1y'
|
|||||||
# reverse proxy.
|
# reverse proxy.
|
||||||
REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
|
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.
|
# Email address that error messages come from.
|
||||||
SERVER_EMAIL = 'root@localhost'
|
SERVER_EMAIL = 'root@localhost'
|
||||||
|
|
||||||
@@ -111,6 +138,10 @@ LOGGING['handlers']['syslog'] = {
|
|||||||
#LOGGING['loggers']['awx.main.signals']['propagate'] = True
|
#LOGGING['loggers']['awx.main.signals']['propagate'] = True
|
||||||
#LOGGING['loggers']['awx.main.permissions']['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
|
# Define additional environment variables to be passed to subprocess started by
|
||||||
# the celery task.
|
# the celery task.
|
||||||
#AWX_TASK_ENV['FOO'] = 'BAR'
|
#AWX_TASK_ENV['FOO'] = 'BAR'
|
||||||
@@ -141,3 +172,32 @@ TEST_SVN_USERNAME = ''
|
|||||||
TEST_SVN_PASSWORD = ''
|
TEST_SVN_PASSWORD = ''
|
||||||
TEST_SVN_PUBLIC_HTTPS = 'https://projects.ninemoreminutes.com/svn/django-site-utils/trunk/'
|
TEST_SVN_PUBLIC_HTTPS = 'https://projects.ninemoreminutes.com/svn/django-site-utils/trunk/'
|
||||||
TEST_SVN_PRIVATE_HTTPS = ''
|
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',
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,3 +54,31 @@ EMAIL_PORT = 25
|
|||||||
EMAIL_HOST_USER = ''
|
EMAIL_HOST_USER = ''
|
||||||
EMAIL_HOST_PASSWORD = ''
|
EMAIL_HOST_PASSWORD = ''
|
||||||
EMAIL_USE_TLS = False
|
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',
|
||||||
|
#}
|
||||||
|
|||||||
@@ -54,3 +54,31 @@ EMAIL_PORT = 25
|
|||||||
EMAIL_HOST_USER = ''
|
EMAIL_HOST_USER = ''
|
||||||
EMAIL_HOST_PASSWORD = ''
|
EMAIL_HOST_PASSWORD = ''
|
||||||
EMAIL_USE_TLS = False
|
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',
|
||||||
|
#}
|
||||||
|
|||||||
Reference in New Issue
Block a user