Autocreate autojoin rooms#3975
Conversation
| # try to create the room if we're the first user on the server | ||
| if self.hs.config.autocreate_auto_join_rooms: | ||
| count = yield self.store.count_all_users() | ||
| if count == 1 and RoomAlias.is_valid(room_identifier): |
There was a problem hiding this comment.
it feels very random to be doing this at this point. (Which is when the first user is registered? when they log in?). what if two users register at once? what if the first user didn't qualify for autojoining for some reason?
Can we not do this when the HS first starts, somehow?
There was a problem hiding this comment.
the only way to do it at start would be if we also autocreated an admin user, which in turn then means shared-secret registering them and then setting up their profile and 3pid and passwords etc correctly. agreed this would be cleaner, but this is intended as a fast fix to avoid having to do that in the provisioning process, on the basis that the first user who signs in is 99% always going to be the server admin, and if it isn't, then you can always repoint the alias.
There was a problem hiding this comment.
ok, fair enough, but still, having _join_user_to_room magically do something different if there is now exactly one registered user feels very magical.
can we pull out the is this the first user logic to register (where we only have to do it once per user, rather than once per user per room)? and have a separate _create_auto_join_room method?
There was a problem hiding this comment.
and have a separate _create_auto_join_room method
or just call room_creation_handler.create_room directly from the for r in self.hs.config.auto_join_rooms loop in register.
| ) | ||
| room_id = info["room_id"] | ||
|
|
||
| directory_handler = self.hs.get_handlers().directory_handler |
There was a problem hiding this comment.
can we not do the alias bits by passing room_alias_name to create_room ?
|
Also: we're not happy with this going in with zero tests to make sure it doesn't suddenly break in the future. It's going to be hard to sytest, but please can it have some unit tests to run the code? |
|
before I go rewrite this, it'd be good to get aligned on the proposed behaviour in general; the current implementation was a quick fix i suggested to @neilisfragile so we could get this feature out quickly rather than getting entangled with complicated provisioning/auth rejigs. |
|
What is |
|
HHS === modular.im |
|
Need to think about gdpr consent and ability to create rooms, could be dependent on element-hq/element-web#7168 |
|
Blocked on #4004 |
|
Blocked how? This and #4004 are unrelated changes |
|
It's currently not possible to create the room prior to consent, so the room is never created on GDPR compliant servers. I suppose you could instead tie room creation into the first user to consent, but given you are already working on moving consent earlier in the flow it seems sensible just to wait for it to land then this PR will magically work. |
| @@ -0,0 +1 @@ | |||
| First user should autocreate autojoin rooms | |||
There was a problem hiding this comment.
could you expand on this a bit so that it makes sense for users, please?
| #auto_join_rooms: | ||
| # - "#example:example.com" | ||
|
|
||
| # Have first user on server autocreate autojoin rooms |
There was a problem hiding this comment.
maybe explain what will happen if it is false, and conversely what happens if it is true?
| for r in self.hs.config.auto_join_rooms: | ||
| try: | ||
| if auto_create_rooms and RoomAlias.is_valid(r): | ||
| room_creation_handler = self.hs.get_room_creation_handler() |
There was a problem hiding this comment.
can you put this in the constructor?
There was a problem hiding this comment.
The first part needs to be on a request basis - I guess the self.hs.get_room_creation_handler() could but this is the only place it is used.
There was a problem hiding this comment.
I know it's the only place it's used, but we prefer to get the handlers in the constructors where possible, so that we detect problems earlier.
|
|
||
| for r in self.hs.config.auto_join_rooms: | ||
| try: | ||
| if auto_create_rooms and RoomAlias.is_valid(r): |
There was a problem hiding this comment.
if the alias is invalid, we silently ignore it? If you're going to sanity check it, put the sanity check in the config module, and raise a ConfigError if it's wrong.
| # try to create the room if we're the first user on the server | ||
| if self.hs.config.autocreate_auto_join_rooms: | ||
| count = yield self.store.count_all_users() | ||
| auto_create_rooms = count == 1 |
There was a problem hiding this comment.
you need to initialise auto_create_rooms for the case where autocreate_auto_join_rooms is False.
|
|
||
| @defer.inlineCallbacks | ||
| def _join_user_to_room(self, requester, room_identifier): | ||
|
|
There was a problem hiding this comment.
it was oversight I promise!
| ) | ||
| self.hs.get_macaroon_generator = Mock(return_value=self.macaroon_generator) | ||
| self.hs.handlers = RegistrationHandlers(self.hs) | ||
| # self.hs.handlers = RegistrationHandlers(self.hs) |
There was a problem hiding this comment.
if it's redundant, remove it, don't comment it out
| @defer.inlineCallbacks | ||
| def test_auto_create_auto_join_rooms(self): | ||
| room_alias_str = "#room:test" | ||
| self.hs.config.autocreate_auto_join_rooms = True |
There was a problem hiding this comment.
this seems to be the default; you could argue it should be set here for certainty but in that case let's have a comment. else just remove it
| self.assertEqual(len(rooms), 1) | ||
|
|
||
| @defer.inlineCallbacks | ||
| def test_auto_create_auto_join_rooms_with_no_rooms(self): |
There was a problem hiding this comment.
can we also have a UT with autocreate_auto_join_rooms=False please?
|
Could portions of this be used to auto-create a room for each user? Useful for creating notes channels, etc. for workflow-specific applications. |
|
@martindale sure, you could use the same ideas, in fact it's not far off what happens for the server notices room that backs GDPR and other consent mechanisms . This specific PR is to support a modular.im feature focusing purely on a shared room for all on the server. |
| @@ -1 +1 @@ | |||
| First user should autocreate autojoin rooms | |||
| Servers with auto join rooms, should autocreate those rooms when first user registers | |||
There was a problem hiding this comment.
s/auto join/auto-join/
s/should/will now/
s/first user/the first user/
possibly, s/autocreate/automatically create/
| from synapse.types import RoomAlias | ||
| from synapse.util.stringutils import random_string_with_symbols | ||
|
|
||
| from ._base import Config |
There was a problem hiding this comment.
it'd be nice to merge this into line 18 while you're at it (absolute imports are preferred over relative ones)
| self.auto_join_rooms = config.get("auto_join_rooms", []) | ||
| for room_alias in self.auto_join_rooms: | ||
| if not RoomAlias.is_valid(room_alias): | ||
| raise ConfigError('Invalid auto_join_rooms entry %s' % room_alias) |
There was a problem hiding this comment.
generally prefer % (room_alias, ) in case room_alias turns out to be a tuple
|
|
||
| # Have first user on server autocreate autojoin rooms | ||
| # Where auto_join_rooms are specified, setting this flag ensures that the | ||
| # the rooms exists by creating them when the first user on the |
| # the rooms exists by creating them when the first user on the | ||
| # homeserver registers. | ||
| # Setting to false means that if the rooms are not manually created, | ||
| # users cannot be auto joined since they do not exist. |
| for r in self.hs.config.auto_join_rooms: | ||
| try: | ||
| if auto_create_rooms and RoomAlias.is_valid(r): | ||
| room_creation_handler = self.hs.get_room_creation_handler() |
There was a problem hiding this comment.
I know it's the only place it's used, but we prefer to get the handlers in the constructors where possible, so that we detect problems earlier.
| RegistrationError, | ||
| SynapseError, | ||
| ) | ||
| from synapse.config._base import ConfigError |
There was a problem hiding this comment.
can you import this from synapse.config rather than the private _base module, please? (not that it's really appropriate to be raising ConfigErrors at runtime imho)
| if should_auto_create_rooms: | ||
| room_creation_handler = self.hs.get_room_creation_handler() | ||
| if self.hs.hostname != RoomAlias.from_string(r).domain: | ||
| raise ConfigError( |
There was a problem hiding this comment.
... and if we can't check it during startup, it's probably more appropriate to warn and move on than to 500 reject the whole registration.
| room_creation_handler = self.hs.get_room_creation_handler() | ||
| if self.hs.hostname != RoomAlias.from_string(r).domain: | ||
| raise ConfigError( | ||
| 'Cannot create room alias %s, it does not match server domain' |
There was a problem hiding this comment.
this is missing its argument too.
| def test_auto_create_auto_join_where_room_is_another_domain(self): | ||
| self.hs.config.auto_join_rooms = ["#room:another"] | ||
| frank = UserID.from_string("@frank:test") | ||
| res = yield self.handler.register(frank.localpart) |
There was a problem hiding this comment.
erm, why does this not throw an exception?
There was a problem hiding this comment.
bare except in the try statement
Have since removed the exception as per comments above
| @@ -1 +1 @@ | |||
| Servers with auto join rooms, should autocreate those rooms when first user registers | |||
| Servers with auto-join rooms, should automatically create those rooms when first user registers | |||
There was a problem hiding this comment.
Still some problems here:
s/, should/will now/
s/first user/the first user/
| from synapse.util.stringutils import random_string_with_symbols | ||
|
|
||
| from ._base import Config | ||
| from ._base import Config, ConfigError |
There was a problem hiding this comment.
again, absolute imports are preferred over relative ones. Please s/._base/synapse.config._base/
There was a problem hiding this comment.
right, misunderstood you
| 'Cannot create room alias %s, ' | ||
| 'it does not match server domain' % (r,) | ||
| ) | ||
| raise SynapseError() |
There was a problem hiding this comment.
why are we raising an exception here (and why does it have no message, but that's a side-issue)
There was a problem hiding this comment.
this is brain dead, sorry
| if self.hs.hostname != RoomAlias.from_string(r).domain: | ||
| raise ConfigError( | ||
| 'Cannot create room alias %s, it does not match server domain' | ||
| logger.warn( |
There was a problem hiding this comment.
fwiw (just a nit fyi): logger.warn is deprecated, prefer logger.warning
| 'Cannot create room alias %s, it does not match server domain' | ||
| logger.warn( | ||
| 'Cannot create room alias %s, ' | ||
| 'it does not match server domain' % (r,) |
There was a problem hiding this comment.
the logger methods automatically interpolate their arguments (with the benefit of doing it after checking if logging is enabled), so, you can write:
logger.warn('message with %s', r)
rather than using % explicitly
| try: | ||
| if should_auto_create_rooms: | ||
| room_creation_handler = self.hs.get_room_creation_handler() | ||
| if self.hs.hostname != RoomAlias.from_string(r).domain: |
There was a problem hiding this comment.
might be nice to do RoomAlias.from_string(r) once and use a local var, rather than twice
richvdh
left a comment
There was a problem hiding this comment.
lgtm other than the nits below. Also your CI is failing.
Co-Authored-By: neilisfragile <neil@matrix.org>
…autocreate_autojoin
Needed for HHS.