feat: support rbw and bw command line

This commit is contained in:
egarette@silique.fr 2025-02-17 09:34:26 +01:00
parent b332195ef3
commit 510b97d7cf
7 changed files with 180 additions and 210 deletions

View file

@ -0,0 +1,104 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2025-02-17 09:25+0100\n"
"PO-Revision-Date: 2025-02-17 09:28+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.5\n"
#: src/rougail/user_data_bitwarden/data.py:52
msgid "\"bitwarden\" is not set in step.user_data"
msgstr "\"bitwarden\" n'est pas dans step.user_data"
#: src/rougail/user_data_bitwarden/data.py:83
msgid "please unlock Bitwarden password database"
msgstr "veuillez déverrouiller la base de donnée de mot de passe Bitwarden"
#: src/rougail/user_data_bitwarden/data.py:84
msgid "cannot find Bitwarden command (rbw or bw) please install it"
msgstr "ne peut trouver la commande Bitwarden (rbw ou bw) veuillez l'installer"
#: src/rougail/user_data_bitwarden/data.py:126
msgid ""
"the value for \"{0}\" at index {1} is already set while it should be filled "
"in by Bitwarden"
msgstr ""
"la valeur de \"{0}\" à l'index {1} est déjà renseigné alors que celle doit "
"devrait être renseigné par Bitwarden"
#: src/rougail/user_data_bitwarden/data.py:128
msgid ""
"the value for \"{0}\" is already set while it should be filled in by "
"Bitwarden"
msgstr ""
"la valeur de \"{0}\" est déjà renseignée alors que celle doit devrait être "
"renseignée par Bitwarden"
#: src/rougail/user_data_bitwarden/data.py:157
msgid "the default value for \"{0}\" must be the Bitwarden item name"
msgstr ""
"la valeur par défaut pour \"{0}\" doit être un nom d'élément de Bitwarden"
#: src/rougail/user_data_bitwarden/data.py:166
msgid "cannot execute the \"{0}\" commandline from Bitwarden for \"{1}\": {2}"
msgstr ""
"ne peut exécuter la ligne de commande \"{0}\" pour Bitwarden pour \"{1}\": "
"{2}"
#: src/rougail/user_data_bitwarden/data.py:169
msgid "cannot find {0} \"{1}\" from Bitwarden for \"{2}\""
msgstr "ne peut trouver {0} \"{1}\" pour Bitwarden pour \"{2}\""
#: src/rougail/user_data_bitwarden/data.py:176
msgid ""
"several items found with name \"{0}\" from Bitwarden for \"{1}\": \"{2}\""
msgstr ""
"plusieurs éléments trouvés avec le nom \"{0}\" pour Bitwarden pour \"{1}\": "
"\"{2}\""
#: src/rougail/user_data_bitwarden/data.py:186
msgid "unexpected datas \"{0}\" from Bitwarden for \"{1}\": {2}"
msgstr "données inattendues \"{0}\" pour Bitwarden pour \"{1}\": {2}"
#~ msgid ""
#~ "only \"unix_user\" or \"secret\" variable type can have \"bitwarden\" "
#~ "attribute, but \"{0}\" has type \"{1}\""
#~ msgstr ""
#~ "seule une variable de type \"unix_user\" ou \"secret\" peuvent avoir "
#~ "l'attribut \"bitwarden\", mais \"{0}\" a le type \"{1}\""
#~ msgid ""
#~ "the variable \"{0}\" has attribute \"bitwarden\" but is a multi variable"
#~ msgstr ""
#~ "la variable \"{0}\" a un attribut \"bitwarden\" mais est une variable "
#~ "multiple"
#~ msgid ""
#~ "the variable \"{0}\" is a follower and leader variable (\"{1}\") is also "
#~ "in Bitwarden so this variable could not have default value"
#~ msgstr ""
#~ "la variable \"{0}\" est une suiveuse et la variable leader (\"{1}\") est "
#~ "aussi dans Bitwarden donc cette variable ne peut avoir de valeur par "
#~ "défaut"
#~ msgid "the variable \"{0}\" is in Bitwarden so should have default value"
#~ msgstr ""
#~ "la variable \"{0}\" est dans Bitwarden donc doit avoir une valeur par "
#~ "défaut"
#~ msgid "cannot get {0} \"{1}\" from Bitwarden for \"{2}\": {3} ({4})"
#~ msgstr ""
#~ "ne peut récupérer {0} \"{1}\" pour Bitwarden pour \"{2}\": {3} ({4})"
#~ msgid "cannot load {0} \"{1}\" from Bitwarden for \"{2}\": {3}"
#~ msgstr "ne peut charger {0} \"{1}\" pour Bitwarden pour \"{2}\": {3}"

View file

@ -1,98 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2025-02-12 15:36+0100\n"
"PO-Revision-Date: 2025-02-12 15:39+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 3.5\n"
#: src/rougail/user_data_bitwarden/annotator.py:46
msgid ""
"only \"unix_user\" or \"secret\" variable type can have \"bitwarden\" "
"attribute, but \"{0}\" has type \"{1}\""
msgstr ""
"seule une variable de type \"unix_user\" ou \"secret\" peuvent avoir "
"l'attribut \"bitwarden\", mais \"{0}\" a le type \"{1}\""
#: src/rougail/user_data_bitwarden/annotator.py:49
msgid ""
"the variable \"{0}\" has attribute \"bitwarden\" but is a multi variable"
msgstr ""
"la variable \"{0}\" a un attribut \"bitwarden\" mais est une variable "
"multiple"
#: src/rougail/user_data_bitwarden/annotator.py:58
msgid ""
"the variable \"{0}\" is a follower and leader variable (\"{1}\") is also in "
"Bitwarden so this variable could not have default value"
msgstr ""
"la variable \"{0}\" est une suiveuse et la variable leader (\"{1}\") est "
"aussi dans Bitwarden donc cette variable ne peut avoir de valeur par défaut"
#: src/rougail/user_data_bitwarden/annotator.py:62
msgid "the variable \"{0}\" is in Bitwarden so should have default value"
msgstr ""
"la variable \"{0}\" est dans Bitwarden donc doit avoir une valeur par défaut"
#: src/rougail/user_data_bitwarden/data.py:51
msgid "\"bitwarden\" is not set in step.user_data"
msgstr "\"bitwarden\" n'est pas dans step.user_data"
#: src/rougail/user_data_bitwarden/data.py:70
msgid ""
"the value for \"{0}\" at index {1} is already set while it should be filled "
"in by Bitwarden"
msgstr ""
"la valeur de \"{0}\" à l'index {1} est déjà renseigné alors que celle doit "
"devrait être renseigné par Bitwarden"
#: src/rougail/user_data_bitwarden/data.py:72
msgid ""
"the value for \"{0}\" is already set while it should be filled in by "
"Bitwarden"
msgstr ""
"la valeur de \"{0}\" est déjà renseigné alors que celle doit devrait être "
"renseigné par Bitwarden"
#: src/rougail/user_data_bitwarden/data.py:101
msgid "the default value for \"{0}\" must be the Bitwarden item name"
msgstr ""
"la valeur par défaut pour \"{0}\" doit être un nom d'élément de Bitwarden"
#: src/rougail/user_data_bitwarden/data.py:110
msgid "cannot execute the \"bw\" commandline from Bitwarden for \"{0}\": {1}"
msgstr ""
"ne peut exécuter la ligne de commande \"bw\" pour Bitwarden pour \"{0}\": {1}"
#: src/rougail/user_data_bitwarden/data.py:115
msgid "cannot get {0} \"{1}\" from Bitwarden for \"{2}\": {3} ({4})"
msgstr "ne peut récupérer {0} \"{1}\" pour Bitwarden pour \"{2}\": {3} ({4})"
#: src/rougail/user_data_bitwarden/data.py:120
msgid "cannot load {0} \"{1}\" from Bitwarden for \"{2}\": {3}"
msgstr "ne peut charger {0} \"{1}\" pour Bitwarden pour \"{2}\": {3}"
#: src/rougail/user_data_bitwarden/data.py:123
msgid "cannot find {0} \"{1}\" from Bitwarden for \"{2}\""
msgstr "ne peut trouver {0} \"{1}\" pour Bitwarden pour \"{2}\""
#: src/rougail/user_data_bitwarden/data.py:130
msgid ""
"several items found with name \"{0}\" from Bitwarden for \"{1}\": \"{2}\""
msgstr ""
"plusieurs éléments trouvés avec le nom \"{0}\" pour Bitwarden pour \"{1}\": "
"\"{2}\""
#: src/rougail/user_data_bitwarden/data.py:140
msgid "unexpected datas \"{0}\" from Bitwarden for \"{1}\": {2}"
msgstr "données inattendues \"{0}\" pour Bitwarden pour \"{1}\": {2}"

View file

@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2025-02-12 15:39+0100\n" "POT-Creation-Date: 2025-02-17 09:28+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,59 +15,43 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
#: src/rougail/user_data_bitwarden/annotator.py:46 #: src/rougail/user_data_bitwarden/data.py:52
msgid "only \"unix_user\" or \"secret\" variable type can have \"bitwarden\" attribute, but \"{0}\" has type \"{1}\""
msgstr ""
#: src/rougail/user_data_bitwarden/annotator.py:49
msgid "the variable \"{0}\" has attribute \"bitwarden\" but is a multi variable"
msgstr ""
#: src/rougail/user_data_bitwarden/annotator.py:58
msgid "the variable \"{0}\" is a follower and leader variable (\"{1}\") is also in Bitwarden so this variable could not have default value"
msgstr ""
#: src/rougail/user_data_bitwarden/annotator.py:62
msgid "the variable \"{0}\" is in Bitwarden so should have default value"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:51
msgid "\"bitwarden\" is not set in step.user_data" msgid "\"bitwarden\" is not set in step.user_data"
msgstr "" msgstr ""
#: src/rougail/user_data_bitwarden/data.py:70 #: src/rougail/user_data_bitwarden/data.py:83
msgid "please unlock Bitwarden password database"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:84
msgid "cannot find Bitwarden command (rbw or bw) please install it"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:126
msgid "the value for \"{0}\" at index {1} is already set while it should be filled in by Bitwarden" msgid "the value for \"{0}\" at index {1} is already set while it should be filled in by Bitwarden"
msgstr "" msgstr ""
#: src/rougail/user_data_bitwarden/data.py:72 #: src/rougail/user_data_bitwarden/data.py:128
msgid "the value for \"{0}\" is already set while it should be filled in by Bitwarden" msgid "the value for \"{0}\" is already set while it should be filled in by Bitwarden"
msgstr "" msgstr ""
#: src/rougail/user_data_bitwarden/data.py:101 #: src/rougail/user_data_bitwarden/data.py:157
msgid "the default value for \"{0}\" must be the Bitwarden item name" msgid "the default value for \"{0}\" must be the Bitwarden item name"
msgstr "" msgstr ""
#: src/rougail/user_data_bitwarden/data.py:110 #: src/rougail/user_data_bitwarden/data.py:166
msgid "cannot execute the \"bw\" commandline from Bitwarden for \"{0}\": {1}" msgid "cannot execute the \"{0}\" commandline from Bitwarden for \"{1}\": {2}"
msgstr "" msgstr ""
#: src/rougail/user_data_bitwarden/data.py:115 #: src/rougail/user_data_bitwarden/data.py:169
msgid "cannot get {0} \"{1}\" from Bitwarden for \"{2}\": {3} ({4})"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:120
msgid "cannot load {0} \"{1}\" from Bitwarden for \"{2}\": {3}"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:123
msgid "cannot find {0} \"{1}\" from Bitwarden for \"{2}\"" msgid "cannot find {0} \"{1}\" from Bitwarden for \"{2}\""
msgstr "" msgstr ""
#: src/rougail/user_data_bitwarden/data.py:130 #: src/rougail/user_data_bitwarden/data.py:176
msgid "several items found with name \"{0}\" from Bitwarden for \"{1}\": \"{2}\"" msgid "several items found with name \"{0}\" from Bitwarden for \"{1}\": \"{2}\""
msgstr "" msgstr ""
#: src/rougail/user_data_bitwarden/data.py:140 #: src/rougail/user_data_bitwarden/data.py:186
msgid "unexpected datas \"{0}\" from Bitwarden for \"{1}\": {2}" msgid "unexpected datas \"{0}\" from Bitwarden for \"{1}\": {2}"
msgstr "" msgstr ""

View file

@ -1,66 +0,0 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2025
distribued with GPL-2 or later license
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
from rougail.error import DictConsistencyError
from rougail.annotator.variable import Walk
from .i18n import _
class Annotator(Walk):
"""Annotate for bitwarden"""
level = 95
def __init__(
self,
objectspace,
*args, # pylint: disable=unused-argument
) -> None:
if not objectspace.paths:
return
self.objectspace = objectspace
self.check_variable()
def check_variable(self):
for variable in self.get_variables():
if not variable.bitwarden:
continue
path = variable.path
if variable.type not in ["unix_user", "secret"]:
msg = _('only "unix_user" or "secret" variable type can have "bitwarden" attribute, but "{0}" has type "{1}"')
raise DictConsistencyError(msg.format(path, variable.type), 301, variable.xmlfiles)
if variable.multi and path not in self.objectspace.leaders:
msg = _('the variable "{0}" has attribute "bitwarden" but is a multi variable')
raise DictConsistencyError(msg.format(path), 302, variable.xmlfiles)
check_default_value = True
if path in self.objectspace.followers:
leadership = path.rsplit('.', 1)[0]
leader_path = self.objectspace.parents[leadership][0]
leader = self.objectspace.paths[leader_path]
if leader.bitwarden:
if variable.default:
msg = _('the variable "{0}" is a follower and leader variable ("{1}") is also in Bitwarden so this variable could not have default value')
raise DictConsistencyError(msg.format(path, leader_path), 303, variable.xmlfiles)
check_default_value = False
if check_default_value and not variable.default:
msg = _('the variable "{0}" is in Bitwarden so should have default value')
raise DictConsistencyError(msg.format(path), 304, variable.xmlfiles)
self.objectspace.informations.add(path, "bitwarden", True)
self.objectspace.properties.add(path, "novalidator", True)

View file

@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from subprocess import run from subprocess import run
from json import loads from json import loads
from os import environ from os import environ
from shutil import which
from rougail.error import ExtentionError from rougail.error import ExtentionError
@ -52,6 +53,36 @@ class RougailUserDataBitwarden:
self.errors = [] self.errors = []
self.warnings = [] self.warnings = []
self.leader_informations = {} self.leader_informations = {}
bitwarden_command_line = None
one_is_find = False
if which('rbw'):
one_is_find = True
try:
cpe = run(['rbw', 'unlocked'], capture_output=True)
except Exception as exc:
pass
else:
if cpe.returncode == 0:
bitwarden_command_line = 'rbw'
if bitwarden_command_line is None and which('bw'):
one_is_find = True
try:
cpe = run(['bw', 'status'], capture_output=True)
except Exception as exc:
pass
else:
if cpe.returncode == 0:
try:
data = loads(cpe.stdout.decode('utf8'))
if data["status"] == "unlocked":
bitwarden_command_line = 'bw'
except:
pass
if bitwarden_command_line is None:
if one_is_find:
raise ExtentionError(_('please unlock Bitwarden password database'))
raise ExtentionError(_('cannot find Bitwarden command (rbw or bw) please install it'))
self.bitwarden_command_line = bitwarden_command_line
def run(self): def run(self):
self.set_passwords(self.config) self.set_passwords(self.config)
@ -59,6 +90,31 @@ class RougailUserDataBitwarden:
'warnings': self.warnings, 'warnings': self.warnings,
} }
def run_commandline(self, cmd) -> str:
cpe = run(cmd, capture_output=True)
err = cpe.stderr.decode('utf8')
if cpe.returncode != 0 or err:
raise Exception('{0} ({1})'.format(err, cpe.returncode))
return cpe.stdout.decode('utf8')
def get_key_from_commandline(self, key_bitwarden: str, allow_multiple: bool) -> list[str]:
if self.bitwarden_command_line == 'rbw':
if allow_multiple:
keys = []
items = self.run_commandline(["rbw", "search", key_bitwarden]).strip()
for item in items.split('\n'):
if item.count('@') != 1:
continue
keys.append(item.split('@', 1)[-1])
else:
keys = [key_bitwarden]
datas = []
for key in keys:
data = loads(self.run_commandline(["rbw", "get", key, '--raw']).strip())
datas.append({'name': key, 'login': data["data"]})
return datas
return loads(self.run_commandline(["bw", "list", "items", "--search", key_bitwarden, '--nointeraction']))
def set_passwords(self, optiondescription): def set_passwords(self, optiondescription):
for option in optiondescription: for option in optiondescription:
if option.isoptiondescription(): if option.isoptiondescription():
@ -105,19 +161,9 @@ class RougailUserDataBitwarden:
return 'Ex4mpL3_P4ssw0rD' return 'Ex4mpL3_P4ssw0rD'
return 'example_login' return 'example_login'
try: try:
cpe = run(["bw", "list", "items", "--search", key_bitwarden, '--nointeraction'], capture_output=True) data = self.get_key_from_commandline(key_bitwarden, allow_multiple)
except Exception as exc: except Exception as exc:
self.errors.append(_('cannot execute the "bw" commandline from Bitwarden for "{0}": {1}').format(path, exc)) self.errors.append(_('cannot execute the "{0}" commandline from Bitwarden for "{1}": {2}').format(self.bitwarden_command_line, path, exc))
return None, None
out = cpe.stdout.decode('utf8')
err = cpe.stderr.decode('utf8')
if cpe.returncode != 0 or err:
self.errors.append(_('cannot get {0} "{1}" from Bitwarden for "{2}": {3} ({4})').format(type_, key_bitwarden, path, err, cpe.returncode))
return None, None
try:
data = loads(out)
except Exception as exc:
self.errors.append(_('cannot load {0} "{1}" from Bitwarden for "{2}": {3}').format(type_, key_bitwarden, path, exc))
return None, None return None, None
if not data: if not data:
self.errors.append(_('cannot find {0} "{1}" from Bitwarden for "{2}"').format(type_, key_bitwarden, path)) self.errors.append(_('cannot find {0} "{1}" from Bitwarden for "{2}"').format(type_, key_bitwarden, path))