forked from MirrorHub/synapse
		
	Add ability to un-shadow-ban via the admin API. (#11347)
This commit is contained in:
		
					parent
					
						
							
								0dda1a7968
							
						
					
				
			
			
				commit
				
					
						24b61f379a
					
				
			
		
					 5 changed files with 53 additions and 12 deletions
				
			
		changelog.d
docs/admin_api
synapse
tests/rest/admin
							
								
								
									
										1
									
								
								changelog.d/11347.feature
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								changelog.d/11347.feature
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| Add admin API to un-shadow-ban a user. | ||||
|  | @ -948,7 +948,7 @@ The following fields are returned in the JSON response body: | |||
| See also the | ||||
| [Client-Server API Spec on pushers](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushers). | ||||
| 
 | ||||
| ## Shadow-banning users | ||||
| ## Controlling whether a user is shadow-banned | ||||
| 
 | ||||
| Shadow-banning is a useful tool for moderating malicious or egregiously abusive users. | ||||
| A shadow-banned users receives successful responses to their client-server API requests, | ||||
|  | @ -961,16 +961,22 @@ or broken behaviour for the client. A shadow-banned user will not receive any | |||
| notification and it is generally more appropriate to ban or kick abusive users. | ||||
| A shadow-banned user will be unable to contact anyone on the server. | ||||
| 
 | ||||
| The API is: | ||||
| To shadow-ban a user the API is: | ||||
| 
 | ||||
| ``` | ||||
| POST /_synapse/admin/v1/users/<user_id>/shadow_ban | ||||
| ``` | ||||
| 
 | ||||
| To un-shadow-ban a user the API is: | ||||
| 
 | ||||
| ``` | ||||
| DELETE /_synapse/admin/v1/users/<user_id>/shadow_ban | ||||
| ``` | ||||
| 
 | ||||
| To use it, you will need to authenticate by providing an `access_token` for a | ||||
| server admin: [Admin API](../usage/administration/admin_api) | ||||
| 
 | ||||
| An empty JSON dict is returned. | ||||
| An empty JSON dict is returned in both cases. | ||||
| 
 | ||||
| **Parameters** | ||||
| 
 | ||||
|  |  | |||
|  | @ -909,7 +909,7 @@ class UserTokenRestServlet(RestServlet): | |||
| 
 | ||||
| 
 | ||||
| class ShadowBanRestServlet(RestServlet): | ||||
|     """An admin API for shadow-banning a user. | ||||
|     """An admin API for controlling whether a user is shadow-banned. | ||||
| 
 | ||||
|     A shadow-banned users receives successful responses to their client-server | ||||
|     API requests, but the events are not propagated into rooms. | ||||
|  | @ -917,11 +917,19 @@ class ShadowBanRestServlet(RestServlet): | |||
|     Shadow-banning a user should be used as a tool of last resort and may lead | ||||
|     to confusing or broken behaviour for the client. | ||||
| 
 | ||||
|     Example: | ||||
|     Example of shadow-banning a user: | ||||
| 
 | ||||
|         POST /_synapse/admin/v1/users/@test:example.com/shadow_ban | ||||
|         {} | ||||
| 
 | ||||
|         200 OK | ||||
|         {} | ||||
| 
 | ||||
|     Example of removing a user from being shadow-banned: | ||||
| 
 | ||||
|         DELETE /_synapse/admin/v1/users/@test:example.com/shadow_ban | ||||
|         {} | ||||
| 
 | ||||
|         200 OK | ||||
|         {} | ||||
|     """ | ||||
|  | @ -945,6 +953,18 @@ class ShadowBanRestServlet(RestServlet): | |||
| 
 | ||||
|         return 200, {} | ||||
| 
 | ||||
|     async def on_DELETE( | ||||
|         self, request: SynapseRequest, user_id: str | ||||
|     ) -> Tuple[int, JsonDict]: | ||||
|         await assert_requester_is_admin(self.auth, request) | ||||
| 
 | ||||
|         if not self.hs.is_mine_id(user_id): | ||||
|             raise SynapseError(400, "Only local users can be shadow-banned") | ||||
| 
 | ||||
|         await self.store.set_shadow_banned(UserID.from_string(user_id), False) | ||||
| 
 | ||||
|         return 200, {} | ||||
| 
 | ||||
| 
 | ||||
| class RateLimitRestServlet(RestServlet): | ||||
|     """An admin API to override ratelimiting for an user. | ||||
|  |  | |||
|  | @ -476,7 +476,7 @@ class RegistrationWorkerStore(CacheInvalidationWorkerStore): | |||
|             shadow_banned: true iff the user is to be shadow-banned, false otherwise. | ||||
|         """ | ||||
| 
 | ||||
|         def set_shadow_banned_txn(txn): | ||||
|         def set_shadow_banned_txn(txn: LoggingTransaction) -> None: | ||||
|             user_id = user.to_string() | ||||
|             self.db_pool.simple_update_one_txn( | ||||
|                 txn, | ||||
|  |  | |||
|  | @ -3592,31 +3592,34 @@ class ShadowBanRestTestCase(unittest.HomeserverTestCase): | |||
|             self.other_user | ||||
|         ) | ||||
| 
 | ||||
|     def test_no_auth(self): | ||||
|     @parameterized.expand(["POST", "DELETE"]) | ||||
|     def test_no_auth(self, method: str): | ||||
|         """ | ||||
|         Try to get information of an user without authentication. | ||||
|         """ | ||||
|         channel = self.make_request("POST", self.url) | ||||
|         channel = self.make_request(method, self.url) | ||||
|         self.assertEqual(401, channel.code, msg=channel.json_body) | ||||
|         self.assertEqual(Codes.MISSING_TOKEN, channel.json_body["errcode"]) | ||||
| 
 | ||||
|     def test_requester_is_not_admin(self): | ||||
|     @parameterized.expand(["POST", "DELETE"]) | ||||
|     def test_requester_is_not_admin(self, method: str): | ||||
|         """ | ||||
|         If the user is not a server admin, an error is returned. | ||||
|         """ | ||||
|         other_user_token = self.login("user", "pass") | ||||
| 
 | ||||
|         channel = self.make_request("POST", self.url, access_token=other_user_token) | ||||
|         channel = self.make_request(method, self.url, access_token=other_user_token) | ||||
|         self.assertEqual(403, channel.code, msg=channel.json_body) | ||||
|         self.assertEqual(Codes.FORBIDDEN, channel.json_body["errcode"]) | ||||
| 
 | ||||
|     def test_user_is_not_local(self): | ||||
|     @parameterized.expand(["POST", "DELETE"]) | ||||
|     def test_user_is_not_local(self, method: str): | ||||
|         """ | ||||
|         Tests that shadow-banning for a user that is not a local returns a 400 | ||||
|         """ | ||||
|         url = "/_synapse/admin/v1/whois/@unknown_person:unknown_domain" | ||||
| 
 | ||||
|         channel = self.make_request("POST", url, access_token=self.admin_user_tok) | ||||
|         channel = self.make_request(method, url, access_token=self.admin_user_tok) | ||||
|         self.assertEqual(400, channel.code, msg=channel.json_body) | ||||
| 
 | ||||
|     def test_success(self): | ||||
|  | @ -3636,6 +3639,17 @@ class ShadowBanRestTestCase(unittest.HomeserverTestCase): | |||
|         result = self.get_success(self.store.get_user_by_access_token(other_user_token)) | ||||
|         self.assertTrue(result.shadow_banned) | ||||
| 
 | ||||
|         # Un-shadow-ban the user. | ||||
|         channel = self.make_request( | ||||
|             "DELETE", self.url, access_token=self.admin_user_tok | ||||
|         ) | ||||
|         self.assertEqual(200, channel.code, msg=channel.json_body) | ||||
|         self.assertEqual({}, channel.json_body) | ||||
| 
 | ||||
|         # Ensure the user is no longer shadow-banned (and the cache was cleared). | ||||
|         result = self.get_success(self.store.get_user_by_access_token(other_user_token)) | ||||
|         self.assertFalse(result.shadow_banned) | ||||
| 
 | ||||
| 
 | ||||
| class RateLimitTestCase(unittest.HomeserverTestCase): | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue