commit
d51766e1f3
|
@ -13,7 +13,7 @@
|
|||
"license": "AGPL-3",
|
||||
"installable": True,
|
||||
"depends": ["web_editor"],
|
||||
"external_dependencies": {"bin": ["/usr/bin/java"]},
|
||||
"external_dependencies": {"python": ["endesive", "cryptography"]},
|
||||
"data": [
|
||||
"data/defaults.xml",
|
||||
"security/ir.model.access.csv",
|
||||
|
|
|
@ -10,6 +10,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
<field name="name">Test OCA certificate</field>
|
||||
<field name="path">test.p12</field>
|
||||
<field name="password_file">test.passwd</field>
|
||||
<field name="signing_method">java</field>
|
||||
<field name="model_id" ref="base.model_res_partner" />
|
||||
<field name="allow_only_one" eval="True" />
|
||||
<field
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
@ -10,6 +11,10 @@ import tempfile
|
|||
import time
|
||||
from contextlib import closing
|
||||
|
||||
from cryptography.hazmat import backends
|
||||
from cryptography.hazmat.primitives.serialization import pkcs12
|
||||
from endesive import pdf
|
||||
|
||||
from odoo import _, models
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
@ -120,28 +125,67 @@ class IrActionsReport(models.Model):
|
|||
jar = "{}/../static/jar/jPdfSign.jar".format(me)
|
||||
return "{} {} {} {}".format(java_bin, java_param, jar, opts)
|
||||
|
||||
def _get_endesive_params(self, certificate):
|
||||
date = datetime.datetime.utcnow() - datetime.timedelta(hours=12)
|
||||
date = date.strftime("D:%Y%m%d%H%M%S+00'00'")
|
||||
return {
|
||||
"sigflags": 3,
|
||||
"sigpage": 0,
|
||||
"sigbutton": False,
|
||||
"contact": certificate.endesive_certificate_mail,
|
||||
"location": certificate.endesive_certificate_location,
|
||||
"signingdate": date.encode(),
|
||||
"reason": certificate.endesive_certificate_reason,
|
||||
"signature": "",
|
||||
"signaturebox": (0, 0, 0, 0),
|
||||
"text": {"fontsize": 0},
|
||||
}
|
||||
|
||||
def _signer_endesive(self, params, p12filepath, pdfpath, pdfsigned, passwd):
|
||||
stringpassword = ""
|
||||
with open(passwd, "r") as pw:
|
||||
for line in pw:
|
||||
stringpassword += line.rstrip()
|
||||
password = stringpassword.encode("utf-8")
|
||||
with open(p12filepath, "rb") as fp:
|
||||
p12 = pkcs12.load_key_and_certificates(
|
||||
fp.read(), password, backends.default_backend()
|
||||
)
|
||||
with open(pdfpath, "rb") as fh:
|
||||
datau = fh.read()
|
||||
datas = pdf.cms.sign(datau, params, p12[0], p12[1], p12[2], "sha256")
|
||||
with open(pdfsigned, "wb") as res:
|
||||
res.write(datau)
|
||||
res.write(datas)
|
||||
return pdfsigned
|
||||
|
||||
def pdf_sign(self, pdf, certificate):
|
||||
pdfsigned = pdf + ".signed.pdf"
|
||||
p12 = _normalize_filepath(certificate.path)
|
||||
passwd = _normalize_filepath(certificate.password_file)
|
||||
if not (p12 and passwd):
|
||||
method_used = certificate.signing_method
|
||||
if not (p12 or passwd):
|
||||
raise UserError(
|
||||
_("Signing report (PDF): " "Certificate or password file not found")
|
||||
)
|
||||
signer_opts = '"{}" "{}" "{}" "{}"'.format(p12, pdf, pdfsigned, passwd)
|
||||
signer = self._signer_bin(signer_opts)
|
||||
process = subprocess.Popen(
|
||||
signer, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
|
||||
)
|
||||
out, err = process.communicate()
|
||||
if process.returncode:
|
||||
raise UserError(
|
||||
_(
|
||||
"Signing report (PDF): jPdfSign failed (error code: %s). "
|
||||
"Message: %s. Output: %s"
|
||||
)
|
||||
% (process.returncode, err, out)
|
||||
if method_used == "java":
|
||||
signer_opts = '"{}" "{}" "{}" "{}"'.format(p12, pdf, pdfsigned, passwd)
|
||||
signer = self._signer_bin(signer_opts)
|
||||
process = subprocess.Popen(
|
||||
signer, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
|
||||
)
|
||||
out, err = process.communicate()
|
||||
if process.returncode:
|
||||
raise UserError(
|
||||
_(
|
||||
"Signing report (PDF): jPdfSign failed (error code: %s). "
|
||||
"Message: %s. Output: %s"
|
||||
)
|
||||
% (process.returncode, err, out)
|
||||
)
|
||||
elif method_used == "endesive":
|
||||
params = self._get_endesive_params(certificate)
|
||||
self._signer_endesive(params, p12, pdf, pdfsigned, passwd)
|
||||
return pdfsigned
|
||||
|
||||
def render_qweb_pdf(self, res_ids=None, data=None):
|
||||
|
|
|
@ -52,3 +52,20 @@ class ReportCertificate(models.Model):
|
|||
required=True,
|
||||
default=_default_company,
|
||||
)
|
||||
signing_method = fields.Selection(
|
||||
selection=[("java", "Java"), ("endesive", "Endesive")],
|
||||
default="java",
|
||||
string="Signing Method",
|
||||
required=True,
|
||||
)
|
||||
endesive_certificate_mail = fields.Char(
|
||||
string="Signature e-mail",
|
||||
help="E-mail address to include in PDF digital signature.",
|
||||
)
|
||||
endesive_certificate_location = fields.Char(
|
||||
string="Signature location",
|
||||
help="Location to include in digital signature (typically, a city name). ",
|
||||
)
|
||||
endesive_certificate_reason = fields.Char(
|
||||
string="Signature reason", help="Reason text to include in digital signature.",
|
||||
)
|
||||
|
|
|
@ -22,6 +22,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
<field name="path" />
|
||||
<field name="password_file" />
|
||||
<field name="model_id" />
|
||||
<field name="signing_method" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="domain" />
|
||||
|
@ -33,6 +34,22 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
groups="base.group_multi_company"
|
||||
/>
|
||||
</group>
|
||||
<group
|
||||
attrs="{'invisible': [('signing_method', '!=', 'endesive')]}"
|
||||
>
|
||||
<field
|
||||
name="endesive_certificate_mail"
|
||||
attrs="{'required': [('signing_method', '=', 'endesive')]}"
|
||||
/>
|
||||
<field
|
||||
name="endesive_certificate_location"
|
||||
attrs="{'required': [('signing_method', '=', 'endesive')]}"
|
||||
/>
|
||||
<field
|
||||
name="endesive_certificate_reason"
|
||||
attrs="{'required': [('signing_method', '=', 'endesive')]}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
py3o.template
|
||||
py3o.formats
|
||||
genshi>=0.7
|
||||
cryptography
|
||||
endesive
|
||||
|
|
Loading…
Reference in New Issue