summary refs log tree commit diff
path: root/synapse/rest/media
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/rest/media')
-rw-r--r--synapse/rest/media/v1/login_qr_resource.py95
-rw-r--r--synapse/rest/media/v1/media_repository.py2
2 files changed, 97 insertions, 0 deletions
diff --git a/synapse/rest/media/v1/login_qr_resource.py b/synapse/rest/media/v1/login_qr_resource.py
new file mode 100644
index 0000000000..55708b2852
--- /dev/null
+++ b/synapse/rest/media/v1/login_qr_resource.py
@@ -0,0 +1,95 @@
+# Copyright 2015 OpenMarket Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from twisted.web.resource import Resource
+from twisted.web.server import NOT_DONE_YET
+from twisted.internet import defer, threads
+
+from synapse.api.errors import CodeMessageException
+
+import simplejson
+import logging
+
+from unpaddedbase64 import encode_base64
+from hashlib import sha256
+from OpenSSL import crypto
+
+logger = logging.getLogger(__name__)
+
+
+class LoginQRResource(Resource):
+    isLeaf = True
+
+    def __init__(self, hs):
+        Resource.__init__(self)
+        self.hs = hs
+        self.auth = hs.get_auth()
+        self.handlers = hs.get_handlers()
+        self.config = hs.get_config()
+
+    def render_GET(self, request):
+        self._async_render_GET(request)
+        return NOT_DONE_YET
+
+    @defer.inlineCallbacks
+    def _async_render_GET(self, request):
+        try:
+            auth_user, _ = yield self.auth.get_user_by_req(request)
+            image = yield self.make_short_term_qr_code(auth_user.to_string())
+            request.setHeader(b"Content-Type", b"image/png")
+
+            image.save(request)
+            request.finish()
+        except CodeMessageException as e:
+            logger.info("Returning: %s", e)
+            request.setResponseCode(e.code)
+            request.finish()
+        except Exception:
+            logger.exception("Exception while generating token")
+            request.setResponseCode(500)
+            request.finish()
+
+    @defer.inlineCallbacks
+    def make_short_term_qr_code(self, user_id):
+        h = self.handlers.auth_handler
+        token = h.make_short_term_token(user_id)
+
+        x509_certificate_bytes = crypto.dump_certificate(
+            crypto.FILETYPE_ASN1,
+            self.config.tls_certificate
+        )
+
+        sha256_fingerprint = sha256(x509_certificate_bytes).digest()
+
+        def gen():
+            import qrcode
+            qr = qrcode.QRCode(
+                version=1,
+                error_correction=qrcode.constants.ERROR_CORRECT_L,
+                box_size=5,
+            )
+            qr.add_data(simplejson.dumps({
+                "user_id": user_id,
+                "token": token,
+                "homeserver_url": self.config.client_addr,
+                "fingerprints": [{
+                    "hash_type": "SHA256",
+                    "bytes": encode_base64(sha256_fingerprint),
+                }],
+            }))
+            qr.make(fit=True)
+            return qr.make_image()
+
+        res = yield threads.deferToThread(gen)
+        defer.returnValue(res)
diff --git a/synapse/rest/media/v1/media_repository.py b/synapse/rest/media/v1/media_repository.py
index 9ca4d884dd..5dcd7b659c 100644
--- a/synapse/rest/media/v1/media_repository.py
+++ b/synapse/rest/media/v1/media_repository.py
@@ -17,6 +17,7 @@ from .upload_resource import UploadResource
 from .download_resource import DownloadResource
 from .thumbnail_resource import ThumbnailResource
 from .identicon_resource import IdenticonResource
+from .login_qr_resource import LoginQRResource
 from .filepath import MediaFilePaths
 
 from twisted.web.resource import Resource
@@ -78,3 +79,4 @@ class MediaRepositoryResource(Resource):
         self.putChild("download", DownloadResource(hs, filepaths))
         self.putChild("thumbnail", ThumbnailResource(hs, filepaths))
         self.putChild("identicon", IdenticonResource())
+        self.putChild("login_qr", LoginQRResource(hs))