[IMP] report_qweb_signer add endesive
parent
89075b4c3e
commit
a770c10b86
|
@ -13,7 +13,7 @@
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"installable": True,
|
"installable": True,
|
||||||
"depends": ["web_editor"],
|
"depends": ["web_editor"],
|
||||||
"external_dependencies": {"bin": ["/usr/bin/java"]},
|
"external_dependencies": {"python": ["endesive", "cryptography"]},
|
||||||
"data": [
|
"data": [
|
||||||
"data/defaults.xml",
|
"data/defaults.xml",
|
||||||
"security/ir.model.access.csv",
|
"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="name">Test OCA certificate</field>
|
||||||
<field name="path">test.p12</field>
|
<field name="path">test.p12</field>
|
||||||
<field name="password_file">test.passwd</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="model_id" ref="base.model_res_partner" />
|
||||||
<field name="allow_only_one" eval="True" />
|
<field name="allow_only_one" eval="True" />
|
||||||
<field
|
<field
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -10,6 +11,10 @@ import tempfile
|
||||||
import time
|
import time
|
||||||
from contextlib import closing
|
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 import _, models
|
||||||
from odoo.exceptions import AccessError, UserError
|
from odoo.exceptions import AccessError, UserError
|
||||||
from odoo.tools.safe_eval import safe_eval
|
from odoo.tools.safe_eval import safe_eval
|
||||||
|
@ -120,14 +125,50 @@ class IrActionsReport(models.Model):
|
||||||
jar = "{}/../static/jar/jPdfSign.jar".format(me)
|
jar = "{}/../static/jar/jPdfSign.jar".format(me)
|
||||||
return "{} {} {} {}".format(java_bin, java_param, jar, opts)
|
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):
|
def pdf_sign(self, pdf, certificate):
|
||||||
pdfsigned = pdf + ".signed.pdf"
|
pdfsigned = pdf + ".signed.pdf"
|
||||||
p12 = _normalize_filepath(certificate.path)
|
p12 = _normalize_filepath(certificate.path)
|
||||||
passwd = _normalize_filepath(certificate.password_file)
|
passwd = _normalize_filepath(certificate.password_file)
|
||||||
if not (p12 and passwd):
|
method_used = certificate.signing_method
|
||||||
|
if not (p12 or passwd):
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_("Signing report (PDF): " "Certificate or password file not found")
|
_("Signing report (PDF): " "Certificate or password file not found")
|
||||||
)
|
)
|
||||||
|
if method_used == "java":
|
||||||
signer_opts = '"{}" "{}" "{}" "{}"'.format(p12, pdf, pdfsigned, passwd)
|
signer_opts = '"{}" "{}" "{}" "{}"'.format(p12, pdf, pdfsigned, passwd)
|
||||||
signer = self._signer_bin(signer_opts)
|
signer = self._signer_bin(signer_opts)
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
|
@ -142,6 +183,9 @@ class IrActionsReport(models.Model):
|
||||||
)
|
)
|
||||||
% (process.returncode, err, out)
|
% (process.returncode, err, out)
|
||||||
)
|
)
|
||||||
|
elif method_used == "endesive":
|
||||||
|
params = self._get_endesive_params(certificate)
|
||||||
|
self._signer_endesive(params, p12, pdf, pdfsigned, passwd)
|
||||||
return pdfsigned
|
return pdfsigned
|
||||||
|
|
||||||
def render_qweb_pdf(self, res_ids=None, data=None):
|
def render_qweb_pdf(self, res_ids=None, data=None):
|
||||||
|
|
|
@ -52,3 +52,20 @@ class ReportCertificate(models.Model):
|
||||||
required=True,
|
required=True,
|
||||||
default=_default_company,
|
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="path" />
|
||||||
<field name="password_file" />
|
<field name="password_file" />
|
||||||
<field name="model_id" />
|
<field name="model_id" />
|
||||||
|
<field name="signing_method" />
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="domain" />
|
<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"
|
groups="base.group_multi_company"
|
||||||
/>
|
/>
|
||||||
</group>
|
</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>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
py3o.template
|
py3o.template
|
||||||
py3o.formats
|
py3o.formats
|
||||||
genshi>=0.7
|
genshi>=0.7
|
||||||
|
cryptography
|
||||||
|
endesive
|
||||||
|
|
Loading…
Reference in New Issue