class TestRegistry(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) registry = getUtility(IRegistry) record = Record(field.TextLine(title=u"Foo Bar"), u"Lorem Ipsum") registry.records['foo.bar'] = record transaction.commit() def test_get_registry_record(self): response = self.api_session.get('/@registry/foo.bar') self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), 'Lorem Ipsum') def test_update_registry_record(self): registry = getUtility(IRegistry) payload = {'foo.bar': 'lorem ipsum'} response = self.api_session.patch('/@registry', json=payload) transaction.commit() self.assertEqual(response.status_code, 204) self.assertEqual(registry['foo.bar'], 'lorem ipsum') def test_update_several_registry_records(self): registry = getUtility(IRegistry) record = Record(field.TextLine(title=u"Foo Bar Baz"), u"Lorem Ipsum Dolor") registry.records['foo.bar.baz'] = record transaction.commit() payload = { 'foo.bar': 'lorem ipsum', 'foo.bar.baz': 'lorem ipsum dolor' } response = self.api_session.patch('/@registry', json=payload) transaction.commit() self.assertEqual(response.status_code, 204) self.assertEqual(registry['foo.bar'], 'lorem ipsum') self.assertEqual(registry['foo.bar.baz'], 'lorem ipsum dolor') def test_update_non_existing_registry_record(self): payload = {'foo.bar.baz': 'lorem ipsum'} response = self.api_session.patch('/@registry', json=payload) self.assertEqual(response.status_code, 500)
class TestRegistry(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) registry = getUtility(IRegistry) record = Record(field.TextLine(title=u"Foo Bar"), u"Lorem Ipsum") registry.records['foo.bar'] = record transaction.commit() def test_get_registry_record(self): response = self.api_session.get('/@registry/foo.bar') self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), 'Lorem Ipsum') def test_update_registry_record(self): registry = getUtility(IRegistry) payload = {'foo.bar': 'lorem ipsum'} response = self.api_session.patch('/@registry', json=payload) transaction.commit() self.assertEqual(response.status_code, 204) self.assertEqual(registry['foo.bar'], 'lorem ipsum') def test_update_several_registry_records(self): registry = getUtility(IRegistry) record = Record(field.TextLine(title=u"Foo Bar Baz"), u"Lorem Ipsum Dolor") registry.records['foo.bar.baz'] = record transaction.commit() payload = {'foo.bar': 'lorem ipsum', 'foo.bar.baz': 'lorem ipsum dolor'} response = self.api_session.patch('/@registry', json=payload) transaction.commit() self.assertEqual(response.status_code, 204) self.assertEqual(registry['foo.bar'], 'lorem ipsum') self.assertEqual(registry['foo.bar.baz'], 'lorem ipsum dolor') def test_update_non_existing_registry_record(self): payload = {'foo.bar.baz': 'lorem ipsum'} response = self.api_session.patch('/@registry', json=payload) self.assertEqual(response.status_code, 500)
def test_gestore_comunicati_can_update_data(self): api_session = RelativeSession(self.portal_url) api_session.headers.update({"Accept": "application/json"}) api_session.auth = ("memberuser", "secret") url = "{}/123".format(self.url) self.assertEqual(api_session.patch(url, json={}).status_code, 401) setRoles(self.portal, "memberuser", ["Gestore Comunicati"]) transaction.commit() # 400 because it's a fake id self.assertEqual(api_session.patch(self.url, json={}).status_code, 400) api_session.close()
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())
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())
class TestControlpanelsEndpoint(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_get_listing(self): # Do we get a list with at least one item? response = self.api_session.get('/@controlpanels') self.assertEqual(200, response.status_code) data = response.json() self.assertIs(type(data), list) self.assertGreater(len(data), 0) def test_get_item_nonexisting(self): response = self.api_session.get('/@controlpanels/no-way-jose') self.assertEqual(404, response.status_code) def test_get_item(self): response = self.api_session.get('/@controlpanels/editing') self.assertEqual(200, response.status_code) def test_all_controlpanels(self): # make sure all define controlpanels serialize response = self.api_session.get('/@controlpanels') for item in response.json(): response = self.api_session.get(item['@id']) self.assertEqual( 200, response.status_code, '{} failed: {}'.format(item['@id'], response.json())) def test_patch_needs_parameter(self): response = self.api_session.patch('/@controlpanels') self.assertEqual(400, response.status_code) self.assertEqual('Missing parameter controlpanelname', response.json()['message']) def test_update(self): # get current settings, switch them and check if it changed response = self.api_session.get('/@controlpanels/editing') old_data = response.json()['data'] # switch values and set new_values = { 'ext_editor': not old_data['ext_editor'], 'lock_on_ttw_edit': not old_data['lock_on_ttw_edit'] } response = self.api_session.patch('/@controlpanels/editing', json=new_values) # check if the values changed response = self.api_session.get('/@controlpanels/editing') self.assertNotEqual(response.json(), old_data) def test_update_all(self): # Mail is in faulty state by default self.api_session.patch('/@controlpanels/mail', json={ 'email_from_address': '*****@*****.**', 'email_from_name': 'Jos Henken', }) # make sure all define controlpanels deserialize response = self.api_session.get('/@controlpanels') for item in response.json(): # get current data response = self.api_session.get(item['@id']) # store the outputted data response = self.api_session.patch(item['@id'], json=response.json()['data']) self.assertEqual( 204, response.status_code, '{} failed: {}'.format(item['@id'], response.content)) def test_update_required(self): KEY = 'email_charset' URL = '/@controlpanels/mail' # sanity check response = self.api_session.get(URL) response = response.json() self.assertIn(KEY, response['schema']['required']) response = self.api_session.patch(URL, json={KEY: None}) self.assertEqual(response.status_code, 400) response = response.json() self.assertIn('message', response) self.assertIn('Required input is missing.', response['message'])
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)
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)
class TestControlpanelsEndpoint(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) def tearDown(self): self.api_session.close() def test_get_listing(self): # Do we get a list with at least one item? response = self.api_session.get("/@controlpanels") self.assertEqual(200, response.status_code) data = response.json() self.assertIs(type(data), list) self.assertGreater(len(data), 0) def test_get_item_nonexisting(self): response = self.api_session.get("/@controlpanels/no-way-jose") self.assertEqual(404, response.status_code) def test_get_item(self): response = self.api_session.get("/@controlpanels/editing") self.assertEqual(200, response.status_code) def test_all_controlpanels(self): # make sure all define controlpanels serialize response = self.api_session.get("/@controlpanels") for item in response.json(): response = self.api_session.get(item["@id"]) self.assertEqual( 200, response.status_code, "{} failed: {}".format(item["@id"], response.json()), ) def test_patch_needs_parameter(self): response = self.api_session.patch("/@controlpanels") self.assertEqual(400, response.status_code) self.assertEqual("Missing parameter controlpanelname", response.json()["message"]) def test_update(self): # get current settings, switch them and check if it changed response = self.api_session.get("/@controlpanels/editing") old_data = response.json()["data"] # switch values and set new_values = { "ext_editor": not old_data["ext_editor"], "lock_on_ttw_edit": not old_data["lock_on_ttw_edit"], } response = self.api_session.patch("/@controlpanels/editing", json=new_values) # check if the values changed response = self.api_session.get("/@controlpanels/editing") self.assertNotEqual(response.json(), old_data) def test_update_all(self): # Mail is in faulty state by default self.api_session.patch( "/@controlpanels/mail", json={ "email_from_address": "*****@*****.**", "email_from_name": "Jos Henken", }, ) # make sure all define controlpanels deserialize response = self.api_session.get("/@controlpanels") for item in response.json(): # get current data response = self.api_session.get(item["@id"]) # store the outputted data response = self.api_session.patch(item["@id"], json=response.json()["data"]) self.assertEqual( 204, response.status_code, "{} failed: {}".format(item["@id"], response.content), ) def test_update_required(self): KEY = "email_charset" URL = "/@controlpanels/mail" # sanity check response = self.api_session.get(URL) response = response.json() self.assertIn(KEY, response["schema"]["required"]) response = self.api_session.patch(URL, json={KEY: None}) self.assertEqual(response.status_code, 400) response = response.json() self.assertIn("message", response) self.assertIn("Required input is missing.", response["message"])
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")
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']))
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')
class TestServicesTiles(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) fti = queryUtility(IDexterityFTI, name='Document') behavior_list = [a for a in fti.behaviors] behavior_list.append('plone.tiles') behavior_list.append('plone.leadimage') fti.behaviors = tuple(behavior_list) self.doc = createContentInContainer(self.portal, u'Document', id=u'doc', title=u'A document') transaction.commit() sampleTileType = TileType(u'sample.tile', u'Sample tile', 'cmf.ModifyPortalContent', 'zope.Public', description=u'A tile used for testing', schema=ISampleTile, icon='testicon') provideUtility(sampleTileType, name=u'sample.tile') provideAdapter(SampleTile, (Interface, Interface), IBasicTile, name=u'sample.tile') def test_get_available_tiles(self): response = self.api_session.get('/@tiles') self.assertEqual(response.status_code, 200) response = response.json() self.assertEquals(len(response), 1) self.assertEquals(response[0]['@id'], self.portal_url + u'/@tiles/sample.tile') self.assertEquals(response[0]['title'], u'Sample tile') self.assertEquals(response[0]['description'], u'A tile used for testing') self.assertEquals(response[0]['icon'], 'testicon') def test_get_tile(self): response = self.api_session.get('/@tiles/sample.tile') self.assertEqual(response.status_code, 200) response = response.json() self.assertEquals(response['title'], u'Sample tile') self.assertEquals(response['properties']['title']['title'], u'Title') self.assertEquals(response['properties']['title']['type'], u'string') def test_patch_tiles_list(self): response = self.api_session.patch('/doc', json={ "tiles": { 'uuid1': { '@type': 'title' }, 'uuid2': { '@type': 'description' } }, }) self.assertEqual(response.status_code, 204) response = self.api_session.get('/doc') response = response.json() self.assertEquals(response['tiles'], { 'uuid1': { '@type': 'title' }, 'uuid2': { '@type': 'description' } }) def test_patch_tiles_layout(self): response = self.api_session.patch( '/doc', json={"tiles_layout": { "items": ["#uuid1", "#uuid2"] }}) self.assertEqual(response.status_code, 204) response = self.api_session.get('/doc') response = response.json() self.assertEquals(response['tiles_layout'], {"items": ["#uuid1", "#uuid2"]}) def test_get_tiles_layout_schema(self): response = self.api_session.get('/@types/Document') self.assertEqual(response.status_code, 200) response = response.json()
class SocialSettingsControlpanelTest(unittest.TestCase): layer = VOLTO_COOKIECONSENT_API_FUNCTIONAL_TESTING def setUp(self): self.app = self.layer["app"] self.portal = self.layer["portal"] self.portal_url = self.portal.absolute_url() self.controlpanel_url = "/@controlpanels/cookieconsent-settings" 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.page = api.content.create(container=self.portal, type="Document", title="A page") commit() def tearDown(self): self.api_session.close() def get_record_value(self): record = api.portal.get_registry_record( "cookie_consent_configuration", interface=ICookieConsentSettings, default="", ) if not record: return [] return json.loads(record) def test_controlpanel_exists(self): response = self.api_session.get(self.controlpanel_url) self.assertEqual(response.status_code, 200) self.assertEqual(response.headers.get("Content-Type"), "application/json") def test_controlpanel_listed(self): response = self.api_session.get("/@controlpanels") titles = [x.get("title") for x in response.json()] self.assertIn("Cookie Consent Settings", titles) def test_deserializer_convert_internal_links_in_resolveuid(self): data = { "it": '<p>Text with <a href="{}">link</a></p>'.format( self.page.absolute_url()) } self.api_session.patch( self.controlpanel_url, json={"cookie_consent_configuration": json.dumps(data)}, ) commit() record = self.get_record_value() self.assertIn("resolveuid/{}".format(self.page.UID()), record["it"]) self.assertNotIn(self.page.absolute_url(), record["it"]) def test_serializer_convert_resolveuids_in_proper_links(self): data = { "it": '<p>Text with <a href="{}">link</a></p>'.format( self.page.absolute_url()) } self.api_session.patch( self.controlpanel_url, json={"cookie_consent_configuration": json.dumps(data)}, ) commit() record = self.get_record_value() response = self.api_session.get(self.controlpanel_url) res = response.json() self.assertIn("resolveuid/{}".format(self.page.UID()), record["it"]) self.assertNotIn( "resolveuid/{}".format(self.page.UID()), res["data"]["cookie_consent_configuration"], ) self.assertIn( self.page.absolute_url(), res["data"]["cookie_consent_configuration"], )
class TestServicesTiles(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) fti = queryUtility(IDexterityFTI, name="Document") behavior_list = [a for a in fti.behaviors] behavior_list.append("plone.tiles") behavior_list.append("plone.leadimage") fti.behaviors = tuple(behavior_list) self.doc = createContentInContainer( self.portal, u"Document", id=u"doc", title=u"A document" ) transaction.commit() sampleTileType = TileType( u"sample.tile", u"Sample tile", "cmf.ModifyPortalContent", "zope.Public", description=u"A tile used for testing", schema=ISampleTile, icon="testicon", ) provideUtility(sampleTileType, name=u"sample.tile") provideAdapter( SampleTile, (Interface, Interface), IBasicTile, name=u"sample.tile" ) def tearDown(self): self.api_session.close() def test_get_available_tiles(self): response = self.api_session.get("/@tiles") self.assertEqual(response.status_code, 200) response = response.json() self.assertEqual(len(response), 1) self.assertEqual(response[0]["@id"], self.portal_url + u"/@tiles/sample.tile") self.assertEqual(response[0]["title"], u"Sample tile") self.assertEqual(response[0]["description"], u"A tile used for testing") self.assertEqual(response[0]["icon"], "testicon") def test_get_tile(self): response = self.api_session.get("/@tiles/sample.tile") self.assertEqual(response.status_code, 200) response = response.json() self.assertEqual(response["title"], u"Sample tile") self.assertEqual(response["properties"]["title"]["title"], u"Title") self.assertEqual(response["properties"]["title"]["type"], u"string") def test_patch_tiles_list(self): response = self.api_session.patch( "/doc", json={ "tiles": { "uuid1": {"@type": "title"}, "uuid2": {"@type": "description"}, } }, ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/doc") response = response.json() self.assertEqual( response["tiles"], {"uuid1": {"@type": "title"}, "uuid2": {"@type": "description"}}, ) def test_patch_tiles_layout(self): response = self.api_session.patch( "/doc", json={"tiles_layout": {"items": ["#uuid1", "#uuid2"]}} ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/doc") response = response.json() self.assertEqual(response["tiles_layout"], {"items": ["#uuid1", "#uuid2"]}) def test_get_tiles_layout_schema(self): response = self.api_session.get("/@types/Document") self.assertEqual(response.status_code, 200) response = response.json()
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)
class TestServicesTypes(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.api_session.post( "/@types/Document", json={ "factory": "fieldset", "title": "Contact Info", "description": "Contact information", }, ) self.api_session.post( "/@types/Document", json={ "factory": "fieldset", "title": "Location", "description": "Location", }, ) self.api_session.post( "/@types/Document", json={ "factory": "Email", "title": "Author email", "description": "Email of the author", }, ) self.api_session.post( "/@types/Document", json={ "factory": "URL", "title": "Author url", "description": "Website of the author", }, ) def tearDown(self): # Remove all custom changed on Document self.api_session.put("/@types/Document", json={}) self.api_session.close() def test_get_types(self): response = self.api_session.get("{}/@types".format( self.portal.absolute_url())) # noqa self.assertEqual(response.status_code, 200) self.assertEqual( response.headers.get("Content-Type"), "application/json", "Sending a GET request to @types endpoint should respond with " + 'Content-Type: "application/json", not ' + '"{}"'.format(response.headers.get("Content-Type")), ) for item in response.json(): self.assertEqual(sorted(item), sorted(["@id", "title", "addable"])) def test_get_types_document(self): response = self.api_session.get("{}/@types/Document".format( self.portal.absolute_url())) self.assertEqual(response.status_code, 200) self.assertEqual( response.headers.get("Content-Type"), "application/json+schema", "Sending a GET request to @types endpoint should respond with " + 'Content-Type: "application/json+schema", not ' + '"{}"'.format(response.headers.get("Content-Type")), ) def test_get_types_document_edit(self): response = self.api_session.get("/@types/Document") self.assertEqual(response.status_code, 200) # Fields are present self.assertIn("author_email", response.json().get("properties")) self.assertIn("author_url", response.json().get("properties")) # All fieldsets are present even empty ones self.assertIn("location", [f["id"] for f in response.json().get("fieldsets")]) # noqa self.assertIn("contact_info", [f["id"] for f in response.json().get("fieldsets")]) # noqa def test_types_document_get_fieldset(self): response = self.api_session.get("/@types/Document/contact_info") self.assertEqual(response.status_code, 200) self.assertEqual("Contact Info", response.json().get("title")) self.assertEqual("Contact information", response.json().get("description")) # noqa self.assertEqual("contact_info", response.json().get("id")) self.assertEqual([], response.json().get("fields")) def test_types_document_get_field(self): response = self.api_session.get("/@types/Document/author_email") self.assertEqual(response.status_code, 200) self.assertEqual("Author email", response.json().get("title")) # noqa self.assertEqual("Email of the author", response.json().get("description")) # noqa self.assertTrue(response.json().get("behavior").startswith( "plone.dexterity.schema.generated.plone_")) # noqa self.assertEqual("string", response.json().get("type")) self.assertEqual("email", response.json().get("widget")) def test_types_document_post_fieldset(self): response = self.api_session.post( "/@types/Document", json={ "factory": "fieldset", "title": "Foo bar", "description": "Foo bar tab", }, ) self.assertEqual(response.status_code, 201) self.assertEqual("Foo bar", response.json().get("title")) self.assertEqual("Foo bar tab", response.json().get("description")) # noqa self.assertEqual("foo_bar", response.json().get("id")) self.assertEqual([], response.json().get("fields")) def test_types_document_post_field(self): response = self.api_session.post( "/@types/Document", json={ "factory": "Email", "title": "Email", "description": "Foo bar email", "required": True, }, ) self.assertEqual(response.status_code, 201) self.assertEqual("Email", response.json().get("title")) self.assertEqual("Foo bar email", response.json().get("description")) self.assertTrue(response.json().get("behavior").startswith( "plone.dexterity.schema.generated.plone_")) # noqa self.assertEqual("string", response.json().get("type")) self.assertEqual("email", response.json().get("widget")) def test_types_document_patch_properties(self): response = self.api_session.patch( "/@types/Document", json={ "properties": { "author_email": { "default": "*****@*****.**", "minLength": 5, "maxLength": 100, } } }, ) # PATCH returns no content self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document/author_email") self.assertEqual(200, response.status_code) self.assertEqual("*****@*****.**", response.json().get("default")) self.assertEqual(5, response.json().get("minLength")) self.assertEqual(100, response.json().get("maxLength")) def test_types_document_patch_fieldsets(self): response = self.api_session.patch( "/@types/Document", json={ "fieldsets": [{ "id": "contact_info", "title": "Contact information", "fields": ["author_email"], }] }, ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document/contact_info") self.assertEqual(200, response.status_code) self.assertEqual("Contact information", response.json().get("title")) self.assertEqual(["author_email"], response.json().get("fields")) def test_types_document_patch_one_fieldset(self): response = self.api_session.patch( "/@types/Document/contact_info", json={ "title": "Contact the author", "description": "Reach the author", "fields": [ "author_url", "author_email", ], }, ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document/contact_info") self.assertEqual(200, response.status_code) self.assertEqual("Contact the author", response.json().get("title")) self.assertEqual("Reach the author", response.json().get("description")) # noqa self.assertEqual(["author_url", "author_email"], response.json().get("fields")) # noqa def test_types_document_patch_one_field(self): response = self.api_session.patch( "/@types/Document/author_email", json={ "title": "Author e-mail", "description": "The e-mail address of the author", "minLength": 10, "maxLength": 200, "required": False, }, ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document/author_email") self.assertEqual(200, response.status_code) self.assertEqual("Author e-mail", response.json().get("title")) self.assertEqual("The e-mail address of the author", response.json().get("description")) # noqa self.assertEqual(10, response.json().get("minLength")) self.assertEqual(200, response.json().get("maxLength")) def test_types_document_patch_create_missing(self): response = self.api_session.patch( "/@types/Document", json={ "fieldsets": [{ "title": "Layout", "fields": ["blocks", "blocks_layout"] }], "properties": { "blocks": { "title": "Blocks", "type": "dict", "widget": "json", "factory": "JSONField", "default": { "230bdd04-6a0d-4cd2-ab60-4c09b315cc2c": { "@type": "title" }, "338013ce-acca-454f-a6f4-14113c187dca": { "@type": "text", "text": { "blocks": [{ "data": {}, "depth": 0, "entityRanges": [], "inlineStyleRanges": [], "key": "99pvk", "text": "Book summary", "type": "unstyled", }], "entityMap": {}, }, }, "5060e030-727b-47bc-8023-b80b7cccd96f": { "@type": "image" }, "e3d8f8e4-8fee-47e7-9451-28724bf74a90": { "@type": "text" }, }, }, "blocks_layout": { "title": "Blocks Layout", "type": "dict", "widget": "json", "factory": "JSONField", "default": { "items": [ "230bdd04-6a0d-4cd2-ab60-4c09b315cc2c", "338013ce-acca-454f-a6f4-14113c187dca", "5060e030-727b-47bc-8023-b80b7cccd96f", "e3d8f8e4-8fee-47e7-9451-28724bf74a90", ] }, }, }, }, ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document") self.assertEqual(200, response.status_code) self.assertIn("blocks", response.json().get("properties")) self.assertIn("blocks_layout", response.json().get("properties")) fieldsets = [ f for f in response.json().get("fieldsets") if f.get("id") == "layout" ] # noqa self.assertTrue(len(fieldsets) == 1) self.assertTrue(["blocks", "blocks_layout"], fieldsets[0].get("fields")) # noqa def test_types_document_update_min_max(self): response = self.api_session.patch( "/@types/Document", json={ "properties": { "custom_text": { "factory": "Text line (String)", "minLength": 2, "maxLength": 20, "title": "Custom text", }, "custom_float": { "title": "Custom float", "factory": "Floating-point number", "minimum": 2.0, "maximum": 14.0, }, } }, ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document/custom_text") self.assertEqual(200, response.status_code) self.assertEqual(2, response.json().get("minLength")) self.assertEqual(20, response.json().get("maxLength")) response = self.api_session.get("/@types/Document/custom_float") self.assertEqual(200, response.status_code) self.assertEqual(2, response.json().get("minimum")) self.assertEqual(14.0, response.json().get("maximum")) def test_types_document_put(self): response = self.api_session.get("/@types/Document") doc_json = response.json() doc_json["layouts"] = ["thumbnail_view", "table_view"] doc_json["fieldsets"] = [ { "id": "author", "title": "Contact the author", "fields": ["author_email", "author_name"], }, { "id": "contact_info", "title": "Contact info", "fields": [] }, ] doc_json["properties"]["author_name"] = { "description": "Name of the author", "factory": "Text line (String)", "title": "Author name", } doc_json["properties"].pop("author_url") doc_json["properties"]["author_email"] = { "minLength": 0, "maxLength": 100, "default": None, } response = self.api_session.put("/@types/Document", json=doc_json) self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document") self.assertEqual(200, response.status_code) # Layouts updated self.assertEqual(["thumbnail_view", "table_view"], response.json().get("layouts")) # noqa # Field added self.assertIn("author_name", response.json().get("properties")) # Field removed self.assertTrue("author_url" not in response.json().get("properties")) # Field updated self.assertEqual(None, response.json().get("properties").get( "author_email").get("default")) # noqa # Fieldset added self.assertIn("author", [f["id"] for f in response.json().get("fieldsets")]) # noqa # Fieldset removed self.assertTrue("location" not in [ f["id"] for f in response.json().get("fieldsets") ]) # noqa # Fieldset updated self.assertIn("contact_info", [f["id"] for f in response.json().get("fieldsets")]) # noqa def test_types_document_remove_field(self): response = self.api_session.delete("/@types/Document/author_email", ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document") self.assertEqual(200, response.status_code) self.assertTrue("author_email" not in response.json().get("properties")) # noqa def test_types_document_remove_fieldset(self): response = self.api_session.delete("/@types/Document/contact_info", ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/@types/Document") self.assertEqual(200, response.status_code) self.assertTrue("contact_info" not in [ f["id"] for f in response.json().get("fieldsets") ]) # noqa def test_get_types_with_unknown_type(self): response = self.api_session.get("{}/@types/UnknownType".format( self.portal.absolute_url())) self.assertEqual(response.status_code, 404) self.assertEqual( "application/json", response.headers.get("Content-Type"), "Sending a GET request to @types endpoint should respond with " + 'Content-Type: "application/json", not ' + '"{}"'.format(response.headers.get("Content-Type")), ) def test_types_endpoint_only_accessible_for_authenticated_users(self): self.api_session.auth = () response = self.api_session.get("{}/@types".format( self.portal.absolute_url())) # noqa self.assertEqual(response.status_code, 401) def test_contextaware_addable(self): response = self.api_session.get("{}/@types".format( self.portal.absolute_url())) # noqa allowed_ids = [x.getId() for x in self.portal.allowedContentTypes()] response_allowed_ids = [ x["@id"].split("/")[-1] for x in response.json() if x["addable"] ] # We check subset here, because only DX types are returned by the # endpoint. self.assertNotEqual([], allowed_ids) self.assertTrue(set(response_allowed_ids).issubset(set(allowed_ids))) def test_image_type(self): response = self.api_session.get("/@types/Image") response = response.json() self.assertIn("fieldsets", response) self.assertIn("image.data", response["properties"]["image"]["properties"]) # noqa def test_file_type(self): response = self.api_session.get("/@types/File") response = response.json() self.assertIn("fieldsets", response) self.assertIn("file.data", response["properties"]["file"]["properties"]) # noqa def test_event_type(self): response = self.api_session.get("/@types/Event") response = response.json() self.assertIn("title", response["properties"]["start"]) def test_addable_types_for_non_manager_user(self): user = api.user.create(email="*****@*****.**", username="******", password="******") folder = api.content.create(container=self.portal, id="folder", type="Folder", title=u"folder") folder_cant_add = api.content.create( container=self.portal, id="folder_cant_add", type="Folder", title=u"folder_cant_add", ) api.user.grant_roles(user=user, obj=folder, roles=["Contributor"]) api.user.grant_roles(user=user, obj=folder_cant_add, roles=["Reader"]) transaction.commit() self.api_session.auth = ("noam", "12345") # In the folder, the user should be able to add types since we granted # Contributor role on it response = self.api_session.get("/folder/@types") response = response.json() # Any addable type will do. self.assertTrue(any(a["addable"] for a in response)) # In the folder where the user only have Reader role, no types are # addable response = self.api_session.get("/folder_cant_add/@types") response = response.json() self.assertEqual(len([a for a in response if a["addable"]]), 0) # and in the root Plone site there's no addable types response = self.api_session.get("/@types") response = response.json() self.assertEqual(len([a for a in response if a["addable"]]), 0)
class TestSearchTextInBlocks(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) fti = queryUtility(IDexterityFTI, name="Document") behavior_list = [a for a in fti.behaviors] behavior_list.append("volto.blocks") fti.behaviors = tuple(behavior_list) self.doc = createContentInContainer(self.portal, u"Document", id=u"doc", title=u"A document") transaction.commit() def tearDown(self): self.api_session.close() def test_search_text(self): response = self.api_session.patch( "/doc", json={ "blocks": { "uuid1": { "@type": "text", "text": { "blocks": [{ "data": {}, "depth": 0, "entityRanges": [], "inlineStyleRanges": [], "key": "acv4f", "text": "Plone " "text " "for " "block ", "type": "unstyled", }], "entityMap": {}, }, }, "uuid2": { "@type": "text", "text": { "blocks": [{ "data": {}, "depth": 0, "entityRanges": [], "inlineStyleRanges": [], "key": "1m9qt", "text": "Volto " "text " "for " "block ", "type": "unstyled", }], "entityMap": {}, }, }, } }, ) self.assertEqual(response.status_code, 204) query = {"SearchableText": "Volto", "metadata_fields": "Title"} response = self.api_session.get("/@search", params=query) json_response = response.json() self.assertEqual(json_response["items_total"], 1) self.assertEqual(json_response["items"][0]["Title"], "A document") query = {"SearchableText": "Plone", "metadata_fields": "Title"} response = self.api_session.get("/@search", params=query) json_response = response.json() self.assertEqual(json_response["items_total"], 1) self.assertEqual(json_response["items"][0]["Title"], "A document")
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())
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)
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)
class TestSubscriptionsUpdate(unittest.TestCase): layer = RER_UFFICIOSTAMPA_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"]) 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) self.anon_api_session = RelativeSession(self.portal_url) self.anon_api_session.headers.update({"Accept": "application/json"}) self.url = "{}/@subscriptions".format(self.portal_url) self.tool = getUtility(ISubscriptionsStore) self.id = self.tool.add( { "channels": ["foo"], "email": "*****@*****.**", "name": "John", "surname": "Doe", "phone": "123456", }, ) transaction.commit() def tearDown(self): self.api_session.close() self.anon_api_session.close() def test_patch_should_be_called_with_id(self): res = self.api_session.patch(self.url, json={}) self.assertEqual( self.api_session.patch(self.url, json={}).status_code, 400) self.assertEqual("Missing id", res.json()["message"]) def test_anon_cant_update_data(self): url = "{}/123".format(self.url) self.assertEqual( self.anon_api_session.patch(url, json={}).status_code, 401) def test_gestore_comunicati_can_update_data(self): api_session = RelativeSession(self.portal_url) api_session.headers.update({"Accept": "application/json"}) api_session.auth = ("memberuser", "secret") url = "{}/123".format(self.url) self.assertEqual(api_session.patch(url, json={}).status_code, 401) setRoles(self.portal, "memberuser", ["Gestore Comunicati"]) transaction.commit() # 400 because it's a fake id self.assertEqual(api_session.patch(self.url, json={}).status_code, 400) api_session.close() def test_bad_request_if_pass_wrong_id(self): res = self.api_session.patch("{}/foo".format(self.url), json={}) self.assertEqual(res.status_code, 400) self.assertEqual(res.json()["message"], "Id should be a number.") res = self.api_session.patch("{}/123".format(self.url), json={}) self.assertEqual(res.status_code, 400) self.assertEqual(res.json()["message"], 'Unable to find item with id "123"') def test_correctly_save_data(self): url = "{}/{}".format(self.url, self.id) record = self.tool.get_record(self.id) self.assertEqual(record.attrs["name"], "John") self.api_session.patch(url, json={"name": "Jack"}) res = self.api_session.get(self.url).json() self.assertEqual(res["items"][0]["name"], "Jack") self.assertEqual(res["items"][0]["id"], self.id)
class TestHistoryEndpoint(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', id='doc_with_history', title='My Document') self.doc = self.portal.doc_with_history self.doc.setTitle('Current version') api.content.transition(self.doc, 'publish') self.endpoint_url = '{}/@history'.format(self.doc.absolute_url()) transaction.commit() def test_get_types(self): # Check if we have all history types in our test setup response = self.api_session.get(self.endpoint_url) data = response.json() types = [item['type'] for item in data] self.assertEqual(set(['versioning', 'workflow']), set(types)) def test_get_datastructure(self): response = self.api_session.get(self.endpoint_url) data = response.json() actor_keys = ['@id', 'id', 'fullname', 'username'] main_keys = [ 'action', 'actor', 'comments', 'time', 'transition_title', 'type', ] history_keys = main_keys + ['@id', 'may_revert', 'version'] workflow_keys = main_keys + [ 'review_state', 'state_title', ] for item in data: # Make sure we'll add tests when new history types are added. self.assertIn(item['type'], ['versioning', 'workflow']) if item['type'] == 'versioning': self.assertEqual(set(item.keys()), set(history_keys)) else: self.assertEqual(set(item.keys()), set(workflow_keys)) self.assertEqual(set(item['actor'].keys()), set(actor_keys)) self.assertIsNotNone(item['action']) def test_revert(self): url = '{}/@history'.format(self.doc.absolute_url()) response = self.api_session.patch(url, json={'version': 0}) self.assertEqual(response.status_code, 200) # My Document is the old title self.assertEqual( response.json(), {u'message': u'My Document has been reverted to revision 0.'}) def test_time_field(self): url = '{}/@history'.format(self.doc.absolute_url()) response = self.api_session.get(url) for item in response.json(): self.assertTrue(isinstance(item['time'], basestring)) def test_get_historical_link(self): # The @id field should link to @history/version. response = self.api_session.get(self.endpoint_url) data = response.json() for item in data: if item['type'] == 'versioning': self.assertTrue(item['@id'].endswith('@history/' + str(item['version']))) else: self.assertNotIn('@id', item.keys()) def test_explicit_current(self): # Does version=current get the current version url = self.doc.absolute_url() + '/@history/current' response = self.api_session.get(url) self.assertEqual(response.json()['title'], 'Current version') def test_previous_version(self): # Does version=0 get the older version? url = self.doc.absolute_url() + '/@history/0' response = self.api_session.get(url) self.assertEqual(response.json()['title'], 'My Document') def test_no_sharing(self): url = self.doc.absolute_url() + '/@history/0' response = self.api_session.get(url) self.assertNotIn('sharing', response.json())
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)
class TestSearchTextInBlocks(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, test=self) self.api_session.headers.update({"Accept": "application/json"}) self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD) fti = queryUtility(IDexterityFTI, name="Document") behavior_list = [a for a in fti.behaviors] behavior_list.append("volto.blocks") fti.behaviors = tuple(behavior_list) self.doc = createContentInContainer(self.portal, "Document", id="doc", title="A document") transaction.commit() def tearDown(self): self.api_session.close() def test_search_text(self): response = self.api_session.patch( "/doc", json={ "blocks_layout": { "items": ["uuid1", "uuid2"] }, "blocks": { "uuid1": { "@type": "text", "text": { "blocks": [{ "data": {}, "depth": 0, "entityRanges": [], "inlineStyleRanges": [], "key": "acv4f", "text": "Plone " "text " "for " "block ", "type": "unstyled", }], "entityMap": {}, }, }, "uuid2": { "@type": "text", "text": { "blocks": [{ "data": {}, "depth": 0, "entityRanges": [], "inlineStyleRanges": [], "key": "1m9qt", "text": "Volto " "text " "for " "block ", "type": "unstyled", }], "entityMap": {}, }, }, }, }, ) self.assertEqual(response.status_code, 204) query = {"SearchableText": "Volto", "metadata_fields": "Title"} response = self.api_session.get("/@search", params=query) json_response = response.json() self.assertEqual(json_response["items_total"], 1) self.assertEqual(json_response["items"][0]["Title"], "A document") query = { "SearchableText": "Plone", "metadata_fields": "Title", "portal_type": "Document", } response = self.api_session.get("/@search", params=query) json_response = response.json() self.assertEqual(json_response["items_total"], 1) self.assertEqual(json_response["items"][0]["Title"], "A document") def test_register_block_searchabletext(self): @implementer(IBlockSearchableText) @adapter(IBlocks, IBrowserRequest) class TestSearchableTextAdapter: def __init__(self, context, request): self.context = context self.request = request def __call__(self, value): return "discovered: %s" % value["text"] provideAdapter( TestSearchableTextAdapter, (IDexterityItem, IBrowserRequest), name="test_block", ) blocks = { "uuid1": { "@type": "text", "text": { "blocks": [{ "data": {}, "depth": 0, "entityRanges": [], "inlineStyleRanges": [], "key": "acv4f", "text": "Plone " "text " "for " "block ", "type": "unstyled", }], "entityMap": {}, }, }, "uuid3": { "@type": "test_block", "text": "sample text" }, } self.doc.blocks = blocks self.doc.blocks_layout = {"items": ["uuid1", "uuid3"]} from plone.indexer.interfaces import IIndexableObject from zope.component import queryMultiAdapter wrapper = queryMultiAdapter((self.doc, self.portal.portal_catalog), IIndexableObject) assert "discovered: sample text" in wrapper.SearchableText def test_index_searchableText_value(self): response = self.api_session.patch( "/doc", json={ "blocks_layout": { "items": ["uuid1", "uuid2"] }, "blocks": { "uuid1": { "@type": "text", "text": { "blocks": [{ "data": {}, "depth": 0, "entityRanges": [], "inlineStyleRanges": [], "key": "acv4f", "text": "Plone " "text " "for " "block ", "type": "unstyled", }], "entityMap": {}, }, }, "uuid2": { "@type": "custom_type", "searchableText": "custom text foo", }, }, }, ) self.assertEqual(response.status_code, 204) query = { "SearchableText": "Volto", "metadata_fields": "Title", "portal_type": "Document", } response = self.api_session.get("/@search", params=query) json_response = response.json() self.assertEqual(json_response["items_total"], 0) query = { "SearchableText": "Plone", "metadata_fields": "Title", "portal_type": "Document", } response = self.api_session.get("/@search", params=query) json_response = response.json() self.assertEqual(json_response["items_total"], 1) self.assertEqual(json_response["items"][0]["Title"], "A document") query = { "SearchableText": "custom", "metadata_fields": "Title", "portal_type": "Document", } response = self.api_session.get("/@search", params=query) json_response = response.json() self.assertEqual(json_response["items_total"], 1) self.assertEqual(json_response["items"][0]["Title"], "A document") def test_search_slate_text(self): """test_search_text.""" self.doc.blocks = { "38541872-06c2-41c9-8709-37107e597b18": { "@type": "slate", "plaintext": "Under a new climatic regime, therefore", "value": [], }, "4fcfeb9b-f73e-427c-9e06-2e4d53b06865": { "@type": "slate", "searchableText": "EEA Climate Change data centre", "value": [], }, } self.doc.blocks_layout = { "items": [ "38541872-06c2-41c9-8709-37107e597b18", "4fcfeb9b-f73e-427c-9e06-2e4d53b06865", ] } self.portal.portal_catalog.indexObject(self.doc) query = {"SearchableText": "climatic"} results = self.portal.portal_catalog.searchResults(**query) self.assertEqual(len(results), 1) brain = results[0] self.assertEqual(brain.Title, "A document") query = {"SearchableText": "EEA"} results = self.portal.portal_catalog.searchResults(**query) self.assertEqual(len(results), 1) brain = results[0] self.assertEqual(brain.Title, "A document") def test_search_table_text(self): """Test text in tables is indexed""" self.doc.blocks = { "uuid1": { "@type": "table", "table": { "rows": [ { "cells": [ { "key": "3dli7", "type": "header", "value": { "blocks": [{ "key": "1kh23", "text": "First header", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": {}, }], "entityMap": {}, }, }, { "key": "3dli8", "type": "header", "value": { "blocks": [{ "key": "1kh23", "text": "Second header", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": {}, }], "entityMap": {}, }, }, ], }, { "cells": [ { "key": "3dli9", "type": "data", "value": { "blocks": [{ "key": "1kh23", "text": "Data foo", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": {}, }], "entityMap": {}, }, }, { "key": "3dl29", "type": "data", "value": { "blocks": [{ "key": "1kh23", "text": "Data bar", "type": "unstyled", "depth": 0, "inlineStyleRanges": [], "entityRanges": [], "data": {}, }], "entityMap": {}, }, }, ], }, ], }, } } self.doc.blocks_layout = {"items": ["uuid1"]} self.portal.portal_catalog.indexObject(self.doc) query = {"SearchableText": "foo"} results = self.portal.portal_catalog.searchResults(**query) self.assertEqual(len(results), 1) brain = results[0] self.assertEqual(brain.Title, "A document") query = {"SearchableText": "first"} results = self.portal.portal_catalog.searchResults(**query) self.assertEqual(len(results), 1) brain = results[0] self.assertEqual(brain.Title, "A document")
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)
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 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)
class TestContentBlocks(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) fti = queryUtility(IDexterityFTI, name="Document") behavior_list = [a for a in fti.behaviors] behavior_list.append("plone.restapi.behaviors.IBlocks") behavior_list.append("plone.leadimage") fti.behaviors = tuple(behavior_list) self.doc = createContentInContainer(self.portal, u"Document", id=u"doc", title=u"A document") transaction.commit() def tearDown(self): self.api_session.close() def test_patch_blocks_list(self): response = self.api_session.patch( "/doc", json={ "blocks": { "uuid1": { "@type": "title" }, "uuid2": { "@type": "description" }, } }, ) self.assertEqual(response.status_code, 204) response = self.api_session.get("/doc") response = response.json() self.assertEqual( response["blocks"], { "uuid1": { "@type": "title" }, "uuid2": { "@type": "description" } }, ) def test_patch_blocks_layout(self): response = self.api_session.patch( "/doc", json={"blocks_layout": { "items": ["#uuid1", "#uuid2"] }}) self.assertEqual(response.status_code, 204) response = self.api_session.get("/doc") response = response.json() self.assertEqual(response["blocks_layout"], {"items": ["#uuid1", "#uuid2"]}) def test_get_blocks_layout_schema(self): response = self.api_session.get("/@types/Document") self.assertEqual(response.status_code, 200) response = response.json()
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"]))
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 TestHistoryEndpoint(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", id="doc_with_history", title="My Document") self.doc = self.portal.doc_with_history self.doc.setTitle("Current version") api.content.transition(self.doc, "publish") self.endpoint_url = "{}/@history".format(self.doc.absolute_url()) transaction.commit() def tearDown(self): self.api_session.close() def test_get_types(self): # Check if we have all history types in our test setup response = self.api_session.get(self.endpoint_url) data = response.json() types = [item["type"] for item in data] self.assertEqual(set(["versioning", "workflow"]), set(types)) def test_get_datastructure(self): response = self.api_session.get(self.endpoint_url) data = response.json() actor_keys = ["@id", "id", "fullname", "username"] main_keys = [ "action", "actor", "comments", "time", "transition_title", "type" ] history_keys = main_keys + ["@id", "may_revert", "version"] workflow_keys = main_keys + ["review_state", "state_title"] for item in data: # Make sure we'll add tests when new history types are added. self.assertIn(item["type"], ["versioning", "workflow"]) if item["type"] == "versioning": self.assertEqual(set(item), set(history_keys)) else: self.assertEqual(set(item), set(workflow_keys)) self.assertEqual(set(item["actor"]), set(actor_keys)) self.assertIsNotNone(item["action"]) def test_revert(self): url = "{}/@history".format(self.doc.absolute_url()) response = self.api_session.patch(url, json={"version": 0}) self.assertEqual(response.status_code, 200) # My Document is the old title self.assertEqual( response.json(), {u"message": u"My Document has been reverted to revision 0."}, ) def test_time_field(self): url = "{}/@history".format(self.doc.absolute_url()) response = self.api_session.get(url) for item in response.json(): self.assertTrue(isinstance(item["time"], six.string_types)) def test_get_historical_link(self): # The @id field should link to @history/version. response = self.api_session.get(self.endpoint_url) data = response.json() for item in data: if item["type"] == "versioning": self.assertTrue(item["@id"].endswith("@history/" + str(item["version"]))) else: self.assertNotIn("@id", list(item)) def test_explicit_current(self): # Does version=current get the current version url = self.doc.absolute_url() + "/@history/current" response = self.api_session.get(url) self.assertEqual(response.json()["title"], "Current version") def test_previous_version(self): # Does version=0 get the older version? url = self.doc.absolute_url() + "/@history/0" response = self.api_session.get(url) self.assertEqual(response.json()["title"], "My Document") def test_no_sharing(self): url = self.doc.absolute_url() + "/@history/0" response = self.api_session.get(url) self.assertNotIn("sharing", response.json())
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") ], )
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)