add registrations_require_3pid
lets homeservers specify a whitelist for 3PIDs that users are allowed to associate with. Typically useful for stopping people from registering with non-work emails
This commit is contained in:
parent
36da256cc6
commit
28a6ccb49c
|
@ -46,6 +46,7 @@ class Codes(object):
|
||||||
THREEPID_AUTH_FAILED = "M_THREEPID_AUTH_FAILED"
|
THREEPID_AUTH_FAILED = "M_THREEPID_AUTH_FAILED"
|
||||||
THREEPID_IN_USE = "M_THREEPID_IN_USE"
|
THREEPID_IN_USE = "M_THREEPID_IN_USE"
|
||||||
THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
|
THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
|
||||||
|
THREEPID_DENIED = "M_THREEPID_DENIED"
|
||||||
INVALID_USERNAME = "M_INVALID_USERNAME"
|
INVALID_USERNAME = "M_INVALID_USERNAME"
|
||||||
SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
|
SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ class RegistrationConfig(Config):
|
||||||
strtobool(str(config["disable_registration"]))
|
strtobool(str(config["disable_registration"]))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.registrations_require_3pid = config.get("registrations_require_3pid", [])
|
||||||
self.registration_shared_secret = config.get("registration_shared_secret")
|
self.registration_shared_secret = config.get("registration_shared_secret")
|
||||||
|
|
||||||
self.bcrypt_rounds = config.get("bcrypt_rounds", 12)
|
self.bcrypt_rounds = config.get("bcrypt_rounds", 12)
|
||||||
|
@ -52,6 +53,18 @@ class RegistrationConfig(Config):
|
||||||
# Enable registration for new users.
|
# Enable registration for new users.
|
||||||
enable_registration: False
|
enable_registration: False
|
||||||
|
|
||||||
|
# Mandate that registrations require a 3PID which matches one or more
|
||||||
|
# of these 3PIDs. N.B. regexp escape backslashes are doubled (once for
|
||||||
|
# YAML and once for the regexp itself)
|
||||||
|
#
|
||||||
|
# registrations_require_3pid:
|
||||||
|
# - medium: email
|
||||||
|
# pattern: ".*@matrix\\.org"
|
||||||
|
# - medium: email
|
||||||
|
# pattern: ".*@vector\\.im"
|
||||||
|
# - medium: msisdn
|
||||||
|
# pattern: "\\+44"
|
||||||
|
|
||||||
# If set, allows registration by anyone who also has the shared
|
# If set, allows registration by anyone who also has the shared
|
||||||
# secret, even if registration is otherwise disabled.
|
# secret, even if registration is otherwise disabled.
|
||||||
registration_shared_secret: "%(registration_shared_secret)s"
|
registration_shared_secret: "%(registration_shared_secret)s"
|
||||||
|
|
|
@ -60,6 +60,28 @@ def set_timeline_upper_limit(filter_json, filter_timeline_limit):
|
||||||
filter_timeline_limit)
|
filter_timeline_limit)
|
||||||
|
|
||||||
|
|
||||||
|
def check_3pid_allowed(hs, medium, address):
|
||||||
|
# check whether the HS has whitelisted the given 3PID
|
||||||
|
|
||||||
|
allow = False
|
||||||
|
if hs.config.registrations_require_3pid:
|
||||||
|
for constraint in hs.config.registrations_require_3pid:
|
||||||
|
logger.debug("Checking 3PID %s (%s) against %s (%s)" % (
|
||||||
|
address, medium, constraint['pattern'], constraint['medium']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
medium == constraint['medium'] and
|
||||||
|
re.match(constraint['pattern'], address)
|
||||||
|
):
|
||||||
|
allow = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
allow = True
|
||||||
|
|
||||||
|
return allow
|
||||||
|
|
||||||
|
|
||||||
def interactive_auth_handler(orig):
|
def interactive_auth_handler(orig):
|
||||||
"""Wraps an on_POST method to handle InteractiveAuthIncompleteErrors
|
"""Wraps an on_POST method to handle InteractiveAuthIncompleteErrors
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ from synapse.http.servlet import (
|
||||||
)
|
)
|
||||||
from synapse.util.async import run_on_reactor
|
from synapse.util.async import run_on_reactor
|
||||||
from synapse.util.msisdn import phone_number_to_msisdn
|
from synapse.util.msisdn import phone_number_to_msisdn
|
||||||
from ._base import client_v2_patterns, interactive_auth_handler
|
from ._base import client_v2_patterns, interactive_auth_handler, check_3pid_allowed
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -47,6 +47,9 @@ class EmailPasswordRequestTokenRestServlet(RestServlet):
|
||||||
'id_server', 'client_secret', 'email', 'send_attempt'
|
'id_server', 'client_secret', 'email', 'send_attempt'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
if not check_3pid_allowed(self.hs, "email", body['email']):
|
||||||
|
raise SynapseError(403, "3PID denied", Codes.THREEPID_DENIED)
|
||||||
|
|
||||||
existingUid = yield self.hs.get_datastore().get_user_id_by_threepid(
|
existingUid = yield self.hs.get_datastore().get_user_id_by_threepid(
|
||||||
'email', body['email']
|
'email', body['email']
|
||||||
)
|
)
|
||||||
|
@ -78,6 +81,9 @@ class MsisdnPasswordRequestTokenRestServlet(RestServlet):
|
||||||
|
|
||||||
msisdn = phone_number_to_msisdn(body['country'], body['phone_number'])
|
msisdn = phone_number_to_msisdn(body['country'], body['phone_number'])
|
||||||
|
|
||||||
|
if not check_3pid_allowed(self.hs, "msisdn", msisdn):
|
||||||
|
raise SynapseError(403, "3PID denied", Codes.THREEPID_DENIED)
|
||||||
|
|
||||||
existingUid = yield self.datastore.get_user_id_by_threepid(
|
existingUid = yield self.datastore.get_user_id_by_threepid(
|
||||||
'msisdn', msisdn
|
'msisdn', msisdn
|
||||||
)
|
)
|
||||||
|
@ -217,6 +223,9 @@ class EmailThreepidRequestTokenRestServlet(RestServlet):
|
||||||
if absent:
|
if absent:
|
||||||
raise SynapseError(400, "Missing params: %r" % absent, Codes.MISSING_PARAM)
|
raise SynapseError(400, "Missing params: %r" % absent, Codes.MISSING_PARAM)
|
||||||
|
|
||||||
|
if not check_3pid_allowed(self.hs, "email", body['email']):
|
||||||
|
raise SynapseError(403, "3PID denied", Codes.THREEPID_DENIED)
|
||||||
|
|
||||||
existingUid = yield self.datastore.get_user_id_by_threepid(
|
existingUid = yield self.datastore.get_user_id_by_threepid(
|
||||||
'email', body['email']
|
'email', body['email']
|
||||||
)
|
)
|
||||||
|
@ -255,6 +264,9 @@ class MsisdnThreepidRequestTokenRestServlet(RestServlet):
|
||||||
|
|
||||||
msisdn = phone_number_to_msisdn(body['country'], body['phone_number'])
|
msisdn = phone_number_to_msisdn(body['country'], body['phone_number'])
|
||||||
|
|
||||||
|
if not check_3pid_allowed(self.hs, "msisdn", msisdn):
|
||||||
|
raise SynapseError(403, "3PID denied", Codes.THREEPID_DENIED)
|
||||||
|
|
||||||
existingUid = yield self.datastore.get_user_id_by_threepid(
|
existingUid = yield self.datastore.get_user_id_by_threepid(
|
||||||
'msisdn', msisdn
|
'msisdn', msisdn
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,9 +27,10 @@ from synapse.http.servlet import (
|
||||||
)
|
)
|
||||||
from synapse.util.msisdn import phone_number_to_msisdn
|
from synapse.util.msisdn import phone_number_to_msisdn
|
||||||
|
|
||||||
from ._base import client_v2_patterns, interactive_auth_handler
|
from ._base import client_v2_patterns, interactive_auth_handler, check_3pid_allowed
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
import hmac
|
import hmac
|
||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from synapse.util.async import run_on_reactor
|
from synapse.util.async import run_on_reactor
|
||||||
|
@ -70,6 +71,9 @@ class EmailRegisterRequestTokenRestServlet(RestServlet):
|
||||||
'id_server', 'client_secret', 'email', 'send_attempt'
|
'id_server', 'client_secret', 'email', 'send_attempt'
|
||||||
])
|
])
|
||||||
|
|
||||||
|
if not check_3pid_allowed(self.hs, "email", body['email']):
|
||||||
|
raise SynapseError(403, "3PID denied", Codes.THREEPID_DENIED)
|
||||||
|
|
||||||
existingUid = yield self.hs.get_datastore().get_user_id_by_threepid(
|
existingUid = yield self.hs.get_datastore().get_user_id_by_threepid(
|
||||||
'email', body['email']
|
'email', body['email']
|
||||||
)
|
)
|
||||||
|
@ -105,6 +109,9 @@ class MsisdnRegisterRequestTokenRestServlet(RestServlet):
|
||||||
|
|
||||||
msisdn = phone_number_to_msisdn(body['country'], body['phone_number'])
|
msisdn = phone_number_to_msisdn(body['country'], body['phone_number'])
|
||||||
|
|
||||||
|
if not check_3pid_allowed(self.hs, "msisdn", msisdn):
|
||||||
|
raise SynapseError(403, "3PID denied", Codes.THREEPID_DENIED)
|
||||||
|
|
||||||
existingUid = yield self.hs.get_datastore().get_user_id_by_threepid(
|
existingUid = yield self.hs.get_datastore().get_user_id_by_threepid(
|
||||||
'msisdn', msisdn
|
'msisdn', msisdn
|
||||||
)
|
)
|
||||||
|
@ -305,31 +312,73 @@ class RegisterRestServlet(RestServlet):
|
||||||
if 'x_show_msisdn' in body and body['x_show_msisdn']:
|
if 'x_show_msisdn' in body and body['x_show_msisdn']:
|
||||||
show_msisdn = True
|
show_msisdn = True
|
||||||
|
|
||||||
|
require_email = False
|
||||||
|
require_msisdn = False
|
||||||
|
for constraint in self.hs.config.registrations_require_3pid:
|
||||||
|
if constraint['medium'] == 'email':
|
||||||
|
require_email = True
|
||||||
|
elif constraint['medium'] == 'msisdn':
|
||||||
|
require_msisdn = True
|
||||||
|
else:
|
||||||
|
logger.warn(
|
||||||
|
"Unrecognised 3PID medium %s in registrations_require_3pid" %
|
||||||
|
constraint['medium']
|
||||||
|
)
|
||||||
|
|
||||||
|
flows = []
|
||||||
if self.hs.config.enable_registration_captcha:
|
if self.hs.config.enable_registration_captcha:
|
||||||
flows = [
|
if not require_email and not require_msisdn:
|
||||||
[LoginType.RECAPTCHA],
|
flows.extend([[LoginType.RECAPTCHA]])
|
||||||
[LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
|
if require_email or not require_msisdn:
|
||||||
]
|
flows.extend([[LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA]])
|
||||||
|
|
||||||
if show_msisdn:
|
if show_msisdn:
|
||||||
|
if not require_email or require_msisdn:
|
||||||
|
flows.extend([[LoginType.MSISDN, LoginType.RECAPTCHA]])
|
||||||
flows.extend([
|
flows.extend([
|
||||||
[LoginType.MSISDN, LoginType.RECAPTCHA],
|
|
||||||
[LoginType.MSISDN, LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
|
[LoginType.MSISDN, LoginType.EMAIL_IDENTITY, LoginType.RECAPTCHA],
|
||||||
])
|
])
|
||||||
else:
|
else:
|
||||||
flows = [
|
if not require_email and not require_msisdn:
|
||||||
[LoginType.DUMMY],
|
flows.extend([[LoginType.DUMMY]])
|
||||||
[LoginType.EMAIL_IDENTITY],
|
if require_email or not require_msisdn:
|
||||||
]
|
flows.extend([[LoginType.EMAIL_IDENTITY]])
|
||||||
|
|
||||||
if show_msisdn:
|
if show_msisdn:
|
||||||
|
if not require_email or require_msisdn:
|
||||||
|
flows.extend([[LoginType.MSISDN]])
|
||||||
flows.extend([
|
flows.extend([
|
||||||
[LoginType.MSISDN],
|
[LoginType.MSISDN, LoginType.EMAIL_IDENTITY]
|
||||||
[LoginType.MSISDN, LoginType.EMAIL_IDENTITY],
|
|
||||||
])
|
])
|
||||||
|
|
||||||
auth_result, params, session_id = yield self.auth_handler.check_auth(
|
auth_result, params, session_id = yield self.auth_handler.check_auth(
|
||||||
flows, body, self.hs.get_ip_from_request(request)
|
flows, body, self.hs.get_ip_from_request(request)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# doublecheck that we're not trying to register an denied 3pid.
|
||||||
|
# the user-facing checks should already have happened when we requested
|
||||||
|
# a 3PID token to validate them in /register/email/requestToken etc
|
||||||
|
|
||||||
|
for constraint in self.hs.config.registrations_require_3pid:
|
||||||
|
if (
|
||||||
|
constraint['medium'] == 'email' and
|
||||||
|
auth_result and LoginType.EMAIL_IDENTITY in auth_result and
|
||||||
|
re.match(
|
||||||
|
constraint['pattern'],
|
||||||
|
auth_result[LoginType.EMAIL_IDENTITY].threepid.address
|
||||||
|
)
|
||||||
|
):
|
||||||
|
raise SynapseError(403, "3PID denied", Codes.THREEPID_DENIED)
|
||||||
|
elif (
|
||||||
|
constraint['medium'] == 'msisdn' and
|
||||||
|
auth_result and LoginType.MSISDN in auth_result and
|
||||||
|
re.match(
|
||||||
|
constraint['pattern'],
|
||||||
|
auth_result[LoginType.MSISDN].threepid.address
|
||||||
|
)
|
||||||
|
):
|
||||||
|
raise SynapseError(403, "3PID denied", Codes.THREEPID_DENIED)
|
||||||
|
|
||||||
if registered_user_id is not None:
|
if registered_user_id is not None:
|
||||||
logger.info(
|
logger.info(
|
||||||
"Already registered user ID %r for this session",
|
"Already registered user ID %r for this session",
|
||||||
|
|
Loading…
Reference in New Issue