PageRenderTime 27ms CodeModel.GetById 40ms RepoModel.GetById 0ms app.codeStats 0ms

/paasmaker/pacemaker/controller/role.py

https://bitbucket.org/paasmaker/paasmaker
Python | 549 lines | 491 code | 43 blank | 15 comment | 11 complexity | d602e6ca223aea65c1a3fc6b39f97d24 MD5 | raw file
  1. #
  2. # Paasmaker - Platform as a Service
  3. #
  4. # This Source Code Form is subject to the terms of the Mozilla Public
  5. # License, v. 2.0. If a copy of the MPL was not distributed with this
  6. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  7. #
  8. import unittest
  9. import paasmaker
  10. import uuid
  11. import logging
  12. import colander
  13. import json
  14. from paasmaker.common.controller import BaseController, BaseControllerTest
  15. from paasmaker.common.core import constants
  16. import tornado
  17. import tornado.testing
  18. import sqlalchemy
  19. logger = logging.getLogger(__name__)
  20. logger.addHandler(logging.NullHandler())
  21. class RoleSchema(colander.MappingSchema):
  22. name = colander.SchemaNode(colander.String(),
  23. title="Role name",
  24. description="A nice name for this role.",
  25. validator=colander.Length(min=2))
  26. permissions = colander.SchemaNode(colander.Sequence(),
  27. colander.SchemaNode(colander.String()),
  28. title="Permissions",
  29. default=[],
  30. missing=[])
  31. class RoleAllocationAssignSchema(colander.MappingSchema):
  32. role_id = colander.SchemaNode(colander.Integer(),
  33. title="Role ID",
  34. description="The role ID.")
  35. user_id = colander.SchemaNode(colander.Integer(),
  36. title="User ID",
  37. description="The user ID.")
  38. workspace_id = colander.SchemaNode(colander.Integer(),
  39. title="Optional Workspace ID",
  40. description="The workspace ID.",
  41. default=None,
  42. missing=None)
  43. class RoleAllocationUnAssignSchema(colander.MappingSchema):
  44. allocation_id = colander.SchemaNode(colander.Integer(),
  45. title="Allocation ID",
  46. description="The allocation ID.")
  47. class RoleListController(BaseController):
  48. AUTH_METHODS = [BaseController.SUPER, BaseController.USER]
  49. def get(self):
  50. self.require_permission(constants.PERMISSION.ROLE_LIST)
  51. roles = self.session.query(
  52. paasmaker.model.Role
  53. )
  54. self._paginate('roles', roles)
  55. # self.add_data('roles', roles)
  56. self.client_side_render()
  57. @staticmethod
  58. def get_routes(configuration):
  59. routes = []
  60. routes.append((r"/role/list", RoleListController, configuration))
  61. return routes
  62. class RoleAllocationListController(BaseController):
  63. AUTH_METHODS = [BaseController.SUPER, BaseController.USER]
  64. def get(self):
  65. self.require_permission(constants.PERMISSION.ROLE_ASSIGN)
  66. allocations = self.session.query(
  67. paasmaker.model.WorkspaceUserRole
  68. ).filter(
  69. paasmaker.model.WorkspaceUserRole.deleted == None
  70. )
  71. self._paginate('allocations', allocations)
  72. self.client_side_render()
  73. @staticmethod
  74. def get_routes(configuration):
  75. routes = []
  76. routes.append((r"/role/allocation/list", RoleAllocationListController, configuration))
  77. return routes
  78. class RoleEditController(BaseController):
  79. AUTH_METHODS = [BaseController.SUPER, BaseController.USER]
  80. def _get_role(self, role_id):
  81. role = None
  82. if role_id:
  83. # Find and load the role.
  84. role = self.session.query(
  85. paasmaker.model.Role
  86. ).get(int(role_id))
  87. if not role:
  88. raise HTTPError(404, "No such role.")
  89. return role
  90. def _default_role(self):
  91. role = paasmaker.model.Role()
  92. role.name = ''
  93. return role
  94. def get(self, role_id=None):
  95. self.require_permission(constants.PERMISSION.ROLE_EDIT)
  96. role = self._get_role(role_id)
  97. if not role:
  98. role = self._default_role()
  99. self.add_data('role', role)
  100. available_permissions = constants.PERMISSION.ALL
  101. available_permissions.sort()
  102. self.add_data('available_permissions', available_permissions)
  103. self.client_side_render()
  104. def post(self, role_id=None):
  105. self.require_permission(constants.PERMISSION.ROLE_EDIT)
  106. role = self._get_role(role_id)
  107. valid_data = self.validate_data(RoleSchema())
  108. if not role:
  109. role = self._default_role()
  110. role.name = self.params['name']
  111. # And a special handler - if supplied with "ALL", replace with all
  112. # possible permissions.
  113. if 'ALL' in self.params['permissions']:
  114. role.permissions = constants.PERMISSION.ALL
  115. else:
  116. role.permissions = self.params['permissions']
  117. if valid_data:
  118. self.session.add(role)
  119. try:
  120. paasmaker.model.WorkspaceUserRoleFlat.build_flat_table(self.session)
  121. self.session.refresh(role)
  122. except sqlalchemy.exc.IntegrityError, ex:
  123. self.session.rollback()
  124. self.reload_current_user()
  125. if 'name is not' in str(ex):
  126. valid_data = False
  127. self.add_error('The role name is not unique.')
  128. else:
  129. raise ex
  130. if valid_data:
  131. self.add_data('role', role)
  132. self.redirect('/role/list')
  133. else:
  134. self.add_data('role', role)
  135. self.add_data_template('available_permissions', constants.PERMISSION.ALL)
  136. self.client_side_render()
  137. @staticmethod
  138. def get_routes(configuration):
  139. routes = []
  140. routes.append((r"/role/create", RoleEditController, configuration))
  141. routes.append((r"/role/(\d+)", RoleEditController, configuration))
  142. return routes
  143. class RoleAllocationAssignController(BaseController):
  144. AUTH_METHODS = [BaseController.SUPER, BaseController.USER]
  145. def get(self):
  146. self.require_permission(constants.PERMISSION.ROLE_ASSIGN)
  147. # List available users, workspaces, and roles.
  148. # TODO: This won't be efficient at large sets.
  149. if self.format == 'html':
  150. # We don't expose this here to the API - this is
  151. # purely for the template to use.
  152. users = self.session.query(
  153. paasmaker.model.User
  154. ).all()
  155. roles = self.session.query(
  156. paasmaker.model.Role
  157. ).all()
  158. workspaces = self.session.query(
  159. paasmaker.model.Workspace
  160. ).all()
  161. self.add_data_template('users', users)
  162. self.add_data_template('roles', roles)
  163. self.add_data_template('workspaces', workspaces)
  164. self.client_side_render()
  165. def post(self):
  166. self.require_permission(constants.PERMISSION.ROLE_ASSIGN)
  167. valid_data = self.validate_data(RoleAllocationAssignSchema())
  168. # Fetch the role, user, and workspace.
  169. role = self.session.query(
  170. paasmaker.model.Role
  171. ).get(int(self.params['role_id']))
  172. user = self.session.query(
  173. paasmaker.model.User
  174. ).get(int(self.params['user_id']))
  175. workspace_id = self.params['workspace_id']
  176. workspace = None
  177. if workspace_id:
  178. workspace = self.session.query(
  179. paasmaker.model.Workspace
  180. ).get(int(workspace_id))
  181. if not role:
  182. self.add_error("No such role.")
  183. valid_data = False
  184. if not user:
  185. self.add_error("No such user.")
  186. valid_data = False
  187. if workspace_id and not workspace:
  188. self.add_error("No such workspace.")
  189. valid_data = False
  190. if valid_data:
  191. allocation = paasmaker.model.WorkspaceUserRole()
  192. allocation.user = user
  193. allocation.role = role
  194. allocation.workspace = workspace
  195. self.session.add(allocation)
  196. paasmaker.model.WorkspaceUserRoleFlat.build_flat_table(self.session)
  197. self.session.refresh(allocation)
  198. self.add_data('allocation', allocation)
  199. self.redirect('/role/allocation/list')
  200. else:
  201. self.client_side_render()
  202. @staticmethod
  203. def get_routes(configuration):
  204. routes = []
  205. routes.append((r"/role/allocation/assign", RoleAllocationAssignController, configuration))
  206. return routes
  207. class RoleAllocationUnAssignController(BaseController):
  208. AUTH_METHODS = [BaseController.SUPER, BaseController.USER]
  209. def post(self):
  210. self.require_permission(constants.PERMISSION.ROLE_ASSIGN)
  211. valid_data = self.validate_data(RoleAllocationUnAssignSchema())
  212. # Fetch this allocation.
  213. allocation = self.session.query(
  214. paasmaker.model.WorkspaceUserRole
  215. ).get(int(self.params['allocation_id']))
  216. if not allocation:
  217. raise tornado.HTTPError(404, "No such allocation.")
  218. allocation.delete()
  219. self.session.add(allocation)
  220. paasmaker.model.WorkspaceUserRoleFlat.build_flat_table(self.session)
  221. self.add_data('success', True)
  222. self.redirect('/role/allocation/list')
  223. @staticmethod
  224. def get_routes(configuration):
  225. routes = []
  226. routes.append((r"/role/allocation/unassign", RoleAllocationUnAssignController, configuration))
  227. return routes
  228. class RoleEditControllerTest(BaseControllerTest):
  229. config_modules = ['pacemaker']
  230. def get_app(self):
  231. self.late_init_configuration(self.io_loop)
  232. routes = RoleEditController.get_routes({'configuration': self.configuration})
  233. routes.extend(RoleListController.get_routes({'configuration': self.configuration}))
  234. routes.extend(RoleAllocationListController.get_routes({'configuration': self.configuration}))
  235. routes.extend(RoleAllocationAssignController.get_routes({'configuration': self.configuration}))
  236. routes.extend(RoleAllocationUnAssignController.get_routes({'configuration': self.configuration}))
  237. application = tornado.web.Application(routes, **self.configuration.get_tornado_configuration())
  238. return application
  239. def test_create(self):
  240. # Create the role.
  241. request = paasmaker.common.api.role.RoleCreateAPIRequest(self.configuration)
  242. request.set_superkey_auth()
  243. request.set_role_params('Test Role', [constants.PERMISSION.USER_EDIT])
  244. request.send(self.stop)
  245. response = self.wait()
  246. self.failIf(not response.success)
  247. self.assertEquals(len(response.errors), 0, "There were errors.")
  248. self.assertEquals(len(response.warnings), 0, "There were warnings.")
  249. self.assertTrue(response.data.has_key('role'), "Missing role object in return data.")
  250. self.assertTrue(response.data['role'].has_key('id'), "Missing ID in return data.")
  251. self.assertTrue(response.data['role'].has_key('permissions'), "Missing permissions in return data.")
  252. self.assertIn(constants.PERMISSION.USER_EDIT, response.data['role']['permissions'])
  253. def test_create_fail(self):
  254. # Send through some bogus data.
  255. request = paasmaker.common.api.role.RoleCreateAPIRequest(self.configuration)
  256. request.set_superkey_auth()
  257. request.set_role_params('', [])
  258. request.send(self.stop)
  259. response = self.wait()
  260. self.failIf(response.success)
  261. self.assertTrue("Invalid" in response.errors[0], "Missing message in error.")
  262. def test_edit(self):
  263. # Create the role.
  264. request = paasmaker.common.api.role.RoleCreateAPIRequest(self.configuration)
  265. request.set_superkey_auth()
  266. request.set_role_params('Test Role', [constants.PERMISSION.USER_EDIT])
  267. request.send(self.stop)
  268. response = self.wait()
  269. self.failIf(not response.success)
  270. role_id = response.data['role']['id']
  271. # Set up the request.
  272. request = paasmaker.common.api.role.RoleEditAPIRequest(self.configuration)
  273. request.set_superkey_auth()
  274. # This loads the role data from the server.
  275. request.load(role_id, self.stop, self.stop)
  276. load_response = self.wait()
  277. # Now attempt to change the role.
  278. request.set_role_permissions(load_response['permissions'] + [constants.PERMISSION.WORKSPACE_EDIT])
  279. # Send it along!
  280. request.send(self.stop)
  281. response = self.wait()
  282. self.failIf(not response.success)
  283. self.assertEquals(len(response.data['role']['permissions']), 2, 'Not enough permissions.')
  284. self.assertIn(constants.PERMISSION.USER_EDIT, response.data['role']['permissions'])
  285. self.assertIn(constants.PERMISSION.WORKSPACE_EDIT, response.data['role']['permissions'])
  286. # Load up the role separately and confirm.
  287. self.configuration.get_database_session(self.stop, None)
  288. session = self.wait()
  289. role = session.query(
  290. paasmaker.model.Role
  291. ).get(role_id)
  292. self.assertIn(constants.PERMISSION.USER_EDIT, role.permissions)
  293. self.assertIn(constants.PERMISSION.WORKSPACE_EDIT, role.permissions)
  294. def test_edit_fail(self):
  295. # Create the role.
  296. request = paasmaker.common.api.role.RoleCreateAPIRequest(self.configuration)
  297. request.set_superkey_auth()
  298. request.set_role_params('Test Role', [constants.PERMISSION.USER_EDIT])
  299. request.send(self.stop)
  300. response = self.wait()
  301. self.failIf(not response.success)
  302. role_id = response.data['role']['id']
  303. # Set up the request.
  304. request = paasmaker.common.api.role.RoleEditAPIRequest(self.configuration)
  305. request.set_superkey_auth()
  306. # This loads the role data from the server.
  307. request.load(role_id, self.stop, self.stop)
  308. load_response = self.wait()
  309. # Now attempt to change the role.
  310. request.set_role_name('')
  311. # Send it along!
  312. request.send(self.stop)
  313. response = self.wait()
  314. self.failIf(response.success)
  315. self.assertTrue("Invalid" in response.errors[0], "Missing message in error.")
  316. def test_list(self):
  317. # Create the role.
  318. request = paasmaker.common.api.role.RoleCreateAPIRequest(self.configuration)
  319. request.set_superkey_auth()
  320. request.set_role_params('Test Role', [constants.PERMISSION.USER_EDIT])
  321. request.send(self.stop)
  322. response = self.wait()
  323. self.failIf(not response.success)
  324. request = paasmaker.common.api.role.RoleListAPIRequest(self.configuration)
  325. request.set_superkey_auth()
  326. request.send(self.stop)
  327. response = self.wait()
  328. self.failIf(not response.success)
  329. self.assertTrue(response.data.has_key('roles'), "Missing roles list.")
  330. self.assertEquals(len(response.data['roles']), 1, "Not enough roles returned.")
  331. self.assertEquals(response.data['roles'][0]['name'], 'Test Role', "Returned role is not as expected.")
  332. def test_list_allocation(self):
  333. self.configuration.get_database_session(self.stop, None)
  334. session = self.wait()
  335. user = paasmaker.model.User()
  336. user.login = 'username'
  337. user.email = 'username@example.com'
  338. user.name = 'User Name'
  339. user.password = 'test'
  340. workspace = paasmaker.model.Workspace()
  341. workspace.name = 'Test Zone'
  342. workspace.stub = 'test'
  343. role = paasmaker.model.Role()
  344. role.name = "Test"
  345. role.permissions = []
  346. session.add(user)
  347. session.add(workspace)
  348. session.add(role)
  349. session.commit()
  350. allocation = paasmaker.model.WorkspaceUserRole()
  351. allocation.user = user
  352. allocation.role = role
  353. session.add(allocation)
  354. other_allocation = paasmaker.model.WorkspaceUserRole()
  355. other_allocation.user = user
  356. other_allocation.role = role
  357. other_allocation.workspace = workspace
  358. session.add(other_allocation)
  359. session.commit()
  360. request = paasmaker.common.api.role.RoleAllocationListAPIRequest(self.configuration)
  361. request.set_superkey_auth()
  362. request.send(self.stop)
  363. response = self.wait()
  364. self.failIf(not response.success)
  365. self.assertTrue(response.data.has_key('allocations'), "Missing allocations list.")
  366. self.assertEquals(len(response.data['allocations']), 2, "Not enough allocations returned.")
  367. self.assertEquals(response.data['allocations'][0]['user']['login'], 'username', "Returned allocations is not as expected.")
  368. # TODO: Test that the workspace is blank in one of the responses.
  369. def test_allocation(self):
  370. self.configuration.get_database_session(self.stop, None)
  371. session = self.wait()
  372. user = paasmaker.model.User()
  373. user.login = 'username'
  374. user.email = 'username@example.com'
  375. user.name = 'User Name'
  376. user.password = 'test'
  377. workspace = paasmaker.model.Workspace()
  378. workspace.name = 'Test Zone'
  379. workspace.stub = 'test'
  380. role = paasmaker.model.Role()
  381. role.name = "Test"
  382. role.permissions = []
  383. session.add(user)
  384. session.add(workspace)
  385. session.add(role)
  386. session.commit()
  387. session.refresh(user)
  388. session.refresh(workspace)
  389. session.refresh(role)
  390. request = paasmaker.common.api.role.RoleAllocationAPIRequest(self.configuration)
  391. request.set_superkey_auth()
  392. request.set_allocation_params(user.id, role.id)
  393. request.send(self.stop)
  394. response = self.wait()
  395. self.failIf(not response.success)
  396. self.assertTrue(response.data.has_key('allocation'), "Missing allocation.")
  397. self.assertEquals(response.data['allocation']['user']['id'], user.id, "User ID not as expected.")
  398. self.assertEquals(response.data['allocation']['role']['id'], role.id, "Role ID not as expected.")
  399. self.assertEquals(response.data['allocation']['workspace'], None, "Workspace is not None.")
  400. first_allocation_id = response.data['allocation']['id']
  401. # Same again, but apply to a workspace.
  402. request = paasmaker.common.api.role.RoleAllocationAPIRequest(self.configuration)
  403. request.set_superkey_auth()
  404. request.set_allocation_params(user.id, role.id, workspace.id)
  405. request.send(self.stop)
  406. response = self.wait()
  407. self.failIf(not response.success)
  408. self.assertTrue(response.data.has_key('allocation'), "Missing allocation.")
  409. self.assertEquals(response.data['allocation']['user']['id'], user.id, "User ID not as expected.")
  410. self.assertEquals(response.data['allocation']['role']['id'], role.id, "Role ID not as expected.")
  411. self.assertEquals(response.data['allocation']['workspace']['id'], workspace.id, "Workspace is not None.")
  412. second_allocation_id = response.data['allocation']['id']
  413. # Remove the allocations.
  414. request = paasmaker.common.api.role.RoleUnAllocationAPIRequest(self.configuration)
  415. request.set_superkey_auth()
  416. request.set_allocation_id(first_allocation_id)
  417. request.send(self.stop)
  418. response = self.wait()
  419. self.failIf(not response.success)
  420. self.assertTrue(response.data.has_key('success'), "Missing success flag.")
  421. # List it.
  422. request = paasmaker.common.api.role.RoleAllocationListAPIRequest(self.configuration)
  423. request.set_superkey_auth()
  424. request.send(self.stop)
  425. response = self.wait()
  426. self.failIf(not response.success)
  427. self.assertTrue(response.data.has_key('allocations'), "Missing allocations list.")
  428. self.assertEquals(len(response.data['allocations']), 1, "Not enough allocations returned.")
  429. self.assertEquals(response.data['allocations'][0]['user']['login'], 'username', "Returned allocations is not as expected.")
  430. # Remove the other allocation.
  431. request = paasmaker.common.api.role.RoleUnAllocationAPIRequest(self.configuration)
  432. request.set_superkey_auth()
  433. request.set_allocation_id(second_allocation_id)
  434. request.send(self.stop)
  435. response = self.wait()
  436. self.failIf(not response.success)
  437. self.assertTrue(response.data.has_key('success'), "Missing success flag.")
  438. # List it. Should now be none.
  439. request = paasmaker.common.api.role.RoleAllocationListAPIRequest(self.configuration)
  440. request.set_superkey_auth()
  441. request.send(self.stop)
  442. response = self.wait()
  443. self.failIf(not response.success)
  444. self.assertTrue(response.data.has_key('allocations'), "Missing allocations list.")
  445. self.assertEquals(len(response.data['allocations']), 0, "Not the right number of allocations.")