PageRenderTime 25ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/paasmaker/pacemaker/controller/user.py

https://bitbucket.org/paasmaker/paasmaker
Python | 321 lines | 305 code | 9 blank | 7 comment | 9 complexity | 6f6ab3891643d3ce7d41616fac3c862e 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 uuid
  10. import logging
  11. import json
  12. import paasmaker
  13. from paasmaker.common.controller import BaseController, BaseControllerTest
  14. from paasmaker.common.core import constants
  15. import colander
  16. import tornado
  17. import tornado.testing
  18. import sqlalchemy
  19. logger = logging.getLogger(__name__)
  20. logger.addHandler(logging.NullHandler())
  21. class Boolean(object):
  22. def serialize(self, node, appstruct):
  23. if appstruct is colander.null:
  24. return colander.null
  25. if not isinstance(appstruct, bool):
  26. raise Invalid(node, '%r is not a boolean')
  27. return appstruct and 'true' or 'false'
  28. def deserialize(self, node, cstruct):
  29. if cstruct is colander.null:
  30. return colander.null
  31. if isinstance(cstruct, bool):
  32. return cstruct
  33. if not isinstance(cstruct, basestring):
  34. raise Invalid(node, '%r is not a string' % cstruct)
  35. value = cstruct.lower()
  36. if value in ('true', 'yes', 'y', 'on', 't', '1'):
  37. return True
  38. return False
  39. def cstruct_children(self):
  40. return []
  41. def unflatten(self, subnode, subpaths, subfstruct):
  42. return self.deserialize(subnode, subfstruct[subnode.name])
  43. class UserSchema(colander.MappingSchema):
  44. name = colander.SchemaNode(colander.String(),
  45. title="User name",
  46. description="A nice name for this user.",
  47. validator=colander.Length(min=2))
  48. login = colander.SchemaNode(colander.String(),
  49. title="Login",
  50. description="The handle that the user uses to login.",
  51. validator=colander.Length(min=2))
  52. email = colander.SchemaNode(colander.String(),
  53. title="Email address",
  54. description="The email address of this user.",
  55. validator=colander.Email())
  56. enabled = colander.SchemaNode(Boolean(),
  57. title="Enabled",
  58. description="If this user is enabled.",
  59. missing=False,
  60. default=False)
  61. password = colander.SchemaNode(colander.String(),
  62. title="Password",
  63. description="The users password (blank to leave unchanged)",
  64. default="",
  65. missing="",
  66. # TODO: more complex password requirements.
  67. validator=colander.Length(min=8))
  68. class UserEditController(BaseController):
  69. AUTH_METHODS = [BaseController.SUPER, BaseController.USER]
  70. def _get_user(self, user_id=None):
  71. user = None
  72. if user_id:
  73. # Find and load the user.
  74. user = self.session.query(
  75. paasmaker.model.User
  76. ).get(int(user_id))
  77. if not user:
  78. raise HTTPError(404, "No such user.")
  79. self.add_data('user', user)
  80. return user
  81. def _default_user(self):
  82. user = paasmaker.model.User()
  83. user.name = ''
  84. user.login = ''
  85. user.email = ''
  86. return user
  87. def get(self, user_id=None):
  88. self.require_permission(constants.PERMISSION.USER_EDIT)
  89. user = self._get_user(user_id)
  90. if not user:
  91. user = self._default_user()
  92. self.add_data('user', user)
  93. self.client_side_render()
  94. def post(self, user_id=None):
  95. self.require_permission(constants.PERMISSION.USER_EDIT)
  96. user = self._get_user(user_id)
  97. valid_data = self.validate_data(UserSchema())
  98. if not user:
  99. user = self._default_user()
  100. user.name = self.params['name']
  101. user.login = self.params['login']
  102. user.email = self.params['email']
  103. user.enabled = self.params['enabled']
  104. if valid_data and not user.id:
  105. # Password must be supplied.
  106. if self.params['password'] == '':
  107. self.add_error("No password supplied.")
  108. valid_data = False
  109. if valid_data:
  110. if self.params.has_key('password') and len(self.params['password']) > 0:
  111. user.password = self.params['password']
  112. self.session.add(user)
  113. try:
  114. self.session.commit()
  115. self.session.refresh(user)
  116. except sqlalchemy.exc.IntegrityError, ex:
  117. self.session.rollback()
  118. self.reload_current_user()
  119. if 'email is not' in str(ex):
  120. valid_data = False
  121. self.add_error('The email address is not unique.')
  122. elif 'login is not' in str(ex):
  123. valid_data = False
  124. self.add_error('The login name is not unique.')
  125. else:
  126. raise ex
  127. if valid_data:
  128. self.add_data('user', user)
  129. self.redirect('/user/list')
  130. else:
  131. self.add_data('user', user)
  132. self.render("user/edit.html")
  133. @staticmethod
  134. def get_routes(configuration):
  135. routes = []
  136. routes.append((r"/user/create", UserEditController, configuration))
  137. routes.append((r"/user/(\d+)", UserEditController, configuration))
  138. return routes
  139. class UserListController(BaseController):
  140. AUTH_METHODS = [BaseController.SUPER, BaseController.USER]
  141. def get(self):
  142. self.require_permission(constants.PERMISSION.USER_LIST)
  143. users = self.session.query(paasmaker.model.User)
  144. self._paginate('users', users)
  145. # self.add_data('users', users)
  146. self.client_side_render()
  147. @staticmethod
  148. def get_routes(configuration):
  149. routes = []
  150. routes.append((r"/user/list", UserListController, configuration))
  151. return routes
  152. class UserEditControllerTest(BaseControllerTest):
  153. config_modules = ['pacemaker']
  154. def get_app(self):
  155. self.late_init_configuration(self.io_loop)
  156. routes = UserEditController.get_routes({'configuration': self.configuration})
  157. routes.extend(UserListController.get_routes({'configuration': self.configuration}))
  158. application = tornado.web.Application(routes, **self.configuration.get_tornado_configuration())
  159. return application
  160. def test_create(self):
  161. # Create the user.
  162. request = paasmaker.common.api.user.UserCreateAPIRequest(self.configuration)
  163. request.set_superkey_auth()
  164. request.set_user_params('User Name', 'username', 'username@example.com', True)
  165. request.set_user_password('testtest')
  166. request.send(self.stop)
  167. response = self.wait()
  168. self.failIf(not response.success)
  169. self.assertEquals(len(response.errors), 0, "There were errors.")
  170. self.assertEquals(len(response.warnings), 0, "There were warnings.")
  171. self.assertTrue(response.data.has_key('user'), "Missing user object in return data.")
  172. self.assertTrue(response.data['user'].has_key('id'), "Missing ID in return data.")
  173. self.assertTrue(response.data['user'].has_key('login'), "Missing login in return data.")
  174. self.assertFalse(response.data['user'].has_key('password'), "Password was present in returned data.")
  175. self.assertEquals(response.data['user']['enabled'], True, "User is not enabled.")
  176. def test_create_fail(self):
  177. # Send through some bogus data.
  178. request = paasmaker.common.api.user.UserCreateAPIRequest(self.configuration)
  179. request.set_superkey_auth()
  180. request.set_user_params('', '', '', True)
  181. request.send(self.stop)
  182. response = self.wait()
  183. self.failIf(response.success)
  184. self.assertTrue('input_errors' in response.data, "Did not fail with errors.")
  185. input_errors = response.data['input_errors']
  186. self.assertTrue(input_errors.has_key('login'), "Missing error on login attribute.")
  187. self.assertTrue(input_errors.has_key('name'), "Missing error on login attribute.")
  188. self.assertTrue(input_errors.has_key('email'), "Missing error on login attribute.")
  189. # Now update the request somewhat, but fail to set a password.
  190. request.set_user_params('User Name', 'username', 'username@example.com', True)
  191. request.send(self.stop)
  192. response = self.wait()
  193. self.failIf(response.success)
  194. self.assertTrue("password" in response.errors[0], "Missing message in error.")
  195. def test_edit(self):
  196. # Create the user.
  197. request = paasmaker.common.api.user.UserCreateAPIRequest(self.configuration)
  198. request.set_superkey_auth()
  199. request.set_user_params('User Name', 'username', 'username@example.com', True)
  200. request.set_user_password('testtest')
  201. request.send(self.stop)
  202. response = self.wait()
  203. self.failIf(not response.success)
  204. user_id = response.data['user']['id']
  205. # Set up the request.
  206. request = paasmaker.common.api.user.UserEditAPIRequest(self.configuration)
  207. request.set_superkey_auth()
  208. # This loads the user data from the server.
  209. request.load(user_id, self.stop, self.stop)
  210. load_response = self.wait()
  211. # Now attempt to change the user.
  212. request.set_user_name('Test Updated')
  213. # Send it along!
  214. request.send(self.stop)
  215. response = self.wait()
  216. self.failIf(not response.success)
  217. self.assertEquals(response.data['user']['name'], 'Test Updated', 'Name was not updated.')
  218. # Load up the user separately and confirm.
  219. self.configuration.get_database_session(self.stop, None)
  220. session = self.wait()
  221. user = session.query(
  222. paasmaker.model.User
  223. ).get(user_id)
  224. self.assertEquals(user.name, 'Test Updated', 'Name was not updated.')
  225. def test_edit_fail(self):
  226. # Create the user.
  227. request = paasmaker.common.api.user.UserCreateAPIRequest(self.configuration)
  228. request.set_superkey_auth()
  229. request.set_user_params('User Name', 'username', 'username@example.com', True)
  230. request.set_user_password('testtest')
  231. request.send(self.stop)
  232. response = self.wait()
  233. self.failIf(not response.success)
  234. user_id = response.data['user']['id']
  235. # Set up the request.
  236. request = paasmaker.common.api.user.UserEditAPIRequest(self.configuration)
  237. request.set_superkey_auth()
  238. # This loads the user data from the server.
  239. request.load(user_id, self.stop, self.stop)
  240. load_response = self.wait()
  241. # Now attempt to change the user.
  242. request.set_user_email('foo')
  243. # Send it along!
  244. request.send(self.stop)
  245. response = self.wait()
  246. self.failIf(response.success)
  247. input_errors = response.data['input_errors']
  248. self.assertTrue(input_errors.has_key('email'), "Missing error on email attribute.")
  249. def test_list(self):
  250. # Create the user.
  251. request = paasmaker.common.api.user.UserCreateAPIRequest(self.configuration)
  252. request.set_superkey_auth()
  253. request.set_user_params('User Name', 'username', 'username@example.com', True)
  254. request.set_user_password('testtest')
  255. request.send(self.stop)
  256. response = self.wait()
  257. self.failIf(not response.success)
  258. request = paasmaker.common.api.user.UserListAPIRequest(self.configuration)
  259. request.set_superkey_auth()
  260. request.send(self.stop)
  261. response = self.wait()
  262. self.failIf(not response.success)
  263. self.assertTrue(response.data.has_key('users'), "Missing users list.")
  264. self.assertEquals(len(response.data['users']), 1, "Not enough users returned.")
  265. self.assertEquals(response.data['users'][0]['name'], 'User Name', "Returned user is not as expected.")