[ADD] report_qweb_signer addon
parent
25f2a553ac
commit
fe8ffc011a
|
@ -0,0 +1,116 @@
|
||||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
|
||||||
|
=======================
|
||||||
|
Qweb PDF reports signer
|
||||||
|
=======================
|
||||||
|
|
||||||
|
This module extends the functionality of report module to sign
|
||||||
|
PDFs using a PKCS#12 certificate.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
To install this module, you need to install Java JDK::
|
||||||
|
|
||||||
|
apt-get install openjdk-7-jre-headless
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
In order to start signing PDF documents you need to configure certificate(s)
|
||||||
|
to use in your company.
|
||||||
|
|
||||||
|
* Go to ``Settings > Companies > Companies > Your company``
|
||||||
|
* Go to ``Report configuration`` tab
|
||||||
|
* Click ``Edit``
|
||||||
|
* Add a new item in ``PDF report certificates`` list
|
||||||
|
* Click ``Create``
|
||||||
|
* Set name, certificate file, password file and model
|
||||||
|
* Optionally you can set a domain and filename pattern for saving as attachment
|
||||||
|
|
||||||
|
For example, if you want to sign only customer invoices in open or paid state:
|
||||||
|
|
||||||
|
* Model: ``account.invoice``
|
||||||
|
* Domain: ``[('type','=','out_invoice'), ('state', 'in', ('open', 'paid'))]``
|
||||||
|
* Save as attachment: ``(object.number or '').replace('/','_') + '.signed.pdf'``
|
||||||
|
|
||||||
|
**Note**: Linux user that executes Odoo server process must have
|
||||||
|
read access to certificate file and password file
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
User just prints PDF documents (only Qweb PDF reports supported) as usual,
|
||||||
|
but signed PDF is automatically downloaded if this document model is configured
|
||||||
|
as indicated above.
|
||||||
|
|
||||||
|
If 'Save as attachment' is configured, signed PDF is saved as attachment and
|
||||||
|
next time saved one is downloaded without signing again. This is appropiate when
|
||||||
|
signing date is important, for example, when signing customer invoices.
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||||
|
:alt: Try me on Runbot
|
||||||
|
:target: https://runbot.odoo-community.org/runbot/143/8.0
|
||||||
|
|
||||||
|
For further information, please visit:
|
||||||
|
|
||||||
|
* https://www.odoo.com/forum/help-1
|
||||||
|
|
||||||
|
|
||||||
|
Known issues / Roadmap
|
||||||
|
======================
|
||||||
|
|
||||||
|
* When signing multiple documents (if 'Allow only one document' is disable)
|
||||||
|
then 'Save as attachment' is not applied and signed result is not
|
||||||
|
saved as attachment.
|
||||||
|
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/reporting-engine/issues>`_.
|
||||||
|
In case of trouble, please check there if your issue has already been reported.
|
||||||
|
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
|
||||||
|
`here <https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_qweb_signer%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
External utilities
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* iText v1.4.8: © 2000-2006, Paulo Soares, Bruno Lowagie and others - License `MPL <http://www.mozilla.org/MPL>`_ or `LGPL2 <http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html>`_ - http://sourceforge.net/projects/itext
|
||||||
|
* jPdfSign: © 2006 Jan Peter Stotz - License `MPL <http://www.mozilla.org/MPL>`_ or `LGPL2 <http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html>`_ (inherited from iText) - http://private.sit.fraunhofer.de/~stotz/software/jpdfsign
|
||||||
|
* Modified jPdfSign: © 2015 Antonio Espinosa - License `MPL <http://www.mozilla.org/MPL>`_ or `LGPL2 <http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html>`_ (inherited from iText) - static/src/java/JPdfSign.java
|
||||||
|
|
||||||
|
Icon
|
||||||
|
----
|
||||||
|
|
||||||
|
`Created by Anton Noskov from the Noun Project <https://thenounproject.com/search/?q=signed+contract&i=65694>`_
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Rafael Blasco <rafabn@antiun.com>
|
||||||
|
* Antonio Espinosa <antonioea@antiun.com>
|
||||||
|
|
||||||
|
Maintainer
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/logo.png
|
||||||
|
:alt: Odoo Community Association
|
||||||
|
:target: https://odoo-community.org
|
||||||
|
|
||||||
|
This module is maintained by the OCA.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
To contribute to this module, please visit http://odoo-community.org.
|
|
@ -0,0 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from . import models
|
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Qweb PDF reports signer",
|
||||||
|
"summary": "Sign Qweb PDFs usign a PKCS#12 certificate",
|
||||||
|
"version": "8.0.1.0.0",
|
||||||
|
"category": "Reporting",
|
||||||
|
"website": "http://www.antiun.com",
|
||||||
|
"author": "Antiun Ingeniería S.L., "
|
||||||
|
"Odoo Community Association (OCA)",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"application": False,
|
||||||
|
"installable": True,
|
||||||
|
"depends": [
|
||||||
|
"report",
|
||||||
|
],
|
||||||
|
"external_dependencies": {
|
||||||
|
"bin": ['/usr/bin/java'],
|
||||||
|
},
|
||||||
|
"data": [
|
||||||
|
"security/ir.model.access.csv",
|
||||||
|
"views/report_certificate_view.xml",
|
||||||
|
"views/res_company_view.xml",
|
||||||
|
],
|
||||||
|
"demo": [
|
||||||
|
"demo/report_partner.xml",
|
||||||
|
"demo/report_certificate.xml",
|
||||||
|
],
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
-->
|
||||||
|
<openerp>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record id="demo_certificate_test" model="report.certificate">
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
<field name="name">Test OCA certificate</field>
|
||||||
|
<field name="path">test.p12</field>
|
||||||
|
<field name="password_file">test.passwd</field>
|
||||||
|
<field name="model_id" ref="base.model_res_partner"/>
|
||||||
|
<field name="domain">[('customer', '=', True)]</field>
|
||||||
|
<field name="allow_only_one" eval="True"/>
|
||||||
|
<field name="attachment">'test_' + (object.name or '').replace(' ', '_').lower() + '.signed.pdf'</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
-->
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<template id="report_partner_demo_document">
|
||||||
|
<t t-call="report.external_layout">
|
||||||
|
<div class="page">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
This is a sample report for testing PDF certificates
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<strong>Partner:</strong> <span t-field="o.name"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="report_partner_demo">
|
||||||
|
<t t-call="report.html_container">
|
||||||
|
<t t-foreach="doc_ids" t-as="doc_id">
|
||||||
|
<t t-raw="translate_doc(doc_id, doc_model, 'lang', 'report_qweb_signer.report_partner_demo_document')"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<report
|
||||||
|
id="partner_demo"
|
||||||
|
model="res.partner"
|
||||||
|
string="Test PDF certificate"
|
||||||
|
report_type="qweb-pdf"
|
||||||
|
name="report_qweb_signer.report_partner_demo"
|
||||||
|
file="report_qweb_signer.report_partner_demo"
|
||||||
|
attachment_use="True"
|
||||||
|
attachment="'test_' + (object.name or '').replace(' ', '_').lower() + '.pdf'"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
|
@ -0,0 +1,161 @@
|
||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * report_qweb_signer
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 8.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2015-11-22 19:28+0000\n"
|
||||||
|
"PO-Revision-Date: 2015-11-22 19:28+0000\n"
|
||||||
|
"Last-Translator: <>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,allow_only_one:0
|
||||||
|
msgid "Allow only one document"
|
||||||
|
msgstr "Sólo un documento"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,path:0
|
||||||
|
msgid "Certificate file path"
|
||||||
|
msgstr "Ruta al certificado"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: view:res.company:report_qweb_signer.view_company_form
|
||||||
|
msgid "Certificates"
|
||||||
|
msgstr "Certificados"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: model:ir.model,name:report_qweb_signer.model_res_company
|
||||||
|
msgid "Companies"
|
||||||
|
msgstr "Compañías"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,company_id:0
|
||||||
|
msgid "Company"
|
||||||
|
msgstr "Compañía"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,create_uid:0
|
||||||
|
msgid "Created by"
|
||||||
|
msgstr "Creado por"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,create_date:0
|
||||||
|
msgid "Created on"
|
||||||
|
msgstr "Creado en"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,domain:0
|
||||||
|
msgid "Domain"
|
||||||
|
msgstr "Dominio"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: help:report.certificate,domain:0
|
||||||
|
msgid "Domain for filtering if sign or not the document"
|
||||||
|
msgstr "Dominio para filrar si firmar o no el documento"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: help:report.certificate,attachment:0
|
||||||
|
msgid "Filename used to store signed document as attachment. Keep empty to not save signed document."
|
||||||
|
msgstr "Nombre de fichero usado para guardar el documento firmado como adjunto. Dejar en blanco para no guardar el documento firmado."
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,id:0
|
||||||
|
msgid "ID"
|
||||||
|
msgstr "ID"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: help:report.certificate,allow_only_one:0
|
||||||
|
msgid "If True, this certificate can not be used to sign a PDF from several documents."
|
||||||
|
msgstr "Si está activo, este certificado no puede usarse para firmar un PDF de varios documentos."
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,write_uid:0
|
||||||
|
msgid "Last Updated by"
|
||||||
|
msgstr "Última actualización por"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,write_date:0
|
||||||
|
msgid "Last Updated on"
|
||||||
|
msgstr "Última actualización en"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,model_id:0
|
||||||
|
msgid "Model"
|
||||||
|
msgstr "Modelo"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: help:report.certificate,model_id:0
|
||||||
|
msgid "Model where apply this certificate"
|
||||||
|
msgstr "Modelo en el que usar este certificado para firmar"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,name:0
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "Nombre"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: model:ir.actions.act_window,name:report_qweb_signer.action_report_certificate
|
||||||
|
#: model:ir.ui.menu,name:report_qweb_signer.menu_report_certificate
|
||||||
|
msgid "PDF certificates"
|
||||||
|
msgstr "Certificados PDF"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: view:report.certificate:report_qweb_signer.view_report_certificate_form
|
||||||
|
msgid "PDF report certificate"
|
||||||
|
msgstr "Certificado de informe PDF"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: view:report.certificate:report_qweb_signer.view_report_certificate_tree
|
||||||
|
#: field:res.company,report_certificate_ids:0
|
||||||
|
msgid "PDF report certificates"
|
||||||
|
msgstr "Certificados de informes PDF"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,password_file:0
|
||||||
|
msgid "Password file path"
|
||||||
|
msgstr "Ruta al fichero de contraseña"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: help:report.certificate,path:0
|
||||||
|
msgid "Path to PKCS#12 certificate file"
|
||||||
|
msgstr "Ruta al fichero de certificado PKCS#12"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: help:report.certificate,password_file:0
|
||||||
|
msgid "Path to certificate password file"
|
||||||
|
msgstr "Ruta al fichero que contiene la contraseña con la que se proteje el fichero de certificado"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: code:addons/report_qweb_signer/models/report.py:77
|
||||||
|
#, python-format
|
||||||
|
msgid "PortableSigner failed (error code: %s). Message: %s"
|
||||||
|
msgstr "PortableSigner falló (código de error: %s). Mensaje: %s"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: model:ir.model,name:report_qweb_signer.model_report
|
||||||
|
msgid "Report"
|
||||||
|
msgstr "Informe"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,attachment:0
|
||||||
|
msgid "Save as attachment"
|
||||||
|
msgstr "Salvar como adjunto"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: field:report.certificate,sequence:0
|
||||||
|
msgid "Sequence"
|
||||||
|
msgstr "Secuencia"
|
||||||
|
|
||||||
|
#. module: report_qweb_signer
|
||||||
|
#: code:addons/report_qweb_signer/models/report.py:76
|
||||||
|
#, python-format
|
||||||
|
msgid "Signing report (PDF)"
|
||||||
|
msgstr "Firmando informe (PDF)"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from . import report
|
||||||
|
from . import report_certificate
|
||||||
|
from . import res_company
|
|
@ -0,0 +1,191 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
import base64
|
||||||
|
from contextlib import closing
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
from openerp import models, api, _
|
||||||
|
from openerp.exceptions import Warning, AccessError
|
||||||
|
from openerp.tools.safe_eval import safe_eval
|
||||||
|
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_filepath(path):
|
||||||
|
path = path or ''
|
||||||
|
path = path.strip()
|
||||||
|
if not os.path.isabs(path):
|
||||||
|
me = os.path.dirname(__file__)
|
||||||
|
path = '{}/../static/certificate/'.format(me) + path
|
||||||
|
path = os.path.normpath(path)
|
||||||
|
return path if os.path.exists(path) else False
|
||||||
|
|
||||||
|
|
||||||
|
class Report(models.Model):
|
||||||
|
_inherit = 'report'
|
||||||
|
|
||||||
|
def _certificate_get(self, cr, uid, ids, report, context=None):
|
||||||
|
if report.report_type != 'qweb-pdf':
|
||||||
|
_logger.info(
|
||||||
|
"Can only sign qweb-pdf reports, this one is '%s' type",
|
||||||
|
report.report_type)
|
||||||
|
return False
|
||||||
|
m_cert = self.pool['report.certificate']
|
||||||
|
company_id = self.pool['res.users']._get_company(cr, uid)
|
||||||
|
certificate_ids = m_cert.search(cr, uid, [
|
||||||
|
('company_id', '=', company_id),
|
||||||
|
('model_id', '=', report.model)], context=context)
|
||||||
|
if not certificate_ids:
|
||||||
|
_logger.info(
|
||||||
|
"No PDF certificate found for report '%s'",
|
||||||
|
report.report_name)
|
||||||
|
return False
|
||||||
|
for cert in m_cert.browse(cr, uid, certificate_ids, context=context):
|
||||||
|
# Check allow only one document
|
||||||
|
if cert.allow_only_one and len(ids) > 1:
|
||||||
|
_logger.info(
|
||||||
|
"Certificate '%s' allows only one document, "
|
||||||
|
"but printing %d documents",
|
||||||
|
cert.name, len(ids))
|
||||||
|
continue
|
||||||
|
# Check domain
|
||||||
|
if cert.domain:
|
||||||
|
m_model = self.pool[cert.model_id.model]
|
||||||
|
domain = [('id', 'in', tuple(ids))]
|
||||||
|
domain = domain + safe_eval(cert.domain)
|
||||||
|
doc_ids = m_model.search(cr, uid, domain, context=context)
|
||||||
|
if not doc_ids:
|
||||||
|
_logger.info(
|
||||||
|
"Certificate '%s' domain not satisfied", cert.name)
|
||||||
|
continue
|
||||||
|
# Certificate match!
|
||||||
|
return cert
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _attach_filename_get(self, cr, uid, ids, certificate, context=None):
|
||||||
|
if len(ids) != 1:
|
||||||
|
return False
|
||||||
|
obj = self.pool[certificate.model_id.model].browse(cr, uid, ids[0])
|
||||||
|
filename = safe_eval(certificate.attachment, {
|
||||||
|
'object': obj,
|
||||||
|
'time': time
|
||||||
|
})
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def _attach_signed_read(self, cr, uid, ids, certificate, context=None):
|
||||||
|
if len(ids) != 1:
|
||||||
|
return False
|
||||||
|
filename = self._attach_filename_get(
|
||||||
|
cr, uid, ids, certificate, context=context)
|
||||||
|
if not filename:
|
||||||
|
return False
|
||||||
|
signed = False
|
||||||
|
m_attachment = self.pool['ir.attachment']
|
||||||
|
attach_ids = m_attachment.search(cr, uid, [
|
||||||
|
('datas_fname', '=', filename),
|
||||||
|
('res_model', '=', certificate.model_id.model),
|
||||||
|
('res_id', '=', ids[0])
|
||||||
|
])
|
||||||
|
if attach_ids:
|
||||||
|
signed = m_attachment.browse(cr, uid, attach_ids[0]).datas
|
||||||
|
signed = base64.decodestring(signed)
|
||||||
|
return signed
|
||||||
|
|
||||||
|
def _attach_signed_write(self, cr, uid, ids, certificate, signed,
|
||||||
|
context=None):
|
||||||
|
if len(ids) != 1:
|
||||||
|
return False
|
||||||
|
filename = self._attach_filename_get(
|
||||||
|
cr, uid, ids, certificate, context=context)
|
||||||
|
if not filename:
|
||||||
|
return False
|
||||||
|
m_attachment = self.pool['ir.attachment']
|
||||||
|
try:
|
||||||
|
attach_id = m_attachment.create(cr, uid, {
|
||||||
|
'name': filename,
|
||||||
|
'datas': base64.encodestring(signed),
|
||||||
|
'datas_fname': filename,
|
||||||
|
'res_model': certificate.model_id.model,
|
||||||
|
'res_id': ids[0],
|
||||||
|
})
|
||||||
|
except AccessError:
|
||||||
|
raise Warning(
|
||||||
|
_('Saving signed report (PDF):') + '\n',
|
||||||
|
_('You do not have enought access rights to save attachments'))
|
||||||
|
else:
|
||||||
|
_logger.info(
|
||||||
|
"The signed PDF document '%s' is now saved in the database",
|
||||||
|
filename)
|
||||||
|
return attach_id
|
||||||
|
|
||||||
|
def _signer_bin(self, opts):
|
||||||
|
me = os.path.dirname(__file__)
|
||||||
|
return 'java -jar {}/../static/jar/jPdfSign.jar '.format(me) + opts
|
||||||
|
|
||||||
|
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):
|
||||||
|
raise Warning(
|
||||||
|
_('Signing report (PDF)'),
|
||||||
|
_('Certificate or password file not found'))
|
||||||
|
signer_opts = '"%s" "%s" "%s" "%s"' % (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 Warning(
|
||||||
|
_('Signing report (PDF)'),
|
||||||
|
_('jPdfSign failed (error code: %s). '
|
||||||
|
'Message: %s') % (str(process.returncode), err))
|
||||||
|
return pdfsigned
|
||||||
|
|
||||||
|
@api.v7
|
||||||
|
def get_pdf(self, cr, uid, ids, report_name, html=None, data=None,
|
||||||
|
context=None):
|
||||||
|
signed_content = False
|
||||||
|
report = self._get_report_from_name(cr, uid, report_name)
|
||||||
|
certificate = self._certificate_get(
|
||||||
|
cr, uid, ids, report, context=context)
|
||||||
|
if certificate and certificate.attachment:
|
||||||
|
signed_content = self._attach_signed_read(
|
||||||
|
cr, uid, ids, certificate, context=context)
|
||||||
|
if signed_content:
|
||||||
|
_logger.info("The signed PDF document '%s/%s' was loaded from "
|
||||||
|
"the database", report_name, ids)
|
||||||
|
return signed_content
|
||||||
|
content = super(Report, self).get_pdf(
|
||||||
|
cr, uid, ids, report_name, html=html, data=data,
|
||||||
|
context=context)
|
||||||
|
if certificate:
|
||||||
|
# Creating temporary origin PDF
|
||||||
|
pdf_fd, pdf = tempfile.mkstemp(
|
||||||
|
suffix='.pdf', prefix='report.tmp.')
|
||||||
|
with closing(os.fdopen(pdf_fd, 'w')) as pf:
|
||||||
|
pf.write(content)
|
||||||
|
_logger.info(
|
||||||
|
"Signing PDF document '%s/%s' with certificate '%s'",
|
||||||
|
report_name, 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, IOError):
|
||||||
|
_logger.error('Error when trying to remove file %s', fname)
|
||||||
|
if certificate.attachment:
|
||||||
|
self._attach_signed_write(
|
||||||
|
cr, uid, ids, certificate, content, context=context)
|
||||||
|
return content
|
|
@ -0,0 +1,42 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from openerp import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ReportCertificate(models.Model):
|
||||||
|
_name = 'report.certificate'
|
||||||
|
_order = 'sequence,id'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_company(self):
|
||||||
|
m_company = self.env['res.company']
|
||||||
|
return m_company._company_default_get('report.certificate')
|
||||||
|
|
||||||
|
sequence = fields.Integer(default=10)
|
||||||
|
name = fields.Char(required=True)
|
||||||
|
path = fields.Char(
|
||||||
|
string="Certificate file path", required=True,
|
||||||
|
help="Path to PKCS#12 certificate file")
|
||||||
|
password_file = fields.Char(
|
||||||
|
string="Password file path", required=True,
|
||||||
|
help="Path to certificate password file")
|
||||||
|
model_id = fields.Many2one(
|
||||||
|
string="Model", required=True,
|
||||||
|
comodel_name='ir.model',
|
||||||
|
help="Model where apply this certificate")
|
||||||
|
domain = fields.Char(
|
||||||
|
string="Domain",
|
||||||
|
help="Domain for filtering if sign or not the document")
|
||||||
|
allow_only_one = fields.Boolean(
|
||||||
|
string="Allow only one document", default=True,
|
||||||
|
help="If True, this certificate can not be useb to sign "
|
||||||
|
"a PDF from several documents.")
|
||||||
|
attachment = fields.Char(
|
||||||
|
string="Save as attachment",
|
||||||
|
help="Filename used to store signed document as attachment. "
|
||||||
|
"Keep empty to not save signed document.")
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
string='Company', comodel_name='res.company',
|
||||||
|
required=True, default=_default_company)
|
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from openerp import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class ResCompany(models.Model):
|
||||||
|
_inherit = 'res.company'
|
||||||
|
|
||||||
|
report_certificate_ids = fields.One2many(
|
||||||
|
string="PDF report certificates",
|
||||||
|
comodel_name='report.certificate',
|
||||||
|
inverse_name='company_id')
|
|
@ -0,0 +1,3 @@
|
||||||
|
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||||
|
"access_report_certificate_public","report_certificate group_public","model_report_certificate","base.group_user",1,0,0,0
|
||||||
|
"access_report_certificate_manager","report_certificate group_manager","model_report_certificate","base.group_erp_manager",1,1,1,1
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
admin
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" enable-background="new 0 0 100 100" xml:space="preserve"><rect x="24.469" y="28.037" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="16.401" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="39.672" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="51.309" fill="#000000" width="24.345" height="5.592"/><path fill="#000000" d="M80.018,5.03H19.982c-3.724,0-6.745,3.019-6.745,6.745v73.077c0,3.727,3.021,6.744,6.745,6.744h33.784v-4.66 H22.829c-2.485,0-4.498-2.014-4.498-4.496V14.187c0-2.483,2.013-4.496,4.498-4.496h54.342c2.483,0,4.498,2.013,4.498,4.496v68.252 c0,2.482-2.015,4.496-4.498,4.496h-1.819v4.66h4.666c3.726,0,6.746-3.019,6.746-6.744V11.775C86.764,8.049,83.742,5.03,80.018,5.03z "/><path fill="#000000" d="M75.308,69.185l5.763-2.743l-5.723-2.822l3.619-5.255l-6.369,0.417l0.51-6.362l-5.309,3.543l-2.741-5.763 l-2.823,5.722l-5.256-3.616l0.416,6.366l-6.36-0.506l3.544,5.306l-5.764,2.742l5.723,2.822l-3.619,5.256l6.367-0.415l-0.508,6.359 l5.309-3.545l2.742,5.765l2.822-5.723l5.255,3.618l-0.414-6.366l6.36,0.509L75.308,69.185z M73.956,63.635L64.892,74.36 c-0.456,0.546-1.134,0.862-1.847,0.867c-0.004,0-0.01,0-0.016,0c-0.707,0-1.379-0.308-1.843-0.838l-4.05-4.647 c-0.886-1.019-0.78-2.561,0.235-3.446c1.019-0.887,2.562-0.781,3.448,0.237l2.178,2.5l7.228-8.55 c0.868-1.03,2.411-1.162,3.441-0.289C74.696,61.065,74.826,62.605,73.956,63.635z"/><polygon fill="#000000" points="72.905,94.957 64.818,88.149 56.777,94.957 56.777,81.658 61.884,79.125 64.818,84.539 67.877,79.141 72.905,81.658 "/><text x="0" y="115" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Anton Noskov</text><text x="0" y="120" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,333 @@
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.security.CodeSource;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.ProviderException;
|
||||||
|
import java.security.Security;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import sun.security.pkcs11.SunPKCS11;
|
||||||
|
|
||||||
|
import com.lowagie.text.pdf.PdfReader;
|
||||||
|
import com.lowagie.text.pdf.PdfSignatureAppearance;
|
||||||
|
import com.lowagie.text.pdf.PdfStamper;
|
||||||
|
|
||||||
|
/*
|
||||||
|
import com.itextpdf.text.pdf.PdfReader;
|
||||||
|
import com.itextpdf.text.pdf.PdfSignatureAppearance;
|
||||||
|
import com.itextpdf.text.pdf.PdfStamper;
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Jan Peter Stotz
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class JPdfSign {
|
||||||
|
|
||||||
|
private static PrivateKey privateKey;
|
||||||
|
|
||||||
|
private static Certificate[] certificateChain;
|
||||||
|
|
||||||
|
private static ResourceBundle bundle = ResourceBundle.getBundle("strings");
|
||||||
|
|
||||||
|
private static String PRODUCTNAME = bundle.getString("productname");
|
||||||
|
|
||||||
|
private static String VERSION = bundle.getString("version");
|
||||||
|
|
||||||
|
private static String JAR_FILENAME = bundle.getString("jar-filename");
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// for (int i = 0; i < args.length; i++) {
|
||||||
|
// System.out.println("arg[" + i + "] :" + args[i]);
|
||||||
|
// }
|
||||||
|
if (args.length < 2)
|
||||||
|
showUsage();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
String pkcs12FileName = args[0].trim();
|
||||||
|
String pdfInputFileName = args[1];
|
||||||
|
String pdfOutputFileName = args[2];
|
||||||
|
boolean usePKCS12 = !(pkcs12FileName.equals("-PKCS11"));
|
||||||
|
|
||||||
|
String passwdfile = "";
|
||||||
|
if (args.length == 4) {
|
||||||
|
passwdfile = args[3];
|
||||||
|
}
|
||||||
|
// System.out.println("");
|
||||||
|
// System.out.println("pdfInputFileName : " + pdfInputFileName);
|
||||||
|
// System.out.println("pdfOutputFileName: " + pdfOutputFileName);
|
||||||
|
|
||||||
|
if (usePKCS12)
|
||||||
|
readPrivateKeyFromPKCS12(pkcs12FileName, passwdfile);
|
||||||
|
else
|
||||||
|
readPrivateKeyFromPKCS11();
|
||||||
|
|
||||||
|
PdfReader reader = null;
|
||||||
|
try {
|
||||||
|
reader = new PdfReader(pdfInputFileName);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while opening the input PDF file: \""
|
||||||
|
+ pdfInputFileName + "\"");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
FileOutputStream fout = null;
|
||||||
|
try {
|
||||||
|
fout = new FileOutputStream(pdfOutputFileName);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while opening the output PDF file: \""
|
||||||
|
+ pdfOutputFileName + "\"");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
PdfStamper stp = null;
|
||||||
|
try {
|
||||||
|
stp = PdfStamper.createSignature(reader, fout, '\0', null, true);
|
||||||
|
PdfSignatureAppearance sap = stp.getSignatureAppearance();
|
||||||
|
sap.setCrypto(privateKey, certificateChain, null, PdfSignatureAppearance.WINCER_SIGNED);
|
||||||
|
// sap.setCrypto(privateKey, certificateChain, null, null);
|
||||||
|
// sap.setReason("I'm the author");
|
||||||
|
// sap.setLocation("Lisbon");
|
||||||
|
// sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1,
|
||||||
|
// null);
|
||||||
|
sap.setCertified(true);
|
||||||
|
stp.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while signing the PDF file:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
} catch (KeyStoreException kse) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while initializing the KeyStore instance:");
|
||||||
|
kse.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readPrivateKeyFromPKCS11() throws KeyStoreException {
|
||||||
|
// Initialize PKCS#11 provider from config file
|
||||||
|
String configFileName = getConfigFilePath("pkcs11.cfg");
|
||||||
|
|
||||||
|
Provider p = null;
|
||||||
|
try {
|
||||||
|
p = new SunPKCS11(configFileName);
|
||||||
|
Security.addProvider(p);
|
||||||
|
} catch (ProviderException e) {
|
||||||
|
System.err
|
||||||
|
.println("Unable to load PKCS#11 provider with config file: "
|
||||||
|
+ configFileName);
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
String pkcs11PIN = "000000";
|
||||||
|
System.out.print("Please enter the smartcard pin: ");
|
||||||
|
try {
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(
|
||||||
|
System.in));
|
||||||
|
pkcs11PIN = in.readLine();
|
||||||
|
// System.out.println(pkcs11PIN);
|
||||||
|
// System.out.println(pkcs11PIN.length());
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while reading the PIN:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
KeyStore ks = null;
|
||||||
|
try {
|
||||||
|
ks = KeyStore.getInstance("pkcs11", p);
|
||||||
|
ks.load(null, pkcs11PIN.toCharArray());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while reading the PKCS#11 smartcard:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while reading the PKCS#11 smartcard:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while reading the PKCS#11 smartcard:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
String alias = "";
|
||||||
|
try {
|
||||||
|
alias = (String) ks.aliases().nextElement();
|
||||||
|
privateKey = (PrivateKey) ks.getKey(alias, pkcs11PIN.toCharArray());
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while retrieving the private key:");
|
||||||
|
System.err
|
||||||
|
.println("The selected PKCS#12 file does not contain any private keys.");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while retrieving the private key:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (UnrecoverableKeyException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while retrieving the private key:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
certificateChain = ks.getCertificateChain(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void readPrivateKeyFromPKCS12(String pkcs12FileName, String pwdFile)
|
||||||
|
throws KeyStoreException {
|
||||||
|
String pkcs12Password = "";
|
||||||
|
KeyStore ks = null;
|
||||||
|
if (!pwdFile.equals("")) {
|
||||||
|
try {
|
||||||
|
FileInputStream pwdfis = new FileInputStream(pwdFile);
|
||||||
|
byte[] pwd = new byte[1024];
|
||||||
|
try {
|
||||||
|
do {
|
||||||
|
int r = pwdfis.read(pwd);
|
||||||
|
if (r < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pkcs12Password += new String(pwd);
|
||||||
|
pkcs12Password = pkcs12Password.trim();
|
||||||
|
} while (pwdfis.available() > 0);
|
||||||
|
pwdfis.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
System.err
|
||||||
|
.println("Can't read password file: " + pwdFile);
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException fnfex) {
|
||||||
|
System.err
|
||||||
|
.println("Password file not found: " + pwdFile);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.print("Please enter the password for \"" + pkcs12FileName
|
||||||
|
+ "\": ");
|
||||||
|
try {
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(
|
||||||
|
System.in));
|
||||||
|
pkcs12Password = in.readLine();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while reading the password:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ks = KeyStore.getInstance("pkcs12");
|
||||||
|
ks.load(new FileInputStream(pkcs12FileName), pkcs12Password
|
||||||
|
.toCharArray());
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while reading the PKCS#12 file:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while reading the PKCS#12 file:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
System.err.println("Unable to open the PKCS#12 keystore file \""
|
||||||
|
+ pkcs12FileName + "\":");
|
||||||
|
System.err
|
||||||
|
.println("The file does not exists or missing read permission.");
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while reading the PKCS#12 file:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
String alias = "";
|
||||||
|
try {
|
||||||
|
alias = (String) ks.aliases().nextElement();
|
||||||
|
privateKey = (PrivateKey) ks.getKey(alias, pkcs12Password
|
||||||
|
.toCharArray());
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while retrieving the private key:");
|
||||||
|
System.err
|
||||||
|
.println("The selected PKCS#12 file does not contain any private keys.");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while retrieving the private key:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (UnrecoverableKeyException e) {
|
||||||
|
System.err
|
||||||
|
.println("An unknown error accoured while retrieving the private key:");
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
certificateChain = ks.getCertificateChain(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String getConfigFilePath(String configFilename) {
|
||||||
|
CodeSource source = JPdfSign.class.getProtectionDomain()
|
||||||
|
.getCodeSource();
|
||||||
|
URL url = source.getLocation();
|
||||||
|
|
||||||
|
String jarPath = URLDecoder.decode(url.getFile());
|
||||||
|
File f = new File(jarPath);
|
||||||
|
try {
|
||||||
|
jarPath = f.getCanonicalPath();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
if (!f.isDirectory()) {
|
||||||
|
f = new File(jarPath);
|
||||||
|
jarPath = f.getParent();
|
||||||
|
}
|
||||||
|
System.out.println(jarPath);
|
||||||
|
if (jarPath.length() > 0) {
|
||||||
|
return jarPath + File.separator + configFilename;
|
||||||
|
} else
|
||||||
|
return configFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showUsage() {
|
||||||
|
System.out.println("jPdfSign v" + VERSION
|
||||||
|
+ " by Jan Peter Stotz - jpstotz@gmx.de\n");
|
||||||
|
System.out.println(PRODUCTNAME + " usage:");
|
||||||
|
System.out
|
||||||
|
.println("\nFor using a PKCS#12 (.p12) file as signature certificate and private key source:");
|
||||||
|
System.out.print("\tjava -jar " + JAR_FILENAME);
|
||||||
|
System.out
|
||||||
|
.println(" pkcs12FileName pdfInputFileName pdfOutputFileName");
|
||||||
|
System.out
|
||||||
|
.println("\nFor using a PKCS#11 smartcard as signature certificate and private key source:");
|
||||||
|
System.out.print("\tjava -jar" + JAR_FILENAME);
|
||||||
|
System.out.println(" -PKCS11 pdfInputFileName pdfOutputFileName");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
productname=jPdfSign
|
||||||
|
version=0.3.1
|
||||||
|
jar-filename=jPdfSign.jar
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
-->
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="view_report_certificate_form" model="ir.ui.view">
|
||||||
|
<field name="name">report.certificate.form</field>
|
||||||
|
<field name="model">report.certificate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="PDF report certificate">
|
||||||
|
<sheet string="PDF report certificate">
|
||||||
|
<div class="oe_title">
|
||||||
|
<label for="name" class="oe_edit_only"/>
|
||||||
|
<h1><field name="name"/></h1>
|
||||||
|
</div>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="path"/>
|
||||||
|
<field name="password_file"/>
|
||||||
|
<field name="model_id"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="domain"/>
|
||||||
|
<field name="allow_only_one"/>
|
||||||
|
<field name="attachment"/>
|
||||||
|
<field name="company_id" widget="selection"
|
||||||
|
groups="base.group_multi_company"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_report_certificate_tree" model="ir.ui.view" >
|
||||||
|
<field name="name">report.certificate.tree</field>
|
||||||
|
<field name="model">report.certificate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="PDF report certificates">
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="path"/>
|
||||||
|
<field name="model_id"/>
|
||||||
|
<field name="domain"/>
|
||||||
|
<field name="company_id"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_report_certificate" model="ir.actions.act_window">
|
||||||
|
<field name="name">PDF certificates</field>
|
||||||
|
<field name="res_model">report.certificate</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_report_certificate"
|
||||||
|
name="PDF certificates"
|
||||||
|
parent="report.reporting_menuitem"
|
||||||
|
action="action_report_certificate"/>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
-->
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="view_company_form" model="ir.ui.view">
|
||||||
|
<field name="name">Add PDF report certificates list</field>
|
||||||
|
<field name="inherit_id" ref="base.view_company_form" />
|
||||||
|
<field name="model">res.company</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<data>
|
||||||
|
<xpath expr="//page[@string='Report Configuration']/group[@string='Configuration']" position="after">
|
||||||
|
<group string="Certificates" col="2">
|
||||||
|
<field name="report_certificate_ids"
|
||||||
|
context="{'default_company_id': active_id}"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
Loading…
Reference in New Issue