diff --git a/report_py3o_signer/README.rst b/report_py3o_signer/README.rst new file mode 100644 index 0000000000..b3adbb18f8 --- /dev/null +++ b/report_py3o_signer/README.rst @@ -0,0 +1,84 @@ +======================= +Py3o PDF reports signer +======================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:e2bb49beb72cd72d59f2da5666c462a06d7c24c1cb22e25ca6647c18b8ea4f6b + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github + :target: https://github.com/OCA/reporting-engine/tree/16.0/report_py3o_signer + :alt: OCA/reporting-engine +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/reporting-engine-16-0/reporting-engine-16-0-report_py3o_signer + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the functionality of report module to sign +PDFs using a PKCS#12 certificate. + +This module extends the functionality the pdf report signing to py3o reports. +Please refer to the documentation of `report_py3o` for how to configure such reports. +Please refer to the documentation of `report_qweb_signer` for how to configure certificates for signing; such certificate will be used for any py3o report if its output is configured to be in pdf. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Lambdao + +Contributors +~~~~~~~~~~~~ + +* `Lambdao `_: + + * Fanny He + * Nans Lefebvre + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/reporting-engine `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/report_py3o_signer/__init__.py b/report_py3o_signer/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/report_py3o_signer/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/report_py3o_signer/__manifest__.py b/report_py3o_signer/__manifest__.py new file mode 100644 index 0000000000..e75a68c47d --- /dev/null +++ b/report_py3o_signer/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2023 fah-mili/Lambdao +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Py3o PDF reports signer", + "summary": "Sign Py3o PDFs using a PKCS#12 certificate", + "version": "16.0.1.0.0", + "category": "Reporting", + "license": "AGPL-3", + "author": "Lambdao, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/reporting-engine", + "depends": ["report_py3o", "report_qweb_signer"], + "data": [], + "installable": True, +} diff --git a/report_py3o_signer/models/__init__.py b/report_py3o_signer/models/__init__.py new file mode 100644 index 0000000000..a248cf2162 --- /dev/null +++ b/report_py3o_signer/models/__init__.py @@ -0,0 +1 @@ +from . import ir_actions_report diff --git a/report_py3o_signer/models/ir_actions_report.py b/report_py3o_signer/models/ir_actions_report.py new file mode 100644 index 0000000000..e28da1b098 --- /dev/null +++ b/report_py3o_signer/models/ir_actions_report.py @@ -0,0 +1,32 @@ +# Copyright 2023 fah-mili/Lambdao +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo import models + +_logger = logging.getLogger(__name__) + + +class IrActionsReport(models.Model): + + _inherit = "ir.actions.report" + + def _is_report_type_signable(self): + res = super()._is_report_type_signable() + return res or (self.report_type == "py3o" and self.py3o_filetype == "pdf") + + def _render_py3o(self, report_ref, res_ids, data=None): + report = self._get_report(report_ref) + certificate = self._certificate_get(report, res_ids) + signed_content, ext = self._read_attached_signed_content( + report, res_ids, certificate + ) + if signed_content: + return signed_content, "pdf" + content, ext = super(IrActionsReport, self)._render_py3o( + report_ref, res_ids, data + ) + if certificate: + content = self._sign_pdf_and_attach(res_ids, certificate, content) + return content, ext diff --git a/report_py3o_signer/readme/CONTRIBUTORS.rst b/report_py3o_signer/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..da792f2657 --- /dev/null +++ b/report_py3o_signer/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Lambdao `_: + + * Fanny He + * Nans Lefebvre diff --git a/report_py3o_signer/readme/DESCRIPTION.rst b/report_py3o_signer/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..5c2bd62891 --- /dev/null +++ b/report_py3o_signer/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module extends the functionality of report module to sign +PDFs using a PKCS#12 certificate. + +This module extends the functionality the pdf report signing to py3o reports. +Please refer to the documentation of `report_py3o` for how to configure such reports. +Please refer to the documentation of `report_qweb_signer` for how to configure certificates for signing; such certificate will be used for any py3o report if its output is configured to be in pdf. diff --git a/report_py3o_signer/static/description/index.html b/report_py3o_signer/static/description/index.html new file mode 100644 index 0000000000..9d6abca727 --- /dev/null +++ b/report_py3o_signer/static/description/index.html @@ -0,0 +1,434 @@ + + + + + +Py3o PDF reports signer + + + +
+

Py3o PDF reports signer

+ + +

Beta License: AGPL-3 OCA/reporting-engine Translate me on Weblate Try me on Runboat

+

This module extends the functionality of report module to sign +PDFs using a PKCS#12 certificate.

+

This module extends the functionality the pdf report signing to py3o reports. +Please refer to the documentation of report_py3o for how to configure such reports. +Please refer to the documentation of report_qweb_signer for how to configure certificates for signing; such certificate will be used for any py3o report if its output is configured to be in pdf.

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Lambdao
  • +
+
+
+

Contributors

+
    +
  • Lambdao:

    +
    +
      +
    • Fanny He
    • +
    • Nans Lefebvre
    • +
    +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/reporting-engine project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/report_py3o_signer/tests/__init__.py b/report_py3o_signer/tests/__init__.py new file mode 100644 index 0000000000..89c3966fb3 --- /dev/null +++ b/report_py3o_signer/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_report_py3o_signer diff --git a/report_py3o_signer/tests/test_report_py3o_signer.py b/report_py3o_signer/tests/test_report_py3o_signer.py new file mode 100644 index 0000000000..7599c73b99 --- /dev/null +++ b/report_py3o_signer/tests/test_report_py3o_signer.py @@ -0,0 +1,42 @@ +# Copyright 2023 fah-mili/Lambdao +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import TransactionCase + + +class TestReportPy3oSignerCase(TransactionCase): + def setUp(self): + super(TestReportPy3oSignerCase, self).setUp() + self.user = self.env.ref("base.user_admin") + self.report = self.env.ref("report_py3o.res_users_report_py3o").with_context( + force_report_rendering=True + ) + self.report.py3o_filetype = "pdf" + self.report.attachment_use = True + self.report.attachment = "'test_' + (object.name or '') + '.pdf'" + + user_model = self.env["ir.model"].search([("model", "=", "res.users")]) + self.certificate = self.env.ref("report_qweb_signer.demo_certificate_test") + self.certificate.model_id = user_model + self.certificate.domain = "[]" + + +class TestReportPy3oSigner(TestReportPy3oSignerCase): + def test_report_py3o_signer(self): + report_ref = self.report.report_name + user_ids = self.user.ids + self.report._render_py3o("py3o_user_info", user_ids) + # we should find the saved signed content + signed_content = self.report._attach_signed_read(user_ids, self.certificate) + self.assertTrue(signed_content) + + # Reprint again for taking the PDF from attachment + IrAttachment = self.env["ir.attachment"] + domain = [ + ("res_id", "=", self.user.id), + ("res_model", "=", self.user._name), + ] + num_attachments = IrAttachment.search_count(domain) + self.report._render_py3o(report_ref, user_ids) + num_attachments_after = IrAttachment.search_count(domain) + self.assertEqual(num_attachments, num_attachments_after) diff --git a/report_qweb_signer/models/ir_actions_report.py b/report_qweb_signer/models/ir_actions_report.py index 3d1bb03aee..3669e4b724 100644 --- a/report_qweb_signer/models/ir_actions_report.py +++ b/report_qweb_signer/models/ir_actions_report.py @@ -211,9 +211,8 @@ def pdf_sign(self, pdf, certificate): self._signer_endesive(params, p12, pdf, pdfsigned, passwd) return pdfsigned - def _render_qweb_pdf(self, report_ref, res_ids=None, data=None): - report = self._get_report(report_ref) - certificate = self._certificate_get(report, res_ids) + def _read_attached_signed_content(self, report, res_ids, certificate): + signed_content, ext = False, False if certificate and certificate.attachment: signed_content = self._attach_signed_read(res_ids, certificate) if signed_content: @@ -223,29 +222,43 @@ def _render_qweb_pdf(self, report_ref, res_ids=None, data=None): res_ids, ) return signed_content, "pdf" + return signed_content, ext + + def _render_qweb_pdf(self, report_ref, res_ids=None, data=None): + report = self._get_report(report_ref) + certificate = self._certificate_get(report, res_ids) + signed_content, ext = self._read_attached_signed_content( + report, res_ids, certificate + ) + if signed_content: + return signed_content, "pdf" content, ext = super()._render_qweb_pdf(report_ref, res_ids=res_ids, data=data) if certificate: - # Creating temporary origin PDF - pdf_fd, pdf = tempfile.mkstemp(suffix=".pdf", prefix="report.tmp.") - with closing(os.fdopen(pdf_fd, "wb")) as pf: - pf.write(content) - _logger.debug( - "Signing PDF document '%s' for IDs %s with certificate '%s'", - self.report_name, - res_ids, - certificate.name, - ) - signed = self.pdf_sign(pdf, certificate) - # Read signed PDF - if os.path.exists(signed): - with open(signed, "rb") as pf: - content = pf.read() - # Manual cleanup of the temporary files - for fname in (pdf, signed): - try: - os.unlink(fname) - except OSError: - _logger.error("Error when trying to remove file %s", fname) - if certificate.attachment: - self._attach_signed_write(res_ids, certificate, content) + content = self._sign_pdf_and_attach(res_ids, certificate, content) return content, ext + + def _sign_pdf_and_attach(self, res_ids, certificate, content): + # Creating temporary origin PDF + pdf_fd, pdf = tempfile.mkstemp(suffix=".pdf", prefix="report.tmp.") + with closing(os.fdopen(pdf_fd, "wb")) as pf: + pf.write(content) + _logger.debug( + "Signing PDF document '%s' for IDs %s with certificate '%s'", + self.report_name, + res_ids, + certificate.name, + ) + signed = self.pdf_sign(pdf, certificate) + # Read signed PDF + if os.path.exists(signed): + with open(signed, "rb") as pf: + content = pf.read() + # Manual cleanup of the temporary files + for fname in (pdf, signed): + try: + os.unlink(fname) + except OSError: + _logger.error("Error when trying to remove file %s", fname) + if certificate.attachment: + self._attach_signed_write(res_ids, certificate, content) + return content diff --git a/setup/report_py3o_signer/odoo/addons/report_py3o_signer b/setup/report_py3o_signer/odoo/addons/report_py3o_signer new file mode 120000 index 0000000000..9c12aee556 --- /dev/null +++ b/setup/report_py3o_signer/odoo/addons/report_py3o_signer @@ -0,0 +1 @@ +../../../../report_py3o_signer \ No newline at end of file diff --git a/setup/report_py3o_signer/setup.py b/setup/report_py3o_signer/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/report_py3o_signer/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)