1# Copyright (C) 2011-2020 by the Free Software Foundation, Inc. 2# 3# This file is part of GNU Mailman. 4# 5# GNU Mailman is free software: you can redistribute it and/or modify it under 6# the terms of the GNU General Public License as published by the Free 7# Software Foundation, either version 3 of the License, or (at your option) 8# any later version. 9# 10# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT 11# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13# more details. 14# 15# You should have received a copy of the GNU General Public License along with 16# GNU Mailman. If not, see <https://www.gnu.org/licenses/>. 17 18"""Test users.""" 19 20import unittest 21 22from mailman.app.lifecycle import create_list 23from mailman.config import config 24from mailman.database.transaction import transaction 25from mailman.interfaces.address import ( 26 AddressAlreadyLinkedError, AddressNotLinkedError) 27from mailman.interfaces.member import MemberRole 28from mailman.interfaces.user import UnverifiedAddressError 29from mailman.interfaces.usermanager import IUserManager 30from mailman.model.preferences import Preferences 31from mailman.testing.helpers import set_preferred 32from mailman.testing.layers import ConfigLayer 33from sqlalchemy import inspect 34from zope.component import getUtility 35 36 37class TestUser(unittest.TestCase): 38 """Test users.""" 39 40 layer = ConfigLayer 41 42 def setUp(self): 43 self._manager = getUtility(IUserManager) 44 self._mlist = create_list('test@example.com') 45 self._anne = self._manager.create_user( 46 'anne@example.com', 'Anne Person') 47 set_preferred(self._anne) 48 49 def test_preferred_address_memberships(self): 50 self._mlist.subscribe(self._anne) 51 memberships = list(self._anne.memberships.members) 52 self.assertEqual(len(memberships), 1) 53 self.assertEqual(memberships[0].address.email, 'anne@example.com') 54 self.assertEqual(memberships[0].user, self._anne) 55 addresses = list(self._anne.memberships.addresses) 56 self.assertEqual(len(addresses), 1) 57 self.assertEqual(addresses[0].email, 'anne@example.com') 58 59 def test_preferred_and_address_memberships(self): 60 self._mlist.subscribe(self._anne) 61 aperson = self._anne.register('aperson@example.com') 62 self._mlist.subscribe(aperson) 63 memberships = list(self._anne.memberships.members) 64 self.assertEqual(len(memberships), 2) 65 self.assertEqual(set(member.address.email for member in memberships), 66 set(['anne@example.com', 'aperson@example.com'])) 67 self.assertEqual(memberships[0].user, memberships[1].user) 68 self.assertEqual(memberships[0].user, self._anne) 69 emails = set(address.email 70 for address in self._anne.memberships.addresses) 71 self.assertEqual(len(emails), 2) 72 self.assertEqual(emails, 73 set(['anne@example.com', 'aperson@example.com'])) 74 75 def test_uid_is_immutable(self): 76 with self.assertRaises(AttributeError): 77 self._anne.user_id = 'foo' 78 79 def test_addresses_may_only_be_linked_to_one_user(self): 80 user = self._manager.create_user() 81 # Anne's preferred address is already linked to her. 82 with self.assertRaises(AddressAlreadyLinkedError) as cm: 83 user.link(self._anne.preferred_address) 84 self.assertEqual(cm.exception.address, self._anne.preferred_address) 85 86 def test_unlink_from_address_not_linked_to(self): 87 # You cannot unlink an address from a user if that address is not 88 # already linked to the user. 89 user = self._manager.create_user() 90 with self.assertRaises(AddressNotLinkedError) as cm: 91 user.unlink(self._anne.preferred_address) 92 self.assertEqual(cm.exception.address, self._anne.preferred_address) 93 94 def test_unlink_address_which_is_not_linked(self): 95 # You cannot unlink an address which is not linked to any user. 96 address = self._manager.create_address('bart@example.com') 97 user = self._manager.create_user() 98 with self.assertRaises(AddressNotLinkedError) as cm: 99 user.unlink(address) 100 self.assertEqual(cm.exception.address, address) 101 102 def test_set_unverified_preferred_address(self): 103 # A user's preferred address cannot be set to an unverified address. 104 new_preferred = self._manager.create_address( 105 'anne.person@example.com') 106 with self.assertRaises(UnverifiedAddressError) as cm: 107 self._anne.preferred_address = new_preferred 108 self.assertEqual(cm.exception.address, new_preferred) 109 110 def test_preferences_deletion_on_user_deletion(self): 111 # LP: #1418276 - deleting a user did not delete their preferences. 112 with transaction(): 113 # This has to happen in a transaction so that both the user and 114 # the preferences objects get valid ids. 115 user = self._manager.create_user() 116 # The user's preference is in the database. 117 preferences = config.db.store.query(Preferences).filter_by( 118 id=user.preferences.id) 119 self.assertEqual(preferences.count(), 1) 120 self._manager.delete_user(user) 121 # The user's preference has been deleted. 122 preferences = config.db.store.query(Preferences).filter_by( 123 id=user.preferences.id) 124 self.assertEqual(preferences.count(), 0) 125 126 def test_absorb_not_a_user(self): 127 bart = self._manager.create_address('bart@example.com') 128 self.assertRaises(TypeError, self._anne.absorb, bart) 129 130 def test_absorb_addresses(self): 131 # Absorbing the user absorbs all of the users addresses. I.e. they 132 # are relinked to the absorbing user. 133 anne_preferred = self._anne.preferred_address 134 with transaction(): 135 # This has to happen in a transaction so that both the user and 136 # the preferences objects get valid ids. 137 bart = self._manager.create_user('bart@example.com', 'Bart Person') 138 bart_secondary = self._manager.create_address( 139 'bart.person@example.com') 140 bart.link(bart_secondary) 141 # Absorb the Bart user into Anne. 142 self._anne.absorb(bart) 143 # Bart's primary and secondary addresses are now linked to Anne. 144 anne_addresses = list( 145 address.email for address in self._anne.addresses) 146 self.assertIn('bart@example.com', anne_addresses) 147 self.assertIn('bart.person@example.com', anne_addresses) 148 # Anne's preferred address shouldn't change. 149 self.assertEqual(self._anne.preferred_address, anne_preferred) 150 # Check the reverse linkages by getting Bart's addresses from the user 151 # manager. They should both point back to the Anne user. 152 self.assertEqual( 153 self._manager.get_user('bart@example.com'), self._anne) 154 self.assertEqual( 155 self._manager.get_user('bart.person@example.com'), self._anne) 156 # The Bart user has been deleted. 157 self.assertIsNone(self._manager.get_user_by_id(bart.user_id)) 158 159 def test_absorb_memberships(self): 160 # When a user is absorbed, all of their user-subscribed memberships 161 # are relinked to the absorbing user. 162 mlist2 = create_list('test2@example.com') 163 mlist3 = create_list('test3@example.com') 164 with transaction(): 165 # This has to happen in a transaction so that both the user and 166 # the preferences objects get valid ids. 167 bart = self._manager.create_user('bart@example.com', 'Bart Person') 168 set_preferred(bart) 169 # Subscribe both users to self._mlist. 170 self._mlist.subscribe(self._anne, MemberRole.member) 171 self._mlist.subscribe(bart, MemberRole.moderator) 172 # Subscribe only Bart to mlist2. 173 mlist2.subscribe(bart, MemberRole.owner) 174 # Subscribe only Bart's address to mlist3. 175 mlist3.subscribe(bart.preferred_address, MemberRole.moderator) 176 # There are now 4 memberships, one with Anne two with Bart's user and 177 # one with Bart's address. 178 all_members = list(self._manager.members) 179 self.assertEqual(len(all_members), 4, all_members) 180 # Do the absorption. 181 self._anne.absorb(bart) 182 # The Bart user has been deleted, leaving only the Anne user in the 183 # user manager. 184 all_users = list(self._manager.users) 185 self.assertEqual(len(all_users), 1) 186 self.assertEqual(all_users[0], self._anne) 187 # There are no leftover memberships for user Bart. Anne owns all the 188 # memberships. 189 all_members = list(self._manager.members) 190 self.assertEqual(len(all_members), 4, all_members) 191 self.assertEqual(self._anne.memberships.member_count, 4) 192 memberships = {(member.list_id, member.role): member 193 for member in self._anne.memberships.members} 194 # Note that Anne is now both a member and moderator of the test list. 195 self.assertEqual(set(memberships), set([ 196 ('test.example.com', MemberRole.member), 197 ('test.example.com', MemberRole.moderator), 198 ('test2.example.com', MemberRole.owner), 199 ('test3.example.com', MemberRole.moderator), 200 ])) 201 # Both of Bart's previous user subscriptions are now transferred to 202 # the Anne user. 203 self.assertEqual( 204 memberships[('test.example.com', MemberRole.moderator)].address, 205 self._anne.preferred_address) 206 self.assertEqual( 207 memberships[('test2.example.com', MemberRole.owner)].address, 208 self._anne.preferred_address) 209 # Bart's address was subscribed; it must not have been changed. Of 210 # course, Anne now controls bart@example.com. 211 key = ('test3.example.com', MemberRole.moderator) 212 self.assertEqual(memberships[key].address.email, 'bart@example.com') 213 self.assertEqual(self._manager.get_user('bart@example.com'), 214 self._anne) 215 216 def test_absorb_duplicates(self): 217 # Duplicate memberships, where the list-id and role match, are 218 # ignored. Here we subscribe Anne to the test list as a member, and 219 # Bart as both a member and an owner. Anne's member membership 220 # remains unchanged, but she gains an owner membership. 221 with transaction(): 222 bart = self._manager.create_user('bart@example.com') 223 set_preferred(bart) 224 self._mlist.subscribe(self._anne, MemberRole.member) 225 self._mlist.subscribe(bart, MemberRole.member) 226 self._mlist.subscribe(bart, MemberRole.owner) 227 # There are now three memberships. 228 all_members = list(self._manager.members) 229 self.assertEqual(len(all_members), 3, all_members) 230 # Do the absorption. 231 self._anne.absorb(bart) 232 # There are now only 2 memberships, both owned by Anne. 233 all_members = list(self._manager.members) 234 self.assertEqual(len(all_members), 2, all_members) 235 memberships = set([ 236 (member.list_id, member.role, member.address.email) 237 for member in all_members 238 ]) 239 self.assertEqual(memberships, set([ 240 ('test.example.com', MemberRole.member, 'anne@example.com'), 241 ('test.example.com', MemberRole.owner, 'anne@example.com'), 242 ])) 243 244 def test_absorb_preferences(self): 245 with transaction(): 246 # This has to happen in a transaction so that both the user and 247 # the preferences objects get valid ids. 248 bart = self._manager.create_user('bart@example.com', 'Bart Person') 249 bart.preferences.acknowledge_posts = True 250 self.assertIsNone(self._anne.preferences.acknowledge_posts) 251 self._anne.absorb(bart) 252 self.assertEqual(self._anne.preferences.acknowledge_posts, True) 253 # Check that Bart's preferences were deleted (requires a DB flush). 254 config.db.store.flush() 255 self.assertTrue(inspect(bart.preferences).deleted) 256 257 def test_absorb_properties(self): 258 properties = { 259 'password': 'dummy', 260 'is_server_owner': True 261 } 262 with transaction(): 263 # This has to happen in a transaction so that both the user and 264 # the preferences objects get valid ids. 265 bart = self._manager.create_user('bart@example.com', 'Bart Person') 266 for name, value in properties.items(): 267 setattr(bart, name, value) 268 self._anne.absorb(bart) 269 for name, value in properties.items(): 270 self.assertEqual(getattr(self._anne, name), value) 271 # This was not empty so it must not have been overwritten. 272 self.assertEqual(self._anne.display_name, 'Anne Person') 273 274 def test_absorb_display_name(self): 275 # Bart has no display name, but once he absorbs Cate, he gains her 276 # display_name. 277 with transaction(): 278 bart = self._manager.create_user('bart@example.com') 279 cate = self._manager.create_user('cate@example.com', 'Cate Person') 280 self.assertEqual(bart.display_name, '') 281 bart.absorb(cate) 282 self.assertEqual(bart.display_name, 'Cate Person') 283 284 def test_absorb_delete_user(self): 285 # Make sure the user was deleted after being absorbed. 286 with transaction(): 287 # This has to happen in a transaction so that both the user and 288 # the preferences objects get valid ids. 289 bart = self._manager.create_user('bart@example.com', 'Bart Person') 290 bart_user_id = bart.user_id 291 self._anne.absorb(bart) 292 self.assertIsNone(self._manager.get_user_by_id(bart_user_id)) 293 294 def test_absorb_self(self): 295 # Absorbing oneself should be a no-op (it must not delete the user). 296 self._mlist.subscribe(self._anne) 297 self._anne.absorb(self._anne) 298 new_anne = self._manager.get_user_by_id(self._anne.user_id) 299 self.assertIsNotNone(new_anne) 300 self.assertEqual( 301 [address.email for address in new_anne.addresses], 302 ['anne@example.com']) 303 self.assertEqual(new_anne.memberships.member_count, 1) 304 305 def test_add_duplicate_address(self): 306 # This duplicates #476 307 # We first create an address. 308 addr = self._manager.create_address('addr@example.com') 309 # Now we create a user with a different address and try to 310 # add this address to it with a different Case. 311 self._anne.register('Addr@example.com') 312 # Now, there should be only a single record for the Address, we make 313 # sure of this by trying to get the address. 314 new_addr = self._manager.get_address('addr@example.com') 315 # Both the addresses should be same. 316 self.assertEqual(addr.id, new_addr.id) 317