user.py 8.5KB

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