Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

create support user #4141

Merged
merged 67 commits into from
Dec 14, 2018
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
a481435
WIP creating and filtering support user
neilisfragile Oct 13, 2018
0bfd8ee
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Oct 23, 2018
c8e1bb6
move to using config.support_user_id
neilisfragile Oct 23, 2018
e9dbd02
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Oct 24, 2018
32b8971
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Oct 25, 2018
0ee8d1b
wip tests to filter out support user
neilisfragile Oct 29, 2018
1d0a5ab
Merge branch 'neilj/create_support_user' of github.com:matrix-org/syn…
neilisfragile Nov 2, 2018
cdb3aae
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Nov 2, 2018
158eccd
test suppoer user filtering
neilisfragile Nov 2, 2018
9d045fe
tc
neilisfragile Nov 2, 2018
76081eb
ensure support user created if does not exist
neilisfragile Nov 2, 2018
8964009
remove unused variable localpart
neilisfragile Nov 5, 2018
5032076
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Nov 5, 2018
0e962f5
fix py2/3 incompatibility
neilisfragile Nov 5, 2018
373a461
remove need to create support user in homeserver
neilisfragile Nov 6, 2018
6f19edb
fix misnamed var
neilisfragile Nov 6, 2018
bccbdb8
replace is with ==
neilisfragile Nov 6, 2018
d8b1f51
remove unused dependency
neilisfragile Nov 6, 2018
b7d1f0d
remove unused import
neilisfragile Nov 6, 2018
4254033
update description due to change in desired behaviour
neilisfragile Nov 6, 2018
06a3ec8
replace filter with list comp to aid py2 py3 compat, make test more p…
neilisfragile Nov 7, 2018
f217b5f
fix case where auto creation of rooms should never auto create for su…
neilisfragile Nov 7, 2018
12d09ac
tiday up cruft
neilisfragile Nov 8, 2018
7430a87
remove white space
neilisfragile Nov 8, 2018
eaac29f
move to db backed support user
neilisfragile Nov 12, 2018
eae8d4a
add db support for support user
neilisfragile Nov 13, 2018
45e0b9d
implementation and tests for db backed support user
neilisfragile Nov 13, 2018
cf10ca9
remove errant prints
neilisfragile Nov 13, 2018
a54aaf4
fix boolean typing
neilisfragile Nov 13, 2018
22418bc
remove unneeded sql
neilisfragile Nov 13, 2018
3121f04
wip - move support user logic into handler from storage
neilisfragile Nov 13, 2018
c708296
fix unit tests
neilisfragile Nov 13, 2018
b01271a
tweak tests and tidy
neilisfragile Nov 13, 2018
add1488
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Nov 14, 2018
b58bf8d
tests for support user behaviour
neilisfragile Nov 14, 2018
3f5fe16
test support user behaviour
neilisfragile Nov 14, 2018
ce27b1c
clean up
neilisfragile Nov 14, 2018
eab8843
improve docstring
neilisfragile Nov 14, 2018
ded5774
fix py2 Mock dep
neilisfragile Nov 14, 2018
44539df
Merge branch 'develop' into neilj/create_support_user
neilisfragile Nov 22, 2018
255515b
isort
neilisfragile Nov 27, 2018
65b4a21
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Nov 27, 2018
40771ff
remove line
neilisfragile Nov 27, 2018
6ae725a
fix race condition
neilisfragile Nov 27, 2018
430ea68
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Nov 28, 2018
b620f0a
fix reference to unused config.support_user_id
neilisfragile Nov 28, 2018
628c96e
Merge branch 'develop' into neilj/create_support_user
neilisfragile Nov 28, 2018
6574fdd
add in missing @defer.inlineCallbacks to test_auto_create_auto_join_w…
neilisfragile Nov 28, 2018
0dae671
allow creation of support users via the admin register api
neilisfragile Dec 11, 2018
33dfdd1
improved comments
neilisfragile Dec 11, 2018
868a72b
check for support users in _initialise_reserved_users. Remove unneces…
neilisfragile Dec 11, 2018
77a4163
Merge branch 'develop' into neilj/create_support_user
neilisfragile Dec 11, 2018
04f197d
fix merge conflict
neilisfragile Dec 11, 2018
1f0075e
linting
neilisfragile Dec 11, 2018
179ad1e
Merge branch 'develop' of github.com:matrix-org/synapse into neilj/cr…
neilisfragile Dec 11, 2018
936b554
change logic to be more explicit in _handle_deltas since order of che…
neilisfragile Dec 11, 2018
da3b125
fix log context leak
neilisfragile Dec 12, 2018
87cd017
add user_type validation and hmac support to admin registraion api
neilisfragile Dec 13, 2018
868cb6b
missing yield in test_is_support_user
neilisfragile Dec 14, 2018
d6d0401
style
neilisfragile Dec 14, 2018
b7c35d9
make user_type arg in admin registration api backward compatible
neilisfragile Dec 14, 2018
151d5c5
Update synapse/handlers/register.py
richvdh Dec 14, 2018
3ddd991
Update synapse/rest/client/v1/admin.py
richvdh Dec 14, 2018
d09cc1d
Update synapse/storage/registration.py
richvdh Dec 14, 2018
a7bb9bc
pep8
neilisfragile Dec 14, 2018
2adb5fc
fix doc typo
neilisfragile Dec 14, 2018
26a5c5f
remove trailing white space
neilisfragile Dec 14, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/4141.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Special-case a support user for use in verifying behaviour of a given server. The support user does not appear in user directory or monthly active user counts.
11 changes: 7 additions & 4 deletions docs/admin_api/register_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ As an example::
}

The MAC is the hex digest output of the HMAC-SHA1 algorithm, with the key being
the shared secret and the content being the nonce, user, password, and either
the string "admin" or "notadmin", each separated by NULs. For an example of
generation in Python::
the shared secret and the content being the nonce, user, password, either the
string "admin" or "notadmin", and optionally the user_type
each separated by NULs. For an example of generation in Python::

import hmac, hashlib

def generate_mac(nonce, user, password, admin=False):
def generate_mac(nonce, user, password, admin=False, user_type=None):

mac = hmac.new(
key=shared_secret,
Expand All @@ -59,5 +59,8 @@ generation in Python::
mac.update(password.encode('utf8'))
mac.update(b"\x00")
mac.update(b"admin" if admin else b"notadmin")
mac.update(b"\x00")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be conditional :/

if user_type:
mac.update(user_type.encode('utf8'))

return mac.hexdigest()
19 changes: 16 additions & 3 deletions synapse/_scripts/register_new_matrix_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def request_registration(
server_location,
shared_secret,
admin=False,
user_type=None,
requests=_requests,
_print=print,
exit=sys.exit,
Expand Down Expand Up @@ -65,6 +66,9 @@ def request_registration(
mac.update(password.encode('utf8'))
mac.update(b"\x00")
mac.update(b"admin" if admin else b"notadmin")
if user_type:
mac.update(b"\x00")
mac.update(user_type.encode('utf8'))

mac = mac.hexdigest()

Expand All @@ -74,6 +78,7 @@ def request_registration(
"password": password,
"mac": mac,
"admin": admin,
"user_type": user_type,
}

_print("Sending registration request...")
Expand All @@ -91,7 +96,7 @@ def request_registration(
_print("Success!")


def register_new_user(user, password, server_location, shared_secret, admin):
def register_new_user(user, password, server_location, shared_secret, admin, user_type):
if not user:
try:
default_user = getpass.getuser()
Expand Down Expand Up @@ -129,7 +134,8 @@ def register_new_user(user, password, server_location, shared_secret, admin):
else:
admin = False

request_registration(user, password, server_location, shared_secret, bool(admin))
request_registration(user, password, server_location, shared_secret,
bool(admin), user_type)


def main():
Expand All @@ -154,6 +160,12 @@ def main():
default=None,
help="New password for user. Will prompt if omitted.",
)
parser.add_argument(
"-t",
"--user_type",
default=None,
help="User type as specified in synapse.api.constants.UserTypes",
)
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
"-a",
Expand Down Expand Up @@ -208,7 +220,8 @@ def main():
if args.admin or args.no_admin:
admin = args.admin

register_new_user(args.user, args.password, args.server_url, secret, admin)
register_new_user(args.user, args.password, args.server_url, secret,
admin, args.user_type)


if __name__ == "__main__":
Expand Down
5 changes: 3 additions & 2 deletions synapse/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -802,9 +802,10 @@ def check_auth_blocking(self, user_id=None, threepid=None):
threepid should never be set at the same time.
"""

# Never fail an auth check for the server notices users
# Never fail an auth check for the server notices users or support user
# This can be a problem where event creation is prohibited due to blocking
if user_id == self.hs.config.server_notices_mxid:
is_support = yield self.store.is_support_user(user_id)
if user_id == self.hs.config.server_notices_mxid or is_support:
return

if self.hs.config.hs_disabled:
Expand Down
8 changes: 8 additions & 0 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,11 @@ class RoomVersions(object):

ServerNoticeMsgType = "m.server_notice"
ServerNoticeLimitReached = "m.server_notice.usage_limit_reached"


class UserTypes(object):
"""Allows for user type specific behaviour. With the benefit of hindsight
'admin' and 'guest' users should also be UserTypes. Normal users are type None
"""
SUPPORT = "support"
ALL_USER_TYPES = (SUPPORT)
15 changes: 13 additions & 2 deletions synapse/handlers/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def register(
make_guest=False,
admin=False,
threepid=None,
user_type=None,
richvdh marked this conversation as resolved.
Show resolved Hide resolved
default_display_name=None,
):
"""Registers a new client on the server.
Expand All @@ -141,6 +142,8 @@ def register(
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.
user_type (str|None): type of user. One of the values from
api.constants.UserTypes, or None for a normal user.
default_display_name (unicode|None): if set, the new user's displayname
will be set to this. Defaults to 'localpart'.
Returns:
Expand Down Expand Up @@ -190,6 +193,7 @@ def register(
make_guest=make_guest,
create_profile_with_displayname=default_display_name,
admin=admin,
user_type=user_type,
)

if self.hs.config.user_directory_search_all_users:
Expand Down Expand Up @@ -242,9 +246,16 @@ def _auto_join_rooms(self, user_id):
# auto-join the user to any rooms we're supposed to dump them into
fake_requester = create_requester(user_id)

# try to create the room if we're the first user on the server
# try to create the room if we're the first real user on the server. Note
# that an auto-generated support user is not a real user and will never be
# the user to create the room
should_auto_create_rooms = False
if self.hs.config.autocreate_auto_join_rooms:
is_support = yield self.store.is_support_user(user_id)
# There is an edge case where the first user is the support user, then
# the room is never created, though this seems unlikely and
# recoverable from given the support user being involved in the first
# place.
if self.hs.config.autocreate_auto_join_rooms and not is_support:
count = yield self.store.count_all_users()
should_auto_create_rooms = count == 1
for r in self.hs.config.auto_join_rooms:
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ def create_room(self, requester, config, ratelimit=True,
"""
user_id = requester.user.to_string()

self.auth.check_auth_blocking(user_id)
yield self.auth.check_auth_blocking(user_id)

if not self.spam_checker.user_may_create_room(user_id):
raise SynapseError(403, "You are not permitted to create rooms")
Expand Down
45 changes: 25 additions & 20 deletions synapse/handlers/user_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,12 @@ def handle_local_profile_change(self, user_id, profile):
"""
# FIXME(#3714): We should probably do this in the same worker as all
# the other changes.
yield self.store.update_profile_in_user_dir(
user_id, profile.display_name, profile.avatar_url, None,
)
is_support = yield self.store.is_support_user(user_id)
# Support users are for diagnostics and should not appear in the user directory.
if not is_support:
richvdh marked this conversation as resolved.
Show resolved Hide resolved
yield self.store.update_profile_in_user_dir(
user_id, profile.display_name, profile.avatar_url, None,
)

@defer.inlineCallbacks
def handle_user_deactivated(self, user_id):
Expand Down Expand Up @@ -329,14 +332,7 @@ def _handle_deltas(self, deltas):
public_value=Membership.JOIN,
)

if change is None:
richvdh marked this conversation as resolved.
Show resolved Hide resolved
# Handle any profile changes
yield self._handle_profile_change(
state_key, room_id, prev_event_id, event_id,
)
continue

if not change:
if change is False:
# Need to check if the server left the room entirely, if so
# we might need to remove all the users in that room
is_in_room = yield self.store.is_host_joined(
Expand All @@ -354,16 +350,25 @@ def _handle_deltas(self, deltas):
else:
logger.debug("Server is still in room: %r", room_id)

if change: # The user joined
event = yield self.store.get_event(event_id, allow_none=True)
profile = ProfileInfo(
avatar_url=event.content.get("avatar_url"),
display_name=event.content.get("displayname"),
)
is_support = yield self.store.is_support_user(state_key)
if not is_support:
if change is None:
# Handle any profile changes
yield self._handle_profile_change(
state_key, room_id, prev_event_id, event_id,
)
continue

if change: # The user joined
event = yield self.store.get_event(event_id, allow_none=True)
profile = ProfileInfo(
avatar_url=event.content.get("avatar_url"),
display_name=event.content.get("displayname"),
)

yield self._handle_new_user(room_id, state_key, profile)
else: # The user left
yield self._handle_remove_user(room_id, state_key)
yield self._handle_new_user(room_id, state_key, profile)
else: # The user left
yield self._handle_remove_user(room_id, state_key)
else:
logger.debug("Ignoring irrelevant type: %r", typ)

Expand Down
11 changes: 10 additions & 1 deletion synapse/rest/client/v1/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from twisted.internet import defer

from synapse.api.constants import Membership
from synapse.api.constants import Membership, UserTypes
from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError
from synapse.http.servlet import (
assert_params_in_dict,
Expand Down Expand Up @@ -158,6 +158,11 @@ def on_POST(self, request):
raise SynapseError(400, "Invalid password")

admin = body.get("admin", None)
user_type = body.get("user_type", None)
richvdh marked this conversation as resolved.
Show resolved Hide resolved

if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES:
raise SynapseError(400, "Invalid user type")

got_mac = body["mac"]

want_mac = hmac.new(
Expand All @@ -171,6 +176,9 @@ def on_POST(self, request):
want_mac.update(password)
want_mac.update(b"\x00")
want_mac.update(b"admin" if admin else b"notadmin")
if user_type:
want_mac.update(b"\x00")
want_mac.update(user_type.encode('utf8'))
want_mac = want_mac.hexdigest()

if not hmac.compare_digest(
Expand All @@ -189,6 +197,7 @@ def on_POST(self, request):
password=body["password"],
admin=bool(admin),
generate_token=False,
user_type=user_type,
)

result = yield register._create_registration_details(user_id, body)
Expand Down
30 changes: 28 additions & 2 deletions synapse/storage/monthly_active_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ def _initialise_reserved_users(self, txn, threepids):
txn,
tp["medium"], tp["address"]
)

if user_id:
self.upsert_monthly_active_user_txn(txn, user_id)
reserved_user_list.append(user_id)
is_support = self.is_support_user_txn(txn, user_id)
if not is_support:
self.upsert_monthly_active_user_txn(txn, user_id)
reserved_user_list.append(user_id)
else:
logger.warning(
"mau limit reserved threepid %s not found in db" % tp
Expand Down Expand Up @@ -182,6 +185,18 @@ def upsert_monthly_active_user(self, user_id):
Args:
user_id (str): user to add/update
"""
# Support user never to be included in MAU stats. Note I can't easily call this
# from upsert_monthly_active_user_txn because then I need a _txn form of
# is_support_user which is complicated because I want to cache the result.
# Therefore I call it here and ignore the case where
# upsert_monthly_active_user_txn is called directly from
# _initialise_reserved_users reasoning that it would be very strange to
# include a support user in this context.
richvdh marked this conversation as resolved.
Show resolved Hide resolved

is_support = yield self.is_support_user(user_id)
if is_support:
return

is_insert = yield self.runInteraction(
"upsert_monthly_active_user", self.upsert_monthly_active_user_txn,
user_id
Expand All @@ -200,6 +215,16 @@ def upsert_monthly_active_user_txn(self, txn, user_id):
in a database thread rather than the main thread, and we can't call
txn.call_after because txn may not be a LoggingTransaction.

We consciously do not call is_support_txn from this method because it
richvdh marked this conversation as resolved.
Show resolved Hide resolved
is not possible to cache the response. is_support_txn will be false in
almost all cases, so it seems reasonable to call it only for
upsert_monthly_active_user and to call is_support_txn manually
for cases where upsert_monthly_active_user_txn is called directly,
like _initialise_reserved_users

In short, don't call this method with support users. (Support users
should not appear in the MAU stats).

Args:
txn (cursor):
user_id (str): user to add/update
Expand All @@ -208,6 +233,7 @@ def upsert_monthly_active_user_txn(self, txn, user_id):
bool: True if a new entry was created, False if an
existing one was updated.
"""

# Am consciously deciding to lock the table on the basis that is ought
# never be a big table and alternative approaches (batching multiple
# upserts into a single txn) introduced a lot of extra complexity.
Expand Down
Loading