from AccessControl import Unauthorized
from plone import api
from plone.app.testing import login
from plone.app.testing import logout
from ploneintranet.microblog.interfaces import IMicroblogTool
from ploneintranet.microblog.statusupdate import StatusUpdate
from ploneintranet.microblog.utils import longkeysortreverse
from ploneintranet.suite.testing import PLONEINTRANET_SUITE_INTEGRATION
from zope.component import queryUtility

import unittest


class TestMicroblogSecurity(unittest.TestCase):
    """
    This test expans on microblog/tests/test_tool.py
    It needs complex test fixures which are not available in microblog.
    Instead we use the existing suite test fixture.
    """

    layer = PLONEINTRANET_SUITE_INTEGRATION

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.tool = queryUtility(IMicroblogTool)
        self.workspace = self.portal.workspaces["open-market-committee"]

    def test_member_allowed_write_read(self):
        """Allan is a member of 'open market committee'"""
        login(self.portal, "allan_neece")
        su1 = StatusUpdate("test #foo")
        su2 = StatusUpdate("test #foo", self.workspace)
        self.tool.add(su1)
        # should NOT raise Unauthorized
        self.tool.add(su2)
        # verify read access on both
        self.assertEqual(su1, self.tool.get(su1.id))
        self.assertEqual(su2, self.tool.get(su2.id))

    def test_member_extranet_allowed_read(self):
        """Ulysses is a member of 'open market committee',
        but not a member of 'intranet'
        """
        login(self.portal, "allan_neece")
        su1 = StatusUpdate("test #foo")
        su2 = StatusUpdate("test #foo", self.workspace)
        self.tool.add(su1)
        self.tool.add(su2)
        login(self.portal, "ulysses_grant")
        # should not see the global update, only the workspace one
        self.assertFalse(su1.id in list(self.tool.keys()))
        self.assertTrue(su2.id in list(self.tool.keys()))
        # hit the actual accessor for good measure
        self.assertRaises(Unauthorized, self.tool.get, su1.id)
        self.assertEqual(su2, self.tool.get(su2.id))

    def test_member_extranet_allowed_write(self):
        """Ulysses is a member of 'open market committee',
        but not a member of 'intranet'
        """
        login(self.portal, "ulysses_grant")
        su1 = StatusUpdate("test #foo")
        su2 = StatusUpdate("test #foo", self.workspace)
        # no global permissions
        self.assertRaises(Unauthorized, self.tool.add, su1)
        # hey, but adding in the workspace works!
        self.tool.add(su2)
        # hit the read accessor for good measure
        self.assertEqual(su2, self.tool.get(su2.id))

    def test_nonmember_intranet_unauthorized_write(self):
        """Alice is *not* a member of 'open market committee'"""
        login(self.portal, "alice_lindstrom")
        su1 = StatusUpdate("test #foo")
        su2 = StatusUpdate("test #foo", self.workspace)
        self.tool.add(su1)
        # should raise, since Alice is not a member of the workspace
        self.assertRaises(Unauthorized, self.tool.add, su2)

    def test_nonmember_intranet_unauthorized_get(self):
        """Alice is *not* a member of 'open market committee'"""
        login(self.portal, "allan_neece")
        su = StatusUpdate("test #foo", self.workspace)
        self.tool.add(su)
        logout()
        login(self.portal, "alice_lindstrom")
        # should raise, since Alice is not a member of the workspace
        self.assertRaises(Unauthorized, self.tool.get, su.id)

    def test_nonmember_intranet_secured_read(self):
        """Alice is *not* a member of 'open market committee'"""
        login(self.portal, "allan_neece")
        su = StatusUpdate("test #foo", self.workspace)
        self.tool.add(su)
        logout()
        login(self.portal, "alice_lindstrom")
        # silently filters all you're not allowed to see
        self.assertFalse(su.id in list(self.tool.keys()))


class TestMicroblogAdminAccess(unittest.TestCase):
    """
    This test expans on microblog/tests/test_tool.py
    It uses the dynamic character of the suite-generated statusupdates,
    which have different ids on every test run, to extra rigourously
    check the statuscontainer accessors and filtering logic.
    """

    layer = PLONEINTRANET_SUITE_INTEGRATION

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.tool = queryUtility(IMicroblogTool)
        self.allkeys = set(self.tool._status_mapping)

    def test_blacklist(self):
        """Admin should have full access"""
        self.assertIn("Manager", api.user.get_roles())
        self.assertSetEqual(set(), self.tool._blacklist_microblogcontext_uuids())

    def test_allowed(self):
        """Admin should have full access"""
        self.assertSetEqual(self.tool.allowed_status_keys(), self.allkeys)

    def test_keys(self):
        """Admin should have full access"""
        got = set(self.tool.keys(limit=None))
        self.assertSetEqual(got, self.allkeys)

    def test_longkeysortreverse(self):
        """Admin access should not be impacted by longkeysortreverse"""
        self.assertIn("Manager", api.user.get_roles())
        got = list(
            longkeysortreverse(self.tool._status_mapping.keys(), None, None, None)
        )
        self.assertEqual(got, sorted(self.allkeys, reverse=True))
