<commit_before>from __future__ import print_function
import shutil
import urllib
import os.path
import tempfile
import binascii

from mock import patch

from django.test import TestCase
from django.test.utils import override_settings
from django.test.client import RequestFactory
from django.core.urlresolvers import reverse
from django.conf import settings

import django.contrib.auth.models as auth_models

from toolkit.members.models import Member, Volunteer
import toolkit.members.member_views as member_views

from .common import MembersTestsMixin

TINY_VALID_BASE64_PNG = (
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BA"
    "ACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAMSURBVBhXY/j//z8ABf4C/qc1gYQAAA"
    "AASUVORK5CYII=")


class SecurityTests(MembersTestsMixin, TestCase):

    """Basic test that the private pages do not load without a login"""

    write_required = {
        # Volunteer urls:
        'add-volunteer': {},
        'edit-volunteer': {'volunteer_id': 1},
        'activate-volunteer': {},
        'inactivate-volunteer': {},
        # Member urls:
        'add-member': {},
        'edit-member': {'member_id': 1},
        'delete-member': {'member_id': 1},
    }

    only_read_required = {
        # Volunteer urls:
        'view-volunteer-list': {},
        'view-volunteer-role-report': {},
        'unretire-select-volunteer': {},
        'retire-select-volunteer': {},
        # Member urls:
        'search-members': {},
        'view-member': {'member_id': 1},
        'member-statistics': {},
    }

    def _assert_need_login(self, views_to_test):
        """Assert that given URLs 302 redirect to the login page"""
        for view_name, kwargs in views_to_test.items():
            url = reverse(view_name, kwargs=kwargs)
            expected_redirect = ("{0}?next={1}".format(
                reverse("login"), url))
            # Test GET:
            response = self.client.get(url)
            self.assertRedirects(response, expected_redirect)
            # Test POST:
            response = self.client.post(url)
            self.assertRedirects(response, expected_redirect)

    def test_need_login(self):
        """
        Checks all URLs that shouldn't work when not logged in at all
        """
        views_to_test = {}
        views_to_test.update(self.write_required)
        views_to_test.update(self.only_read_required)

        self._assert_need_login(views_to_test)

    def test_need_write(self):
        """
        Checks all URLs that shouldn't work when logged in user doesn't have
        'toolkit.write' permission
        """
        # login as read only user:
        self.client.login(username="read_only", password="<PASSWORD>")
        self._assert_need_login(self.write_required)

    def test_need_read_or_write(self):
        """
        Checks all URLs that shouldn't work when logged in user doesn't have
        'toolkit.write' or 'toolkit.read' permission
        """
        views_to_test = {}
        views_to_test.update(self.write_required)
        views_to_test.update(self.only_read_required)

        # login as no permission user:
        self.client.login(username="no_perm", password="<PASSWORD>")

        self._assert_need_login(views_to_test)

    def test_protected_urls(self):
        """URLs which should only work if the member key is known"""

        views_to_test = (
            'edit-member',
            'unsubscribe-member',
        )

        member = Member.objects.get(id=1)

        # First try without a key:
        for view_name in views_to_test:
            url = reverse(view_name, kwargs={'member_id': member.id})
            expected_redirect = "{0}?next={1}".format(
                reverse("login"), url)

            # Test GET:
            response = self.client.get(url)
            self.assertRedirects(response, expected_redirect)

            # Test POST:
            response = self.client.post(url)
            self.assertRedirects(response, expected_redirect)

        # Now try with the key:
        for view_name in views_to_test:
            url = "{0}?k={1}".format(
                reverse(view_name, kwargs={'member_id': member.id}),
                member.mailout_key)
            # Test GET:
            response = self.client.get(url)
            self.assertEqual(response.status_code, 200)
            self.assertContains(response, member.name)


class AddMemberIPAuth(TestCase):

    def setUp(self):
        factory = RequestFactory()
        self.url = reverse("add-member")
        self.request = factory.get(self.url)
        self.request.user = auth_models.AnonymousUser()

    def test_auth_by_ip_matching_ip_denied(self):
        # Request should be denied from 127.0.0.1

        # Check that this shouldn't work
        self.assertNotIn('127.0.0.1', settings.CUBE_IP_ADDRESSES)

        # Issue the request
        response = member_views.add_member(self.request)

        expected_redirect = (
            "{0}?next={1}"
            .format(reverse("login"), self.url)
        )
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['Location'], expected_redirect)

    def test_auth_by_ip_matching_ip_permitted(self):
        # Request should be permitted from IP in settings

        # Check that this should work:
        self.assertTrue(len(settings.CUBE_IP_ADDRESSES))

        # set source IP:
        self.request.META['REMOTE_ADDR'] = settings.CUBE_IP_ADDRESSES[0]

        # Issue the request
        response = member_views.add_member(self.request)

        self.assertEqual(response.status_code, 200)
        self.assertNotIn('Location', response)


class TestMemberModelManager(MembersTestsMixin, TestCase):

    def test_email_recipients(self):
        recipients = Member.objects.mailout_recipients()
        self.assertEqual(recipients.count(), 6)
        for member in recipients:
            self.assertTrue(member.mailout)
            self.assertFalse(member.mailout_failed)
            self.assertTrue(member.email)


class TestMemberModel(TestCase):

    def setUp(self):
        member_one = Member(name="<NAME>", number="1",
                            email="<EMAIL>")
        member_one.save()

    def test_membership_number_no_existing(self):
        new_member = Member(name="<NAME>", email="<EMAIL>")
        new_member.save()
        self.assertEqual(str(new_member.pk), new_member.number)

    def test_membership_number_exists(self):
        old_member = Member.objects.get(id=1)
        old_member.number = "2"
        old_member.save()

        new_member = Member(name="<NAME>", email="<EMAIL>")
        new_member.save()
        self.assertEqual("100002", new_member.number)

    def test_membership_number_exists_twice(self):
        old_member = Member.objects.get(id=1)
        old_member.number = "3"
        old_member.save()

        new_member_one = Member(name="<NAME>", email="<EMAIL>")
        new_member_one.save()
        new_member_one.number = "100003"
        new_member_one.save()

        new_member_two = Member(name="<NAME>", email="<EMAIL>")
        new_member_two.save()
        self.assertEqual("200003", new_member_two.number)

    def test_membership_number_custom(self):
        new_member = Member(name="<NAME>", email="<EMAIL>")
        new_member.number = "Orange squash"
        new_member.save()

        new_member = Member.objects.get(id=new_member.pk)
        self.assertEqual(new_member.number, "Orange squash")

    def test_membership_number_custom_edit(self):
        old_member = Member.objects.get(id=1)
        old_member.number = "Orange squash"
        old_member.save()

        old_member = Member.objects.get(id=1)
        self.assertEqual(old_member.number, "Orange squash")


class TestVolunteerEditViews(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestVolunteerEditViews, self).setUp()

        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    def test_retire(self):
        v = Volunteer.objects.get(id=1)
        self.assertTrue(v.active)

        url = reverse("inactivate-volunteer")
        response = self.client.post(url, {'volunteer': v.id})

        # Should have deactivated volunteer:
        v = Volunteer.objects.get(id=1)
        self.assertFalse(v.active)

        # And redirected to volunteer list
        self.assertRedirects(response, reverse("view-volunteer-list"))

    def test_unretire(self):
        v = Volunteer.objects.get(id=1)
        v.active = False
        v.save()

        url = reverse("activate-volunteer")
        response = self.client.post(url, {'volunteer': v.id})

        # Shoudl have activated volunteer:
        v = Volunteer.objects.get(id=1)
        self.assertTrue(v.active)

        # And redirected to volunteer list
        self.assertRedirects(response, reverse("view-volunteer-list"))


class TestAddMemberView(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestAddMemberView, self).setUp()

        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    def test_get_form(self):
        url = reverse("add-member")
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_new_member.html")

    def test_post_form(self):
        new_name = u"<NAME> \u20acejit"

        self.assertEqual(Member.objects.filter(name=new_name).count(), 0)

        url = reverse("add-member")
        response = self.client.post(url, data={
            u"name": new_name,
            u"email": u"<EMAIL>",
            u"postcode": "SW1A 1AA",
            u"mailout": "on",
        }, follow=True)

        self.assertRedirects(response, url)
        self.assertTemplateUsed(response, "form_new_member.html")

        member = Member.objects.get(name=new_name)
        self.assertEqual(
            member.email, u"<EMAIL>")
        self.assertEqual(member.postcode, u"SW1A 1AA")
        self.assertEqual(member.mailout, True)

        self.assertContains(
            response, u"Added member: {0}".format(member.number))

    def test_post_minimal_submission(self):
        new_name = u"<NAME> \u20acejit"

        self.assertEqual(Member.objects.filter(name=new_name).count(), 0)

        url = reverse("add-member")
        response = self.client.post(url, data={
            u"name": new_name,
        }, follow=True)

        self.assertRedirects(response, url)
        self.assertTemplateUsed(response, "form_new_member.html")

        member = Member.objects.get(name=new_name)
        self.assertEqual(member.email, u"")
        self.assertEqual(member.postcode, u"")
        self.assertEqual(member.is_member, False)

        self.assertContains(
            response, u"Added member: {0}".format(member.number))

    def test_post_form_invalid_data_missing(self):
        url = reverse("add-member")
        response = self.client.post(url)

        count_before = Member.objects.count()

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_new_member.html")

        self.assertFormError(response, 'form', 'name',
                             u'This field is required.')

        self.assertEqual(count_before, Member.objects.count())

    def test_invalid_method(self):
        url = reverse("add-member")
        response = self.client.put(url)
        self.assertEqual(response.status_code, 405)


class TestSearchMemberView(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestSearchMemberView, self).setUp()

        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    @patch('toolkit.members.member_views.Member')
    def test_no_query(self, member_patch):
        url = reverse("search-members")
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "search_members.html")

        self.assertFalse(member_patch.objects.filter.called)

    def test_query_with_results(self):
        url = reverse("search-members")
        response = self.client.get(url, data={'q': u'member'})

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "search_members_results.html")

        self.assertContains(
            response, u"<td><a href='/members/1'>Member On\u0205</a></td>",
            html=True)
        self.assertContains(
            response, u'<a href="mailto:<EMAIL>"><EMAIL></a>',
            html=True)
        self.assertContains(response, u"<td>BS1 1AA</td>", html=True)

        self.assertContains(
            response, u"<td><a href='/members/2'>Tw\u020d Member</a></td>",
            html=True)
        self.assertContains(
            response, u'<a href="mailto:<EMAIL>"><EMAIL></a>',
            html=True)

        self.assertContains(
            response, u"<td><a href='/members/3'>Some Third Chap</a></td>",
            html=True)
        self.assertContains(
            response,
            u'<td><a href="mailto:<EMAIL>"><EMAIL></a></td>',
            html=True)
        self.assertContains(response, u"<td>NORAD</td>", html=True)

        # Should have Edit / Delete buttons:
        self.assertContains(
            response, u'<input type="submit" value="Edit">', html=True)
        self.assertContains(
            response, u'<input type="submit" value="Delete">', html=True)

        expected_edit_form = ('<form method="get" action="{0}">'
                              '<input type="submit" value="Edit"></form>'
                              .format(reverse(
                                  "edit-member", kwargs={"member_id": 3})))

        expected_delete_form = ('<form class="delete" method="post" '
                                'action="{0}">'
                                .format(reverse(
                                    "delete-member", kwargs={"member_id": 3})))
        self.assertContains(response, expected_edit_form)
        self.assertContains(response, expected_delete_form)

    def test_query_no_results(self):
        url = reverse("search-members")

        response = self.client.get(url, data={'q': u'toast'})

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "search_members_results.html")


class TestDeleteMemberView(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestDeleteMemberView, self).setUp()

        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    def test_delete(self):
        self.assertEqual(Member.objects.filter(id=1).count(), 1)

        url = reverse("delete-member", kwargs={"member_id": 1})
        response = self.client.post(url, follow=True)

        self.assertRedirects(response, reverse("search-members"))
        self.assertContains(response, u"Deleted member: 1 (Member On\u0205)")

        self.assertEqual(Member.objects.filter(id=1).count(), 0)

    def test_delete_nonexistent(self):
        url = reverse("delete-member", kwargs={"member_id": 1000})
        response = self.client.post(url)
        self.assertEqual(response.status_code, 404)

    def test_get_not_allowed(self):
        self.assertEqual(Member.objects.filter(id=1).count(), 1)

        url = reverse("delete-member", kwargs={"member_id": 1})

        response = self.client.get(url)

        self.assertEqual(response.status_code, 405)
        self.assertEqual(Member.objects.filter(id=1).count(), 1)


class TestEditMemberViewNotLoggedIn(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestEditMemberViewNotLoggedIn, self).setUp()

    def _assert_redirect_to_login(self, response, url, extra_parameters=""):
        expected_redirect = (
            reverse("login") +
            "?next=" +
            urllib.quote(url + extra_parameters)
        )
        self.assertRedirects(response, expected_redirect)

    # GET tests ###########################################
    def test_edit_get_form(self):
        member = Member.objects.get(pk=2)

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.get(url, data={
            'k': member.mailout_key,
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

    def test_edit_get_form_no_key(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.get(url)

        self._assert_redirect_to_login(response, url)

    def test_edit_get_form_incorrect_key(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.get(url, data={
            'k': "the WRONG KEY",
        })
        self._assert_redirect_to_login(response, url, "?k=the+WRONG+KEY")

    def test_edit_get_form_invalid_memberid(self):
        url = reverse("edit-member", kwargs={"member_id": 21212})
        response = self.client.get(url, data={
            'k': "the WRONG KEY",
        })
        # If the member doesn't exist then don't give a specific error to that
        # effect, just redirect to the login page:
        self._assert_redirect_to_login(response, url, "?k=the+WRONG+KEY")

    # POST tests ###########################################
    def test_edit_post_form_minimal_data(self):
        new_name = u'N\u018EW Name'

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, u"Tw\u020d Member")
        member_mailout_key = member.mailout_key
        self.assertTrue(member.is_member)

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'name': new_name,
            'k': member_mailout_key,
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, new_name)
        self.assertEqual(member.email, "")
        self.assertEqual(member.address, "")
        self.assertEqual(member.posttown, "")
        self.assertEqual(member.postcode, "")
        self.assertEqual(member.country, "")
        self.assertEqual(member.website, "")
        self.assertEqual(member.phone, "")
        self.assertEqual(member.altphone, "")
        self.assertEqual(member.notes, "")
        self.assertFalse(member.mailout)
        self.assertFalse(member.mailout_failed)
        self.assertFalse(member.is_member)

        # Shouldn't have been changed:
        self.assertEqual(member.mailout_key, member_mailout_key)

        self.assertContains(response, new_name)
        self.assertContains(response, "Member 02 updated")

    def test_edit_post_form_all_data(self):
        new_name = u'N\u018EW Name'

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, u"Tw\u020d Member")
        member_mailout_key = member.mailout_key
        self.assertTrue(member.is_member)

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'name': new_name,
            'email': '<EMAIL>',
            'k': member_mailout_key,
            'address': "somewhere over the rainbow, I guess",
            'posttown': "Town Town Town!",
            'postcode': "< Sixteen chars?",
            'country': "Suriname",
            'website': "http://don't_care/",
            'phone': "+44 0000000000000001",
            'altphone': "-1 3202394 2352 23 234",
            'notes': "plays the balalaika really badly",
            'mailout': "t",
            'mailout_failed': "t",
            'is_member': "t",
            "mailout_key": "sinister",
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, new_name)
        self.assertEqual(member.email, '<EMAIL>')
        self.assertEqual(member.address, "somewhere over the rainbow, I guess")
        self.assertEqual(member.posttown, "Town Town Town!")
        self.assertEqual(member.postcode, "< Sixteen chars?")
        self.assertEqual(member.country, "Suriname")
        self.assertEqual(member.website, "http://don't_care/")
        self.assertEqual(member.phone, "+44 0000000000000001")
        self.assertEqual(member.altphone, "-1 3202394 2352 23 234")
        self.assertEqual(member.notes, "plays the balalaika really badly")
        self.assertTrue(member.mailout)
        self.assertTrue(member.mailout_failed)
        self.assertTrue(member.is_member)

        # Shouldn't have been changed:
        self.assertEqual(member.mailout_key, member_mailout_key)

        self.assertContains(response, new_name)
        self.assertContains(response, "Member 02 updated")

    def test_edit_post_form_invalid_emails(self):
        new_name = u'N\u018EW Name'

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, u"Tw\u020d Member")
        member_mailout_key = member.mailout_key
        self.assertTrue(member.is_member)

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'name': new_name,
            'email': 'definitely_invalid@example/com',
            'k': member_mailout_key,
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        self.assertFormError(response, 'form', 'email',
                             u'Enter a valid email address.')

        member = Member.objects.get(pk=2)
        self.assertNotEqual(member.name, new_name)
        self.assertEqual(member.email, "<EMAIL>")
        self.assertEqual(member.mailout_key, member_mailout_key)

    def test_edit_post_form_invalid_data_missing(self):
        member = Member.objects.get(pk=2)
        start_name = member.name

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'k': member.mailout_key,
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        # Only mandatory field is "name":
        self.assertFormError(response, 'form', 'name',
                             u'This field is required.')

        member = Member.objects.get(pk=2)
        self.assertEqual(start_name, member.name)

    def test_edit_post_form_no_key(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url)

        self._assert_redirect_to_login(response, url)

    def test_edit_post_form_incorrect_key(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'k': "the WRONG KEY",
        })
        self._assert_redirect_to_login(response, url)

    def test_edit_post_form_invalid_memberid(self):
        url = reverse("edit-member", kwargs={"member_id": 21212})
        response = self.client.post(url, data={
            'k': "the WRONG KEY",
        })
        # If the member doesn't exist then don't give a specific error to that
        # effect, just redirect to the login page:
        self._assert_redirect_to_login(response, url)


class TestEditMemberViewLoggedIn(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestEditMemberViewLoggedIn, self).setUp()
        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    # GET tests ###########################################
    def test_edit_get_form(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

    def test_edit_get_form_invalid_memberid(self):
        url = reverse("edit-member", kwargs={"member_id": 21212})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    # POST tests ###########################################
    # Only test differences from not logged in view...
    def test_edit_post_form_minimal_data(self):
        new_name = u'N\u018EW Name'

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, u"Tw\u020d Member")

        member_mailout_key = member.mailout_key

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(
            url, data={'name': new_name, }, follow=True)

        member = Member.objects.get(pk=2)
        # New name set:
        self.assertEqual(member.name, new_name)

        # Secret key shouldn't have been changed:
        self.assertEqual(member.mailout_key, member_mailout_key)

        # Should redirect to search page, with a success message inserted:
        self.assertRedirects(response, reverse("search-members"))
        self.assertContains(response, "Member 02 updated")

    def test_edit_post_form_invalid_data_missing(self):
        member = Member.objects.get(pk=2)
        start_name = member.name

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url)
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        # Only mandatory field is "name":
        self.assertFormError(response, 'form', 'name',
                             u'This field is required.')

        member = Member.objects.get(pk=2)
        self.assertEqual(start_name, member.name)

    def test_edit_post_form_invalid_memberid(self):
        url = reverse("edit-member", kwargs={"member_id": 21212})
        response = self.client.post(url)
        self.assertEqual(response.status_code, 404)


class TestUnsubscribeMemberView(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestUnsubscribeMemberView, self).setUp()

    def _assert_redirect_to_login(self, response, url, extra_parameters=""):
        expected_redirect = (
            reverse("login") +
            "?next=" +
            urllib.quote(url + extra_parameters)
        )
        self.assertRedirects(response, expected_redirect)

    def _assert_subscribed(self, member_id):
        member = Member.objects.get(pk=member_id)
        self.assertTrue(member.mailout)

    def _assert_unsubscribed(self, member_id):
        member = Member.objects.get(pk=member_id)
        self.assertFalse(member.mailout)

    # GET tests ###########################################
    def test_unsubscribe_get_form(self):
        self._assert_subscribed(2)<commit_msg>Py3k urllib fixup<commit_after>from __future__ import print_function
import shutil
import os.path
import tempfile
import binascii

from mock import patch

from six.moves import urllib
from django.test import TestCase
from django.test.utils import override_settings
from django.test.client import RequestFactory
from django.core.urlresolvers import reverse
from django.conf import settings

import django.contrib.auth.models as auth_models

from toolkit.members.models import Member, Volunteer
import toolkit.members.member_views as member_views

from .common import MembersTestsMixin

TINY_VALID_BASE64_PNG = (
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BA"
    "ACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAMSURBVBhXY/j//z8ABf4C/qc1gYQAAA"
    "AASUVORK5CYII=")


class SecurityTests(MembersTestsMixin, TestCase):

    """Basic test that the private pages do not load without a login"""

    write_required = {
        # Volunteer urls:
        'add-volunteer': {},
        'edit-volunteer': {'volunteer_id': 1},
        'activate-volunteer': {},
        'inactivate-volunteer': {},
        # Member urls:
        'add-member': {},
        'edit-member': {'member_id': 1},
        'delete-member': {'member_id': 1},
    }

    only_read_required = {
        # Volunteer urls:
        'view-volunteer-list': {},
        'view-volunteer-role-report': {},
        'unretire-select-volunteer': {},
        'retire-select-volunteer': {},
        # Member urls:
        'search-members': {},
        'view-member': {'member_id': 1},
        'member-statistics': {},
    }

    def _assert_need_login(self, views_to_test):
        """Assert that given URLs 302 redirect to the login page"""
        for view_name, kwargs in views_to_test.items():
            url = reverse(view_name, kwargs=kwargs)
            expected_redirect = ("{0}?next={1}".format(
                reverse("login"), url))
            # Test GET:
            response = self.client.get(url)
            self.assertRedirects(response, expected_redirect)
            # Test POST:
            response = self.client.post(url)
            self.assertRedirects(response, expected_redirect)

    def test_need_login(self):
        """
        Checks all URLs that shouldn't work when not logged in at all
        """
        views_to_test = {}
        views_to_test.update(self.write_required)
        views_to_test.update(self.only_read_required)

        self._assert_need_login(views_to_test)

    def test_need_write(self):
        """
        Checks all URLs that shouldn't work when logged in user doesn't have
        'toolkit.write' permission
        """
        # login as read only user:
        self.client.login(username="read_only", password="<PASSWORD>")
        self._assert_need_login(self.write_required)

    def test_need_read_or_write(self):
        """
        Checks all URLs that shouldn't work when logged in user doesn't have
        'toolkit.write' or 'toolkit.read' permission
        """
        views_to_test = {}
        views_to_test.update(self.write_required)
        views_to_test.update(self.only_read_required)

        # login as no permission user:
        self.client.login(username="no_perm", password="<PASSWORD>")

        self._assert_need_login(views_to_test)

    def test_protected_urls(self):
        """URLs which should only work if the member key is known"""

        views_to_test = (
            'edit-member',
            'unsubscribe-member',
        )

        member = Member.objects.get(id=1)

        # First try without a key:
        for view_name in views_to_test:
            url = reverse(view_name, kwargs={'member_id': member.id})
            expected_redirect = "{0}?next={1}".format(
                reverse("login"), url)

            # Test GET:
            response = self.client.get(url)
            self.assertRedirects(response, expected_redirect)

            # Test POST:
            response = self.client.post(url)
            self.assertRedirects(response, expected_redirect)

        # Now try with the key:
        for view_name in views_to_test:
            url = "{0}?k={1}".format(
                reverse(view_name, kwargs={'member_id': member.id}),
                member.mailout_key)
            # Test GET:
            response = self.client.get(url)
            self.assertEqual(response.status_code, 200)
            self.assertContains(response, member.name)


class AddMemberIPAuth(TestCase):

    def setUp(self):
        factory = RequestFactory()
        self.url = reverse("add-member")
        self.request = factory.get(self.url)
        self.request.user = auth_models.AnonymousUser()

    def test_auth_by_ip_matching_ip_denied(self):
        # Request should be denied from 127.0.0.1

        # Check that this shouldn't work
        self.assertNotIn('127.0.0.1', settings.CUBE_IP_ADDRESSES)

        # Issue the request
        response = member_views.add_member(self.request)

        expected_redirect = (
            "{0}?next={1}"
            .format(reverse("login"), self.url)
        )
        self.assertEqual(response.status_code, 302)
        self.assertEqual(response['Location'], expected_redirect)

    def test_auth_by_ip_matching_ip_permitted(self):
        # Request should be permitted from IP in settings

        # Check that this should work:
        self.assertTrue(len(settings.CUBE_IP_ADDRESSES))

        # set source IP:
        self.request.META['REMOTE_ADDR'] = settings.CUBE_IP_ADDRESSES[0]

        # Issue the request
        response = member_views.add_member(self.request)

        self.assertEqual(response.status_code, 200)
        self.assertNotIn('Location', response)


class TestMemberModelManager(MembersTestsMixin, TestCase):

    def test_email_recipients(self):
        recipients = Member.objects.mailout_recipients()
        self.assertEqual(recipients.count(), 6)
        for member in recipients:
            self.assertTrue(member.mailout)
            self.assertFalse(member.mailout_failed)
            self.assertTrue(member.email)


class TestMemberModel(TestCase):

    def setUp(self):
        member_one = Member(name="<NAME>", number="1",
                            email="<EMAIL>")
        member_one.save()

    def test_membership_number_no_existing(self):
        new_member = Member(name="<NAME>", email="<EMAIL>")
        new_member.save()
        self.assertEqual(str(new_member.pk), new_member.number)

    def test_membership_number_exists(self):
        old_member = Member.objects.get(id=1)
        old_member.number = "2"
        old_member.save()

        new_member = Member(name="<NAME>", email="<EMAIL>")
        new_member.save()
        self.assertEqual("100002", new_member.number)

    def test_membership_number_exists_twice(self):
        old_member = Member.objects.get(id=1)
        old_member.number = "3"
        old_member.save()

        new_member_one = Member(name="<NAME>", email="<EMAIL>")
        new_member_one.save()
        new_member_one.number = "100003"
        new_member_one.save()

        new_member_two = Member(name="<NAME>", email="<EMAIL>")
        new_member_two.save()
        self.assertEqual("200003", new_member_two.number)

    def test_membership_number_custom(self):
        new_member = Member(name="<NAME>", email="<EMAIL>")
        new_member.number = "Orange squash"
        new_member.save()

        new_member = Member.objects.get(id=new_member.pk)
        self.assertEqual(new_member.number, "Orange squash")

    def test_membership_number_custom_edit(self):
        old_member = Member.objects.get(id=1)
        old_member.number = "Orange squash"
        old_member.save()

        old_member = Member.objects.get(id=1)
        self.assertEqual(old_member.number, "Orange squash")


class TestVolunteerEditViews(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestVolunteerEditViews, self).setUp()

        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    def test_retire(self):
        v = Volunteer.objects.get(id=1)
        self.assertTrue(v.active)

        url = reverse("inactivate-volunteer")
        response = self.client.post(url, {'volunteer': v.id})

        # Should have deactivated volunteer:
        v = Volunteer.objects.get(id=1)
        self.assertFalse(v.active)

        # And redirected to volunteer list
        self.assertRedirects(response, reverse("view-volunteer-list"))

    def test_unretire(self):
        v = Volunteer.objects.get(id=1)
        v.active = False
        v.save()

        url = reverse("activate-volunteer")
        response = self.client.post(url, {'volunteer': v.id})

        # Shoudl have activated volunteer:
        v = Volunteer.objects.get(id=1)
        self.assertTrue(v.active)

        # And redirected to volunteer list
        self.assertRedirects(response, reverse("view-volunteer-list"))


class TestAddMemberView(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestAddMemberView, self).setUp()

        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    def test_get_form(self):
        url = reverse("add-member")
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_new_member.html")

    def test_post_form(self):
        new_name = u"<NAME> \u20acejit"

        self.assertEqual(Member.objects.filter(name=new_name).count(), 0)

        url = reverse("add-member")
        response = self.client.post(url, data={
            u"name": new_name,
            u"email": u"<EMAIL>",
            u"postcode": "SW1A 1AA",
            u"mailout": "on",
        }, follow=True)

        self.assertRedirects(response, url)
        self.assertTemplateUsed(response, "form_new_member.html")

        member = Member.objects.get(name=new_name)
        self.assertEqual(
            member.email, u"<EMAIL>")
        self.assertEqual(member.postcode, u"SW1A 1AA")
        self.assertEqual(member.mailout, True)

        self.assertContains(
            response, u"Added member: {0}".format(member.number))

    def test_post_minimal_submission(self):
        new_name = u"Another New \u20acejit"

        self.assertEqual(Member.objects.filter(name=new_name).count(), 0)

        url = reverse("add-member")
        response = self.client.post(url, data={
            u"name": new_name,
        }, follow=True)

        self.assertRedirects(response, url)
        self.assertTemplateUsed(response, "form_new_member.html")

        member = Member.objects.get(name=new_name)
        self.assertEqual(member.email, u"")
        self.assertEqual(member.postcode, u"")
        self.assertEqual(member.is_member, False)

        self.assertContains(
            response, u"Added member: {0}".format(member.number))

    def test_post_form_invalid_data_missing(self):
        url = reverse("add-member")
        response = self.client.post(url)

        count_before = Member.objects.count()

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_new_member.html")

        self.assertFormError(response, 'form', 'name',
                             u'This field is required.')

        self.assertEqual(count_before, Member.objects.count())

    def test_invalid_method(self):
        url = reverse("add-member")
        response = self.client.put(url)
        self.assertEqual(response.status_code, 405)


class TestSearchMemberView(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestSearchMemberView, self).setUp()

        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    @patch('toolkit.members.member_views.Member')
    def test_no_query(self, member_patch):
        url = reverse("search-members")
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "search_members.html")

        self.assertFalse(member_patch.objects.filter.called)

    def test_query_with_results(self):
        url = reverse("search-members")
        response = self.client.get(url, data={'q': u'member'})

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "search_members_results.html")

        self.assertContains(
            response, u"<td><a href='/members/1'>Member On\u0205</a></td>",
            html=True)
        self.assertContains(
            response, u'<a href="mailto:<EMAIL>"><EMAIL></a>',
            html=True)
        self.assertContains(response, u"<td>BS1 1AA</td>", html=True)

        self.assertContains(
            response, u"<td><a href='/members/2'>Tw\u020d Member</a></td>",
            html=True)
        self.assertContains(
            response, u'<a href="mailto:<EMAIL>"><EMAIL></a>',
            html=True)

        self.assertContains(
            response, u"<td><a href='/members/3'>Some Third Chap</a></td>",
            html=True)
        self.assertContains(
            response,
            u'<td><a href="mailto:<EMAIL>"><EMAIL></a></td>',
            html=True)
        self.assertContains(response, u"<td>NORAD</td>", html=True)

        # Should have Edit / Delete buttons:
        self.assertContains(
            response, u'<input type="submit" value="Edit">', html=True)
        self.assertContains(
            response, u'<input type="submit" value="Delete">', html=True)

        expected_edit_form = ('<form method="get" action="{0}">'
                              '<input type="submit" value="Edit"></form>'
                              .format(reverse(
                                  "edit-member", kwargs={"member_id": 3})))

        expected_delete_form = ('<form class="delete" method="post" '
                                'action="{0}">'
                                .format(reverse(
                                    "delete-member", kwargs={"member_id": 3})))
        self.assertContains(response, expected_edit_form)
        self.assertContains(response, expected_delete_form)

    def test_query_no_results(self):
        url = reverse("search-members")

        response = self.client.get(url, data={'q': u'toast'})

        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "search_members_results.html")


class TestDeleteMemberView(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestDeleteMemberView, self).setUp()

        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    def test_delete(self):
        self.assertEqual(Member.objects.filter(id=1).count(), 1)

        url = reverse("delete-member", kwargs={"member_id": 1})
        response = self.client.post(url, follow=True)

        self.assertRedirects(response, reverse("search-members"))
        self.assertContains(response, u"Deleted member: 1 (Member On\u0205)")

        self.assertEqual(Member.objects.filter(id=1).count(), 0)

    def test_delete_nonexistent(self):
        url = reverse("delete-member", kwargs={"member_id": 1000})
        response = self.client.post(url)
        self.assertEqual(response.status_code, 404)

    def test_get_not_allowed(self):
        self.assertEqual(Member.objects.filter(id=1).count(), 1)

        url = reverse("delete-member", kwargs={"member_id": 1})

        response = self.client.get(url)

        self.assertEqual(response.status_code, 405)
        self.assertEqual(Member.objects.filter(id=1).count(), 1)


class TestEditMemberViewNotLoggedIn(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestEditMemberViewNotLoggedIn, self).setUp()

    def _assert_redirect_to_login(self, response, url, extra_parameters=""):
        expected_redirect = (
            reverse("login") +
            "?next=" +
            urllib.parse.quote(url + extra_parameters)
        )
        self.assertRedirects(response, expected_redirect)

    # GET tests ###########################################
    def test_edit_get_form(self):
        member = Member.objects.get(pk=2)

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.get(url, data={
            'k': member.mailout_key,
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

    def test_edit_get_form_no_key(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.get(url)

        self._assert_redirect_to_login(response, url)

    def test_edit_get_form_incorrect_key(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.get(url, data={
            'k': "the WRONG KEY",
        })
        self._assert_redirect_to_login(response, url, "?k=the+WRONG+KEY")

    def test_edit_get_form_invalid_memberid(self):
        url = reverse("edit-member", kwargs={"member_id": 21212})
        response = self.client.get(url, data={
            'k': "the WRONG KEY",
        })
        # If the member doesn't exist then don't give a specific error to that
        # effect, just redirect to the login page:
        self._assert_redirect_to_login(response, url, "?k=the+WRONG+KEY")

    # POST tests ###########################################
    def test_edit_post_form_minimal_data(self):
        new_name = u'N\u018EW Name'

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, u"Tw\u020d Member")
        member_mailout_key = member.mailout_key
        self.assertTrue(member.is_member)

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'name': new_name,
            'k': member_mailout_key,
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, new_name)
        self.assertEqual(member.email, "")
        self.assertEqual(member.address, "")
        self.assertEqual(member.posttown, "")
        self.assertEqual(member.postcode, "")
        self.assertEqual(member.country, "")
        self.assertEqual(member.website, "")
        self.assertEqual(member.phone, "")
        self.assertEqual(member.altphone, "")
        self.assertEqual(member.notes, "")
        self.assertFalse(member.mailout)
        self.assertFalse(member.mailout_failed)
        self.assertFalse(member.is_member)

        # Shouldn't have been changed:
        self.assertEqual(member.mailout_key, member_mailout_key)

        self.assertContains(response, new_name)
        self.assertContains(response, "Member 02 updated")

    def test_edit_post_form_all_data(self):
        new_name = u'N\u018EW Name'

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, u"Tw\u020d Member")
        member_mailout_key = member.mailout_key
        self.assertTrue(member.is_member)

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'name': new_name,
            'email': '<EMAIL>',
            'k': member_mailout_key,
            'address': "somewhere over the rainbow, I guess",
            'posttown': "Town Town Town!",
            'postcode': "< Sixteen chars?",
            'country': "Suriname",
            'website': "http://don't_care/",
            'phone': "+44 0000000000000001",
            'altphone': "-1 3202394 2352 23 234",
            'notes': "plays the balalaika really badly",
            'mailout': "t",
            'mailout_failed': "t",
            'is_member': "t",
            "mailout_key": "sinister",
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, new_name)
        self.assertEqual(member.email, '<EMAIL>')
        self.assertEqual(member.address, "somewhere over the rainbow, I guess")
        self.assertEqual(member.posttown, "Town Town Town!")
        self.assertEqual(member.postcode, "< Sixteen chars?")
        self.assertEqual(member.country, "Suriname")
        self.assertEqual(member.website, "http://don't_care/")
        self.assertEqual(member.phone, "+44 0000000000000001")
        self.assertEqual(member.altphone, "-1 3202394 2352 23 234")
        self.assertEqual(member.notes, "plays the balalaika really badly")
        self.assertTrue(member.mailout)
        self.assertTrue(member.mailout_failed)
        self.assertTrue(member.is_member)

        # Shouldn't have been changed:
        self.assertEqual(member.mailout_key, member_mailout_key)

        self.assertContains(response, new_name)
        self.assertContains(response, "Member 02 updated")

    def test_edit_post_form_invalid_emails(self):
        new_name = u'N\u018EW Name'

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, u"Tw\u020d Member")
        member_mailout_key = member.mailout_key
        self.assertTrue(member.is_member)

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'name': new_name,
            'email': 'definitely_invalid@example/com',
            'k': member_mailout_key,
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        self.assertFormError(response, 'form', 'email',
                             u'Enter a valid email address.')

        member = Member.objects.get(pk=2)
        self.assertNotEqual(member.name, new_name)
        self.assertEqual(member.email, "<EMAIL>")
        self.assertEqual(member.mailout_key, member_mailout_key)

    def test_edit_post_form_invalid_data_missing(self):
        member = Member.objects.get(pk=2)
        start_name = member.name

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'k': member.mailout_key,
        })
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        # Only mandatory field is "name":
        self.assertFormError(response, 'form', 'name',
                             u'This field is required.')

        member = Member.objects.get(pk=2)
        self.assertEqual(start_name, member.name)

    def test_edit_post_form_no_key(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url)

        self._assert_redirect_to_login(response, url)

    def test_edit_post_form_incorrect_key(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url, data={
            'k': "the WRONG KEY",
        })
        self._assert_redirect_to_login(response, url)

    def test_edit_post_form_invalid_memberid(self):
        url = reverse("edit-member", kwargs={"member_id": 21212})
        response = self.client.post(url, data={
            'k': "the WRONG KEY",
        })
        # If the member doesn't exist then don't give a specific error to that
        # effect, just redirect to the login page:
        self._assert_redirect_to_login(response, url)


class TestEditMemberViewLoggedIn(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestEditMemberViewLoggedIn, self).setUp()
        self.assertTrue(self.client.login(
            username="admin", password="<PASSWORD>!"))

    def tearDown(self):
        self.client.logout()

    # GET tests ###########################################
    def test_edit_get_form(self):
        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

    def test_edit_get_form_invalid_memberid(self):
        url = reverse("edit-member", kwargs={"member_id": 21212})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    # POST tests ###########################################
    # Only test differences from not logged in view...
    def test_edit_post_form_minimal_data(self):
        new_name = u'N\u018EW Name'

        member = Member.objects.get(pk=2)
        self.assertEqual(member.name, u"Tw\u020d Member")

        member_mailout_key = member.mailout_key

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(
            url, data={'name': new_name, }, follow=True)

        member = Member.objects.get(pk=2)
        # New name set:
        self.assertEqual(member.name, new_name)

        # Secret key shouldn't have been changed:
        self.assertEqual(member.mailout_key, member_mailout_key)

        # Should redirect to search page, with a success message inserted:
        self.assertRedirects(response, reverse("search-members"))
        self.assertContains(response, "Member 02 updated")

    def test_edit_post_form_invalid_data_missing(self):
        member = Member.objects.get(pk=2)
        start_name = member.name

        url = reverse("edit-member", kwargs={"member_id": 2})
        response = self.client.post(url)
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "form_member.html")

        # Only mandatory field is "name":
        self.assertFormError(response, 'form', 'name',
                             u'This field is required.')

        member = Member.objects.get(pk=2)
        self.assertEqual(start_name, member.name)

    def test_edit_post_form_invalid_memberid(self):
        url = reverse("edit-member", kwargs={"member_id": 21212})
        response = self.client.post(url)
        self.assertEqual(response.status_code, 404)


class TestUnsubscribeMemberView(MembersTestsMixin, TestCase):

    def setUp(self):
        super(TestUnsubscribeMemberView, self).setUp()

    def _assert_redirect_to_login(self, response, url, extra_parameters=""):
        expected_redirect = (
            reverse("login") +
            "?next=" +
            urllib.parse.quote(url + extra_parameters)
        )
        self.assertRedirects(response, expected_redirect)

    def _assert_subscribed(self, member_id):
        member = Member.objects.get(pk=member_id)
        self.assertTrue(member.mailout)

    def _assert_unsubscribed(self, member_id):
        member = Member.objects.get(pk=member_id)
        self.assertFalse(member.mailout)

    # GET tests ###########################################
    def test_unsubscribe_get_form(self):
        self._assert_subscribed(2)
{'fraction_non_alphanumeric': 0.12866980119210691, 'fraction_numerical': 0.01208766798711636, 'mean_word_length': 2.6742841596953, 'pattern_counts': {'":': 58, '<': 130, '<?xml version=': 0, '>': 126, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3731733', 'n_tokens_mistral': 16711, 'n_tokens_neox': 15870, 'n_words': 2862}