feat: improvment + Calculation with hidden

This commit is contained in:
egarette@silique.fr 2025-12-23 09:45:29 +01:00
parent 61fd155025
commit 7fe91e9eff
19 changed files with 159 additions and 155 deletions

View file

@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2025-12-20 20:33+0100\n"
"PO-Revision-Date: 2025-12-20 20:33+0100\n"
"POT-Creation-Date: 2025-12-23 09:42+0100\n"
"PO-Revision-Date: 2025-12-23 09:44+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
@ -47,57 +47,53 @@ msgstr "Ligne de commande doit être dans le PATH"
msgid "Simulate password generation instead of retrieve them"
msgstr "Simuler la génération de mots de passe au lieu de les récupérer"
#: src/rougail/user_data_bitwarden/data.py:132
#: src/rougail/user_data_bitwarden/data.py:141
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:169
#: src/rougail/user_data_bitwarden/data.py:178
msgid "please unlock Bitwarden password database with {0} command line"
msgstr ""
"veuillez déverrouiller la base de mot de passe Bitwarden avec la ligne de "
"commande {0}"
#: src/rougail/user_data_bitwarden/data.py:171
#: src/rougail/user_data_bitwarden/data.py:180
msgid "cannot find Bitwarden command line {0} please install it"
msgstr ""
"ne peut trouver la ligne de commande Bitwarden {0} veuillez l'installer"
#: src/rougail/user_data_bitwarden/data.py:208
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:198
msgid "the default value must be the Bitwarden item name"
msgstr "la valeur par défaut doit être un nom d'élément de Bitwarden"
#: src/rougail/user_data_bitwarden/data.py:219
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:210
msgid "cannot execute the \"{0}\" commandline from Bitwarden: {1}"
msgstr "ne peut exécuter la ligne de commande \"{0}\" pour Bitwarden : {1}"
#: src/rougail/user_data_bitwarden/data.py:217
msgid "item \"{0}\" in Bitwarden is not found\""
msgstr "l'élément \"{0}\" dans Bitwarden n'est pas trouvé"
#: src/rougail/user_data_bitwarden/data.py:228
msgid "item \"{0}\" in Bitwarden is not found for \"{1}\""
msgstr "l'élément \"{0}\" dans Bitwarden n'est pas trouvé pour \"{1}\""
msgid "several items found with name \"{0}\" in Bitwarden: \"{1}\""
msgstr ""
"plusieurs éléments trouvés avec le nom \"{0}\" dans Bitwarden : \"{1}\""
#: src/rougail/user_data_bitwarden/data.py:243
msgid "several items found with name \"{0}\" in Bitwarden for \"{1}\": \"{2}\""
msgstr ""
"plusieurs éléments trouvés avec le nom \"{0}\" dans Bitwarden pour \"{1}\": "
"\"{2}\""
msgid "unexpected datas \"{0}\" from Bitwarden: {1}"
msgstr "données inattendues \"{0}\" pour Bitwarden : {1}"
#: src/rougail/user_data_bitwarden/data.py:262
msgid "unexpected datas \"{0}\" from Bitwarden for \"{1}\": {2}"
msgstr "données inattendues \"{0}\" pour Bitwarden pour \"{1}\": {2}"
#: src/rougail/user_data_bitwarden/data.py:270
#: src/rougail/user_data_bitwarden/data.py:249
msgid "password"
msgstr "de mot de passe"
#: src/rougail/user_data_bitwarden/data.py:272
#: src/rougail/user_data_bitwarden/data.py:251
msgid "username"
msgstr "de nom d'utilisateur"
#: src/rougail/user_data_bitwarden/data.py:274
msgid "item \"{0}\" in Bitwarden has no {1} for \"{2}\""
msgstr "l'élément \"{0}\" dans Bitwarden n'a pas {1} for \"{2}\""
#: src/rougail/user_data_bitwarden/data.py:253
msgid "item \"{0}\" in Bitwarden has no {1}"
msgstr "l'élément \"{0}\" dans Bitwarden n'a pas {1}"
#~ msgid "Configuration rougail-user-data-bitwarden"
#~ msgstr "Configuration de rougail-user-data-bitwarden"

View file

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2025-12-20 20:33+0100\n"
"POT-Creation-Date: 2025-12-23 09:44+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -39,47 +39,47 @@ msgstr ""
msgid "Simulate password generation instead of retrieve them"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:132
#: src/rougail/user_data_bitwarden/data.py:141
msgid "\"bitwarden\" is not set in step.user_data"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:169
#: src/rougail/user_data_bitwarden/data.py:178
msgid "please unlock Bitwarden password database with {0} command line"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:171
#: src/rougail/user_data_bitwarden/data.py:180
msgid "cannot find Bitwarden command line {0} please install it"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:208
msgid "the default value for \"{0}\" must be the Bitwarden item name"
#: src/rougail/user_data_bitwarden/data.py:198
msgid "the default value must be the Bitwarden item name"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:219
msgid "cannot execute the \"{0}\" commandline from Bitwarden for \"{1}\": {2}"
#: src/rougail/user_data_bitwarden/data.py:210
msgid "cannot execute the \"{0}\" commandline from Bitwarden: {1}"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:217
msgid "item \"{0}\" in Bitwarden is not found\""
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:228
msgid "item \"{0}\" in Bitwarden is not found for \"{1}\""
msgid "several items found with name \"{0}\" in Bitwarden: \"{1}\""
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:243
msgid "several items found with name \"{0}\" in Bitwarden for \"{1}\": \"{2}\""
msgid "unexpected datas \"{0}\" from Bitwarden: {1}"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:262
msgid "unexpected datas \"{0}\" from Bitwarden for \"{1}\": {2}"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:270
#: src/rougail/user_data_bitwarden/data.py:249
msgid "password"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:272
#: src/rougail/user_data_bitwarden/data.py:251
msgid "username"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:274
msgid "item \"{0}\" in Bitwarden has no {1} for \"{2}\""
#: src/rougail/user_data_bitwarden/data.py:253
msgid "item \"{0}\" in Bitwarden has no {1}"
msgstr ""

View file

@ -44,5 +44,7 @@ class Annotator(Walk):
path = variable.path
self.objectspace.informations.add(path, "bitwarden", True)
self.objectspace.informations.add(path, "default_value_makes_sense", False)
if "force_default_on_freeze" in self.objectspace.properties.get(path):
self.objectspace.properties.remove(path, "force_default_on_freeze")
self.objectspace.properties.add(path, "novalidator", True)
variable.default = variable.secret_manager

View file

@ -34,7 +34,7 @@ class FakeBW:
def get_key(
self,
key_bitwarden: str,
multiple: bool,
leader: bool,
):
return [{'name': key_bitwarden, 'login': {'username': "example_login", 'password': "Ex4mpL3_P4ssw0rD"}}]
@ -50,7 +50,7 @@ class RBW:
def get_key(
self,
key_bitwarden: str,
multiple: bool,
leader: bool,
):
keys = []
items = run_commandline(["rbw", "search", key_bitwarden]).strip()
@ -63,14 +63,14 @@ class RBW:
keys.append(item.split("@", 1)[-1])
else:
keys.append(item.rsplit("/", 1)[-1])
if not multiple:
if not leader:
if not items:
return []
if len(items) > 1:
return [{"name": key} for key in keys]
keys = [key_bitwarden]
datas = []
for key in keys:
for key in sorted(keys):
data = loads(
run_commandline(
["rbw", "get", key, "--raw", "--ignorecase"]
@ -108,6 +108,15 @@ class BW:
)
def run_commandline(cmd) -> str:
cpe = run(cmd, capture_output=True)
returncode = cpe.returncode
err = cpe.stderr.decode("utf8")
if returncode != 0 or err:
raise ExtensionError("{0} ({1})".format(err, returncode))
return cpe.stdout.decode("utf8")
class RougailUserDataBitwarden:
def __init__(
self,
@ -132,7 +141,7 @@ class RougailUserDataBitwarden:
raise ExtensionError(_('"bitwarden" is not set in step.user_data'))
self.errors = []
self.warnings = []
self.leader_informations = {}
self.cache = {}
self.commands = {'rbw': RBW(),
'bw': BW(),
}
@ -176,69 +185,54 @@ class RougailUserDataBitwarden:
if option.isoptiondescription():
self.set_passwords(option, values)
elif option.information.get("bitwarden", False):
values[option.path(uncalculated=True)] = (set_password, self.leader_informations, self.command)
values[option.path(uncalculated=True)] = (set_password, self.cache, self.command)
def set_password(leader_informations, command, *, option):
path = option.path()
leader_key = None
if option.isfollower():
leader_path = option.parent().leader().path()
if leader_path in leader_informations:
leader_key = leader_informations[leader_path][option.index()]
if not option.isleader():
return get_values(command, option, leader_key=leader_key)
leader_informations[path], option_values = get_values(
command, option, multiple=True, leader_key=option.value.default()[0]
)
return option_values
def get_values(command, option, *, multiple=False, leader_key=None):
if leader_key:
key = leader_key
else:
key = option.value.default()
type_ = option.information.get("type")
if "validator" not in option.property.get():
option.property.add("validator")
def set_password(cache, command, *, option):
key = option.value.default()
leader = option.isleader()
if leader:
key = key[0]
if not isinstance(key, str):
raise ConfigError(
_('the default value must be the Bitwarden item name')
)
try:
data = command.get_key(key, multiple)
except Exception as exc:
raise ConfigError(
_(
'cannot execute the "{0}" commandline from Bitwarden: {1}'
).format(command, exc)
)
if key in cache:
data = cache[key]
if option.isfollower():
data = [data[option.index()]]
key = data[0]["name"]
else:
try:
data = command.get_key(key, leader)
except Exception as exc:
raise ConfigError(
_(
'cannot execute the "{0}" commandline from Bitwarden: {1}'
).format(command, exc)
)
cache[key] = data.copy()
if not data:
raise ConfigError(
_('item "{0}" in Bitwarden is not found"').format(
key
)
)
if len(data) != 1:
names = [d["name"] for d in data]
if multiple:
ret = []
return names, [
get_value(key, type_, d) for d in data
]
type_ = option.information.get("type")
if leader:
return [
get_value(key, type_, d) for d in data
]
elif len(data) != 1:
raise ConfigError(
_(
'several items found with name "{0}" in Bitwarden: "{1}"'
).format(key, '", "'.join(names))
).format(key, '", "'.join([d["name"] for d in data]))
)
value = get_value(key, type_, data[0])
if multiple:
return [data[0]["name"]], [value]
return value
return get_value(key, type_, data[0])
def get_value( key_bitwarden: str, type_: str, data: dict) -> str:
def get_value(key_bitwarden: str, type_: str, data: dict) -> str:
try:
if type_ == "secret":
value = data["login"]["password"]
@ -246,28 +240,18 @@ def get_value( key_bitwarden: str, type_: str, data: dict) -> str:
value = data["login"]["username"]
except Exception as exc:
raise ConfigError(
_('unexpected datas "{0}" from Bitwarden: {2}').format(
_('unexpected datas "{0}" from Bitwarden: {1}').format(
key_bitwarden, exc
)
)
else:
if value is None:
if type_ == "secret":
bw_type = _("password")
else:
bw_type = _("username")
raise ConfigError(
_('item "{0}" in Bitwarden has no {1}').format(
key_bitwarden, bw_type
)
if value is None:
if type_ == "secret":
bw_type = _("password")
else:
bw_type = _("username")
raise ConfigError(
_('item "{0}" in Bitwarden has no {1}').format(
key_bitwarden, bw_type
)
)
return value
def run_commandline(cmd) -> str:
cpe = run(cmd, capture_output=True)
returncode = cpe.returncode
err = cpe.stderr.decode("utf8")
if returncode != 0 or err:
raise ExtensionError("{0} ({1})".format(err, returncode))
return cpe.stdout.decode("utf8")

View file

@ -1,6 +1,4 @@
{
"errors": [
"item \"1_secret_unknown - environment - service - user\" in Bitwarden is not found for \"rougail.secret\""
],
"errors": [],
"warnings": []
}

View file

@ -1,3 +1 @@
{
"rougail.secret": null
}
"item \"1_secret_unknown - environment - service - user\" in Bitwarden is not found\""

View file

@ -1,12 +0,0 @@
{
"rougail.leader.username": [
{
"rougail.leader.username": "bitwarden_username_2",
"rougail.leader.secret": "bitwarden_password_2"
},
{
"rougail.leader.username": "bitwarden_username",
"rougail.leader.secret": "bitwarden_password"
}
]
}

View file

@ -1,6 +1,4 @@
{
"errors": [
"several items found with name \"4_several_secrets - environment - service - user\" in Bitwarden for \"rougail.secret\": \"4_several_secrets - environment - service - user_1\", \"4_several_secrets - environment - service - user_2\""
],
"errors": [],
"warnings": []
}

View file

@ -1,3 +1 @@
{
"rougail.secret": null
}
"several items found with name \"4_several_secrets - environment - service - user\" in Bitwarden: \"4_several_secrets - environment - service - user_1\", \"4_several_secrets - environment - service - user_2\""

View file

@ -1,6 +1,4 @@
{
"errors": [
"several items found with name \"3_leadership_secret - ENVIRONMENT - SERVICE - USER\" in Bitwarden for \"rougail.secret\": \"3_leadership_secret - environment - service - user_1\", \"3_leadership_secret - environment - service - user_2\""
],
"errors": [],
"warnings": []
}

View file

@ -1,3 +1 @@
{
"rougail.secret": null
}
"several items found with name \"3_leadership_secret - ENVIRONMENT - SERVICE - USER\" in Bitwarden: \"3_leadership_secret - environment - service - user_1\", \"3_leadership_secret - environment - service - user_2\""

View file

@ -1,4 +1,8 @@
{
"rougail.host": "test",
"rougail.project": "5_secret_calc",
"rougail.environment": "environment",
"rougail.service": "service",
"rougail.modified_variable_single": "user_2",
"rougail.secret": "bitwarden_password_2"
}

View file

@ -1,6 +1,4 @@
{
"errors": [
"item \"6_no_username - environment - service - user_1\" in Bitwarden has no username for \"rougail.username\""
],
"errors": [],
"warnings": []
}

View file

@ -1,4 +1 @@
{
"rougail.username": null,
"rougail.secret": "ET6RrpdZv5bNZu"
}
"item \"6_no_username - environment - service - user_1\" in Bitwarden has no username"

View file

@ -0,0 +1,4 @@
{
"errors": [],
"warnings": []
}

View file

@ -0,0 +1,4 @@
{
"rougail.username": "example_login",
"rougail.secret": "Ex4mpL3_P4ssw0rD"
}

View file

@ -0,0 +1,23 @@
---
version: 1.1
username:
description: the username
type: unix_user
secret_manager:
host: test
project: 2_username_secret
environment: environment
service: service
user: user_1
secret:
description: the secret
type: secret
secret_manager:
host: test
project: 2_username_secret
environment: environment
service: service
user: user_1
hidden: true

View file

@ -8,6 +8,7 @@ from json import load, dump
#########################
from pytest import raises
from unittest import mock
from tiramisu.error import ConfigError
from rougail_tests.utils import config_to_dict
@ -33,7 +34,10 @@ def _test_structural_files(test_dir, command, *, env=False, modified=False, mock
generated_user_data.insert(0, {'source': 'By Hand', 'errors': [], 'warnings': [], 'values': {'rougail.modified_variable': ['user_1', 'user_2'], 'rougail.modified_variable_single': 'user_2'}})
errors = rougail.user_data(generated_user_data)
#expected output
config_dict = dict(config_to_dict(config.value.get()))
try:
config_dict = dict(config_to_dict(config.value.get()))
except (ConfigError, ValueError) as err:
config_dict = str(err)
if not env:
base_filename = 'bitwarden.json'
else:
@ -50,6 +54,10 @@ def _test_structural_files(test_dir, command, *, env=False, modified=False, mock
errors_file = Path('tests') / 'results' / test_dir.name / 'errors' / (base_filename + '.' + command)
if not errors_file.is_file():
errors_file = Path('tests') / 'results' / test_dir.name / 'errors' / base_filename
for l, data in errors.items():
for i, d in enumerate(data):
if isinstance(d, dict):
data[i] = list(d)
if not errors_file.is_file():
errors_file.parent.mkdir(parents=True, exist_ok=True)
with open(errors_file, 'a') as json_file:
@ -186,6 +194,14 @@ def test_structural_files_6_no_username_bw():
_test_structural_files(test_dir / '6_no_username', 'bw')
def test_structural_files_7_secret_calc_rbw():
_test_structural_files(test_dir / '7_secret_hidden', 'rbw', env=True, mock=True)
def test_structural_files_7_secret_hidden_bw():
_test_structural_files(test_dir / '7_secret_hidden', 'bw', env=True, mock=True)
def test_structural_files_8_multi_variable_rbw():
"tests the output"
with raises(DictConsistencyError) as err: