user.py 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. # -*- coding: utf-8 -*-
  2. from smtplib import SMTPException
  3. import transaction
  4. import typing as typing
  5. from sqlalchemy.orm import Session
  6. from sqlalchemy.orm.exc import NoResultFound
  7. from tracim import CFG
  8. from tracim.models.auth import User
  9. from tracim.models.auth import Group
  10. from tracim.exceptions import WrongUserPassword
  11. from tracim.exceptions import NoUserSetted
  12. from tracim.exceptions import PasswordDoNotMatch
  13. from tracim.exceptions import UserDoesNotExist
  14. from tracim.exceptions import AuthenticationFailed
  15. from tracim.exceptions import NotificationNotSend
  16. from tracim.exceptions import UserNotActive
  17. from tracim.models.context_models import UserInContext
  18. from tracim.lib.mail_notifier.notifier import get_email_manager
  19. class UserApi(object):
  20. def __init__(
  21. self,
  22. current_user: typing.Optional[User],
  23. session: Session,
  24. config: CFG,
  25. ) -> None:
  26. self._session = session
  27. self._user = current_user
  28. self._config = config
  29. def _base_query(self):
  30. return self._session.query(User)
  31. def get_user_with_context(self, user: User) -> UserInContext:
  32. """
  33. Return UserInContext object from User
  34. """
  35. user = UserInContext(
  36. user=user,
  37. dbsession=self._session,
  38. config=self._config,
  39. )
  40. return user
  41. # Getters
  42. def get_one(self, user_id: int) -> User:
  43. """
  44. Get one user by user id
  45. """
  46. try:
  47. user = self._base_query().filter(User.user_id == user_id).one()
  48. except NoResultFound as exc:
  49. raise UserDoesNotExist('User "{}" not found in database'.format(user_id)) from exc # nopep8
  50. return user
  51. def get_one_by_email(self, email: str) -> User:
  52. """
  53. Get one user by email
  54. :param email: Email of the user
  55. :return: one user
  56. """
  57. try:
  58. user = self._base_query().filter(User.email == email).one()
  59. except NoResultFound as exc:
  60. raise UserDoesNotExist('User "{}" not found in database'.format(email)) from exc # nopep8
  61. return user
  62. # FIXME - G.M - 24-04-2018 - Duplicate method with get_one.
  63. def get_one_by_id(self, id: int) -> User:
  64. return self.get_one(user_id=id)
  65. def get_current_user(self) -> User:
  66. """
  67. Get current_user
  68. """
  69. if not self._user:
  70. raise UserDoesNotExist('There is no current user')
  71. return self._user
  72. def get_all(self) -> typing.Iterable[User]:
  73. return self._session.query(User).order_by(User.display_name).all()
  74. # Check methods
  75. def user_with_email_exists(self, email: str) -> bool:
  76. try:
  77. self.get_one_by_email(email)
  78. return True
  79. # TODO - G.M - 09-04-2018 - Better exception
  80. except:
  81. return False
  82. def authenticate_user(self, email: str, password: str) -> User:
  83. """
  84. Authenticate user with email and password, raise AuthenticationFailed
  85. if uncorrect.
  86. :param email: email of the user
  87. :param password: cleartext password of the user
  88. :return: User who was authenticated.
  89. """
  90. try:
  91. user = self.get_one_by_email(email)
  92. if not user.is_active:
  93. raise UserNotActive('User "{}" is not active'.format(email))
  94. if user.validate_password(password):
  95. return user
  96. else:
  97. raise WrongUserPassword('User "{}" password is incorrect'.format(email)) # nopep8
  98. except (WrongUserPassword, UserDoesNotExist) as exc:
  99. raise AuthenticationFailed('User "{}" authentication failed'.format(email)) from exc # nopep8
  100. # Actions
  101. def set_password(
  102. self,
  103. user: User,
  104. loggedin_user_password: str,
  105. new_password: str,
  106. new_password2: str,
  107. do_save: bool=True
  108. ):
  109. """
  110. Set User password if loggedin user password is correct
  111. and both new_password are the same.
  112. :param user: User who need password changed
  113. :param loggedin_user_password: cleartext password of logged user (not
  114. same as user)
  115. :param new_password: new password for user
  116. :param new_password2: should be same as new_password
  117. :param do_save: should we save new user password ?
  118. :return:
  119. """
  120. if not self._user:
  121. raise NoUserSetted('Current User should be set in UserApi to use this method') # nopep8
  122. if not self._user.validate_password(loggedin_user_password): # nopep8
  123. raise WrongUserPassword(
  124. 'Wrong password for authenticated user {}'. format(self._user.user_id) # nopep8
  125. )
  126. if new_password != new_password2:
  127. raise PasswordDoNotMatch('Passwords given are different')
  128. self.update(
  129. user=user,
  130. password=new_password,
  131. do_save=do_save,
  132. )
  133. if do_save:
  134. # TODO - G.M - 2018-07-24 - Check why commit is needed here
  135. transaction.commit()
  136. return user
  137. def set_email(
  138. self,
  139. user: User,
  140. loggedin_user_password: str,
  141. email: str,
  142. do_save: bool = True
  143. ):
  144. """
  145. Set email address of user if loggedin user password is correct
  146. :param user: User who need email changed
  147. :param loggedin_user_password: cleartext password of logged user (not
  148. same as user)
  149. :param email:
  150. :param do_save:
  151. :return:
  152. """
  153. if not self._user:
  154. raise NoUserSetted('Current User should be set in UserApi to use this method') # nopep8
  155. if not self._user.validate_password(loggedin_user_password): # nopep8
  156. raise WrongUserPassword(
  157. 'Wrong password for authenticated user {}'. format(self._user.user_id) # nopep8
  158. )
  159. self.update(
  160. user=user,
  161. email=email,
  162. do_save=do_save,
  163. )
  164. return user
  165. def update(
  166. self,
  167. user: User,
  168. name: str=None,
  169. email: str=None,
  170. password: str=None,
  171. timezone: str=None,
  172. groups: typing.Optional[typing.List[Group]]=None,
  173. do_save=True,
  174. ) -> User:
  175. if name is not None:
  176. user.display_name = name
  177. if email is not None:
  178. user.email = email
  179. if password is not None:
  180. user.password = password
  181. if timezone is not None:
  182. user.timezone = timezone
  183. if groups is not None:
  184. # INFO - G.M - 2018-07-18 - Delete old groups
  185. for group in user.groups:
  186. if group not in groups:
  187. user.groups.remove(group)
  188. # INFO - G.M - 2018-07-18 - add new groups
  189. for group in groups:
  190. if group not in user.groups:
  191. user.groups.append(group)
  192. if do_save:
  193. self.save(user)
  194. return user
  195. def create_user(
  196. self,
  197. email,
  198. password: str = None,
  199. name: str = None,
  200. timezone: str = '',
  201. groups=[],
  202. do_save: bool=True,
  203. do_notify: bool=True,
  204. ) -> User:
  205. new_user = self.create_minimal_user(email, groups, save_now=False)
  206. self.update(
  207. user=new_user,
  208. name=name,
  209. email=email,
  210. password=password,
  211. timezone=timezone,
  212. do_save=False,
  213. )
  214. if do_notify:
  215. try:
  216. email_manager = get_email_manager(self._config, self._session)
  217. email_manager.notify_created_account(
  218. new_user,
  219. password=password
  220. )
  221. except SMTPException as e:
  222. raise NotificationNotSend()
  223. if do_save:
  224. self.save(new_user)
  225. return new_user
  226. def create_minimal_user(
  227. self,
  228. email,
  229. groups=[],
  230. save_now=False
  231. ) -> User:
  232. """Previous create_user method"""
  233. user = User()
  234. user.email = email
  235. for group in groups:
  236. user.groups.append(group)
  237. self._session.add(user)
  238. if save_now:
  239. self._session.flush()
  240. return user
  241. def enable(self, user: User, do_save=False):
  242. user.is_active = True
  243. if do_save:
  244. self.save(user)
  245. def disable(self, user:User, do_save=False):
  246. user.is_active = False
  247. if do_save:
  248. self.save(user)
  249. def save(self, user: User):
  250. self._session.flush()
  251. def execute_created_user_actions(self, created_user: User) -> None:
  252. """
  253. Execute actions when user just been created
  254. :return:
  255. """
  256. # NOTE: Cyclic import
  257. # TODO - G.M - 28-03-2018 - [Calendar] Reenable Calendar stuff
  258. #from tracim.lib.calendar import CalendarManager
  259. #from tracim.model.organisational import UserCalendar
  260. # TODO - G.M - 04-04-2018 - [auth]
  261. # Check if this is already needed with
  262. # new auth system
  263. created_user.ensure_auth_token(
  264. session=self._session,
  265. validity_seconds=self._config.USER_AUTH_TOKEN_VALIDITY
  266. )
  267. # Ensure database is up-to-date
  268. self._session.flush()
  269. transaction.commit()
  270. # TODO - G.M - 28-03-2018 - [Calendar] Reenable Calendar stuff
  271. # calendar_manager = CalendarManager(created_user)
  272. # calendar_manager.create_then_remove_fake_event(
  273. # calendar_class=UserCalendar,
  274. # related_object_id=created_user.user_id,
  275. # )