class EmailNotificationEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        self.mailhost = getUtility(IMailHost)

        registry = getUtility(IRegistry)
        registry['plone.email_from_address'] = '*****@*****.**'
        registry['plone.email_from_name'] = u'Plone test site'

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)
        self.anon_api_session = RelativeSession(self.portal_url)
        self.anon_api_session.headers.update({'Accept': 'application/json'})

        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_email_notification(self):
        response = self.api_session.post('/@email-notification',
                                         json={
                                             'from': '*****@*****.**',
                                             'message': 'Just want to say hi.'
                                         })
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        self.assertTrue('Subject: [No Subject]' in self.mailhost.messages[0])
        self.assertTrue('To: [email protected]' in self.mailhost.messages[0])
        self.assertTrue('Reply-To: [email protected]' in self.mailhost.messages[0])
        self.assertTrue('Just want to say hi.' in self.mailhost.messages[0])

    def test_email_notification_all_parameters(self):
        response = self.api_session.post('/@email-notification',
                                         json={
                                             'from': '*****@*****.**',
                                             'message': 'Just want to say hi.',
                                             'name': 'John Doe',
                                             'subject': 'This is the subject.'
                                         })
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        self.assertTrue(
            '=?utf-8?q?This_is_the_subject' in self.mailhost.messages[0])
        self.assertTrue('To: [email protected]' in self.mailhost.messages[0])
        self.assertTrue('John Doe' in self.mailhost.messages[0])
        self.assertTrue('Reply-To: [email protected]' in self.mailhost.messages[0])
        self.assertTrue('Just want to say hi.' in self.mailhost.messages[0])
Ejemplo n.º 2
0
class SerializerFunctionalTest(unittest.TestCase):
    """ """
    layer = PLONE_APP_JSON_FIELD_FUNCTIONAL_TESTING

    def setUp(self):
        """ """
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

    def test_serializer(self):
        """" """
        id_ = 'test-hospital'
        json_body = {
            '@type': 'TestToken',
            'title': 'Test Organization xxx',
            'id': id_
        }
        with open(os.path.join(FHIR_FIXTURE_PATH, 'Organization.json'), 'r') as f:
            json_body['resource'] = json.load(f)

        response = self.api_session.post(
             self.portal_url,
             json=json_body,
        )
        self.assertEqual(201, response.status_code)
        response = self.api_session.get(self.portal_url + '/' + id_)

        self.assertEqual(200, response.status_code)
        self.assertEqual(response.json()['resource']['resourceType'], json_body['resource']['resourceType'])
Ejemplo n.º 3
0
class TestTUSWithAT(unittest.TestCase):

    layer = PLONE_RESTAPI_AT_FUNCTIONAL_TESTING

    def setUp(self):
        if not HAS_AT:
            raise unittest.SkipTest("Skip tests if Archetypes is not present")
        self.portal = self.layer["portal"]
        setRoles(self.portal, TEST_USER_ID, ["Manager"])
        login(self.portal, TEST_USER_NAME)

        self.folder = api.content.create(container=self.portal,
                                         type="Folder",
                                         id="testfolder",
                                         title="Testfolder")
        self.upload_url = "{}/@tus-upload".format(self.folder.absolute_url())
        transaction.commit()

        self.api_session = RelativeSession(self.portal.absolute_url())
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (TEST_USER_NAME, TEST_USER_PASSWORD)

    def tearDown(self):
        self.api_session.close()

    def test_tus_can_upload_pdf_file(self):
        # initialize the upload with POST
        pdf_file_path = os.path.join(os.path.dirname(__file__),
                                     UPLOAD_PDF_FILENAME)
        pdf_file_size = os.path.getsize(pdf_file_path)
        metadata = _prepare_metadata(UPLOAD_PDF_FILENAME, UPLOAD_PDF_MIMETYPE)
        response = self.api_session.post(
            self.upload_url,
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(pdf_file_size),
                "Upload-Metadata": metadata,
            },
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers["Location"]

        # upload the data with PATCH
        with open(pdf_file_path, "rb") as pdf_file:
            response = self.api_session.patch(
                location,
                headers={
                    "Content-Type": "application/offset+octet-stream",
                    "Upload-Offset": "0",
                    "Tus-Resumable": "1.0.0",
                },
                data=pdf_file,
            )
        self.assertEqual(response.status_code, 204)

        transaction.commit()
        self.assertEqual([UPLOAD_PDF_FILENAME], self.folder.contentIds())
Ejemplo n.º 4
0
class TestTUSWithAT(unittest.TestCase):

    layer = PLONE_RESTAPI_AT_FUNCTIONAL_TESTING

    def setUp(self):
        if not HAS_AT:
            raise unittest.SkipTest('Skip tests if Archetypes is not present')
        self.portal = self.layer['portal']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        login(self.portal, TEST_USER_NAME)

        self.folder = api.content.create(container=self.portal,
                                         type='Folder',
                                         id='testfolder',
                                         title='Testfolder')
        self.upload_url = '{}/@tus-upload'.format(self.folder.absolute_url())
        transaction.commit()

        self.api_session = RelativeSession(self.portal.absolute_url())
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (TEST_USER_NAME, TEST_USER_PASSWORD)

    def tearDown(self):
        self.api_session.close()

    def test_tus_can_upload_pdf_file(self):
        # initialize the upload with POST
        pdf_file_path = os.path.join(os.path.dirname(__file__),
                                     UPLOAD_PDF_FILENAME)
        pdf_file_size = os.path.getsize(pdf_file_path)
        metadata = _prepare_metadata(UPLOAD_PDF_FILENAME, UPLOAD_PDF_MIMETYPE)
        response = self.api_session.post(
            self.upload_url,
            headers={'Tus-Resumable': '1.0.0',
                     'Upload-Length': str(pdf_file_size),
                     'Upload-Metadata': metadata}
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers['Location']

        # upload the data with PATCH
        with open(pdf_file_path, 'rb') as pdf_file:
            response = self.api_session.patch(
                location,
                headers={
                    'Content-Type': 'application/offset+octet-stream',
                    'Upload-Offset': '0',
                    'Tus-Resumable': '1.0.0'
                },
                data=pdf_file)
        self.assertEqual(response.status_code, 204)

        transaction.commit()
        self.assertEqual([UPLOAD_PDF_FILENAME], self.folder.contentIds())
Ejemplo n.º 5
0
class TestSchemaformdataEndpoint(unittest.TestCase):

    layer = PLONE_FORMBUILDER_ACCEPTANCE_TESTING

    def setUp(self):

        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        # import ipdb;ipdb.set_trace()
        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        self.portal.invokeFactory(
            'SchemaFormFolder',
            id='folder1',
            title='My Folder'
        )
        # wftool = getToolByName(self.portal, 'portal_workflow')
        # wftool.doActionFor(self.portal.folder1, 'publish')
        transaction.commit()

    def test_post_to_folder_creates_document(self):
        response = self.api_session.post(
            self.portal.folder1.absolute_url() + '/@schemaformdata',
            headers={'Accept': 'application/json'},
            auth=(SITE_OWNER_NAME, SITE_OWNER_PASSWORD),
            json={
                "@type": "SchemaFormData",
                "id": "mydocument",
                "title": "My Document",
                "schema_form_data": '{"k1": "val1", "k2": 2}'
            },
        )
        self.assertEqual(201, response.status_code)
        transaction.begin()
        self.assertEqual("My Document", self.portal.folder1.mydocument.Title())
        self.assertEqual("SchemaFormData", response.json().get('@type'))
        self.assertEqual("mydocument", response.json().get('id'))
        self.assertEqual("My Document", response.json().get('title'))
        saved_dict = response.json().get('schema_form_data')
        saved_dict = json.loads(saved_dict)
        self.assertEqual(saved_dict.get('k1'), 'val1')
        self.assertEqual(saved_dict.get('k2'), 2)

        expected_url = "http://*****:*****@id'))
class SendCompleteServiceTest(unittest.TestCase):

    layer = RER_NEWSLETTERPLUGIN_FLASK_API_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.portal_url = self.portal.absolute_url()

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.channel = api.content.create(
            container=self.portal,
            type='Channel',
            title='Example channel',
            is_subscribable=True,
        )
        self.channel_url = self.channel.absolute_url()
        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_reply_with_error_if_required_fields_not_passed(self):
        payload = {}
        response = self.api_session.post('{}/@send-complete'.format(
            self.channel_url),
                                         json=payload)
        self.assertEqual(response.status_code, 400)

    def test_reply_with_error_if_id_is_invalid(self):
        payload = {'send_uid': 'foo'}
        response = self.api_session.post('{}/@send-complete'.format(
            self.channel_url),
                                         json=payload)
        self.assertEqual(response.status_code, 500)

    def test_update_end_date_if_is_right_uid(self):
        adapter = getMultiAdapter((self.channel, self.request), IChannelSender)
        history = adapter.get_annotations_for_channel(key=HISTORY_KEY)
        history.append(PersistentDict({'uid': 'foo'}))
        transaction.commit()

        payload = {'send_uid': 'foo'}
        response = self.api_session.post('{}/@send-complete'.format(
            self.channel_url),
                                         json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        self.assertNotEqual(history[0]['send_date_end'], '---')
        self.assertTrue(history[0]['completed'])

    def test_mark_not_complete_if_pass_error_flag(self):
        adapter = getMultiAdapter((self.channel, self.request), IChannelSender)
        history = adapter.get_annotations_for_channel(key=HISTORY_KEY)
        history.append(PersistentDict({'uid': 'foo'}))
        transaction.commit()

        payload = {'send_uid': 'foo', 'error': True}
        response = self.api_session.post('{}/@send-complete'.format(
            self.channel_url),
                                         json=payload)
        transaction.commit()
        self.assertEqual(response.status_code, 204)
        self.assertNotEqual(history[0]['send_date_end'], '---')
        self.assertFalse(history[0]['completed'])
Ejemplo n.º 7
0
class TestAddons(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.ps = getToolByName(self.portal, "portal_setup")
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

    def test_get_addon_record(self):
        response = self.api_session.get("/@addons/plone.session")

        self.assertEqual(response.status_code, 200)
        result = response.json()

        self.assertEqual(result["@id"],
                         self.portal_url + u"/@addons/plone.session")
        self.assertEqual(result["id"], u"plone.session")
        # self.assertEqual(result['is_installed'], False)
        self.assertEqual(result["title"], u"Session refresh support")
        self.assertEqual(result["description"],
                         u"Optional plone.session refresh support.")
        self.assertEqual(result["profile_type"], u"default")
        self.assertEqual(result["upgrade_info"], {})
        self.assertEqual(result["install_profile_id"],
                         u"plone.session:default")

    def test_get_addon_listing(self):
        response = self.api_session.get("/@addons")

        self.assertEqual(response.status_code, 200)
        response = response.json()
        self.assertIn("items", response)

    def test_install_uninstall_addon(self):
        def _get_install_status(self):
            response = self.api_session.get("/@addons/plone.session")
            result = response.json()
            return result["is_installed"]

        # Check to make sure the addon is currently shown as not installed
        self.assertEqual(_get_install_status(self), False)

        response = self.api_session.post("/@addons/plone.session/install")
        self.assertEqual(response.status_code, 204)
        self.assertEqual(safe_unicode(response.content), "")

        # Check to make sure the addon is currently shown as installed
        self.assertEqual(_get_install_status(self), True)

        # Now uninstall the addon
        response = self.api_session.post("/@addons/plone.session/uninstall")
        self.assertEqual(response.status_code, 204)
        self.assertEqual(safe_unicode(response.content), "")

        # Check to make sure the addon is currently shown as not installed
        self.assertEqual(_get_install_status(self), False)

    def test_install_uninstall_addon_with_representation(self):

        # Check to make sure the addon is currently shown as not installed
        response = self.api_session.get("/@addons/plone.session")
        result = response.json()
        self.assertEqual(result["is_installed"], False)

        # Install the addon
        response = self.api_session.post(
            "/@addons/plone.session/install",
            headers={"Prefer": "return=representation"},
        )
        self.assertEqual(response.status_code, 200)
        result = response.json()

        # Check to make sure the addon is currently shown as installed
        session = [a for a in result["items"] if a["id"] == u"plone.session"]
        self.assertEqual(len(session), 1)
        self.assertTrue(session[0]["is_installed"])

        # Now uninstall the addon
        response = self.api_session.post(
            "/@addons/plone.session/uninstall",
            headers={"Prefer": "return=representation"},
        )
        self.assertEqual(response.status_code, 200)
        result = response.json()

        # Check to make sure the addon is currently shown as not installed
        session = [a for a in result["items"] if a["id"] == u"plone.session"]
        self.assertEqual(len(session), 1)
        self.assertFalse(session[0]["is_installed"])

    def test_upgrade_addon(self):
        def _get_upgrade_info(self):
            response = self.api_session.get("/@addons/plone.restapi")
            result = response.json()
            return result["upgrade_info"]

        # Set need upgrade state
        self.ps.setLastVersionForProfile("plone.restapi:default", "0002")
        transaction.commit()
        self.assertEqual(
            {
                "available": True,
                "hasProfile": True,
                "installedVersion": "0002",
                "newVersion": "0006",
                "required": True,
            },
            _get_upgrade_info(self),
        )

        # Now call the upgrade
        response = self.api_session.post("/@addons/plone.restapi/upgrade")
        self.assertEqual(response.status_code, 204)
        self.assertEqual(safe_unicode(response.content), "")
        self.assertEqual(
            {
                "available": False,
                "hasProfile": True,
                "installedVersion": "0006",
                "newVersion": "0006",
                "required": False,
            },
            _get_upgrade_info(self),
        )

    def test_upgrade_addon_with_representation(self):
        response = self.api_session.get("/@addons/plone.restapi")
        result = response.json()
        last_version = result["upgrade_info"]

        # Set need upgrade state
        self.ps.setLastVersionForProfile("plone.restapi:default", "0002")
        transaction.commit()
        response = self.api_session.get("/@addons/plone.restapi")
        result = response.json()
        self.assertEqual(
            {
                "available": True,
                "hasProfile": True,
                "installedVersion": "0002",
                "newVersion": last_version["newVersion"],
                "required": True,
            },
            result["upgrade_info"],
        )

        # Now call the upgrade
        response = self.api_session.post(
            "/@addons/plone.restapi/upgrade",
            headers={"Prefer": "return=representation"},
        )
        self.assertEqual(response.status_code, 200)
        result = response.json()

        # Check to make sure the addon is at last version
        session = [a for a in result["items"] if a["id"] == u"plone.restapi"]
        self.assertEqual(len(session), 1)
        self.assertEqual(last_version, session[0]["upgrade_info"])
Ejemplo n.º 8
0
class TestLocking(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        self.request = self.layer['request']
        login(self.portal, SITE_OWNER_NAME)
        self.doc = self.portal[self.portal.invokeFactory('Document',
                                                         id='doc1',
                                                         title='My Document')]
        alsoProvides(self.doc, ITTWLockable)

        self.api_session = RelativeSession(self.doc.absolute_url())
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_lock_object(self):
        response = self.api_session.post('/@lock', )
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertTrue(ILockable(self.doc).locked())

    def test_lock_object_non_stealable(self):
        response = self.api_session.post('/@lock', json={'stealable': False})
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertTrue(INonStealableLock.providedBy(self.doc))

    def test_lock_object_with_custom_timeout(self):
        response = self.api_session.post('/@lock', json={'timeout': 86400})
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.doc.wl_lockValues()[0].getTimeout(), 86400)

    def test_unlock_object(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        transaction.commit()
        response = self.api_session.post('/@unlock')
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertFalse(lockable.locked())

    def test_refresh_lock(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        modified = self.doc.wl_lockValues()[0].getModifiedTime()
        transaction.commit()
        response = self.api_session.post('/@refresh-lock')
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertTrue(
            self.doc.wl_lockValues()[0].getModifiedTime() > modified)

    def test_lock_info_for_locked_object(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        transaction.commit()
        response = self.api_session.get('/@lock')

        self.assertEqual(response.status_code, 200)
        self.assertTrue(response.json()['locked'])

    def test_lock_info_for_unlocked_object(self):
        response = self.api_session.get('/@lock')

        self.assertEqual(response.status_code, 200)
        self.assertFalse(response.json()['locked'])

    def test_update_locked_object_without_token_fails(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        transaction.commit()
        response = self.api_session.patch('/', json={'title': 'New Title'})
        transaction.commit()
        self.assertEqual(response.status_code, 403)
        self.assertEqual(self.doc.Title(), 'My Document')

    def test_update_locked_object_with_token_succeeds(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        transaction.commit()
        response = self.api_session.patch(
            '/',
            headers={'Lock-Token': lockable.lock_info()[0]['token']},
            json={'title': 'New Title'})
        transaction.commit()
        self.assertEqual(response.status_code, 204)
        self.assertEqual(self.doc.Title(), 'New Title')
Ejemplo n.º 9
0
class TestTraversal(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.request = self.layer['request']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Document', id='front-page')
        self.document = self.portal['front-page']
        self.document.title = u"Welcome to Plone"
        self.document.description = \
            u"Congratulations! You have successfully installed Plone."
        self.document.text = RichTextValue(
            u"If you're seeing this instead of the web site you were " +
            u"expecting, the owner of this web site has just installed " +
            u"Plone. Do not contact the Plone Team or the Plone mailing " +
            u"lists about this.",
            'text/plain',
            'text/html'
        )
        self.document.creation_date = DateTime('2016-01-21T01:14:48+00:00')
        IMutableUUID(self.document).set('1f699ffa110e45afb1ba502f75f7ec33')
        self.document.reindexObject()
        self.document.modification_date = DateTime('2016-01-21T01:24:11+00:00')
        import transaction
        transaction.commit()
        self.browser = Browser(self.app)
        self.browser.handleErrors = False
        self.browser.addHeader(
            'Authorization',
            'Basic %s:%s' % (SITE_OWNER_NAME, SITE_OWNER_PASSWORD,)
        )

    def test_documentation_document(self):
        response = self.api_session.get(self.document.absolute_url())
        save_response_for_documentation('document.json', response)

    def test_documentation_news_item(self):
        self.portal.invokeFactory('News Item', id='newsitem')
        self.portal.newsitem.title = 'My News Item'
        self.portal.newsitem.description = u'This is a news item'
        self.portal.newsitem.text = RichTextValue(
            u"Lorem ipsum",
            'text/plain',
            'text/html'
        )
        image_file = os.path.join(os.path.dirname(__file__), u'image.png')
        self.portal.newsitem.image = NamedBlobImage(
            data=open(image_file, 'r').read(),
            contentType='image/png',
            filename=u'image.png'
        )
        self.portal.newsitem.image_caption = u'This is an image caption.'
        self.portal.newsitem.creation_date = DateTime(
            '2016-01-21T02:14:48+00:00')
        self.portal.newsitem.modification_date = DateTime(
            '2016-01-21T02:24:11+00:00')
        IMutableUUID(self.portal.newsitem).set(
            '80c2a074cb4240d08c9a129e3a834c74')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.newsitem.absolute_url())
        save_response_for_documentation('newsitem.json', response)

    def test_documentation_event(self):
        self.portal.invokeFactory('Event', id='event')
        self.portal.event.title = 'Event'
        self.portal.event.description = u'This is an event'
        self.portal.event.start = datetime(2013, 1, 1, 10, 0)
        self.portal.event.end = datetime(2013, 1, 1, 12, 0)
        self.portal.event.creation_date = DateTime('2016-01-21T03:14:48+00:00')
        self.portal.event.modification_date = DateTime(
            '2016-01-21T03:24:11+00:00')
        IMutableUUID(self.portal.event).set('846d632bc0854c5aa6d3dcae171ed2db')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.event.absolute_url())
        save_response_for_documentation('event.json', response)

    def test_documentation_link(self):
        self.portal.invokeFactory('Link', id='link')
        self.portal.link.title = 'My Link'
        self.portal.link.description = u'This is a link'
        self.portal.remoteUrl = 'http://plone.org'
        self.portal.link.creation_date = DateTime('2016-01-21T04:14:48+00:00')
        self.portal.link.modification_date = DateTime(
            '2016-01-21T04:24:11+00:00')
        IMutableUUID(self.portal.link).set('6ff48d27762143a0ae8d63cee73d9fc2')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.link.absolute_url())
        save_response_for_documentation('link.json', response)

    def test_documentation_file(self):
        self.portal.invokeFactory('File', id='file')
        self.portal.file.title = 'My File'
        self.portal.file.description = u'This is a file'
        pdf_file = os.path.join(
            os.path.dirname(__file__), u'file.pdf'
        )
        self.portal.file.file = NamedBlobFile(
            data=open(pdf_file, 'r').read(),
            contentType='application/pdf',
            filename=u'file.pdf'
        )
        intids = getUtility(IIntIds)
        file_id = intids.getId(self.portal.file)
        self.portal.file.file = RelationValue(file_id)
        self.portal.file.creation_date = DateTime('2016-01-21T05:14:48+00:00')
        self.portal.file.modification_date = DateTime(
            '2016-01-21T05:24:11+00:00')
        IMutableUUID(self.portal.file).set('9b6a4eadb9074dde97d86171bb332ae9')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.file.absolute_url())
        save_response_for_documentation('file.json', response)

    def test_documentation_image(self):
        self.portal.invokeFactory('Image', id='image')
        self.portal.image.title = 'My Image'
        self.portal.image.description = u'This is an image'
        image_file = os.path.join(os.path.dirname(__file__), u'image.png')
        self.portal.image.image = NamedBlobImage(
            data=open(image_file, 'r').read(),
            contentType='image/png',
            filename=u'image.png'
        )
        self.portal.image.creation_date = DateTime('2016-01-21T06:14:48+00:00')
        self.portal.image.modification_date = DateTime(
            '2016-01-21T06:24:11+00:00')
        IMutableUUID(self.portal.image).set('2166e81a0c224fe3b62e197c7fdc9c3e')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.image.absolute_url())
        save_response_for_documentation('image.json', response)

    def test_documentation_folder(self):
        self.portal.invokeFactory('Folder', id='folder')
        self.portal.folder.title = 'My Folder'
        self.portal.folder.description = u'This is a folder with two documents'
        self.portal.folder.invokeFactory(
            'Document',
            id='doc1',
            title='A document within a folder'
        )
        self.portal.folder.invokeFactory(
            'Document',
            id='doc2',
            title='A document within a folder'
        )
        self.portal.folder.creation_date = DateTime(
            '2016-01-21T07:14:48+00:00')
        self.portal.folder.modification_date = DateTime(
            '2016-01-21T07:24:11+00:00')
        IMutableUUID(self.portal.folder).set(
            'fc7881c46d61452db4177bc059d9dcb5')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.folder.absolute_url())
        save_response_for_documentation('folder.json', response)

    def test_documentation_collection(self):
        self.portal.invokeFactory('Collection', id='collection')
        self.portal.collection.title = 'My Collection'
        self.portal.collection.description = \
            u'This is a collection with two documents'
        self.portal.collection.query = [{
            'i': 'portal_type',
            'o': 'plone.app.querystring.operation.string.is',
            'v': 'Document',
        }]
        self.portal.invokeFactory(
            'Document',
            id='doc1',
            title='Document 1'
        )
        self.portal.invokeFactory(
            'Document',
            id='doc2',
            title='Document 2'
        )
        self.portal.collection.creation_date = DateTime(
            '2016-01-21T08:14:48+00:00')
        self.portal.collection.modification_date = DateTime(
            '2016-01-21T08:24:11+00:00')
        IMutableUUID(self.portal.collection).set(
            'd0c89bc77f874ce1aad5720921d875c0')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.collection.absolute_url())
        save_response_for_documentation('collection.json', response)

    def test_documentation_siteroot(self):
        response = self.api_session.get(self.portal.absolute_url())
        save_response_for_documentation('siteroot.json', response)

    def test_documentation_404_not_found(self):
        response = self.api_session.get('non-existing-resource')
        save_response_for_documentation('404_not_found.json', response)

    def test_documentation_search(self):
        query = {'sort_on': 'path'}
        response = self.api_session.get('/search', params=query)
        save_response_for_documentation('search.json', response)

    def test_documentation_workflow(self):
        response = self.api_session.get(
            '{}/workflow'.format(self.document.absolute_url()))
        save_response_for_documentation('workflow_get.json', response)

    def test_documentation_workflow_transition(self):
        response = self.api_session.post(
            '{}/workflow/publish'.format(self.document.absolute_url()))
        save_response_for_documentation('workflow_post.json', response)

    def test_documentation_registry_get(self):
        response = self.api_session.get(
            '/@registry/plone.app.querystring.field.path.title')
        save_response_for_documentation('registry_get.json', response)

    def test_documentation_registry_update(self):
        response = self.api_session.patch(
            '/@registry/',
            json={'plone.app.querystring.field.path.title': 'Value'})
        save_response_for_documentation('registry_update.json', response)

    def test_documentation_types(self):
        response = self.api_session.get('/@types')
        save_response_for_documentation('types.json', response)

    def test_documentation_types_document(self):
        response = self.api_session.get('@types/Document')
        save_response_for_documentation('types_document.json', response)
Ejemplo n.º 10
0
class TestTraversal(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.request = self.layer['request']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()

        self.time_freezer = freeze_time("2016-10-21 19:00:00")
        self.frozen_time = self.time_freezer.start()

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Document', id='front-page')
        self.document = self.portal['front-page']
        self.document.title = u"Welcome to Plone"
        self.document.description = \
            u"Congratulations! You have successfully installed Plone."
        self.document.text = RichTextValue(
            u"If you're seeing this instead of the web site you were " +
            u"expecting, the owner of this web site has just installed " +
            u"Plone. Do not contact the Plone Team or the Plone mailing " +
            u"lists about this.", 'text/plain', 'text/html')
        self.document.creation_date = DateTime('2016-01-21T01:14:48+00:00')
        IMutableUUID(self.document).set('1f699ffa110e45afb1ba502f75f7ec33')
        self.document.reindexObject()
        self.document.modification_date = DateTime('2016-01-21T01:24:11+00:00')
        import transaction
        transaction.commit()
        self.browser = Browser(self.app)
        self.browser.handleErrors = False
        self.browser.addHeader(
            'Authorization', 'Basic %s:%s' % (
                SITE_OWNER_NAME,
                SITE_OWNER_PASSWORD,
            ))

    def tearDown(self):
        self.time_freezer.stop()

    def test_documentation_document(self):
        response = self.api_session.get(self.document.absolute_url())
        save_response_for_documentation('document.json', response)

    def test_documentation_news_item(self):
        self.portal.invokeFactory('News Item', id='newsitem')
        self.portal.newsitem.title = 'My News Item'
        self.portal.newsitem.description = u'This is a news item'
        self.portal.newsitem.text = RichTextValue(u"Lorem ipsum", 'text/plain',
                                                  'text/html')
        image_file = os.path.join(os.path.dirname(__file__), u'image.png')
        self.portal.newsitem.image = NamedBlobImage(data=open(image_file,
                                                              'r').read(),
                                                    contentType='image/png',
                                                    filename=u'image.png')
        self.portal.newsitem.image_caption = u'This is an image caption.'
        self.portal.newsitem.creation_date = DateTime(
            '2016-01-21T02:14:48+00:00')
        self.portal.newsitem.modification_date = DateTime(
            '2016-01-21T02:24:11+00:00')
        IMutableUUID(
            self.portal.newsitem).set('80c2a074cb4240d08c9a129e3a834c74')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.newsitem.absolute_url())
        save_response_for_documentation('newsitem.json', response)

    def test_documentation_event(self):
        self.portal.invokeFactory('Event', id='event')
        self.portal.event.title = 'Event'
        self.portal.event.description = u'This is an event'
        self.portal.event.start = datetime(2013, 1, 1, 10, 0)
        self.portal.event.end = datetime(2013, 1, 1, 12, 0)
        self.portal.event.creation_date = DateTime('2016-01-21T03:14:48+00:00')
        self.portal.event.modification_date = DateTime(
            '2016-01-21T03:24:11+00:00')
        IMutableUUID(self.portal.event).set('846d632bc0854c5aa6d3dcae171ed2db')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.event.absolute_url())
        save_response_for_documentation('event.json', response)

    def test_documentation_link(self):
        self.portal.invokeFactory('Link', id='link')
        self.portal.link.title = 'My Link'
        self.portal.link.description = u'This is a link'
        self.portal.remoteUrl = 'http://plone.org'
        self.portal.link.creation_date = DateTime('2016-01-21T04:14:48+00:00')
        self.portal.link.modification_date = DateTime(
            '2016-01-21T04:24:11+00:00')
        IMutableUUID(self.portal.link).set('6ff48d27762143a0ae8d63cee73d9fc2')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.link.absolute_url())
        save_response_for_documentation('link.json', response)

    def test_documentation_file(self):
        self.portal.invokeFactory('File', id='file')
        self.portal.file.title = 'My File'
        self.portal.file.description = u'This is a file'
        pdf_file = os.path.join(os.path.dirname(__file__), u'file.pdf')
        self.portal.file.file = NamedBlobFile(data=open(pdf_file, 'r').read(),
                                              contentType='application/pdf',
                                              filename=u'file.pdf')
        self.portal.file.creation_date = DateTime('2016-01-21T05:14:48+00:00')
        self.portal.file.modification_date = DateTime(
            '2016-01-21T05:24:11+00:00')
        IMutableUUID(self.portal.file).set('9b6a4eadb9074dde97d86171bb332ae9')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.file.absolute_url())
        save_response_for_documentation('file.json', response)

    def test_documentation_image(self):
        self.portal.invokeFactory('Image', id='image')
        self.portal.image.title = 'My Image'
        self.portal.image.description = u'This is an image'
        image_file = os.path.join(os.path.dirname(__file__), u'image.png')
        self.portal.image.image = NamedBlobImage(data=open(image_file,
                                                           'r').read(),
                                                 contentType='image/png',
                                                 filename=u'image.png')
        self.portal.image.creation_date = DateTime('2016-01-21T06:14:48+00:00')
        self.portal.image.modification_date = DateTime(
            '2016-01-21T06:24:11+00:00')
        IMutableUUID(self.portal.image).set('2166e81a0c224fe3b62e197c7fdc9c3e')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.image.absolute_url())
        save_response_for_documentation('image.json', response)

    def test_documentation_folder(self):
        self.portal.invokeFactory('Folder', id='folder')
        self.portal.folder.title = 'My Folder'
        self.portal.folder.description = u'This is a folder with two documents'
        self.portal.folder.invokeFactory('Document',
                                         id='doc1',
                                         title='A document within a folder')
        self.portal.folder.invokeFactory('Document',
                                         id='doc2',
                                         title='A document within a folder')
        self.portal.folder.creation_date = DateTime(
            '2016-01-21T07:14:48+00:00')
        self.portal.folder.modification_date = DateTime(
            '2016-01-21T07:24:11+00:00')
        IMutableUUID(
            self.portal.folder).set('fc7881c46d61452db4177bc059d9dcb5')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.folder.absolute_url())
        save_response_for_documentation('folder.json', response)

    def test_documentation_collection(self):
        self.portal.invokeFactory('Collection', id='collection')
        self.portal.collection.title = 'My Collection'
        self.portal.collection.description = \
            u'This is a collection with two documents'
        self.portal.collection.query = [{
            'i': 'portal_type',
            'o': 'plone.app.querystring.operation.string.is',
            'v': 'Document',
        }]
        self.portal.invokeFactory('Document', id='doc1', title='Document 1')
        self.portal.invokeFactory('Document', id='doc2', title='Document 2')
        self.portal.collection.creation_date = DateTime(
            '2016-01-21T08:14:48+00:00')
        self.portal.collection.modification_date = DateTime(
            '2016-01-21T08:24:11+00:00')
        IMutableUUID(
            self.portal.collection).set('d0c89bc77f874ce1aad5720921d875c0')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.collection.absolute_url())
        save_response_for_documentation('collection.json', response)

    def test_documentation_siteroot(self):
        response = self.api_session.get(self.portal.absolute_url())
        save_response_for_documentation('siteroot.json', response)

    def test_documentation_404_not_found(self):
        response = self.api_session.get('non-existing-resource')
        save_response_for_documentation('404_not_found.json', response)

    def test_documentation_search(self):
        query = {'sort_on': 'path'}
        response = self.api_session.get('/@search', params=query)
        save_response_for_documentation('search.json', response)

    def test_documentation_workflow(self):
        response = self.api_session.get('{}/@workflow'.format(
            self.document.absolute_url()))
        save_response_for_documentation('workflow_get.json', response)

    def test_documentation_workflow_transition(self):
        self.frozen_time.tick(timedelta(minutes=5))
        response = self.api_session.post('{}/@workflow/publish'.format(
            self.document.absolute_url()))
        save_response_for_documentation('workflow_post.json', response)

    def test_documentation_registry_get(self):
        response = self.api_session.get(
            '/@registry/plone.app.querystring.field.path.title')
        save_response_for_documentation('registry_get.json', response)

    def test_documentation_registry_update(self):
        response = self.api_session.patch(
            '/@registry/',
            json={'plone.app.querystring.field.path.title': 'Value'})
        save_response_for_documentation('registry_update.json', response)

    def test_documentation_types(self):
        response = self.api_session.get('/@types')
        save_response_for_documentation('types.json', response)

    def test_documentation_types_document(self):
        response = self.api_session.get('@types/Document')
        save_response_for_documentation('types_document.json', response)

    def test_documentation_login(self):
        self.portal.acl_users.jwt_auth._secret = 'secret'
        self.portal.acl_users.jwt_auth.use_keyring = False
        self.portal.acl_users.jwt_auth.token_timeout = 0
        import transaction
        transaction.commit()
        self.api_session.auth = None
        response = self.api_session.post('{}/@login'.format(
            self.portal.absolute_url()),
                                         json={
                                             'login': SITE_OWNER_NAME,
                                             'password': SITE_OWNER_PASSWORD
                                         })
        save_response_for_documentation('login.json', response)

    def test_documentation_login_renew(self):
        self.portal.acl_users.jwt_auth._secret = 'secret'
        self.portal.acl_users.jwt_auth.use_keyring = False
        self.portal.acl_users.jwt_auth.token_timeout = 0
        import transaction
        transaction.commit()
        self.api_session.auth = None
        response = self.api_session.post('{}/@login'.format(
            self.portal.absolute_url()),
                                         json={
                                             'login': SITE_OWNER_NAME,
                                             'password': SITE_OWNER_PASSWORD
                                         })
        token = json.loads(response.content)['token']
        response = self.api_session.post(
            '{}/@login-renew'.format(self.portal.absolute_url()),
            headers={'Authorization': 'Bearer {}'.format(token)})
        save_response_for_documentation('login_renew.json', response)

    def test_documentation_logout(self):
        self.portal.acl_users.jwt_auth._secret = 'secret'
        self.portal.acl_users.jwt_auth.use_keyring = False
        self.portal.acl_users.jwt_auth.token_timeout = 0
        self.portal.acl_users.jwt_auth.store_tokens = True
        import transaction
        transaction.commit()
        self.api_session.auth = None
        response = self.api_session.post('{}/@login'.format(
            self.portal.absolute_url()),
                                         json={
                                             'login': SITE_OWNER_NAME,
                                             'password': SITE_OWNER_PASSWORD
                                         })
        token = json.loads(response.content)['token']
        response = self.api_session.post(
            '{}/@logout'.format(self.portal.absolute_url()),
            headers={'Authorization': 'Bearer {}'.format(token)})
        save_response_for_documentation('logout.json', response)

    def test_documentation_batching(self):
        folder = self.portal[self.portal.invokeFactory('Folder',
                                                       id='folder',
                                                       title='Folder')]
        for i in range(7):
            folder.invokeFactory('Document',
                                 id='doc-%s' % str(i + 1),
                                 title='Document %s' % str(i + 1))
        transaction.commit()

        query = {'sort_on': 'path'}
        response = self.api_session.get('/folder/@search?b_size=5',
                                        params=query)
        save_response_for_documentation('batching.json', response)

    def test_documentation_users(self):
        test_user = api.user.get(username=TEST_USER_ID)
        properties = {
            "description": "This is a test user",
            "email": "*****@*****.**",
            "fullname": "Test User",
            "home_page": "http://www.example.com",
            "location": "Bonn",
            "username": "******"
        }
        test_user.setMemberProperties(mapping=properties)
        admin = api.user.get(username='******')
        properties = {
            "description": "This is an admin user",
            "email": "*****@*****.**",
            "fullname": "Administrator",
            "home_page": "http://www.example.com",
            "location": "Berlin",
            "username": "******"
        }
        admin.setMemberProperties(mapping=properties)
        transaction.commit()
        response = self.api_session.get('/@users')
        save_response_for_documentation('users.json', response)

    def test_documentation_users_get(self):
        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(email='*****@*****.**',
                        username='******',
                        properties=properties)
        transaction.commit()
        response = self.api_session.get('@users/noam')
        save_response_for_documentation('users_get.json', response)

    def test_documentation_users_created(self):
        response = self.api_session.post(
            '/@users',
            json={
                'username': '******',
                'email': '*****@*****.**',
                'password': '******',
                'username': '******',
                'fullname': 'Noam Avram Chomsky',
                'home_page': 'web.mit.edu/chomsky',
                'description': 'Professor of Linguistics',
                'location': 'Cambridge, MA'
            },
        )
        save_response_for_documentation('users_created.json', response)

    def test_documentation_users_update(self):
        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(email='*****@*****.**',
                        username='******',
                        properties=properties)
        transaction.commit()

        response = self.api_session.patch(
            '/@users/noam',
            json={
                'email': '*****@*****.**',
            },
        )
        save_response_for_documentation('users_update.json', response)

    def test_documentation_users_delete(self):
        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(email='*****@*****.**',
                        username='******',
                        properties=properties)
        transaction.commit()

        response = self.api_session.delete('/@users/noam')
        save_response_for_documentation('users_delete.json', response)

    def test_documentation_breadcrumbs(self):
        response = self.api_session.get('{}/@components/breadcrumbs'.format(
            self.document.absolute_url()))
        save_response_for_documentation('breadcrumbs.json', response)

    def test_documentation_navigation(self):
        response = self.api_session.get('{}/@components/navigation'.format(
            self.document.absolute_url()))
        save_response_for_documentation('navigation.json', response)
Ejemplo n.º 11
0
class TestLocking(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        login(self.portal, SITE_OWNER_NAME)
        self.doc = self.portal[self.portal.invokeFactory("Document",
                                                         id="doc1",
                                                         title="My Document")]
        alsoProvides(self.doc, ITTWLockable)

        self.api_session = RelativeSession(self.doc.absolute_url(), test=self)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_lock_object(self):
        response = self.api_session.post("/@lock")
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertTrue(ILockable(self.doc).locked())

    def test_lock_object_non_stealable(self):
        response = self.api_session.post("/@lock", json={"stealable": False})
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertTrue(INonStealableLock.providedBy(self.doc))

    def test_lock_object_with_custom_timeout(self):
        response = self.api_session.post("/@lock", json={"timeout": 86400})
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.doc.wl_lockValues()[0].getTimeout(), 86400)

    def test_unlock_object(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        transaction.commit()
        response = self.api_session.delete("/@lock")
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertFalse(lockable.locked())

    def test_refresh_lock(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        modified = self.doc.wl_lockValues()[0].getModifiedTime()
        transaction.commit()
        response = self.api_session.patch("/@lock")
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertTrue(
            self.doc.wl_lockValues()[0].getModifiedTime() > modified)

    def test_lock_info_for_locked_object(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        transaction.commit()
        response = self.api_session.get("/@lock")

        self.assertEqual(response.status_code, 200)
        self.assertTrue(response.json()["locked"])

    def test_lock_info_for_unlocked_object(self):
        response = self.api_session.get("/@lock")

        self.assertEqual(response.status_code, 200)
        self.assertFalse(response.json()["locked"])

    def test_update_locked_object_without_token_fails(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        transaction.commit()
        response = self.api_session.patch("/", json={"title": "New Title"})
        transaction.commit()
        self.assertEqual(response.status_code, 403)
        self.assertEqual(self.doc.Title(), "My Document")

    def test_update_locked_object_with_token_succeeds(self):
        lockable = ILockable(self.doc)
        lockable.lock()
        transaction.commit()
        response = self.api_session.patch(
            "/",
            headers={"Lock-Token": lockable.lock_info()[0]["token"]},
            json={"title": "New Title"},
        )
        transaction.commit()
        self.assertEqual(response.status_code, 204)
        self.assertEqual(self.doc.Title(), "New Title")
Ejemplo n.º 12
0
class TestUsersEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(
            email='*****@*****.**',
            username='******',
            properties=properties
        )
        transaction.commit()

    def test_list_users(self):
        response = self.api_session.get('/@users')

        self.assertEqual(200, response.status_code)
        self.assertEqual(3, len(response.json()))
        user_ids = [user['id'] for user in response.json()]
        self.assertIn('admin', user_ids)
        self.assertIn('test_user_1_', user_ids)
        self.assertIn('noam', user_ids)
        noam = [x for x in response.json() if x.get('username') == 'noam'][0]
        self.assertEqual('noam', noam.get('id'))
        self.assertEqual(
            self.portal.absolute_url() + '/@users/noam',
            noam.get('@id')
        )
        self.assertEqual('*****@*****.**', noam.get('email'))
        self.assertEqual('Noam Avram Chomsky', noam.get('fullname'))
        self.assertEqual('web.mit.edu/chomsky', noam.get('home_page'))  # noqa
        self.assertEqual('Professor of Linguistics', noam.get('description'))  # noqa
        self.assertEqual('Cambridge, MA', noam.get('location'))

    def test_add_user(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        howard = api.user.get(userid='howard')
        self.assertEqual(
            "*****@*****.**", howard.getProperty('email')
        )

    def test_add_user_username_is_required(self):
        response = self.api_session.post(
            '/@users',
            json={
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue('"Property \'username\' is required' in response.text)

    def test_add_user_password_is_required(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******"
            },
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue('"Property \'password\' is required' in response.text)

    def test_add_user_email_is_required_if_email_login_is_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "password": "******"
            },
        )

        self.assertEqual(400, response.status_code)
        self.assertTrue('"Property \'email\' is required' in response.text)

    def test_add_user_email_with_email_login_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            '/@users',
            json={
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        self.assertTrue(api.user.get(userid='*****@*****.**'))

    def test_add_user_with_email_login_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user = api.user.get(userid='*****@*****.**')
        self.assertTrue(user)
        self.assertEqual('*****@*****.**', user.getUserName())
        self.assertEqual('*****@*****.**', user.getProperty('email'))

    def test_get_user(self):
        response = self.api_session.get('/@users/noam')

        self.assertEqual(response.status_code, 200)
        self.assertEqual('noam', response.json().get('id'))
        self.assertEqual(
            self.portal.absolute_url() + '/@users/noam',
            response.json().get('@id')
        )
        self.assertEqual(
            '*****@*****.**',
            response.json().get('email')
        )
        self.assertEqual('Noam Avram Chomsky', response.json().get('fullname'))
        self.assertEqual('web.mit.edu/chomsky', response.json().get('home_page'))  # noqa
        self.assertEqual('Professor of Linguistics', response.json().get('description'))  # noqa
        self.assertEqual('Cambridge, MA', response.json().get('location'))

    def test_get_search_user_with_filter(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()
        response = self.api_session.get('/@users', params={'query': 'noa'})

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual('noam', response.json()[0].get('id'))
        self.assertEqual(
            self.portal.absolute_url() + '/@users/noam',
            response.json()[0].get('@id')
        )
        self.assertEqual(
            '*****@*****.**',
            response.json()[0].get('email')
        )
        self.assertEqual('Noam Avram Chomsky', response.json()[0].get('fullname'))  # noqa

        response = self.api_session.get('/@users', params={'query': 'howa'})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual('howard', response.json()[0].get('id'))

    def test_get_non_existing_user(self):
        response = self.api_session.get('/@users/non-existing-user')

        self.assertEqual(response.status_code, 404)

    def test_update_user(self):
        payload = {
            'fullname': 'Noam A. Chomsky',
            'username': '******',
            'email': '*****@*****.**'
        }
        response = self.api_session.patch('/@users/noam', json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        noam = api.user.get(userid='noam')
        self.assertEqual('noam', noam.getUserId())  # user id never changes
        self.assertEqual('avram', noam.getUserName())
        self.assertEqual('Noam A. Chomsky', noam.getProperty('fullname'))
        self.assertEqual(
            '*****@*****.**',
            noam.getProperty('email')
        )

    def test_update_user_password(self):
        old_password_hashes = dict(
            self.portal.acl_users.source_users._user_passwords
        )
        payload = {'password': '******'}
        self.api_session.patch('/@users/noam', json=payload)
        transaction.commit()

        new_password_hashes = dict(
            self.portal.acl_users.source_users._user_passwords
        )
        self.assertNotEqual(
            old_password_hashes['noam'], new_password_hashes['noam']
        )

    def test_delete_user(self):
        response = self.api_session.delete('/@users/noam')
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        self.assertEqual(None, api.user.get(userid='noam'))

    def test_delete_non_existing_user(self):
        response = self.api_session.delete('/@users/non-existing-user')
        transaction.commit()

        self.assertEqual(response.status_code, 404)
Ejemplo n.º 13
0
class TestCaptcha(unittest.TestCase):

    layer = VOLTO_FORMSUPPORT_API_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.mailhost = getUtility(IMailHost)

        self.registry = getUtility(IRegistry)
        self.registry["plone.email_from_address"] = "*****@*****.**"
        self.registry["plone.email_from_name"] = "Plone test site"

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)
        self.anon_api_session = RelativeSession(self.portal_url)
        self.anon_api_session.headers.update({"Accept": "application/json"})

        self.document = api.content.create(
            type="Document",
            title="Example context",
            container=self.portal,
        )
        self.document.blocks = {
            "text-id": {
                "@type": "text"
            },
            "form-id": {
                "@type": "form"
            },
        }
        self.document_url = self.document.absolute_url()
        transaction.commit()

    def tearDown(self):
        self.api_session.close()
        self.anon_api_session.close()

        # set default block
        self.document.blocks = {
            "text-id": {
                "@type": "text"
            },
            "form-id": {
                "@type": "form"
            },
        }
        transaction.commit()

    def submit_form(self, data):
        url = "{}/@submit-form".format(self.document_url)
        response = self.api_session.post(
            url,
            json=data,
        )
        # transaction.commit()
        return response

    def test_recaptcha_no_settings(self):
        self.document.blocks = {
            "text-id": {
                "@type": "text"
            },
            "form-id": {
                "@type":
                "form",
                "default_subject":
                "block subject",
                "default_from":
                "*****@*****.**",
                "send":
                True,
                "subblocks": [
                    {
                        "field_id": "contact",
                        "field_type": "from",
                        "use_as_bcc": True,
                    },
                ],
                "captcha":
                "recaptcha",
            },
        }
        self.registry.registerInterface(IReCaptchaSettings)
        transaction.commit()
        response = self.submit_form(data={
            "data": [
                {
                    "label": "Message",
                    "value": "just want to say hi"
                },
            ],
            "block_id":
            "form-id",
        }, )
        transaction.commit()
        self.assertEqual(response.status_code, 500)
        self.assertEqual(
            response.json()["message"],
            "No recaptcha private key configured. Go to path/to/site/@@recaptcha-settings "
            "to configure.",
        )

    def test_recaptcha(self):
        self.registry.registerInterface(IReCaptchaSettings)
        settings = self.registry.forInterface(IReCaptchaSettings)
        settings.public_key = "public"
        settings.private_key = "private"

        self.document.blocks = {
            "text-id": {
                "@type": "text"
            },
            "form-id": {
                "@type":
                "form",
                "default_subject":
                "block subject",
                "default_from":
                "*****@*****.**",
                "send":
                True,
                "subblocks": [
                    {
                        "field_id": "contact",
                        "field_type": "from",
                        "use_as_bcc": True,
                    },
                ],
                "captcha":
                "recaptcha",
            },
        }
        transaction.commit()

        response = self.submit_form(data={
            "data": [
                {
                    "label": "Message",
                    "value": "just want to say hi"
                },
            ],
            "block_id":
            "form-id",
        }, )
        transaction.commit()
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["message"],
                         "No captcha token provided.")

        with patch("collective.volto.formsupport.captcha.recaptcha.submit"
                   ) as mock_submit:
            mock_submit.return_value = Mock(is_valid=False)
            response = self.submit_form(data={
                "data": [
                    {
                        "label": "Message",
                        "value": "just want to say hi"
                    },
                ],
                "block_id":
                "form-id",
                "captcha": {
                    "token": "12345"
                },
            }, )
            transaction.commit()
            mock_submit.assert_called_once_with("12345", "private",
                                                "127.0.0.1")
            self.assertEqual(response.status_code, 400)
            self.assertEqual(
                response.json()["message"],
                "The code you entered was wrong, please enter the new one.",
            )

        with patch("collective.volto.formsupport.captcha.recaptcha.submit"
                   ) as mock_submit:
            mock_submit.return_value = Mock(is_valid=True)
            response = self.submit_form(data={
                "data": [
                    {
                        "label": "Message",
                        "value": "just want to say hi"
                    },
                ],
                "block_id":
                "form-id",
                "captcha": {
                    "token": "12345"
                },
            }, )
            transaction.commit()
            mock_submit.assert_called_once_with("12345", "private",
                                                "127.0.0.1")
            self.assertEqual(response.status_code, 204)

    def test_hcaptcha(self, ):
        self.registry.registerInterface(IHCaptchaSettings)
        settings = self.registry.forInterface(IHCaptchaSettings)
        settings.public_key = "public"
        settings.private_key = "private"

        self.document.blocks = {
            "text-id": {
                "@type": "text"
            },
            "form-id": {
                "@type":
                "form",
                "default_subject":
                "block subject",
                "default_from":
                "*****@*****.**",
                "send":
                True,
                "subblocks": [
                    {
                        "field_id": "contact",
                        "field_type": "from",
                        "use_as_bcc": True,
                    },
                ],
                "captcha":
                "hcaptcha",
            },
        }
        transaction.commit()

        response = self.submit_form(data={
            "data": [
                {
                    "label": "Message",
                    "value": "just want to say hi"
                },
            ],
            "block_id":
            "form-id",
        }, )
        transaction.commit()
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["message"],
                         "No captcha token provided.")

        with patch("collective.volto.formsupport.captcha.hcaptcha.submit"
                   ) as mock_submit:
            mock_submit.return_value = Mock(is_valid=False)
            response = self.submit_form(data={
                "data": [
                    {
                        "label": "Message",
                        "value": "just want to say hi"
                    },
                ],
                "block_id":
                "form-id",
                "captcha": {
                    "token": "12345"
                },
            }, )
            transaction.commit()
            mock_submit.assert_called_once_with("12345", "private",
                                                "127.0.0.1")
            self.assertEqual(response.status_code, 400)
            self.assertEqual(
                response.json()["message"],
                "The code you entered was wrong, please enter the new one.",
            )

        with patch("collective.volto.formsupport.captcha.hcaptcha.submit"
                   ) as mock_submit:
            mock_submit.return_value = Mock(is_valid=True)
            response = self.submit_form(data={
                "data": [
                    {
                        "label": "Message",
                        "value": "just want to say hi"
                    },
                ],
                "block_id":
                "form-id",
                "captcha": {
                    "token": "12345"
                },
            }, )
            transaction.commit()
            mock_submit.assert_called_once_with("12345", "private",
                                                "127.0.0.1")
            self.assertEqual(response.status_code, 204)

    def test_get_vocabulary(self):
        response = self.api_session.get(
            "/@vocabularies/collective.volto.formsupport.captcha.providers")
        self.assertEqual(response.status_code, 200)
        data = response.json()
        # no adapters configured
        self.assertTrue(data["@id"].endswith(
            "@vocabularies/collective.volto.formsupport.captcha.providers"))
        self.assertEqual(data["items_total"], 0)
        self.assertEqual(data["items"], [])

        # configure recaptcha
        self.registry.registerInterface(IReCaptchaSettings)
        settings = self.registry.forInterface(IReCaptchaSettings)
        settings.public_key = "public"
        settings.private_key = "private"
        transaction.commit()
        response = self.api_session.get(
            "/@vocabularies/collective.volto.formsupport.captcha.providers")
        self.assertEqual(response.status_code, 200)
        data = response.json()
        # no adapters configured
        self.assertTrue(data["@id"].endswith(
            "@vocabularies/collective.volto.formsupport.captcha.providers"))
        self.assertEqual(data["items_total"], 1)
        self.assertEqual(data["items"], [{
            "title": "Google ReCaptcha",
            "token": "recaptcha"
        }])
class TestUsersEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        self.mailhost = getUtility(IMailHost)

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)
        self.anon_api_session = RelativeSession(self.portal_url)
        self.anon_api_session.headers.update({'Accept': 'application/json'})

        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(email='*****@*****.**',
                        username='******',
                        properties=properties,
                        password=u'password')
        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Other user',
        }
        api.user.create(email='*****@*****.**',
                        username='******',
                        properties=properties,
                        password=u'otherpassword')
        transaction.commit()

    def tearDown(self):
        self.api_session.close()
        self.anon_api_session.close()

    def test_list_users(self):
        response = self.api_session.get('/@users')

        self.assertEqual(200, response.status_code)
        self.assertEqual(4, len(response.json()))
        user_ids = [user['id'] for user in response.json()]
        self.assertIn('admin', user_ids)
        self.assertIn('test_user_1_', user_ids)
        self.assertIn('noam', user_ids)
        noam = [x for x in response.json() if x.get('username') == 'noam'][0]
        self.assertEqual('noam', noam.get('id'))
        self.assertEqual(self.portal.absolute_url() + '/@users/noam',
                         noam.get('@id'))
        self.assertEqual('*****@*****.**', noam.get('email'))
        self.assertEqual('Noam Avram Chomsky', noam.get('fullname'))
        self.assertEqual('web.mit.edu/chomsky', noam.get('home_page'))  # noqa
        self.assertEqual('Professor of Linguistics',
                         noam.get('description'))  # noqa
        self.assertEqual('Cambridge, MA', noam.get('location'))

    def test_list_users_without_being_manager(self):
        noam_api_session = RelativeSession(self.portal_url)
        noam_api_session.headers.update({'Accept': 'application/json'})
        noam_api_session.auth = ('noam', 'password')

        response = noam_api_session.get('/@users')
        self.assertEqual(response.status_code, 401)
        noam_api_session.close()

    def test_list_users_as_anonymous(self):

        response = self.anon_api_session.get('/@users')
        self.assertEqual(response.status_code, 401)

    def test_add_user(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
                "roles": [
                    "Contributor",
                ],
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        howard = api.user.get(userid='howard')
        self.assertEqual("*****@*****.**",
                         howard.getProperty('email'))
        self.assertIn('Contributor', api.user.get_roles(username="******"))

    def test_add_user_username_is_required(self):
        response = self.api_session.post(
            '/@users',
            json={"password": "******"},
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue('Property \'username\' is required' in response.text)

    def test_add_user_password_is_required(self):
        response = self.api_session.post(
            '/@users',
            json={"username": "******"},
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue(('You have to either send a '
                         'password or sendPasswordReset') in response.text)

    def test_add_user_email_is_required_if_email_login_is_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "password": "******"
            },
        )

        self.assertEqual(400, response.status_code)
        self.assertTrue(
            'Property \'username\' is not allowed' in response.text)

    def test_add_user_email_with_email_login_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            '/@users',
            json={
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        self.assertTrue(api.user.get(userid='*****@*****.**'))

    def test_username_is_not_allowed_with_email_login_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue(
            'Property \'username\' is not allowed' in response.text)

    def test_add_user_with_email_login_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            '/@users',
            json={
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user = api.user.get(userid='*****@*****.**')
        self.assertTrue(user)
        self.assertEqual('*****@*****.**', user.getUserName())
        self.assertEqual('*****@*****.**', user.getProperty('email'))

    def test_add_user_with_sendPasswordRest_sends_email(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "sendPasswordReset": True
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        self.assertTrue(
            'To: [email protected]' in self.mailhost.messages[0])

    def test_add_user_send_properties(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "password": "******",
                "email": "*****@*****.**",
                "fullname": "Howard Zinn",
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        member = api.user.get(username='******')
        self.assertEqual(member.getProperty('fullname'), 'Howard Zinn')

    def test_add_anon_user_sends_properties_are_saved(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "Howard Zinn",
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        member = api.user.get(username='******')
        self.assertEqual(member.getProperty('fullname'), 'Howard Zinn')

    def test_add_anon_no_roles(self):
        """Make sure anonymous users cannot set their own roles.
           Allowing so would make them Manager.
        """
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "roles": ['Manager'],
            },
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        errors = response.json()['error']['errors']
        fields = [x['field'] for x in errors]
        self.assertEqual(['roles'], fields)

    def test_add_user_with_uuid_as_userid_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        security_settings.use_uuid_as_userid = True
        transaction.commit()
        response = self.api_session.post(
            '/@users',
            json={
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user_id = response.json()['id']
        user = api.user.get(userid=user_id)
        self.assertTrue(user)
        self.assertEqual('*****@*****.**', user.getUserName())
        self.assertEqual('*****@*****.**', user.getProperty('email'))

    def test_get_user(self):
        response = self.api_session.get('/@users/noam')

        self.assertEqual(response.status_code, 200)
        self.assertEqual('noam', response.json().get('id'))
        self.assertEqual(self.portal.absolute_url() + '/@users/noam',
                         response.json().get('@id'))
        self.assertEqual('*****@*****.**',
                         response.json().get('email'))
        self.assertEqual('Noam Avram Chomsky', response.json().get('fullname'))
        self.assertEqual('web.mit.edu/chomsky',
                         response.json().get('home_page'))  # noqa
        self.assertEqual('Professor of Linguistics',
                         response.json().get('description'))  # noqa
        self.assertEqual('Cambridge, MA', response.json().get('location'))

    def test_get_user_as_anonymous(self):
        response = self.anon_api_session.get('/@users/noam')
        self.assertEqual(response.status_code, 401)

    def test_get_other_user_info_when_logged_in(self):
        noam_api_session = RelativeSession(self.portal_url)
        noam_api_session.headers.update({'Accept': 'application/json'})
        noam_api_session.auth = ('noam', 'password')

        response = noam_api_session.get('/@users/otheruser')
        self.assertEqual(response.status_code, 401)
        noam_api_session.close()

    def test_get_search_user_with_filter(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()
        response = self.api_session.get('/@users', params={'query': 'noa'})

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual('noam', response.json()[0].get('id'))
        self.assertEqual(self.portal.absolute_url() + '/@users/noam',
                         response.json()[0].get('@id'))
        self.assertEqual('*****@*****.**',
                         response.json()[0].get('email'))
        self.assertEqual('Noam Avram Chomsky',
                         response.json()[0].get('fullname'))  # noqa

        response = self.api_session.get('/@users', params={'query': 'howa'})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual('howard', response.json()[0].get('id'))

    def test_get_search_user_with_filter_as_anonymous(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()
        response = self.anon_api_session.get('/@users',
                                             params={'query': 'howa'})
        self.assertEqual(response.status_code, 401)

    def test_get_search_user_with_filter_as_unauthorized_user(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()
        noam_api_session = RelativeSession(self.portal_url)
        noam_api_session.headers.update({'Accept': 'application/json'})
        noam_api_session.auth = ('noam', 'password')

        response = noam_api_session.get('/@users', params={'query': 'howa'})
        self.assertEqual(response.status_code, 401)
        noam_api_session.close()

    def test_get_non_existing_user(self):
        response = self.api_session.get('/@users/non-existing-user')

        self.assertEqual(response.status_code, 404)

    def test_update_user(self):
        payload = {
            'fullname': 'Noam A. Chomsky',
            'username': '******',
            'email': '*****@*****.**'
        }
        response = self.api_session.patch('/@users/noam', json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        noam = api.user.get(userid='noam')
        self.assertEqual('noam', noam.getUserId())  # user id never changes
        self.assertEqual('avram', noam.getUserName())
        self.assertEqual('Noam A. Chomsky', noam.getProperty('fullname'))
        self.assertEqual('*****@*****.**',
                         noam.getProperty('email'))

    def test_user_can_update_himself(self):
        payload = {
            'fullname': 'Noam A. Chomsky',
            'username': '******',
            'email': '*****@*****.**'
        }
        self.api_session.auth = ('noam', 'password')
        response = self.api_session.patch('/@users/noam', json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        noam = api.user.get(userid='noam')
        self.assertEqual('noam', noam.getUserId())  # user id never changes
        self.assertEqual('Noam A. Chomsky', noam.getProperty('fullname'))
        self.assertEqual('*****@*****.**', noam.getProperty('email'))

    def test_update_roles(self):
        self.assertNotIn('Contributor', api.user.get_roles(username='******'))

        self.api_session.patch('/@users/noam',
                               json={'roles': {
                                   'Contributor': True
                               }})
        transaction.commit()
        self.assertIn('Contributor', api.user.get_roles(username='******'))

        self.api_session.patch('/@users/noam',
                               json={'roles': {
                                   'Contributor': False
                               }})
        transaction.commit()
        self.assertNotIn('Contributor', api.user.get_roles(username='******'))

    def test_update_user_password(self):
        old_password_hashes = dict(
            self.portal.acl_users.source_users._user_passwords)
        payload = {'password': '******'}
        response = self.api_session.patch('/@users/noam', json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 204)

        new_password_hashes = dict(
            self.portal.acl_users.source_users._user_passwords)
        self.assertNotEqual(old_password_hashes['noam'],
                            new_password_hashes['noam'])

    def test_anonymous_user_can_not_update_existing_user(self):
        payload = {
            'fullname': 'Noam A. Chomsky',
            'username': '******',
            'email': '*****@*****.**'
        }
        self.api_session.auth = ('noam', 'password')
        response = self.anon_api_session.patch('/@users/noam', json=payload)

        self.assertEqual(response.status_code, 401)

    def test_user_can_not_update_another_user(self):
        payload = {
            'fullname': 'Noam A. Chomsky',
            'username': '******',
            'email': '*****@*****.**'
        }
        self.api_session.auth = ('otheruser', 'otherpassword')
        response = self.api_session.patch('/@users/noam', json=payload)

        self.assertEqual(response.status_code, 403)

    def test_user_requests_password_sends_password_via_mail(self):
        self.api_session.auth = ('noam', 'password')
        payload = {}
        response = self.api_session.post('/@users/noam/reset-password',
                                         json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        # FIXME: Test that mail is sent

    def test_user_can_set_her_own_password(self):
        self.api_session.auth = ('noam', 'password')
        self.portal.manage_permission(SetOwnPassword,
                                      roles=['Authenticated', 'Manager'],
                                      acquire=False)
        transaction.commit()

        payload = {'old_password': '******', 'new_password': '******'}
        response = self.api_session.post('/@users/noam/reset-password',
                                         json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        authed = self.portal.acl_users.authenticate('noam', 'new_password', {})
        self.assertTrue(authed)

    def test_normal_authenticated_user_cannot_set_other_users_password(self):
        self.api_session.auth = ('noam', 'password')
        self.portal.manage_permission(SetOwnPassword,
                                      roles=['Authenticated', 'Manager'],
                                      acquire=False)
        transaction.commit()

        payload = {'old_password': '******', 'new_password': '******'}
        response = self.api_session.post('/@users/otheruser/reset-password',
                                         json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 403)
        self.assertEqual(response.json()['error']['type'], 'Wrong user')

    def test_user_set_own_password_requires_set_own_password_permission(self):
        self.api_session.auth = ('noam', 'password')
        self.portal.manage_permission(SetOwnPassword,
                                      roles=['Manager'],
                                      acquire=False)
        transaction.commit()

        payload = {'old_password': '******', 'new_password': '******'}
        response = self.api_session.post('/@users/noam/reset-password',
                                         json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 403)

    def test_user_set_own_password_requires_old_and_new_password(self):
        self.api_session.auth = ('noam', 'password')
        payload = {'old_password': '******'}
        response = self.api_session.post('/@users/noam/reset-password',
                                         json=payload)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()['error']['type'],
                         'Invalid parameters')
        payload = {'new_password': '******'}
        response = self.api_session.post('/@users/noam/reset-password',
                                         json=payload)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()['error']['type'],
                         'Invalid parameters')

    def test_user_set_own_password_checks_old_password(self):
        self.api_session.auth = ('noam', 'password')
        payload = {
            'new_password': '******',
            'old_password': '******'
        }
        response = self.api_session.post('/@users/noam/reset-password',
                                         json=payload)

        self.assertEqual(response.status_code, 403)
        self.assertEqual(response.json()['error']['type'], 'Wrong password')

    def test_user_set_reset_token_requires_new_password(self):
        self.api_session.auth = ('noam', 'password')
        payload = {'reset_token': 'abc'}
        response = self.api_session.post('/@users/noam/reset-password',
                                         json=payload)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()['error']['type'],
                         'Invalid parameters')

    def test_reset_with_token(self):
        reset_tool = getToolByName(self.portal, 'portal_password_reset')
        reset_info = reset_tool.requestReset('noam')
        token = reset_info['randomstring']
        transaction.commit()

        payload = {'reset_token': token, 'new_password': '******'}
        response = self.api_session.post('/@users/noam/reset-password',
                                         json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        authed = self.portal.acl_users.authenticate('noam', 'new_password', {})
        self.assertTrue(authed)

    def test_reset_with_uuid_as_userid_and_login_email_using_id(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        security_settings.use_uuid_as_userid = True
        transaction.commit()

        response = self.api_session.post(
            '/@users',
            json={
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user_id = response.json()['id']
        user = api.user.get(userid=user_id)
        self.assertTrue(user)

        reset_tool = getToolByName(self.portal, 'portal_password_reset')
        reset_info = reset_tool.requestReset(user.id)
        token = reset_info['randomstring']
        transaction.commit()

        payload = {'reset_token': token, 'new_password': '******'}
        response = self.api_session.post('/@users/{}/reset-password'.format(
            user.id),
                                         json=payload)

        self.assertEqual(response.status_code, 200)

    def test_reset_with_uuid_as_userid_and_login_email_using_mail(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        security_settings.use_uuid_as_userid = True
        transaction.commit()

        response = self.api_session.post(
            '/@users',
            json={
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user_id = response.json()['id']
        user = api.user.get(userid=user_id)
        self.assertTrue(user)

        reset_tool = getToolByName(self.portal, 'portal_password_reset')
        reset_info = reset_tool.requestReset(user.id)
        token = reset_info['randomstring']
        transaction.commit()

        payload = {'reset_token': token, 'new_password': '******'}
        response = self.api_session.post('/@users/{}/reset-password'.format(
            user.getUserName()),
                                         json=payload)

        self.assertEqual(response.status_code, 200)

    def test_reset_and_login_email_using_mail(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()

        response = self.api_session.post(
            '/@users',
            json={
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user_id = response.json()['id']
        user = api.user.get(userid=user_id)
        self.assertTrue(user)

        reset_tool = getToolByName(self.portal, 'portal_password_reset')
        reset_info = reset_tool.requestReset(user.id)
        token = reset_info['randomstring']
        transaction.commit()

        payload = {'reset_token': token, 'new_password': '******'}
        response = self.api_session.post('/@users/{}/reset-password'.format(
            user.getUserName()),
                                         json=payload)

        self.assertEqual(response.status_code, 200)

    def test_delete_user(self):
        response = self.api_session.delete('/@users/noam')
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        self.assertEqual(None, api.user.get(userid='noam'))

    def test_delete_non_existing_user(self):
        response = self.api_session.delete('/@users/non-existing-user')
        transaction.commit()

        self.assertEqual(response.status_code, 404)

    def test_anonymous_requires_enable_self_reg(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = False
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={"password": "******"},
        )
        transaction.commit()

        self.assertEqual(403, response.status_code)

        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={
                "username": "******",
                'email': '*****@*****.**'
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)

    def test_anonymous_without_enable_user_pwd_choice_sends_mail(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={
                "username": "******",
                'email': '*****@*****.**'
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        self.assertTrue(
            'To: [email protected]' in self.mailhost.messages[0])

    def test_anonymous_can_set_password_with_enable_user_pwd_choice(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={
                "username": "******",
                'email': '*****@*****.**',
                'password': '******'
            },
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue(
            'Property \'password\' is not allowed' in response.text)

        security_settings.enable_user_pwd_choice = True
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={
                "username": "******",
                'email': '*****@*****.**',
                'password': '******'
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)

    def test_anonymous_with_enable_user_pwd_choice_doent_send_email(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        security_settings.enable_user_pwd_choice = True
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={
                "username": "******",
                'email': '*****@*****.**',
                'password': '******'
            },
        )
        transaction.commit()

        self.assertEqual(self.mailhost.messages, [])
        self.assertEqual(201, response.status_code)

    def test_anonymous_with_enable_user_sets_only_member_role(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        security_settings.enable_user_pwd_choice = True
        transaction.commit()

        response = self.anon_api_session.post(
            '/@users',
            json={
                "username": "******",
                'email': '*****@*****.**',
                'password': '******'
            },
        )

        response = response.json()
        self.assertIn('Member', response['roles'])
        self.assertEqual(1, len(response['roles']))

    def test_add_user_no_roles_sets_member_as_sensible_default(self):
        response = self.api_session.post(
            '/@users',
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******"
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)

        response = response.json()

        self.assertIn('Member', response['roles'])
        self.assertEqual(1, len(response['roles']))
Ejemplo n.º 15
0
class TestCommentsEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.portal_url = self.portal.absolute_url()

        # Allow discussion
        registry = getUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        settings.globally_enabled = True
        settings.edit_comment_enabled = True
        settings.delete_own_comment_enabled = True

        # doc with comments
        self.doc = api.content.create(
            container=self.portal,
            type="Document",
            id="doc_with_comments",
            title="Document with comments",
            allow_discussion=True,
        )
        api.content.transition(self.doc, "publish")

        api.user.create(username="******", password="******", email="*****@*****.**")

        # Admin session
        self.api_session = RelativeSession(self.portal_url, test=self)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        # User session
        self.user_session = RelativeSession(self.portal_url, test=self)
        self.user_session.headers.update({"Accept": "application/json"})
        self.user_session.auth = ("jos", "jos")

        transaction.commit()

    def tearDown(self):
        self.api_session.close()
        self.user_session.close()

    def test_list_datastructure(self):
        url = f"{self.doc.absolute_url()}/@comments"
        response = self.api_session.get(url)

        self.assertEqual(200, response.status_code)
        data = response.json()
        self.assertEqual({"items_total", "items", "@id"}, set(data))

    def test_list_batching(self):
        url = f"{self.doc.absolute_url()}/@comments"

        self.api_session.post(url, json={"text": "comment 1"})
        self.api_session.post(url, json={"text": "comment 2"})

        response = self.api_session.get(url, params={"b_size": 1})
        self.assertEqual(200, response.status_code)
        data = response.json()
        self.assertIn("batching", data)

    def test_add_comment_to_root(self):
        url = f"{self.doc.absolute_url()}/@comments"

        response = self.api_session.get(url)
        self.assertEqual(0, response.json()["items_total"])

        response = self.api_session.post(url, json={"text": "comment 1"})
        self.assertEqual(204, response.status_code)
        self.assertIn("location", response.headers)

        response = self.api_session.get(url)
        data = response.json()
        self.assertEqual(1, data["items_total"])
        self.assertIsNone(data["items"][0]["in_reply_to"])
        self.assertIsNone(data["items"][0]["@parent"])

    def test_add_comment_to_comment(self):
        url = f"{self.doc.absolute_url()}/@comments"

        response = self.api_session.post(url, json={"text": "comment 1"})
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(url)
        data = response.json()
        parent_id = data["items"][0]["comment_id"]

        SUBTEXT = "sub comment"

        payload = {"text": SUBTEXT, "in_reply_to": parent_id}
        response = self.api_session.post(url, json=payload)
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(url)
        data = response.json()
        sub = [x for x in data["items"] if x["text"]["data"] == SUBTEXT][0]
        self.assertEqual(parent_id, sub["in_reply_to"])

    def test_update(self):
        url = f"{self.doc.absolute_url()}/@comments"
        OLD_TEXT = "comment 1"
        NEW_TEXT = "new text"

        self.api_session.post(url, json={"text": OLD_TEXT})

        response = self.api_session.get(url)
        data = response.json()
        item_texts = [x["text"]["data"] for x in data["items"]]
        self.assertNotIn(NEW_TEXT, item_texts)
        self.assertIn(OLD_TEXT, item_texts)
        comment = data["items"][0]

        payload = {"text": NEW_TEXT}
        response = self.api_session.patch(comment["@id"], json=payload)
        self.assertEqual(204, response.status_code)
        self.assertIn("location", response.headers)

        response = self.api_session.get(url)
        data = response.json()
        item_texts = [x["text"]["data"] for x in data["items"]]
        self.assertIn(NEW_TEXT, item_texts)
        self.assertNotIn(OLD_TEXT, item_texts)

    def test_permissions_delete_comment(self):
        url = f"{self.doc.absolute_url()}/@comments"

        response = self.api_session.post(url, json={"text": "comment"})
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(url)
        comment_url = response.json()["items"][0]["@id"]
        self.assertFalse(comment_url.endswith("@comments"))
        self.assertTrue(response.json()["items"][0]["is_deletable"])

        # Other user may not delete this
        response = self.user_session.delete(comment_url)
        self.assertEqual(401, response.status_code)

        response = self.user_session.get(url)
        self.assertFalse(response.json()["items"][0]["is_deletable"])

        # The owner may
        response = self.api_session.delete(comment_url)
        self.assertEqual(204, response.status_code)

    def test_permissions_update_comment(self):
        url = f"{self.doc.absolute_url()}/@comments"

        response = self.api_session.post(url, json={"text": "comment"})
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(url)
        comment_url = response.json()["items"][0]["@id"]
        self.assertFalse(comment_url.endswith("@comments"))
        self.assertTrue(response.json()["items"][0]["is_editable"])

        # Other user may not update this
        response = self.user_session.patch(comment_url, json={"text": "new"})
        self.assertEqual(401, response.status_code)

        response = self.user_session.get(url)
        self.assertFalse(response.json()["items"][0]["is_editable"])

        # The owner may
        response = self.api_session.patch(comment_url, json={"text": "new"})
        self.assertEqual(204, response.status_code)
Ejemplo n.º 16
0
class TestUsersEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.mailhost = getUtility(IMailHost)

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)
        self.anon_api_session = RelativeSession(self.portal_url)
        self.anon_api_session.headers.update({"Accept": "application/json"})

        properties = {
            "email": "*****@*****.**",
            "username": "******",
            "fullname": "Noam Avram Chomsky",
            "home_page": "web.mit.edu/chomsky",
            "description": "Professor of Linguistics",
            "location": "Cambridge, MA",
        }
        api.user.create(
            email="*****@*****.**",
            username="******",
            properties=properties,
            password=u"password",
        )
        properties = {
            "email": "*****@*****.**",
            "username": "******",
            "fullname": "Other user",
        }
        api.user.create(
            email="*****@*****.**",
            username="******",
            properties=properties,
            password=u"otherpassword",
        )
        transaction.commit()

    def tearDown(self):
        self.api_session.close()
        self.anon_api_session.close()

    def test_list_users(self):
        response = self.api_session.get("/@users")

        self.assertEqual(200, response.status_code)
        self.assertEqual(4, len(response.json()))
        user_ids = [user["id"] for user in response.json()]
        self.assertIn("admin", user_ids)
        self.assertIn("test_user_1_", user_ids)
        self.assertIn("noam", user_ids)
        noam = [x for x in response.json() if x.get("username") == "noam"][0]
        self.assertEqual("noam", noam.get("id"))
        self.assertEqual(self.portal.absolute_url() + "/@users/noam", noam.get("@id"))
        self.assertEqual("*****@*****.**", noam.get("email"))
        self.assertEqual("Noam Avram Chomsky", noam.get("fullname"))
        self.assertEqual("web.mit.edu/chomsky", noam.get("home_page"))  # noqa
        self.assertEqual("Professor of Linguistics", noam.get("description"))  # noqa
        self.assertEqual("Cambridge, MA", noam.get("location"))

    def test_list_users_without_being_manager(self):
        noam_api_session = RelativeSession(self.portal_url)
        noam_api_session.headers.update({"Accept": "application/json"})
        noam_api_session.auth = ("noam", "password")

        response = noam_api_session.get("/@users")
        self.assertEqual(response.status_code, 401)
        noam_api_session.close()

    def test_list_users_as_anonymous(self):

        response = self.anon_api_session.get("/@users")
        self.assertEqual(response.status_code, 401)

    def test_add_user(self):
        response = self.api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
                "roles": ["Contributor"],
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        howard = api.user.get(userid="howard")
        self.assertEqual("*****@*****.**", howard.getProperty("email"))
        self.assertIn("Contributor", api.user.get_roles(username="******"))

    def test_add_user_username_is_required(self):
        response = self.api_session.post("/@users", json={"password": "******"})
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue("Property 'username' is required" in response.text)

    def test_add_user_password_is_required(self):
        response = self.api_session.post("/@users", json={"username": "******"})
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue(
            ("You have to either send a " "password or sendPasswordReset")
            in response.text
        )

    def test_add_user_email_is_required_if_email_login_is_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            "/@users", json={"username": "******", "password": "******"}
        )

        self.assertEqual(400, response.status_code)
        self.assertTrue("Property 'username' is not allowed" in response.text)

    def test_add_user_email_with_email_login_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            "/@users", json={"email": "*****@*****.**", "password": "******"}
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        self.assertTrue(api.user.get(userid="*****@*****.**"))

    def test_username_is_not_allowed_with_email_login_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue("Property 'username' is not allowed" in response.text)

    def test_add_user_with_email_login_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()
        response = self.api_session.post(
            "/@users", json={"email": "*****@*****.**", "password": "******"}
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user = api.user.get(userid="*****@*****.**")
        self.assertTrue(user)
        self.assertEqual("*****@*****.**", user.getUserName())
        self.assertEqual("*****@*****.**", user.getProperty("email"))

    def test_add_user_with_sendPasswordRest_sends_email(self):
        response = self.api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "sendPasswordReset": True,
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        msg = self.mailhost.messages[0]
        if isinstance(msg, bytes) and bytes is not str:
            # Python 3 with Products.MailHost 4.10+
            msg = msg.decode("utf-8")
        self.assertTrue("To: [email protected]" in msg)

    def test_add_user_send_properties(self):
        response = self.api_session.post(
            "/@users",
            json={
                "username": "******",
                "password": "******",
                "email": "*****@*****.**",
                "fullname": "Howard Zinn",
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        member = api.user.get(username="******")
        self.assertEqual(member.getProperty("fullname"), "Howard Zinn")

    def test_add_anon_user_sends_properties_are_saved(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "Howard Zinn",
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        member = api.user.get(username="******")
        self.assertEqual(member.getProperty("fullname"), "Howard Zinn")

    def test_add_anon_no_roles(self):
        """Make sure anonymous users cannot set their own roles.
        Allowing so would make them Manager.
        """
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "roles": ["Manager"],
            },
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        errors = response.json()["error"]["errors"]
        fields = [x["field"] for x in errors]
        self.assertEqual(["roles"], fields)

    def test_add_user_with_uuid_as_userid_enabled(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        security_settings.use_uuid_as_userid = True
        transaction.commit()
        response = self.api_session.post(
            "/@users", json={"email": "*****@*****.**", "password": "******"}
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user_id = response.json()["id"]
        user = api.user.get(userid=user_id)
        self.assertTrue(user)
        self.assertEqual("*****@*****.**", user.getUserName())
        self.assertEqual("*****@*****.**", user.getProperty("email"))

    def test_get_user(self):
        response = self.api_session.get("/@users/noam")

        self.assertEqual(response.status_code, 200)
        self.assertEqual("noam", response.json().get("id"))
        self.assertEqual(
            self.portal.absolute_url() + "/@users/noam", response.json().get("@id")
        )
        self.assertEqual("*****@*****.**", response.json().get("email"))
        self.assertEqual("Noam Avram Chomsky", response.json().get("fullname"))
        self.assertEqual(
            "web.mit.edu/chomsky", response.json().get("home_page")
        )  # noqa
        self.assertEqual(
            "Professor of Linguistics", response.json().get("description")
        )  # noqa
        self.assertEqual("Cambridge, MA", response.json().get("location"))

    def test_get_user_as_anonymous(self):
        response = self.anon_api_session.get("/@users/noam")
        self.assertEqual(response.status_code, 401)

    def test_get_other_user_info_when_logged_in(self):
        noam_api_session = RelativeSession(self.portal_url)
        noam_api_session.headers.update({"Accept": "application/json"})
        noam_api_session.auth = ("noam", "password")

        response = noam_api_session.get("/@users/otheruser")
        self.assertEqual(response.status_code, 401)
        noam_api_session.close()

    def test_get_search_user_with_filter(self):
        response = self.api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )
        transaction.commit()
        response = self.api_session.get("/@users", params={"query": "noa"})

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual("noam", response.json()[0].get("id"))
        self.assertEqual(
            self.portal.absolute_url() + "/@users/noam", response.json()[0].get("@id")
        )
        self.assertEqual("*****@*****.**", response.json()[0].get("email"))
        self.assertEqual(
            "Noam Avram Chomsky", response.json()[0].get("fullname")
        )  # noqa

        response = self.api_session.get("/@users", params={"query": "howa"})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual("howard", response.json()[0].get("id"))

    def test_get_search_user_with_filter_as_anonymous(self):
        response = self.api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )
        transaction.commit()
        response = self.anon_api_session.get("/@users", params={"query": "howa"})
        self.assertEqual(response.status_code, 401)

    def test_get_search_user_with_filter_as_unauthorized_user(self):
        response = self.api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )
        transaction.commit()
        noam_api_session = RelativeSession(self.portal_url)
        noam_api_session.headers.update({"Accept": "application/json"})
        noam_api_session.auth = ("noam", "password")

        response = noam_api_session.get("/@users", params={"query": "howa"})
        self.assertEqual(response.status_code, 401)
        noam_api_session.close()

    def test_get_non_existing_user(self):
        response = self.api_session.get("/@users/non-existing-user")

        self.assertEqual(response.status_code, 404)

    def test_update_user(self):
        payload = {
            "fullname": "Noam A. Chomsky",
            "username": "******",
            "email": "*****@*****.**",
        }
        response = self.api_session.patch("/@users/noam", json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        noam = api.user.get(userid="noam")
        self.assertEqual("noam", noam.getUserId())  # user id never changes
        self.assertEqual("avram", noam.getUserName())
        self.assertEqual("Noam A. Chomsky", noam.getProperty("fullname"))
        self.assertEqual("*****@*****.**", noam.getProperty("email"))

    def test_user_can_update_himself(self):
        payload = {
            "fullname": "Noam A. Chomsky",
            "username": "******",
            "email": "*****@*****.**",
        }
        self.api_session.auth = ("noam", "password")
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        noam = api.user.get(userid="noam")
        self.assertEqual("noam", noam.getUserId())  # user id never changes
        self.assertEqual("Noam A. Chomsky", noam.getProperty("fullname"))
        self.assertEqual("*****@*****.**", noam.getProperty("email"))

    def test_user_can_update_himself_remove_values(self):
        payload = {
            "fullname": "Noam A. Chomsky",
            "username": "******",
            "email": "*****@*****.**",
            "home_page": None,
        }
        self.api_session.auth = ("noam", "password")
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        noam = api.user.get(userid="noam")

        self.assertEqual(None, noam.getProperty("home_page"))

    def test_update_roles(self):
        self.assertNotIn("Contributor", api.user.get_roles(username="******"))

        self.api_session.patch("/@users/noam", json={"roles": {"Contributor": True}})
        transaction.commit()
        self.assertIn("Contributor", api.user.get_roles(username="******"))

        self.api_session.patch("/@users/noam", json={"roles": {"Contributor": False}})
        transaction.commit()
        self.assertNotIn("Contributor", api.user.get_roles(username="******"))

    def test_update_user_password(self):
        old_password_hashes = dict(self.portal.acl_users.source_users._user_passwords)
        payload = {"password": "******"}
        response = self.api_session.patch("/@users/noam", json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 204)

        new_password_hashes = dict(self.portal.acl_users.source_users._user_passwords)
        self.assertNotEqual(old_password_hashes["noam"], new_password_hashes["noam"])

    def test_update_portrait(self):
        payload = {
            "portrait": {
                "filename": "image.gif",
                "encoding": "base64",
                "data": u"R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=",
                "content-type": "image/gif",
            }
        }
        self.api_session.auth = ("noam", "password")
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        user = self.api_session.get("/@users/noam").json()
        self.assertTrue(
            user.get("portrait").endswith("plone/portal_memberdata/portraits/noam")
        )

    def test_update_portrait_with_default_plone_scaling(self):
        payload = {
            "portrait": {
                "filename": "image.gif",
                "encoding": "base64",
                "data": u"R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=",
                "content-type": "image/gif",
                "scale": True,
            }
        }
        self.api_session.auth = ("noam", "password")
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        user = self.api_session.get("/@users/noam").json()
        self.assertTrue(
            user.get("portrait").endswith("plone/portal_memberdata/portraits/noam")
        )

    def test_update_portrait_by_manager(self):
        payload = {
            "portrait": {
                "filename": "image.gif",
                "encoding": "base64",
                "data": u"R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=",
                "content-type": "image/gif",
            }
        }
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        user = self.api_session.get("/@users/noam").json()
        self.assertTrue(
            user.get("portrait").endswith("plone/portal_memberdata/portraits/noam")
        )

    def test_delete_portrait(self):
        payload = {
            "portrait": None,
        }
        self.api_session.auth = ("noam", "password")
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        user = self.api_session.get("/@users/noam").json()

        self.assertTrue(user.get("portrait") is None)

    def test_delete_portrait_by_manager(self):
        payload = {
            "portrait": None,
        }
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        user = self.api_session.get("/@users/noam").json()

        self.assertTrue(user.get("portrait") is None)

    def test_update_user_with_portrait_set_without_updating_portrait(self):
        payload = {
            "portrait": {
                "filename": "image.gif",
                "encoding": "base64",
                "data": u"R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=",
                "content-type": "image/gif",
            }
        }
        self.api_session.auth = ("noam", "password")
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        payload = {
            "fullname": "Noam A. Chomsky",
            "username": "******",
            "email": "*****@*****.**",
            "portrait": "http://*****:*****@users/noam", json=payload)

        self.assertEqual(response.status_code, 204)
        transaction.commit()

        user = self.api_session.get("/@users/noam").json()
        self.assertTrue(
            user.get("portrait").endswith("plone/portal_memberdata/portraits/noam")
        )

    def test_anonymous_user_can_not_update_existing_user(self):
        payload = {
            "fullname": "Noam A. Chomsky",
            "username": "******",
            "email": "*****@*****.**",
        }
        self.api_session.auth = ("noam", "password")
        response = self.anon_api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 401)

    def test_user_can_not_update_another_user(self):
        payload = {
            "fullname": "Noam A. Chomsky",
            "username": "******",
            "email": "*****@*****.**",
        }
        self.api_session.auth = ("otheruser", "otherpassword")
        response = self.api_session.patch("/@users/noam", json=payload)

        self.assertEqual(response.status_code, 403)

    def test_user_requests_password_sends_password_via_mail(self):
        self.api_session.auth = ("noam", "password")
        payload = {}
        response = self.api_session.post("/@users/noam/reset-password", json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        # FIXME: Test that mail is sent

    def test_user_can_set_her_own_password(self):
        self.api_session.auth = ("noam", "password")
        self.portal.manage_permission(
            SetOwnPassword, roles=["Authenticated", "Manager"], acquire=False
        )
        transaction.commit()

        payload = {"old_password": "******", "new_password": "******"}
        response = self.api_session.post("/@users/noam/reset-password", json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        authed = self.portal.acl_users.authenticate("noam", "new_password", {})
        self.assertTrue(authed)

    def test_normal_authenticated_user_cannot_set_other_users_password(self):
        self.api_session.auth = ("noam", "password")
        self.portal.manage_permission(
            SetOwnPassword, roles=["Authenticated", "Manager"], acquire=False
        )
        transaction.commit()

        payload = {"old_password": "******", "new_password": "******"}
        response = self.api_session.post(
            "/@users/otheruser/reset-password", json=payload
        )
        transaction.commit()

        self.assertEqual(response.status_code, 403)
        self.assertEqual(response.json()["error"]["type"], "Wrong user")

    def test_user_set_own_password_requires_set_own_password_permission(self):
        self.api_session.auth = ("noam", "password")
        self.portal.manage_permission(SetOwnPassword, roles=["Manager"], acquire=False)
        transaction.commit()

        payload = {"old_password": "******", "new_password": "******"}
        response = self.api_session.post("/@users/noam/reset-password", json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 403)

    def test_user_set_own_password_requires_old_and_new_password(self):
        self.api_session.auth = ("noam", "password")
        payload = {"old_password": "******"}
        response = self.api_session.post("/@users/noam/reset-password", json=payload)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["error"]["type"], "Invalid parameters")
        payload = {"new_password": "******"}
        response = self.api_session.post("/@users/noam/reset-password", json=payload)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["error"]["type"], "Invalid parameters")

    def test_user_set_own_password_checks_old_password(self):
        self.api_session.auth = ("noam", "password")
        payload = {"new_password": "******", "old_password": "******"}
        response = self.api_session.post("/@users/noam/reset-password", json=payload)

        self.assertEqual(response.status_code, 403)
        self.assertEqual(response.json()["error"]["type"], "Wrong password")

    def test_user_set_reset_token_requires_new_password(self):
        self.api_session.auth = ("noam", "password")
        payload = {"reset_token": "abc"}
        response = self.api_session.post("/@users/noam/reset-password", json=payload)

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["error"]["type"], "Invalid parameters")

    def test_reset_with_token(self):
        reset_tool = getToolByName(self.portal, "portal_password_reset")
        reset_info = reset_tool.requestReset("noam")
        token = reset_info["randomstring"]
        transaction.commit()

        payload = {"reset_token": token, "new_password": "******"}
        response = self.api_session.post("/@users/noam/reset-password", json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        authed = self.portal.acl_users.authenticate("noam", "new_password", {})
        self.assertTrue(authed)

    def test_reset_with_uuid_as_userid_and_login_email_using_id(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        security_settings.use_uuid_as_userid = True
        transaction.commit()

        response = self.api_session.post(
            "/@users", json={"email": "*****@*****.**", "password": "******"}
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user_id = response.json()["id"]
        user = api.user.get(userid=user_id)
        self.assertTrue(user)

        reset_tool = getToolByName(self.portal, "portal_password_reset")
        reset_info = reset_tool.requestReset(user.id)
        token = reset_info["randomstring"]
        transaction.commit()

        payload = {"reset_token": token, "new_password": "******"}
        response = self.api_session.post(
            "/@users/{}/reset-password".format(user.id), json=payload
        )

        self.assertEqual(response.status_code, 200)

    def test_reset_with_uuid_as_userid_and_login_email_using_mail(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        security_settings.use_uuid_as_userid = True
        transaction.commit()

        response = self.api_session.post(
            "/@users", json={"email": "*****@*****.**", "password": "******"}
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user_id = response.json()["id"]
        user = api.user.get(userid=user_id)
        self.assertTrue(user)

        reset_tool = getToolByName(self.portal, "portal_password_reset")
        reset_info = reset_tool.requestReset(user.id)
        token = reset_info["randomstring"]
        transaction.commit()

        payload = {"reset_token": token, "new_password": "******"}
        response = self.api_session.post(
            "/@users/{}/reset-password".format(user.getUserName()), json=payload
        )

        self.assertEqual(response.status_code, 200)

    def test_reset_and_login_email_using_mail(self):
        # enable use_email_as_login
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.use_email_as_login = True
        transaction.commit()

        response = self.api_session.post(
            "/@users", json={"email": "*****@*****.**", "password": "******"}
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        user_id = response.json()["id"]
        user = api.user.get(userid=user_id)
        self.assertTrue(user)

        reset_tool = getToolByName(self.portal, "portal_password_reset")
        reset_info = reset_tool.requestReset(user.id)
        token = reset_info["randomstring"]
        transaction.commit()

        payload = {"reset_token": token, "new_password": "******"}
        response = self.api_session.post(
            "/@users/{}/reset-password".format(user.getUserName()), json=payload
        )

        self.assertEqual(response.status_code, 200)

    def test_delete_user(self):
        response = self.api_session.delete("/@users/noam")
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        self.assertEqual(None, api.user.get(userid="noam"))

    def test_delete_non_existing_user(self):
        response = self.api_session.delete("/@users/non-existing-user")
        transaction.commit()

        self.assertEqual(response.status_code, 404)

    def test_anonymous_requires_enable_self_reg(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = False
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users", json={"password": "******"}
        )
        transaction.commit()

        self.assertEqual(403, response.status_code)

        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users",
            json={"username": "******", "email": "*****@*****.**"},
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)

    def test_anonymous_without_enable_user_pwd_choice_sends_mail(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users",
            json={"username": "******", "email": "*****@*****.**"},
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        msg = self.mailhost.messages[0]
        if isinstance(msg, bytes) and bytes is not str:
            # Python 3 with Products.MailHost 4.10+
            msg = msg.decode("utf-8")
        self.assertTrue("To: [email protected]" in msg)

    def test_anonymous_can_set_password_with_enable_user_pwd_choice(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue("Property 'password' is not allowed" in response.text)

        security_settings.enable_user_pwd_choice = True
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)

    def test_anonymous_with_enable_user_pwd_choice_doent_send_email(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        security_settings.enable_user_pwd_choice = True
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )
        transaction.commit()

        self.assertEqual(self.mailhost.messages, [])
        self.assertEqual(201, response.status_code)

    def test_anonymous_with_enable_user_sets_only_member_role(self):
        security_settings = getAdapter(self.portal, ISecuritySchema)
        security_settings.enable_self_reg = True
        security_settings.enable_user_pwd_choice = True
        transaction.commit()

        response = self.anon_api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )

        response = response.json()
        self.assertIn("Member", response["roles"])
        self.assertEqual(1, len(response["roles"]))

    def test_add_user_no_roles_sets_member_as_sensible_default(self):
        response = self.api_session.post(
            "/@users",
            json={
                "username": "******",
                "email": "*****@*****.**",
                "password": "******",
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)

        response = response.json()

        self.assertIn("Member", response["roles"])
        self.assertEqual(1, len(response["roles"]))
Ejemplo n.º 17
0
class TestGroupsEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.api_session = RelativeSession(self.portal_url, test=self)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        self.gtool = api.portal.get_tool("portal_groups")
        properties = {
            "title": "Plone Team",
            "description": "We are Plone",
            "email": "*****@*****.**",
        }
        self.gtool.addGroup(
            "ploneteam",
            (),
            (),
            properties=properties,
            title=properties["title"],
            description=properties["description"],
        )
        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_list_groups(self):
        response = self.api_session.get("/@groups")

        self.assertEqual(200, response.status_code)
        self.assertEqual(5, len(response.json()))
        user_ids = [group["id"] for group in response.json()]
        self.assertIn("Administrators", user_ids)
        self.assertIn("Reviewers", user_ids)
        self.assertIn("AuthenticatedUsers", user_ids)
        self.assertIn("ploneteam", user_ids)
        ptgroup = [x for x in response.json() if x.get("groupname") == "ploneteam"][0]
        self.assertEqual("ploneteam", ptgroup.get("id"))
        self.assertEqual(
            self.portal.absolute_url() + "/@groups/ploneteam", ptgroup.get("@id")
        )
        self.assertEqual("*****@*****.**", ptgroup.get("email"))
        self.assertEqual("Plone Team", ptgroup.get("title"))
        self.assertEqual("We are Plone", ptgroup.get("description"))

        self.assertEqual(ptgroup.get("roles"), ["Authenticated"])

        # # Assert batched list of group members
        self.assertTrue(
            all(["members" in group for group in response.json()]),
            "Members key found in groups listing",
        )

    def test_add_group(self):
        response = self.api_session.post(
            "/@groups",
            json={
                "groupname": "fwt",
                "email": "*****@*****.**",
                "title": "Framework Team",
                "description": "The Plone Framework Team",
                "roles": ["Manager"],
                "groups": ["Administrators"],
                "users": [SITE_OWNER_NAME, TEST_USER_ID],
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        fwt = self.gtool.getGroupById("fwt")
        self.assertEqual("*****@*****.**", fwt.getProperty("email"))
        self.assertTrue(
            {SITE_OWNER_NAME, TEST_USER_ID}.issubset(set(fwt.getGroupMemberIds())),
            "Userids not found in group",
        )

    def test_add_group_groupname_is_required(self):
        response = self.api_session.post("/@groups", json={"title": "Framework Team"})
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue("\"Property 'groupname' is required" in response.text)

    def test_get_group(self):
        response = self.api_session.get("/@groups/ploneteam")

        self.assertEqual(response.status_code, 200)
        self.assertEqual("ploneteam", response.json().get("id"))
        self.assertEqual(
            self.portal.absolute_url() + "/@groups/ploneteam",
            response.json().get("@id"),
        )
        self.assertEqual("*****@*****.**", response.json().get("email"))
        self.assertEqual("*****@*****.**", response.json().get("email"))
        self.assertEqual("Plone Team", response.json().get("title"))
        self.assertEqual("We are Plone", response.json().get("description"))
        self.assertIn("members", response.json())

    def test_get_search_group_with_filter(self):
        response = self.api_session.get("/@groups", params={"query": "plo"})

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual("ploneteam", response.json()[0].get("id"))
        self.assertEqual(
            self.portal.absolute_url() + "/@groups/ploneteam",
            response.json()[0].get("@id"),
        )
        self.assertEqual("*****@*****.**", response.json()[0].get("email"))

        response = self.api_session.get("/@groups", params={"query": "Auth"})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual("AuthenticatedUsers", response.json()[0].get("id"))

    def test_get_non_existing_group(self):
        response = self.api_session.get("/@groups/non-existing-group")

        self.assertEqual(response.status_code, 404)

    def test_update_group(self):
        ploneteam = self.gtool.getGroupById("ploneteam")
        ploneteam.addMember(SITE_OWNER_NAME)
        transaction.commit()
        self.assertNotIn(TEST_USER_ID, ploneteam.getGroupMemberIds())
        self.assertIn(SITE_OWNER_NAME, ploneteam.getGroupMemberIds())

        payload = {
            "groupname": "ploneteam",
            "email": "*****@*****.**",
            "users": {TEST_USER_ID: True, SITE_OWNER_NAME: False},
        }
        response = self.api_session.patch("/@groups/ploneteam", json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        ploneteam = self.gtool.getGroupById("ploneteam")
        self.assertEqual("ploneteam", ploneteam.id)
        self.assertEqual("Plone Team", ploneteam.getProperty("title"))
        self.assertEqual("*****@*****.**", ploneteam.getProperty("email"))
        self.assertIn(TEST_USER_ID, ploneteam.getGroupMemberIds())
        self.assertNotIn(SITE_OWNER_NAME, ploneteam.getGroupMemberIds())

    def test_delete_group(self):
        response = self.api_session.delete("/@groups/ploneteam")
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        self.assertEqual(None, self.gtool.getGroupById("ploneteam"))

    def test_delete_non_existing_group(self):
        response = self.api_session.delete("/@groups/non-existing-group")
        transaction.commit()

        self.assertEqual(response.status_code, 404)
class TestPublicationFieldsFixes(unittest.TestCase):

    layer = REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING

    def setUp(self):
        tz = "Europe/Rome"
        os.environ["TZ"] = tz
        time.tzset()

        # Patch DateTime's timezone for deterministic behavior.
        self.DT_orig_localZone = DateTime.localZone
        DateTime.localZone = lambda cls=None, ltm=None: tz

        from plone.dexterity import content

        content.FLOOR_DATE = DateTime(1970, 0)
        content.CEILING_DATE = DateTime(2500, 0)
        self._orig_content_zone = content._zone
        content._zone = "GMT+2"

        registry = getUtility(IRegistry)
        registry["plone.portal_timezone"] = tz
        registry["plone.available_timezones"] = [tz]
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        commit()

    def tearDown(self):
        self.api_session.close()

        if "TZ" in os.environ:
            del os.environ["TZ"]
        time.tzset()

        from DateTime import DateTime

        DateTime.localZone = self.DT_orig_localZone

        from plone.dexterity import content

        content._zone = self._orig_content_zone
        content.FLOOR_DATE = DateTime(1970, 0)
        content.CEILING_DATE = DateTime(2500, 0)

        registry = getUtility(IRegistry)
        registry["plone.portal_timezone"] = "UTC"
        registry["plone.available_timezones"] = ["UTC"]

    def test_set_effective_date_store_right_value_in_plone(self):
        effective = DateTime()
        expires = effective + 1
        self.api_session.post(
            self.portal_url,
            json={
                "@type": "Document",
                "id": "mydocument",
                "title": "My Document",
                "effective": "{}Z".format(effective.utcdatetime().isoformat()),
                "expires": "{}Z".format(expires.utcdatetime().isoformat()),
            },
        )
        commit()

        self.assertEqual(
            self.portal["mydocument"].effective().strftime("%d-%m-%Y %H:%M"),
            effective.strftime("%d-%m-%Y %H:%M"),
        )
        self.assertEqual(
            self.portal["mydocument"].expires().strftime("%d-%m-%Y %H:%M"),
            expires.strftime("%d-%m-%Y %H:%M"),
        )
Ejemplo n.º 19
0
class TestSearchFunctional(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.portal_url = self.portal.absolute_url()
        self.request = self.portal.REQUEST
        self.catalog = getToolByName(self.portal, "portal_catalog")

        self.api_session = RelativeSession(self.portal_url, test=self)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        api.user.create(
            email="*****@*****.**",
            username="******",
            password="******",
        )
        api.user.create(
            email="*****@*****.**",
            username="******",
            password="******",
        )

        # /plone/folder
        self.folder = createContentInContainer(self.portal,
                                               "Folder",
                                               id="folder",
                                               title="Some Folder")
        api.user.grant_roles(username="******", roles=["Editor"])
        api.user.grant_roles(username="******",
                             obj=self.folder,
                             roles=["Editor", "Reader"])

        # /plone/folder/doc
        self.doc = createContentInContainer(
            self.folder,
            "DXTestDocument",
            id="doc",
            title="Lorem Ipsum",
            start=DateTime(1950, 1, 1, 0, 0),
            effective=DateTime(1995, 1, 1, 0, 0),
            expires=DateTime(1999, 1, 1, 0, 0),
            test_int_field=42,
            test_list_field=["Keyword1", "Keyword2", "Keyword3"],
            test_bool_field=True,
            test_richtext_field=RichTextValue(
                raw="<p>Some Text</p>",
                mimeType="text/html",
                outputMimeType="text/html",
            ),
        )
        IMutableUUID(self.doc).set("77779ffa110e45afb1ba502f75f77777")
        self.doc.reindexObject()

        # /plone/folder/other-document
        self.doc2 = createContentInContainer(
            self.folder,
            "DXTestDocument",
            id="other-document",
            title="Other Document",
            description="\xdcbersicht",
            start=DateTime(1975, 1, 1, 0, 0),
            effective=DateTime(2015, 1, 1, 0, 0),
            expires=DateTime(2020, 1, 1, 0, 0),
            test_list_field=["Keyword2", "Keyword3"],
            test_bool_field=False,
        )

        # /plone/folder2
        self.folder2 = createContentInContainer(self.portal,
                                                "Folder",
                                                id="folder2",
                                                title="Another Folder")

        # /plone/folder2/doc
        createContentInContainer(
            self.folder2,
            "DXTestDocument",
            id="doc",
            title="Document in second folder",
            start=DateTime(1975, 1, 1, 0, 0),
            effective=DateTime(2015, 1, 1, 0, 0),
            expires=DateTime(2020, 1, 1, 0, 0),
            test_bool_field=False,
        )

        # /plone/doc-outside-folder
        createContentInContainer(
            self.portal,
            "DXTestDocument",
            id="doc-outside-folder",
            title="Doc outside folder",
        )

        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_overall_response_format(self):
        response = self.api_session.get("/@search")

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers.get("Content-Type"),
                         "application/json")

        results = response.json()
        self.assertEqual(
            results["items_total"],
            len(results["items"]),
            "items_total property should match actual item count.",
        )

    def test_search_on_context_constrains_query_by_path(self):
        response = self.api_session.get("/folder/@search")
        self.assertSetEqual(
            {
                "/plone/folder", "/plone/folder/doc",
                "/plone/folder/other-document"
            },
            set(result_paths(response.json())),
        )

    def test_search_in_vhm(self):
        # Install a Virtual Host Monster
        if "virtual_hosting" not in self.app.objectIds():
            # If ZopeLite was imported, we have no default virtual
            # host monster
            from Products.SiteAccess.VirtualHostMonster import (
                manage_addVirtualHostMonster, )

            manage_addVirtualHostMonster(self.app, "virtual_hosting")
        transaction.commit()

        # we don't get a result if we do not provide the full physical path
        response = self.api_session.get("/@search?path=/folder")
        self.assertSetEqual(set(), set(result_paths(response.json())))

        # If we go through the VHM will will get results if we only use
        # the part of the path inside the VHM
        vhm_url = "{}/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/{}".format(
            self.app.absolute_url(),
            "@search?path=/folder",
        )
        response = self.api_session.get(vhm_url)
        self.assertSetEqual(
            {"/folder", "/folder/doc", "/folder/other-document"},
            set(result_paths(response.json())),
        )

    def test_search_in_vhm_multiple_paths(self):
        # Install a Virtual Host Monster
        if "virtual_hosting" not in self.app.objectIds():
            # If ZopeLite was imported, we have no default virtual
            # host monster
            from Products.SiteAccess.VirtualHostMonster import (
                manage_addVirtualHostMonster, )

            manage_addVirtualHostMonster(self.app, "virtual_hosting")
        transaction.commit()

        # path as a list
        query = {"path": ["/folder", "/folder2"]}

        # If we go through the VHM we will get results for multiple paths
        # if we only use the part of the path inside the VHM
        vhm_url = "{}/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/{}".format(
            self.app.absolute_url(),
            "@search",
        )
        response = self.api_session.get(vhm_url, params=query)
        self.assertSetEqual(
            {
                "/folder",
                "/folder/doc",
                "/folder/other-document",
                "/folder2",
                "/folder2/doc",
            },
            set(result_paths(response.json())),
        )

        # path as a dict with a query list
        query = {"path.query": ["/folder", "/folder2"]}

        # If we go through the VHM we will get results for multiple paths
        # if we only use the part of the path inside the VHM
        vhm_url = "{}/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/{}".format(
            self.app.absolute_url(),
            "@search",
        )
        response = self.api_session.get(vhm_url, params=query)
        self.assertSetEqual(
            {
                "/folder",
                "/folder/doc",
                "/folder/other-document",
                "/folder2",
                "/folder2/doc",
            },
            set(result_paths(response.json())),
        )

    def test_path_gets_prefilled_if_missing_from_path_query_dict(self):
        response = self.api_session.get("/@search?path.depth=1")
        self.assertSetEqual(
            {"/plone/folder", "/plone/folder2", "/plone/doc-outside-folder"},
            set(result_paths(response.json())),
        )

    def test_partial_metadata_retrieval(self):
        query = {
            "SearchableText": "lorem",
            "metadata_fields": ["portal_type", "review_state"],
        }
        response = self.api_session.get("/@search", params=query)

        self.assertDictContainsSubset(
            {
                "@id": self.portal_url + "/folder/doc",
                "title": "Lorem Ipsum",
                "portal_type": "DXTestDocument",
                "review_state": "private",
            },
            response.json()["items"][0],
        )

    def test_full_metadata_retrieval(self):
        query = {"SearchableText": "lorem", "metadata_fields": "_all"}
        response = self.api_session.get("/@search", params=query)

        first_item = response.json()["items"][0]
        self.assertLessEqual(
            {
                "@id": self.portal_url + "/folder/doc",
                "Creator": "test_user_1_",
                "Description": "",
                "EffectiveDate": "None",
                "ExpirationDate": "None",
                "Subject": [],
                "Title": "Lorem Ipsum",
                "Type": "DX Test Document",
                "UID": "77779ffa110e45afb1ba502f75f77777",
                "author_name": None,
                "cmf_uid": None,
                "commentators": [],
                "description": "",
                "effective": "1995-01-01T00:00:00+00:00",
                "end": None,
                "exclude_from_nav": False,
                "expires": "1999-01-01T00:00:00+00:00",
                "getId": "doc",
                "getPath": "/plone/folder/doc",
                "getRemoteUrl": None,
                "getURL": self.portal_url + "/folder/doc",
                "id": "doc",
                "in_response_to": None,
                "is_folderish": False,
                "last_comment_date": None,
                "listCreators": ["test_user_1_"],
                "location": None,
                "portal_type": "DXTestDocument",
                "review_state": "private",
                "start": "1950-01-01T00:00:00+00:00",
                "sync_uid": None,
                "title": "Lorem Ipsum",
                "total_comments": 0,
            }.items(),
            first_item.items(),
        )
        # This value changed in Plone 5.2
        # (Dexterity gained support for getObjSize)
        self.assertIn(first_item["getObjSize"], ("0 KB", "1 KB"))

    def test_full_objects_retrieval(self):
        query = {
            "SearchableText": "lorem",
            "metadata_fields": ["portal_type", "review_state"],
            "fullobjects": True,
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            "<p>Some Text</p>",
            response.json()["items"][0]["test_richtext_field"]["data"],
        )
        self.assertEqual(self.portal_url + "/folder/doc",
                         response.json()["items"][0]["@id"])

    def test_full_objects_retrieval_discussion(self):
        # Allow discussion
        registry = getUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        settings.globally_enabled = True
        self.doc.allow_discussion = True

        transaction.commit()

        url = f"{self.doc.absolute_url()}/@comments"
        self.api_session.post(url, json={"text": "comment 1"})
        transaction.commit()

        query = {"portal_type": "Discussion Item", "fullobjects": True}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()["items"]), 1)

    def test_full_objects_retrieval_collections(self):
        self.collection = createContentInContainer(self.folder,
                                                   "Collection",
                                                   id="collection")
        transaction.commit()

        query = {"portal_type": "Collection", "fullobjects": True}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()["items"]), 1)

    def test_search_orphan_brain(self):

        # prevent unindex when deleting self.doc
        old__unindexObject = self.doc.__class__.unindexObject
        self.doc.__class__.unindexObject = lambda *args: None
        self.doc.aq_parent.manage_delObjects([self.doc.getId()])
        self.doc.__class__.unindexObject = old__unindexObject
        # doc deleted but still in portal_catalog
        doc_uid = self.doc.UID()
        self.assertFalse(self.doc in self.doc.aq_parent)
        self.assertTrue(self.portal.portal_catalog(UID=doc_uid))
        transaction.commit()

        # query with fullobjects
        query = {
            "portal_type": "DXTestDocument",
            "fullobjects": True,
            "UID": doc_uid
        }
        response = self.api_session.get("/@search", params=query)
        self.assertEqual(response.status_code, 200, response.content)
        results = response.json()
        self.assertEqual(len(results["items"]), 0)

        # query without fullobjects
        query = {"portal_type": "DXTestDocument", "UID": doc_uid}
        response = self.api_session.get("/@search", params=query)
        self.assertEqual(response.status_code, 200, response.content)
        results = response.json()
        self.assertEqual(len(results["items"]), 1)

    # ZCTextIndex

    def test_fulltext_search(self):
        query = {"SearchableText": "lorem"}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

    def test_fulltext_search_with_non_ascii_characters(self):
        query = {"SearchableText": "\xfcbersicht"}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/folder/other-document"],
                         result_paths(response.json()))

    # KeywordIndex

    def test_keyword_index_str_query(self):
        query = {"test_list_field": "Keyword1"}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

    def test_keyword_index_str_query_or(self):
        query = {"test_list_field": ["Keyword2", "Keyword3"]}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            ["/plone/folder/doc", "/plone/folder/other-document"],
            result_paths(response.json()),
        )

    def test_keyword_index_str_query_and(self):
        query = {
            "test_list_field.query": ["Keyword1", "Keyword2"],
            "test_list_field.operator": "and",
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

    # BooleanIndex

    def test_boolean_index_query(self):
        query = {"test_bool_field": True, "portal_type": "DXTestDocument"}
        response = self.api_session.get("/folder/@search", params=query)
        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

        query = {"test_bool_field": False, "portal_type": "DXTestDocument"}
        response = self.api_session.get("/folder/@search", params=query)
        self.assertEqual(["/plone/folder/other-document"],
                         result_paths(response.json()))

    # FieldIndex

    def test_field_index_int_query(self):
        query = {"test_int_field:int": 42}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

    def test_field_index_int_range_query(self):
        query = {
            "test_int_field.query:int": [41, 43],
            "test_int_field.range": "min:max",
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

    # ExtendedPathIndex

    def test_extended_path_index_query(self):
        query = {"path": "/".join(self.folder.getPhysicalPath())}

        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            sorted([
                "/plone/folder",
                "/plone/folder/doc",
                "/plone/folder/other-document",
            ]),
            sorted(result_paths(response.json())),
        )

    def test_extended_path_index_query_multiple(self):
        # path as a list
        query = {
            "path": [
                "/".join(self.folder.getPhysicalPath()),
                "/".join(self.folder2.getPhysicalPath()),
            ]
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            sorted([
                "/plone/folder",
                "/plone/folder/doc",
                "/plone/folder/other-document",
                "/plone/folder2",
                "/plone/folder2/doc",
            ]),
            sorted(result_paths(response.json())),
        )

        # path as a dict with a query list
        query = {
            "path.query": [
                "/".join(self.folder.getPhysicalPath()),
                "/".join(self.folder2.getPhysicalPath()),
            ]
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            sorted([
                "/plone/folder",
                "/plone/folder/doc",
                "/plone/folder/other-document",
                "/plone/folder2",
                "/plone/folder2/doc",
            ]),
            sorted(result_paths(response.json())),
        )

    def test_extended_path_index_depth_limiting(self):
        lvl1 = createContentInContainer(self.portal, "Folder", id="lvl1")
        lvl2 = createContentInContainer(lvl1, "Folder", id="lvl2")
        createContentInContainer(lvl2, "Folder", id="lvl3")
        transaction.commit()

        path = "/plone/lvl1"

        # Depth 0 - only object identified by path
        query = {"path.query": path, "path.depth": 0}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/lvl1"], result_paths(response.json()))

        # Depth 1 - immediate children
        query = {"path.query": path, "path.depth": 1}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/lvl1/lvl2"], result_paths(response.json()))

        # No depth - object itself and all children
        query = {"path": path}
        response = self.api_session.get("/@search", params=query)

        self.assertSetEqual(
            {"/plone/lvl1", "/plone/lvl1/lvl2", "/plone/lvl1/lvl2/lvl3"},
            set(result_paths(response.json())),
        )

    # DateIndex

    def test_date_index_query(self):
        query = {"start": date(1950, 1, 1).isoformat()}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

    def test_date_index_ranged_query(self):
        query = {
            "start.query":
            [date(1949, 1, 1).isoformat(),
             date(1951, 1, 1).isoformat()],
            "start.range":
            "min:max",
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

    # DateRangeIndex

    def test_date_range_index_query(self):
        query = {"effectiveRange": date(1997, 1, 1).isoformat()}
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual(2, len(result_paths(response.json())))
        self.assertTrue("/plone/folder" in result_paths(response.json()))
        self.assertTrue("/plone/folder/doc" in result_paths(response.json()))

    # DateRecurringIndex

    def test_date_recurring_index_query(self):
        from datetime import datetime

        createContentInContainer(
            self.folder,
            "Event",
            id="event",
            title="Event",
            start=datetime(2013, 1, 1, 0, 0),
            end=datetime(2013, 1, 1, 23, 59),
            whole_day=True,
            recurrence="FREQ=DAILY;COUNT=10;INTERVAL=2",
            timezone="UTC",
        )
        import transaction

        transaction.commit()

        # First occurrence
        query = {"start": date(2013, 1, 1).isoformat()}
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual(["/plone/folder/event"],
                         result_paths(response.json()))

        # No event that day
        query = {"start": date(2013, 1, 2).isoformat()}
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual([], result_paths(response.json()))

        # Second occurrence
        query = {"start": date(2013, 1, 3).isoformat()}
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual(["/plone/folder/event"],
                         result_paths(response.json()))

        # Ranged query
        query = {
            "start.query":
            [date(2013, 1, 1).isoformat(),
             date(2013, 1, 5).isoformat()],
            "start.range":
            "min:max",
        }
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual(["/plone/folder/event"],
                         result_paths(response.json()))

    # UUIDIndex

    def test_uuid_index_query(self):
        IMutableUUID(self.doc).set("7777a074cb4240d08c9a129e3a837777")
        self.doc.reindexObject()
        transaction.commit()

        query = {"UID": "7777a074cb4240d08c9a129e3a837777"}
        response = self.api_session.get("/@search", params=query)
        self.assertEqual(["/plone/folder/doc"], result_paths(response.json()))

    def test_respect_access_inactive_permission(self):
        # admin can see everything
        response = self.api_session.get("/@search", params={}).json()
        if HAS_PLONE_6:
            # Since Plone 6 the Plone site is indexed ...
            self.assertEqual(response["items_total"], 7)
        else:
            # ... before it was not
            self.assertEqual(response["items_total"], 6)
        response = self.api_session.get("/@search",
                                        params={
                                            "Title": "Lorem Ipsum"
                                        }).json()
        self.assertEqual(response["items_total"], 1)

        # not admin users can't see expired items
        self.api_session.auth = ("editoruser", "secret")

        response = self.api_session.get("/@search", params={}).json()
        if HAS_PLONE_6:
            # Since Plone 6 the Plone site is indexed ...
            self.assertEqual(response["items_total"], 4)
        else:
            # ... before it was not
            self.assertEqual(response["items_total"], 3)
        response = self.api_session.get("/@search",
                                        params={
                                            "Title": "Lorem Ipsum"
                                        }).json()
        self.assertEqual(response["items_total"], 0)

        # now grant permission to Editor to access inactive content
        self.portal.manage_permission("Access inactive portal content",
                                      roles=["Manager", "Editor"])
        transaction.commit()

        #  portal-enabled Editor can see expired contents
        response = self.api_session.get("/@search", params={}).json()
        if HAS_PLONE_6:
            # Since Plone 6 the Plone site is indexed ...
            self.assertEqual(response["items_total"], 7)
        else:
            # ... before it was not
            self.assertEqual(response["items_total"], 6)
        response = self.api_session.get("/@search",
                                        params={
                                            "Title": "Lorem Ipsum"
                                        }).json()
        self.assertEqual(response["items_total"], 1)

        # local-enabled Editor can only access expired contents inside folder
        self.api_session.auth = ("localeditor", "secret")
        response = self.api_session.get("/@search", params={}).json()
        if HAS_PLONE_6:
            # Since Plone 6 the Plone site is indexed ...
            self.assertEqual(response["items_total"], 2)
        else:
            # ... before it was not
            self.assertEqual(response["items_total"], 1)
        response = self.api_session.get("/@search",
                                        params={
                                            "path": "/plone/folder"
                                        }).json()

        self.assertEqual(response["items_total"], 3)
        response = self.api_session.get("/@search",
                                        params={
                                            "Title": "Lorem Ipsum"
                                        }).json()
        self.assertEqual(response["items_total"], 0)
        response = self.api_session.get(
            "/@search",
            params={
                "Title": "Lorem Ipsum",
                "path": "/plone/folder"
            },
        ).json()
        self.assertEqual(response["items_total"], 1)

    def test_search_use_site_search_settings_for_types(self):
        response = self.api_session.get("/@search",
                                        params={
                                            "use_site_search_settings": 1
                                        }).json()
        types = {item["@type"] for item in response["items"]}

        self.assertEqual(set(types), {"Folder", "DXTestDocument"})

        registry = getUtility(IRegistry)
        from Products.CMFPlone.interfaces import ISearchSchema

        search_settings = registry.forInterface(ISearchSchema, prefix="plone")
        old = search_settings.types_not_searched
        search_settings.types_not_searched += ("DXTestDocument", )
        transaction.commit()

        response = self.api_session.get("/@search",
                                        params={
                                            "use_site_search_settings": 1
                                        }).json()
        types = {item["@type"] for item in response["items"]}

        self.assertEqual(set(types), {"Folder"})
        search_settings.types_not_searched = old
        transaction.commit()

    def test_search_use_site_search_settings_for_default_sort_order(self):
        response = self.api_session.get("/@search",
                                        params={
                                            "use_site_search_settings": 1
                                        }).json()
        titles = [
            "Some Folder",
            "Lorem Ipsum",
            "Other Document",
            "Another Folder",
            "Document in second folder",
            "Doc outside folder",
        ]
        self.assertEqual([item["title"] for item in response["items"]], titles)

        response = self.api_session.get("/@search",
                                        params={
                                            "use_site_search_settings": 1,
                                            "sort_on": "effective"
                                        }).json()
        self.assertEqual(
            [item["title"] for item in response["items"]][0],
            "Other Document",
        )

    def test_search_use_site_search_settings_with_navigation_root(self):

        alsoProvides(self.folder, INavigationRoot)
        transaction.commit()

        response = self.api_session.get("/folder/@search",
                                        params={
                                            "use_site_search_settings": 1
                                        }).json()
        titles = ["Some Folder", "Lorem Ipsum", "Other Document"]
        self.assertEqual([item["title"] for item in response["items"]], titles)

        noLongerProvides(self.folder, INavigationRoot)
        transaction.commit()

    def test_search_use_site_search_settings_with_navigation_root_and_vhm(
            self):

        if "virtual_hosting" not in self.app.objectIds():
            # If ZopeLite was imported, we have no default virtual
            # host monster
            from Products.SiteAccess.VirtualHostMonster import (
                manage_addVirtualHostMonster, )

            manage_addVirtualHostMonster(self.app, "virtual_hosting")
        alsoProvides(self.folder, INavigationRoot)
        transaction.commit()

        vhm_url = "{}/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/{}".format(
            self.app.absolute_url(),
            "/folder/@search",
        )
        response = self.api_session.get(vhm_url,
                                        params={
                                            "use_site_search_settings": 1,
                                            "path": "/folder"
                                        }).json()
        titles = ["Some Folder", "Lorem Ipsum", "Other Document"]
        self.assertEqual([item["title"] for item in response["items"]], titles)

        noLongerProvides(self.folder, INavigationRoot)
        transaction.commit()

    def test_search_use_site_search_settings_with_vhm(self):

        if "virtual_hosting" not in self.app.objectIds():
            # If ZopeLite was imported, we have no default virtual
            # host monster
            from Products.SiteAccess.VirtualHostMonster import (
                manage_addVirtualHostMonster, )

            manage_addVirtualHostMonster(self.app, "virtual_hosting")
        transaction.commit()

        vhm_url = "{}/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/{}".format(
            self.app.absolute_url(),
            "/@search",
        )
        response = self.api_session.get(vhm_url,
                                        params={
                                            "use_site_search_settings": 1,
                                            "path": "/"
                                        }).json()
        titles = sorted([
            "Another Folder",
            "Doc outside folder",
            "Document in second folder",
            "Lorem Ipsum",
            "Other Document",
            "Some Folder",
        ])
        self.assertEqual(sorted([item["title"] for item in response["items"]]),
                         titles)

        noLongerProvides(self.folder, INavigationRoot)
        transaction.commit()
Ejemplo n.º 20
0
class TestTUS(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        login(self.portal, SITE_OWNER_NAME)

        self.folder = api.content.create(container=self.portal,
                                         type="Folder",
                                         id="testfolder",
                                         title="Testfolder")
        self.upload_url = f"{self.folder.absolute_url()}/@tus-upload"
        transaction.commit()

        self.api_session = RelativeSession(self.portal.absolute_url(),
                                           test=self)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

    def get_tus_uid_from_url(self, url):
        uid = url.rsplit("/", 1)[-1]
        assert len(uid) == 32
        return uid

    def get_tus_upload_instance(self, uid):
        return TUSUpload(uid)

    def test_tus_option_headers(self):
        response = self.api_session.options(self.upload_url)
        headers = response.headers
        self.assertEqual(response.status_code, 204)
        self.assertEqual(headers["Tus-Version"], "1.0.0")
        self.assertEqual(headers["Tus-Extension"], "creation,expiration")
        self.assertEqual(headers["Tus-Resumable"], "1.0.0")

    def test_tus_post_without_version_header_returns_412(self):
        response = self.api_session.post(self.upload_url)
        self.assertEqual(412, response.status_code)

    def test_tus_post_with_wrong_version_header_returns_412(self):
        response = self.api_session.post(self.upload_url,
                                         headers={"Tus-Resumable": "0.2.2"})
        self.assertEqual(412, response.status_code)

    def test_tus_post_initialization_requires_header_length(self):
        response = self.api_session.post(self.upload_url,
                                         headers={"Tus-Resumable": "1.0.0"})
        self.assertEqual(response.json()["error"]["type"], "Bad Request")
        self.assertEqual(
            response.json()["error"]["message"],
            "Missing or invalid Upload-Length header",
        )
        self.assertEqual(response.status_code, 400)

    def test_tus_post_initialization(self):
        response = self.api_session.post(
            self.upload_url,
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(UPLOAD_LENGTH)
            },
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers["Location"]
        url_base, uid = location.rsplit("/", 1)
        self.assertEqual(url_base, self.upload_url)
        self.assertEqual(len(uid), 32)
        upload = TUSUpload(uid)
        stored_metadata = upload.metadata()
        self.assertEqual(stored_metadata, {"length": 8, "mode": "create"})
        upload.cleanup()

    def test_tus_post_initialization_with_metadata(self):
        metadata = _prepare_metadata(UPLOAD_FILENAME, UPLOAD_MIMETYPE)
        response = self.api_session.post(
            self.upload_url,
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(UPLOAD_LENGTH),
                "Upload-Metadata": metadata,
            },
        )
        self.assertEqual(response.status_code, 201)
        uid = self.get_tus_uid_from_url(response.headers["Location"])
        upload = TUSUpload(uid)
        stored_metadata = upload.metadata()
        self.assertEqual(
            stored_metadata,
            {
                "content-type": "text/plain",
                "filename": "test.txt",
                "length": 8,
                "mode": "create",
            },
        )
        upload.cleanup()

    def test_tus_post_replace(self):
        self.file = api.content.create(container=self.portal,
                                       type="File",
                                       id="testfile",
                                       title="Testfile")
        transaction.commit()
        response = self.api_session.post(
            f"{self.file.absolute_url()}/@tus-replace",
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(UPLOAD_LENGTH)
            },
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers["Location"]
        url_base, uid = location.rsplit("/", 1)
        upload = TUSUpload(uid)
        stored_metadata = upload.metadata()
        self.assertEqual(stored_metadata, {"length": 8, "mode": "replace"})
        upload.cleanup()

    def test_tus_head_on_not_existing_resource_returns_404(self):
        response = self.api_session.head(self.upload_url + "/myuid/123",
                                         headers={"Tus-Resumable": "1.0.0"})
        self.assertEqual(404, response.status_code)
        response = self.api_session.head(self.upload_url + "/non-existing-uid",
                                         headers={"Tus-Resumable": "1.0.0"})
        self.assertEqual(404, response.status_code)
        response = self.api_session.head(self.upload_url,
                                         headers={"Tus-Resumable": "1.0.0"})
        self.assertEqual(404, response.status_code)

    def test_tus_head_with_unsupported_version_returns_412(self):
        tus = TUSUpload("myuid", {"length": 2048})
        response = self.api_session.head(self.upload_url + "/myuid",
                                         headers={"Tus-Resumable": "0.2.2"})
        self.assertEqual(412, response.status_code)
        tus.cleanup()

    def test_tus_head_response_includes_required_headers(self):
        tus = TUSUpload("myuid", {"length": 2048})
        response = self.api_session.head(self.upload_url + "/myuid",
                                         headers={"Tus-Resumable": "1.0.0"})
        self.assertIn("Upload-Length", response.headers)
        self.assertEqual("2048", response.headers["Upload-Length"])
        self.assertIn("Upload-Offset", response.headers)
        self.assertIn("Tus-Resumable", response.headers)
        self.assertIn("Cache-Control", response.headers)
        tus.cleanup()

    def test_head_in_create_mode_without_add_permission_raises_401(self):
        self.folder.manage_permission("Add portal content", [], 0)
        transaction.commit()
        tus = TUSUpload("myuid", {"mode": "create", "length": 12})
        response = self.api_session.head(
            self.upload_url + "/myuid",
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Offset": "0"
            },
        )
        self.assertEqual(401, response.status_code)
        tus.cleanup()

    def test_head_in_replace_mode_without_modify_permission_raises_401(self):
        self.folder.manage_permission("Modify portal content", [], 0)
        transaction.commit()
        tus = TUSUpload("myuid", {"mode": "replace", "length": 12})
        response = self.api_session.head(
            self.upload_url + "/myuid",
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Offset": "0"
            },
        )
        self.assertEqual(401, response.status_code)
        tus.cleanup()

    def test_tus_patch_on_not_existing_resource_returns_404(self):
        response = self.api_session.patch(self.upload_url + "/myuid/123",
                                          headers={"Tus-Resumable": "1.0.0"})
        self.assertEqual(404, response.status_code)
        response = self.api_session.patch(self.upload_url + "/myuid",
                                          headers={"Tus-Resumable": "1.0.0"})
        self.assertEqual(404, response.status_code)
        response = self.api_session.patch(self.upload_url,
                                          headers={"Tus-Resumable": "1.0.0"})
        self.assertEqual(404, response.status_code)

    def test_tus_patch_with_unsupported_version_returns_412(self):
        tus = TUSUpload("myuid", {"length": 2048})
        response = self.api_session.patch(self.upload_url + "/myuid",
                                          headers={"Tus-Resumable": "0.2.2"})
        self.assertEqual(412, response.status_code)
        tus.cleanup()

    def test_tus_patch_with_unsupported_content_type_returns_400(self):
        tus = TUSUpload("myuid", {"length": 2048})
        response = self.api_session.patch(
            self.upload_url + "/myuid",
            headers={
                "Tus-Resumable": "1.0.0",
                "Content-Type": "application/json"
            },
        )
        self.assertEqual(400, response.status_code)
        tus.cleanup()

    def test_tus_patch_with_invalid_offset_returns_400(self):
        tus = TUSUpload("myuid", {"length": 2048})
        response = self.api_session.patch(
            self.upload_url + "/myuid",
            headers={
                "Tus-Resumable": "1.0.0",
                "Content-Type": "application/offset+octet-stream",
            },
        )
        self.assertEqual(400, response.status_code)
        tus.cleanup()

    def test_tus_patch_unfinished_upload_returns_expires_header(self):
        tus = TUSUpload("myuid", {"length": 2048})
        response = self.api_session.patch(
            self.upload_url + "/myuid",
            headers={
                "Tus-Resumable": "1.0.0",
                "Content-Type": "application/offset+octet-stream",
                "Upload-Offset": "0",
            },
            data=BytesIO(b"abcdefghijkl"),
        )
        self.assertEqual(204, response.status_code)
        self.assertIn("Upload-Expires", response.headers)
        tus.cleanup()

    def test_tus_patch_non_primary_field(self):
        tus = TUSUpload(
            "myuid",
            {
                "@type": "DXTestDocument",
                "length": 12,
                "fieldname": "test_namedblobfile_field",
            },
        )
        response = self.api_session.patch(
            self.upload_url + "/myuid",
            headers={
                "Tus-Resumable": "1.0.0",
                "Content-Type": "application/offset+octet-stream",
                "Upload-Offset": "0",
            },
            data=BytesIO(b"abcdefghijkl"),
        )

        self.assertEqual(204, response.status_code)
        transaction.commit()
        self.assertEqual(1, len(self.folder.objectIds()))
        id_ = self.folder.objectIds()[0]
        self.assertEqual(b"abcdefghijkl",
                         self.folder[id_].test_namedblobfile_field.data)
        tus.cleanup()

    def test_patch_in_create_mode_without_add_permission_raises_401(self):
        self.folder.manage_permission("Add portal content", [], 0)
        transaction.commit()
        tus = TUSUpload("myuid", {"mode": "create", "length": 12})
        response = self.api_session.patch(
            self.upload_url + "/myuid",
            headers={
                "Tus-Resumable": "1.0.0",
                "Content-Type": "application/offset+octet-stream",
                "Upload-Offset": "0",
            },
            data=BytesIO(b"abcdefghijkl"),
        )
        self.assertEqual(401, response.status_code)
        tus.cleanup()

    def test_patch_in_replace_mode_without_modify_permission_raises_401(self):
        self.folder.manage_permission("Modify portal content", [], 0)
        transaction.commit()
        tus = TUSUpload("myuid", {"mode": "replace", "length": 12})
        response = self.api_session.patch(
            self.upload_url + "/myuid",
            headers={
                "Tus-Resumable": "1.0.0",
                "Content-Type": "application/offset+octet-stream",
                "Upload-Offset": "0",
            },
            data=BytesIO(b"abcdefghijkl"),
        )
        self.assertEqual(401, response.status_code)
        tus.cleanup()

    def test_tus_can_upload_pdf_file(self):
        # initialize the upload with POST
        pdf_file_path = os.path.join(os.path.dirname(__file__),
                                     UPLOAD_PDF_FILENAME)
        pdf_file_size = os.path.getsize(pdf_file_path)
        metadata = _prepare_metadata(UPLOAD_PDF_FILENAME, UPLOAD_PDF_MIMETYPE)
        response = self.api_session.post(
            self.upload_url,
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(pdf_file_size),
                "Upload-Metadata": metadata,
            },
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers["Location"]

        # upload the data with PATCH
        with open(pdf_file_path, "rb") as pdf_file:
            response = self.api_session.patch(
                location,
                headers={
                    "Content-Type": "application/offset+octet-stream",
                    "Upload-Offset": "0",
                    "Tus-Resumable": "1.0.0",
                },
                data=pdf_file,
            )
        self.assertEqual(response.status_code, 204)

        transaction.commit()
        self.assertEqual([UPLOAD_PDF_FILENAME], self.folder.contentIds())

    def test_tus_can_upload_text_file(self):
        # initialize the upload with POST
        metadata = _prepare_metadata(UPLOAD_FILENAME, UPLOAD_MIMETYPE)
        response = self.api_session.post(
            self.upload_url,
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(UPLOAD_LENGTH),
                "Upload-Metadata": metadata,
            },
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers["Location"]

        # upload the data with PATCH
        response = self.api_session.patch(
            location,
            headers={
                "Content-Type": "application/offset+octet-stream",
                "Upload-Offset": "0",
                "Tus-Resumable": "1.0.0",
            },
            data=BytesIO(UPLOAD_DATA),
        )
        self.assertEqual(response.status_code, 204)

    def test_tus_can_replace_pdf_file(self):
        # Create a test file
        self.file = api.content.create(container=self.portal,
                                       type="File",
                                       id="testfile",
                                       title="Testfile")
        transaction.commit()
        # initialize the upload with POST
        pdf_file_path = os.path.join(os.path.dirname(__file__),
                                     UPLOAD_PDF_FILENAME)
        pdf_file_size = os.path.getsize(pdf_file_path)
        metadata = _prepare_metadata(UPLOAD_PDF_FILENAME, UPLOAD_PDF_MIMETYPE)
        response = self.api_session.post(
            f"{self.file.absolute_url()}/@tus-replace",
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(pdf_file_size),
                "Upload-Metadata": metadata,
            },
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers["Location"]

        # upload the data with PATCH
        with open(pdf_file_path, "rb") as pdf_file:
            response = self.api_session.patch(
                location,
                headers={
                    "Content-Type": "application/offset+octet-stream",
                    "Upload-Offset": "0",
                    "Tus-Resumable": "1.0.0",
                },
                data=pdf_file,
            )
        self.assertEqual(response.status_code, 204)

        transaction.commit()
        self.assertEqual(UPLOAD_PDF_FILENAME, self.file.file.filename)
        self.assertEqual(pdf_file_size, self.file.file.size)

    def test_create_with_tus_fires_proper_events(self):
        sm = getGlobalSiteManager()
        fired_events = []

        def record_event(event):
            fired_events.append(event.__class__.__name__)

        sm.registerHandler(record_event, (IObjectCreatedEvent, ))
        sm.registerHandler(record_event, (IObjectWillBeAddedEvent, ))
        sm.registerHandler(record_event, (IObjectAddedEvent, ))
        sm.registerHandler(record_event, (IObjectModifiedEvent, ))

        # initialize the upload with POST
        pdf_file_path = os.path.join(os.path.dirname(__file__),
                                     UPLOAD_PDF_FILENAME)
        pdf_file_size = os.path.getsize(pdf_file_path)
        metadata = _prepare_metadata(UPLOAD_PDF_FILENAME, UPLOAD_PDF_MIMETYPE)
        response = self.api_session.post(
            self.upload_url,
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(pdf_file_size),
                "Upload-Metadata": metadata,
            },
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers["Location"]

        # upload the data with PATCH
        with open(pdf_file_path, "rb") as pdf_file:
            response = self.api_session.patch(
                location,
                headers={
                    "Content-Type": "application/offset+octet-stream",
                    "Upload-Offset": "0",
                    "Tus-Resumable": "1.0.0",
                },
                data=pdf_file,
            )
        self.assertEqual(response.status_code, 204)

        self.assertEqual(
            fired_events,
            [
                "ObjectCreatedEvent",
                "ObjectWillBeAddedEvent",
                "ObjectAddedEvent",
                "ContainerModifiedEvent",
            ],
        )

        sm.unregisterHandler(record_event, (IObjectCreatedEvent, ))
        sm.unregisterHandler(record_event, (IObjectWillBeAddedEvent, ))
        sm.unregisterHandler(record_event, (IObjectAddedEvent, ))
        sm.unregisterHandler(record_event, (IObjectModifiedEvent, ))

    def test_replace_with_tus_fires_proper_events(self):
        # Create a test file
        self.file = api.content.create(container=self.portal,
                                       type="File",
                                       id="testfile",
                                       title="Testfile")
        transaction.commit()

        sm = getGlobalSiteManager()
        fired_events = []

        def record_event(event):
            fired_events.append(event.__class__.__name__)

        sm.registerHandler(record_event, (IObjectCreatedEvent, ))
        sm.registerHandler(record_event, (IObjectWillBeAddedEvent, ))
        sm.registerHandler(record_event, (IObjectAddedEvent, ))
        sm.registerHandler(record_event, (IObjectModifiedEvent, ))

        # initialize the upload with POST
        pdf_file_path = os.path.join(os.path.dirname(__file__),
                                     UPLOAD_PDF_FILENAME)
        pdf_file_size = os.path.getsize(pdf_file_path)
        metadata = _prepare_metadata(UPLOAD_PDF_FILENAME, UPLOAD_PDF_MIMETYPE)
        response = self.api_session.post(
            f"{self.file.absolute_url()}/@tus-replace",
            headers={
                "Tus-Resumable": "1.0.0",
                "Upload-Length": str(pdf_file_size),
                "Upload-Metadata": metadata,
            },
        )
        self.assertEqual(response.status_code, 201)
        location = response.headers["Location"]

        # upload the data with PATCH
        with open(pdf_file_path, "rb") as pdf_file:
            response = self.api_session.patch(
                location,
                headers={
                    "Content-Type": "application/offset+octet-stream",
                    "Upload-Offset": "0",
                    "Tus-Resumable": "1.0.0",
                },
                data=pdf_file,
            )
        self.assertEqual(response.status_code, 204)

        self.assertEqual(fired_events, ["ObjectModifiedEvent"])

        sm.unregisterHandler(record_event, (IObjectCreatedEvent, ))
        sm.unregisterHandler(record_event, (IObjectWillBeAddedEvent, ))
        sm.unregisterHandler(record_event, (IObjectAddedEvent, ))
        sm.unregisterHandler(record_event, (IObjectModifiedEvent, ))

    def tearDown(self):
        self.api_session.close()
        client_home = os.environ.get("CLIENT_HOME")
        tmp_dir = os.path.join(client_home, "tus-uploads")
        if os.path.isdir(tmp_dir):
            shutil.rmtree(tmp_dir)
Ejemplo n.º 21
0
class TestTUS(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        login(self.portal, SITE_OWNER_NAME)

        self.folder = api.content.create(container=self.portal,
                                         type='Folder',
                                         id='testfolder',
                                         title='Testfolder')
        self.upload_url = '{}/@tus-upload'.format(self.folder.absolute_url())
        transaction.commit()

        self.api_session = RelativeSession(self.portal.absolute_url())
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

    def get_tus_uid_from_url(self, url):
        uid = url.rsplit('/', 1)[-1]
        assert len(uid) == 32
        return uid

    def get_tus_upload_instance(self, uid):
        return TUSUpload(uid)

    def test_tus_option_headers(self):
        response = self.api_session.options(self.upload_url)
        headers = response.headers
        self.assertEqual(response.status_code, 204)
        self.assertEqual(headers['Tus-Version'], '1.0.0')
        self.assertEqual(headers['Tus-Extension'], 'creation,expiration')
        self.assertEqual(headers['Tus-Resumable'], '1.0.0')

    def test_tus_post_without_version_header_returns_412(self):
        response = self.api_session.post(self.upload_url)
        self.assertEqual(412, response.status_code)

    def test_tus_post_with_wrong_version_header_returns_412(self):
        response = self.api_session.post(self.upload_url,
                                         headers={'Tus-Resumable': '0.2.2'})
        self.assertEqual(412, response.status_code)

    def test_tus_post_initialization_requires_header_length(self):
        response = self.api_session.post(self.upload_url,
                                         headers={'Tus-Resumable': '1.0.0'})
        self.assertEqual(response.json()['error']['type'], 'Bad Request')
        self.assertEqual(response.json()['error']['message'],
                         'Missing or invalid Upload-Length header')
        self.assertEqual(response.status_code, 400)

    def test_tus_post_initialization(self):
        response = self.api_session.post(self.upload_url,
                                         headers={
                                             'Tus-Resumable': '1.0.0',
                                             'Upload-Length':
                                             str(UPLOAD_LENGTH),
                                         })
        self.assertEqual(response.status_code, 201)
        location = response.headers['Location']
        url_base, uid = location.rsplit('/', 1)
        self.assertEqual(url_base, self.upload_url)
        self.assertEqual(len(uid), 32)
        upload = TUSUpload(uid)
        stored_metadata = upload.metadata()
        self.assertEqual(stored_metadata, {u'length': 8, u'mode': u'create'})
        upload.cleanup()

    def test_tus_post_initialization_with_metadata(self):
        metadata = 'filename {},content-type {}'.format(
            b64encode(UPLOAD_FILENAME), b64encode(UPLOAD_MIMETYPE))
        response = self.api_session.post(self.upload_url,
                                         headers={
                                             'Tus-Resumable': '1.0.0',
                                             'Upload-Length':
                                             str(UPLOAD_LENGTH),
                                             'Upload-Metadata': metadata
                                         })
        self.assertEqual(response.status_code, 201)
        uid = self.get_tus_uid_from_url(response.headers['Location'])
        upload = TUSUpload(uid)
        stored_metadata = upload.metadata()
        self.assertEqual(
            stored_metadata, {
                u'content-type': u'text/plain',
                u'filename': u'test.txt',
                u'length': 8,
                u'mode': u'create'
            })
        upload.cleanup()

    def test_tus_post_replace(self):
        self.file = api.content.create(container=self.portal,
                                       type='File',
                                       id='testfile',
                                       title='Testfile')
        transaction.commit()
        response = self.api_session.post(
            '{}/@tus-replace'.format(self.file.absolute_url()),
            headers={
                'Tus-Resumable': '1.0.0',
                'Upload-Length': str(UPLOAD_LENGTH),
            })
        self.assertEqual(response.status_code, 201)
        location = response.headers['Location']
        url_base, uid = location.rsplit('/', 1)
        upload = TUSUpload(uid)
        stored_metadata = upload.metadata()
        self.assertEqual(stored_metadata, {u'length': 8, u'mode': u'replace'})
        upload.cleanup()

    def test_tus_head_on_not_existing_resource_returns_404(self):
        response = self.api_session.head(self.upload_url + '/myuid/123',
                                         headers={'Tus-Resumable': '1.0.0'})
        self.assertEqual(404, response.status_code)
        response = self.api_session.head(self.upload_url + '/non-existing-uid',
                                         headers={'Tus-Resumable': '1.0.0'})
        self.assertEqual(404, response.status_code)
        response = self.api_session.head(self.upload_url,
                                         headers={'Tus-Resumable': '1.0.0'})
        self.assertEqual(404, response.status_code)

    def test_tus_head_with_unsupported_version_returns_412(self):
        tus = TUSUpload('myuid', {'length': 2048})
        response = self.api_session.head(self.upload_url + '/myuid',
                                         headers={'Tus-Resumable': '0.2.2'})
        self.assertEqual(412, response.status_code)
        tus.cleanup()

    def test_tus_head_response_includes_required_headers(self):
        tus = TUSUpload('myuid', {'length': 2048})
        response = self.api_session.head(self.upload_url + '/myuid',
                                         headers={'Tus-Resumable': '1.0.0'})
        self.assertIn('Upload-Length', response.headers)
        self.assertEqual('2048', response.headers['Upload-Length'])
        self.assertIn('Upload-Offset', response.headers)
        self.assertIn('Tus-Resumable', response.headers)
        self.assertIn('Cache-Control', response.headers)
        tus.cleanup()

    def test_head_in_create_mode_without_add_permission_raises_401(self):
        self.folder.manage_permission('Add portal content', [], 0)
        transaction.commit()
        tus = TUSUpload('myuid', {'mode': 'create', 'length': 12})
        response = self.api_session.head(self.upload_url + '/myuid',
                                         headers={
                                             'Tus-Resumable': '1.0.0',
                                             'Upload-Offset': '0'
                                         })
        self.assertEqual(401, response.status_code)
        tus.cleanup()

    def test_head_in_replace_mode_without_modify_permission_raises_401(self):
        self.folder.manage_permission('Modify portal content', [], 0)
        transaction.commit()
        tus = TUSUpload('myuid', {'mode': 'replace', 'length': 12})
        response = self.api_session.head(self.upload_url + '/myuid',
                                         headers={
                                             'Tus-Resumable': '1.0.0',
                                             'Upload-Offset': '0'
                                         })
        self.assertEqual(401, response.status_code)
        tus.cleanup()

    def test_tus_patch_on_not_existing_resource_returns_404(self):
        response = self.api_session.patch(self.upload_url + '/myuid/123',
                                          headers={'Tus-Resumable': '1.0.0'})
        self.assertEqual(404, response.status_code)
        response = self.api_session.patch(self.upload_url + '/myuid',
                                          headers={'Tus-Resumable': '1.0.0'})
        self.assertEqual(404, response.status_code)
        response = self.api_session.patch(self.upload_url,
                                          headers={'Tus-Resumable': '1.0.0'})
        self.assertEqual(404, response.status_code)

    def test_tus_patch_with_unsupported_version_returns_412(self):
        tus = TUSUpload('myuid', {'length': 2048})
        response = self.api_session.patch(self.upload_url + '/myuid',
                                          headers={'Tus-Resumable': '0.2.2'})
        self.assertEqual(412, response.status_code)
        tus.cleanup()

    def test_tus_patch_with_unsupported_content_type_returns_400(self):
        tus = TUSUpload('myuid', {'length': 2048})
        response = self.api_session.patch(self.upload_url + '/myuid',
                                          headers={
                                              'Tus-Resumable': '1.0.0',
                                              'Content-Type':
                                              'application/json'
                                          })
        self.assertEqual(400, response.status_code)
        tus.cleanup()

    def test_tus_patch_with_invalid_offset_returns_400(self):
        tus = TUSUpload('myuid', {'length': 2048})
        response = self.api_session.patch(self.upload_url + '/myuid',
                                          headers={
                                              'Tus-Resumable':
                                              '1.0.0',
                                              'Content-Type':
                                              'application/offset+octet-stream'
                                          })
        self.assertEqual(400, response.status_code)
        tus.cleanup()

    def test_tus_patch_unfinished_upload_returns_expires_header(self):
        tus = TUSUpload('myuid', {'length': 2048})
        response = self.api_session.patch(
            self.upload_url + '/myuid',
            headers={
                'Tus-Resumable': '1.0.0',
                'Content-Type': 'application/offset+octet-stream',
                'Upload-Offset': '0'
            },
            data=StringIO('abcdefghijkl'))
        self.assertEqual(204, response.status_code)
        self.assertIn('Upload-Expires', response.headers)
        tus.cleanup()

    def test_tus_patch_non_primary_field(self):
        tus = TUSUpload(
            'myuid', {
                '@type': 'DXTestDocument',
                'length': 12,
                'fieldname': 'test_namedblobfile_field'
            })
        response = self.api_session.patch(
            self.upload_url + '/myuid',
            headers={
                'Tus-Resumable': '1.0.0',
                'Content-Type': 'application/offset+octet-stream',
                'Upload-Offset': '0'
            },
            data=StringIO('abcdefghijkl'))

        self.assertEqual(204, response.status_code)
        transaction.commit()
        self.assertEqual(1, len(self.folder.objectIds()))
        id_ = self.folder.objectIds()[0]
        self.assertEqual('abcdefghijkl',
                         self.folder[id_].test_namedblobfile_field.data)
        tus.cleanup()

    def test_patch_in_create_mode_without_add_permission_raises_401(self):
        self.folder.manage_permission('Add portal content', [], 0)
        transaction.commit()
        tus = TUSUpload('myuid', {'mode': 'create', 'length': 12})
        response = self.api_session.patch(
            self.upload_url + '/myuid',
            headers={
                'Tus-Resumable': '1.0.0',
                'Content-Type': 'application/offset+octet-stream',
                'Upload-Offset': '0'
            },
            data=StringIO('abcdefghijkl'))
        self.assertEqual(401, response.status_code)
        tus.cleanup()

    def test_patch_in_replace_mode_without_modify_permission_raises_401(self):
        self.folder.manage_permission('Modify portal content', [], 0)
        transaction.commit()
        tus = TUSUpload('myuid', {'mode': 'replace', 'length': 12})
        response = self.api_session.patch(
            self.upload_url + '/myuid',
            headers={
                'Tus-Resumable': '1.0.0',
                'Content-Type': 'application/offset+octet-stream',
                'Upload-Offset': '0'
            },
            data=StringIO('abcdefghijkl'))
        self.assertEqual(401, response.status_code)
        tus.cleanup()

    def test_tus_can_upload_pdf_file(self):
        # initialize the upload with POST
        pdf_file_path = os.path.join(os.path.dirname(__file__),
                                     UPLOAD_PDF_FILENAME)
        pdf_file_size = os.path.getsize(pdf_file_path)
        metadata = 'filename {},content-type {}'.format(
            b64encode(UPLOAD_PDF_FILENAME), b64encode(UPLOAD_PDF_MIMETYPE))
        response = self.api_session.post(self.upload_url,
                                         headers={
                                             'Tus-Resumable': '1.0.0',
                                             'Upload-Length':
                                             str(pdf_file_size),
                                             'Upload-Metadata': metadata
                                         })
        self.assertEqual(response.status_code, 201)
        location = response.headers['Location']

        # upload the data with PATCH
        pdf_file = open(pdf_file_path, 'rb')
        response = self.api_session.patch(
            location,
            headers={
                'Content-Type': 'application/offset+octet-stream',
                'Upload-Offset': '0',
                'Tus-Resumable': '1.0.0'
            },
            data=pdf_file)
        self.assertEqual(response.status_code, 204)

        transaction.commit()
        self.assertEqual([UPLOAD_PDF_FILENAME], self.folder.contentIds())

    def test_tus_can_upload_text_file(self):
        # initialize the upload with POST
        metadata = 'filename {},content-type {}'.format(
            b64encode(UPLOAD_FILENAME), b64encode(UPLOAD_MIMETYPE))
        response = self.api_session.post(self.upload_url,
                                         headers={
                                             'Tus-Resumable': '1.0.0',
                                             'Upload-Length':
                                             str(UPLOAD_LENGTH),
                                             'Upload-Metadata': metadata
                                         })
        self.assertEqual(response.status_code, 201)
        location = response.headers['Location']

        # upload the data with PATCH
        response = self.api_session.patch(
            location,
            headers={
                'Content-Type': 'application/offset+octet-stream',
                'Upload-Offset': '0',
                'Tus-Resumable': '1.0.0'
            },
            data=StringIO(UPLOAD_DATA))
        self.assertEqual(response.status_code, 204)

    def test_tus_can_replace_pdf_file(self):
        # Create a test file
        self.file = api.content.create(container=self.portal,
                                       type='File',
                                       id='testfile',
                                       title='Testfile')
        transaction.commit()
        # initialize the upload with POST
        pdf_file_path = os.path.join(os.path.dirname(__file__),
                                     UPLOAD_PDF_FILENAME)
        pdf_file_size = os.path.getsize(pdf_file_path)
        metadata = 'filename {},content-type {}'.format(
            b64encode(UPLOAD_PDF_FILENAME), b64encode(UPLOAD_PDF_MIMETYPE))
        response = self.api_session.post(
            '{}/@tus-replace'.format(self.file.absolute_url()),
            headers={
                'Tus-Resumable': '1.0.0',
                'Upload-Length': str(pdf_file_size),
                'Upload-Metadata': metadata
            })
        self.assertEqual(response.status_code, 201)
        location = response.headers['Location']

        # upload the data with PATCH
        pdf_file = open(pdf_file_path, 'rb')
        response = self.api_session.patch(
            location,
            headers={
                'Content-Type': 'application/offset+octet-stream',
                'Upload-Offset': '0',
                'Tus-Resumable': '1.0.0'
            },
            data=pdf_file)
        self.assertEqual(response.status_code, 204)

        transaction.commit()
        self.assertEqual(UPLOAD_PDF_FILENAME, self.file.file.filename)
        self.assertEqual(pdf_file_size, self.file.file.size)

    def tearDown(self):
        client_home = os.environ.get('CLIENT_HOME')
        tmp_dir = os.path.join(client_home, 'tus-uploads')
        if os.path.isdir(tmp_dir):
            shutil.rmtree(tmp_dir)
class TestCommentsEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.request = self.layer['request']
        self.portal_url = self.portal.absolute_url()

        # Allow discussion
        registry = getUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        settings.globally_enabled = True
        settings.edit_comment_enabled = True
        settings.delete_own_comment_enabled = True

        # doc with comments
        self.doc = api.content.create(container=self.portal,
                                      type='Document',
                                      id='doc_with_comments',
                                      title='Document with comments',
                                      allow_discussion=True)
        api.content.transition(self.doc, 'publish')

        api.user.create(username='******', password='******', email='*****@*****.**')

        # Admin session
        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        # User session
        self.user_session = RelativeSession(self.portal_url)
        self.user_session.headers.update({'Accept': 'application/json'})
        self.user_session.auth = ('jos', 'jos')

        transaction.commit()

    def test_list_datastructure(self):
        url = '{}/@comments'.format(self.doc.absolute_url())
        response = self.api_session.get(url)

        self.assertEqual(200, response.status_code)
        data = response.json()
        self.assertEqual(set(['items_total', 'items', '@id']), set(data))

    def test_list_batching(self):
        url = '{}/@comments'.format(self.doc.absolute_url())

        self.api_session.post(url, json={'text': 'comment 1'})
        self.api_session.post(url, json={'text': 'comment 2'})

        response = self.api_session.get(url, params={'b_size': 1})
        self.assertEqual(200, response.status_code)
        data = response.json()
        self.assertIn('batching', data)

    def test_add_comment_to_root(self):
        url = '{}/@comments'.format(self.doc.absolute_url())

        response = self.api_session.get(url)
        self.assertEqual(0, response.json()['items_total'])

        response = self.api_session.post(url, json={'text': 'comment 1'})
        self.assertEqual(204, response.status_code)
        self.assertIn('location', response.headers)

        response = self.api_session.get(url)
        data = response.json()
        self.assertEqual(1, data['items_total'])
        self.assertIsNone(data['items'][0]['in_reply_to'])
        self.assertIsNone(data['items'][0]['@parent'])

    def test_add_comment_to_comment(self):
        url = '{}/@comments'.format(self.doc.absolute_url())

        response = self.api_session.post(url, json={'text': 'comment 1'})
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(url)
        data = response.json()
        parent_id = data['items'][0]['comment_id']

        SUBTEXT = 'sub comment'

        payload = {'text': SUBTEXT, 'in_reply_to': parent_id}
        response = self.api_session.post(url, json=payload)
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(url)
        data = response.json()
        sub = [x for x in data['items'] if x['text']['data'] == SUBTEXT][0]
        self.assertEqual(parent_id, sub['in_reply_to'])

    def test_update(self):
        url = '{}/@comments'.format(self.doc.absolute_url())
        OLD_TEXT = 'comment 1'
        NEW_TEXT = 'new text'

        self.api_session.post(url, json={'text': OLD_TEXT})

        response = self.api_session.get(url)
        data = response.json()
        item_texts = [x['text']['data'] for x in data['items']]
        self.assertNotIn(NEW_TEXT, item_texts)
        self.assertIn(OLD_TEXT, item_texts)
        comment = data['items'][0]

        payload = {'text': NEW_TEXT}
        response = self.api_session.patch(comment['@id'], json=payload)
        self.assertEqual(204, response.status_code)
        self.assertIn('location', response.headers)

        response = self.api_session.get(url)
        data = response.json()
        item_texts = [x['text']['data'] for x in data['items']]
        self.assertIn(NEW_TEXT, item_texts)
        self.assertNotIn(OLD_TEXT, item_texts)

    def test_permissions_delete_comment(self):
        url = '{}/@comments'.format(self.doc.absolute_url())

        response = self.api_session.post(url, json={'text': 'comment'})
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(url)
        comment_url = response.json()['items'][0]['@id']
        self.assertFalse(comment_url.endswith('@comments'))
        self.assertTrue(response.json()['items'][0]['is_deletable'])

        # Other user may not delete this
        response = self.user_session.delete(comment_url)
        self.assertEqual(401, response.status_code)

        response = self.user_session.get(url)
        self.assertFalse(response.json()['items'][0]['is_deletable'])

        # The owner may
        response = self.api_session.delete(comment_url)
        self.assertEqual(204, response.status_code)

    def test_permissions_update_comment(self):
        url = '{}/@comments'.format(self.doc.absolute_url())

        response = self.api_session.post(url, json={'text': 'comment'})
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(url)
        comment_url = response.json()['items'][0]['@id']
        self.assertFalse(comment_url.endswith('@comments'))
        self.assertTrue(response.json()['items'][0]['is_editable'])

        # Other user may not update this
        response = self.user_session.patch(comment_url, json={'text': 'new'})
        self.assertEqual(401, response.status_code)

        response = self.user_session.get(url)
        self.assertFalse(response.json()['items'][0]['is_editable'])

        # The owner may
        response = self.api_session.patch(comment_url, json={'text': 'new'})
        self.assertEqual(204, response.status_code)
Ejemplo n.º 23
0
class TestTraversal(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.request = self.layer['request']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()

        self.time_freezer = freeze_time("2016-10-21 19:00:00")
        self.frozen_time = self.time_freezer.start()

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Document', id='front-page')
        self.document = self.portal['front-page']
        self.document.title = u"Welcome to Plone"
        self.document.description = \
            u"Congratulations! You have successfully installed Plone."
        self.document.text = RichTextValue(
            u"If you're seeing this instead of the web site you were " +
            u"expecting, the owner of this web site has just installed " +
            u"Plone. Do not contact the Plone Team or the Plone mailing " +
            u"lists about this.",
            'text/plain',
            'text/html'
        )
        self.document.creation_date = DateTime('2016-01-21T01:14:48+00:00')
        IMutableUUID(self.document).set('1f699ffa110e45afb1ba502f75f7ec33')
        self.document.reindexObject()
        self.document.modification_date = DateTime('2016-01-21T01:24:11+00:00')
        import transaction
        transaction.commit()
        self.browser = Browser(self.app)
        self.browser.handleErrors = False
        self.browser.addHeader(
            'Authorization',
            'Basic %s:%s' % (SITE_OWNER_NAME, SITE_OWNER_PASSWORD,)
        )

    def tearDown(self):
        self.time_freezer.stop()

    def test_documentation_document(self):
        response = self.api_session.get(self.document.absolute_url())
        save_response_for_documentation('document.json', response)

    def test_documentation_news_item(self):
        self.portal.invokeFactory('News Item', id='newsitem')
        self.portal.newsitem.title = 'My News Item'
        self.portal.newsitem.description = u'This is a news item'
        self.portal.newsitem.text = RichTextValue(
            u"Lorem ipsum",
            'text/plain',
            'text/html'
        )
        image_file = os.path.join(os.path.dirname(__file__), u'image.png')
        self.portal.newsitem.image = NamedBlobImage(
            data=open(image_file, 'r').read(),
            contentType='image/png',
            filename=u'image.png'
        )
        self.portal.newsitem.image_caption = u'This is an image caption.'
        self.portal.newsitem.creation_date = DateTime(
            '2016-01-21T02:14:48+00:00')
        self.portal.newsitem.modification_date = DateTime(
            '2016-01-21T02:24:11+00:00')
        IMutableUUID(self.portal.newsitem).set(
            '80c2a074cb4240d08c9a129e3a834c74')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.newsitem.absolute_url())
        save_response_for_documentation('newsitem.json', response)

    def test_documentation_event(self):
        self.portal.invokeFactory('Event', id='event')
        self.portal.event.title = 'Event'
        self.portal.event.description = u'This is an event'
        self.portal.event.start = datetime(2013, 1, 1, 10, 0)
        self.portal.event.end = datetime(2013, 1, 1, 12, 0)
        self.portal.event.creation_date = DateTime('2016-01-21T03:14:48+00:00')
        self.portal.event.modification_date = DateTime(
            '2016-01-21T03:24:11+00:00')
        IMutableUUID(self.portal.event).set('846d632bc0854c5aa6d3dcae171ed2db')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.event.absolute_url())
        save_response_for_documentation('event.json', response)

    def test_documentation_link(self):
        self.portal.invokeFactory('Link', id='link')
        self.portal.link.title = 'My Link'
        self.portal.link.description = u'This is a link'
        self.portal.remoteUrl = 'http://plone.org'
        self.portal.link.creation_date = DateTime('2016-01-21T04:14:48+00:00')
        self.portal.link.modification_date = DateTime(
            '2016-01-21T04:24:11+00:00')
        IMutableUUID(self.portal.link).set('6ff48d27762143a0ae8d63cee73d9fc2')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.link.absolute_url())
        save_response_for_documentation('link.json', response)

    def test_documentation_file(self):
        self.portal.invokeFactory('File', id='file')
        self.portal.file.title = 'My File'
        self.portal.file.description = u'This is a file'
        pdf_file = os.path.join(
            os.path.dirname(__file__), u'file.pdf'
        )
        self.portal.file.file = NamedBlobFile(
            data=open(pdf_file, 'r').read(),
            contentType='application/pdf',
            filename=u'file.pdf'
        )
        self.portal.file.creation_date = DateTime('2016-01-21T05:14:48+00:00')
        self.portal.file.modification_date = DateTime(
            '2016-01-21T05:24:11+00:00')
        IMutableUUID(self.portal.file).set('9b6a4eadb9074dde97d86171bb332ae9')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.file.absolute_url())
        save_response_for_documentation('file.json', response)

    def test_documentation_image(self):
        self.portal.invokeFactory('Image', id='image')
        self.portal.image.title = 'My Image'
        self.portal.image.description = u'This is an image'
        image_file = os.path.join(os.path.dirname(__file__), u'image.png')
        self.portal.image.image = NamedBlobImage(
            data=open(image_file, 'r').read(),
            contentType='image/png',
            filename=u'image.png'
        )
        self.portal.image.creation_date = DateTime('2016-01-21T06:14:48+00:00')
        self.portal.image.modification_date = DateTime(
            '2016-01-21T06:24:11+00:00')
        IMutableUUID(self.portal.image).set('2166e81a0c224fe3b62e197c7fdc9c3e')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.image.absolute_url())
        save_response_for_documentation('image.json', response)

    def test_documentation_folder(self):
        self.portal.invokeFactory('Folder', id='folder')
        self.portal.folder.title = 'My Folder'
        self.portal.folder.description = u'This is a folder with two documents'
        self.portal.folder.invokeFactory(
            'Document',
            id='doc1',
            title='A document within a folder'
        )
        self.portal.folder.invokeFactory(
            'Document',
            id='doc2',
            title='A document within a folder'
        )
        self.portal.folder.creation_date = DateTime(
            '2016-01-21T07:14:48+00:00')
        self.portal.folder.modification_date = DateTime(
            '2016-01-21T07:24:11+00:00')
        IMutableUUID(self.portal.folder).set(
            'fc7881c46d61452db4177bc059d9dcb5')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.folder.absolute_url())
        save_response_for_documentation('folder.json', response)

    def test_documentation_collection(self):
        self.portal.invokeFactory('Collection', id='collection')
        self.portal.collection.title = 'My Collection'
        self.portal.collection.description = \
            u'This is a collection with two documents'
        self.portal.collection.query = [{
            'i': 'portal_type',
            'o': 'plone.app.querystring.operation.string.is',
            'v': 'Document',
        }]
        self.portal.invokeFactory(
            'Document',
            id='doc1',
            title='Document 1'
        )
        self.portal.invokeFactory(
            'Document',
            id='doc2',
            title='Document 2'
        )
        self.portal.collection.creation_date = DateTime(
            '2016-01-21T08:14:48+00:00')
        self.portal.collection.modification_date = DateTime(
            '2016-01-21T08:24:11+00:00')
        IMutableUUID(self.portal.collection).set(
            'd0c89bc77f874ce1aad5720921d875c0')
        import transaction
        transaction.commit()
        response = self.api_session.get(self.portal.collection.absolute_url())
        save_response_for_documentation('collection.json', response)

    def test_documentation_siteroot(self):
        response = self.api_session.get(self.portal.absolute_url())
        save_response_for_documentation('siteroot.json', response)

    def test_documentation_404_not_found(self):
        response = self.api_session.get('non-existing-resource')
        save_response_for_documentation('404_not_found.json', response)

    def test_documentation_search(self):
        query = {'sort_on': 'path'}
        response = self.api_session.get('/@search', params=query)
        save_response_for_documentation('search.json', response)

    def test_documentation_workflow(self):
        response = self.api_session.get(
            '{}/@workflow'.format(self.document.absolute_url()))
        save_response_for_documentation('workflow_get.json', response)

    def test_documentation_workflow_transition(self):
        self.frozen_time.tick(timedelta(minutes=5))
        response = self.api_session.post(
            '{}/@workflow/publish'.format(self.document.absolute_url()))
        save_response_for_documentation('workflow_post.json', response)

    def test_documentation_registry_get(self):
        response = self.api_session.get(
            '/@registry/plone.app.querystring.field.path.title')
        save_response_for_documentation('registry_get.json', response)

    def test_documentation_registry_update(self):
        response = self.api_session.patch(
            '/@registry/',
            json={'plone.app.querystring.field.path.title': 'Value'})
        save_response_for_documentation('registry_update.json', response)

    def test_documentation_types(self):
        response = self.api_session.get('/@types')
        save_response_for_documentation('types.json', response)

    def test_documentation_types_document(self):
        response = self.api_session.get('@types/Document')
        save_response_for_documentation('types_document.json', response)

    def test_documentation_login(self):
        self.portal.acl_users.jwt_auth._secret = 'secret'
        self.portal.acl_users.jwt_auth.use_keyring = False
        self.portal.acl_users.jwt_auth.token_timeout = 0
        import transaction
        transaction.commit()
        self.api_session.auth = None
        response = self.api_session.post(
            '{}/@login'.format(self.portal.absolute_url()),
            json={'login': SITE_OWNER_NAME, 'password': SITE_OWNER_PASSWORD})
        save_response_for_documentation('login.json', response)

    def test_documentation_login_renew(self):
        self.portal.acl_users.jwt_auth._secret = 'secret'
        self.portal.acl_users.jwt_auth.use_keyring = False
        self.portal.acl_users.jwt_auth.token_timeout = 0
        import transaction
        transaction.commit()
        self.api_session.auth = None
        response = self.api_session.post(
            '{}/@login'.format(self.portal.absolute_url()),
            json={'login': SITE_OWNER_NAME, 'password': SITE_OWNER_PASSWORD})
        token = json.loads(response.content)['token']
        response = self.api_session.post(
            '{}/@login-renew'.format(self.portal.absolute_url()),
            headers={'Authorization': 'Bearer {}'.format(token)})
        save_response_for_documentation('login_renew.json', response)

    def test_documentation_logout(self):
        self.portal.acl_users.jwt_auth._secret = 'secret'
        self.portal.acl_users.jwt_auth.use_keyring = False
        self.portal.acl_users.jwt_auth.token_timeout = 0
        self.portal.acl_users.jwt_auth.store_tokens = True
        import transaction
        transaction.commit()
        self.api_session.auth = None
        response = self.api_session.post(
            '{}/@login'.format(self.portal.absolute_url()),
            json={'login': SITE_OWNER_NAME, 'password': SITE_OWNER_PASSWORD})
        token = json.loads(response.content)['token']
        response = self.api_session.post(
            '{}/@logout'.format(self.portal.absolute_url()),
            headers={'Authorization': 'Bearer {}'.format(token)})
        save_response_for_documentation('logout.json', response)

    def test_documentation_batching(self):
        folder = self.portal[self.portal.invokeFactory(
            'Folder',
            id='folder',
            title='Folder'
        )]
        for i in range(7):
            folder.invokeFactory(
                'Document',
                id='doc-%s' % str(i + 1),
                title='Document %s' % str(i + 1)
            )
        transaction.commit()

        query = {'sort_on': 'path'}
        response = self.api_session.get(
            '/folder/@search?b_size=5', params=query)
        save_response_for_documentation('batching.json', response)

    def test_documentation_users(self):
        test_user = api.user.get(username=TEST_USER_ID)
        properties = {
            "description": "This is a test user",
            "email": "*****@*****.**",
            "fullname": "Test User",
            "home_page": "http://www.example.com",
            "location": "Bonn",
            "username": "******"
        }
        test_user.setMemberProperties(mapping=properties)
        admin = api.user.get(username='******')
        properties = {
            "description": "This is an admin user",
            "email": "*****@*****.**",
            "fullname": "Administrator",
            "home_page": "http://www.example.com",
            "location": "Berlin",
            "username": "******"
        }
        admin.setMemberProperties(mapping=properties)
        transaction.commit()
        response = self.api_session.get('/@users')
        save_response_for_documentation('users.json', response)

    def test_documentation_users_get(self):
        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(
            email='*****@*****.**',
            username='******',
            properties=properties
        )
        transaction.commit()
        response = self.api_session.get('@users/noam')
        save_response_for_documentation('users_get.json', response)

    def test_documentation_users_filtered_get(self):
        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(
            email='*****@*****.**',
            username='******',
            properties=properties
        )
        transaction.commit()
        response = self.api_session.get('@users', params={'query': 'noa'})
        save_response_for_documentation('users_filtered_by_username.json', response)  # noqa

    def test_documentation_users_created(self):
        response = self.api_session.post(
            '/@users',
            json={
                'username': '******',
                'email': '*****@*****.**',
                'password': '******',
                'username': '******',
                'fullname': 'Noam Avram Chomsky',
                'home_page': 'web.mit.edu/chomsky',
                'description': 'Professor of Linguistics',
                'location': 'Cambridge, MA'
            },
        )
        save_response_for_documentation('users_created.json', response)

    def test_documentation_users_update(self):
        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(
            email='*****@*****.**',
            username='******',
            properties=properties
        )
        transaction.commit()

        response = self.api_session.patch(
            '/@users/noam',
            json={
                'email': '*****@*****.**',
            },
        )
        save_response_for_documentation('users_update.json', response)

    def test_documentation_users_delete(self):
        properties = {
            'email': '*****@*****.**',
            'username': '******',
            'fullname': 'Noam Avram Chomsky',
            'home_page': 'web.mit.edu/chomsky',
            'description': 'Professor of Linguistics',
            'location': 'Cambridge, MA'
        }
        api.user.create(
            email='*****@*****.**',
            username='******',
            properties=properties
        )
        transaction.commit()

        response = self.api_session.delete(
            '/@users/noam')
        save_response_for_documentation('users_delete.json', response)

    def test_documentation_breadcrumbs(self):
        response = self.api_session.get(
            '{}/@components/breadcrumbs'.format(self.document.absolute_url()))
        save_response_for_documentation('breadcrumbs.json', response)

    def test_documentation_navigation(self):
        response = self.api_session.get(
            '{}/@components/navigation'.format(self.document.absolute_url()))
        save_response_for_documentation('navigation.json', response)
Ejemplo n.º 24
0
class TestCopyMoveFunctional(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])
        self.doc1 = self.portal[self.portal.invokeFactory("Document",
                                                          id="doc1",
                                                          title="My Document")]
        self.doc2 = self.portal[self.portal.invokeFactory("Document",
                                                          id="doc2",
                                                          title="My Document")]
        self.folder1 = self.portal[self.portal.invokeFactory(
            "Folder", id="folder1", title="My Folder")]

        api.user.create(email="*****@*****.**",
                        username="******",
                        password="******")

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_copy_single_object(self):
        response = self.api_session.post(
            "/@copy", json={"source": self.doc1.absolute_url()})
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertIn("copy_of_doc1", self.portal.objectIds())

    def test_move_single_object(self):
        response = self.api_session.post(
            "/folder1/@move", json={"source": self.doc1.absolute_url()})
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertIn("doc1", self.folder1.objectIds())
        self.assertNotIn("doc1", self.portal.objectIds())

    def test_move_multiple_objects(self):
        response = self.api_session.post(
            "/folder1/@move",
            json={
                "source": [self.doc1.absolute_url(),
                           self.doc2.absolute_url()]
            },
        )
        self.assertEqual(response.status_code, 200)
        transaction.commit()

        self.assertIn("doc1", self.folder1.objectIds())
        self.assertIn("doc2", self.folder1.objectIds())
        self.assertNotIn("doc1", self.portal.objectIds())
        self.assertNotIn("doc2", self.portal.objectIds())

    def test_copy_without_source_raises_400(self):
        response = self.api_session.post("/folder1/@copy")
        self.assertEqual(response.status_code, 400)

    def test_copy_not_existing_object(self):
        response = self.api_session.post("/@copy",
                                         json={"source": "does-not-exist"})

        self.assertEqual(response.status_code, 200)
        self.assertEqual([], response.json())

    def test_copy_multiple_objects(self):
        response = self.api_session.post(
            "/@copy",
            json={
                "source": [self.doc1.absolute_url(),
                           self.doc2.absolute_url()]
            },
        )
        transaction.commit()

        self.assertEqual(response.status_code, 200)
        self.assertIn("copy_of_doc1", self.portal.objectIds())
        self.assertIn("copy_of_doc2", self.portal.objectIds())

    def test_copy_single_object_no_permissions_raises_403(self):
        self.api_session.auth = ("memberuser", "secret")
        response = self.api_session.post(
            "/@copy", json={"source": self.doc1.absolute_url()})

        self.assertEqual(response.status_code, 403)

    def test_copy_single_object_no_auth_raises_401(self):
        self.api_session.auth = ("nonexistent", "secret")
        response = self.api_session.post(
            "/@copy", json={"source": self.doc1.absolute_url()})

        self.assertEqual(response.status_code, 401)

    def test_move_single_object_no_permissions_raises_403(self):
        self.api_session.auth = ("memberuser", "secret")
        response = self.api_session.post(
            "/@move", json={"source": self.doc1.absolute_url()})

        self.assertEqual(response.status_code, 403)

    def test_move_single_object_no_auth_raises_401(self):
        self.api_session.auth = ("nonexistent", "secret")
        response = self.api_session.post(
            "/@move", json={"source": self.doc1.absolute_url()})

        self.assertEqual(response.status_code, 401)

    def test_move_single_object_no_permission_delete_source_raises_403(self):
        api.user.grant_roles(username="******",
                             obj=self.folder1,
                             roles=["Manager"])
        api.content.transition(obj=self.doc1, transition="publish")
        transaction.commit()

        self.api_session.auth = ("memberuser", "secret")
        response = self.api_session.post(
            "/folder1/@move", json={"source": self.doc1.absolute_url()})

        self.assertEqual(response.status_code, 403)
class DeserializerFunctionalTest(unittest.TestCase):
    """ """
    layer = PLONE_APP_JSON_FIELD_FUNCTIONAL_TESTING

    def setUp(self):
        """ """
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

    def test_deserializer(self):
        """ """
        json_body = {
            '@type': 'TestToken',
            'title': 'Test Organization xxx',
            'id': 'test-hospital'
        }
        with open(os.path.join(FHIR_FIXTURE_PATH, 'Organization.json'), 'r') as f:
            json_body['resource'] = json.load(f)

        response = self.api_session.post(
             self.portal_url,
             json=json_body,
        )
        self.assertEqual(201, response.status_code)
        self.assertEqual(response.json()['resource']['resourceType'], json_body['resource']['resourceType'])

        # Test with mismatched fhir json, I mean mismatcged resource
        json_body['resource'] = """{
          "resourceType": "ValueSet",
          "id": "yesnodontknow",
          "url": "http://hl7.org/fhir/ValueSet/yesnodontknow",
          "name": "Yes/No/Don't Know",
          "status": "draft",
          "description": "For Capturing simple yes-no-don't know answers",
          "compose": {
            "include": [
              {
                "valueSet": [
                  "http://hl7.org/fhir/ValueSet/v2-0136"
                ]
              },
              {
                "system": "http://hl7.org/fhir/data-absent-reason",
                "concept": [
                  {
                    "code": "asked",
                    "display": "Don't know"
                  }
                ]
              }
            ]
          },
          "expansion": {
            "identifier": "urn:uuid:bf99fe50-2c2b-41ad-bd63-bee6919810b4",
            "timestamp": "2015-07-14T10:00:00Z",
            "contains": [
              {
                "system": "http://hl7.org/fhir/v2/0136",
                "code": "Y",
                "display": "Yes"
              },
              {
                "system": "http://hl7.org/fhir/v2/0136",
                "code": "N",
                "display": "No"
              },
              {
                "system": "http://hl7.org/fhir/data-absent-reason",
                "code": "asked",
                "display": "Don't know"
              }
            ]
          }
        }
        """
        json_body['resource'] = json.loads(json_body['resource'])
        json_body['id'] = 'another-hospital'

        response = self.api_session.post(
             self.portal_url,
             json=json_body,
        )
Ejemplo n.º 26
0
class TestCopyMoveFunctional(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.doc1 = self.portal[self.portal.invokeFactory('Document',
                                                          id='doc1',
                                                          title='My Document')]
        self.doc2 = self.portal[self.portal.invokeFactory('Document',
                                                          id='doc2',
                                                          title='My Document')]
        self.folder1 = self.portal[self.portal.invokeFactory(
            'Folder', id='folder1', title='My Folder')]

        api.user.create(email='*****@*****.**',
                        username='******',
                        password='******')

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        transaction.commit()

    def test_copy_single_object(self):
        response = self.api_session.post(
            '/@copy', json={"source": self.doc1.absolute_url()})
        transaction.commit()

        self.assertEquals(response.status_code, 200)
        self.assertIn('copy_of_doc1', self.portal.objectIds())

    def test_move_single_object(self):
        response = self.api_session.post(
            '/folder1/@move', json={"source": self.doc1.absolute_url()})
        transaction.commit()

        self.assertEquals(response.status_code, 200)
        self.assertIn('doc1', self.folder1.objectIds())
        self.assertNotIn('doc1', self.portal.objectIds())

    def test_move_multiple_objects(self):
        response = self.api_session.post(
            '/folder1/@move',
            json={
                "source": [self.doc1.absolute_url(),
                           self.doc2.absolute_url()]
            })
        self.assertEquals(response.status_code, 200)
        transaction.commit()

        self.assertIn('doc1', self.folder1.objectIds())
        self.assertIn('doc2', self.folder1.objectIds())
        self.assertNotIn('doc1', self.portal.objectIds())
        self.assertNotIn('doc2', self.portal.objectIds())

    def test_copy_without_source_raises_400(self):
        response = self.api_session.post('/folder1/@copy')
        self.assertEquals(response.status_code, 400)

    def test_copy_not_existing_object(self):
        response = self.api_session.post('/@copy',
                                         json={"source": "does-not-exist"})

        self.assertEquals(response.status_code, 200)
        self.assertEqual([], response.json())

    def test_copy_multiple_objects(self):
        response = self.api_session.post(
            '/@copy',
            json={
                "source": [self.doc1.absolute_url(),
                           self.doc2.absolute_url()]
            })
        transaction.commit()

        self.assertEquals(response.status_code, 200)
        self.assertIn('copy_of_doc1', self.portal.objectIds())
        self.assertIn('copy_of_doc2', self.portal.objectIds())

    def test_copy_single_object_no_permissions_raises_403(self):
        self.api_session.auth = ('memberuser', 'secret')
        response = self.api_session.post(
            '/@copy', json={"source": self.doc1.absolute_url()})

        self.assertEquals(response.status_code, 403)

    def test_copy_single_object_no_auth_raises_401(self):
        self.api_session.auth = ('nonexistent', 'secret')
        response = self.api_session.post(
            '/@copy', json={"source": self.doc1.absolute_url()})

        self.assertEquals(response.status_code, 401)
Ejemplo n.º 27
0
class TestSearchFunctional(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.portal_url = self.portal.absolute_url()
        self.request = self.portal.REQUEST
        self.catalog = getToolByName(self.portal, "portal_catalog")

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        # /plone/folder
        self.folder = createContentInContainer(self.portal,
                                               u"Folder",
                                               id=u"folder",
                                               title=u"Some Folder")

        # /plone/folder/doc
        self.doc = createContentInContainer(
            self.folder,
            u"DXTestDocument",
            id="doc",
            title=u"Lorem Ipsum",
            start=DateTime(1950, 1, 1, 0, 0),
            effective=DateTime(1995, 1, 1, 0, 0),
            expires=DateTime(1999, 1, 1, 0, 0),
            test_int_field=42,
            test_list_field=["Keyword1", "Keyword2", "Keyword3"],
            test_bool_field=True,
            test_richtext_field=RichTextValue(
                raw=u"<p>Some Text</p>",
                mimeType="text/html",
                outputMimeType="text/html",
            ),
        )
        IMutableUUID(self.doc).set("77779ffa110e45afb1ba502f75f77777")
        self.doc.reindexObject()

        # /plone/folder/other-document
        self.doc2 = createContentInContainer(
            self.folder,
            u"DXTestDocument",
            id="other-document",
            title=u"Other Document",
            description=u"\xdcbersicht",
            start=DateTime(1975, 1, 1, 0, 0),
            effective=DateTime(2015, 1, 1, 0, 0),
            expires=DateTime(2020, 1, 1, 0, 0),
            test_list_field=["Keyword2", "Keyword3"],
            test_bool_field=False,
        )

        # /plone/folder2
        self.folder2 = createContentInContainer(self.portal,
                                                u"Folder",
                                                id=u"folder2",
                                                title=u"Another Folder")

        # /plone/folder2/doc
        createContentInContainer(
            self.folder2,
            u"DXTestDocument",
            id="doc",
            title=u"Document in second folder",
            start=DateTime(1975, 1, 1, 0, 0),
            effective=DateTime(2015, 1, 1, 0, 0),
            expires=DateTime(2020, 1, 1, 0, 0),
            test_bool_field=False,
        )

        # /plone/doc-outside-folder
        createContentInContainer(
            self.portal,
            u"DXTestDocument",
            id="doc-outside-folder",
            title=u"Doc outside folder",
        )

        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_overall_response_format(self):
        response = self.api_session.get("/@search")

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers.get("Content-Type"),
                         "application/json")

        results = response.json()
        self.assertEqual(
            results[u"items_total"],
            len(results[u"items"]),
            "items_total property should match actual item count.",
        )

    def test_search_on_context_constrains_query_by_path(self):
        response = self.api_session.get("/folder/@search")
        self.assertSetEqual(
            {
                u"/plone/folder", u"/plone/folder/doc",
                u"/plone/folder/other-document"
            },
            set(result_paths(response.json())),
        )

    def test_search_in_vhm(self):
        # Install a Virtual Host Monster
        if "virtual_hosting" not in self.app.objectIds():
            # If ZopeLite was imported, we have no default virtual
            # host monster
            from Products.SiteAccess.VirtualHostMonster import (
                manage_addVirtualHostMonster, )

            manage_addVirtualHostMonster(self.app, "virtual_hosting")
        transaction.commit()

        # we don't get a result if we do not provide the full physical path
        response = self.api_session.get("/@search?path=/folder")
        self.assertSetEqual(set(), set(result_paths(response.json())))

        # If we go through the VHM will will get results if we only use
        # the part of the path inside the VHM
        vhm_url = "%s/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/%s" % (
            self.app.absolute_url(),
            "@search?path=/folder",
        )
        response = self.api_session.get(vhm_url)
        self.assertSetEqual(
            {u"/folder", u"/folder/doc", u"/folder/other-document"},
            set(result_paths(response.json())),
        )

    def test_search_in_vhm_multiple_paths(self):
        # Install a Virtual Host Monster
        if "virtual_hosting" not in self.app.objectIds():
            # If ZopeLite was imported, we have no default virtual
            # host monster
            from Products.SiteAccess.VirtualHostMonster import (
                manage_addVirtualHostMonster, )

            manage_addVirtualHostMonster(self.app, "virtual_hosting")
        transaction.commit()

        # path as a list
        query = {"path": ["/folder", "/folder2"]}

        # If we go through the VHM we will get results for multiple paths
        # if we only use the part of the path inside the VHM
        vhm_url = "%s/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/%s" % (
            self.app.absolute_url(),
            "@search",
        )
        response = self.api_session.get(vhm_url, params=query)
        self.assertSetEqual(
            {
                u"/folder",
                u"/folder/doc",
                u"/folder/other-document",
                u"/folder2",
                u"/folder2/doc",
            },
            set(result_paths(response.json())),
        )

        # path as a dict with a query list
        query = {"path.query": ["/folder", "/folder2"]}

        # If we go through the VHM we will get results for multiple paths
        # if we only use the part of the path inside the VHM
        vhm_url = "%s/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/%s" % (
            self.app.absolute_url(),
            "@search",
        )
        response = self.api_session.get(vhm_url, params=query)
        self.assertSetEqual(
            {
                u"/folder",
                u"/folder/doc",
                u"/folder/other-document",
                u"/folder2",
                u"/folder2/doc",
            },
            set(result_paths(response.json())),
        )

    def test_path_gets_prefilled_if_missing_from_path_query_dict(self):
        response = self.api_session.get("/@search?path.depth=1")
        self.assertSetEqual(
            {
                u"/plone/folder", u"/plone/folder2",
                u"/plone/doc-outside-folder"
            },
            set(result_paths(response.json())),
        )

    def test_partial_metadata_retrieval(self):
        query = {
            "SearchableText": "lorem",
            "metadata_fields": ["portal_type", "review_state"],
        }
        response = self.api_session.get("/@search", params=query)

        self.assertDictContainsSubset(
            {
                u"@id": self.portal_url + u"/folder/doc",
                u"title": u"Lorem Ipsum",
                u"portal_type": u"DXTestDocument",
                u"review_state": u"private",
            },
            response.json()["items"][0],
        )

    def test_full_metadata_retrieval(self):
        query = {"SearchableText": "lorem", "metadata_fields": "_all"}
        response = self.api_session.get("/@search", params=query)

        first_item = response.json()["items"][0]
        self.assertDictContainsSubset(
            {
                u"@id": self.portal_url + u"/folder/doc",
                u"Creator": u"test_user_1_",
                u"Description": u"",
                u"EffectiveDate": u"None",
                u"ExpirationDate": u"None",
                u"Subject": [],
                u"Title": u"Lorem Ipsum",
                u"Type": u"DX Test Document",
                u"UID": u"77779ffa110e45afb1ba502f75f77777",
                u"author_name": None,
                u"cmf_uid": None,
                u"commentators": [],
                u"description": u"",
                u"effective": u"1995-01-01T00:00:00+00:00",
                u"end": None,
                u"exclude_from_nav": False,
                u"expires": u"1999-01-01T00:00:00+00:00",
                u"getId": u"doc",
                u"getPath": u"/plone/folder/doc",
                u"getRemoteUrl": None,
                u"getURL": self.portal_url + u"/folder/doc",
                u"id": u"doc",
                u"in_response_to": None,
                u"is_folderish": False,
                u"last_comment_date": None,
                u"listCreators": [u"test_user_1_"],
                u"location": None,
                u"meta_type": u"Dexterity Item",
                u"portal_type": u"DXTestDocument",
                u"review_state": u"private",
                u"start": u"1950-01-01T00:00:00+00:00",
                u"sync_uid": None,
                u"title": u"Lorem Ipsum",
                u"total_comments": 0,
            },
            first_item,
        )
        # This value changed in Plone 5.2
        # (Dexterity gained support for getObjSize)
        self.assertIn(first_item[u"getObjSize"], (u"0 KB", u"1 KB"))

    def test_full_objects_retrieval(self):
        query = {
            "SearchableText": "lorem",
            "metadata_fields": ["portal_type", "review_state"],
            "fullobjects": True,
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            u"<p>Some Text</p>",
            response.json()["items"][0]["test_richtext_field"]["data"],
        )
        self.assertEqual(self.portal_url + u"/folder/doc",
                         response.json()["items"][0]["@id"])

    def test_full_objects_retrieval_discussion(self):
        # Allow discussion
        registry = getUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        settings.globally_enabled = True
        self.doc.allow_discussion = True

        transaction.commit()

        url = "{}/@comments".format(self.doc.absolute_url())
        self.api_session.post(url, json={"text": "comment 1"})
        transaction.commit()

        query = {"portal_type": "Discussion Item", "fullobjects": True}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()["items"]), 1)

    def test_full_objects_retrieval_collections(self):
        self.collection = createContentInContainer(self.folder,
                                                   u"Collection",
                                                   id="collection")
        transaction.commit()

        query = {"portal_type": "Collection", "fullobjects": True}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()["items"]), 1)

    # ZCTextIndex

    def test_fulltext_search(self):
        query = {"SearchableText": "lorem"}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

    def test_fulltext_search_with_non_ascii_characters(self):
        query = {"SearchableText": u"\xfcbersicht"}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/other-document"],
                         result_paths(response.json()))

    # KeywordIndex

    def test_keyword_index_str_query(self):
        query = {"test_list_field": "Keyword1"}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

    def test_keyword_index_str_query_or(self):
        query = {"test_list_field": ["Keyword2", "Keyword3"]}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            [u"/plone/folder/doc", u"/plone/folder/other-document"],
            result_paths(response.json()),
        )

    def test_keyword_index_str_query_and(self):
        query = {
            "test_list_field.query": ["Keyword1", "Keyword2"],
            "test_list_field.operator": "and",
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

    @unittest.skipIf(six.PY3, "Python 3 can't sort mixed types")
    def test_keyword_index_int_query(self):
        self.doc.test_list_field = [42, 23]
        self.doc.reindexObject()
        transaction.commit()

        query = {"test_list_field:int": 42}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

    # BooleanIndex

    def test_boolean_index_query(self):
        query = {"test_bool_field": True, "portal_type": "DXTestDocument"}
        response = self.api_session.get("/folder/@search", params=query)
        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

        query = {"test_bool_field": False, "portal_type": "DXTestDocument"}
        response = self.api_session.get("/folder/@search", params=query)
        self.assertEqual([u"/plone/folder/other-document"],
                         result_paths(response.json()))

    # FieldIndex

    def test_field_index_int_query(self):
        query = {"test_int_field:int": 42}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

    def test_field_index_int_range_query(self):
        query = {
            "test_int_field.query:int": [41, 43],
            "test_int_field.range": "min:max",
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

    # ExtendedPathIndex

    def test_extended_path_index_query(self):
        query = {"path": "/".join(self.folder.getPhysicalPath())}

        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            sorted([
                u"/plone/folder",
                u"/plone/folder/doc",
                u"/plone/folder/other-document",
            ]),
            sorted(result_paths(response.json())),
        )

    def test_extended_path_index_query_multiple(self):
        # path as a list
        query = {
            "path": [
                "/".join(self.folder.getPhysicalPath()),
                "/".join(self.folder2.getPhysicalPath()),
            ]
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            sorted([
                u"/plone/folder",
                u"/plone/folder/doc",
                u"/plone/folder/other-document",
                u"/plone/folder2",
                u"/plone/folder2/doc",
            ]),
            sorted(result_paths(response.json())),
        )

        # path as a dict with a query list
        query = {
            "path.query": [
                "/".join(self.folder.getPhysicalPath()),
                "/".join(self.folder2.getPhysicalPath()),
            ]
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual(
            sorted([
                u"/plone/folder",
                u"/plone/folder/doc",
                u"/plone/folder/other-document",
                u"/plone/folder2",
                u"/plone/folder2/doc",
            ]),
            sorted(result_paths(response.json())),
        )

    def test_extended_path_index_depth_limiting(self):
        lvl1 = createContentInContainer(self.portal, u"Folder", id=u"lvl1")
        lvl2 = createContentInContainer(lvl1, u"Folder", id=u"lvl2")
        createContentInContainer(lvl2, u"Folder", id=u"lvl3")
        transaction.commit()

        path = "/plone/lvl1"

        # Depth 0 - only object identified by path
        query = {"path.query": path, "path.depth": 0}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/lvl1"], result_paths(response.json()))

        # Depth 1 - immediate children
        query = {"path.query": path, "path.depth": 1}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/lvl1/lvl2"], result_paths(response.json()))

        # No depth - object itself and all children
        query = {"path": path}
        response = self.api_session.get("/@search", params=query)

        self.assertSetEqual(
            {u"/plone/lvl1", u"/plone/lvl1/lvl2", u"/plone/lvl1/lvl2/lvl3"},
            set(result_paths(response.json())),
        )

    # DateIndex

    def test_date_index_query(self):
        query = {"start": date(1950, 1, 1).isoformat()}
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

    def test_date_index_ranged_query(self):
        query = {
            "start.query":
            [date(1949, 1, 1).isoformat(),
             date(1951, 1, 1).isoformat()],
            "start.range":
            "min:max",
        }
        response = self.api_session.get("/@search", params=query)

        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))

    # DateRangeIndex

    def test_date_range_index_query(self):
        query = {"effectiveRange": date(1997, 1, 1).isoformat()}
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual(2, len(result_paths(response.json())))
        self.assertTrue(u"/plone/folder" in result_paths(response.json()))
        self.assertTrue(u"/plone/folder/doc" in result_paths(response.json()))

    # DateRecurringIndex

    def test_date_recurring_index_query(self):
        from datetime import datetime

        createContentInContainer(
            self.folder,
            u"Event",
            id=u"event",
            title=u"Event",
            start=datetime(2013, 1, 1, 0, 0),
            end=datetime(2013, 1, 1, 23, 59),
            whole_day=True,
            recurrence="FREQ=DAILY;COUNT=10;INTERVAL=2",
            timezone="UTC",
        )
        import transaction

        transaction.commit()

        # First occurrence
        query = {"start": date(2013, 1, 1).isoformat()}
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual([u"/plone/folder/event"],
                         result_paths(response.json()))

        # No event that day
        query = {"start": date(2013, 1, 2).isoformat()}
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual([], result_paths(response.json()))

        # Second occurrence
        query = {"start": date(2013, 1, 3).isoformat()}
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual([u"/plone/folder/event"],
                         result_paths(response.json()))

        # Ranged query
        query = {
            "start.query":
            [date(2013, 1, 1).isoformat(),
             date(2013, 1, 5).isoformat()],
            "start.range":
            "min:max",
        }
        response = self.api_session.get("/folder/@search", params=query)

        self.assertEqual([u"/plone/folder/event"],
                         result_paths(response.json()))

    # UUIDIndex

    def test_uuid_index_query(self):
        IMutableUUID(self.doc).set("7777a074cb4240d08c9a129e3a837777")
        self.doc.reindexObject()
        transaction.commit()

        query = {"UID": "7777a074cb4240d08c9a129e3a837777"}
        response = self.api_session.get("/@search", params=query)
        self.assertEqual([u"/plone/folder/doc"], result_paths(response.json()))
Ejemplo n.º 28
0
class TestSearchFunctional(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        self.request = self.portal.REQUEST
        self.catalog = getToolByName(self.portal, 'portal_catalog')

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        # /plone/folder
        self.folder = createContentInContainer(
            self.portal, u'Folder',
            id=u'folder',
            title=u'Some Folder')

        # /plone/folder/doc
        self.doc = createContentInContainer(
            self.folder, u'DXTestDocument',
            id='doc',
            title=u'Lorem Ipsum',
            start=DateTime(1950, 1, 1, 0, 0),
            effective=DateTime(1995, 1, 1, 0, 0),
            expires=DateTime(1999, 1, 1, 0, 0),
            test_int_field=42,
            test_list_field=['Keyword1', 'Keyword2', 'Keyword3'],
            test_bool_field=True,
            test_richtext_field=RichTextValue(
                raw=u'<p>Some Text</p>',
                mimeType='text/html',
                outputMimeType='text/html'
            ),
        )
        IMutableUUID(self.doc).set('77779ffa110e45afb1ba502f75f77777')
        self.doc.reindexObject()

        # /plone/folder/other-document
        self.doc2 = createContentInContainer(
            self.folder, u'DXTestDocument',
            id='other-document',
            title=u'Other Document',
            description=u'\xdcbersicht',
            start=DateTime(1975, 1, 1, 0, 0),
            effective=DateTime(2015, 1, 1, 0, 0),
            expires=DateTime(2020, 1, 1, 0, 0),
            test_list_field=['Keyword2', 'Keyword3'],
            test_bool_field=False,
        )

        # /plone/doc-outside-folder
        createContentInContainer(
            self.portal, u'DXTestDocument',
            id='doc-outside-folder',
            title=u'Doc outside folder',
        )

        transaction.commit()

    def test_overall_response_format(self):
        response = self.api_session.get('/@search')

        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.headers.get('Content-Type'),
            'application/json',
        )

        results = response.json()
        self.assertEqual(
            results[u'items_total'],
            len(results[u'items']),
            'items_total property should match actual item count.'
        )

    def test_search_on_context_constrains_query_by_path(self):
        response = self.api_session.get('/folder/@search')
        self.assertSetEqual(
            {u'/plone/folder',
             u'/plone/folder/doc',
             u'/plone/folder/other-document'},
            set(result_paths(response.json())))

    def test_search_in_vhm(self):
        # Install a Virtual Host Monster
        if 'virtual_hosting' not in self.app.objectIds():
            # If ZopeLite was imported, we have no default virtual
            # host monster
            from Products.SiteAccess.VirtualHostMonster \
                import manage_addVirtualHostMonster
            manage_addVirtualHostMonster(self.app, 'virtual_hosting')
        transaction.commit()

        # we don't get a result if we do not provide the full physical path
        response = self.api_session.get('/@search?path=/folder',)
        self.assertSetEqual(set(), set(result_paths(response.json())))

        # If we go through the VHM will will get results if we only use
        # the part of the path inside the VHM
        vhm_url = (
            '%s/VirtualHostBase/http/plone.org/plone/VirtualHostRoot/%s' %
            (self.app.absolute_url(), '@search?path=/folder'))
        response = self.api_session.get(vhm_url)
        self.assertSetEqual(
            {u'/folder',
             u'/folder/doc',
             u'/folder/other-document'},
            set(result_paths(response.json())))

    def test_path_gets_prefilled_if_missing_from_path_query_dict(self):
        response = self.api_session.get('/@search?path.depth=1')
        self.assertSetEqual(
            {u'/plone/folder',
             u'/plone/doc-outside-folder'},
            set(result_paths(response.json())))

    def test_partial_metadata_retrieval(self):
        query = {'SearchableText': 'lorem',
                 'metadata_fields': ['portal_type', 'review_state']}
        response = self.api_session.get('/@search', params=query)

        self.assertDictContainsSubset(
            {u'@id': u'http://*****:*****@search', params=query)

        self.assertDictContainsSubset(
            {u'@id': u'http://*****:*****@search', params=query)

        self.assertEqual(
            u'<p>Some Text</p>',
            response.json()['items'][0]['test_richtext_field']['data'])
        self.assertEqual(
            'http://*****:*****@id'])

    def test_full_objects_retrieval_discussion(self):
        # Allow discussion
        registry = getUtility(IRegistry)
        settings = registry.forInterface(IDiscussionSettings, check=False)
        settings.globally_enabled = True
        self.doc.allow_discussion = True

        transaction.commit()

        url = '{}/@comments'.format(self.doc.absolute_url())
        self.api_session.post(url, json={'text': 'comment 1'})
        transaction.commit()

        query = {'portal_type': 'Discussion Item',
                 'fullobjects': True}
        response = self.api_session.get('/@search', params=query)

        self.assertEquals(response.status_code, 200)
        self.assertEquals(len(response.json()['items']), 1)

    def test_full_objects_retrieval_collections(self):
        self.collection = createContentInContainer(
            self.folder, u'Collection',
            id='collection',
        )
        transaction.commit()

        query = {'portal_type': 'Collection',
                 'fullobjects': True}
        response = self.api_session.get('/@search', params=query)

        self.assertEquals(response.status_code, 200)
        self.assertEquals(len(response.json()['items']), 1)

    # ZCTextIndex

    def test_fulltext_search(self):
        query = {'SearchableText': 'lorem'}
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

    def test_fulltext_search_with_non_ascii_characters(self):
        query = {'SearchableText': u'\xfcbersicht'}
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/other-document'],
            result_paths(response.json())
        )

    # KeywordIndex

    def test_keyword_index_str_query(self):
        query = {'test_list_field': 'Keyword1'}
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

    def test_keyword_index_str_query_or(self):
        query = {'test_list_field': ['Keyword2', 'Keyword3']}
        response = self.api_session.get('/@search', params=query)

        self.assertItemsEqual(
            [u'/plone/folder/doc',
             u'/plone/folder/other-document'],
            result_paths(response.json())
        )

    def test_keyword_index_str_query_and(self):
        query = {
            'test_list_field.query': ['Keyword1', 'Keyword2'],
            'test_list_field.operator': 'and',
        }
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

    def test_keyword_index_int_query(self):
        self.doc.test_list_field = [42, 23]
        self.doc.reindexObject()
        transaction.commit()

        query = {'test_list_field:int': 42}
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

    # BooleanIndex

    def test_boolean_index_query(self):
        query = {'test_bool_field': True, 'portal_type': 'DXTestDocument'}
        response = self.api_session.get('/folder/@search', params=query)
        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

        query = {'test_bool_field': False, 'portal_type': 'DXTestDocument'}
        response = self.api_session.get('/folder/@search', params=query)
        self.assertEqual(
            [u'/plone/folder/other-document'],
            result_paths(response.json())
        )

    # FieldIndex

    def test_field_index_int_query(self):
        query = {'test_int_field:int': 42}
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

    def test_field_index_int_range_query(self):
        query = {
            'test_int_field.query:int': [41, 43],
            'test_int_field.range': 'min:max',
        }
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

    # ExtendedPathIndex

    def test_extended_path_index_query(self):
        query = {'path': '/'.join(self.folder.getPhysicalPath())}

        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder',
             u'/plone/folder/doc',
             u'/plone/folder/other-document'],
            result_paths(response.json())
        )

    def test_extended_path_index_depth_limiting(self):
        lvl1 = createContentInContainer(self.portal, u'Folder', id=u'lvl1')
        lvl2 = createContentInContainer(lvl1, u'Folder', id=u'lvl2')
        createContentInContainer(lvl2, u'Folder', id=u'lvl3')
        transaction.commit()

        path = '/plone/lvl1'

        # Depth 0 - only object identified by path
        query = {'path.query': path, 'path.depth': 0}
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/lvl1'],
            result_paths(response.json()))

        # Depth 1 - immediate children
        query = {'path.query': path, 'path.depth': 1}
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/lvl1/lvl2'],
            result_paths(response.json()))

        # No depth - object itself and all children
        query = {'path': path}
        response = self.api_session.get('/@search', params=query)

        self.assertSetEqual(
            {u'/plone/lvl1', u'/plone/lvl1/lvl2', u'/plone/lvl1/lvl2/lvl3'},
            set(result_paths(response.json())))

    # DateIndex

    def test_date_index_query(self):
        query = {'start': date(1950, 1, 1).isoformat()}
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

    def test_date_index_ranged_query(self):
        query = {
            'start.query': [
                date(1949, 1, 1).isoformat(),
                date(1951, 1, 1).isoformat(),
            ],
            'start.range': 'min:max',
        }
        response = self.api_session.get('/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )

    # DateRangeIndex

    def test_date_range_index_query(self):
        query = {'effectiveRange': date(1997, 1, 1).isoformat()}
        response = self.api_session.get('/folder/@search', params=query)

        self.assertEqual(
            [u'/plone/folder',
             u'/plone/folder/doc'],
            result_paths(response.json())
        )

    # DateRecurringIndex

    def test_date_recurring_index_query(self):
        from datetime import datetime
        createContentInContainer(
            self.folder, u'Event', id=u'event',
            title=u'Event',
            start=datetime(2013, 1, 1, 0, 0),
            end=datetime(2013, 1, 1, 23, 59),
            whole_day=True,
            recurrence='FREQ=DAILY;COUNT=10;INTERVAL=2',
            timezone='UTC',
        )
        import transaction
        transaction.commit()

        # First occurrence
        query = {'start': date(2013, 1, 1).isoformat()}
        response = self.api_session.get('/folder/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/event'],
            result_paths(response.json())
        )

        # No event that day
        query = {'start': date(2013, 1, 2).isoformat()}
        response = self.api_session.get('/folder/@search', params=query)

        self.assertEqual(
            [],
            result_paths(response.json())
        )

        # Second occurrence
        query = {'start': date(2013, 1, 3).isoformat()}
        response = self.api_session.get('/folder/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/event'],
            result_paths(response.json())
        )

        # Ranged query
        query = {
            'start.query': [date(2013, 1, 1).isoformat(),
                            date(2013, 1, 5).isoformat()],
            'start.range': 'min:max',
        }
        response = self.api_session.get('/folder/@search', params=query)

        self.assertEqual(
            [u'/plone/folder/event'],
            result_paths(response.json())
        )

    # UUIDIndex

    def test_uuid_index_query(self):
        IMutableUUID(self.doc).set('7777a074cb4240d08c9a129e3a837777')
        self.doc.reindexObject()
        transaction.commit()

        query = {'UID': '7777a074cb4240d08c9a129e3a837777'}
        response = self.api_session.get('/@search', params=query)
        self.assertEqual(
            [u'/plone/folder/doc'],
            result_paths(response.json())
        )
Ejemplo n.º 29
0
class TestGroupsEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.request = self.layer['request']
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ['Manager'])

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        self.gtool = api.portal.get_tool('portal_groups')
        properties = {
            'title': 'Plone Team',
            'description': 'We are Plone',
            'email': '*****@*****.**',
        }
        self.gtool.addGroup('ploneteam', (), (),
                            properties=properties,
                            title=properties['title'],
                            description=properties['description'])
        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_list_groups(self):
        response = self.api_session.get('/@groups')

        self.assertEqual(200, response.status_code)
        self.assertEqual(5, len(response.json()))
        user_ids = [group['id'] for group in response.json()]
        self.assertIn('Administrators', user_ids)
        self.assertIn('Reviewers', user_ids)
        self.assertIn('AuthenticatedUsers', user_ids)
        self.assertIn('ploneteam', user_ids)
        ptgroup = [
            x for x in response.json() if x.get('groupname') == 'ploneteam'
        ][0]
        self.assertEqual('ploneteam', ptgroup.get('id'))
        self.assertEqual(self.portal.absolute_url() + '/@groups/ploneteam',
                         ptgroup.get('@id'))
        self.assertEqual('*****@*****.**', ptgroup.get('email'))
        self.assertEqual('Plone Team', ptgroup.get('title'))
        self.assertEqual('We are Plone', ptgroup.get('description'))

        # We don't want the group members listed in the overview as there
        # might be loads.
        self.assertTrue(
            not any(['users' in group for group in response.json()]),
            "Users key found in groups listing")

    def test_add_group(self):
        response = self.api_session.post(
            '/@groups',
            json={
                'groupname': 'fwt',
                'email': '*****@*****.**',
                'title': 'Framework Team',
                'description': 'The Plone Framework Team',
                'roles': ['Manager'],
                'groups': ['Administrators'],
                'users': [SITE_OWNER_NAME, TEST_USER_ID]
            },
        )
        transaction.commit()

        self.assertEqual(201, response.status_code)
        fwt = self.gtool.getGroupById('fwt')
        self.assertEqual("*****@*****.**", fwt.getProperty('email'))
        self.assertTrue(
            set([SITE_OWNER_NAME,
                 TEST_USER_ID]).issubset(set(fwt.getGroupMemberIds())),
            "Userids not found in group")

    def test_add_group_groupname_is_required(self):
        response = self.api_session.post(
            '/@groups',
            json={"title": "Framework Team"},
        )
        transaction.commit()

        self.assertEqual(400, response.status_code)
        self.assertTrue('"Property \'groupname\' is required' in response.text)

    def test_get_group(self):
        response = self.api_session.get('/@groups/ploneteam')

        self.assertEqual(response.status_code, 200)
        self.assertEqual('ploneteam', response.json().get('id'))
        self.assertEqual(self.portal.absolute_url() + '/@groups/ploneteam',
                         response.json().get('@id'))
        self.assertEqual('*****@*****.**', response.json().get('email'))
        self.assertEqual('*****@*****.**', response.json().get('email'))
        self.assertEqual('Plone Team', response.json().get('title'))
        self.assertEqual('We are Plone', response.json().get('description'))
        self.assertIn('users', response.json())

    def test_get_search_group_with_filter(self):
        response = self.api_session.get('/@groups', params={'query': 'plo'})

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual('ploneteam', response.json()[0].get('id'))
        self.assertEqual(self.portal.absolute_url() + '/@groups/ploneteam',
                         response.json()[0].get('@id'))
        self.assertEqual('*****@*****.**',
                         response.json()[0].get('email'))

        response = self.api_session.get('/@groups', params={'query': 'Auth'})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.json()), 1)
        self.assertEqual('AuthenticatedUsers', response.json()[0].get('id'))

    def test_get_non_existing_group(self):
        response = self.api_session.get('/@groups/non-existing-group')

        self.assertEqual(response.status_code, 404)

    def test_update_group(self):
        ploneteam = self.gtool.getGroupById('ploneteam')
        ploneteam.addMember(SITE_OWNER_NAME)
        transaction.commit()
        self.assertNotIn(TEST_USER_ID, ploneteam.getGroupMemberIds())
        self.assertIn(SITE_OWNER_NAME, ploneteam.getGroupMemberIds())

        payload = {
            'groupname': 'ploneteam',
            'email': '*****@*****.**',
            'users': {
                TEST_USER_ID: True,
                SITE_OWNER_NAME: False,
            }
        }
        response = self.api_session.patch('/@groups/ploneteam', json=payload)
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        ploneteam = self.gtool.getGroupById('ploneteam')
        self.assertEqual('ploneteam', ploneteam.id)
        self.assertEqual('Plone Team', ploneteam.getProperty('title'))
        self.assertEqual('*****@*****.**',
                         ploneteam.getProperty('email'))
        self.assertIn(TEST_USER_ID, ploneteam.getGroupMemberIds())
        self.assertNotIn(SITE_OWNER_NAME, ploneteam.getGroupMemberIds())

    def test_delete_group(self):
        response = self.api_session.delete('/@groups/ploneteam')
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        self.assertEqual(None, self.gtool.getGroupById('ploneteam'))

    def test_delete_non_existing_group(self):
        response = self.api_session.delete('/@groups/non-existing-group')
        transaction.commit()

        self.assertEqual(response.status_code, 404)
Ejemplo n.º 30
0
class EmailSendEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.mailhost = getUtility(IMailHost)

        registry = getUtility(IRegistry)
        registry["plone.email_from_address"] = "*****@*****.**"
        registry["plone.email_from_name"] = "Plone test site"

        self.api_session = RelativeSession(self.portal_url, test=self)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)
        self.anon_api_session = RelativeSession(self.portal_url, test=self)
        self.anon_api_session.headers.update({"Accept": "application/json"})

        transaction.commit()

    def tearDown(self):
        self.api_session.close()
        self.anon_api_session.close()

    def test_email_send(self):
        response = self.api_session.post(
            "/@email-send",
            json={
                "to": "*****@*****.**",
                "from": "*****@*****.**",
                "message": "Just want to say hi.",
            },
        )
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        msg = self.mailhost.messages[0]
        if isinstance(msg, bytes) and bytes is not str:
            # Python 3 with Products.MailHost 4.10+
            msg = msg.decode("utf-8")
        self.assertTrue(
            "Subject: =?utf-8?q?A_portal_user_via_Plone_site?=" in msg)
        self.assertTrue("From: [email protected]" in msg)
        self.assertTrue("Reply-To: [email protected]" in msg)
        self.assertTrue("Just want to say hi." in msg)

    def test_email_send_all_parameters(self):
        response = self.api_session.post(
            "/@email-send",
            json={
                "to": "*****@*****.**",
                "from": "*****@*****.**",
                "message": "Just want to say hi.",
                "name": "John Doe",
                "subject": "This is the subject.",
            },
        )
        transaction.commit()

        self.assertEqual(response.status_code, 204)
        msg = self.mailhost.messages[0]
        if isinstance(msg, bytes) and bytes is not str:
            # Python 3 with Products.MailHost 4.10+
            msg = msg.decode("utf-8")
        self.assertTrue("=?utf-8?q?This_is_the_subject" in msg)
        self.assertTrue("From: [email protected]" in msg)
        self.assertTrue("John Doe" in msg)
        self.assertTrue("Reply-To: [email protected]" in msg)
        self.assertTrue("Just want to say hi." in msg)

    def test_email_send_anonymous(self):
        response = self.anon_api_session.post(
            "/@email-send",
            json={
                "to": "*****@*****.**",
                "from": "*****@*****.**",
                "message": "Just want to say hi.",
                "name": "John Doe",
                "subject": "This is the subject.",
            },
        )

        self.assertEqual(response.status_code, 401)
class TestQuerystringSearchEndpoint(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        self.portal.invokeFactory("Document", "testdocument", title="Test Document")
        self.doc = self.portal.testdocument

        transaction.commit()

    def tearDown(self):
        self.api_session.close()

    def test_querystringsearch_basic(self):
        response = self.api_session.post(
            "/@querystring-search",
            json={
                "query": [
                    {
                        "i": "portal_type",
                        "o": "plone.app.querystring.operation.selection.is",
                        "v": ["Document"],
                    }
                ]
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertIn("items", response.json())
        self.assertIn("items_total", response.json())
        self.assertEqual(response.json()["items_total"], 1)
        self.assertEqual(len(response.json()["items"]), 1)
        self.assertNotIn("effective", response.json()["items"][0])

    def test_querystringsearch_fullobjects(self):
        response = self.api_session.post(
            "/@querystring-search",
            json={
                "query": [
                    {
                        "i": "portal_type",
                        "o": "plone.app.querystring.operation.selection.is",
                        "v": ["Document"],
                    }
                ],
                "fullobjects": True,
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertIn("items", response.json())
        self.assertIn("items_total", response.json())
        self.assertIn("effective", response.json()["items"][0])
        self.assertEqual(response.json()["items_total"], 1)
        self.assertEqual(len(response.json()["items"]), 1)

    def test_querystringsearch_complex(self):

        for a in range(1, 10):
            self.portal.invokeFactory(
                "Document", "testdocument" + str(a), title="Test Document " + str(a)
            )
            self.doc = self.portal.testdocument

        transaction.commit()

        response = self.api_session.post(
            "/@querystring-search",
            json={
                "query": [
                    {
                        "i": "portal_type",
                        "o": "plone.app.querystring.operation.selection.is",
                        "v": ["Document"],
                    }
                ],
                "b_size": 5,
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertIn("items", response.json())
        self.assertIn("items_total", response.json())
        self.assertEqual(response.json()["items_total"], 10)
        self.assertEqual(len(response.json()["items"]), 5)
        self.assertNotIn("effective", response.json()["items"][0])
        self.assertEqual(response.json()["items"][4]["title"], u"Test Document 4")

        response = self.api_session.post(
            "/@querystring-search",
            json={
                "query": [
                    {
                        "i": "portal_type",
                        "o": "plone.app.querystring.operation.selection.is",
                        "v": ["Document"],
                    }
                ],
                "b_size": 5,
                "b_start": 5,
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertIn("items", response.json())
        self.assertIn("items_total", response.json())
        self.assertEqual(response.json()["items_total"], 10)
        self.assertEqual(len(response.json()["items"]), 5)
        self.assertNotIn("effective", response.json()["items"][0])
        self.assertEqual(response.json()["items"][4]["title"], u"Test Document 9")

    @unittest.skipIf(
        not SUPPORT_NOT_UUID_QUERIES,
        "Skipping because ZCatalog allows not queries on UUIDIndex from >=5.1",
    )
    def test_querystringsearch_do_not_return_context(self):
        self.portal.invokeFactory("Document", "testdocument2", title="Test Document 2")
        self.doc = self.portal.testdocument

        transaction.commit()

        response = self.api_session.post(
            "/testdocument/@querystring-search",
            json={
                "query": [
                    {
                        "i": "portal_type",
                        "o": "plone.app.querystring.operation.selection.is",
                        "v": ["Document"],
                    }
                ],
            },
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()["items_total"], 1)
        self.assertEqual(
            response.json()["items"][0]["@id"],
            "http://localhost:55001/plone/testdocument2",
        )
Ejemplo n.º 32
0
class TestDexterityTypesControlpanel(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer["app"]
        self.portal = self.layer["portal"]
        self.request = self.layer["request"]
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ["Manager"])

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({"Accept": "application/json"})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

    def tearDown(self):
        self.api_session.close()

    def test_controlpanels_dexterity_types_get(self):
        response = self.api_session.get("/@controlpanels/dexterity-types")
        self.assertEqual(200, response.status_code)
        self.assertEqual(
            [
                "Collection",
                "Document",
                "Folder",
                "Link",
                "File",
                "Image",
                "News Item",
                "Event",
                "DXTestDocument",
            ],
            [
                x.get("id") for x in self.api_session.get(
                    "/@controlpanels/dexterity-types").json().get("items")
            ],
        )

    def test_controlpanels_dexterity_types_document_get(self):
        response = self.api_session.get(
            "/@controlpanels/dexterity-types/Document")
        self.assertEqual(200, response.status_code)
        self.assertEqual(
            "{}/@controlpanels/dexterity-types/Document".format(
                self.portal_url),
            response.json().get("@id"),
        )
        self.assertEqual("Page", response.json().get("title"))

    def test_controlpanels_dexterity_types_post(self):
        response = self.api_session.post(
            "/@controlpanels/dexterity-types",
            json={
                "title": "My Custom Content Type",
                "description": "A custom content-type",
            },
        )

        self.assertEqual(201, response.status_code)
        self.assertEqual(
            "{}/@controlpanels/dexterity-types/my_custom_content_type".format(
                self.portal_url),
            response.json().get("@id"),
        )
        self.assertEqual("My Custom Content Type",
                         response.json().get("title"))
        self.assertEqual("A custom content-type",
                         response.json().get("description"))

    def test_controlpanels_dexterity_types_document_patch(self):
        response = self.api_session.patch(
            "/@controlpanels/dexterity-types/Document",
            json={
                "title": "New Content Type Title",
                "description": "New content type description",
            },
        )

        # PATCH returns no content
        self.assertEqual(204, response.status_code)

        response = self.api_session.get(
            "/@controlpanels/dexterity-types/Document")
        self.assertEqual(200, response.status_code)
        self.assertEqual("New Content Type Title",
                         response.json().get("title"))
        self.assertEqual("New content type description",
                         response.json().get("description"))

    def test_controlpanels_dexterity_types_document_delete(self):
        response = self.api_session.delete(
            "/@controlpanels/dexterity-types/Document")

        self.assertEqual(204, response.status_code)
        self.assertEqual(
            [
                "Collection",
                "Folder",
                "Link",
                "File",
                "Image",
                "News Item",
                "Event",
                "DXTestDocument",
            ],
            [
                x.get("id") for x in self.api_session.get(
                    "/@controlpanels/dexterity-types").json().get("items")
            ],
        )
Ejemplo n.º 33
0
class TestContentPatch(unittest.TestCase):

    layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

    def setUp(self):
        self.app = self.layer['app']
        self.portal = self.layer['portal']
        self.portal_url = self.portal.absolute_url()
        setRoles(self.portal, TEST_USER_ID, ['Member'])
        login(self.portal, SITE_OWNER_NAME)

        self.api_session = RelativeSession(self.portal_url)
        self.api_session.headers.update({'Accept': 'application/json'})
        self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

        self.portal.invokeFactory('Document', id='doc1', title='My Document')
        wftool = getToolByName(self.portal, 'portal_workflow')
        wftool.doActionFor(self.portal.doc1, 'publish')
        transaction.commit()

    def test_patch_document(self):
        response = requests.patch(
            self.portal.doc1.absolute_url(),
            headers={'Accept': 'application/json'},
            auth=(SITE_OWNER_NAME, SITE_OWNER_PASSWORD),
            data='{"title": "Patched Document"}',
        )
        self.assertEqual(204, response.status_code)
        transaction.begin()
        self.assertEqual("Patched Document", self.portal.doc1.Title())

    def test_patch_document_with_invalid_body_returns_400(self):
        response = requests.patch(
            self.portal.doc1.absolute_url(),
            headers={'Accept': 'application/json'},
            auth=(SITE_OWNER_NAME, SITE_OWNER_PASSWORD),
            data='foo',
        )
        self.assertEqual(400, response.status_code)
        self.assertIn('DeserializationError', response.text)

    def test_patch_undeserializable_object_returns_501(self):
        obj = PortalContent()
        obj.id = 'obj1'
        obj.portal_type = 'Undeserializable Type'
        self.portal._setObject(obj.id, obj)
        transaction.commit()

        response = requests.patch(
            self.portal.obj1.absolute_url(),
            headers={'Accept': 'application/json'},
            auth=(SITE_OWNER_NAME, SITE_OWNER_PASSWORD),
            data='{"id": "patched_obj1"}',
        )
        self.assertEqual(501, response.status_code)
        self.assertIn('Undeserializable Type', response.text)

    def test_patch_document_returns_401_unauthorized(self):
        response = requests.patch(
            self.portal.doc1.absolute_url(),
            headers={'Accept': 'application/json'},
            auth=(TEST_USER_NAME, TEST_USER_PASSWORD),
            data='{"title": "Patched Document"}',
        )
        self.assertEqual(401, response.status_code)

    def test_patch_feed_event_with_get_contents(self):
        start_date = DateTime(datetime.datetime.today() +
                              datetime.timedelta(days=1)).ISO8601()
        end_date = DateTime(datetime.datetime.today() +
                            datetime.timedelta(days=1, hours=1)).ISO8601()
        response = self.api_session.post(
            '/',
            json={
                "title": "An Event",
                "@type": "Event",
                "start": start_date,
                "end": end_date,
                "timezone": "Europe/Vienna"
            },
        )

        self.assertEqual(201, response.status_code)
        response = response.json()
        event_id = response['id']
        two_days_ahead = DateTime(datetime.datetime.today() +
                                  datetime.timedelta(days=2))
        response = self.api_session.patch('/{}'.format(event_id),
                                          json={
                                              "start": response['start'],
                                              "end": two_days_ahead.ISO8601()
                                          })

        self.assertEqual(204, response.status_code)

        response = self.api_session.get('/{}'.format(event_id))
        response = response.json()
        self.assertEquals(
            DateTime(response['end']).day(), two_days_ahead.day())

        self.assertEquals(
            DateTime(response['end']).hour(), two_days_ahead.hour())