launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #30217
[Merge] ~cjwatson/launchpad:stormify-logintoken into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:stormify-logintoken into launchpad:master.
Commit message:
Convert LoginToken to Storm
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/446394
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-logintoken into launchpad:master.
diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py
index 53e2358..10f2342 100644
--- a/lib/lp/registry/model/person.py
+++ b/lib/lp/registry/model/person.py
@@ -49,6 +49,7 @@ from storm.expr import (
Desc,
Exists,
In,
+ Is,
Join,
LeftJoin,
Min,
@@ -3178,16 +3179,22 @@ class Person(
@property
def unvalidatedemails(self):
"""See `IPerson`."""
- query = """
- requester = %s
- AND (tokentype=%s OR tokentype=%s)
- AND date_consumed IS NULL
- """ % sqlvalues(
- self.id,
- LoginTokenType.VALIDATEEMAIL,
- LoginTokenType.VALIDATETEAMEMAIL,
+ return sorted(
+ {
+ token.email
+ for token in IStore(LoginToken).find(
+ LoginToken,
+ LoginToken.requester == self,
+ LoginToken.tokentype.is_in(
+ (
+ LoginTokenType.VALIDATEEMAIL,
+ LoginTokenType.VALIDATETEAMEMAIL,
+ )
+ ),
+ Is(LoginToken.date_consumed, None),
+ )
+ }
)
- return sorted({token.email for token in LoginToken.select(query)})
@property
def guessedemails(self):
diff --git a/lib/lp/registry/stories/gpg-coc/xx-gpg-coc.rst b/lib/lp/registry/stories/gpg-coc/xx-gpg-coc.rst
index d5b1fc6..d6f3073 100644
--- a/lib/lp/registry/stories/gpg-coc/xx-gpg-coc.rst
+++ b/lib/lp/registry/stories/gpg-coc/xx-gpg-coc.rst
@@ -227,13 +227,14 @@ token to a fixed value:
>>> from datetime import datetime, timezone
>>> import hashlib
>>> from lp.services.verification.model.logintoken import LoginToken
- >>> logintoken = LoginToken.selectOneBy(
- ... _token=hashlib.sha256(token_value).hexdigest()
+ >>> logintoken = (
+ ... IStore(LoginToken)
+ ... .find(LoginToken, _token=hashlib.sha256(token_value).hexdigest())
+ ... .one()
... )
>>> logintoken.date_created = datetime(
... 2005, 4, 1, 12, 0, 0, tzinfo=timezone.utc
... )
- >>> logintoken.sync()
Back to Sample User. They visit the token URL and is asked to sign some
text to prove they own the key.
@@ -317,8 +318,10 @@ If they sign the text correctly, they are redirected to their home page.
Now that the key has been validated, the login token is consumed:
- >>> consumed_token = LoginToken.selectOneBy(
- ... _token=hashlib.sha256(token_value).hexdigest()
+ >>> consumed_token = (
+ ... IStore(LoginToken)
+ ... .find(LoginToken, _token=hashlib.sha256(token_value).hexdigest())
+ ... .one()
... )
>>> consumed_token.date_consumed is not None
True
diff --git a/lib/lp/services/verification/model/logintoken.py b/lib/lp/services/verification/model/logintoken.py
index 5aafa18..572cce2 100644
--- a/lib/lp/services/verification/model/logintoken.py
+++ b/lib/lp/services/verification/model/logintoken.py
@@ -10,7 +10,9 @@ import hashlib
from datetime import timezone
import six
-from storm.expr import And
+from storm.expr import And, Is
+from storm.properties import DateTime, Int, Unicode
+from storm.references import Reference
from zope.component import getUtility
from zope.interface import implementer
@@ -20,15 +22,9 @@ from lp.registry.interfaces.gpg import IGPGKeySet
from lp.registry.interfaces.person import IPersonSet
from lp.services.config import config
from lp.services.database.constants import UTC_NOW
-from lp.services.database.datetimecol import UtcDateTimeCol
from lp.services.database.enumcol import DBEnum
from lp.services.database.interfaces import IPrimaryStore, IStore
-from lp.services.database.sqlbase import SQLBase, sqlvalues
-from lp.services.database.sqlobject import (
- ForeignKey,
- SQLObjectNotFound,
- StringCol,
-)
+from lp.services.database.stormbase import StormBase
from lp.services.gpg.interfaces import IGPGHandler
from lp.services.mail.helpers import get_email_template
from lp.services.mail.sendmail import format_address, simple_sendmail
@@ -44,35 +40,51 @@ MAIL_APP = "services/verification"
@implementer(ILoginToken)
-class LoginToken(SQLBase):
- _table = "LoginToken"
-
- redirection_url = StringCol(default=None)
- requester = ForeignKey(dbName="requester", foreignKey="Person")
- requesteremail = StringCol(
- dbName="requesteremail", notNull=False, default=None
+class LoginToken(StormBase):
+ __storm_table__ = "LoginToken"
+
+ id = Int(primary=True)
+ redirection_url = Unicode(default=None)
+ requester_id = Int(name="requester")
+ requester = Reference(requester_id, "Person.id")
+ requesteremail = Unicode(
+ name="requesteremail", allow_none=True, default=None
)
- email = StringCol(dbName="email", notNull=True)
+ email = Unicode(name="email", allow_none=False)
# The hex SHA-256 hash of the token.
- _token = StringCol(dbName="token", unique=True)
+ _token = Unicode(name="token")
tokentype = DBEnum(name="tokentype", allow_none=False, enum=LoginTokenType)
- date_created = UtcDateTimeCol(dbName="created", notNull=True)
- fingerprint = StringCol(dbName="fingerprint", notNull=False, default=None)
- date_consumed = UtcDateTimeCol(default=None)
+ date_created = DateTime(
+ name="created", allow_none=False, tzinfo=timezone.utc
+ )
+ fingerprint = Unicode(name="fingerprint", allow_none=True, default=None)
+ date_consumed = DateTime(default=None, tzinfo=timezone.utc)
password = "" # Quick fix for Bug #2481
title = "Launchpad Email Verification"
- def __init__(self, *args, **kwargs):
- token = kwargs.pop("token", None)
+ def __init__(
+ self,
+ email,
+ tokentype,
+ redirection_url=None,
+ requester=None,
+ requesteremail=None,
+ token=None,
+ fingerprint=None,
+ ):
+ super().__init__()
+ self.email = email
+ self.tokentype = tokentype
+ self.redirection_url = redirection_url
+ self.requester = requester
+ self.requesteremail = requesteremail
if token is not None:
self._plaintext_token = token
- kwargs["_token"] = hashlib.sha256(
- token.encode("UTF-8")
- ).hexdigest()
- super().__init__(*args, **kwargs)
+ self._token = hashlib.sha256(token.encode("UTF-8")).hexdigest()
+ self.fingerprint = fingerprint
_plaintext_token = None
@@ -267,6 +279,10 @@ class LoginToken(SQLBase):
self.consume()
return lpkey, new
+ def destroySelf(self):
+ """See `ILoginToken`."""
+ IStore(self).remove(self)
+
@implementer(ILoginTokenSet)
class LoginTokenSet:
@@ -275,10 +291,10 @@ class LoginTokenSet:
def get(self, id, default=None):
"""See ILoginTokenSet."""
- try:
- return LoginToken.get(id)
- except SQLObjectNotFound:
+ token = IStore(LoginToken).get(LoginToken, id)
+ if token is None:
return default
+ return token
def searchByEmailRequesterAndType(
self, email, requester, type, consumed=None
@@ -337,18 +353,20 @@ class LoginTokenSet:
def getPendingGPGKeys(self, requesterid=None):
"""See ILoginTokenSet."""
- query = (
- "date_consumed IS NULL AND "
- "(tokentype = %s OR tokentype = %s) "
- % sqlvalues(
- LoginTokenType.VALIDATEGPG, LoginTokenType.VALIDATESIGNONLYGPG
- )
- )
+ clauses = [
+ Is(LoginToken.date_consumed, None),
+ LoginToken.tokentype.is_in(
+ (
+ LoginTokenType.VALIDATEGPG,
+ LoginTokenType.VALIDATESIGNONLYGPG,
+ )
+ ),
+ ]
if requesterid:
- query += "AND requester=%s" % requesterid
+ clauses.append(LoginToken.requester == requesterid)
- return LoginToken.select(query)
+ return IStore(LoginToken).find(LoginToken, *clauses)
def deleteByFingerprintRequesterAndType(
self, fingerprint, requester, type
@@ -383,7 +401,6 @@ class LoginTokenSet:
email=email,
token=token,
tokentype=tokentype,
- created=UTC_NOW,
fingerprint=fingerprint,
redirection_url=redirection_url,
)