Browse Source

Changes for new C/S auth: send links in mails rather than codes (or currently, as well as ).

David Baker 9 years ago
parent
commit
27b757a626

+ 10 - 0
res/verify_response_page_template

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+<title></title>
+</head>
+<body>
+<p>%(message)s</p>
+</body>
+</html>

+ 28 - 2
sydent/http/servlets/emailservlet.py

@@ -39,17 +39,24 @@ class EmailRequestCodeServlet(Resource):
         email = request.args['email'][0]
         clientSecret = request.args['clientSecret'][0]
         sendAttempt = request.args['sendAttempt'][0]
+        ipaddress = self.sydent.ip_from_request(request)
+
+        nextLink = None
+        if 'nextLink' in request.args:
+            nextLink = request.args['nextLink'][0]
 
         resp = None
 
         try:
-            sid = self.sydent.validators.email.requestToken(email, clientSecret, sendAttempt)
+            sid = self.sydent.validators.email.requestToken(
+                email, clientSecret, sendAttempt, nextLink, ipaddress=ipaddress
+            )
         except EmailAddressException:
             request.setResponseCode(400)
             resp = {'errcode': 'M_INVALID_EMAIL', 'error':'Invalid email address'}
         except EmailSendException:
             request.setResponseCode(500)
-            resp = {'errcode': 'M_EMAIL_SEND_ERROR', 'error': 'Failed to sent email'}
+            resp = {'errcode': 'M_EMAIL_SEND_ERROR', 'error': 'Failed to send email'}
 
         if not resp:
             resp = {'success': True, 'sid': sid}
@@ -69,8 +76,27 @@ class EmailValidateCodeServlet(Resource):
     def __init__(self, syd):
         self.sydent = syd
 
+    def render_GET(self, request):
+        resp = self.do_validate_request(request)
+        if resp['success']:
+            msg = "Verification successful! Please return to your Matrix client to continue."
+            if 'nextLink' in request.args:
+                next_link = request.args['nextLink'][0]
+                request.setResponseCode(302)
+                request.setHeader("Location", next_link)
+        else:
+            msg = "Verification failed: you may need to request another verification email"
+
+        templateFile = self.sydent.cfg.get('http', 'verify_response_template')
+
+        request.setHeader("Content-Type", "text/html")
+        return open(templateFile).read() % {'message': msg}
+
     @jsonwrap
     def render_POST(self, request):
+        return self.do_validate_request(request)
+
+    def do_validate_request(self, request):
         send_cors(request)
 
         err = require_args(request, ('token', 'sid', 'clientSecret'))

+ 8 - 1
sydent/sydent.py

@@ -59,7 +59,8 @@ class Sydent:
         'email.subject': 'Your Validation Token',
         'email.smtphost': 'localhost',
         'log.path': '',
-        'ed25519.signingkey': ''
+        'ed25519.signingkey': '',
+        'obey_x_forwarded_for': False
     }
 
     def __init__(self):
@@ -132,6 +133,12 @@ class Sydent:
         self.pusher.setup()
         twisted.internet.reactor.run()
 
+    def ip_from_request(self, request):
+        if (self.cfg.get('http', 'obey_x_forwarded_for') and
+                request.requestHeaders.hasHeader("X-Forwarded-For")):
+            return request.requestHeaders.getRawHeaders("X-Forwarded-For")[0]
+        return request.getClientIP()
+
 
 class Validators:
     pass

+ 31 - 14
sydent/validators/emailvalidator.py

@@ -18,10 +18,12 @@ import smtplib
 import os
 import email.utils
 import logging
+import random
+import string
+import urllib
 import twisted.python.log
 
-from email.mime.text import MIMEText
-from email.mime.multipart import MIMEMultipart
+import email.utils
 
 from sydent.db.valsession import ThreePidValSessionStore
 from sydent.validators import ValidationSession
@@ -37,7 +39,7 @@ class EmailValidator:
     def __init__(self, sydent):
         self.sydent = sydent
 
-    def requestToken(self, emailAddress, clientSecret, sendAttempt):
+    def requestToken(self, emailAddress, clientSecret, sendAttempt, nextLink, ipaddress=None):
         valSessionStore = ThreePidValSessionStore(self.sydent)
 
         valSession = valSessionStore.getOrCreateTokenSession(medium='email', address=emailAddress,
@@ -50,21 +52,24 @@ class EmailValidator:
             return valSession.id
 
         myHostname = os.uname()[1]
+        midRandom = "".join([random.choice(string.ascii_letters) for _ in range(16)])
+        messageid = "%d%s@%s" % (time_msec(), midRandom, myHostname)
+        ipstring = ipaddress if ipaddress else u"an unknown location"
 
-        mailFrom = self.sydent.cfg.get('email', 'email.from').format(hostname=myHostname)
         mailTo = emailAddress
+        mailFrom = self.sydent.cfg.get('email', 'email.from')
 
         mailTemplateFile = self.sydent.cfg.get('email', 'email.template')
 
-        mailString = open(mailTemplateFile).read().format(token=valSession.token)
-
-        msg = MIMEMultipart('alternative')
-        msg['Subject'] = self.sydent.cfg.get('email', 'email.subject')
-        msg['From'] = mailFrom
-        msg['To'] = mailTo
-
-        plainPart = MIMEText(mailString)
-        msg.attach(plainPart)
+        mailString = open(mailTemplateFile).read() % {
+            'date': email.utils.formatdate(localtime=False),
+            'to': mailTo,
+            'from': mailFrom,
+            'messageid': messageid,
+            'ipaddress': ipstring,
+            'link': self.makeValidateLink(valSession, clientSecret, nextLink),
+            'token': valSession.token,
+        }
 
         rawFrom = email.utils.parseaddr(mailFrom)[1]
         rawTo = email.utils.parseaddr(mailTo)[1]
@@ -79,7 +84,7 @@ class EmailValidator:
 
         try:
             smtp = smtplib.SMTP(mailServer)
-            smtp.sendmail(rawFrom, rawTo, msg.as_string())
+            smtp.sendmail(rawFrom, rawTo, mailString)
             smtp.quit()
         except Exception as origException:
             twisted.python.log.err()
@@ -91,6 +96,18 @@ class EmailValidator:
 
         return valSession.id
 
+    def makeValidateLink(self, valSession, clientSecret, nextLink):
+        base = self.sydent.cfg.get('http', 'client_http_base')
+        link = "%s/_matrix/identity/api/v1/validate/email/submitToken?token=%s&clientSecret=%s&sid=%d" % (
+            base,
+            urllib.quote(valSession.token),
+            urllib.quote(clientSecret),
+            valSession.id,
+        )
+        if nextLink:
+            link += "&nextLink=%s" % (urllib.quote(nextLink))
+        return link
+
     def validateSessionWithToken(self, sid, clientSecret, token):
         valSessionStore = ThreePidValSessionStore(self.sydent)
         s = valSessionStore.getTokenSessionById(sid)