feat: doc
This commit is contained in:
parent
88e7548fa4
commit
a86a8bdc28
10 changed files with 391 additions and 289 deletions
21
README.fr.md
Normal file
21
README.fr.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
[🇬🇧 (EN)](README.md) - [🇫🇷 (FR)](README.fr.md)
|
||||
|
||||
## Les secrets sont dans Bitwarden
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Charge les secrets depuis Bitwarden. Les données sont récupérées à l'aide de la commande officielle « bw » ou de la commande alternative « rbw » (plus rapide). Avant d'utiliser Rougail, la commande doit être connectée et déverrouillée.\
|
||||
> **Chemin** : bitwarden\
|
||||
> *`désactivé`*\
|
||||
> **Désactivé** : si bitwarden n'est pas dans "[Sélection pour données utilisateur](#step.user_data)"
|
||||
|
||||
| Variable | Description | Valeur par défaut | Type | Validateur |
|
||||
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|--------------------------|-------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|
|
||||
| **<a id="bitwarden.command" name="bitwarden.command">bitwarden.command</a>**<br/>**Ligne de commande** : --bitwarden.command<br/>**Variable d'environnement** : BITWARDEN.COMMAND | Ligne de commande utilisée pour récupérer les secrets.<br/>Ligne de commande doit être dans le PATH. | • rbw<br/>• bw | [`choice`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `multiple` `obligatoire` | `unique`<br/>**Choix** : <br/>• rbw<br/>• bw |
|
||||
| **<a id="bitwarden.mock_enable" name="bitwarden.mock_enable">bitwarden.mock_enable</a>**<br/>**Ligne de commande** : <br/>• --bitwarden.mock_enable<br/>• --bitwarden.no-mock_enable<br/>**Variable d'environnement** : BITWARDEN.MOCK_ENABLE | Simuler la génération de mots de passe au lieu de les récupérer. | false | [`boolean`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `obligatoire` | |
|
||||
|
||||
|
||||
21
README.md
21
README.md
|
|
@ -1,2 +1,21 @@
|
|||
# rougail-user-data-bitwarden
|
||||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
[🇬🇧 (EN)](README.md) - [🇫🇷 (FR)](README.fr.md)
|
||||
|
||||
## Secrets are in Bitwarden
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Load the secrets from Bitwarden. The data is retrieved using the official command line "bw" or the alternative command "rbw" (faster). Before using Rougail, the command must be logged and unlocked.\
|
||||
> **Path**: bitwarden\
|
||||
> *`disabled`*\
|
||||
> **Disabled**: if bitwarden is not set in "[Select for user datas](#step.user_data)"
|
||||
|
||||
| Variable | Description | Default value | Type | Validator |
|
||||
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|--------------------------|-----------------------------------------------------------------------------------------------------------|---------------------------------------------------------|
|
||||
| **<a id="bitwarden.command" name="bitwarden.command">bitwarden.command</a>**<br/>**Command line**: --bitwarden.command<br/>**Environment variable**: BITWARDEN.COMMAND | Command line used to retrieve secrets.<br/>Command line must be in PATH. | • rbw<br/>• bw | [`choice`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `multiple` `mandatory` | `unique`<br/>**Choices**: <br/>• rbw<br/>• bw |
|
||||
| **<a id="bitwarden.mock_enable" name="bitwarden.mock_enable">bitwarden.mock_enable</a>**<br/>**Command line**: <br/>• --bitwarden.mock_enable<br/>• --bitwarden.no-mock_enable<br/>**Environment variable**: BITWARDEN.MOCK_ENABLE | Simulate password generation instead of retrieve them. | false | [`boolean`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `mandatory` | |
|
||||
|
||||
|
||||
|
|
|
|||
44
doc.py
Executable file
44
doc.py
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python3
|
||||
from rougail import Rougail
|
||||
from rougail.config import get_common_rougail_config, get_rougail_config
|
||||
from rougail.output_doc import RougailOutputDoc
|
||||
from rougail.user_data_bitwarden.config import get_rougail_config as local_get_rougail_config
|
||||
|
||||
|
||||
def to_yaml(options):
|
||||
return """%YAML 1.2
|
||||
---
|
||||
version: 1.1
|
||||
""" + options + "..."
|
||||
|
||||
backward_compatibility = False
|
||||
rougailconfig = get_rougail_config(backward_compatibility=backward_compatibility)
|
||||
rougailconfig['force_optional'] = True
|
||||
rougailconfig['step.output'] = "doc"
|
||||
# rougailconfig['tiramisu_cache'] = "a.py"
|
||||
rougailconfig['step.structural'] = ["commandline", "string"]
|
||||
rougailconfig['main_namespace'] = None
|
||||
rougailconfig["doc.root"] = "bitwarden"
|
||||
rougailconfig["doc.title_level"] = 2
|
||||
rougailconfig["doc.tabulars.with_commandline"] = True
|
||||
rougailconfig["doc.tabulars.with_environment"] = True
|
||||
rougailconfig["doc.tabulars.environment_prefix"] = "ROUGAILCLI"
|
||||
rougailconfig["doc.output_format"] = "github"
|
||||
rougailconfig['doc.tabular_template'] = 'six_columns'
|
||||
rougail_config = to_yaml(get_common_rougail_config(backward_compatibility=backward_compatibility)[2])
|
||||
module_config = to_yaml(local_get_rougail_config(backward_compatibility=False)['options'])
|
||||
rougailconfig['main_structural_strings'] = [rougail_config, module_config]
|
||||
rougail = Rougail(rougailconfig)
|
||||
config = rougail.run()
|
||||
config.property.read_write()
|
||||
output = RougailOutputDoc(
|
||||
config=config,
|
||||
rougailconfig=rougailconfig,
|
||||
)
|
||||
print("""---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
[🇬🇧 (EN)](README.md) - [🇫🇷 (FR)](README.fr.md)
|
||||
""")
|
||||
output.print()
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2025-11-05 08:24+0100\n"
|
||||
"PO-Revision-Date: 2025-11-05 08:24+0100\n"
|
||||
"POT-Creation-Date: 2025-12-20 20:33+0100\n"
|
||||
"PO-Revision-Date: 2025-12-20 20:33+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr\n"
|
||||
|
|
@ -14,74 +14,100 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 3.7\n"
|
||||
"X-Generator: Poedit 3.8\n"
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:46
|
||||
msgid "Configuration rougail-user-data-bitwarden"
|
||||
msgstr "Configuration de rougail-user-data-bitwarden"
|
||||
#: src/rougail/user_data_bitwarden/config.py:30
|
||||
msgid "Secrets are in Bitwarden"
|
||||
msgstr "Les secrets sont dans Bitwarden"
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:54
|
||||
msgid "Application used to retrieve secrets"
|
||||
msgstr "Application utilisé pour récupérer des secrets"
|
||||
#: src/rougail/user_data_bitwarden/config.py:32
|
||||
msgid ""
|
||||
"Load the secrets from Bitwarden. The data is retrieved using the official "
|
||||
"command line \"bw\" or the alternative command \"rbw\" (faster). Before "
|
||||
"using Rougail, the command must be logged and unlocked."
|
||||
msgstr ""
|
||||
"Charge les secrets depuis Bitwarden. Les données sont récupérées à l'aide de "
|
||||
"la commande officielle « bw » ou de la commande alternative « rbw » (plus "
|
||||
"rapide). Avant d'utiliser Rougail, la commande doit être connectée et "
|
||||
"déverrouillée."
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:53
|
||||
#: src/rougail/user_data_bitwarden/config.py:37
|
||||
msgid "if bitwarden is not set in \"_.step.user_data\""
|
||||
msgstr "si bitwarden n'est pas dans \"_.step.user_data\""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:40
|
||||
msgid "Command line used to retrieve secrets"
|
||||
msgstr "Ligne de commande utilisée pour récupérer les secrets"
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:41
|
||||
msgid "Command line must be in PATH"
|
||||
msgstr "Ligne de commande doit être dans le PATH"
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:49
|
||||
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
|
||||
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:75
|
||||
msgid "\"rbw\" or \"bw\""
|
||||
msgstr "\"rbw\" ou \"bw\""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:77
|
||||
msgid "\"{0}\""
|
||||
msgstr "\"{0}\""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:80
|
||||
msgid "please unlock Bitwarden password database with {0}"
|
||||
#: src/rougail/user_data_bitwarden/data.py:169
|
||||
msgid "please unlock Bitwarden password database with {0} command line"
|
||||
msgstr ""
|
||||
"veuillez déverrouiller la base de donnée de mot de passe Bitwarden avec {0}"
|
||||
"veuillez déverrouiller la base de mot de passe Bitwarden avec la ligne de "
|
||||
"commande {0}"
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:85
|
||||
msgid "cannot find Bitwarden command {0} please install it"
|
||||
msgstr "ne peut trouver la commande Bitwarden {0} veuillez l'installer"
|
||||
#: src/rougail/user_data_bitwarden/data.py:171
|
||||
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:163
|
||||
#: 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:186
|
||||
#: 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:195
|
||||
#: 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}\""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:210
|
||||
#: 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}\""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:229
|
||||
#: 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:237
|
||||
#: src/rougail/user_data_bitwarden/data.py:270
|
||||
msgid "password"
|
||||
msgstr "de mot de passe"
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:239
|
||||
#: src/rougail/user_data_bitwarden/data.py:272
|
||||
msgid "username"
|
||||
msgstr "de nom d'utilisateur"
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:241
|
||||
#: 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}\""
|
||||
|
||||
#~ msgid "Configuration rougail-user-data-bitwarden"
|
||||
#~ msgstr "Configuration de rougail-user-data-bitwarden"
|
||||
|
||||
#~ msgid "\"rbw\" or \"bw\""
|
||||
#~ msgstr "\"rbw\" ou \"bw\""
|
||||
|
||||
#~ msgid "\"{0}\""
|
||||
#~ msgstr "\"{0}\""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "the value for \"{0}\" at index {1} is already set while it should be "
|
||||
#~ "filled in by Bitwarden"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: 2025-11-05 08:24+0100\n"
|
||||
"POT-Creation-Date: 2025-12-20 20:33+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"
|
||||
|
|
@ -15,63 +15,71 @@ msgstr ""
|
|||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:46
|
||||
msgid "Configuration rougail-user-data-bitwarden"
|
||||
#: src/rougail/user_data_bitwarden/config.py:30
|
||||
msgid "Secrets are in Bitwarden"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:54
|
||||
msgid "Application used to retrieve secrets"
|
||||
#: src/rougail/user_data_bitwarden/config.py:32
|
||||
msgid "Load the secrets from Bitwarden. The data is retrieved using the official command line \"bw\" or the alternative command \"rbw\" (faster). Before using Rougail, the command must be logged and unlocked."
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:53
|
||||
#: src/rougail/user_data_bitwarden/config.py:37
|
||||
msgid "if bitwarden is not set in \"_.step.user_data\""
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:40
|
||||
msgid "Command line used to retrieve secrets"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:41
|
||||
msgid "Command line must be in PATH"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/config.py:49
|
||||
msgid "Simulate password generation instead of retrieve them"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:132
|
||||
msgid "\"bitwarden\" is not set in step.user_data"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:75
|
||||
msgid "\"rbw\" or \"bw\""
|
||||
#: src/rougail/user_data_bitwarden/data.py:169
|
||||
msgid "please unlock Bitwarden password database with {0} command line"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:77
|
||||
msgid "\"{0}\""
|
||||
#: src/rougail/user_data_bitwarden/data.py:171
|
||||
msgid "cannot find Bitwarden command line {0} please install it"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:80
|
||||
msgid "please unlock Bitwarden password database with {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:85
|
||||
msgid "cannot find Bitwarden command {0} please install it"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:163
|
||||
#: src/rougail/user_data_bitwarden/data.py:208
|
||||
msgid "the default value for \"{0}\" must be the Bitwarden item name"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:186
|
||||
#: src/rougail/user_data_bitwarden/data.py:219
|
||||
msgid "cannot execute the \"{0}\" commandline from Bitwarden for \"{1}\": {2}"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:195
|
||||
#: src/rougail/user_data_bitwarden/data.py:228
|
||||
msgid "item \"{0}\" in Bitwarden is not found for \"{1}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:210
|
||||
#: src/rougail/user_data_bitwarden/data.py:243
|
||||
msgid "several items found with name \"{0}\" in Bitwarden for \"{1}\": \"{2}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:229
|
||||
#: 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:237
|
||||
#: src/rougail/user_data_bitwarden/data.py:270
|
||||
msgid "password"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:239
|
||||
#: src/rougail/user_data_bitwarden/data.py:272
|
||||
msgid "username"
|
||||
msgstr ""
|
||||
|
||||
#: src/rougail/user_data_bitwarden/data.py:241
|
||||
#: src/rougail/user_data_bitwarden/data.py:274
|
||||
msgid "item \"{0}\" in Bitwarden has no {1} for \"{2}\""
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
48
src/rougail/user_data_bitwarden/annotator.py
Normal file
48
src/rougail/user_data_bitwarden/annotator.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
"""
|
||||
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
|
||||
|
||||
|
||||
class Annotator(Walk):
|
||||
"""Annotate for bitwarden"""
|
||||
|
||||
level = 90
|
||||
|
||||
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.secret_manager:
|
||||
continue
|
||||
path = variable.path
|
||||
self.objectspace.informations.add(path, "bitwarden", True)
|
||||
self.objectspace.informations.add(path, "default_value_makes_sense", False)
|
||||
self.objectspace.properties.add(path, "novalidator", True)
|
||||
variable.default = variable.secret_manager
|
||||
|
|
@ -26,36 +26,27 @@ def get_rougail_config(
|
|||
*,
|
||||
backward_compatibility=True,
|
||||
) -> dict:
|
||||
options = f"""
|
||||
step:
|
||||
|
||||
structural:
|
||||
redefine: true
|
||||
default:
|
||||
jinja: |-
|
||||
{{% if step.user_data is not propertyerror and 'bitwarden' in step.user_data %}}
|
||||
bitwarden
|
||||
{{% endif %}}
|
||||
{{% if step.user_data is not propertyerror and 'risotto' in step.user_data %}}
|
||||
risotto
|
||||
{{% else %}}
|
||||
directory
|
||||
{{% endif %}}
|
||||
|
||||
bitwarden:
|
||||
description: {_("Configuration rougail-user-data-bitwarden")}
|
||||
options = f"""bitwarden:
|
||||
description: {_("Secrets are in Bitwarden")}
|
||||
help: |-
|
||||
{_("Load the secrets from Bitwarden. The data is retrieved using the official command line \"bw\" or the alternative command \"rbw\" (faster). Before using Rougail, the command must be logged and unlocked.")}
|
||||
disabled:
|
||||
jinja: |
|
||||
{{% if step.user_data is propertyerror or 'bitwarden' not in step.user_data %}}
|
||||
disabled
|
||||
{{% endif %}}
|
||||
jinja: |-
|
||||
{{{{ _.step.user_data is propertyerror or 'bitwarden' not in _.step.user_data }}}}
|
||||
return_type: boolean
|
||||
description: {_('if bitwarden is not set in "_.step.user_data"')}
|
||||
|
||||
command:
|
||||
description: {_("Application used to retrieve secrets")}
|
||||
description: {_("Command line used to retrieve secrets")}
|
||||
help: {_("Command line must be in PATH")}
|
||||
choices:
|
||||
- rbw
|
||||
- bw
|
||||
mandatory: false
|
||||
default:
|
||||
- rbw
|
||||
- bw
|
||||
|
||||
mock_enable: false # {_("Simulate password generation instead of retrieve them")}
|
||||
"""
|
||||
return {
|
||||
"name": "bitwarden",
|
||||
|
|
|
|||
|
|
@ -25,12 +25,90 @@ from os import environ
|
|||
from shutil import which
|
||||
|
||||
|
||||
from tiramisu.error import display_list
|
||||
from rougail.error import ExtensionError
|
||||
from .i18n import _
|
||||
|
||||
|
||||
class RougailUserDataBitwarden:
|
||||
class FakeBW:
|
||||
def get_key(
|
||||
self,
|
||||
key_bitwarden: str,
|
||||
multiple: bool,
|
||||
):
|
||||
return [{'name': key_bitwarden, 'login': {'username': "example_login", 'password': "Ex4mpL3_P4ssw0rD"}}]
|
||||
|
||||
|
||||
class RBW:
|
||||
def unlocked(self):
|
||||
try:
|
||||
cpe = run(["rbw", "unlocked"], capture_output=True)
|
||||
except Exception as exc:
|
||||
return False
|
||||
return cpe.returncode == 0
|
||||
|
||||
def get_key(
|
||||
self,
|
||||
key_bitwarden: str,
|
||||
multiple: bool,
|
||||
):
|
||||
keys = []
|
||||
items = run_commandline(["rbw", "search", key_bitwarden]).strip()
|
||||
if items:
|
||||
items = items.split("\n")
|
||||
else:
|
||||
items = []
|
||||
for item in items:
|
||||
if "@" in item:
|
||||
keys.append(item.split("@", 1)[-1])
|
||||
else:
|
||||
keys.append(item.rsplit("/", 1)[-1])
|
||||
if not multiple:
|
||||
if not items:
|
||||
return []
|
||||
if len(items) > 1:
|
||||
return [{"name": key} for key in keys]
|
||||
keys = [key_bitwarden]
|
||||
datas = []
|
||||
for key in keys:
|
||||
data = loads(
|
||||
run_commandline(
|
||||
["rbw", "get", key, "--raw", "--ignorecase"]
|
||||
).strip()
|
||||
)
|
||||
datas.append({"name": key, "login": data["data"]})
|
||||
return datas
|
||||
|
||||
|
||||
class BW:
|
||||
def unlocked(self):
|
||||
try:
|
||||
cpe = run(["bw", "status"], capture_output=True)
|
||||
except Exception as exc:
|
||||
return False
|
||||
if cpe.returncode != 0:
|
||||
return False
|
||||
try:
|
||||
data = loads(cpe.stdout.decode("utf8"))
|
||||
if data["status"] == "unlocked":
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def get_key(
|
||||
self,
|
||||
key_bitwarden: str,
|
||||
*args,
|
||||
):
|
||||
return loads(
|
||||
run_commandline(
|
||||
["bw", "list", "items", "--search", key_bitwarden, "--nointeraction"]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class RougailUserDataBitwarden:
|
||||
def __init__(
|
||||
self,
|
||||
config: "Config",
|
||||
|
|
@ -47,6 +125,7 @@ class RougailUserDataBitwarden:
|
|||
if "bitwarden" not in user_data:
|
||||
user_data.append("bitwarden")
|
||||
rougailconfig["step.user_data"] = user_data
|
||||
else:
|
||||
user_data = rougailconfig["step.user_data"]
|
||||
self.rougailconfig = rougailconfig
|
||||
if "bitwarden" not in user_data:
|
||||
|
|
@ -54,65 +133,14 @@ class RougailUserDataBitwarden:
|
|||
self.errors = []
|
||||
self.warnings = []
|
||||
self.leader_informations = {}
|
||||
self.bitwarden_command_line = self.get_command(rougailconfig)
|
||||
|
||||
def get_command(self, rougailconfig):
|
||||
if "ROUGAIL_BITWARDEN_MOCK_ENABLE" in environ:
|
||||
return None
|
||||
one_is_find = False
|
||||
force_command = rougailconfig["bitwarden.command"]
|
||||
if force_command:
|
||||
commands = [force_command]
|
||||
else:
|
||||
commands = ["rbw", "bw"]
|
||||
for command in commands:
|
||||
status = self.test_command(command)
|
||||
if status is False:
|
||||
one_is_find = True
|
||||
elif status:
|
||||
return command
|
||||
if not force_command:
|
||||
command_string = _('"rbw" or "bw"')
|
||||
else:
|
||||
command_string = _('"{0}"').format(force_command)
|
||||
if one_is_find:
|
||||
raise ExtensionError(
|
||||
_("please unlock Bitwarden password database with {0}").format(
|
||||
command_string
|
||||
)
|
||||
)
|
||||
raise ExtensionError(
|
||||
_("cannot find Bitwarden command {0} please install it").format(
|
||||
command_string
|
||||
)
|
||||
)
|
||||
|
||||
def test_command(self, command):
|
||||
if not which(command):
|
||||
return None
|
||||
if command == "rbw":
|
||||
cmd = ["rbw", "unlocked"]
|
||||
else:
|
||||
cmd = ["bw", "status"]
|
||||
try:
|
||||
cpe = run(cmd, capture_output=True)
|
||||
except Exception as exc:
|
||||
return False
|
||||
if cpe.returncode != 0:
|
||||
return False
|
||||
if command == "rbw":
|
||||
return True
|
||||
try:
|
||||
data = loads(cpe.stdout.decode("utf8"))
|
||||
if data["status"] == "unlocked":
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
self.commands = {'rbw': RBW(),
|
||||
'bw': BW(),
|
||||
}
|
||||
|
||||
def run(self):
|
||||
values = {}
|
||||
self.set_passwords(self.config.forcepermissive, values)
|
||||
self.command = self.get_command()
|
||||
self.set_passwords(self.config.unrestraint, values)
|
||||
return [
|
||||
{
|
||||
"source": 'Bitwarden',
|
||||
|
|
@ -125,15 +153,33 @@ class RougailUserDataBitwarden:
|
|||
}
|
||||
]
|
||||
|
||||
def get_command(self):
|
||||
if self.rougailconfig["bitwarden.mock_enable"]:
|
||||
return FakeBW()
|
||||
one_is_find = False
|
||||
for command_name in self.rougailconfig["bitwarden.command"]:
|
||||
if not which(command_name):
|
||||
continue
|
||||
command = self.commands[command_name]
|
||||
if not command.unlocked():
|
||||
one_is_find = True
|
||||
continue
|
||||
return command
|
||||
if one_is_find:
|
||||
msg = _("please unlock Bitwarden password database with {0} command line")
|
||||
else:
|
||||
msg = _("cannot find Bitwarden command line {0} please install it")
|
||||
raise ExtensionError(msg.format(display_list(list(self.commands))))
|
||||
|
||||
def set_passwords(self, optiondescription, values):
|
||||
for option in optiondescription.list(uncalculated=True):
|
||||
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.bitwarden_command_line)
|
||||
values[option.path(uncalculated=True)] = (set_password, self.leader_informations, self.command)
|
||||
|
||||
|
||||
def set_password(leader_informations, bitwarden_command_line, *, option, warnings, errors):
|
||||
def set_password(leader_informations, command, *, option, warnings, errors):
|
||||
path = option.path()
|
||||
leader_key = None
|
||||
if option.isfollower():
|
||||
|
|
@ -141,21 +187,20 @@ def set_password(leader_informations, bitwarden_command_line, *, option, warning
|
|||
if leader_path in leader_informations:
|
||||
leader_key = leader_informations[leader_path][option.index()]
|
||||
if not option.isleader():
|
||||
return get_values(bitwarden_command_line, option, warnings, errors, leader_key=leader_key)
|
||||
return get_values(command, option, warnings, errors, leader_key=leader_key)
|
||||
leader_informations[path], option_values = get_values(
|
||||
bitwarden_command_line, option, warnings, errors, multiple=True, leader_key=option.value.default()[0]
|
||||
command, option, warnings, errors, multiple=True, leader_key=option.value.default()[0]
|
||||
)
|
||||
return option_values
|
||||
|
||||
|
||||
def get_values(bitwarden_command_line, option, warnings, errors, *, multiple=False, leader_key=None):
|
||||
def get_values(command, option, warnings, errors, *, multiple=False, leader_key=None):
|
||||
if leader_key:
|
||||
key = leader_key
|
||||
else:
|
||||
key = option.value.default()
|
||||
path = option.path()
|
||||
type_ = option.information.get("type")
|
||||
# FIXME retrait du permissive suffit ?
|
||||
if "validator" not in option.property.get():
|
||||
option.property.add("validator")
|
||||
if not isinstance(key, str):
|
||||
|
|
@ -167,25 +212,13 @@ def get_values(bitwarden_command_line, option, warnings, errors, *, multiple=Fal
|
|||
if multiple:
|
||||
return [], []
|
||||
return None
|
||||
if "ROUGAIL_BITWARDEN_MOCK_ENABLE" in environ:
|
||||
if type_ == "secret":
|
||||
value = "Ex4mpL3_P4ssw0rD"
|
||||
else:
|
||||
value = "example_login"
|
||||
if multiple:
|
||||
return [key], [value]
|
||||
return value
|
||||
try:
|
||||
if bitwarden_command_line == "rbw":
|
||||
data = get_key_from_rbw(key, multiple)
|
||||
else:
|
||||
data = get_key_from_bw(key)
|
||||
|
||||
data = command.get_key(key, multiple)
|
||||
except Exception as exc:
|
||||
errors.append(
|
||||
_(
|
||||
'cannot execute the "{0}" commandline from Bitwarden for "{1}": {2}'
|
||||
).format(bitwarden_command_line, path, exc)
|
||||
).format(command, path, exc)
|
||||
)
|
||||
if multiple:
|
||||
return [], []
|
||||
|
|
@ -245,52 +278,10 @@ def get_value( key_bitwarden: str, path: str, type_: str, data: dict, errors: di
|
|||
return value
|
||||
|
||||
|
||||
def get_key_from_rbw(
|
||||
key_bitwarden: str, multiple: bool
|
||||
):
|
||||
keys = []
|
||||
items = run_commandline(["rbw", "search", key_bitwarden]).strip()
|
||||
if items:
|
||||
items = items.split("\n")
|
||||
else:
|
||||
items = []
|
||||
for item in items:
|
||||
# if item.count('@') != 1:
|
||||
# continue
|
||||
if "@" in item:
|
||||
keys.append(item.split("@", 1)[-1])
|
||||
else:
|
||||
keys.append(item.rsplit("/", 1)[-1])
|
||||
if not multiple:
|
||||
if not items:
|
||||
return []
|
||||
if len(items) > 1:
|
||||
return [{"name": key} for key in keys]
|
||||
keys = [key_bitwarden]
|
||||
datas = []
|
||||
for key in keys:
|
||||
data = loads(
|
||||
run_commandline(
|
||||
["rbw", "get", key, "--raw", "--ignorecase"]
|
||||
).strip()
|
||||
)
|
||||
datas.append({"name": key, "login": data["data"]})
|
||||
return datas
|
||||
|
||||
|
||||
def get_key_from_bw(
|
||||
key_bitwarden: str,
|
||||
):
|
||||
return loads(
|
||||
run_commandline(
|
||||
["bw", "list", "items", "--search", key_bitwarden, "--nointeraction"]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def run_commandline(cmd) -> str:
|
||||
cpe = run(cmd, capture_output=True)
|
||||
returncode = cpe.returncode
|
||||
err = cpe.stderr.decode("utf8")
|
||||
if cpe.returncode != 0 or err:
|
||||
raise ExtensionError("{0} ({1})".format(err, cpe.returncode))
|
||||
if returncode != 0 or err:
|
||||
raise ExtensionError("{0} ({1})".format(err, returncode))
|
||||
return cpe.stdout.decode("utf8")
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -16,12 +16,14 @@ from rougail_tests.utils import config_to_dict
|
|||
test_dir = Path(__file__).parent / 'structures'
|
||||
|
||||
|
||||
def _test_structural_files(test_dir, command, *, env=False, modified=False):
|
||||
def _test_structural_files(test_dir, command, *, env=False, modified=False, mock=False):
|
||||
rougailconfig = RougailConfig.copy()
|
||||
rougailconfig['main_structural_directories'] = [str(test_dir)]
|
||||
# rougailconfig['tiramisu_cache'] = "cache.py"
|
||||
rougailconfig['step.user_data'] = ['bitwarden']
|
||||
rougailconfig['bitwarden.command'] = command
|
||||
rougailconfig['bitwarden.command'] = [command]
|
||||
if mock:
|
||||
rougailconfig["bitwarden.mock_enable"] = True
|
||||
rougail = Rougail(rougailconfig)
|
||||
config = rougail.run()
|
||||
config.property.read_write()
|
||||
|
|
@ -29,7 +31,7 @@ def _test_structural_files(test_dir, command, *, env=False, modified=False):
|
|||
generated_user_data = RougailUserData(config, rougailconfig=rougailconfig).run()
|
||||
if modified:
|
||||
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_datas(generated_user_data)
|
||||
errors = rougail.user_data(generated_user_data)
|
||||
#expected output
|
||||
config_dict = dict(config_to_dict(config.value.get()))
|
||||
if not env:
|
||||
|
|
@ -213,152 +215,104 @@ def test_structural_files_9_unknown_type_bw():
|
|||
|
||||
|
||||
def test_structural_files_env_1_secret_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '1_secret', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '1_secret', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_1_secret_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '1_secret', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '1_secret', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_1_secret_unknown_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '1_secret_unknown', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '1_secret_unknown', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_1_secret_unknown_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '1_secret_unknown', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '1_secret_unknown', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_2_username_secret_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '2_username_secret', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '2_username_secret', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_2_username_secret_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '2_username_secret', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '2_username_secret', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_2_username_secret_upper_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '2_username_secret_upper', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '2_username_secret_upper', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_2_username_secret_upper_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '2_username_secret_upper', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '2_username_secret_upper', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_2_username_secret_invalid_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '2_username_secret_invalid', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '2_username_secret_invalid', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_2_username_secret_invalid_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '2_username_secret_invalid', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '2_username_secret_invalid', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_3_leadership_secret_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '3_leadership_secret', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '3_leadership_secret', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_3_leadership_secret_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '3_leadership_secret', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '3_leadership_secret', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_3_leadership_secret_several_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '3_leadership_secret_several', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '3_leadership_secret_several', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_3_leadership_secret_several_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '3_leadership_secret_several', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '3_leadership_secret_several', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_4_several_secrets_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '4_several_secrets', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '4_several_secrets', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_4_several_secrets_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '4_several_secrets', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '4_several_secrets', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_4_several_secrets_upper_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '4_several_secrets_upper', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '4_several_secrets_upper', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_4_several_secrets_upper_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '4_several_secrets_upper', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '4_several_secrets_upper', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_5_secret_calc_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '5_secret_calc', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '5_secret_calc', 'rbw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_5_secret_calc_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
_test_structural_files(test_dir / '5_secret_calc', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '5_secret_calc', 'bw', env=True, mock=True)
|
||||
|
||||
|
||||
def test_structural_files_env_8_multi_variable_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
with raises(DictConsistencyError) as err:
|
||||
_test_structural_files(test_dir / '8_multi_variable', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '8_multi_variable', 'rbw', env=True, mock=True)
|
||||
assert err.value.errno == 57
|
||||
|
||||
|
||||
def test_structural_files_env_8_multi_variable_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
with raises(DictConsistencyError) as err:
|
||||
_test_structural_files(test_dir / '8_multi_variable', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '8_multi_variable', 'bw', env=True, mock=True)
|
||||
assert err.value.errno == 57
|
||||
|
||||
|
||||
def test_structural_files_env_9_unknown_type_rbw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
with raises(DictConsistencyError) as err:
|
||||
_test_structural_files(test_dir / '9_unknown_type', 'rbw', env=True)
|
||||
_test_structural_files(test_dir / '9_unknown_type', 'rbw', env=True, mock=True)
|
||||
assert err.value.errno == 56
|
||||
|
||||
|
||||
def test_structural_files_env_9_unknown_type_bw():
|
||||
"tests the output"
|
||||
with mock.patch.dict(os.environ, {'ROUGAIL_BITWARDEN_MOCK_ENABLE': '1'}):
|
||||
with raises(DictConsistencyError) as err:
|
||||
_test_structural_files(test_dir / '9_unknown_type', 'bw', env=True)
|
||||
_test_structural_files(test_dir / '9_unknown_type', 'bw', env=True, mock=True)
|
||||
assert err.value.errno == 56
|
||||
|
|
|
|||
Loading…
Reference in a new issue