[IMP]add support for signature passphrase

This commit is contained in:
Luc De Meyer 2023-12-02 18:35:04 +01:00
parent 57f885dc88
commit 91d4490bb0
7 changed files with 178 additions and 63 deletions

View File

@ -3,7 +3,7 @@
{ {
"name": "EBICS banking protocol", "name": "EBICS banking protocol",
"version": "16.0.1.6.2", "version": "16.0.1.7.0",
"license": "LGPL-3", "license": "LGPL-3",
"author": "Noviat", "author": "Noviat",
"website": "https://www.noviat.com/", "website": "https://www.noviat.com/",

View File

@ -115,6 +115,17 @@ class EbicsUserID(models.Model):
ebics_passphrase_invisible = fields.Boolean( ebics_passphrase_invisible = fields.Boolean(
compute="_compute_ebics_passphrase_view_modifiers" compute="_compute_ebics_passphrase_view_modifiers"
) )
ebics_sig_passphrase = fields.Char(
string="EBICS Signature Passphrase",
store=False,
help="You can set here a different passphrase for the EBICS "
"signing key. This passphrase will never be stored hence "
"you'll need to specify your passphrase for each transaction that "
"requires a digital signature.",
)
ebics_sig_passphrase_invisible = fields.Boolean(
compute="_compute_ebics_sig_passphrase_invisible"
)
ebics_ini_letter = fields.Binary( ebics_ini_letter = fields.Binary(
string="EBICS INI Letter", string="EBICS INI Letter",
readonly=True, readonly=True,
@ -227,13 +238,22 @@ class EbicsUserID(models.Model):
rec.ebics_passphrase_required = False rec.ebics_passphrase_required = False
rec.ebics_passphrase_invisible = True rec.ebics_passphrase_invisible = True
@api.depends("state")
def _compute_ebics_sig_passphrase_invisible(self):
for rec in self:
rec.ebics_sig_passphrase_invisible = True
if fintech.__version_info__ < (7, 3, 1):
continue
if rec.transaction_rights != "down" and rec.state == "draft":
rec.ebics_sig_passphrase_invisible = False
@api.constrains("ebics_key_x509") @api.constrains("ebics_key_x509")
def _check_ebics_key_x509(self): def _check_ebics_key_x509(self):
for cfg in self: for cfg in self:
if cfg.ebics_version == "H005" and not cfg.ebics_key_x509: if cfg.ebics_version == "H005" and not cfg.ebics_key_x509:
raise UserError(_("X.509 certificates must be used with EBICS 3.0.")) raise UserError(_("X.509 certificates must be used with EBICS 3.0."))
@api.constrains("ebics_passphrase") @api.constrains("ebics_passphrase", "ebics_sig_passphrase")
def _check_ebics_passphrase(self): def _check_ebics_passphrase(self):
for rec in self: for rec in self:
if rec.ebics_passphrase and len(rec.ebics_passphrase) < 8: if rec.ebics_passphrase and len(rec.ebics_passphrase) < 8:
@ -295,9 +315,13 @@ class EbicsUserID(models.Model):
ebics_version = self.ebics_config_id.ebics_version ebics_version = self.ebics_config_id.ebics_version
try: try:
keyring = EbicsKeyRing( keyring_params = {
keys=self.ebics_keys_fn, passphrase=self.ebics_passphrase "keys": self.ebics_keys_fn,
) "passphrase": self.ebics_passphrase,
}
if self.ebics_sig_passphrase:
keyring_params["ebics_sig_passphrase"] = self.ebics_sig_passphrase
keyring = EbicsKeyRing(**keyring_params)
bank = EbicsBank( bank = EbicsBank(
keyring=keyring, keyring=keyring,
hostid=self.ebics_config_id.ebics_host, hostid=self.ebics_config_id.ebics_host,
@ -536,7 +560,7 @@ class EbicsUserID(models.Model):
def change_passphrase(self): def change_passphrase(self):
self.ensure_one() self.ensure_one()
ctx = dict(self._context, default_ebics_userid_id=self.id) ctx = dict(self.env.context, default_ebics_userid_id=self.id)
module = __name__.split("addons.")[1].split(".")[0] module = __name__.split("addons.")[1].split(".")[0]
view = self.env.ref("%s.ebics_change_passphrase_view_form" % module) view = self.env.ref("%s.ebics_change_passphrase_view_form" % module)
return { return {

View File

@ -88,6 +88,7 @@
<field name="ebics_version" invisible="1" /> <field name="ebics_version" invisible="1" />
<field name="ebics_passphrase_required" invisible="1" /> <field name="ebics_passphrase_required" invisible="1" />
<field name="ebics_passphrase_invisible" invisible="1" /> <field name="ebics_passphrase_invisible" invisible="1" />
<field name="ebics_sig_passphrase_invisible" invisible="1" />
<group name="main-left"> <group name="main-left">
<field name="name" /> <field name="name" />
<field <field
@ -96,6 +97,11 @@
attrs="{'required': [('ebics_passphrase_required', '=', True)], 'invisible': [('ebics_passphrase_invisible', '=', True)]}" attrs="{'required': [('ebics_passphrase_required', '=', True)], 'invisible': [('ebics_passphrase_invisible', '=', True)]}"
/> />
<field name="ebics_passphrase_store" /> <field name="ebics_passphrase_store" />
<field
name="ebics_sig_passphrase"
password="True"
attrs="{'invisible': [('ebics_sig_passphrase_invisible', '=', True)]}"
/>
<field name="transaction_rights" /> <field name="transaction_rights" />
<field name="active" /> <field name="active" />
</group> </group>

View File

@ -24,37 +24,75 @@ class EbicsChangePassphrase(models.TransientModel):
ebics_userid_id = fields.Many2one( ebics_userid_id = fields.Many2one(
comodel_name="ebics.userid", string="EBICS UserID", readonly=True comodel_name="ebics.userid", string="EBICS UserID", readonly=True
) )
old_pass = fields.Char(string="Old Passphrase", required=True) old_pass = fields.Char(string="Old Passphrase")
new_pass = fields.Char(string="New Passphrase", required=True) new_pass = fields.Char(string="New Passphrase")
new_pass_check = fields.Char(string="New Passphrase (verification)", required=True) new_pass_check = fields.Char(string="New Passphrase (verification)")
old_sig_pass = fields.Char(string="Old Signature Passphrase")
new_sig_pass = fields.Char(string="New Signature Passphrase")
new_sig_pass_check = fields.Char(string="New Signature Passphrase (verification)")
ebics_sig_passphrase_invisible = fields.Boolean(
compute="_compute_ebics_sig_passphrase_invisible"
)
note = fields.Text(string="Notes", readonly=True) note = fields.Text(string="Notes", readonly=True)
def _compute_ebics_sig_passphrase_invisible(self):
for rec in self:
if fintech.__version_info__ < (7, 3, 1):
rec.ebics_sig_passphrase_invisible = True
else:
rec.ebics_sig_passphrase_invisible = False
def change_passphrase(self): def change_passphrase(self):
self.ensure_one() self.ensure_one()
self.note = ""
if ( if (
self.ebics_userid_id.ebics_passphrase_store self.ebics_userid_id.ebics_passphrase_store
and self.old_pass
and self.old_pass != self.ebics_userid_id.ebics_passphrase and self.old_pass != self.ebics_userid_id.ebics_passphrase
): ):
raise UserError(_("Incorrect old passphrase.")) raise UserError(_("Incorrect old passphrase."))
if self.new_pass != self.new_pass_check: if self.new_pass != self.new_pass_check:
raise UserError(_("New passphrase verification error.")) raise UserError(_("New passphrase verification error."))
if self.new_pass == self.ebics_userid_id.ebics_passphrase: if self.new_pass and self.new_pass == self.ebics_userid_id.ebics_passphrase:
raise UserError(_("New passphrase equal to old passphrase.")) raise UserError(_("New passphrase equal to old passphrase."))
try: if (
self.new_sig_pass
and self.old_sig_pass
and self.new_sig_pass == self.old_sig_pass
):
raise UserError(
_("New signature passphrase equal to old signature passphrase.")
)
if self.new_sig_pass != self.new_sig_pass_check:
raise UserError(_("New signature passphrase verification error."))
passphrase = ( passphrase = (
self.ebics_userid_id.ebics_passphrase_store self.ebics_userid_id.ebics_passphrase_store
and self.ebics_userid_id.ebics_passphrase and self.ebics_userid_id.ebics_passphrase
or self.old_pass or self.old_pass
) )
keyring = EbicsKeyRing( try:
keys=self.ebics_userid_id.ebics_keys_fn, keyring_params = {
passphrase=passphrase, "keys": self.ebics_userid_id.ebics_keys_fn,
) "passphrase": passphrase,
keyring.change_passphrase(self.new_pass) }
except ValueError as err: if self.new_sig_pass:
keyring_params["sig_passphrase"] = self.old_sig_pass
keyring = EbicsKeyRing(**keyring_params)
change_params = {}
if self.new_pass:
change_params["passphrase"] = self.new_pass
if self.new_sig_pass:
change_params["sig_passphrase"] = self.new_sig_pass
if change_params:
keyring.change_passphrase(**change_params)
except (ValueError, RuntimeError) as err:
raise UserError(str(err)) from err raise UserError(str(err)) from err
if self.new_pass:
self.ebics_userid_id.ebics_passphrase = self.new_pass self.ebics_userid_id.ebics_passphrase = self.new_pass
self.note = "The EBICS Passphrase has been changed." self.note += "The EBICS Passphrase has been changed."
if self.new_sig_pass:
self.note += "The EBICS Signature Passphrase has been changed."
module = __name__.split("addons.")[1].split(".")[0] module = __name__.split("addons.")[1].split(".")[0]
result_view = self.env.ref( result_view = self.env.ref(

View File

@ -8,19 +8,37 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="EBICS Keys Change Passphrase"> <form string="EBICS Keys Change Passphrase">
<group> <group>
<group name="pass">
<field name="old_pass" password="True" /> <field name="old_pass" password="True" />
<field name="new_pass" password="True" /> <field name="new_pass" password="True" />
<field name="new_pass_check" password="True" /> <field name="new_pass_check" password="True" />
</group> </group>
<group
name="sig_pass"
attrs="{'invisible': [('ebics_sig_passphrase_invisible', '=', True)]}"
>
<field name="old_sig_pass" password="True" />
<field name="new_sig_pass" password="True" />
<field name="new_sig_pass_check" password="True" />
</group>
<group name="invisible" invisible="1">
<field name="ebics_sig_passphrase_invisible" />
</group>
</group>
<footer> <footer>
<button <button
name="change_passphrase" name="change_passphrase"
string="Change Passphrase" string="Change Passphrase"
type="object" type="object"
class="oe_highlight" class="btn-primary"
data-hotkey="q"
/>
<button
string="Cancel"
class="btn-secondary"
special="cancel"
data-hotkey="z"
/> />
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer> </footer>
</form> </form>
</field> </field>
@ -35,7 +53,12 @@
<separator colspan="4" string="Results :" /> <separator colspan="4" string="Results :" />
<field name="note" colspan="4" nolabel="1" width="850" height="400" /> <field name="note" colspan="4" nolabel="1" width="850" height="400" />
<footer> <footer>
<button name="button_close" type="object" string="Close" /> <button
name="button_close"
type="object"
string="Close"
data-hotkey="z"
/>
</footer> </footer>
</form> </form>
</field> </field>

View File

@ -67,6 +67,12 @@ class EbicsXfer(models.TransientModel):
ebics_passphrase_store = fields.Boolean( ebics_passphrase_store = fields.Boolean(
related="ebics_userid_id.ebics_passphrase_store" related="ebics_userid_id.ebics_passphrase_store"
) )
ebics_sig_passphrase = fields.Char(
string="EBICS Signature Passphrase",
)
ebics_sig_passphrase_invisible = fields.Boolean(
compute="_compute_ebics_sig_passphrase_invisible"
)
date_from = fields.Date() date_from = fields.Date()
date_to = fields.Date() date_to = fields.Date()
upload_data = fields.Binary(string="File to Upload") upload_data = fields.Binary(string="File to Upload")
@ -110,6 +116,14 @@ class EbicsXfer(models.TransientModel):
else: else:
return cfg_mod return cfg_mod
def _compute_ebics_sig_passphrase_invisible(self):
for rec in self:
rec.ebics_sig_passphrase_invisible = True
if fintech.__version_info__ < (7, 3, 1):
rec.ebics_sig_passphrase_invisible = True
else:
rec.ebics_sig_passphrase_invisible = False
@api.onchange("ebics_config_id") @api.onchange("ebics_config_id")
def _onchange_ebics_config_id(self): def _onchange_ebics_config_id(self):
avail_userids = self.ebics_config_id.ebics_userid_ids.filtered( avail_userids = self.ebics_config_id.ebics_userid_ids.filtered(
@ -139,11 +153,11 @@ class EbicsXfer(models.TransientModel):
if len(avail_userids) == 1: if len(avail_userids) == 1:
self.ebics_userid_id = avail_userids self.ebics_userid_id = avail_userids
else: else:
with_passphrs_userids = avail_userids.filtered( with_passphrase_userids = avail_userids.filtered(
lambda r: r.ebics_passphrase_store lambda r: r.ebics_passphrase_store
) )
if len(with_passphrs_userids) == 1: if len(with_passphrase_userids) == 1:
self.ebics_userid_id = with_passphrs_userids self.ebics_userid_id = with_passphrase_userids
else: else:
self.ebics_userid_id = False self.ebics_userid_id = False
@ -444,10 +458,14 @@ class EbicsXfer(models.TransientModel):
def _setup_client(self): def _setup_client(self):
self.ebics_config_id._check_ebics_keys() self.ebics_config_id._check_ebics_keys()
passphrase = self._get_passphrase() passphrase = self._get_passphrase()
keyring_params = {
"keys": self.ebics_userid_id.ebics_keys_fn,
"passphrase": passphrase,
}
if self.ebics_sig_passphrase:
keyring_params["sig_passphrase"] = self.ebics_sig_passphrase
try: try:
keyring = EbicsKeyRing( keyring = EbicsKeyRing(**keyring_params)
keys=self.ebics_userid_id.ebics_keys_fn, passphrase=passphrase
)
except (RuntimeError, ValueError) as err: except (RuntimeError, ValueError) as err:
error = _("Error while accessing the EBICS Keys:") error = _("Error while accessing the EBICS Keys:")
error += "\n" error += "\n"

View File

@ -78,7 +78,13 @@
password="True" password="True"
attrs="{'invisible': [('ebics_passphrase_store', '=', True)], 'required': [('ebics_passphrase_store', '=', False)]}" attrs="{'invisible': [('ebics_passphrase_store', '=', True)], 'required': [('ebics_passphrase_store', '=', False)]}"
/> />
<field
name="ebics_sig_passphrase"
password="True"
attrs="{'invisible': [('ebics_sig_passphrase_invisible', '=', True)]}"
/>
<field name="ebics_passphrase_store" invisible="1" /> <field name="ebics_passphrase_store" invisible="1" />
<field name="ebics_sig_passphrase_invisible" invisible="1" />
<separator string="Select your file :" colspan="2" /> <separator string="Select your file :" colspan="2" />
<field name="upload_data" filename="upload_fname" required="1" /> <field name="upload_data" filename="upload_fname" required="1" />
<field name="upload_fname" invisible="1" /> <field name="upload_fname" invisible="1" />