async def test_body_field_with_site_use_case(
            self, mocked_get_user_roles_for_site):
        """
        If a body field is present, it must be a dictionary containing:
        * the target user id (user_id)
        * the role to be assigned (role_id)
        * either the site or domain to which it must be assigned (site_id or role_id)
        """
        @requester_has_role(body_field=1)  # Correct
        def call_with_body(request, body, **kwargs):
            return True

        body = {"user_id": self.user.hex, "role_id": 1, "site_id": 1}

        # Test the case where the requester has no roles for the specified domain.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            set())

        with self.assertRaises(JSONForbidden):
            await call_with_body(self.mocked_request, body)

        # Test the case where the requester has the role for the specified domain.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {1})

        self.assertTrue(await call_with_body(self.mocked_request, body))

        # Test the case where the requester has the TECH_ADMIN role
        mocked_get_user_roles_for_site.side_effect = return_tech_admin_role_for_testing

        self.assertTrue(await call_with_body(self.mocked_request, body))
    async def test_no_body_field_with_site_use_case(
            self, field, mocked_get_user_roles_for_site):
        kwargs = {field: 1, "site_id_field": 2, "role_id_field": 3}

        @requester_has_role(**kwargs)
        def call_without_body(request, user_or_invitation, site, role,
                              **kwargs):
            return True

        # Test the case where the requester has no roles for the specified domain.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            set())

        with self.assertRaises(JSONForbidden):
            await call_without_body(self.mocked_request, self.user.hex, 1, 1)

        # Test the case where the requester has the role for the specified domain.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {1})

        self.assertTrue(await call_without_body(self.mocked_request,
                                                self.user.hex, 1, 1))

        # Test the case where the requester has the TECH_ADMIN role
        mocked_get_user_roles_for_site.side_effect = return_tech_admin_role_for_testing

        self.assertTrue(await call_without_body(self.mocked_request,
                                                self.user.hex, 1, 1))
    async def test_empty_resource_permissions_lists(self, mocked_function):
        """
        Check the expected behaviour when an empty resource permission list
        is used with the any or all operation.
        We mock a response for the get_user_roles_for_site() function in
        order to test the case where the specified required resource
        permission list is empty.
        """
        # require_permissions(all, []) always succeeds, regardless of which
        # roles the user has.
        @require_permissions(all, [], nocache=True)
        def empty_all(_request):
            return True

        # require_permissions(any, []) always fails, regardless of which roles
        # the user has.
        @require_permissions(any, [], nocache=True)
        def empty_any(_request):
            return "You must be a tech admin"

        mocked_function.side_effect = make_coroutine_returning(
            {0})  # An arbitrary id

        # Always gets allowed, regardless of the user's roles
        self.assertTrue(await empty_all(self.mocked_request))

        # Never gets allowed unless the user has the TECH_ADMIN role
        with self.assertRaises(JSONForbidden):
            await empty_any(self.mocked_request)

        mocked_function.side_effect = make_coroutine_returning(
            {Mappings.role_id_for(TECH_ADMIN_ROLE_LABEL)})
        self.assertEqual(await empty_any(self.mocked_request),
                         "You must be a tech admin")
    async def test_management_portal_allowed_permission(self, mocked_function):
        """
        When a permission decorator specifies that the Management Portal should be
        allowed to make the request, even if the user does not have the permission, the
        permission is granted.
        """
        @require_permissions(any, [],
                             nocache=True,
                             allow_for_management_portal=True)
        def example_call(_request):
            # The caller is also the target user
            return _request["token"]["aud"] == MANAGEMENT_PORTAL_CLIENT_ID

        mocked_function.side_effect = make_coroutine_returning(
            set())  # No roles

        # This call is not allowed because the request is not made from the Management Portal
        with self.assertRaises(JSONForbidden):
            await example_call(self.mocked_request)

        mocked_request_from_management_portal = self.mocked_request
        mocked_request_from_management_portal["token"][
            "aud"] = MANAGEMENT_PORTAL_CLIENT_ID

        # Allow the call when the call is made from the Management Portal
        self.assertTrue(await
                        example_call(mocked_request_from_management_portal))
    async def test_body_field_with_custom_fields_names_and_site(
            self, mocked_get_user_roles_for_site):
        """
        If a body field is present, it must be a dictionary containing:
        * the target user id (user_id)
        * the role to be assigned (role_id)
        * either the site or domain to which it must be assigned (site_id or role_id)
        """
        @requester_has_role(request_field=1,
                            body_field=0,
                            target_user_id_field="some_user",
                            site_id_field="some_site",
                            role_id_field="some_role")
        def call_with_body(body, request,
                           **kwargs):  # Note that request is not first
            return True

        body = {"some_user": self.user.hex, "some_role": 1, "some_site": 1}

        # Test the case where the requester has no roles for the specified domain.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            set())

        with self.assertRaises(JSONForbidden):
            await call_with_body(body, self.mocked_request)

        # Test the case where the requester has the role for the specified domain.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {1})

        self.assertTrue(await call_with_body(body, self.mocked_request))

        # Test the case where the requester has the TECH_ADMIN role
        mocked_get_user_roles_for_site.side_effect = return_tech_admin_role_for_testing

        self.assertTrue(await call_with_body(body, self.mocked_request))
    async def test_user_has_permissions(self, mocked_get_user_roles_for_site,
                                        mocked_get_role_resource_permissions):
        """
        """
        # All users will have only 'role1' on any site
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {TEST_ROLE_LABEL_TO_ID_MAP["role1"]})

        async def dummy_get_role_resource_permissions(_request,
                                                      role,
                                                      resource,
                                                      nocache=False):
            # Return hand-crafted responses for this test.
            role_id = role if type(
                role) is int else TEST_ROLE_LABEL_TO_ID_MAP[role]
            resource_id = resource if type(resource) is int else \
                TEST_RESOURCE_URN_TO_ID_MAP[resource]
            responses = {
                (1, 1): {TEST_PERMISSION_NAME_TO_ID_MAP["permission1"]},
                (2, 2): {TEST_PERMISSION_NAME_TO_ID_MAP["permission2"]}
            }
            return responses.get((role_id, resource_id), [])

        mocked_get_role_resource_permissions.side_effect = \
            dummy_get_role_resource_permissions

        user = uuid1()
        site = 1

        # Because the user has only role1 assigned and role1 implies only
        # permission1 on resource1, only this one permission check will succeed.
        self.assertTrue(await utils.user_has_permissions(
            self.dummy_request,
            user,
            any, [("urn:resource1", "permission1")],
            site=site))

        self.assertFalse(await utils.user_has_permissions(
            self.dummy_request,
            user,
            any, [("urn:resource1", "permission2")],
            site=site))

        self.assertFalse(await utils.user_has_permissions(
            self.dummy_request,
            user,
            any, [("urn:resource2", "permission2")],
            site=site))
    async def test_positional_args(self, mocked_function):
        """
        Check that positional argument lookups of the request works.
        We mock a response for the get_user_roles_for_site() function in
        order to check that it was called with the appropriate values.
        """
        @require_permissions(all, [], request_field=1, nocache=True)
        def positional_args(_arg1, _request):
            return True

        mocked_function.side_effect = make_coroutine_returning(set())
        # Call the test function...
        await positional_args("some_value", self.mocked_request)
        # ...and verify that the mocked function was called with the right
        # arguments.
        mocked_function.assert_called_with(self.mocked_request,
                                           self.user,
                                           self.site_id,
                                           nocache=True)
    async def test_keyword_args(self, mocked_function):
        """
        Check that keyword-based lookups of the user and site information works.
        We mock a response for the get_user_roles_for_site() function in
        order to check that it was called with the appropriate values.
        """
        @require_permissions(all, [],
                             request_field="i_am_a_request",
                             nocache=True)
        def keyword_args(**kwargs):
            return True

        mocked_function.side_effect = make_coroutine_returning(set())
        # Call the test function...
        await keyword_args(arg=123, i_am_a_request=self.mocked_request)
        # ...and verify that the mocked function was called with the right
        # arguments.
        mocked_function.assert_called_with(self.mocked_request,
                                           self.user,
                                           self.site_id,
                                           nocache=True)
    async def test_target_user_implied_permission(self, mocked_function):
        """
        When a permission decorator specifies a target user field and that field
        contains the user id of the user making the request, then implicit permission
        is granted.
        """
        @require_permissions(any, [], nocache=True, target_user_field=1)
        def example_call_with_user(_request, the_user_id):
            # The caller is also the target user
            return _request["token"]["sub"] == the_user_id

        mocked_function.side_effect = make_coroutine_returning(
            set())  # No roles

        # Never gets allowed if the user id in the request token does not match
        # the value in the target user field.
        with self.assertRaises(JSONForbidden):
            await example_call_with_user(self.mocked_request, "fake_user_id")

        # Allow the call when the caller is also the target user.
        self.assertTrue(await example_call_with_user(self.mocked_request,
                                                     str(self.user)))
    async def test_stacked_decorators(self, mocked_function):
        """
        We mock a response for the get_user_roles_for_site() function in
        order to test the case where the specified required resource
        permission list is empty.
        """
        @require_permissions(all, [], nocache=True)
        @require_permissions(any, [], nocache=True)
        def stack(_request):
            raise RuntimeError("This should never get executed")

        @require_permissions(any, [], nocache=True)
        @require_permissions(all, [], nocache=True)
        def reverse_stack(_request):
            raise RuntimeError("This should never get executed")

        mocked_function.side_effect = make_coroutine_returning(
            {1000})  # An arbitrary id

        with self.assertRaises(JSONForbidden):
            await stack(self.mocked_request)

        with self.assertRaises(JSONForbidden):
            await reverse_stack(self.mocked_request)
    async def test_context_header(self, mocked_site_function,
                                  mocked_domain_function):
        """
        """
        @require_permissions(all, [("urn:resource1", "permission1")],
                             nocache=True)
        async def somecall(_request):
            return True

        mocked_site_function.side_effect = make_coroutine_returning(
            set())  # No roles
        mocked_domain_function.side_effect = make_coroutine_returning(
            set())  # No roles

        # Invalid value
        mocked_request = make_mocked_request(
            "GET", "/", headers={PORTAL_CONTEXT_HEADER: "foo"})
        mocked_request["token"] = {
            "sub": str(self.user),
            "aud": MANAGEMENT_PORTAL_CLIENT_ID
        }

        with self.assertRaises(JSONBadRequest):
            await somecall(mocked_request)

        # Invalid value
        mocked_request = make_mocked_request(
            "GET", "/", headers={PORTAL_CONTEXT_HEADER: "foo:bar"})
        mocked_request["token"] = {
            "sub": str(self.user),
            "aud": MANAGEMENT_PORTAL_CLIENT_ID
        }
        with self.assertRaises(JSONBadRequest):
            await somecall(mocked_request)

        # Invalid value
        mocked_request = make_mocked_request(
            "GET", "/", headers={PORTAL_CONTEXT_HEADER: "foo:123"})
        mocked_request["token"] = {
            "sub": str(self.user),
            "aud": MANAGEMENT_PORTAL_CLIENT_ID
        }
        with self.assertRaises(JSONBadRequest):
            await somecall(mocked_request)

        # Valid header, but Management Portal is not the caller
        mocked_request = make_mocked_request(
            "GET", "/", headers={PORTAL_CONTEXT_HEADER: "d:123"})
        mocked_request["token"] = {
            "sub": str(self.user),
            "aud": MANAGEMENT_PORTAL_CLIENT_ID + "foo"
        }
        with self.assertRaises(JSONForbidden):
            await somecall(mocked_request)

        # Valid site but not roles
        mocked_request = make_mocked_request(
            "GET", "/", headers={PORTAL_CONTEXT_HEADER: "s:123"})
        mocked_request["token"] = {
            "sub": str(self.user),
            "aud": MANAGEMENT_PORTAL_CLIENT_ID
        }
        with self.assertRaises(JSONForbidden):
            await somecall(mocked_request)

        # Valid domain, but no roles
        mocked_request = make_mocked_request(
            "GET", "/", headers={PORTAL_CONTEXT_HEADER: "d:123"})
        mocked_request["token"] = {
            "sub": str(self.user),
            "aud": MANAGEMENT_PORTAL_CLIENT_ID
        }
        with self.assertRaises(JSONForbidden):
            await somecall(mocked_request)

        # Roles will be returned
        mocked_site_function.side_effect = make_coroutine_returning(
            {Mappings.role_id_for(TECH_ADMIN_ROLE_LABEL)})
        mocked_domain_function.side_effect = make_coroutine_returning(
            {Mappings.role_id_for(TECH_ADMIN_ROLE_LABEL)})

        # Valid site
        mocked_request = make_mocked_request(
            "GET", "/", headers={PORTAL_CONTEXT_HEADER: "s:123"})
        mocked_request["token"] = {
            "sub": str(self.user),
            "aud": MANAGEMENT_PORTAL_CLIENT_ID
        }
        self.assertTrue(await somecall(mocked_request))

        # Valid domain, but no roles
        mocked_request = make_mocked_request(
            "GET", "/", headers={PORTAL_CONTEXT_HEADER: "d:123"})
        mocked_request["token"] = {
            "sub": str(self.user),
            "aud": MANAGEMENT_PORTAL_CLIENT_ID
        }
        self.assertTrue(await somecall(mocked_request))
    async def test_combo(self, mocked_get_user_roles_for_site,
                         mocked_get_role_resource_permissions):
        """
        A test of the 'any' operator by mocking roles, resources and
        permissions.

        :param mocked_get_user_roles_for_site: Function mocked by @patch
            decorator
        :param mocked_get_role_resource_permissions: Function mocked by @patch
            decorator
        """
        async def dummy_get_role_resource_permissions(_request,
                                                      role,
                                                      resource,
                                                      nocache=False):
            # Return hand-crafted responses for this test.
            responses = {
                (1, "urn:resource1"): [1],
                (2, "urn:resource2"): [2],
                (3, "urn:resource3"): [3],
            }
            return responses.get((role, resource), [])

        mocked_get_role_resource_permissions.side_effect = \
            dummy_get_role_resource_permissions

        @require_permissions(all, [("urn:resource1", "permission1")],
                             nocache=True)
        @require_permissions(any, [("urn:resource2", "permission2"),
                                   ("urn:resource3", "permission3")],
                             nocache=True)
        def combo_requirement(_request):
            return True

        # The values for the user and site is not important since this test
        # mocks the roles returned.

        # If the user has no roles, then access is denied.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            set())
        with self.assertRaises(JSONForbidden):
            await combo_requirement(self.mocked_request)

        # If the user has roles partially fulfilling the required permissions,
        # then access is denied.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {1})
        with self.assertRaises(JSONForbidden):
            await combo_requirement(self.mocked_request)

        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {2})
        with self.assertRaises(JSONForbidden):
            await combo_requirement(self.mocked_request)

        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {3})
        with self.assertRaises(JSONForbidden):
            await combo_requirement(self.mocked_request)

        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {2, 3})
        with self.assertRaises(JSONForbidden):
            await combo_requirement(self.mocked_request)

        # If the user has roles with the necessary permissions,
        # then access is allowed.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {1, 2})
        self.assertTrue(await combo_requirement(self.mocked_request))

        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {1, 3})
        self.assertTrue(await combo_requirement(self.mocked_request))
    async def test_any(self, mocked_get_user_roles_for_site,
                       mocked_get_role_resource_permissions):
        """
        A test of the 'any' operator by mocking roles, resources and
        permissions.
        """
        async def dummy_get_role_resource_permissions(_request,
                                                      role,
                                                      resource,
                                                      nocache=False):
            # Return hand-crafted responses for this test.
            responses = {
                (1, "urn:resource1"): [1],
                (2, "urn:resource2"): [2],
            }
            return responses.get((role, resource), [])

        mocked_get_role_resource_permissions.side_effect = \
            dummy_get_role_resource_permissions

        @require_permissions(any, [("urn:resource1", "permission1")],
                             nocache=True)
        def single_requirement(_request):
            return True

        # The values for the user and site is not important since this test
        # mocks the roles returned.

        # If the user has no roles, then access is denied.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            set())
        with self.assertRaises(JSONForbidden):
            await single_requirement(self.mocked_request)

        # If the user has a role without the necessary permission,
        # then access is denied.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {2})
        with self.assertRaises(JSONForbidden):
            await single_requirement(self.mocked_request)

        # If the user has a role with the necessary permission,
        # then access is allowed.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            {1})
        self.assertTrue(await single_requirement(self.mocked_request))

        @require_permissions(any, [("urn:resource1", "permission1"),
                                   ("urn:resource2", "permission2")],
                             nocache=True)
        def multiple_requirements(_request):
            return True

        # If the user has any one of the permissions, then access is
        # allowed.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            ["role1"])
        self.assertTrue(await multiple_requirements(self.mocked_request))

        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            ["role2"])
        self.assertTrue(await multiple_requirements(self.mocked_request))

        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            ["role1", "role2"])
        self.assertTrue(await multiple_requirements(self.mocked_request))

        # If the user has a role without the necessary permission,
        # then access is denied.
        mocked_get_user_roles_for_site.side_effect = make_coroutine_returning(
            ["role3"])
        with self.assertRaises(JSONForbidden):
            await single_requirement(self.mocked_request)