tests.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  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. """
  19. Tests for "user admin"
  20. """
  21. import re
  22. import urllib
  23. import ldap
  24. from nose.plugins.attrib import attr
  25. from nose.tools import assert_true, assert_equal, assert_false
  26. import desktop.conf
  27. from desktop.lib.test_utils import grant_access
  28. from desktop.lib.django_test_util import make_logged_in_client
  29. from django.conf import settings
  30. from django.contrib.auth.models import User, Group
  31. from django.utils.encoding import smart_unicode
  32. from django.core.urlresolvers import reverse
  33. from useradmin.models import HuePermission, GroupPermission, LdapGroup, UserProfile
  34. from useradmin.models import get_profile, get_default_user_group
  35. import useradmin.conf
  36. from hadoop import pseudo_hdfs4
  37. from views import sync_ldap_users, sync_ldap_groups, import_ldap_users, import_ldap_groups, \
  38. add_ldap_users, add_ldap_groups, sync_ldap_users_groups
  39. import ldap_access
  40. def reset_all_users():
  41. """Reset to a clean state by deleting all users"""
  42. for user in User.objects.all():
  43. user.delete()
  44. def reset_all_groups():
  45. """Reset to a clean state by deleting all groups"""
  46. useradmin.conf.DEFAULT_USER_GROUP.set_for_testing(None)
  47. for grp in Group.objects.all():
  48. grp.delete()
  49. class LdapTestConnection(object):
  50. """
  51. Test class which mimics the behaviour of LdapConnection (from ldap_access.py).
  52. It also includes functionality to fake modifications to an LDAP server. It is designed
  53. as a singleton, to allow for changes to persist across discrete connections.
  54. """
  55. def __init__(self):
  56. self._instance = LdapTestConnection.Data()
  57. def add_user_group_for_test(self, user, group):
  58. self._instance.groups[group]['members'].append(user)
  59. def remove_user_group_for_test(self, user, group):
  60. self._instance.groups[group]['members'].remove(user)
  61. def find_users(self, username_pattern, find_by_dn=False, scope=ldap.SCOPE_SUBTREE):
  62. """ Returns info for a particular user """
  63. if find_by_dn:
  64. data = filter(lambda attrs: attrs['dn'] == username_pattern, self._instance.users.values())
  65. else:
  66. username_pattern = username_pattern.replace('.','\\.').replace('*', '.*')
  67. usernames = filter(lambda username: re.match(username_pattern, username), self._instance.users.keys())
  68. data = [self._instance.users.get(username) for username in usernames]
  69. return data
  70. def find_groups(self, groupname_pattern, find_by_dn=False, scope=ldap.SCOPE_SUBTREE):
  71. """ Return all groups in the system with parents and children """
  72. if find_by_dn:
  73. data = filter(lambda attrs: attrs['dn'] == groupname_pattern, self._instance.groups.values())
  74. # SCOPE_SUBTREE means we return all sub-entries of the desired entry along with the desired entry.
  75. if data and scope == ldap.SCOPE_SUBTREE:
  76. sub_data = filter(lambda attrs: attrs['dn'].endswith(data[0]['dn']), self._instance.groups.values())
  77. data.extend(sub_data)
  78. else:
  79. groupname_pattern = groupname_pattern.replace('.','\\.').replace('*', '.*')
  80. groupnames = filter(lambda username: re.match(groupname_pattern, username), self._instance.groups.keys())
  81. data = [self._instance.groups.get(groupname) for groupname in groupnames]
  82. return data
  83. class Data:
  84. def __init__(self):
  85. self.users = {'moe': {'dn': 'uid=moe,ou=People,dc=example,dc=com', 'username':'moe', 'first':'Moe', 'email':'moe@stooges.com', 'groups': ['cn=TestUsers,ou=Groups,dc=example,dc=com']},
  86. 'lårry': {'dn': 'uid=lårry,ou=People,dc=example,dc=com', 'username':'lårry', 'first':'Larry', 'last':'Stooge', 'email':'larry@stooges.com', 'groups': ['cn=TestUsers,ou=Groups,dc=example,dc=com', 'cn=Test Administrators,cn=TestUsers,ou=Groups,dc=example,dc=com']},
  87. 'curly': {'dn': 'uid=curly,ou=People,dc=example,dc=com', 'username':'curly', 'first':'Curly', 'last':'Stooge', 'email':'curly@stooges.com', 'groups': ['cn=TestUsers,ou=Groups,dc=example,dc=com', 'cn=Test Administrators,cn=TestUsers,ou=Groups,dc=example,dc=com']},
  88. 'rock': {'dn': 'uid=rock,ou=People,dc=example,dc=com', 'username':'rock', 'first':'rock', 'last':'man', 'email':'rockman@stooges.com', 'groups': ['cn=Test Administrators,cn=TestUsers,ou=Groups,dc=example,dc=com']},
  89. 'otherguy': {'dn': 'uid=otherguy,ou=People,dc=example,dc=com', 'username':'otherguy', 'first':'Other', 'last':'Guy', 'email':'other@guy.com'}}
  90. self.groups = {'TestUsers': {
  91. 'dn': 'cn=TestUsers,ou=Groups,dc=example,dc=com',
  92. 'name':'TestUsers',
  93. 'members':['uid=moe,ou=People,dc=example,dc=com','uid=lårry,ou=People,dc=example,dc=com','uid=curly,ou=People,dc=example,dc=com']},
  94. 'Test Administrators': {
  95. 'dn': 'cn=Test Administrators,cn=TestUsers,ou=Groups,dc=example,dc=com',
  96. 'name':'Test Administrators',
  97. 'members':['uid=rock,ou=People,dc=example,dc=com','uid=lårry,ou=People,dc=example,dc=com','uid=curly,ou=People,dc=example,dc=com']},
  98. 'OtherGroup': {
  99. 'dn': 'cn=OtherGroup,cn=TestUsers,ou=Groups,dc=example,dc=com',
  100. 'name':'OtherGroup',
  101. 'members':[]}}
  102. def test_invalid_username():
  103. BAD_NAMES = ('-foo', 'foo:o', 'foo o', ' foo')
  104. c = make_logged_in_client(username="test", is_superuser=True)
  105. for bad_name in BAD_NAMES:
  106. assert_true(c.get('/useradmin/users/new'))
  107. response = c.post('/useradmin/users/new', dict(username=bad_name, password1="test", password2="test"))
  108. assert_true('not allowed' in response.context["form"].errors['username'][0])
  109. def test_group_permissions():
  110. reset_all_users()
  111. reset_all_groups()
  112. # Get ourselves set up with a user and a group
  113. c = make_logged_in_client(username="test", is_superuser=True)
  114. Group.objects.create(name="test-group")
  115. test_user = User.objects.get(username="test")
  116. test_user.groups.add(Group.objects.get(name="test-group"))
  117. test_user.save()
  118. # Make sure that a superuser can always access applications
  119. response = c.get('/useradmin/users')
  120. assert_true('Hue Users' in response.content)
  121. assert_true(len(GroupPermission.objects.all()) == 0)
  122. c.post('/useradmin/groups/edit/test-group',
  123. dict(name="test-group",
  124. members=[User.objects.get(username="test").pk],
  125. permissions=[HuePermission.objects.get(app='useradmin',action='access').pk],
  126. save="Save"), follow=True)
  127. assert_true(len(GroupPermission.objects.all()) == 1)
  128. # Now test that we have limited access
  129. c1 = make_logged_in_client(username="nonadmin", is_superuser=False)
  130. response = c1.get('/useradmin/users')
  131. assert_true('You do not have permission to access the Useradmin application.' in response.content)
  132. # Add the non-admin to a group that should grant permissions to the app
  133. test_user = User.objects.get(username="nonadmin")
  134. test_user.groups.add(Group.objects.get(name='test-group'))
  135. test_user.save()
  136. # Check that we have access now
  137. response = c1.get('/useradmin/users')
  138. assert_true(get_profile(test_user).has_hue_permission('access','useradmin'))
  139. assert_true('Hue Users' in response.content)
  140. # Make sure we can't modify permissions
  141. response = c1.get('/useradmin/permissions/edit/useradmin/access')
  142. assert_true('must be a superuser to change permissions' in response.content)
  143. # And revoke access from the group
  144. c.post('/useradmin/permissions/edit/useradmin/access',
  145. dict(app='useradmin',
  146. priv='access',
  147. groups=[],
  148. save="Save"), follow=True)
  149. assert_true(len(GroupPermission.objects.all()) == 0)
  150. assert_false(get_profile(test_user).has_hue_permission('access','useradmin'))
  151. # We should no longer have access to the app
  152. response = c1.get('/useradmin/users')
  153. assert_true('You do not have permission to access the Useradmin application.' in response.content)
  154. def test_default_group():
  155. reset_all_users()
  156. reset_all_groups()
  157. useradmin.conf.DEFAULT_USER_GROUP.set_for_testing('test_default')
  158. get_default_user_group()
  159. c = make_logged_in_client(username='test', is_superuser=True)
  160. # Create default group if it doesn't already exist.
  161. assert_true(Group.objects.filter(name='test_default').exists())
  162. # Try deleting the default group
  163. assert_true(Group.objects.filter(name='test_default').exists())
  164. response = c.post('/useradmin/groups/delete/test_default')
  165. assert_true('default user group may not be deleted' in response.content)
  166. assert_true(Group.objects.filter(name='test_default').exists())
  167. # Change the name of the default group, and try deleting again
  168. useradmin.conf.DEFAULT_USER_GROUP.set_for_testing('new_default')
  169. response = c.post('/useradmin/groups/delete/test_default')
  170. assert_false(Group.objects.filter(name='test_default').exists())
  171. assert_true(Group.objects.filter(name='new_default').exists())
  172. def test_get_profile():
  173. # Ensure profiles are created after get_profile is called.
  174. reset_all_users()
  175. reset_all_groups()
  176. c = make_logged_in_client(username='test', password='test', is_superuser=True)
  177. assert_equal(0, UserProfile.objects.count())
  178. p = get_profile(User.objects.get(username='test'))
  179. assert_equal(1, UserProfile.objects.count())
  180. def test_group_admin():
  181. reset_all_users()
  182. reset_all_groups()
  183. c = make_logged_in_client(username="test", is_superuser=True)
  184. response = c.get('/useradmin/groups')
  185. # No groups just yet
  186. assert_true(len(response.context["groups"]) == 0)
  187. assert_true("Hue Groups" in response.content)
  188. # Create a group
  189. response = c.get('/useradmin/groups/new')
  190. assert_equal('/useradmin/groups/new', response.context['action'])
  191. c.post('/useradmin/groups/new', dict(name="testgroup"))
  192. # We should have an empty group in the DB now
  193. assert_true(len(Group.objects.all()) == 1)
  194. assert_true(Group.objects.filter(name="testgroup").exists())
  195. assert_true(len(Group.objects.get(name="testgroup").user_set.all()) == 0)
  196. # And now, just for kicks, let's try adding a user
  197. response = c.post('/useradmin/groups/edit/testgroup',
  198. dict(name="testgroup",
  199. members=[User.objects.get(username="test").pk],
  200. save="Save"), follow=True)
  201. assert_true(len(Group.objects.get(name="testgroup").user_set.all()) == 1)
  202. assert_true(Group.objects.get(name="testgroup").user_set.filter(username="test").exists())
  203. # Test some permissions
  204. c2 = make_logged_in_client(username="nonadmin", is_superuser=False)
  205. # Need to give access to the user for the rest of the test
  206. group = Group.objects.create(name="access-group")
  207. perm = HuePermission.objects.get(app='useradmin', action='access')
  208. GroupPermission.objects.create(group=group, hue_permission=perm)
  209. test_user = User.objects.get(username="nonadmin")
  210. test_user.groups.add(Group.objects.get(name="access-group"))
  211. test_user.save()
  212. # Make sure non-superusers can't do bad things
  213. response = c2.get('/useradmin/groups/new')
  214. assert_true("You must be a superuser" in response.content)
  215. response = c2.get('/useradmin/groups/edit/testgroup')
  216. assert_true("You must be a superuser" in response.content)
  217. response = c2.post('/useradmin/groups/new', dict(name="nonsuperuser"))
  218. assert_true("You must be a superuser" in response.content)
  219. response = c2.post('/useradmin/groups/edit/testgroup',
  220. dict(name="nonsuperuser",
  221. members=[User.objects.get(username="test").pk],
  222. save="Save"), follow=True)
  223. assert_true("You must be a superuser" in response.content)
  224. # Should be one group left, because we created the other group
  225. response = c.post('/useradmin/groups/delete/testgroup')
  226. assert_true(len(Group.objects.all()) == 1)
  227. group_count = len(Group.objects.all())
  228. response = c.post('/useradmin/groups/new', dict(name="with space"))
  229. assert_equal(len(Group.objects.all()), group_count + 1)
  230. def test_user_admin():
  231. FUNNY_NAME = '~`!@#$%^&*()_-+={}[]|\;"<>?/,.'
  232. FUNNY_NAME_QUOTED = urllib.quote(FUNNY_NAME)
  233. reset_all_users()
  234. reset_all_groups()
  235. useradmin.conf.DEFAULT_USER_GROUP.set_for_testing('test_default')
  236. c = make_logged_in_client('test', is_superuser=True)
  237. user = User.objects.get(username='test')
  238. # Test basic output.
  239. response = c.get('/useradmin/')
  240. assert_true(len(response.context["users"]) > 0)
  241. assert_true("Hue Users" in response.content)
  242. # Test editing a superuser
  243. # Just check that this comes back
  244. response = c.get('/useradmin/users/edit/test')
  245. # Edit it, to add a first and last name
  246. response = c.post('/useradmin/users/edit/test',
  247. dict(username="test",
  248. first_name=u"Inglés",
  249. last_name=u"Español",
  250. is_superuser="True",
  251. is_active="True"),
  252. follow=True)
  253. assert_true("User information updated" in response.content,
  254. "Notification should be displayed in: %s" % response.content)
  255. # Edit it, can't change username
  256. response = c.post('/useradmin/users/edit/test',
  257. dict(username="test2",
  258. first_name=u"Inglés",
  259. last_name=u"Español",
  260. is_superuser="True",
  261. is_active="True"),
  262. follow=True)
  263. assert_true("You cannot change a username" in response.content)
  264. # Now make sure that those were materialized
  265. response = c.get('/useradmin/users/edit/test')
  266. assert_equal(smart_unicode("Inglés"), response.context["form"].instance.first_name)
  267. assert_true("Español" in response.content)
  268. # Shouldn't be able to demote to non-superuser
  269. response = c.post('/useradmin/users/edit/test', dict(username="test",
  270. first_name=u"Inglés", last_name=u"Español",
  271. is_superuser=False, is_active=True))
  272. assert_true("You cannot remove" in response.content,
  273. "Shouldn't be able to remove the last superuser")
  274. # Shouldn't be able to delete oneself
  275. response = c.post('/useradmin/users/delete', {u'user_ids': [user.id]})
  276. assert_true("You cannot remove yourself" in response.content,
  277. "Shouldn't be able to delete the last superuser")
  278. # Let's try changing the password
  279. response = c.post('/useradmin/users/edit/test', dict(username="test", first_name="Tom", last_name="Tester", is_superuser=True, password1="foo", password2="foobar"))
  280. assert_equal(["Passwords do not match."], response.context["form"]["password2"].errors, "Should have complained about mismatched password")
  281. response = c.post('/useradmin/users/edit/test', dict(username="test", first_name="Tom", last_name="Tester", password1="foo", password2="foo", is_active=True, is_superuser=True))
  282. assert_true(User.objects.get(username="test").is_superuser)
  283. assert_true(User.objects.get(username="test").check_password("foo"))
  284. # Change it back!
  285. response = c.post('/useradmin/users/edit/test', dict(username="test", first_name="Tom", last_name="Tester", password1="test", password2="test", is_active="True", is_superuser="True"))
  286. assert_true(User.objects.get(username="test").check_password("test"))
  287. assert_true(make_logged_in_client(username = "test", password = "test"),
  288. "Check that we can still login.")
  289. # Check new user form for default group
  290. group = get_default_user_group()
  291. response = c.get('/useradmin/users/new')
  292. assert_true(response)
  293. assert_true(('<option value="1" selected="selected">%s</option>' % group) in str(response))
  294. # Create a new regular user (duplicate name)
  295. response = c.post('/useradmin/users/new', dict(username="test", password1="test", password2="test"))
  296. assert_equal({ 'username': ["User with this Username already exists."]}, response.context["form"].errors)
  297. # Create a new regular user (for real)
  298. response = c.post('/useradmin/users/new', dict(username=FUNNY_NAME,
  299. password1="test",
  300. password2="test",
  301. is_active="True"))
  302. response = c.get('/useradmin/')
  303. assert_true(FUNNY_NAME_QUOTED in response.content)
  304. assert_true(len(response.context["users"]) > 1)
  305. assert_true("Hue Users" in response.content)
  306. # Validate profile is created.
  307. assert_true(UserProfile.objects.filter(user__username=FUNNY_NAME).exists())
  308. # Need to give access to the user for the rest of the test
  309. group = Group.objects.create(name="test-group")
  310. perm = HuePermission.objects.get(app='useradmin', action='access')
  311. GroupPermission.objects.create(group=group, hue_permission=perm)
  312. # Verify that we can modify user groups through the user admin pages
  313. response = c.post('/useradmin/users/new', dict(username="group_member", password1="test", password2="test", groups=[group.pk]))
  314. User.objects.get(username='group_member')
  315. assert_true(User.objects.get(username='group_member').groups.filter(name='test-group').exists())
  316. response = c.post('/useradmin/users/edit/group_member', dict(username="group_member", password1="test", password2="test", groups=[]))
  317. assert_false(User.objects.get(username='group_member').groups.filter(name='test-group').exists())
  318. # Check permissions by logging in as the new user
  319. c_reg = make_logged_in_client(username=FUNNY_NAME, password="test")
  320. test_user = User.objects.get(username=FUNNY_NAME)
  321. test_user.groups.add(Group.objects.get(name="test-group"))
  322. test_user.save()
  323. # Regular user should be able to modify oneself
  324. response = c_reg.post('/useradmin/users/edit/%s' % (FUNNY_NAME_QUOTED,),
  325. dict(username = FUNNY_NAME,
  326. first_name = "Hello",
  327. is_active = True))
  328. response = c_reg.get('/useradmin/users/edit/%s' % (FUNNY_NAME_QUOTED,))
  329. assert_equal("Hello", response.context["form"].instance.first_name)
  330. funny_user = User.objects.get(username=FUNNY_NAME)
  331. # Can't edit other people.
  332. response = c_reg.post("/useradmin/users/delete", {u'user_ids': [funny_user.id]})
  333. assert_true("You must be a superuser" in response.content,
  334. "Regular user can't edit other people")
  335. # Revert to regular "test" user, that has superuser powers.
  336. c_su = make_logged_in_client()
  337. # Inactivate FUNNY_NAME
  338. c_su.post('/useradmin/users/edit/%s' % (FUNNY_NAME_QUOTED,),
  339. dict(username = FUNNY_NAME,
  340. first_name = "Hello",
  341. is_active = False))
  342. # Now make sure FUNNY_NAME can't log back in
  343. response = c_reg.get('/useradmin/users/edit/%s' % (FUNNY_NAME_QUOTED,))
  344. assert_true(response.status_code == 302 and "login" in response["location"],
  345. "Inactivated user gets redirected to login page")
  346. # Delete that regular user
  347. funny_profile = get_profile(test_user)
  348. response = c_su.post('/useradmin/users/delete', {u'user_ids': [funny_user.id]})
  349. assert_equal(302, response.status_code)
  350. assert_false(User.objects.filter(username=FUNNY_NAME).exists())
  351. assert_false(UserProfile.objects.filter(id=funny_profile.id).exists())
  352. # Bulk delete users
  353. u1 = User.objects.create(username='u1', password="u1")
  354. u2 = User.objects.create(username='u2', password="u2")
  355. assert_equal(User.objects.filter(username__in=['u1', 'u2']).count(), 2)
  356. response = c_su.post('/useradmin/users/delete', {u'user_ids': [u1.id, u2.id]})
  357. assert_equal(User.objects.filter(username__in=['u1', 'u2']).count(), 0)
  358. # Make sure that user deletion works if the user has never performed a request.
  359. funny_user = User.objects.create(username=FUNNY_NAME, password='test')
  360. assert_true(User.objects.filter(username=FUNNY_NAME).exists())
  361. assert_false(UserProfile.objects.filter(user__username=FUNNY_NAME).exists())
  362. response = c_su.post('/useradmin/users/delete', {u'user_ids': [funny_user.id]})
  363. assert_equal(302, response.status_code)
  364. assert_false(User.objects.filter(username=FUNNY_NAME).exists())
  365. assert_false(UserProfile.objects.filter(user__username=FUNNY_NAME).exists())
  366. # You shouldn't be able to create a user without a password
  367. response = c_su.post('/useradmin/users/new', dict(username="test"))
  368. assert_true("You must specify a password when creating a new user." in response.content)
  369. def test_useradmin_ldap_user_group_membership_sync():
  370. reset = [desktop.conf.AUTH.USER_GROUP_MEMBERSHIP_SYNCHRONIZATION_BACKEND.set_for_testing('desktop.auth.backend.LdapSynchronizationBackend')]
  371. settings.MIDDLEWARE_CLASSES.append('desktop.middleware.UserGroupSynchronizationMiddleware')
  372. reset_all_users()
  373. reset_all_groups()
  374. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  375. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  376. try:
  377. # Import curly who is part of TestUsers and Test Administrators
  378. import_ldap_users('curly', sync_groups=False, import_by_dn=False)
  379. # Set a password so that we can login
  380. user = User.objects.get(username='curly')
  381. user.set_password('test')
  382. user.save()
  383. # Should have 0 groups
  384. assert_equal(0, user.groups.all().count())
  385. # Make an authenticated request as curly so that we can see call middleware.
  386. c = make_logged_in_client('curly', 'test', is_superuser=False)
  387. grant_access("curly", "test", "useradmin")
  388. response = c.get('/useradmin/users')
  389. # Refresh user groups
  390. user = User.objects.get(username='curly')
  391. # Should have 3 groups now. 2 from LDAP and 1 from 'grant_access' call.
  392. assert_equal(3, user.groups.all().count(), user.groups.all())
  393. # Now remove a group and try again.
  394. old_group = ldap_access.CACHED_LDAP_CONN._instance.users['curly']['groups'].pop()
  395. # Make an authenticated request as curly so that we can see call middleware.
  396. response = c.get('/useradmin/users')
  397. # Refresh user groups
  398. user = User.objects.get(username='curly')
  399. # Should have 2 groups now. 1 from LDAP and 1 from 'grant_access' call.
  400. assert_equal(3, user.groups.all().count(), user.groups.all())
  401. finally:
  402. for finish in reset:
  403. finish()
  404. settings.MIDDLEWARE_CLASSES.remove('desktop.middleware.UserGroupSynchronizationMiddleware')
  405. def test_useradmin_ldap_group_integration():
  406. reset_all_users()
  407. reset_all_groups()
  408. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  409. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  410. # Import groups only
  411. import_ldap_groups('TestUsers', import_members=False, import_members_recursive=False, sync_users=False, import_by_dn=False)
  412. test_users = Group.objects.get(name='TestUsers')
  413. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  414. assert_equal(test_users.user_set.all().count(), 0)
  415. # Import all members of TestUsers
  416. import_ldap_groups('TestUsers', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  417. test_users = Group.objects.get(name='TestUsers')
  418. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  419. assert_equal(test_users.user_set.all().count(), 3)
  420. # Should import a group, but will only sync already-imported members
  421. import_ldap_groups('Test Administrators', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  422. assert_equal(User.objects.all().count(), 3)
  423. assert_equal(Group.objects.all().count(), 2)
  424. test_admins = Group.objects.get(name='Test Administrators')
  425. assert_equal(test_admins.user_set.all().count(), 2)
  426. larry = User.objects.get(username='lårry')
  427. assert_equal(test_admins.user_set.all()[0].username, larry.username)
  428. # Only sync already imported
  429. ldap_access.CACHED_LDAP_CONN.remove_user_group_for_test('uid=moe,ou=People,dc=example,dc=com', 'TestUsers')
  430. import_ldap_groups('TestUsers', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  431. assert_equal(test_users.user_set.all().count(), 2)
  432. assert_equal(User.objects.get(username='moe').groups.all().count(), 0)
  433. # Import missing user
  434. ldap_access.CACHED_LDAP_CONN.add_user_group_for_test('uid=moe,ou=People,dc=example,dc=com', 'TestUsers')
  435. import_ldap_groups('TestUsers', import_members=True, import_members_recursive=False, sync_users=True, import_by_dn=False)
  436. assert_equal(test_users.user_set.all().count(), 3)
  437. assert_equal(User.objects.get(username='moe').groups.all().count(), 1)
  438. # Import all members of TestUsers and members of subgroups
  439. import_ldap_groups('TestUsers', import_members=True, import_members_recursive=True, sync_users=True, import_by_dn=False)
  440. test_users = Group.objects.get(name='TestUsers')
  441. assert_true(LdapGroup.objects.filter(group=test_users).exists())
  442. assert_equal(test_users.user_set.all().count(), 4)
  443. # Make sure Hue groups with naming collisions don't get marked as LDAP groups
  444. hue_user = User.objects.create(username='otherguy', first_name='Different', last_name='Guy')
  445. hue_group = Group.objects.create(name='OtherGroup')
  446. hue_group.user_set.add(hue_user)
  447. hue_group.save()
  448. import_ldap_groups('OtherGroup', import_members=False, import_members_recursive=False, sync_users=True, import_by_dn=False)
  449. assert_false(LdapGroup.objects.filter(group=hue_group).exists())
  450. assert_true(hue_group.user_set.filter(username=hue_user.username).exists())
  451. def test_useradmin_ldap_user_integration():
  452. reset_all_users()
  453. reset_all_groups()
  454. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  455. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  456. # Try importing a user
  457. import_ldap_users('lårry', sync_groups=False, import_by_dn=False)
  458. larry = User.objects.get(username='lårry')
  459. assert_true(larry.first_name == 'Larry')
  460. assert_true(larry.last_name == 'Stooge')
  461. assert_true(larry.email == 'larry@stooges.com')
  462. assert_true(get_profile(larry).creation_method == str(UserProfile.CreationMethod.EXTERNAL))
  463. # Should be a noop
  464. sync_ldap_users()
  465. sync_ldap_groups()
  466. assert_equal(User.objects.all().count(), 1)
  467. assert_equal(Group.objects.all().count(), 0)
  468. # Make sure that if a Hue user already exists with a naming collision, we
  469. # won't overwrite any of that user's information.
  470. hue_user = User.objects.create(username='otherguy', first_name='Different', last_name='Guy')
  471. import_ldap_users('otherguy', sync_groups=False, import_by_dn=False)
  472. hue_user = User.objects.get(username='otherguy')
  473. assert_equal(get_profile(hue_user).creation_method, str(UserProfile.CreationMethod.HUE))
  474. assert_equal(hue_user.first_name, 'Different')
  475. # Try importing a user and sync groups
  476. import_ldap_users('curly', sync_groups=True, import_by_dn=False)
  477. curly = User.objects.get(username='curly')
  478. assert_equal(curly.first_name, 'Curly')
  479. assert_equal(curly.last_name, 'Stooge')
  480. assert_equal(curly.email, 'curly@stooges.com')
  481. assert_equal(get_profile(curly).creation_method, str(UserProfile.CreationMethod.EXTERNAL))
  482. assert_equal(2, curly.groups.all().count(), curly.groups.all())
  483. def test_add_ldap_users():
  484. URL = reverse(add_ldap_users)
  485. reset_all_users()
  486. reset_all_groups()
  487. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  488. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  489. c = make_logged_in_client('test', is_superuser=True)
  490. assert_true(c.get(URL))
  491. response = c.post(URL, dict(username_pattern='moe', password1='test', password2='test'))
  492. assert_true('Location' in response, response)
  493. assert_true('/useradmin/users' in response['Location'], response)
  494. response = c.post(URL, dict(username_pattern='bad_name', password1='test', password2='test'))
  495. assert_true('Could not' in response.context['form'].errors['username_pattern'][0], response)
  496. # Test wild card
  497. response = c.post(URL, dict(username_pattern='*r*', password1='test', password2='test'))
  498. assert_true('/useradmin/users' in response['Location'], response)
  499. def test_add_ldap_groups():
  500. URL = reverse(add_ldap_groups)
  501. reset_all_users()
  502. reset_all_groups()
  503. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  504. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  505. c = make_logged_in_client(username='test', is_superuser=True)
  506. assert_true(c.get(URL))
  507. response = c.post(URL, dict(groupname_pattern='TestUsers'))
  508. assert_true('Location' in response, response)
  509. assert_true('/useradmin/groups' in response['Location'])
  510. # Test with space
  511. response = c.post(URL, dict(groupname_pattern='Test Administrators'))
  512. assert_true('Location' in response, response)
  513. assert_true('/useradmin/groups' in response['Location'], response)
  514. response = c.post(URL, dict(groupname_pattern='toolongnametoolongnametoolongnametoolongnametoolongnametoolongnametoolongnametoolongname'))
  515. assert_true('Ensure this value has at most 80 characters' in response.context['form'].errors['groupname_pattern'][0], response)
  516. # Test wild card
  517. response = c.post(URL, dict(groupname_pattern='*r*'))
  518. assert_true('/useradmin/groups' in response['Location'], response)
  519. def test_sync_ldap_users_groups():
  520. URL = reverse(sync_ldap_users_groups)
  521. reset_all_users()
  522. reset_all_groups()
  523. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  524. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  525. c = make_logged_in_client('test', is_superuser=True)
  526. assert_true(c.get(URL))
  527. assert_true(c.post(URL))
  528. def test_ldap_exception_handling():
  529. reset_all_users()
  530. reset_all_groups()
  531. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  532. class LdapTestConnectionError(LdapTestConnection):
  533. def find_users(self, user, find_by_dn=False):
  534. raise ldap.LDAPError('No such object')
  535. ldap_access.CACHED_LDAP_CONN = LdapTestConnectionError()
  536. c = make_logged_in_client('test', is_superuser=True)
  537. response = c.post(reverse(add_ldap_users), dict(username_pattern='moe', password1='test', password2='test'), follow=True)
  538. assert_true('There was an error when communicating with LDAP' in response.content, response)
  539. @attr('requires_hadoop')
  540. def test_ensure_home_directory_add_ldap_users():
  541. URL = reverse(add_ldap_users)
  542. reset_all_users()
  543. reset_all_groups()
  544. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  545. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  546. cluster = pseudo_hdfs4.shared_cluster()
  547. c = make_logged_in_client(cluster.superuser, is_superuser=True)
  548. cluster.fs.setuser(cluster.superuser)
  549. assert_true(c.get(URL))
  550. response = c.post(URL, dict(username_pattern='moe', password1='test', password2='test'))
  551. assert_true('/useradmin/users' in response['Location'])
  552. assert_false(cluster.fs.exists('/user/moe'))
  553. # Try same thing with home directory creation.
  554. response = c.post(URL, dict(username_pattern='curly', password1='test', password2='test', ensure_home_directory=True))
  555. assert_true('/useradmin/users' in response['Location'])
  556. assert_true(cluster.fs.exists('/user/curly'))
  557. response = c.post(URL, dict(username_pattern='bad_name', password1='test', password2='test'))
  558. assert_true('Could not' in response.context['form'].errors['username_pattern'][0])
  559. assert_false(cluster.fs.exists('/user/bad_name'))
  560. # See if moe, who did not ask for his home directory, has a home directory.
  561. assert_false(cluster.fs.exists('/user/moe'))
  562. # Try wild card now
  563. response = c.post(URL, dict(username_pattern='*r*', password1='test', password2='test', ensure_home_directory=True))
  564. assert_true('/useradmin/users' in response['Location'])
  565. assert_true(cluster.fs.exists('/user/curly'))
  566. assert_true(cluster.fs.exists(u'/user/lårry'))
  567. assert_true(cluster.fs.exists('/user/otherguy'))
  568. # Clean up
  569. cluster.fs.rmtree('/user/curly')
  570. cluster.fs.rmtree(u'/user/lårry')
  571. cluster.fs.rmtree('/user/otherguy')
  572. @attr('requires_hadoop')
  573. def test_ensure_home_directory_sync_ldap_users_groups():
  574. URL = reverse(sync_ldap_users_groups)
  575. reset_all_users()
  576. reset_all_groups()
  577. # Set up LDAP tests to use a LdapTestConnection instead of an actual LDAP connection
  578. ldap_access.CACHED_LDAP_CONN = LdapTestConnection()
  579. cluster = pseudo_hdfs4.shared_cluster()
  580. c = make_logged_in_client(cluster.superuser, is_superuser=True)
  581. cluster.fs.setuser(cluster.superuser)
  582. response = c.post(reverse(add_ldap_users), dict(username_pattern='curly', password1='test', password2='test'))
  583. assert_false(cluster.fs.exists('/user/curly'))
  584. assert_true(c.post(URL, dict(ensure_home_directory=True)))
  585. assert_true(cluster.fs.exists('/user/curly'))
  586. @attr('requires_hadoop')
  587. def test_ensure_home_directory():
  588. reset_all_users()
  589. reset_all_groups()
  590. # Cluster and client for home directory creation
  591. cluster = pseudo_hdfs4.shared_cluster()
  592. c = make_logged_in_client(cluster.superuser, is_superuser=True, groupname='test1')
  593. cluster.fs.setuser(cluster.superuser)
  594. # Create a user with a home directory
  595. assert_false(cluster.fs.exists('/user/test1'))
  596. response = c.post('/useradmin/users/new', dict(username="test1", password1='test', password2='test', ensure_home_directory=True))
  597. assert_true(cluster.fs.exists('/user/test1'))
  598. dir_stat = cluster.fs.stats('/user/test1')
  599. assert_equal('test1', dir_stat.user)
  600. assert_equal('test1', dir_stat.group)
  601. assert_equal('40755', '%o' % dir_stat.mode)
  602. # Create a user, then add their home directory
  603. assert_false(cluster.fs.exists('/user/test2'))
  604. response = c.post('/useradmin/users/new', dict(username="test2", password1='test', password2='test'))
  605. assert_false(cluster.fs.exists('/user/test2'))
  606. response = c.post('/useradmin/users/edit/%s' % "test2", dict(username="test2", password1='test', password2='test', ensure_home_directory=True))
  607. assert_true(cluster.fs.exists('/user/test2'))
  608. dir_stat = cluster.fs.stats('/user/test2')
  609. assert_equal('test2', dir_stat.user)
  610. assert_equal('test2', dir_stat.group)
  611. assert_equal('40755', '%o' % dir_stat.mode)