From 07205bcb88023acbea4cfd53e63de9aa253ba076 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Sat, 30 May 2015 16:08:19 -0400 Subject: [PATCH] Disable LDAP support when not allowed by license. --- awx/api/serializers.py | 13 +++++--- awx/api/views.py | 2 +- awx/main/backend.py | 7 +++-- awx/main/tests/users.py | 69 +++++++++++++++++++++++++++++++++-------- 4 files changed, 70 insertions(+), 21 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 80333a5f35..9bf08b51d2 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -608,11 +608,12 @@ class UserSerializer(BaseSerializer): new_password = getattr(obj, '_new_password', None) # For now we're not raising an error, just not saving password for # users managed by LDAP who already have an unusable password set. - try: - if obj.pk and obj.profile.ldap_dn and not obj.has_usable_password(): - new_password = None - except AttributeError: - pass + if getattr(settings, 'AUTH_LDAP_SERVER_URI', None) and feature_enabled('ldap'): + try: + if obj.pk and obj.profile.ldap_dn and not obj.has_usable_password(): + new_password = None + except AttributeError: + pass if new_password: obj.set_password(new_password) if not obj.password: @@ -633,6 +634,8 @@ class UserSerializer(BaseSerializer): return res def _validate_ldap_managed_field(self, attrs, source): + if not getattr(settings, 'AUTH_LDAP_SERVER_URI', None) or not feature_enabled('ldap'): + return attrs try: is_ldap_user = bool(self.object.profile.ldap_dn) except AttributeError: diff --git a/awx/api/views.py b/awx/api/views.py index 68ff0f318e..d9064842c0 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -190,7 +190,7 @@ class ApiV1ConfigView(APIView): # If LDAP is enabled, user_ldap_fields will return a list of field # names that are managed by LDAP and should be read-only for users with # a non-empty ldap_dn attribute. - if getattr(settings, 'AUTH_LDAP_SERVER_URI', None): + if getattr(settings, 'AUTH_LDAP_SERVER_URI', None) and feature_enabled('ldap'): user_ldap_fields = ['username', 'password'] user_ldap_fields.extend(getattr(settings, 'AUTH_LDAP_USER_ATTR_MAP', {}).keys()) user_ldap_fields.extend(getattr(settings, 'AUTH_LDAP_USER_FLAGS_BY_GROUP', {}).keys()) diff --git a/awx/main/backend.py b/awx/main/backend.py index 79e8a7a59c..537c1f33dc 100644 --- a/awx/main/backend.py +++ b/awx/main/backend.py @@ -9,6 +9,9 @@ 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 +# Ansible Tower +from awx.api.license import feature_enabled + class LDAPSettings(BaseLDAPSettings): defaults = dict(BaseLDAPSettings.defaults.items() + { @@ -34,12 +37,12 @@ class LDAPBackend(BaseLDAPBackend): settings = property(_get_settings, _set_settings) def authenticate(self, username, password): - if not self.settings.SERVER_URI: + if not self.settings.SERVER_URI or not feature_enabled('ldap'): return None return super(LDAPBackend, self).authenticate(username, password) def get_user(self, user_id): - if not self.settings.SERVER_URI: + if not self.settings.SERVER_URI or not feature_enabled('ldap'): return None return super(LDAPBackend, self).get_user(user_id) diff --git a/awx/main/tests/users.py b/awx/main/tests/users.py index d5b34aae13..329eafcc14 100644 --- a/awx/main/tests/users.py +++ b/awx/main/tests/users.py @@ -860,6 +860,7 @@ class LdapTest(BaseTest): def setUp(self): super(LdapTest, self).setUp() + self.create_test_license_file(features={'ldap': True}) # 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') @@ -933,6 +934,15 @@ class LdapTest(BaseTest): user = self.check_login() for attr in settings.AUTH_LDAP_USER_FLAGS_BY_GROUP.keys(): self.assertTrue(getattr(user, attr)) + # Check that LDAP login fails when not enabled by license, but using a + # local password will work in either case. + user.set_password('local pass') + user.save() + self.check_login() + self.check_login(password='local pass') + self.create_test_license_file(features={'ldap': False}) + self.check_login(should_fail=True) + self.check_login(password='local pass') def test_ldap_organization_mapping(self): for name in ('USER_SEARCH', 'ALWAYS_UPDATE_USER', 'USER_ATTR_MAP', @@ -1014,25 +1024,25 @@ class LdapTest(BaseTest): self.use_test_setting(name) user = self.check_login() self.setup_users() - url = reverse('api:api_v1_config_view') + config_url = reverse('api:api_v1_config_view') with self.current_user(self.super_django_user): - response = self.get(url, expect=200) + response = self.get(config_url, expect=200) user_ldap_fields = response.get('user_ldap_fields', []) self.assertTrue(user_ldap_fields) - url = reverse('api:user_detail', args=(user.pk,)) + user_url = reverse('api:user_detail', args=(user.pk,)) for user_field in user_ldap_fields: with self.current_user(self.super_django_user): - data = self.get(url, expect=200) + data = self.get(user_url, expect=200) if user_field == 'password': data[user_field] = 'my new password' with self.current_user(self.super_django_user): - self.put(url, data, expect=200) + self.put(user_url, data, expect=200) + user = User.objects.get(pk=user.pk) + self.assertFalse(user.has_usable_password()) + with self.current_user(self.super_django_user): + self.patch(user_url, {'password': 'try again'}, expect=200) user = User.objects.get(pk=user.pk) self.assertFalse(user.has_usable_password()) - #with self.current_user(self.super_django_user): - # self.patch(url, {'password': 'try again'}, expect=200) - #user = User.objects.get(pk=user.pk) - #self.assertFalse(user.has_usable_password()) elif user_field in data: value = data[user_field] if isinstance(value, bool): @@ -1041,7 +1051,40 @@ class LdapTest(BaseTest): value = unicode(value).upper() data[user_field] = value with self.current_user(self.super_django_user): - self.put(url, data, expect=400) - #patch_data = {user_field: data[user_field]} - #with self.current_user(self.super_django_user): - # self.patch(url, patch_data, expect=400) + self.put(user_url, data, expect=400) + patch_data = {user_field: data[user_field]} + with self.current_user(self.super_django_user): + self.patch(user_url, patch_data, expect=400) + # Install a license with LDAP disabled; ldap fields should not be in + # config and all user fields should be changeable. + self.create_test_license_file(features={'ldap': False}) + with self.current_user(self.super_django_user): + response = self.get(config_url, expect=200) + self.assertFalse('user_ldap_fields' in response) + for user_field in user_ldap_fields: + with self.current_user(self.super_django_user): + data = self.get(user_url, expect=200) + if user_field == 'password': + data[user_field] = 'my new password' + with self.current_user(self.super_django_user): + self.put(user_url, data, expect=200) + user = User.objects.get(pk=user.pk) + self.assertTrue(user.has_usable_password()) + self.assertTrue(user.check_password, 'my new password') + with self.current_user(self.super_django_user): + self.patch(user_url, {'password': 'try again'}, expect=200) + user = User.objects.get(pk=user.pk) + self.assertTrue(user.has_usable_password()) + self.assertTrue(user.check_password, 'try again') + elif user_field in data: + value = data[user_field] + if isinstance(value, bool): + value = not value + else: + value = unicode(value).upper() + data[user_field] = value + with self.current_user(self.super_django_user): + self.put(user_url, data, expect=200) + patch_data = {user_field: data[user_field]} + with self.current_user(self.super_django_user): + self.patch(user_url, patch_data, expect=200)