test_ldap.py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # Licensed to Cloudera, Inc. under one
  4. # or more contributor license agreements. See the NOTICE file
  5. # distributed with this work for additional information
  6. # regarding copyright ownership. Cloudera, Inc. licenses this file
  7. # to you under the Apache License, Version 2.0 (the
  8. # "License"); you may not use this file except in compliance
  9. # with the License. You may obtain a copy of the License at
  10. #
  11. # http://www.apache.org/licenses/LICENSE-2.0
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an "AS IS" BASIS,
  15. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. # See the License for the specific language governing permissions and
  17. # limitations under the License.
  18. import ldap
  19. from nose.plugins.attrib import attr
  20. from nose.plugins.skip import SkipTest
  21. from nose.tools import assert_true, assert_equal, assert_false
  22. import desktop.conf
  23. from desktop.lib.test_utils import grant_access
  24. from desktop.lib.django_test_util import make_logged_in_client
  25. from django.conf import settings
  26. from django.contrib.auth.models import User, Group
  27. from django.urls import reverse
  28. from useradmin.models import LdapGroup, UserProfile
  29. from useradmin.models import get_profile
  30. from hadoop import pseudo_hdfs4
  31. from hadoop.pseudo_hdfs4 import is_live_cluster
  32. from views import sync_ldap_users, sync_ldap_groups, import_ldap_users, import_ldap_groups, \
  33. add_ldap_users, add_ldap_groups, sync_ldap_users_groups
  34. import ldap_access
  35. from tests import BaseUserAdminTests, LdapTestConnection, reset_all_groups, reset_all_users
  36. def get_multi_ldap_config():
  37. return {'multi_ldap_conf': {
  38. 'users': {},
  39. 'groups': {}
  40. }}
  41. class TestUserAdminLdap(BaseUserAdminTests):
  42. def test_useradmin_ldap_user_group_membership_sync(self):
  43. settings.MIDDLEWARE_CLASSES.append('useradmin.middleware.LdapSynchronizationMiddleware')
  44. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  45. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  46. # Make sure LDAP groups exist or they won't sync
  47. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  48. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'Test Administrators', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  49. reset = []
  50. # Set to nonsensical value just to force new config usage.
  51. # Should continue to use cached connection.
  52. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  53. try:
  54. # Import curly who is part of TestUsers and Test Administrators
  55. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'curly', sync_groups=False, import_by_dn=False)
  56. # Set a password so that we can login
  57. user = User.objects.get(username='curly')
  58. user.set_password('test')
  59. user.save()
  60. # Should have 0 groups
  61. assert_equal(0, user.groups.all().count())
  62. # Make an authenticated request as curly so that we can see call middleware.
  63. c = make_logged_in_client('curly', 'test', is_superuser=False)
  64. grant_access("curly", "test", "useradmin")
  65. response = c.get('/useradmin/users', dict(server='multi_ldap_conf'))
  66. # Refresh user groups
  67. user = User.objects.get(username='curly')
  68. # Should have 3 groups now. 2 from LDAP and 1 from 'grant_access' call.
  69. assert_equal(3, user.groups.all().count(), user.groups.all())
  70. # Now remove a group and try again.
  71. old_group = ldap_access.CACHED_LDAP_CONN._instance.users['curly']['groups'].pop()
  72. # Make an authenticated request as curly so that we can see call middleware.
  73. response = c.get('/useradmin/users', dict(server='multi_ldap_conf'))
  74. # Refresh user groups
  75. user = User.objects.get(username='curly')
  76. # Should have 2 groups now. 1 from LDAP and 1 from 'grant_access' call.
  77. assert_equal(3, user.groups.all().count(), user.groups.all())
  78. finally:
  79. settings.MIDDLEWARE_CLASSES.remove('useradmin.middleware.LdapSynchronizationMiddleware')
  80. for finish in reset:
  81. finish()
  82. def test_useradmin_ldap_suboordinate_group_integration(self):
  83. reset = []
  84. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  85. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  86. # Test old subgroups
  87. reset.append(desktop.conf.LDAP.SUBGROUPS.set_for_testing("suboordinate"))
  88. # Set to nonsensical value just to force new config usage.
  89. # Should continue to use cached connection.
  90. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  91. try:
  92. # Import groups only
  93. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  94. test_users = Group.objects.get(name='TestUsers')
  95. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  96. assert_equal(test_users.user_set.all().count(), 0)
  97. # Import all members of TestUsers
  98. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  99. test_users = Group.objects.get(name='TestUsers')
  100. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  101. assert_equal(test_users.user_set.all().count(), 3)
  102. # Should import a group, but will only sync already-imported members
  103. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'Test Administrators', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  104. assert_equal(User.objects.all().count(), 3)
  105. assert_equal(Group.objects.all().count(), 2)
  106. test_admins = Group.objects.get(name='Test Administrators')
  107. assert_equal(test_admins.user_set.all().count(), 2)
  108. larry = User.objects.get(username='lårry')
  109. assert_equal(test_admins.user_set.all().order_by('username')[1].username, larry.username)
  110. # Only sync already imported
  111. ldap_access.CACHED_LDAP_CONN.remove_user_group_for_test('uid=moe,ou=People,dc=example,dc=com', 'TestUsers')
  112. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  113. assert_equal(test_users.user_set.all().count(), 2)
  114. assert_equal(User.objects.get(username='moe').groups.all().count(), 0)
  115. # Import missing user
  116. ldap_access.CACHED_LDAP_CONN.add_user_group_for_test('uid=moe,ou=People,dc=example,dc=com', 'TestUsers')
  117. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  118. assert_equal(test_users.user_set.all().count(), 3)
  119. assert_equal(User.objects.get(username='moe').groups.all().count(), 1)
  120. # Import all members of TestUsers and members of subgroups
  121. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=True, import_members_recursive=True, sync_users=True, import_by_dn=False)
  122. test_users = Group.objects.get(name='TestUsers')
  123. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  124. assert_equal(test_users.user_set.all().count(), 4)
  125. # Make sure Hue groups with naming collisions don't get marked as LDAP groups
  126. hue_user = User.objects.create(username='otherguy', first_name='Different', last_name='Guy')
  127. hue_group = Group.objects.create(name='OtherGroup')
  128. hue_group.user_set.add(hue_user)
  129. hue_group.save()
  130. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'OtherGroup', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  131. assert_false(LdapGroup.objects.filter(group=hue_group).exists())
  132. assert_true(hue_group.user_set.filter(username=hue_user.username).exists())
  133. finally:
  134. for finish in reset:
  135. finish()
  136. def test_useradmin_ldap_nested_group_integration(self):
  137. reset = []
  138. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  139. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  140. # Test old subgroups
  141. reset.append(desktop.conf.LDAP.SUBGROUPS.set_for_testing("nested"))
  142. # Set to nonsensical value just to force new config usage.
  143. # Should continue to use cached connection.
  144. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  145. try:
  146. # Import groups only
  147. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  148. test_users = Group.objects.get(name='TestUsers')
  149. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  150. assert_equal(test_users.user_set.all().count(), 0)
  151. # Import all members of TestUsers
  152. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  153. test_users = Group.objects.get(name='TestUsers')
  154. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  155. assert_equal(test_users.user_set.all().count(), 3)
  156. # Should import a group, but will only sync already-imported members
  157. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'Test Administrators', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  158. assert_equal(User.objects.all().count(), 3)
  159. assert_equal(Group.objects.all().count(), 2)
  160. test_admins = Group.objects.get(name='Test Administrators')
  161. assert_equal(test_admins.user_set.all().count(), 2)
  162. larry = User.objects.get(username='lårry')
  163. assert_equal(test_admins.user_set.all().order_by('username')[1].username, larry.username)
  164. # Only sync already imported
  165. assert_equal(test_users.user_set.all().count(), 3)
  166. ldap_access.CACHED_LDAP_CONN.remove_user_group_for_test('uid=moe,ou=People,dc=example,dc=com', 'TestUsers')
  167. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  168. assert_equal(test_users.user_set.all().count(), 2)
  169. assert_equal(User.objects.get(username='moe').groups.all().count(), 0)
  170. # Import missing user
  171. ldap_access.CACHED_LDAP_CONN.add_user_group_for_test('uid=moe,ou=People,dc=example,dc=com', 'TestUsers')
  172. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  173. assert_equal(test_users.user_set.all().count(), 3)
  174. assert_equal(User.objects.get(username='moe').groups.all().count(), 1)
  175. # Import all members of TestUsers and not members of suboordinate groups (even though specified)
  176. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=True, import_members_recursive=True, sync_users=True, import_by_dn=False)
  177. test_users = Group.objects.get(name='TestUsers')
  178. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  179. assert_equal(test_users.user_set.all().count(), 3)
  180. # Nested group import
  181. # First without recursive import, then with.
  182. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'NestedGroups', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  183. nested_groups = Group.objects.get(name='NestedGroups')
  184. nested_group = Group.objects.get(name='NestedGroup')
  185. assert_true(LdapGroup.objects.filter(group=nested_groups).exists())
  186. assert_true(LdapGroup.objects.filter(group=nested_group).exists())
  187. assert_equal(nested_groups.user_set.all().count(), 0, nested_groups.user_set.all())
  188. assert_equal(nested_group.user_set.all().count(), 0, nested_group.user_set.all())
  189. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'NestedGroups', import_members=True, import_members_recursive=True, sync_users=True, import_by_dn=False)
  190. nested_groups = Group.objects.get(name='NestedGroups')
  191. nested_group = Group.objects.get(name='NestedGroup')
  192. assert_true(LdapGroup.objects.filter(group=nested_groups).exists())
  193. assert_true(LdapGroup.objects.filter(group=nested_group).exists())
  194. assert_equal(nested_groups.user_set.all().count(), 0, nested_groups.user_set.all())
  195. assert_equal(nested_group.user_set.all().count(), 1, nested_group.user_set.all())
  196. # Make sure Hue groups with naming collisions don't get marked as LDAP groups
  197. hue_user = User.objects.create(username='otherguy', first_name='Different', last_name='Guy')
  198. hue_group = Group.objects.create(name='OtherGroup')
  199. hue_group.user_set.add(hue_user)
  200. hue_group.save()
  201. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'OtherGroup', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  202. assert_false(LdapGroup.objects.filter(group=hue_group).exists())
  203. assert_true(hue_group.user_set.filter(username=hue_user.username).exists())
  204. finally:
  205. for finish in reset:
  206. finish()
  207. def test_useradmin_ldap_suboordinate_posix_group_integration(self):
  208. reset = []
  209. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  210. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  211. # Test old subgroups
  212. reset.append(desktop.conf.LDAP.SUBGROUPS.set_for_testing("suboordinate"))
  213. # Set to nonsensical value just to force new config usage.
  214. # Should continue to use cached connection.
  215. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  216. try:
  217. # Import groups only
  218. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  219. test_users = Group.objects.get(name='PosixGroup')
  220. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  221. assert_equal(test_users.user_set.all().count(), 0)
  222. # Import all members of TestUsers
  223. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  224. test_users = Group.objects.get(name='PosixGroup')
  225. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  226. assert_equal(test_users.user_set.all().count(), 2)
  227. # Should import a group, but will only sync already-imported members
  228. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'Test Administrators', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  229. assert_equal(User.objects.all().count(), 2, User.objects.all())
  230. assert_equal(Group.objects.all().count(), 2, Group.objects.all())
  231. test_admins = Group.objects.get(name='Test Administrators')
  232. assert_equal(test_admins.user_set.all().count(), 1)
  233. larry = User.objects.get(username='lårry')
  234. assert_equal(test_admins.user_set.all()[0].username, larry.username)
  235. # Only sync already imported
  236. ldap_access.CACHED_LDAP_CONN.remove_posix_user_group_for_test('posix_person', 'PosixGroup')
  237. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  238. assert_equal(test_users.user_set.all().count(), 1)
  239. assert_equal(User.objects.get(username='posix_person').groups.all().count(), 0)
  240. # Import missing user
  241. ldap_access.CACHED_LDAP_CONN.add_posix_user_group_for_test('posix_person', 'PosixGroup')
  242. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  243. assert_equal(test_users.user_set.all().count(), 2)
  244. assert_equal(User.objects.get(username='posix_person').groups.all().count(), 1)
  245. # Import all members of PosixGroup and members of subgroups
  246. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=True, import_members_recursive=True, sync_users=True, import_by_dn=False)
  247. test_users = Group.objects.get(name='PosixGroup')
  248. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  249. assert_equal(test_users.user_set.all().count(), 3)
  250. # Make sure Hue groups with naming collisions don't get marked as LDAP groups
  251. hue_user = User.objects.create(username='otherguy', first_name='Different', last_name='Guy')
  252. hue_group = Group.objects.create(name='OtherGroup')
  253. hue_group.user_set.add(hue_user)
  254. hue_group.save()
  255. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'OtherGroup', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  256. assert_false(LdapGroup.objects.filter(group=hue_group).exists())
  257. assert_true(hue_group.user_set.filter(username=hue_user.username).exists())
  258. finally:
  259. for finish in reset:
  260. finish()
  261. def test_useradmin_ldap_nested_posix_group_integration(self):
  262. reset = []
  263. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  264. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  265. # Test nested groups
  266. reset.append(desktop.conf.LDAP.SUBGROUPS.set_for_testing("nested"))
  267. # Set to nonsensical value just to force new config usage.
  268. # Should continue to use cached connection.
  269. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  270. try:
  271. # Import groups only
  272. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  273. test_users = Group.objects.get(name='PosixGroup')
  274. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  275. assert_equal(test_users.user_set.all().count(), 0)
  276. # Import all members of TestUsers
  277. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  278. test_users = Group.objects.get(name='PosixGroup')
  279. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  280. assert_equal(test_users.user_set.all().count(), 2)
  281. # Should import a group, but will only sync already-imported members
  282. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'Test Administrators', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  283. assert_equal(User.objects.all().count(), 2, User.objects.all())
  284. assert_equal(Group.objects.all().count(), 2, Group.objects.all())
  285. test_admins = Group.objects.get(name='Test Administrators')
  286. assert_equal(test_admins.user_set.all().count(), 1)
  287. larry = User.objects.get(username='lårry')
  288. assert_equal(test_admins.user_set.all()[0].username, larry.username)
  289. # Only sync already imported
  290. ldap_access.CACHED_LDAP_CONN.remove_posix_user_group_for_test('posix_person', 'PosixGroup')
  291. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  292. assert_equal(test_users.user_set.all().count(), 1)
  293. assert_equal(User.objects.get(username='posix_person').groups.all().count(), 0)
  294. # Import missing user
  295. ldap_access.CACHED_LDAP_CONN.add_posix_user_group_for_test('posix_person', 'PosixGroup')
  296. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  297. assert_equal(test_users.user_set.all().count(), 2)
  298. assert_equal(User.objects.get(username='posix_person').groups.all().count(), 1)
  299. # Import all members of PosixGroup and members of subgroups (there should be no subgroups)
  300. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'PosixGroup', import_members=True, import_members_recursive=True, sync_users=True, import_by_dn=False)
  301. test_users = Group.objects.get(name='PosixGroup')
  302. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  303. assert_equal(test_users.user_set.all().count(), 2)
  304. # Import all members of NestedPosixGroups and members of subgroups
  305. reset_all_users()
  306. reset_all_groups()
  307. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'NestedPosixGroups', import_members=True, import_members_recursive=True, sync_users=True, import_by_dn=False)
  308. test_users = Group.objects.get(name='NestedPosixGroups')
  309. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  310. assert_equal(test_users.user_set.all().count(), 0)
  311. test_users = Group.objects.get(name='PosixGroup')
  312. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  313. assert_equal(test_users.user_set.all().count(), 2)
  314. # Make sure Hue groups with naming collisions don't get marked as LDAP groups
  315. hue_user = User.objects.create(username='otherguy', first_name='Different', last_name='Guy')
  316. hue_group = Group.objects.create(name='OtherGroup')
  317. hue_group.user_set.add(hue_user)
  318. hue_group.save()
  319. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'OtherGroup', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  320. assert_false(LdapGroup.objects.filter(group=hue_group).exists())
  321. assert_true(hue_group.user_set.filter(username=hue_user.username).exists())
  322. finally:
  323. for finish in reset:
  324. finish()
  325. @attr('integration')
  326. def test_useradmin_ldap_user_integration(self):
  327. if is_live_cluster():
  328. raise SkipTest('HUE-2897: Skipping because the DB may not be case sensitive')
  329. done = []
  330. # Set to nonsensical value just to force new config usage.
  331. # Should continue to use cached connection.
  332. done.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  333. try:
  334. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  335. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  336. # Try importing a user
  337. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'lårry', sync_groups=False, import_by_dn=False)
  338. larry = User.objects.get(username='lårry')
  339. assert_true(larry.first_name == 'Larry')
  340. assert_true(larry.last_name == 'Stooge')
  341. assert_true(larry.email == 'larry@stooges.com')
  342. assert_true(get_profile(larry).creation_method == UserProfile.CreationMethod.EXTERNAL.name)
  343. # Should be a noop
  344. sync_ldap_users(ldap_access.CACHED_LDAP_CONN)
  345. sync_ldap_groups(ldap_access.CACHED_LDAP_CONN)
  346. assert_equal(User.objects.all().count(), 1)
  347. assert_equal(Group.objects.all().count(), 0)
  348. # Make sure that if a Hue user already exists with a naming collision, we
  349. # won't overwrite any of that user's information.
  350. hue_user = User.objects.create(username='otherguy', first_name='Different', last_name='Guy')
  351. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'otherguy', sync_groups=False, import_by_dn=False)
  352. hue_user = User.objects.get(username='otherguy')
  353. assert_equal(get_profile(hue_user).creation_method, UserProfile.CreationMethod.HUE.name)
  354. assert_equal(hue_user.first_name, 'Different')
  355. # Make sure LDAP groups exist or they won't sync
  356. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'TestUsers', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  357. import_ldap_groups(ldap_access.CACHED_LDAP_CONN, 'Test Administrators', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  358. # Try importing a user and sync groups
  359. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'curly', sync_groups=True, import_by_dn=False, server='multi_ldap_conf')
  360. curly = User.objects.get(username='curly')
  361. assert_equal(curly.first_name, 'Curly')
  362. assert_equal(curly.last_name, 'Stooge')
  363. assert_equal(curly.email, 'curly@stooges.com')
  364. assert_equal(get_profile(curly).creation_method, UserProfile.CreationMethod.EXTERNAL.name)
  365. assert_equal(2, curly.groups.all().count(), curly.groups.all())
  366. reset_all_users()
  367. reset_all_groups()
  368. # Test import case sensitivity
  369. done.append(desktop.conf.LDAP.IGNORE_USERNAME_CASE.set_for_testing(True))
  370. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'Lårry', sync_groups=False, import_by_dn=False)
  371. assert_false(User.objects.filter(username='Lårry').exists())
  372. assert_true(User.objects.filter(username='lårry').exists())
  373. # Test lower case
  374. User.objects.filter(username__iexact='Rock').delete()
  375. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'Rock', sync_groups=False, import_by_dn=False)
  376. assert_false(User.objects.filter(username='Rock').exists())
  377. assert_true(User.objects.filter(username='rock').exists())
  378. done.append(desktop.conf.LDAP.FORCE_USERNAME_LOWERCASE.set_for_testing(True))
  379. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'Rock', sync_groups=False, import_by_dn=False)
  380. assert_false(User.objects.filter(username='Rock').exists())
  381. assert_true(User.objects.filter(username='rock').exists())
  382. User.objects.filter(username='Rock').delete()
  383. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'Rock', sync_groups=False, import_by_dn=False)
  384. assert_false(User.objects.filter(username='Rock').exists())
  385. assert_true(User.objects.filter(username='rock').exists())
  386. finally:
  387. for finish in done:
  388. finish()
  389. def test_useradmin_ldap_force_uppercase(self):
  390. if is_live_cluster():
  391. raise SkipTest('HUE-2897: Skipping because the DB may not be case sensitive')
  392. done = []
  393. # Set to nonsensical value just to force new config usage.
  394. # Should continue to use cached connection.
  395. done.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  396. try:
  397. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  398. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  399. # Test upper case
  400. User.objects.filter(username__iexact='Rock').delete()
  401. done.append(desktop.conf.LDAP.IGNORE_USERNAME_CASE.set_for_testing(False))
  402. done.append(desktop.conf.LDAP.FORCE_USERNAME_LOWERCASE.set_for_testing(False))
  403. done.append(desktop.conf.LDAP.FORCE_USERNAME_UPPERCASE.set_for_testing(True))
  404. import_ldap_users(ldap_access.CACHED_LDAP_CONN, 'Rock', sync_groups=False, import_by_dn=False)
  405. assert_true(User.objects.filter(username='ROCK').exists())
  406. finally:
  407. for finish in done:
  408. finish()
  409. @attr('integration')
  410. def test_add_ldap_users(self):
  411. if is_live_cluster():
  412. raise SkipTest('HUE-2897: Skipping because the DB may not be case sensitive')
  413. done = []
  414. # Set to nonsensical value just to force new config usage.
  415. # Should continue to use cached connection.
  416. done.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  417. try:
  418. URL = reverse(add_ldap_users)
  419. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  420. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  421. c = make_logged_in_client('test', is_superuser=True)
  422. assert_true(c.get(URL))
  423. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='moe', password1='test', password2='test'))
  424. assert_true('Location' in response, response)
  425. assert_true('/useradmin/users' in response['Location'], response)
  426. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='bad_name', password1='test', password2='test'))
  427. assert_true('Could not' in response.context[0]['form'].errors['username_pattern'][0], response)
  428. # Test wild card
  429. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='*rr*', password1='test', password2='test'))
  430. assert_true('/useradmin/users' in response['Location'], response)
  431. # Test ignore case
  432. done.append(desktop.conf.LDAP.IGNORE_USERNAME_CASE.set_for_testing(True))
  433. User.objects.filter(username='moe').delete()
  434. assert_false(User.objects.filter(username='Moe').exists())
  435. assert_false(User.objects.filter(username='moe').exists())
  436. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='Moe', password1='test', password2='test'))
  437. assert_true('Location' in response, response)
  438. assert_true('/useradmin/users' in response['Location'], response)
  439. assert_false(User.objects.filter(username='Moe').exists())
  440. assert_true(User.objects.filter(username='moe').exists())
  441. # Test lower case
  442. done.append(desktop.conf.LDAP.FORCE_USERNAME_LOWERCASE.set_for_testing(True))
  443. User.objects.filter(username__iexact='Rock').delete()
  444. assert_false(User.objects.filter(username='Rock').exists())
  445. assert_false(User.objects.filter(username='rock').exists())
  446. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='rock', password1='test', password2='test'))
  447. assert_true('Location' in response, response)
  448. assert_true('/useradmin/users' in response['Location'], response)
  449. assert_false(User.objects.filter(username='Rock').exists())
  450. assert_true(User.objects.filter(username='rock').exists())
  451. # Test regular with spaces (should fail)
  452. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='user with space', password1='test', password2='test'))
  453. assert_true("Username must not contain whitespaces and ':'" in response.context[0]['form'].errors['username_pattern'][0], response)
  454. # Test dn with spaces in username and dn (should fail)
  455. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='uid=user with space,ou=People,dc=example,dc=com', password1='test', password2='test', dn=True))
  456. assert_true("Could not get LDAP details for users in pattern" in response.content, response.content)
  457. response = c.get(reverse(desktop.views.log_view))
  458. assert_true("{username}: Username must not contain whitespaces".format(username='user with space') in response.content, response.content)
  459. # Test dn with spaces in dn, but not username (should succeed)
  460. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='uid=user without space,ou=People,dc=example,dc=com', password1='test', password2='test', dn=True))
  461. assert_true(User.objects.filter(username='spaceless').exists())
  462. finally:
  463. for finish in done:
  464. finish()
  465. def test_add_ldap_users_force_uppercase(self):
  466. if is_live_cluster():
  467. raise SkipTest('HUE-2897: Skipping because the DB may not be case sensitive')
  468. done = []
  469. # Set to nonsensical value just to force new config usage.
  470. # Should continue to use cached connection.
  471. done.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  472. try:
  473. URL = reverse(add_ldap_users)
  474. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  475. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  476. c = make_logged_in_client('test', is_superuser=True)
  477. assert_true(c.get(URL))
  478. # Test upper case
  479. done.append(desktop.conf.LDAP.IGNORE_USERNAME_CASE.set_for_testing(False))
  480. done.append(desktop.conf.LDAP.FORCE_USERNAME_LOWERCASE.set_for_testing(False))
  481. done.append(desktop.conf.LDAP.FORCE_USERNAME_UPPERCASE.set_for_testing(True))
  482. User.objects.filter(username='rock').delete()
  483. assert_false(User.objects.filter(username='Rock').exists())
  484. assert_false(User.objects.filter(username='ROCK').exists())
  485. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='Rock', password1='test', password2='test'))
  486. assert_true('Location' in response, response)
  487. assert_true('/useradmin/users' in response['Location'], response)
  488. assert_true(User.objects.filter(username='ROCK').exists())
  489. finally:
  490. for finish in done:
  491. finish()
  492. def test_ldap_import_truncate_first_last_name(self):
  493. test_ldap_data = [('uid=testuser,ou=people,dc=sec,dc=test,dc=com', {'objectClass': ['inetOrgPerson', 'posixAccount', 'shadowAccount'], 'mail': ['testuser@sec.test.com'], 'givenName': ['Firstnamehasmorethanthirtycharacters'], 'uid': ['testuser'], 'sn': ['Lastnamehasmorethanthirtycharacters']})]
  494. # Checking if first/last name truncation works for LDAP imports
  495. user_info = ldap_access.LdapConnection._transform_find_user_results(result_data=test_ldap_data, user_name_attr='uid')
  496. assert_false(len(user_info[0]['first']) > 30)
  497. assert_false(len(user_info[0]['last']) > 30)
  498. assert_true(user_info[0]['first'] == 'Firstnamehasmorethanthirtychar', user_info[0]['first'])
  499. assert_true(user_info[0]['last'] == 'Lastnamehasmorethanthirtychara', user_info[0]['last'])
  500. def test_add_ldap_groups(self):
  501. URL = reverse(add_ldap_groups)
  502. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  503. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  504. c = make_logged_in_client(username='test', is_superuser=True)
  505. reset = []
  506. # Set to nonsensical value just to force new config usage.
  507. # Should continue to use cached connection.
  508. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  509. try:
  510. assert_true(c.get(URL))
  511. response = c.post(URL, dict(server='multi_ldap_conf', groupname_pattern='TestUsers'))
  512. assert_true('Location' in response, response)
  513. assert_true('/useradmin/groups' in response['Location'])
  514. # Test warning notification for failed users on group import
  515. # Import test_longfirstname user
  516. ldap_access.CACHED_LDAP_CONN.add_user_group_for_test('uid=test_longfirstname,ou=People,dc=example,dc=com', 'TestUsers')
  517. response = c.post(URL, dict(server='multi_ldap_conf', groupname_pattern='TestUsers', import_members=True), follow=True)
  518. assert_true('Failed to import following users: test_toolongusernametoolongusername, test_longfirstname' in response.content, response.content)
  519. # Test with space
  520. response = c.post(URL, dict(server='multi_ldap_conf', groupname_pattern='Test Administrators'))
  521. assert_true('Location' in response, response)
  522. assert_true('/useradmin/groups' in response['Location'], response)
  523. response = c.post(URL, dict(server='multi_ldap_conf', groupname_pattern='toolongnametoolongnametoolongnametoolongname'
  524. 'toolongnametoolongnametoolongnametoolongname'
  525. 'toolongnametoolongnametoolongnametoolongname'
  526. 'toolongnametoolongnametoolongnametoolongname'
  527. 'toolongnametoolongnametoolongnametoolongname'
  528. 'toolongnametoolongnametoolongnametoolongname'))
  529. assert_true('Ensure this value has at most 256 characters' in response.context[0]['form'].errors['groupname_pattern'][0], response)
  530. # Test wild card
  531. response = c.post(URL, dict(server='multi_ldap_conf', groupname_pattern='*r*'))
  532. assert_true('/useradmin/groups' in response['Location'], response)
  533. finally:
  534. for finish in reset:
  535. finish()
  536. def test_sync_ldap_users_groups(self):
  537. URL = reverse(sync_ldap_users_groups)
  538. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  539. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  540. c = make_logged_in_client('test', is_superuser=True)
  541. reset = []
  542. # Set to nonsensical value just to force new config usage.
  543. # Should continue to use cached connection.
  544. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  545. try:
  546. assert_true(c.get(URL))
  547. assert_true(c.post(URL))
  548. finally:
  549. for finish in reset:
  550. finish()
  551. def test_ldap_exception_handling(self):
  552. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  553. class LdapTestConnectionError(LdapTestConnection):
  554. def find_users(self, user, find_by_dn=False):
  555. raise ldap.LDAPError('No such object')
  556. ldap_access.CACHED_LDAP_CONN = LdapTestConnectionError()
  557. c = make_logged_in_client('test', is_superuser=True)
  558. reset = []
  559. # Set to nonsensical value just to force new config usage.
  560. # Should continue to use cached connection.
  561. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  562. try:
  563. response = c.post(reverse(add_ldap_users), dict(server='multi_ldap_conf', username_pattern='moe', password1='test', password2='test'), follow=True)
  564. assert_true('There was an error when communicating with LDAP' in response.content, response)
  565. finally:
  566. for finish in reset:
  567. finish()
  568. class TestUserAdminLdapWithHadoop(BaseUserAdminTests):
  569. requires_hadoop = True
  570. integration = True
  571. def test_ensure_home_directory_add_ldap_users(self):
  572. URL = reverse(add_ldap_users)
  573. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  574. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  575. cluster = pseudo_hdfs4.shared_cluster()
  576. c = make_logged_in_client(cluster.superuser, is_superuser=True)
  577. cluster.fs.setuser(cluster.superuser)
  578. reset = []
  579. # Set to nonsensical value just to force new config usage.
  580. # Should continue to use cached connection.
  581. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  582. try:
  583. assert_true(c.get(URL))
  584. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='moe', password1='test', password2='test'))
  585. assert_true('/useradmin/users' in response['Location'])
  586. assert_false(cluster.fs.exists('/user/moe'))
  587. # Try same thing with home directory creation.
  588. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='curly', password1='test', password2='test', ensure_home_directory=True))
  589. assert_true('/useradmin/users' in response['Location'])
  590. assert_true(cluster.fs.exists('/user/curly'))
  591. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='bad_name', password1='test', password2='test'))
  592. assert_true('Could not' in response.context[0]['form'].errors['username_pattern'][0])
  593. assert_false(cluster.fs.exists('/user/bad_name'))
  594. # See if moe, who did not ask for his home directory, has a home directory.
  595. assert_false(cluster.fs.exists('/user/moe'))
  596. # Try wild card now
  597. response = c.post(URL, dict(server='multi_ldap_conf', username_pattern='*rr*', password1='test', password2='test', ensure_home_directory=True))
  598. assert_true('/useradmin/users' in response['Location'])
  599. assert_true(cluster.fs.exists('/user/curly'))
  600. assert_true(cluster.fs.exists(u'/user/lårry'))
  601. assert_false(cluster.fs.exists('/user/otherguy'))
  602. finally:
  603. # Clean up
  604. for finish in reset:
  605. finish()
  606. if cluster.fs.exists('/user/curly'):
  607. cluster.fs.rmtree('/user/curly')
  608. if cluster.fs.exists(u'/user/lårry'):
  609. cluster.fs.rmtree(u'/user/lårry')
  610. if cluster.fs.exists('/user/otherguy'):
  611. cluster.fs.rmtree('/user/otherguy')
  612. def test_ensure_home_directory_sync_ldap_users_groups(self):
  613. URL = reverse(sync_ldap_users_groups)
  614. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  615. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  616. cluster = pseudo_hdfs4.shared_cluster()
  617. c = make_logged_in_client(cluster.superuser, is_superuser=True)
  618. cluster.fs.setuser(cluster.superuser)
  619. reset = []
  620. # Set to nonsensical value just to force new config usage.
  621. # Should continue to use cached connection.
  622. reset.append(desktop.conf.LDAP.LDAP_SERVERS.set_for_testing(get_multi_ldap_config()))
  623. try:
  624. c.post(reverse(add_ldap_users), dict(server='multi_ldap_conf', username_pattern='curly', password1='test', password2='test'))
  625. assert_false(cluster.fs.exists('/user/curly'))
  626. assert_true(c.post(URL, dict(server='multi_ldap_conf', ensure_home_directory=True)))
  627. assert_true(cluster.fs.exists('/user/curly'))
  628. finally:
  629. for finish in reset:
  630. finish()
  631. if cluster.fs.exists('/user/curly'):
  632. cluster.fs.rmtree('/user/curly')