@@ -0,0 +1 @@ | |||
Add support for shadow-banning users (ignoring any message send requests). |
@@ -213,6 +213,7 @@ class Auth(object): | |||
user = user_info["user"] | |||
token_id = user_info["token_id"] | |||
is_guest = user_info["is_guest"] | |||
shadow_banned = user_info["shadow_banned"] | |||
# Deny the request if the user account has expired. | |||
if self._account_validity.enabled and not allow_expired: | |||
@@ -252,7 +253,12 @@ class Auth(object): | |||
opentracing.set_tag("device_id", device_id) | |||
return synapse.types.create_requester( | |||
user, token_id, is_guest, device_id, app_service=app_service | |||
user, | |||
token_id, | |||
is_guest, | |||
shadow_banned, | |||
device_id, | |||
app_service=app_service, | |||
) | |||
except KeyError: | |||
raise MissingClientTokenError() | |||
@@ -297,6 +303,7 @@ class Auth(object): | |||
dict that includes: | |||
`user` (UserID) | |||
`is_guest` (bool) | |||
`shadow_banned` (bool) | |||
`token_id` (int|None): access token id. May be None if guest | |||
`device_id` (str|None): device corresponding to access token | |||
Raises: | |||
@@ -356,6 +363,7 @@ class Auth(object): | |||
ret = { | |||
"user": user, | |||
"is_guest": True, | |||
"shadow_banned": False, | |||
"token_id": None, | |||
# all guests get the same device id | |||
"device_id": GUEST_DEVICE_ID, | |||
@@ -365,6 +373,7 @@ class Auth(object): | |||
ret = { | |||
"user": user, | |||
"is_guest": False, | |||
"shadow_banned": False, | |||
"token_id": None, | |||
"device_id": None, | |||
} | |||
@@ -488,6 +497,7 @@ class Auth(object): | |||
"user": UserID.from_string(ret.get("name")), | |||
"token_id": ret.get("token_id", None), | |||
"is_guest": False, | |||
"shadow_banned": ret.get("shadow_banned"), | |||
"device_id": ret.get("device_id"), | |||
"valid_until_ms": ret.get("valid_until_ms"), | |||
} | |||
@@ -142,6 +142,7 @@ class RegistrationHandler(BaseHandler): | |||
address=None, | |||
bind_emails=[], | |||
by_admin=False, | |||
shadow_banned=False, | |||
): | |||
"""Registers a new client on the server. | |||
@@ -159,6 +160,7 @@ class RegistrationHandler(BaseHandler): | |||
bind_emails (List[str]): list of emails to bind to this account. | |||
by_admin (bool): True if this registration is being made via the | |||
admin api, otherwise False. | |||
shadow_banned (bool): Shadow-ban the created user. | |||
Returns: | |||
str: user_id | |||
Raises: | |||
@@ -194,6 +196,7 @@ class RegistrationHandler(BaseHandler): | |||
admin=admin, | |||
user_type=user_type, | |||
address=address, | |||
shadow_banned=shadow_banned, | |||
) | |||
if self.hs.config.user_directory_search_all_users: | |||
@@ -224,6 +227,7 @@ class RegistrationHandler(BaseHandler): | |||
make_guest=make_guest, | |||
create_profile_with_displayname=default_display_name, | |||
address=address, | |||
shadow_banned=shadow_banned, | |||
) | |||
# Successfully registered | |||
@@ -529,6 +533,7 @@ class RegistrationHandler(BaseHandler): | |||
admin=False, | |||
user_type=None, | |||
address=None, | |||
shadow_banned=False, | |||
): | |||
"""Register user in the datastore. | |||
@@ -546,6 +551,7 @@ class RegistrationHandler(BaseHandler): | |||
user_type (str|None): type of user. One of the values from | |||
api.constants.UserTypes, or None for a normal user. | |||
address (str|None): the IP address used to perform the registration. | |||
shadow_banned (bool): Whether to shadow-ban the user | |||
Returns: | |||
Awaitable | |||
@@ -561,6 +567,7 @@ class RegistrationHandler(BaseHandler): | |||
admin=admin, | |||
user_type=user_type, | |||
address=address, | |||
shadow_banned=shadow_banned, | |||
) | |||
else: | |||
return self.store.register_user( | |||
@@ -572,6 +579,7 @@ class RegistrationHandler(BaseHandler): | |||
create_profile_with_displayname=create_profile_with_displayname, | |||
admin=admin, | |||
user_type=user_type, | |||
shadow_banned=shadow_banned, | |||
) | |||
async def register_device( | |||
@@ -44,6 +44,7 @@ class ReplicationRegisterServlet(ReplicationEndpoint): | |||
admin, | |||
user_type, | |||
address, | |||
shadow_banned, | |||
): | |||
""" | |||
Args: | |||
@@ -60,6 +61,7 @@ class ReplicationRegisterServlet(ReplicationEndpoint): | |||
user_type (str|None): type of user. One of the values from | |||
api.constants.UserTypes, or None for a normal user. | |||
address (str|None): the IP address used to perform the regitration. | |||
shadow_banned (bool): Whether to shadow-ban the user | |||
""" | |||
return { | |||
"password_hash": password_hash, | |||
@@ -70,6 +72,7 @@ class ReplicationRegisterServlet(ReplicationEndpoint): | |||
"admin": admin, | |||
"user_type": user_type, | |||
"address": address, | |||
"shadow_banned": shadow_banned, | |||
} | |||
async def _handle_request(self, request, user_id): | |||
@@ -87,6 +90,7 @@ class ReplicationRegisterServlet(ReplicationEndpoint): | |||
admin=content["admin"], | |||
user_type=content["user_type"], | |||
address=content["address"], | |||
shadow_banned=content["shadow_banned"], | |||
) | |||
return 200, {} | |||
@@ -304,7 +304,7 @@ class RegistrationWorkerStore(SQLBaseStore): | |||
def _query_for_auth(self, txn, token): | |||
sql = ( | |||
"SELECT users.name, users.is_guest, access_tokens.id as token_id," | |||
"SELECT users.name, users.is_guest, users.shadow_banned, access_tokens.id as token_id," | |||
" access_tokens.device_id, access_tokens.valid_until_ms" | |||
" FROM users" | |||
" INNER JOIN access_tokens on users.name = access_tokens.user_id" | |||
@@ -952,6 +952,7 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
create_profile_with_displayname=None, | |||
admin=False, | |||
user_type=None, | |||
shadow_banned=False, | |||
): | |||
"""Attempts to register an account. | |||
@@ -968,6 +969,8 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
admin (boolean): is an admin user? | |||
user_type (str|None): type of user. One of the values from | |||
api.constants.UserTypes, or None for a normal user. | |||
shadow_banned (bool): Whether the user is shadow-banned, | |||
i.e. they may be told their requests succeeded but we ignore them. | |||
Raises: | |||
StoreError if the user_id could not be registered. | |||
@@ -986,6 +989,7 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
create_profile_with_displayname, | |||
admin, | |||
user_type, | |||
shadow_banned, | |||
) | |||
def _register_user( | |||
@@ -999,6 +1003,7 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
create_profile_with_displayname, | |||
admin, | |||
user_type, | |||
shadow_banned, | |||
): | |||
user_id_obj = UserID.from_string(user_id) | |||
@@ -1028,6 +1033,7 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
"appservice_id": appservice_id, | |||
"admin": 1 if admin else 0, | |||
"user_type": user_type, | |||
"shadow_banned": shadow_banned, | |||
}, | |||
) | |||
else: | |||
@@ -1042,6 +1048,7 @@ class RegistrationStore(RegistrationBackgroundUpdateStore): | |||
"appservice_id": appservice_id, | |||
"admin": 1 if admin else 0, | |||
"user_type": user_type, | |||
"shadow_banned": shadow_banned, | |||
}, | |||
) | |||
@@ -0,0 +1,18 @@ | |||
/* Copyright 2020 The Matrix.org Foundation C.I.C | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
-- A shadow-banned user may be told that their requests succeeded when they were | |||
-- actually ignored. | |||
ALTER TABLE users ADD COLUMN shadow_banned BOOLEAN; |
@@ -51,7 +51,15 @@ JsonDict = Dict[str, Any] | |||
class Requester( | |||
namedtuple( | |||
"Requester", ["user", "access_token_id", "is_guest", "device_id", "app_service"] | |||
"Requester", | |||
[ | |||
"user", | |||
"access_token_id", | |||
"is_guest", | |||
"shadow_banned", | |||
"device_id", | |||
"app_service", | |||
], | |||
) | |||
): | |||
""" | |||
@@ -62,6 +70,7 @@ class Requester( | |||
access_token_id (int|None): *ID* of the access token used for this | |||
request, or None if it came via the appservice API or similar | |||
is_guest (bool): True if the user making this request is a guest user | |||
shadow_banned (bool): True if the user making this request has been shadow-banned. | |||
device_id (str|None): device_id which was set at authentication time | |||
app_service (ApplicationService|None): the AS requesting on behalf of the user | |||
""" | |||
@@ -77,6 +86,7 @@ class Requester( | |||
"user_id": self.user.to_string(), | |||
"access_token_id": self.access_token_id, | |||
"is_guest": self.is_guest, | |||
"shadow_banned": self.shadow_banned, | |||
"device_id": self.device_id, | |||
"app_server_id": self.app_service.id if self.app_service else None, | |||
} | |||
@@ -101,13 +111,19 @@ class Requester( | |||
user=UserID.from_string(input["user_id"]), | |||
access_token_id=input["access_token_id"], | |||
is_guest=input["is_guest"], | |||
shadow_banned=input["shadow_banned"], | |||
device_id=input["device_id"], | |||
app_service=appservice, | |||
) | |||
def create_requester( | |||
user_id, access_token_id=None, is_guest=False, device_id=None, app_service=None | |||
user_id, | |||
access_token_id=None, | |||
is_guest=False, | |||
shadow_banned=False, | |||
device_id=None, | |||
app_service=None, | |||
): | |||
""" | |||
Create a new ``Requester`` object | |||
@@ -117,6 +133,7 @@ def create_requester( | |||
access_token_id (int|None): *ID* of the access token used for this | |||
request, or None if it came via the appservice API or similar | |||
is_guest (bool): True if the user making this request is a guest user | |||
shadow_banned (bool): True if the user making this request is shadow-banned. | |||
device_id (str|None): device_id which was set at authentication time | |||
app_service (ApplicationService|None): the AS requesting on behalf of the user | |||
@@ -125,7 +142,9 @@ def create_requester( | |||
""" | |||
if not isinstance(user_id, UserID): | |||
user_id = UserID.from_string(user_id) | |||
return Requester(user_id, access_token_id, is_guest, device_id, app_service) | |||
return Requester( | |||
user_id, access_token_id, is_guest, shadow_banned, device_id, app_service | |||
) | |||
def get_domain_from_id(string): | |||
@@ -38,7 +38,7 @@ class CleanupExtremBackgroundUpdateStoreTestCase(HomeserverTestCase): | |||
# Create a test user and room | |||
self.user = UserID("alice", "test") | |||
self.requester = Requester(self.user, None, False, None, None) | |||
self.requester = Requester(self.user, None, False, False, None, None) | |||
info, _ = self.get_success(self.room_creator.create_room(self.requester, {})) | |||
self.room_id = info["room_id"] | |||
@@ -260,7 +260,7 @@ class CleanupExtremDummyEventsTestCase(HomeserverTestCase): | |||
# Create a test user and room | |||
self.user = UserID.from_string(self.register_user("user1", "password")) | |||
self.token1 = self.login("user1", "password") | |||
self.requester = Requester(self.user, None, False, None, None) | |||
self.requester = Requester(self.user, None, False, False, None, None) | |||
info, _ = self.get_success(self.room_creator.create_room(self.requester, {})) | |||
self.room_id = info["room_id"] | |||
self.event_creator = homeserver.get_event_creation_handler() | |||
@@ -27,7 +27,7 @@ class ExtremStatisticsTestCase(HomeserverTestCase): | |||
room_creator = self.hs.get_room_creation_handler() | |||
user = UserID("alice", "test") | |||
requester = Requester(user, None, False, None, None) | |||
requester = Requester(user, None, False, False, None, None) | |||
# Real events, forward extremities | |||
events = [(3, 2), (6, 2), (4, 6)] | |||
@@ -187,7 +187,7 @@ class CurrentStateMembershipUpdateTestCase(unittest.HomeserverTestCase): | |||
# Now let's create a room, which will insert a membership | |||
user = UserID("alice", "test") | |||
requester = Requester(user, None, False, None, None) | |||
requester = Requester(user, None, False, False, None, None) | |||
self.get_success(self.room_creator.create_room(requester, {})) | |||
# Register the background update to run again. | |||
@@ -42,7 +42,7 @@ class MessageAcceptTests(unittest.HomeserverTestCase): | |||
) | |||
user_id = UserID("us", "test") | |||
our_user = Requester(user_id, None, False, None, None) | |||
our_user = Requester(user_id, None, False, False, None, None) | |||
room_creator = self.homeserver.get_room_creation_handler() | |||
room_deferred = ensureDeferred( | |||
room_creator.create_room( | |||
@@ -250,7 +250,11 @@ class HomeserverTestCase(TestCase): | |||
async def get_user_by_req(request, allow_guest=False, rights="access"): | |||
return create_requester( | |||
UserID.from_string(self.helper.auth_user_id), 1, False, None | |||
UserID.from_string(self.helper.auth_user_id), | |||
1, | |||
False, | |||
False, | |||
None, | |||
) | |||
self.hs.get_auth().get_user_by_req = get_user_by_req | |||
@@ -540,7 +544,7 @@ class HomeserverTestCase(TestCase): | |||
""" | |||
event_creator = self.hs.get_event_creation_handler() | |||
secrets = self.hs.get_secrets() | |||
requester = Requester(user, None, False, None, None) | |||
requester = Requester(user, None, False, False, None, None) | |||
event, context = self.get_success( | |||
event_creator.create_event( | |||