Newer
Older
postorius / src / postorius / tests / mailman_api_tests / test_list_members.py
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2015 by the Free Software Foundation, Inc.
#
# This file is part of Postorius.
#
# Postorius is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# Postorius is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# Postorius.  If not, see <http://www.gnu.org/licenses/>.
import logging

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.utils.timezone import now
from django.test import Client, TestCase
from django.test.utils import override_settings
from six.moves.urllib_error import HTTPError
from six.moves.urllib_parse import quote

from postorius.models import MailmanUser, Mailman404Error
from postorius.tests import MM_VCR, API_CREDENTIALS
from postorius.tests.utils import get_flash_messages
from postorius.utils import get_client


logger = logging.getLogger(__name__)
vcr_log = logging.getLogger('vcr')
vcr_log.setLevel(logging.WARNING)


@override_settings(**API_CREDENTIALS)
class ListMembersAccessTest(TestCase):
    """Tests for the list members page.

    Tests permissions and creation of list owners and moderators.
    """

    @MM_VCR.use_cassette('list_members_access.yaml')
    def setUp(self):
        try:
            self.domain = get_client().create_domain('example.com')
        except HTTPError:
            self.domain = get_client().get_domain('example.com')
        try:
            self.foo_list = self.domain.create_list('foo')
        except HTTPError:
            self.foo_list = get_client().get_list('foo.example.com')
        self.user = User.objects.create_user(
            'testuser', 'test@example.com', 'testpass')
        self.superuser = User.objects.create_superuser(
            'testsu', 'su@example.com', 'testpass')
        self.owner = User.objects.create_user(
            'testowner', 'owner@example.com', 'testpass')
        self.moderator = User.objects.create_user(
            'testmoderator', 'moderator@example.com', 'testpass')
        self.foo_list.add_owner('owner@example.com')
        self.foo_list.add_moderator('moderator@example.com')

    @MM_VCR.use_cassette('list_members_access.yaml')
    def tearDown(self):
        self.foo_list.delete()
        self.user.delete()
        self.superuser.delete()
        self.owner.delete()
        self.moderator.delete()

    @MM_VCR.use_cassette('list_members_access.yaml')
    def test_page_not_accessible_if_not_logged_in(self):
        url = reverse('list_members', args=('foo@example.com', ))
        response = self.client.get(url)
        if "%40" not in url: # Django < 1.8
            url = quote(url)
        expected_redirect = "http://testserver%s?next=%s" % (
            reverse(settings.LOGIN_URL), url)
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response["location"], expected_redirect)

    @MM_VCR.use_cassette('list_members_access.yaml')
    def test_page_not_accessible_for_unprivileged_users(self):
        self.client.login(username='testuser', password='testpass')
        response = self.client.get(reverse('list_members',
                                           args=('foo@example.com', )))
        self.assertEqual(response.status_code, 403)

    @MM_VCR.use_cassette('list_members_page.yaml')
    def test_not_accessible_for_moderator(self):
        self.client.login(username='testmoderator', password='testpass')
        response = self.client.get(reverse('list_members',
                                           args=('foo@example.com', )))
        self.assertEqual(response.status_code, 403)

    @MM_VCR.use_cassette('list_members_page.yaml')
    def test_page_accessible_for_superuser(self):
        self.client.login(username='testsu', password='testpass')
        response = self.client.get(reverse('list_members',
                                           args=('foo@example.com', )))
        self.assertEqual(response.status_code, 200)

    @MM_VCR.use_cassette('list_members_page.yaml')
    def test_page_accessible_for_owner(self):
        self.client.login(username='testowner', password='testpass')
        response = self.client.get(reverse('list_members',
                                           args=('foo@example.com', )))
        self.assertEqual(response.status_code, 200)


@override_settings(**API_CREDENTIALS)
class AddRemoveOwnerTest(TestCase):
    """Tests for the list members page.

    Tests creation of list owners.
    """

    @MM_VCR.use_cassette('test_list_members_owner.yaml')
    def setUp(self):
        self.mm_client = get_client()
        try:
            self.domain = self.mm_client.create_domain('example.com')
        except HTTPError:
            self.domain = self.mm_client.get_domain('example.com')
        self.foo_list = self.domain.create_list('foo')
        self.su = User.objects.create_superuser(
            'su', 'su@example.com', 'pwd')
        self.client.login(username='su', password='pwd')
        self.mm_client.get_list('foo@example.com').add_owner('su@example.com')

    @MM_VCR.use_cassette('test_list_members_owner.yaml')
    def tearDown(self):
        self.foo_list.delete()
        self.su.delete()

    @MM_VCR.use_cassette('test_list_members_owner_add_remove.yaml')
    def test_add_remove_owner(self):
        self.client.post(
            reverse('list_members', args=('foo@example.com', )),
            {'owner_email': 'newowner@example.com'})
        self.assertTrue('newowner@example.com' in self.foo_list.owners)
        self.client.post(
            reverse('remove_role', args=('foo@example.com', 'owner',
                                         'newowner@example.com')))
        self.assertFalse('newowner@example.com' in self.foo_list.owners)

    @MM_VCR.use_cassette('test_list_members_owner_by_owner.yaml')
    def test_remove_owner_by_owner(self):
        self.assertTrue('su@example.com' in self.foo_list.owners)
        # Make the logged in user a simple list owner
        self.su.is_superuser = False
        self.su.save()
        # It must still be allowed to create and remove owners
        self.client.post(
            reverse('list_members', args=('foo@example.com', )),
            {'owner_email': 'newowner@example.com'})
        self.assertTrue('newowner@example.com' in self.foo_list.owners)
        response = self.client.post(
            reverse('remove_role', args=('foo@example.com', 'owner',
                                         'newowner@example.com')))
        self.assertFalse('newowner@example.com' in self.foo_list.owners)
        msgs = get_flash_messages(response)
        self.assertEqual(len(msgs), 1)
        self.assertEqual(msgs[0].level, messages.SUCCESS, msgs[0].message)

    @MM_VCR.use_cassette('test_list_members_owner_self_last.yaml')
    def test_remove_owner_as_owner_self_last(self):
        # It is allowed to remove itself, but only if there's another owner
        # left.
        mm_list = self.mm_client.get_list('foo@example.com')
        mm_list.add_owner('otherowner@example.com')
        self.assertTrue('su@example.com' in self.foo_list.owners)
        self.assertTrue('otherowner@example.com' in self.foo_list.owners)
        response = self.client.post(
            reverse('remove_role', args=('foo@example.com', 'owner',
                                         'su@example.com')))
        self.assertFalse('su@example.com' in self.foo_list.owners)
        self.assertEqual(response.status_code, 302)
        msgs = get_flash_messages(response)
        self.assertEqual(len(msgs), 1)
        self.assertEqual(msgs[0].level, messages.SUCCESS, msgs[0].message)
        # But not to remove the last owner
        mm_list.add_owner('su@example.com')
        mm_list.remove_owner('otherowner@example.com')
        self.assertTrue('su@example.com' in self.foo_list.owners)
        self.assertFalse('otherowner@example.com' in self.foo_list.owners)
        response = self.client.post(
            reverse('remove_role', args=('foo@example.com', 'owner',
                                         'su@example.com')))
        self.assertTrue('su@example.com' in self.foo_list.owners)
        self.assertEqual(response.status_code, 302)
        msgs = get_flash_messages(response)
        self.assertEqual(len(msgs), 2)
        self.assertEqual(msgs[1].level, messages.ERROR, msgs[1].message)



@override_settings(**API_CREDENTIALS)
class AddModeratorTest(TestCase):
    """Tests for the list members page.

    Tests creation of moderators.
    """

    @MM_VCR.use_cassette('test_list_members_add_moderator.yaml')
    def setUp(self):
        try:
            self.domain = get_client().create_domain('example.com')
        except HTTPError:
            self.domain = get_client().get_domain('example.com')
        self.foo_list = self.domain.create_list('foo')
        self.su = User.objects.create_superuser(
            'su', 'su@example.com', 'pwd')
        # login and post new moderator data to url
        self.client.login(username='su', password='pwd')
        self.client.post(
            reverse('list_members', args=('foo@example.com', )),
            {'moderator_email': 'newmod@example.com'})
        moderators = self.foo_list.moderators

    @MM_VCR.use_cassette('test_list_members_add_moderator.yaml')
    def tearDown(self):
        self.foo_list.delete()
        self.su.delete()

    @MM_VCR.use_cassette('test_list_members_new_moderator_added.yaml')
    def test_new_moderator_added(self):
        self.assertTrue(u'newmod@example.com' in self.foo_list.moderators)