New registration for C/S API v2. Only ReCAPTCHA working currently.

This commit is contained in:
David Baker 2015-03-30 18:13:10 +01:00
parent 6f4f7e4e22
commit 59bf16eddc
8 changed files with 192 additions and 16 deletions

View file

@ -19,9 +19,12 @@ from ._base import BaseHandler
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 twisted.web.client import PartialDownloadError
import logging
import bcrypt
import simplejson
logger = logging.getLogger(__name__)
@ -33,7 +36,7 @@ class AuthHandler(BaseHandler):
super(AuthHandler, self).__init__(hs)
@defer.inlineCallbacks
def check_auth(self, flows, clientdict):
def check_auth(self, flows, clientdict, clientip=None):
"""
Takes a dictionary sent by the client in the login / registration
protocol and handles the login flow.
@ -50,11 +53,12 @@ class AuthHandler(BaseHandler):
login request and should be passed back to the client.
"""
types = {
LoginType.PASSWORD: self.check_password_auth
LoginType.PASSWORD: self.check_password_auth,
LoginType.RECAPTCHA: self.check_recaptcha,
}
if 'auth' not in clientdict:
defer.returnValue((False, auth_dict_for_flows(flows)))
if not clientdict or 'auth' not in clientdict:
defer.returnValue((False, self.auth_dict_for_flows(flows)))
authdict = clientdict['auth']
@ -67,7 +71,7 @@ class AuthHandler(BaseHandler):
raise LoginError(400, "", Codes.MISSING_PARAM)
if authdict['type'] not in types:
raise LoginError(400, "", Codes.UNRECOGNIZED)
result = yield types[authdict['type']](authdict)
result = yield types[authdict['type']](authdict, clientip)
if result:
creds[authdict['type']] = result
@ -76,12 +80,12 @@ class AuthHandler(BaseHandler):
logger.info("Auth completed with creds: %r", creds)
defer.returnValue((True, creds))
ret = auth_dict_for_flows(flows)
ret = self.auth_dict_for_flows(flows)
ret['completed'] = creds.keys()
defer.returnValue((False, ret))
@defer.inlineCallbacks
def check_password_auth(self, authdict):
def check_password_auth(self, authdict, _):
if "user" not in authdict or "password" not in authdict:
raise LoginError(400, "", Codes.MISSING_PARAM)
@ -93,17 +97,77 @@ class AuthHandler(BaseHandler):
user_info = yield self.store.get_user_by_id(user_id=user)
if not user_info:
logger.warn("Attempted to login as %s but they do not exist", user)
raise LoginError(403, "", errcode=Codes.FORBIDDEN)
raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)
stored_hash = user_info[0]["password_hash"]
if bcrypt.checkpw(password, stored_hash):
defer.returnValue(user)
else:
logger.warn("Failed password login for user %s", user)
raise LoginError(403, "", errcode=Codes.FORBIDDEN)
raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)
@defer.inlineCallbacks
def check_recaptcha(self, authdict, clientip):
try:
user_response = authdict["response"]
except KeyError:
# Client tried to provide captcha but didn't give the parameter:
# bad request.
raise LoginError(
400, "Captcha response is required",
errcode=Codes.CAPTCHA_NEEDED
)
def auth_dict_for_flows(flows):
return {
"flows": {"stages": f for f in flows}
}
logger.info(
"Submitting recaptcha response %s with remoteip %s",
user_response, clientip
)
# TODO: get this from the homeserver rather than creating a new one for
# each request
try:
client = SimpleHttpClient(self.hs)
data = yield client.post_urlencoded_get_json(
"https://www.google.com/recaptcha/api/siteverify",
args={
'secret': self.hs.config.recaptcha_private_key,
'response': user_response,
'remoteip': clientip,
}
)
except PartialDownloadError as pde:
# Twisted is silly
data = pde.response
resp_body = simplejson.loads(data)
if 'success' in resp_body and resp_body['success']:
defer.returnValue(True)
raise LoginError(401, "", errcode=Codes.UNAUTHORIZED)
def get_params_recaptcha(self):
return {"public_key": self.hs.config.recaptcha_public_key}
def auth_dict_for_flows(self, flows):
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)
get_params = {
LoginType.RECAPTCHA: self.get_params_recaptcha,
}
params = {}
for f in public_flows:
for stage in f:
if stage in get_params and stage not in params:
params[stage] = get_params[stage]()
return {
"flows": [{"stages": f} for f in public_flows],
"params": params
}