Bump version to 1.3.6 and update NEWS.rst for 1.3.5
[mailman-postorious.git] / src / postorius / tests / mailman_api_tests / test_user.py
blobe706e5f256de95da808d7639d515d0dfb425cb21
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2016-2021 by the Free Software Foundation, Inc.
4 # This file is part of Postorius.
6 # Postorius is free software: you can redistribute it and/or modify it under
7 # the terms of the GNU General Public License as published by the Free
8 # Software Foundation, either version 3 of the License, or (at your option)
9 # any later version.
10 # Postorius 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.
15 # You should have received a copy of the GNU General Public License along with
16 # Postorius. If not, see <http://www.gnu.org/licenses/>.
19 from django.contrib.auth.models import User
20 from django.test.utils import override_settings
21 from django.urls import reverse
23 from allauth.account.models import EmailAddress
24 from django_mailman3.lib.mailman import get_mailman_user
26 from postorius.forms import ChangeSubscriptionForm, UserPreferences
27 from postorius.models import Mailman404Error, MailmanUser
28 from postorius.tests.utils import ViewTestCase
31 class MailmanUserTest(ViewTestCase):
32 """
33 Tests for the mailman user preferences settings page.
34 """
36 def setUp(self):
37 super(MailmanUserTest, self).setUp()
38 self.domain = self.mm_client.create_domain('example.com')
39 self.foo_list = self.domain.create_list('foo')
40 self.foo_list.send_welcome_message = False
41 self.user = User.objects.create_user(
42 'user', 'user@example.com', 'testpass')
43 EmailAddress.objects.create(
44 user=self.user, email=self.user.email, verified=True, primary=True)
45 self.mm_user = get_mailman_user(self.user)
47 def test_address_preferences_not_logged_in(self):
48 self.assertRedirectsToLogin(reverse('user_address_preferences'))
50 def test_subscriptions_not_logged_in(self):
51 self.assertRedirectsToLogin(reverse('ps_user_profile'))
53 def test_subscriptions_logged_in(self):
54 self.client.login(username='user', password='testpass')
55 response = self.client.get(reverse('ps_user_profile'))
56 self.assertEqual(response.status_code, 200)
58 def test_address_based_preferences(self):
59 self.client.login(username='user', password='testpass')
60 self.mm_user.add_address('user2@example.com')
61 self.mm_user.add_address('user3@example.com')
62 response = self.client.get(reverse('user_address_preferences'))
63 self.assertEqual(response.status_code, 200)
64 self.assertEqual(len(response.context["formset"]), 3)
66 # this test needs re-thinking with the way we've hacked preferences
67 # it has been disabled for now
68 # def test_preferences_none(self):
69 # # Mailman does not accept None values for boolean preferences. When
70 # # those preferences are unset, they must be excluded from the POST
71 # # data.
72 # self.client.login(username='user', password='testpass')
73 # self.foo_list.subscribe(self.user.email, pre_verified=True,
74 # pre_confirmed=True, pre_approved=True)
75 # prefs_with_none = (
76 # 'receive_own_postings', 'acknowledge_posts',
77 # 'hide_address', 'receive_list_copy',
78 # )
79 # # Prepare a Preferences subclass that will check the POST data
80 # import mailmanclient._client
82 # class TestingPrefs(mailmanclient._client.Preferences):
83 # testcase = self
85 # def save(self):
86 # for pref in prefs_with_none:
87 # self.testcase.assertNotIn(pref, self._changed_rest_data)
88 # # Now check the relevant URLs
89 # with patch('mailmanclient._client.Preferences') as pref_class:
90 # pref_class.side_effect = TestingPrefs
91 # # Simple forms
92 # for url in (
93 # reverse('user_mailmansettings'),
94 # reverse('user_list_options',
95 # args=[self.foo_list.list_id]),
96 # ):
97 # response = self.client.post(
98 # url, dict((pref, None) for pref in prefs_with_none))
99 # self.assertEqual(response.status_code, 302)
100 # # Formsets
101 # for url in ('user_address_preferences',
102 # 'user_subscription_preferences'):
103 # url = reverse(url)
104 # post_data = dict(
105 # ('form-0-%s' % pref, None)
106 # for pref in prefs_with_none)
107 # post_data.update({
108 # 'form-TOTAL_FORMS': '1',
109 # 'form-INITIAL_FORMS': '0',
110 # 'form-MAX_NUM_FORMS': ''
111 # })
112 # response = self.client.post(url, post_data)
113 # self.assertEqual(response.status_code, 302)
115 @override_settings(AUTOCREATE_MAILMAN_USER=False)
116 def test_subscriptions_no_mailman_user(self):
117 # Existing Django users without a corresponding Mailman user must not
118 # cause views to crash.
119 user = User.objects.create_user(
120 'old-user', 'old-user@example.com', 'testpass')
121 EmailAddress.objects.create(
122 user=user, email=user.email, verified=True)
123 self.client.login(username='old-user', password='testpass')
124 self.assertRaises(Mailman404Error, MailmanUser.objects.get,
125 address=user.email)
126 response = self.client.get(reverse('ps_user_profile'))
127 self.assertEqual(response.status_code, 200)
128 # The Mailman user must have been created
129 self.assertIsNotNone(MailmanUser.objects.get(address=user.email))
131 def test_presence_of_form_in_user_global_settings(self):
132 self.client.login(username='user', password='testpass')
133 response = self.client.get(reverse('user_mailmansettings'))
134 self.assertEqual(response.status_code, 200)
135 self.assertIsInstance(response.context['form'], UserPreferences)
137 def test_presence_of_form_in_user_subscription_preferences(self):
138 self.client.login(username='user', password='testpass')
139 self.foo_list.subscribe(self.user.email, pre_verified=True,
140 pre_confirmed=True, pre_approved=True)
141 response = self.client.get(reverse('user_subscription_preferences'))
142 self.assertEqual(response.status_code, 200)
143 self.assertIsNotNone(response.context['formset'])
144 self.assertEqual(len(response.context['formset']), 1)
146 def test_presence_of_form_in_user_list_options(self):
147 self.client.login(username='user', password='testpass')
148 member = self.foo_list.subscribe(
149 self.user.email,
150 pre_verified=True, pre_confirmed=True, pre_approved=True)
151 response = self.client.get(reverse('user_list_options',
152 args=[member.member_id]))
153 self.assertEqual(response.status_code, 200)
154 self.assertIsInstance(response.context['form'],
155 UserPreferences)
156 self.assertIsInstance(response.context['change_subscription_form'],
157 ChangeSubscriptionForm)
159 def test_list_options_shows_all_addresses(self):
160 self.client.login(username='user', password='testpass')
161 member = self.foo_list.subscribe(self.user.email, pre_verified=True,
162 pre_confirmed=True, pre_approved=True)
163 # Add another email
164 EmailAddress.objects.create(
165 user=self.user, email='anotheremail@example.com', verified=True)
166 user = self.mm_client.get_user('user@example.com')
167 address = user.add_address('anotheremail@example.com')
168 address.verify()
169 # Check response
170 response = self.client.get(reverse('user_list_options',
171 args=[member.member_id]))
172 self.assertEqual(response.status_code, 200)
173 self.assertContains(response, 'anotheremail@example.com')
175 def _set_primary(self, user, mm_user):
176 for addr in mm_user.addresses:
177 addr.verify()
178 mm_user.preferred_address = user.email
180 def test_change_subscription_to_new_email(self):
181 # Test that we can change subscription to a new email.
182 self.client.login(username='user', password='testpass')
183 user = self.mm_client.get_user('user@example.com')
184 EmailAddress.objects.create(
185 user=self.user, email='anotheremail@example.com', verified=True)
186 address = user.add_address('anotheremail@example.com')
187 address.verify()
188 member = self.foo_list.subscribe(
189 self.user.email,
190 pre_verified=True, pre_confirmed=True, pre_approved=True)
191 # Now, first verify that the list_options page has all the emails.
192 # Check response
193 response = self.client.get(reverse('user_list_options',
194 args=[member.member_id]))
195 self.assertContains(response, 'anotheremail@example.com')
196 self.assertContains(
197 response,
198 '<option value="user@example.com"'
199 ' selected>user@example.com</option>')
200 member = self.mm_client.get_member(
201 self.foo_list.list_id, 'user@example.com')
202 self.assertIsNotNone(member)
203 # Initially, all preferences are none. Let's set it to something
204 # custom.
205 self.assertIsNone(member.preferences.get('acknowledge_posts'))
206 member.preferences['acknowledge_posts'] = True
207 member.preferences.save()
208 # now, let's switch the subscription to a new user.
209 response = self.client.post(
210 reverse('change_subscription', args=(self.foo_list.list_id, )),
211 {'subscriber': 'anotheremail@example.com',
212 'member_id': member.member_id}
214 self.assertEqual(response.status_code, 302)
215 self.assertHasSuccessMessage(response)
216 member_new = self.mm_client.get_member(
217 self.foo_list.list_id, 'anotheremail@example.com')
218 self.assertIsNotNone(member_new)
219 # There is no 'member_id' attribute, so we simply use the self_link to
220 # compare and make sure that the Member object is same.
221 self.assertEqual(member.self_link, member_new.self_link)
222 self.assertEqual(member_new.subscription_mode, 'as_address')
223 # Also, assert that the new member's preferences are same.
224 self.assertEqual(member.preferences['acknowledge_posts'],
225 member_new.preferences['acknowledge_posts'])
227 def test_change_subscription_to_from_primary_address(self):
228 # Test that we can change subscription to a new email.
229 self.client.login(username='user', password='testpass')
230 user = self.mm_client.get_user('user@example.com')
231 self._set_primary(self.user, user)
232 member = self.foo_list.subscribe(
233 self.user.email,
234 pre_verified=True, pre_confirmed=True, pre_approved=True)
235 # Now, first verify that the list_options page has the primary address.
236 response = self.client.get(reverse('user_list_options',
237 args=[member.member_id]))
238 self.assertContains(response, 'Primary Address (user@example.com)')
239 self.assertContains(
240 response,
241 '<option value="user@example.com" '
242 'selected>user@example.com</option>')
243 member = self.mm_client.get_member(
244 self.foo_list.list_id, 'user@example.com')
245 self.assertIsNotNone(member)
246 # Initially, all preferences are none. Let's set it to something
247 # custom.
248 self.assertIsNone(member.preferences.get('acknowledge_posts'))
249 member.preferences['acknowledge_posts'] = True
250 member.preferences.save()
251 # now, let's switch the subscription to a new user.
252 response = self.client.post(
253 reverse('change_subscription', args=(self.foo_list.list_id, )),
254 {'subscriber': str(user.user_id), 'member_id': member.member_id}
256 self.assertEqual(response.status_code, 302)
257 self.assertHasSuccessMessage(response)
258 new_member = self.mm_client.get_member(
259 self.foo_list.list_id, 'user@example.com')
260 self.assertIsNotNone(new_member)
261 self.assertEqual(new_member.subscription_mode, 'as_user')
262 # we can't compare against the preferences object of `member` since the
263 # resource is now Deleted due to unsubscribe-subscribe dance.
264 self.assertEqual(new_member.preferences['acknowledge_posts'], True)
266 def test_already_subscribed(self):
267 self.client.login(username='user', password='testpass')
269 member = self.foo_list.subscribe(
270 self.user.email,
271 pre_verified=True, pre_confirmed=True, pre_approved=True)
272 # Now, first verify that the list_options page has all the emails.
273 # Check response
274 response = self.client.get(reverse('user_list_options',
275 args=[member.member_id]))
276 self.assertContains(
277 response,
278 '<option value="user@example.com" '
279 'selected>user@example.com</option>')
280 # now, let's switch the subscription to a new user.
281 response = self.client.post(
282 reverse('change_subscription', args=(self.foo_list.list_id, )),
283 {'subscriber': 'user@example.com', 'member_id': member.member_id}
285 self.assertEqual(response.status_code, 302)
286 error = self.assertHasErrorMessage(response)
287 self.assertIn('You are already subscribed', error)
289 def test_already_subscribed_with_primary_address(self):
290 # Test that we can change subscription to a new email.
291 self.client.login(username='user', password='testpass')
292 user = self.mm_client.get_user('user@example.com')
293 self._set_primary(self.user, user)
294 member = self.foo_list.subscribe(
295 user.user_id,
296 pre_verified=True, pre_confirmed=True, pre_approved=True)
297 # Now, first verify that the list_options page has the primary address.
298 response = self.client.get(reverse('user_list_options',
299 args=[member.member_id]))
300 self.assertContains(
301 response,
302 ('<option value="{}" selected>Primary Address (user@example.com)'
303 '</option>').format(user.user_id))
304 # now, let's switch the subscription to a new user.
305 response = self.client.post(
306 reverse('change_subscription', args=(self.foo_list.list_id, )),
307 {'subscriber': str(user.user_id), 'member_id': member.member_id}
309 self.assertEqual(response.status_code, 302)
310 error = self.assertHasErrorMessage(response)
311 self.assertIn('You are already subscribed', error)
313 def test_list_options_sets_preferred_address(self):
314 # Test that preferred address is set.
315 mm_user = get_mailman_user(self.user)
316 self.assertIsNone(mm_user.preferred_address)
317 member = self.foo_list.subscribe(
318 self.user.email,
319 pre_verified=True, pre_confirmed=True, pre_approved=True)
320 self.client.login(username='user', password='testpass')
321 self.client.get(reverse('user_list_options',
322 args=[member.member_id]))
323 self.assertEqual(mm_user.preferred_address.email, self.user.email)
325 def test_access_list_options_multiple_subscriptions(self):
326 # Test that when multiple addresses of a single user are subscribed to
327 # the same list that they are able to access them.
328 mm_user = get_mailman_user(self.user)
329 self.assertIsNone(mm_user.preferred_address)
330 self._set_primary(self.user, mm_user)
331 # Subscribe the user twice, once with their address and then with their
332 # primary address.
333 member_primary = self.foo_list.subscribe(
334 mm_user.user_id,
335 pre_verified=True, pre_confirmed=True, pre_approved=True)
336 member_addr = self.foo_list.subscribe(
337 self.user.email,
338 pre_verified=True, pre_confirmed=True, pre_approved=True)
339 self.assertEqual(len(self.foo_list.members), 2)
341 self.client.login(username='user', password='testpass')
342 response = self.client.get(
343 reverse('user_subscription_preferences'))
344 self.assertEqual(response.status_code, 200)
345 # There should be list options for two users.
346 self.assertContains(response, 'Primary Address')
347 self.assertContains(response, 'user@example.com')
348 # Get the list options for both memberships and check subscriber ==
349 # address.
350 response = self.client.get(
351 reverse('user_list_options',
352 args=[member_addr.member_id]))
353 self.assertEqual(response.status_code, 200)
354 subscriber = response.context.get(
355 'change_subscription_form').initial.get('subscriber')
356 self.assertEqual(subscriber, member_addr.address.email)
357 # Check subscriber == member_id
358 response = self.client.get(
359 reverse('user_list_options',
360 args=[member_primary.member_id]))
361 self.assertEqual(response.status_code, 200)
362 subscriber = response.context.get(
363 'change_subscription_form').initial.get('subscriber')
364 self.assertEqual(subscriber, member_primary.user.user_id)
366 def test_access_list_options_other_member(self):
367 # Test that a user can't access member options for a different user.
368 member_addr = self.foo_list.subscribe(
369 self.user.email,
370 pre_verified=True, pre_confirmed=True, pre_approved=True)
371 another_member = self.foo_list.subscribe(
372 'anoter@example.com',
373 pre_verified=True, pre_confirmed=True, pre_approved=True)
374 self.client.login(username='user', password='testpass')
375 response = self.client.get(
376 reverse('user_list_options',
377 args=[another_member.member_id]))
378 self.assertEqual(response.status_code, 404)
379 # But they can access their own.
380 response = self.client.get(
381 reverse('user_list_options',
382 args=[member_addr.member_id]))
383 self.assertEqual(response.status_code, 200)
386 class TestListAllUsers(ViewTestCase):
388 def setUp(self):
389 super().setUp()
390 for i in range(11):
391 self.mm_client.create_user('user{}@example.com'.format(i),
392 'testpass',
393 'User {}'.format(i))
394 self.su = User.objects.create_superuser('su', 'su@example.com', 'pass')
396 def test_get_all_users_forbidden(self):
397 response = self.client.get(reverse('list_users'))
398 self.assertEqual(response.status_code, 403)
400 def test_get_all_users_as_superuser(self):
401 self.client.force_login(self.su)
402 url = reverse('list_users')
403 response = self.client.get(url)
404 self.assertEqual(response.status_code, 200)
405 # default page size is 10, so we will get 10.
406 self.assertEqual(len(response.context['all_users']), 10)
407 # lets get all users by setting count.
408 url += '?count=15'
409 response = self.client.get(url)
410 self.assertEqual(response.status_code, 200)
411 # default page size is 10, so we will get 10.
412 self.assertEqual(len(response.context['all_users']), 11)
414 def test_search_user(self):
415 self.client.force_login(self.su)
417 def _get_url(query):
418 return reverse('list_users') + '?q={}'.format(query)
420 response = self.client.get(_get_url('0@e'))
421 self.assertEqual(response.status_code, 200)
422 # It should be two users, user0@example.com and user10@example.com
423 self.assertEqual(len(response.context['all_users']), 2)
424 # search with display name.
425 response = self.client.get(_get_url('User 7'))
426 self.assertEqual(response.status_code, 200)
427 # It should be one user, user7@example.com, but it should search with
428 # display name because of the space.
429 self.assertEqual(len(response.context['all_users']), 1)
430 self.assertEqual(response.context['all_users'][0].display_name,
431 'User 7')
434 class TestManageUser(ViewTestCase):
436 def setUp(self):
437 super().setUp()
438 self.user = self.mm_client.create_user('user@example.com',
439 'testpass')
440 self.su = User.objects.create_superuser('su', 'su@example.com', 'pass')
441 dom = self.mm_client.create_domain('example.com')
442 self.mlist = dom.create_list('test')
443 self.mlist.subscribe('user@example.com',
444 pre_verified=True, pre_confirmed=True)
446 def test_get_all_users_forbidden(self):
447 response = self.client.get(
448 reverse('manage_user', args=[self.user.user_id]))
449 self.assertEqual(response.status_code, 403)
451 def test_get_manage_user(self):
452 self.client.force_login(self.su)
453 response = self.client.get(
454 reverse('manage_user', args=[self.user.user_id]))
455 self.assertEqual(response.status_code, 200)
456 self.assertEqual(response.context['auser'].user_id, self.user.user_id)
457 user_form = response.context['user_form']
458 self.assertEqual(user_form.user.user_id, self.user.user_id)
459 addr_forms = response.context['addresses']
460 self.assertEqual(len(addr_forms.forms), 1)
461 subform = response.context['subscriptions']
462 self.assertEqual(len(subform.forms), 1)
463 self.assertIsNone(response.context['django_user'])
464 self.assertIsNone(response.context['change_password'])
466 def test_get_manage_user_with_django_user(self):
467 user = User.objects.create_user(username='tester', password='test')
468 for addr in self.user.addresses:
469 EmailAddress.objects.create(
470 user=user, email=addr.email)
471 self.client.force_login(self.su)
472 response = self.client.get(
473 reverse('manage_user', args=[self.user.user_id]))
474 self.assertEqual(response.status_code, 200)
475 self.assertEqual(response.context['auser'].user_id, self.user.user_id)
476 user_form = response.context['user_form']
477 self.assertEqual(user_form.user.user_id, self.user.user_id)
478 addr_forms = response.context['addresses']
479 self.assertEqual(len(addr_forms.forms), 1)
480 subform = response.context['subscriptions']
481 self.assertEqual(len(subform.forms), 1)
482 self.assertEqual(response.context['django_user'], user)
483 self.assertIsNotNone(response.context['change_password'])
485 def test_update_display_name(self):
486 self.client.force_login(self.su)
487 response = self.client.post(
488 reverse('manage_user', args=[self.user.user_id]),
489 data={'display_name': 'My User', 'user_form': 'Update'})
490 self.assertEqual(response.status_code, 200)
491 self.assertIn('Successfully updated user.', response.content.decode())
493 def test_update_user_address(self):
494 self.client.force_login(self.su)
495 addresses = self.user.addresses
496 addresses[0].unverify()
497 self.assertFalse(self.user.addresses[0].verified)
498 formdata = {
499 'form-TOTAL_FORMS': '1',
500 'form-INITIAL_FORMS': '1',
501 'form-MIN_NUM_FORMS': '1',
502 'form-MAX_NUM_FORMS': '1',
503 'form-0-verified': 'on',
504 'address_form': 'Update',
506 response = self.client.post(
507 reverse('manage_user', args=[self.user.user_id]),
508 data=formdata)
509 self.assertEqual(response.status_code, 200)
510 self.assertTrue(self.user.addresses[0].verified)
511 self.assertIn('Successfully updated addresses user@example.com',
512 response.content.decode())
514 def test_update_user_subscriptions(self):
515 self.client.force_login(self.su)
516 data = {
517 'form-TOTAL_FORMS': '1',
518 'form-INITIAL_FORMS': '1',
519 'form-MIN_NUM_FORMS': '0',
520 'form-MAX_NUM_FORMS': '1',
522 'form-0-moderation_action': 'discard',
523 'form-0-delivery_mode': 'mime_digests',
524 'subs_form': 'Update',
526 response = self.client.post(
527 reverse('manage_user', args=[self.user.user_id]),
528 data=data)
529 self.assertEqual(response.status_code, 200)
530 self.assertIn('Successfully updated memberships for test.example.com',
531 response.content.decode())
533 def test_update_user_password(self):
534 user = User.objects.create_user(
535 username='myuser', password='mypassword')
536 EmailAddress.objects.create(user=user, email='user@example.com')
537 self.assertTrue(
538 self.client.login(username='myuser', password='mypassword'))
539 self.client.force_login(self.su)
540 data = {
541 'change_password': 'Update',
542 'password1': 'newpsdsd1987',
543 'password2': 'newpsdsd1987',
545 response = self.client.post(
546 reverse('manage_user', args=[self.user.user_id]),
547 data=data)
548 self.assertEqual(response.status_code, 200)
549 # Verify by tring to login.
550 self.assertTrue(
551 self.client.login(username='myuser', password='newpsdsd1987'))