Regstration with email in v2

This commit is contained in:
David Baker 2015-04-15 15:50:38 +01:00
parent 4eb6d66b45
commit a19b739909
6 changed files with 123 additions and 29 deletions

View File

@ -30,6 +30,7 @@ from .admin import AdminHandler
from .appservice import ApplicationServicesHandler from .appservice import ApplicationServicesHandler
from .sync import SyncHandler from .sync import SyncHandler
from .auth import AuthHandler from .auth import AuthHandler
from .identity import IdentityHandler
class Handlers(object): class Handlers(object):
@ -60,3 +61,4 @@ class Handlers(object):
) )
self.sync_handler = SyncHandler(hs) self.sync_handler = SyncHandler(hs)
self.auth_handler = AuthHandler(hs) self.auth_handler = AuthHandler(hs)
self.identity_handler = IdentityHandler(hs)

View File

@ -20,6 +20,7 @@ from synapse.api.constants import LoginType
from synapse.types import UserID from synapse.types import UserID
from synapse.api.errors import LoginError, Codes from synapse.api.errors import LoginError, Codes
from synapse.http.client import SimpleHttpClient from synapse.http.client import SimpleHttpClient
from synapse.util.async import run_on_reactor
from twisted.web.client import PartialDownloadError from twisted.web.client import PartialDownloadError
@ -40,6 +41,7 @@ class AuthHandler(BaseHandler):
self.checkers = { self.checkers = {
LoginType.PASSWORD: self._check_password_auth, LoginType.PASSWORD: self._check_password_auth,
LoginType.RECAPTCHA: self._check_recaptcha, LoginType.RECAPTCHA: self._check_recaptcha,
LoginType.EMAIL_IDENTITY: self._check_email_identity,
} }
self.sessions = {} self.sessions = {}
@ -54,24 +56,37 @@ class AuthHandler(BaseHandler):
authdict: The dictionary from the client root level, not the authdict: The dictionary from the client root level, not the
'auth' key: this method prompts for auth if none is sent. 'auth' key: this method prompts for auth if none is sent.
Returns: Returns:
A tuple of authed, dict where authed is true if 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 dict has successfully completed an auth flow. If it is true, the first
contains the authenticated credentials of each stage. 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. 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: authdict = None
sess = self._get_session_info(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( 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: if 'creds' not in sess:
sess['creds'] = {} sess['creds'] = {}
creds = sess['creds'] creds = sess['creds']
@ -89,11 +104,11 @@ class AuthHandler(BaseHandler):
if len(set(f) - set(creds.keys())) == 0: if len(set(f) - set(creds.keys())) == 0:
logger.info("Auth completed with creds: %r", creds) logger.info("Auth completed with creds: %r", creds)
self._remove_session(sess) self._remove_session(sess)
defer.returnValue((True, creds)) defer.returnValue((True, creds, clientdict))
ret = self._auth_dict_for_flows(flows, sess) ret = self._auth_dict_for_flows(flows, sess)
ret['completed'] = creds.keys() ret['completed'] = creds.keys()
defer.returnValue((False, ret)) defer.returnValue((False, ret, clientdict))
@defer.inlineCallbacks @defer.inlineCallbacks
def add_oob_auth(self, stagetype, authdict, clientip): def add_oob_auth(self, stagetype, authdict, clientip):
@ -175,18 +190,25 @@ class AuthHandler(BaseHandler):
defer.returnValue(True) defer.returnValue(True)
raise LoginError(401, "", errcode=Codes.UNAUTHORIZED) 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): def _get_params_recaptcha(self):
return {"public_key": self.hs.config.recaptcha_public_key} return {"public_key": self.hs.config.recaptcha_public_key}
def _auth_dict_for_flows(self, flows, session): def _auth_dict_for_flows(self, flows, session):
public_flows = [] public_flows = []
for f in flows: for f in flows:
hidden = False public_flows.append(f)
for stagetype in f:
if stagetype in LoginType.HIDDEN_TYPES:
hidden = True
if not hidden:
public_flows.append(f)
get_params = { get_params = {
LoginType.RECAPTCHA: self._get_params_recaptcha, LoginType.RECAPTCHA: self._get_params_recaptcha,

View File

@ -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)

View File

@ -180,7 +180,11 @@ class RegistrationHandler(BaseHandler):
@defer.inlineCallbacks @defer.inlineCallbacks
def register_email(self, threepidCreds): 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: for c in threepidCreds:
logger.info("validating theeepidcred sid %s on id server %s", logger.info("validating theeepidcred sid %s on id server %s",

View File

@ -41,7 +41,7 @@ class PasswordRestServlet(RestServlet):
def on_POST(self, request): def on_POST(self, request):
body = parse_json_dict_from_request(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] [LoginType.PASSWORD]
], body) ], body)
@ -61,9 +61,9 @@ class PasswordRestServlet(RestServlet):
user_id = auth_user.to_string() user_id = auth_user.to_string()
if 'new_password' not in body: if 'new_password' not in params:
raise SynapseError(400, "", Codes.MISSING_PARAM) raise SynapseError(400, "", Codes.MISSING_PARAM)
new_password = body['new_password'] new_password = params['new_password']
yield self.login_handler.set_password( yield self.login_handler.set_password(
user_id, new_password, client.token_id user_id, new_password, client.token_id

View File

@ -74,7 +74,7 @@ class RegisterRestServlet(RestServlet):
) )
is_using_shared_secret = True is_using_shared_secret = True
else: else:
authed, result = yield self.auth_handler.check_auth([ authed, result, params = yield self.auth_handler.check_auth([
[LoginType.RECAPTCHA], [LoginType.RECAPTCHA],
[LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA], [LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
], body, self.hs.get_ip_from_request(request)) ], body, self.hs.get_ip_from_request(request))
@ -90,10 +90,10 @@ class RegisterRestServlet(RestServlet):
if not can_register: if not can_register:
raise SynapseError(403, "Registration has been disabled") 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) raise SynapseError(400, "", Codes.MISSING_PARAM)
desired_username = body['username'] desired_username = params['username']
new_password = body['password'] new_password = params['password']
(user_id, token) = yield self.registration_handler.register( (user_id, token) = yield self.registration_handler.register(
localpart=desired_username, localpart=desired_username,