← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/logintoken-sha256-compat into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/logintoken-sha256-compat into lp:launchpad.

Commit message:
LoginTokens can now be looked up from a SHA-256-hashed token in the database.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/logintoken-sha256-compat/+merge/232890

LoginTokens can now be looked up from a SHA-256-hashed token in the database.
-- 
https://code.launchpad.net/~wgrant/launchpad/logintoken-sha256-compat/+merge/232890
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/logintoken-sha256-compat into lp:launchpad.
=== modified file 'lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt'
--- lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt	2014-09-01 13:10:39 +0000
@@ -211,7 +211,7 @@
 
     >>> import pytz, datetime
     >>> from lp.services.verification.model.logintoken import LoginToken
-    >>> logintoken = LoginToken.selectOneBy(token=token_value)
+    >>> logintoken = LoginToken.selectOneBy(_token=token_value)
     >>> logintoken.date_created = datetime.datetime(
     ...     2005,04,01, 12,00,00, tzinfo=pytz.timezone('UTC'))
     >>> logintoken.sync()
@@ -296,7 +296,7 @@
 
 Now that the key has been validated, the login token is consumed:
 
-    >>> LoginToken.selectOneBy(token=token_value).date_consumed is not None
+    >>> LoginToken.selectOneBy(_token=token_value).date_consumed is not None
     True
 
 Now Sample Person's sign-only key is associated with his account. He

=== modified file 'lib/lp/services/verification/model/logintoken.py'
--- lib/lp/services/verification/model/logintoken.py	2014-09-01 10:17:30 +0000
+++ lib/lp/services/verification/model/logintoken.py	2014-09-01 13:10:39 +0000
@@ -7,6 +7,8 @@
     'LoginTokenSet',
     ]
 
+import hashlib
+
 import pytz
 from sqlobject import (
     ForeignKey,
@@ -25,7 +27,10 @@
 from lp.services.database.constants import UTC_NOW
 from lp.services.database.datetimecol import UtcDateTimeCol
 from lp.services.database.enumcol import EnumCol
-from lp.services.database.interfaces import IMasterStore
+from lp.services.database.interfaces import (
+    IMasterStore,
+    IStore,
+    )
 from lp.services.database.sqlbase import (
     SQLBase,
     sqlvalues,
@@ -57,7 +62,7 @@
     requesteremail = StringCol(dbName='requesteremail', notNull=False,
                                default=None)
     email = StringCol(dbName='email', notNull=True)
-    token = StringCol(dbName='token', unique=True)
+    _token = StringCol(dbName='token', unique=True)
     tokentype = EnumCol(dbName='tokentype', notNull=True, enum=LoginTokenType)
     date_created = UtcDateTimeCol(dbName='created', notNull=True)
     fingerprint = StringCol(dbName='fingerprint', notNull=False, default=None)
@@ -66,6 +71,23 @@
 
     title = 'Launchpad Email Verification'
 
+    def __init__(self, *args, **kwargs):
+        token = kwargs.pop('token', None)
+        if token is not None:
+            self._plaintext_token = token
+            kwargs['_token'] = token
+        super(LoginToken, self).__init__(*args, **kwargs)
+
+    _plaintext_token = None
+
+    @property
+    def token(self):
+        if self._plaintext_token is None:
+            raise AssertionError(
+                "Token only available for LoginTokens obtained by token in "
+                "the first place. The DB only stores the hashed version.")
+        return self._plaintext_token
+
     def consume(self):
         """See ILoginToken."""
         self.date_consumed = UTC_NOW
@@ -347,7 +369,10 @@
 
     def __getitem__(self, tokentext):
         """See ILoginTokenSet."""
-        token = LoginToken.selectOneBy(token=tokentext)
+        token = IStore(LoginToken).find(
+            LoginToken, LoginToken._token.is_in((
+                hashlib.sha256(tokentext).hexdigest(), tokentext))).one()
         if token is None:
             raise NotFoundError(tokentext)
+        token._plaintext_token = tokentext
         return token


Follow ups