Compare commits
8 Commits
v1.140.0rc
...
saml2_auth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87af3d1c05 | ||
|
|
fb22c9ce19 | ||
|
|
aee4a33996 | ||
|
|
76c7d62876 | ||
|
|
147d714a37 | ||
|
|
0c914c220e | ||
|
|
bbd0f83067 | ||
|
|
1798073a43 |
1
changelog.d/4272.feature
Normal file
1
changelog.d/4272.feature
Normal file
@@ -0,0 +1 @@
|
||||
SAML2 authentication: Initialise user display name from SAML2 data
|
||||
1
changelog.d/4273.misc
Normal file
1
changelog.d/4273.misc
Normal file
@@ -0,0 +1 @@
|
||||
Update the example systemd config to use a virtualenv
|
||||
31
contrib/systemd/matrix-synapse.service
Normal file
31
contrib/systemd/matrix-synapse.service
Normal file
@@ -0,0 +1,31 @@
|
||||
# Example systemd configuration file for synapse. Copy into
|
||||
# /etc/systemd/system/, update the paths if necessary, then:
|
||||
#
|
||||
# systemctl enable matrix-synapse
|
||||
# systemctl start matrix-synapse
|
||||
#
|
||||
# This assumes that Synapse has been installed in a virtualenv in
|
||||
# /opt/synapse/env.
|
||||
#
|
||||
# **NOTE:** This is an example service file that may change in the future. If you
|
||||
# wish to use this please copy rather than symlink it.
|
||||
|
||||
[Unit]
|
||||
Description=Synapse Matrix homeserver
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=on-abort
|
||||
|
||||
User=synapse
|
||||
Group=nogroup
|
||||
|
||||
WorkingDirectory=/opt/synapse
|
||||
ExecStart=/opt/synapse/env/bin/python -m synapse.app.homeserver --config-path=/opt/synapse/homeserver.yaml
|
||||
|
||||
# adjust the cache factor if necessary
|
||||
# Environment=SYNAPSE_CACHE_FACTOR=2.0
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# This assumes that Synapse has been installed as a system package
|
||||
# (e.g. https://www.archlinux.org/packages/community/any/matrix-synapse/ for ArchLinux)
|
||||
# rather than in a user home directory or similar under virtualenv.
|
||||
|
||||
# **NOTE:** This is an example service file that may change in the future. If you
|
||||
# wish to use this please copy rather than symlink it.
|
||||
|
||||
[Unit]
|
||||
Description=Synapse Matrix homeserver
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=synapse
|
||||
Group=synapse
|
||||
WorkingDirectory=/var/lib/synapse
|
||||
ExecStart=/usr/bin/python2.7 -m synapse.app.homeserver --config-path=/etc/synapse/homeserver.yaml
|
||||
ExecStop=/usr/bin/synctl stop /etc/synapse/homeserver.yaml
|
||||
# EnvironmentFile=-/etc/sysconfig/synapse # Can be used to e.g. set SYNAPSE_CACHE_FACTOR
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -126,6 +126,7 @@ class RegistrationHandler(BaseHandler):
|
||||
make_guest=False,
|
||||
admin=False,
|
||||
threepid=None,
|
||||
default_display_name=None,
|
||||
):
|
||||
"""Registers a new client on the server.
|
||||
|
||||
@@ -140,6 +141,8 @@ class RegistrationHandler(BaseHandler):
|
||||
since it offers no means of associating a device_id with the
|
||||
access_token. Instead you should call auth_handler.issue_access_token
|
||||
after registration.
|
||||
default_display_name (unicode|None): if set, the new user's displayname
|
||||
will be set to this. Defaults to 'localpart'.
|
||||
Returns:
|
||||
A tuple of (user_id, access_token).
|
||||
Raises:
|
||||
@@ -169,6 +172,13 @@ class RegistrationHandler(BaseHandler):
|
||||
user = UserID(localpart, self.hs.hostname)
|
||||
user_id = user.to_string()
|
||||
|
||||
if was_guest:
|
||||
# If the user was a guest then they already have a profile
|
||||
default_display_name = None
|
||||
|
||||
elif default_display_name is None:
|
||||
default_display_name = localpart
|
||||
|
||||
token = None
|
||||
if generate_token:
|
||||
token = self.macaroon_gen.generate_access_token(user_id)
|
||||
@@ -178,10 +188,7 @@ class RegistrationHandler(BaseHandler):
|
||||
password_hash=password_hash,
|
||||
was_guest=was_guest,
|
||||
make_guest=make_guest,
|
||||
create_profile_with_localpart=(
|
||||
# If the user was a guest then they already have a profile
|
||||
None if was_guest else user.localpart
|
||||
),
|
||||
create_profile_with_displayname=default_display_name,
|
||||
admin=admin,
|
||||
)
|
||||
|
||||
@@ -203,13 +210,15 @@ class RegistrationHandler(BaseHandler):
|
||||
yield self.check_user_id_not_appservice_exclusive(user_id)
|
||||
if generate_token:
|
||||
token = self.macaroon_gen.generate_access_token(user_id)
|
||||
if default_display_name is None:
|
||||
default_display_name = localpart
|
||||
try:
|
||||
yield self.store.register(
|
||||
user_id=user_id,
|
||||
token=token,
|
||||
password_hash=password_hash,
|
||||
make_guest=make_guest,
|
||||
create_profile_with_localpart=user.localpart,
|
||||
create_profile_with_displayname=default_display_name,
|
||||
)
|
||||
except SynapseError:
|
||||
# if user id is taken, just generate another
|
||||
@@ -300,7 +309,7 @@ class RegistrationHandler(BaseHandler):
|
||||
user_id=user_id,
|
||||
password_hash="",
|
||||
appservice_id=service_id,
|
||||
create_profile_with_localpart=user.localpart,
|
||||
create_profile_with_displayname=user.localpart,
|
||||
)
|
||||
defer.returnValue(user_id)
|
||||
|
||||
@@ -478,7 +487,7 @@ class RegistrationHandler(BaseHandler):
|
||||
user_id=user_id,
|
||||
token=token,
|
||||
password_hash=password_hash,
|
||||
create_profile_with_localpart=user.localpart,
|
||||
create_profile_with_displayname=user.localpart,
|
||||
)
|
||||
else:
|
||||
yield self._auth_handler.delete_access_tokens_for_user(user_id)
|
||||
|
||||
@@ -28,6 +28,7 @@ from synapse.http.servlet import (
|
||||
parse_json_object_from_request,
|
||||
parse_string,
|
||||
)
|
||||
from synapse.rest.well_known import WellKnownBuilder
|
||||
from synapse.types import UserID, map_username_to_mxid_localpart
|
||||
from synapse.util.msisdn import phone_number_to_msisdn
|
||||
|
||||
@@ -95,6 +96,7 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||
self.auth_handler = self.hs.get_auth_handler()
|
||||
self.device_handler = self.hs.get_device_handler()
|
||||
self.handlers = hs.get_handlers()
|
||||
self._well_known_builder = WellKnownBuilder(hs)
|
||||
|
||||
def on_GET(self, request):
|
||||
flows = []
|
||||
@@ -132,16 +134,18 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||
if self.jwt_enabled and (login_submission["type"] ==
|
||||
LoginRestServlet.JWT_TYPE):
|
||||
result = yield self.do_jwt_login(login_submission)
|
||||
defer.returnValue(result)
|
||||
elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
|
||||
result = yield self.do_token_login(login_submission)
|
||||
defer.returnValue(result)
|
||||
else:
|
||||
result = yield self._do_other_login(login_submission)
|
||||
defer.returnValue(result)
|
||||
except KeyError:
|
||||
raise SynapseError(400, "Missing JSON keys.")
|
||||
|
||||
well_known_data = self._well_known_builder.get_well_known()
|
||||
if well_known_data:
|
||||
result["well_known"] = well_known_data
|
||||
defer.returnValue((200, result))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _do_other_login(self, login_submission):
|
||||
"""Handle non-token/saml/jwt logins
|
||||
@@ -150,7 +154,7 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||
login_submission:
|
||||
|
||||
Returns:
|
||||
(int, object): HTTP code/response
|
||||
dict: HTTP response
|
||||
"""
|
||||
# Log the request we got, but only certain fields to minimise the chance of
|
||||
# logging someone's password (even if they accidentally put it in the wrong
|
||||
@@ -233,7 +237,7 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||
if callback is not None:
|
||||
yield callback(result)
|
||||
|
||||
defer.returnValue((200, result))
|
||||
defer.returnValue(result)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def do_token_login(self, login_submission):
|
||||
@@ -253,7 +257,7 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||
"device_id": device_id,
|
||||
}
|
||||
|
||||
defer.returnValue((200, result))
|
||||
defer.returnValue(result)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def do_jwt_login(self, login_submission):
|
||||
@@ -307,7 +311,7 @@ class LoginRestServlet(ClientV1RestServlet):
|
||||
"home_server": self.hs.hostname,
|
||||
}
|
||||
|
||||
defer.returnValue((200, result))
|
||||
defer.returnValue(result)
|
||||
|
||||
def _register_device(self, user_id, login_submission):
|
||||
"""Register a device for a user.
|
||||
@@ -451,6 +455,7 @@ class SSOAuthHandler(object):
|
||||
@defer.inlineCallbacks
|
||||
def on_successful_auth(
|
||||
self, username, request, client_redirect_url,
|
||||
user_display_name=None,
|
||||
):
|
||||
"""Called once the user has successfully authenticated with the SSO.
|
||||
|
||||
@@ -467,6 +472,9 @@ class SSOAuthHandler(object):
|
||||
client_redirect_url (unicode): the redirect_url the client gave us when
|
||||
it first started the process.
|
||||
|
||||
user_display_name (unicode|None): if set, and we have to register a new user,
|
||||
we will set their displayname to this.
|
||||
|
||||
Returns:
|
||||
Deferred[none]: Completes once we have handled the request.
|
||||
"""
|
||||
@@ -478,6 +486,7 @@ class SSOAuthHandler(object):
|
||||
yield self._registration_handler.register(
|
||||
localpart=localpart,
|
||||
generate_token=False,
|
||||
default_display_name=user_display_name,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -66,6 +66,9 @@ class SAML2ResponseResource(Resource):
|
||||
raise CodeMessageException(400, "uid not in SAML2 response")
|
||||
|
||||
username = saml2_auth.ava["uid"][0]
|
||||
|
||||
displayName = saml2_auth.ava.get("displayName", [None])[0]
|
||||
return self._sso_auth_handler.on_successful_auth(
|
||||
username, request, relay_state,
|
||||
user_display_name=displayName,
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ from twisted.internet import defer
|
||||
from synapse.api.errors import Codes, StoreError
|
||||
from synapse.storage import background_updates
|
||||
from synapse.storage._base import SQLBaseStore
|
||||
from synapse.types import UserID
|
||||
from synapse.util.caches.descriptors import cached, cachedInlineCallbacks
|
||||
|
||||
|
||||
@@ -167,7 +168,7 @@ class RegistrationStore(RegistrationWorkerStore,
|
||||
|
||||
def register(self, user_id, token=None, password_hash=None,
|
||||
was_guest=False, make_guest=False, appservice_id=None,
|
||||
create_profile_with_localpart=None, admin=False):
|
||||
create_profile_with_displayname=None, admin=False):
|
||||
"""Attempts to register an account.
|
||||
|
||||
Args:
|
||||
@@ -181,8 +182,8 @@ class RegistrationStore(RegistrationWorkerStore,
|
||||
make_guest (boolean): True if the the new user should be guest,
|
||||
false to add a regular user account.
|
||||
appservice_id (str): The ID of the appservice registering the user.
|
||||
create_profile_with_localpart (str): Optionally create a profile for
|
||||
the given localpart.
|
||||
create_profile_with_displayname (unicode): Optionally create a profile for
|
||||
the user, setting their displayname to the given value
|
||||
Raises:
|
||||
StoreError if the user_id could not be registered.
|
||||
"""
|
||||
@@ -195,7 +196,7 @@ class RegistrationStore(RegistrationWorkerStore,
|
||||
was_guest,
|
||||
make_guest,
|
||||
appservice_id,
|
||||
create_profile_with_localpart,
|
||||
create_profile_with_displayname,
|
||||
admin
|
||||
)
|
||||
|
||||
@@ -208,9 +209,11 @@ class RegistrationStore(RegistrationWorkerStore,
|
||||
was_guest,
|
||||
make_guest,
|
||||
appservice_id,
|
||||
create_profile_with_localpart,
|
||||
create_profile_with_displayname,
|
||||
admin,
|
||||
):
|
||||
user_id_obj = UserID.from_string(user_id)
|
||||
|
||||
now = int(self.clock.time())
|
||||
|
||||
next_id = self._access_tokens_id_gen.get_next()
|
||||
@@ -273,12 +276,15 @@ class RegistrationStore(RegistrationWorkerStore,
|
||||
(next_id, user_id, token,)
|
||||
)
|
||||
|
||||
if create_profile_with_localpart:
|
||||
if create_profile_with_displayname:
|
||||
# set a default displayname serverside to avoid ugly race
|
||||
# between auto-joins and clients trying to set displaynames
|
||||
#
|
||||
# *obviously* the 'profiles' table uses localpart for user_id
|
||||
# while everything else uses the full mxid.
|
||||
txn.execute(
|
||||
"INSERT INTO profiles(user_id, displayname) VALUES (?,?)",
|
||||
(create_profile_with_localpart, create_profile_with_localpart)
|
||||
(user_id_obj.localpart, create_profile_with_displayname)
|
||||
)
|
||||
|
||||
self._invalidate_cache_and_stream(
|
||||
|
||||
@@ -149,7 +149,7 @@ class MonthlyActiveUsersTestCase(HomeserverTestCase):
|
||||
|
||||
def test_populate_monthly_users_is_guest(self):
|
||||
# Test that guest users are not added to mau list
|
||||
user_id = "user_id"
|
||||
user_id = "@user_id:host"
|
||||
self.store.register(
|
||||
user_id=user_id, token="123", password_hash=None, make_guest=True
|
||||
)
|
||||
|
||||
@@ -140,6 +140,8 @@ def default_config(name):
|
||||
config.rc_messages_per_second = 10000
|
||||
config.rc_message_burst_count = 10000
|
||||
config.saml2_enabled = False
|
||||
config.public_baseurl = None
|
||||
config.default_identity_server = None
|
||||
|
||||
config.use_frozen_dicts = False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user