valsession.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2014 OpenMarket Ltd
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from __future__ import absolute_import
  16. import sydent.util.tokenutils
  17. from sydent.validators import ValidationSession, IncorrectClientSecretException, InvalidSessionIdException, \
  18. SessionExpiredException, SessionNotValidatedException
  19. from sydent.util import time_msec
  20. from random import SystemRandom
  21. class ThreePidValSessionStore:
  22. def __init__(self, syd):
  23. self.sydent = syd
  24. self.random = SystemRandom()
  25. def getOrCreateTokenSession(self, medium, address, clientSecret):
  26. """
  27. Retrieves the validation session for a given medium, address and client secret,
  28. or creates one if none was found.
  29. :param medium: The medium to use when looking up or creating the session.
  30. :type medium: unicode
  31. :param address: The address to use when looking up or creating the session.
  32. :type address: unicode
  33. :param clientSecret: The client secret to use when looking up or creating the
  34. session.
  35. :type clientSecret: unicode
  36. :return: The session that was retrieved or created.
  37. :rtype: ValidationSession
  38. """
  39. cur = self.sydent.db.cursor()
  40. cur.execute("select s.id, s.medium, s.address, s.clientSecret, s.validated, s.mtime, "
  41. "t.token, t.sendAttemptNumber from threepid_validation_sessions s,threepid_token_auths t "
  42. "where s.medium = ? and s.address = ? and s.clientSecret = ? and t.validationSession = s.id",
  43. (medium, address, clientSecret))
  44. row = cur.fetchone()
  45. if row:
  46. s = ValidationSession(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7])
  47. return s
  48. sid = self.addValSession(medium, address, clientSecret, time_msec(), commit=False)
  49. tokenString = sydent.util.tokenutils.generateTokenForMedium(medium)
  50. cur.execute("insert into threepid_token_auths (validationSession, token, sendAttemptNumber) values (?, ?, ?)",
  51. (sid, tokenString, -1))
  52. self.sydent.db.commit()
  53. s = ValidationSession(sid, medium, address, clientSecret, False, time_msec(), tokenString, -1)
  54. return s
  55. def addValSession(self, medium, address, clientSecret, mtime, commit=True):
  56. """
  57. Creates a validation session with the given parameters.
  58. :param medium: The medium to create the session for.
  59. :type medium: unicode
  60. :param address: The address to create the session for.
  61. :type address: unicode
  62. :param clientSecret: The client secret to use when looking up or creating the
  63. session.
  64. :type clientSecret: unicode
  65. :param mtime: The current time in milliseconds.
  66. :type mtime: int
  67. :param commit: Whether to commit the transaction after executing the insert
  68. statement.
  69. :type commit: bool
  70. :return: The ID of the created session.
  71. :rtype: int
  72. """
  73. cur = self.sydent.db.cursor()
  74. # Let's make up a random sid rather than using sequential ones. This
  75. # should be safe enough given we reap old sessions.
  76. sid = self.random.randint(0, 2 ** 31)
  77. cur.execute("insert into threepid_validation_sessions ('id', 'medium', 'address', 'clientSecret', 'mtime')" +
  78. " values (?, ?, ?, ?, ?)", (sid, medium, address, clientSecret, mtime))
  79. if commit:
  80. self.sydent.db.commit()
  81. return sid
  82. def setSendAttemptNumber(self, sid, attemptNo):
  83. """
  84. Updates the send attempt number for the session with the given ID.
  85. :param sid: The ID of the session to update
  86. :type sid: unicode
  87. :param attemptNo: The send attempt number to update the session with.
  88. :type attemptNo: int
  89. """
  90. cur = self.sydent.db.cursor()
  91. cur.execute("update threepid_token_auths set sendAttemptNumber = ? where id = ?", (attemptNo, sid))
  92. self.sydent.db.commit()
  93. def setValidated(self, sid, validated):
  94. """
  95. Updates a session to set the validated flag to the given value.
  96. :param sid: The ID of the session to update.
  97. :type sid: unicode
  98. :param validated: The value to set the validated flag.
  99. :type validated: bool
  100. """
  101. cur = self.sydent.db.cursor()
  102. cur.execute("update threepid_validation_sessions set validated = ? where id = ?", (validated, sid))
  103. self.sydent.db.commit()
  104. def setMtime(self, sid, mtime):
  105. """
  106. Set the time of the last send attempt for the session with the given ID
  107. :param sid: The ID of the session to update.
  108. :type sid: unicode
  109. :param mtime: The time of the last send attempt for that session.
  110. :type mtime: int
  111. """
  112. cur = self.sydent.db.cursor()
  113. cur.execute("update threepid_validation_sessions set mtime = ? where id = ?", (mtime, sid))
  114. self.sydent.db.commit()
  115. def getSessionById(self, sid):
  116. """
  117. Retrieves the session matching the given sid.
  118. :param sid: The ID of the session to retrieve.
  119. :type sid: unicode
  120. :return: The retrieved session, or None if no session could be found with that
  121. sid.
  122. :rtype: ValidationSession or None
  123. """
  124. cur = self.sydent.db.cursor()
  125. cur.execute("select id, medium, address, clientSecret, validated, mtime from "+
  126. "threepid_validation_sessions where id = ?", (sid,))
  127. row = cur.fetchone()
  128. if not row:
  129. return None
  130. return ValidationSession(row[0], row[1], row[2], row[3], row[4], row[5], None, None)
  131. def getTokenSessionById(self, sid):
  132. """
  133. Retrieves a validation session using the session's ID.
  134. :param sid: The ID of the session to retrieve.
  135. :type sid: unicode
  136. :return: The validation session, or None if no session was found with that ID.
  137. :rtype: ValidationSession or None
  138. """
  139. cur = self.sydent.db.cursor()
  140. cur.execute("select s.id, s.medium, s.address, s.clientSecret, s.validated, s.mtime, "
  141. "t.token, t.sendAttemptNumber from threepid_validation_sessions s,threepid_token_auths t "
  142. "where s.id = ? and t.validationSession = s.id", (sid,))
  143. row = cur.fetchone()
  144. if row:
  145. s = ValidationSession(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7])
  146. return s
  147. return None
  148. def getValidatedSession(self, sid, clientSecret):
  149. """
  150. Retrieve a validated and still-valid session whose client secret matches the
  151. one passed in.
  152. :param sid: The ID of the session to retrieve.
  153. :type sid: unicode
  154. :param clientSecret: A client secret to check against the one retrieved from
  155. the database.
  156. :type clientSecret: unicode
  157. :return: The retrieved session.
  158. :rtype: ValidationSession
  159. :raise InvalidSessionIdException: No session could be found with this ID.
  160. :raise IncorrectClientSecretException: The session's client secret doesn't
  161. match the one passed in.
  162. :raise SessionExpiredException: The session exists but has expired.
  163. :raise SessionNotValidatedException: The session exists but hasn't been
  164. validated yet.
  165. """
  166. s = self.getSessionById(sid)
  167. if not s:
  168. raise InvalidSessionIdException()
  169. if not s.clientSecret == clientSecret:
  170. raise IncorrectClientSecretException()
  171. if s.mtime + ValidationSession.THREEPID_SESSION_VALID_LIFETIME_MS < time_msec():
  172. raise SessionExpiredException()
  173. if not s.validated:
  174. raise SessionNotValidatedException()
  175. return s
  176. def deleteOldSessions(self):
  177. """Delete old threepid validation sessions that are long expired.
  178. """
  179. cur = self.sydent.db.cursor()
  180. delete_before_ts = time_msec() - 5 * ValidationSession.THREEPID_SESSION_VALID_LIFETIME_MS
  181. sql = """
  182. DELETE FROM threepid_validation_sessions
  183. WHERE mtime < ?
  184. """
  185. cur.execute(sql, (delete_before_ts,))
  186. sql = """
  187. DELETE FROM threepid_token_auths
  188. WHERE validationSession NOT IN (
  189. SELECT id FROM threepid_validation_sessions
  190. )
  191. """
  192. cur.execute(sql)
  193. self.sydent.db.commit()