diff --git a/synapse/handlers/__init__.py b/synapse/handlers/__init__.py
index 336ce15701..d1b0e032a3 100644
--- a/synapse/handlers/__init__.py
+++ b/synapse/handlers/__init__.py
@@ -30,6 +30,7 @@ from .admin import AdminHandler
from .appservice import ApplicationServicesHandler
from .sync import SyncHandler
from .auth import AuthHandler
+from .identity import IdentityHandler
class Handlers(object):
@@ -60,3 +61,4 @@ class Handlers(object):
)
self.sync_handler = SyncHandler(hs)
self.auth_handler = AuthHandler(hs)
+ self.identity_handler = IdentityHandler(hs)
diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py
index 3d2461dd7d..2cc54707a2 100644
--- a/synapse/handlers/auth.py
+++ b/synapse/handlers/auth.py
@@ -20,6 +20,7 @@ from synapse.api.constants import LoginType
from synapse.types import UserID
from synapse.api.errors import LoginError, Codes
from synapse.http.client import SimpleHttpClient
+from synapse.util.async import run_on_reactor
from twisted.web.client import PartialDownloadError
@@ -40,6 +41,7 @@ class AuthHandler(BaseHandler):
self.checkers = {
LoginType.PASSWORD: self._check_password_auth,
LoginType.RECAPTCHA: self._check_recaptcha,
+ LoginType.EMAIL_IDENTITY: self._check_email_identity,
}
self.sessions = {}
@@ -54,24 +56,37 @@ class AuthHandler(BaseHandler):
authdict: The dictionary from the client root level, not the
'auth' key: this method prompts for auth if none is sent.
Returns:
- A tuple of authed, dict where authed is true if the client
- has successfully completed an auth flow. If it is true, the dict
- contains the authenticated credentials of each stage.
- If authed is false, the dictionary is the server response to the
- login request and should be passed back to the client.
+ A tuple of authed, dict, dict where authed is true if the client
+ has successfully completed an auth flow. If it is true, the first
+ dict contains the authenticated credentials of each stage.
+
+ If authed is false, the first dictionary is the server response to
+ the login request and should be passed back to the client.
+
+ In either case, the second dict contains the parameters for this
+ request (which may have been given only in a previous call).
"""
- if not clientdict or 'auth' not in clientdict:
- sess = self._get_session_info(None)
+ authdict = None
+ sid = None
+ if clientdict and 'auth' in clientdict:
+ authdict = clientdict['auth']
+ del clientdict['auth']
+ if 'session' in authdict:
+ sid = authdict['session']
+ sess = self._get_session_info(sid)
+
+ if len(clientdict) > 0:
+ sess['clientdict'] = clientdict
+ self._save_session(sess)
+ elif 'clientdict' in sess:
+ clientdict = sess['clientdict']
+
+ if not authdict:
defer.returnValue(
- (False, self._auth_dict_for_flows(flows, sess))
+ (False, self._auth_dict_for_flows(flows, sess), clientdict)
)
- authdict = clientdict['auth']
-
- sess = self._get_session_info(
- authdict['session'] if 'session' in authdict else None
- )
if 'creds' not in sess:
sess['creds'] = {}
creds = sess['creds']
@@ -89,11 +104,11 @@ class AuthHandler(BaseHandler):
if len(set(f) - set(creds.keys())) == 0:
logger.info("Auth completed with creds: %r", creds)
self._remove_session(sess)
- defer.returnValue((True, creds))
+ defer.returnValue((True, creds, clientdict))
ret = self._auth_dict_for_flows(flows, sess)
ret['completed'] = creds.keys()
- defer.returnValue((False, ret))
+ defer.returnValue((False, ret, clientdict))
@defer.inlineCallbacks
def add_oob_auth(self, stagetype, authdict, clientip):
@@ -175,18 +190,25 @@ class AuthHandler(BaseHandler):
defer.returnValue(True)
raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)
+ @defer.inlineCallbacks
+ def _check_email_identity(self, authdict, _):
+ yield run_on_reactor()
+
+ threepidCreds = authdict['threepidCreds']
+ identity_handler = self.hs.get_handlers().identity_handler
+
+ logger.debug("Getting validated threepid. threepidcreds: %r" % (threepidCreds,))
+ threepid = yield identity_handler.threepid_from_creds(threepidCreds)
+
+ defer.returnValue(threepid)
+
def _get_params_recaptcha(self):
return {"public_key": self.hs.config.recaptcha_public_key}
def _auth_dict_for_flows(self, flows, session):
public_flows = []
for f in flows:
- hidden = False
- for stagetype in f:
- if stagetype in LoginType.HIDDEN_TYPES:
- hidden = True
- if not hidden:
- public_flows.append(f)
+ public_flows.append(f)
get_params = {
LoginType.RECAPTCHA: self._get_params_recaptcha,
diff --git a/synapse/handlers/identity.py b/synapse/handlers/identity.py
new file mode 100644
index 0000000000..671d366e40
--- /dev/null
+++ b/synapse/handlers/identity.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""Utilities for interacting with Identity Servers"""
+from twisted.internet import defer
+
+from synapse.api.errors import (
+ CodeMessageException
+)
+from ._base import BaseHandler
+from synapse.http.client import SimpleHttpClient
+from synapse.util.async import run_on_reactor
+
+import json
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class IdentityHandler(BaseHandler):
+
+ def __init__(self, hs):
+ super(IdentityHandler, self).__init__(hs)
+
+ @defer.inlineCallbacks
+ def threepid_from_creds(self, creds):
+ yield run_on_reactor()
+
+ # TODO: get this from the homeserver rather than creating a new one for
+ # each request
+ http_client = SimpleHttpClient(self.hs)
+ # XXX: make this configurable!
+ #trustedIdServers = ['matrix.org', 'localhost:8090']
+ trustedIdServers = ['matrix.org']
+ if not creds['idServer'] in trustedIdServers:
+ logger.warn('%s is not a trusted ID server: rejecting 3pid ' +
+ 'credentials', creds['idServer'])
+ defer.returnValue(None)
+
+ data = {}
+ try:
+ data = yield http_client.get_json(
+ "https://%s%s" % (
+ creds['idServer'],
+ "/_matrix/identity/api/v1/3pid/getValidated3pid"
+ ),
+ {'sid': creds['sid'], 'clientSecret': creds['clientSecret']}
+ )
+ except CodeMessageException as e:
+ data = json.loads(e.msg)
+
+ if 'medium' in data:
+ defer.returnValue(data)
+ defer.returnValue(None)
\ No newline at end of file
diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py
index 542759a827..6759a8c582 100644
--- a/synapse/handlers/register.py
+++ b/synapse/handlers/register.py
@@ -180,7 +180,11 @@ class RegistrationHandler(BaseHandler):
@defer.inlineCallbacks
def register_email(self, threepidCreds):
- """Registers emails with an identity server."""
+ """
+ Registers emails with an identity server.
+
+ Used only by c/s api v1
+ """
for c in threepidCreds:
logger.info("validating theeepidcred sid %s on id server %s",
diff --git a/synapse/rest/client/v2_alpha/password.py b/synapse/rest/client/v2_alpha/password.py
index 85954c71cd..cb0c8cfb55 100644
--- a/synapse/rest/client/v2_alpha/password.py
+++ b/synapse/rest/client/v2_alpha/password.py
@@ -41,7 +41,7 @@ class PasswordRestServlet(RestServlet):
def on_POST(self, request):
body = parse_json_dict_from_request(request)
- authed, result = yield self.auth_handler.check_auth([
+ authed, result, params = yield self.auth_handler.check_auth([
[LoginType.PASSWORD]
], body)
@@ -61,9 +61,9 @@ class PasswordRestServlet(RestServlet):
user_id = auth_user.to_string()
- if 'new_password' not in body:
+ if 'new_password' not in params:
raise SynapseError(400, "", Codes.MISSING_PARAM)
- new_password = body['new_password']
+ new_password = params['new_password']
yield self.login_handler.set_password(
user_id, new_password, client.token_id
diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py
index 72319a3bb2..d7a20fc964 100644
--- a/synapse/rest/client/v2_alpha/register.py
+++ b/synapse/rest/client/v2_alpha/register.py
@@ -74,7 +74,7 @@ class RegisterRestServlet(RestServlet):
)
is_using_shared_secret = True
else:
- authed, result = yield self.auth_handler.check_auth([
+ authed, result, params = yield self.auth_handler.check_auth([
[LoginType.RECAPTCHA],
[LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
], body, self.hs.get_ip_from_request(request))
@@ -90,10 +90,10 @@ class RegisterRestServlet(RestServlet):
if not can_register:
raise SynapseError(403, "Registration has been disabled")
- if 'username' not in body or 'password' not in body:
+ if 'username' not in params or 'password' not in params:
raise SynapseError(400, "", Codes.MISSING_PARAM)
- desired_username = body['username']
- new_password = body['password']
+ desired_username = params['username']
+ new_password = params['password']
(user_id, token) = yield self.registration_handler.register(
localpart=desired_username,
|