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