# -*- coding: utf-8 -*- import io import zipfile import base64 from odoo import http, fields from odoo.addons.base.tests.common import HttpCaseWithUserDemo from odoo.tools import mute_logger class TestDocumentsRoutes(HttpCaseWithUserDemo): def setUp(self): super().setUp() self.folder_a, self.folder_b = self.env['documents.folder'].create([ {'name': 'folder A'}, {'name': 'folder B'}, ]) self.document_txt = self.env['documents.document'].create({ 'raw': b'TEST', 'name': 'file.txt', 'mimetype': 'text/plain', 'folder_id': self.folder_a.id, }) self.share_folder_b = self.env['documents.share'].create( { 'folder_id': self.folder_b.id, 'type': 'domain', 'action': 'downloadupload', } ) def test_documents_content(self): self.authenticate('admin', 'admin') response = self.url_open('/documents/content/%s' % self.document_txt.id) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'TEST') def test_documents_zip(self): self.authenticate('admin', 'admin') response = self.url_open('/document/zip', data={ 'file_ids': [self.document_txt.id], 'zip_name': 'testZip.zip', 'csrf_token': http.Request.csrf_token(self), }) self.assertEqual(response.status_code, 200) with io.BytesIO(response.content) as buffer, zipfile.ZipFile(buffer) as zipfile_obj: self.assertEqual(zipfile_obj.read(self.document_txt.name), b'TEST') def test_documents_zip_authentification(self): self.authenticate('admin', 'admin') vals = { "name": "private_folder", "parent_folder_id": False, "company_id": False, "facet_ids": [], "group_ids": [self.env.ref('base.group_system').id], "user_specific_write": True, "read_group_ids": [self.env.ref('base.group_system').id], "user_specific": True, "description": False } workspace_private = self.env['documents.folder'].create(vals) vals = { "name": "public_folder", } workspace_public = self.env['documents.folder'].create(vals) rawpdf_base64 = 'JVBERi0xLjYNJeLjz9MNCjI0IDAgb2JqDTw8L0ZpbHRlci9GbGF0ZURlY29kZS9GaXJzdCA0L0xlbmd0aCAyMTYvTiAxL1R5cGUvT2JqU3RtPj5zdHJlYW0NCmjePI9RS8MwFIX/yn1bi9jepCQ6GYNpFBTEMsW97CVLbjWYNpImmz/fVsXXcw/f/c4SEFarepPTe4iFok8dU09DgtDBQx6TMwT74vaLTE7uSPDUdXM0Xe/73r1FnVwYYEtHR6d9WdY3kX4ipRMV6oojSmxQMoGyac5RLBAXf63p38aGA7XPorLewyvFcYaJile8rB+D/YcwiRdMMGScszO8/IW0MdhsaKKYGA46gXKTr/cUQVY4We/cYMNpnLVeXPJUXHs9fECr7kAFk+eZ5Xr9LcAAfKpQrA0KZW5kc3RyZWFtDWVuZG9iag0yNSAwIG9iag08PC9GaWx0ZXIvRmxhdGVEZWNvZGUvRmlyc3QgNC9MZW5ndGggNDkvTiAxL1R5cGUvT2JqU3RtPj5zdHJlYW0NCmjeslAwULCx0XfOL80rUTDU985MKY42NAIKBsXqh1QWpOoHJKanFtvZAQQYAN/6C60NCmVuZHN0cmVhbQ1lbmRvYmoNMjYgMCBvYmoNPDwvRmlsdGVyL0ZsYXRlRGVjb2RlL0ZpcnN0IDkvTGVuZ3RoIDQyL04gMi9UeXBlL09ialN0bT4+c3RyZWFtDQpo3jJTMFAwVzC0ULCx0fcrzS2OBnENFIJi7eyAIsH6LnZ2AAEGAI2FCDcNCmVuZHN0cmVhbQ1lbmRvYmoNMjcgMCBvYmoNPDwvRmlsdGVyL0ZsYXRlRGVjb2RlL0ZpcnN0IDUvTGVuZ3RoIDEyMC9OIDEvVHlwZS9PYmpTdG0+PnN0cmVhbQ0KaN4yNFIwULCx0XfOzytJzSspVjAyBgoE6TsX5Rc45VdEGwB5ZoZGCuaWRrH6vqkpmYkYogGJRUCdChZgfUGpxfmlRcmpxUAzA4ryk4NTS6L1A1zc9ENSK0pi7ez0g/JLEktSFQz0QyoLUoF601Pt7AACDADYoCeWDQplbmRzdHJlYW0NZW5kb2JqDTIgMCBvYmoNPDwvTGVuZ3RoIDM1MjUvU3VidHlwZS9YTUwvVHlwZS9NZXRhZGF0YT4+c3RyZWFtDQo8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjQtYzAwNSA3OC4xNDczMjYsIDIwMTIvMDgvMjMtMTM6MDM6MDMgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnBkZj0iaHR0cDovL25zLmFkb2JlLmNvbS9wZGYvMS4zLyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPHBkZjpQcm9kdWNlcj5BY3JvYmF0IERpc3RpbGxlciA2LjAgKFdpbmRvd3MpPC9wZGY6UHJvZHVjZXI+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDA2LTAzLTA2VDE1OjA2OjMzLTA1OjAwPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZVBTNS5kbGwgVmVyc2lvbiA1LjIuMjwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxNi0wNy0xNVQxMDoxMjoyMSswODowMDwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6TWV0YWRhdGFEYXRlPjIwMTYtMDctMTVUMTA6MTI6MjErMDg6MDA8L3htcDpNZXRhZGF0YURhdGU+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPnV1aWQ6ZmYzZGNmZDEtMjNmYS00NzZmLTgzOWEtM2U1Y2FlMmRhMmViPC94bXBNTTpEb2N1bWVudElEPgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD51dWlkOjM1OTM1MGIzLWFmNDAtNGQ4YS05ZDZjLTAzMTg2YjRmZmIzNjwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPGRjOmZvcm1hdD5hcHBsaWNhdGlvbi9wZGY8L2RjOmZvcm1hdD4KICAgICAgICAgPGRjOnRpdGxlPgogICAgICAgICAgICA8cmRmOkFsdD4KICAgICAgICAgICAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij5CbGFuayBQREYgRG9jdW1lbnQ8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6QWx0PgogICAgICAgICA8L2RjOnRpdGxlPgogICAgICAgICA8ZGM6Y3JlYXRvcj4KICAgICAgICAgICAgPHJkZjpTZXE+CiAgICAgICAgICAgICAgIDxyZGY6bGk+RGVwYXJ0bWVudCBvZiBKdXN0aWNlIChFeGVjdXRpdmUgT2ZmaWNlIG9mIEltbWlncmF0aW9uIFJldmlldyk8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6U2VxPgogICAgICAgICA8L2RjOmNyZWF0b3I+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgog' + 682*'ICAg' + 'Cjw/eHBhY2tldCBlbmQ9InciPz4NCmVuZHN0cmVhbQ1lbmRvYmoNMTEgMCBvYmoNPDwvTWV0YWRhdGEgMiAwIFIvUGFnZUxhYmVscyA2IDAgUi9QYWdlcyA4IDAgUi9UeXBlL0NhdGFsb2c+Pg1lbmRvYmoNMjMgMCBvYmoNPDwvRmlsdGVyL0ZsYXRlRGVjb2RlL0xlbmd0aCAxMD4+c3RyZWFtDQpIiQIIMAAAAAABDQplbmRzdHJlYW0NZW5kb2JqDTI4IDAgb2JqDTw8L0RlY29kZVBhcm1zPDwvQ29sdW1ucyA0L1ByZWRpY3RvciAxMj4+L0ZpbHRlci9GbGF0ZURlY29kZS9JRFs8REI3Nzc1Q0NFMjI3RjZCMzBDNDQwREY0MjIxREMzOTA+PEJGQ0NDRjNGNTdGNjEzNEFCRDNDMDRBOUU0Q0ExMDZFPl0vSW5mbyA5IDAgUi9MZW5ndGggODAvUm9vdCAxMSAwIFIvU2l6ZSAyOS9UeXBlL1hSZWYvV1sxIDIgMV0+PnN0cmVhbQ0KaN5iYgACJjDByGzIwPT/73koF0wwMUiBWYxA4v9/EMHA9I/hBVCxoDOQeH8DxH2KrIMIglFwIpD1vh5IMJqBxPpArHYgwd/KABBgAP8bEC0NCmVuZHN0cmVhbQ1lbmRvYmoNc3RhcnR4cmVmDQo0NTc2DQolJUVPRg0K' rawpdf = base64.b64decode(rawpdf_base64.encode()) private_doc = self.env['documents.document'].create({ 'raw': rawpdf, 'name': 'private_file.pdf', 'mimetype': 'application/pdf', 'folder_id': workspace_private.id, }) document_group_id_manager = self.env.ref('documents.group_documents_manager') document_group_id_user = self.env.ref('documents.group_documents_user') user = self.env.user.create({'name': 'test_doc', 'login': 'test_doc', 'password': 'test_doc'}) # User with explicitely removed manager right and given user right on the document app user.groups_id -= document_group_id_manager user.groups_id += document_group_id_user self.assertNotIn(private_doc, self.env['documents.document'].with_user(user.id).search([])) # Our new user should not be able to find the private doc document_public = self.env['documents.document'].create({ 'raw': rawpdf, 'name': 'public.pdf', 'mimetype': 'application/pdf', 'folder_id': workspace_public.id, }) self.assertIn(document_public, self.env['documents.document'].with_user(user.id).search([])) # but he should see the public document self.authenticate('test_doc', 'test_doc') with mute_logger('odoo.http'): response = self.url_open('/document/zip', data={ 'file_ids': f'{private_doc.id},{document_public.id}', 'zip_name': 'testZip.zip', 'csrf_token': http.Request.csrf_token(self), }) self.assertNotEqual(response.status_code, 200) with mute_logger('odoo.http'): response = self.url_open('/document/zip', data={ 'file_ids': f'{document_public.id},{private_doc.id}', 'zip_name': 'testZip.zip', 'csrf_token': http.Request.csrf_token(self), }) self.assertNotEqual(response.status_code, 200) def test_documents_from_web(self): self.authenticate('admin', 'admin') raw_gif = b"R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=" document_gif = self.env['documents.document'].create({ 'raw': raw_gif, 'name': 'file.gif', 'mimetype': 'image/gif', 'folder_id': self.folder_a.id, }) response = self.url_open('/web/image/%s?model=documents.document' % document_gif.id) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, raw_gif) def test_documents_share_expired_link(self): self.authenticate('admin', 'admin') # Test on available link tomorrow = fields.Date.from_string(fields.Date.add(fields.Date.today(), days=1)) vals = { 'document_ids': [(6, 0, [self.document_txt.id])], 'folder_id': self.folder_a.id, 'date_deadline': tomorrow, 'type': 'ids', } self.result_share_documents_act = self.env['documents.share'].create(vals) response = self.url_open(self.result_share_documents_act.full_url) self.assertEqual(response.status_code, 200) self.assertIn(b'is sharing content with you', response.content, "Failed route test on available link") # Test on expired link vals = { 'document_ids': [(6, 0, [self.document_txt.id])], 'folder_id': self.folder_a.id, 'date_deadline': '2001-11-05', 'type': 'ids', } self.result_share_documents_act = self.env['documents.share'].create(vals) response = self.url_open(self.result_share_documents_act.full_url) self.assertEqual(response.status_code, 200) self.assertTrue(b'Sorry, this link is no longer valid.' in response.content, "Failed route test on expired link") def test_download_all_documents_with_url(self): document_url = self.env['documents.document'].create({ 'name': 'file.txt', 'type': 'url', 'folder_id': self.folder_a.id, }) vals = { 'document_ids': [(6, 0, [self.document_txt.id, document_url.id])], 'folder_id': self.folder_a.id, 'type': 'ids', } share = self.env['documents.share'].create(vals) response = self.url_open(f"/document/download/all/{share.id}/{share.access_token}") self.assertEqual(response.status_code, 200) def test_upload_attachment_public(self): """Check the upload and notifications for public users.""" files = [('files', ('test.txt', b'test', 'image/svg+xml'))] # Authenticate to set `self.session` for `http.Request.csrf_token(self)`. Use `None` to simulate public auth. + self.authenticate(None, None) self.authenticate(None, None) response = self.url_open( f'/document/upload/{self.share_folder_b.id}/{self.share_folder_b.access_token}', files=files, data={'csrf_token': http.Request.csrf_token(self)} ) document = self.env['documents.document'].search([('folder_id', '=', self.folder_b.id)]) self.assertEqual(response.status_code, 200) self.assertEqual(len(document), 1) self.assertEqual(document.name, 'test.txt') self.assertEqual(document.raw, b'test') self.assertEqual(document.mimetype, 'text/plain') public_user = self.env.ref('base.public_user') file_uploaded_note = document.message_ids[0] self.assertEqual(file_uploaded_note.author_id, public_user.partner_id) self.assertIn('File uploaded by: Public user
', file_uploaded_note.body) self.assertIn(f'Link created by: {self.share_folder_b.create_uid.name}', file_uploaded_note.body) def test_upload_attachment_user(self): """Check that logged user's name is used in notification.""" files = [('files', ('test.txt', b'test', 'text/plain'))] demo_session = self.authenticate('demo', 'demo') demo_user = self.env['res.users'].browse(demo_session.uid) self.url_open(f'/document/upload/{self.share_folder_b.id}/{self.share_folder_b.access_token}', files=files, data={'csrf_token': http.Request.csrf_token(self)}) document = self.env['documents.document'].search([('folder_id', '=', self.folder_b.id)]) file_uploaded_note = document.message_ids[0] self.assertEqual(file_uploaded_note.author_id, demo_user.partner_id) self.assertIn('File uploaded by: Marc Demo
', file_uploaded_note.body) self.assertIn(f'Link created by: {self.share_folder_b.create_uid.name}', file_uploaded_note.body)