Compare commits

..

57 commits

Author SHA1 Message Date
61fd155025 bump: version 0.1.0a26 → 0.1.0a27 2025-12-22 08:53:18 +01:00
89112ba2e1 feat: use Calculation 2025-12-22 08:53:11 +01:00
a86a8bdc28 feat: doc 2025-12-20 20:36:44 +01:00
88e7548fa4 bump: version 0.1.0a25 → 0.1.0a26 2025-11-21 08:02:02 +01:00
08e50e8aea fix: ExtentionError => ExtensionError 2025-11-21 08:01:55 +01:00
6f358f88f2 bump: version 0.1.0a24 → 0.1.0a25 2025-11-06 06:26:48 +01:00
2f50351b9d fix: disable config if bitwarden is not selected 2025-11-05 21:38:15 +01:00
495b54dd0f bump: version 0.1.0a23 → 0.1.0a24 2025-10-16 08:21:35 +02:00
ec3b740aea fix: path is uncalculated 2025-10-16 08:20:27 +02:00
1088164eed bump: version 0.1.0a22 → 0.1.0a23 2025-10-16 08:14:38 +02:00
c83675ae7d feat: update rougail-structural-bitwarden 2025-10-16 08:14:25 +02:00
6e3b48de07 bump: version 0.1.0a21 → 0.1.0a22 2025-10-10 08:14:22 +02:00
669c6cce98 feat: use user_datas to load datas 2025-10-05 21:33:44 +02:00
0cff9b8691 bump: version 0.1.0a20 → 0.1.0a21 2025-06-20 07:27:00 +03:00
13abd6ac4d feat: update rougail-structural-bitwarden package version 2025-06-20 07:26:01 +03:00
24fcf6f814 bump: version 0.1.0a19 → 0.1.0a20 2025-05-12 09:10:08 +02:00
72980ca358 fix: black 2025-05-11 19:11:17 +02:00
d30680cbed bump: version 0.1.0a18 → 0.1.0a19 2025-05-02 08:16:26 +02:00
a2fb9e8509 fix: do not force use_data usage 2025-05-02 08:16:24 +02:00
16119b432e bump: version 0.1.0a17 → 0.1.0a18 2025-04-09 21:28:06 +02:00
d4e3d06cc2 fix: version 2025-04-09 21:28:03 +02:00
a17ae75c59 bump: version 0.1.0a16 → 0.1.0a17 2025-04-09 14:21:34 +02:00
54bdf4861c fix: better error message 2025-04-09 14:20:56 +02:00
71a05d631c bump: version 0.1.0a15 → 0.1.0a16 2025-04-04 08:27:19 +02:00
1d401f7e35 fix: with ROUGAIL_BITWARDEN_MOCK_ENABLE variable, unix_user and password are different 2025-04-04 08:27:05 +02:00
2bb0b0db3a bump: version 0.1.0a14 → 0.1.0a15 2025-03-19 15:32:30 +01:00
c4c7e0da4a fix: support item without username 2025-03-19 15:31:52 +01:00
34963ddca2 bump: version 0.1.0a13 → 0.1.0a14 2025-03-19 10:25:25 +01:00
a273820c84 feat: port to rougail secret_manager 2025-03-19 10:25:09 +01:00
c3a0ecd557 feat: same behavour between bw et rbw 2025-03-01 18:04:38 +01:00
3b72988cb5 bump: version 0.1.0a12 → 0.1.0a13 2025-02-19 09:27:51 +01:00
d3c0f94f86 fix: MOCK 2025-02-19 09:27:47 +01:00
80b1155d2a bump: version 0.1.0a11 → 0.1.0a12 2025-02-19 09:14:00 +01:00
65e318e883 fix: MOCK 2025-02-19 09:13:55 +01:00
e9ef79e80b bump: version 0.1.0a10 → 0.1.0a11 2025-02-19 08:59:22 +01:00
a12cf396fd fix: if mock is active, do not search rbw or bw 2025-02-19 08:59:13 +01:00
4c72346aa6 bump: version 0.1.0a9 → 0.1.0a10 2025-02-17 15:40:59 +01:00
80ddd65206 fix: support hidden attribut 2025-02-17 15:40:55 +01:00
d96f888e48 bump: version 0.1.0a8 → 0.1.0a9 2025-02-17 15:26:20 +01:00
82e0c371a0 fix: support hidden attribut 2025-02-17 15:25:59 +01:00
5d8dd6881f bump: version 0.1.0a7 → 0.1.0a8 2025-02-17 09:40:49 +01:00
4fd27de57e fix: update structural plugin version 2025-02-17 09:40:43 +01:00
76b43846ed bump: version 0.1.0a6 → 0.1.0a7 2025-02-17 09:34:40 +01:00
510b97d7cf feat: support rbw and bw command line 2025-02-17 09:34:26 +01:00
b332195ef3 bump: version 0.1.0a5 → 0.1.0a6 2025-02-13 22:09:30 +01:00
8b180e131e fix: bitwarden key could be an invalid username/secret 2025-02-13 22:09:15 +01:00
52a56476d3 bump: version 0.1.0a4 → 0.1.0a5 2025-02-13 13:31:00 +01:00
7925cdcc6a fix: structural load 2025-02-13 13:30:55 +01:00
01feb985fb bump: version 0.1.0a3 → 0.1.0a4 2025-02-13 13:23:23 +01:00
c16fcdb7fe fix: structural version 2025-02-13 13:23:20 +01:00
b16e71846b bump: version 0.1.0a2 → 0.1.0a3 2025-02-13 13:20:59 +01:00
eea2badb64 bump: version 0.1.0a1 → 0.1.0a2 2025-02-13 13:19:26 +01:00
b6e4744085 fix: separate user-data and structural 2025-02-13 13:16:05 +01:00
653790b9af bump: version 0.1.0a0 → 0.1.0a1 2025-02-12 15:51:19 +01:00
971311e0f5 fix: many improvment 2025-02-12 15:39:53 +01:00
abb4b87924 bump: version 0.0.0 → 0.1.0a0 2025-02-05 11:30:57 +01:00
5682ac96b6 feat: first version 2025-02-05 11:30:51 +01:00
80 changed files with 1929 additions and 1 deletions

163
CHANGELOG.md Normal file
View file

@ -0,0 +1,163 @@
## 0.1.0a27 (2025-12-22)
### Feat
- use Calculation
- doc
## 0.1.0a26 (2025-11-21)
### Fix
- ExtentionError => ExtensionError
## 0.1.0a25 (2025-11-06)
### Fix
- disable config if bitwarden is not selected
## 0.1.0a24 (2025-10-16)
### Fix
- path is uncalculated
## 0.1.0a23 (2025-10-16)
### Feat
- update rougail-structural-bitwarden
## 0.1.0a22 (2025-10-10)
### Feat
- use user_datas to load datas
## 0.1.0a21 (2025-06-20)
### Feat
- update rougail-structural-bitwarden package version
## 0.1.0a20 (2025-05-12)
### Fix
- black
## 0.1.0a19 (2025-05-02)
### Fix
- do not force use_data usage
## 0.1.0a18 (2025-04-09)
### Fix
- version
## 0.1.0a17 (2025-04-09)
### Fix
- better error message
## 0.1.0a16 (2025-04-04)
### Fix
- with ROUGAIL_BITWARDEN_MOCK_ENABLE variable, unix_user and password are different
## 0.1.0a15 (2025-03-19)
### Fix
- support item without username
## 0.1.0a14 (2025-03-19)
### Feat
- port to rougail secret_manager
- same behavour between bw et rbw
## 0.1.0a13 (2025-02-19)
### Fix
- MOCK
## 0.1.0a12 (2025-02-19)
### Fix
- MOCK
## 0.1.0a11 (2025-02-19)
### Fix
- if mock is active, do not search rbw or bw
## 0.1.0a10 (2025-02-17)
### Fix
- support hidden attribut
## 0.1.0a9 (2025-02-17)
### Fix
- support hidden attribut
## 0.1.0a8 (2025-02-17)
### Fix
- update structural plugin version
## 0.1.0a7 (2025-02-17)
### Feat
- support rbw and bw command line
## 0.1.0a6 (2025-02-13)
### Fix
- bitwarden key could be an invalid username/secret
## 0.1.0a5 (2025-02-13)
### Fix
- structural load
## 0.1.0a4 (2025-02-13)
### Fix
- structural version
## 0.1.0a2 (2025-02-13)
### Fix
- separate user-data and structural
## 0.1.0a1 (2025-02-12)
### Fix
- many improvment
## 0.1.0a0 (2025-02-05)
### Feat
- first version

21
README.fr.md Normal file
View 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. | •&nbsp;rbw<br/>&nbsp;bw | [`choice`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `multiple` `obligatoire` | `unique`<br/>**Choix** : <br/>&nbsp;rbw<br/>&nbsp;bw |
| **<a id="bitwarden.mock_enable" name="bitwarden.mock_enable">bitwarden.mock_enable</a>**<br/>**Ligne de commande** : <br/>&nbsp;--bitwarden.mock_enable<br/>&nbsp;--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` | |

View file

@ -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. | •&nbsp;rbw<br/>&nbsp;bw | [`choice`](https://rougail.readthedocs.io/en/latest/variable.html#variables-types) `multiple` `mandatory` | `unique`<br/>**Choices**: <br/>&nbsp;rbw<br/>&nbsp;bw |
| **<a id="bitwarden.mock_enable" name="bitwarden.mock_enable">bitwarden.mock_enable</a>**<br/>**Command line**: <br/>&nbsp;--bitwarden.mock_enable<br/>&nbsp;--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
View 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()

View file

@ -0,0 +1,159 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
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"
"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.8\n"
#: 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: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/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:169
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
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: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: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: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: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
msgid "password"
msgstr "de mot de passe"
#: src/rougail/user_data_bitwarden/data.py:272
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}\""
#~ 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"
#~ msgstr ""
#~ "la valeur de \"{0}\" à l'index {1} est déjà renseigné alors que celle "
#~ "doit devrait être renseigné par Bitwarden"
#~ 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"
#~ msgid "cannot find {0} \"{1}\" from Bitwarden for \"{2}\""
#~ msgstr "ne peut trouver {0} \"{1}\" pour Bitwarden pour \"{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

@ -0,0 +1,85 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\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"
"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"
#: src/rougail/user_data_bitwarden/config.py:30
msgid "Secrets are in Bitwarden"
msgstr ""
#: 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/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:169
msgid "please unlock Bitwarden password database with {0} command line"
msgstr ""
#: 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:208
msgid "the default value for \"{0}\" 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}"
msgstr ""
#: 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:243
msgid "several items found with name \"{0}\" in Bitwarden for \"{1}\": \"{2}\""
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
msgid "password"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:272
msgid "username"
msgstr ""
#: src/rougail/user_data_bitwarden/data.py:274
msgid "item \"{0}\" in Bitwarden has no {1} for \"{2}\""
msgstr ""

44
pyproject.toml Normal file
View file

@ -0,0 +1,44 @@
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.8.0,<4"]
[project]
name = "rougail.user_data_bitwarden"
version = "0.1.0a27"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md"
description = "Rougail user_data Bitwarden"
requires-python = ">=3.8"
license = {file = "LICENSE"}
classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
"Natural Language :: English",
"Natural Language :: French",
]
dependencies = [
"rougail >= 1.1,<2",
"rougail-structural-bitwarden == 0.1.0a6",
]
[project.urls]
Home = "https://forge.cloud.silique.fr/stove/rougail-user-data-bitwarden"
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "$version"
version_scheme = "pep440"
version_provider = "pep621"
version_files = [
"src/rougail/user_data_bitwarden/__version__.py",
"pyproject.toml:version"
]
update_changelog_on_bump = true
changelog_merge_prerelease = true

View file

@ -0,0 +1,27 @@
"""
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 .data import RougailUserDataBitwarden
from .__version__ import __version__
RougailUserData = RougailUserDataBitwarden
__all__ = ("RougailUserDataBitwarden",)

View file

@ -0,0 +1 @@
__version__ = "0.1.0a27"

View 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

View file

@ -0,0 +1,59 @@
"""
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 .i18n import _
def get_rougail_config(
*,
backward_compatibility=True,
) -> dict:
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: |-
{{{{ _.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: {_("Command line used to retrieve secrets")}
help: {_("Command line must be in PATH")}
choices:
- rbw
- bw
default:
- rbw
- bw
mock_enable: false # {_("Simulate password generation instead of retrieve them")}
"""
return {
"name": "bitwarden",
"process": "user data",
"options": options,
"level": 90,
}
__all__ = ("get_rougail_config",)

View file

@ -0,0 +1,273 @@
"""
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 subprocess import run
from json import loads
from os import environ
from shutil import which
from tiramisu.error import ConfigError, display_list
from rougail.error import ExtensionError
from .i18n import _
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",
*,
rougailconfig: "RougailConfig" = None,
):
# this is the tiramisu config object
self.config = config
if rougailconfig is None:
from rougail.config import RougailConfig
rougailconfig = RougailConfig
user_data = rougailconfig["step.user_data"]
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:
raise ExtensionError(_('"bitwarden" is not set in step.user_data'))
self.errors = []
self.warnings = []
self.leader_informations = {}
self.commands = {'rbw': RBW(),
'bw': BW(),
}
def run(self):
values = {}
self.command = self.get_command()
self.set_passwords(self.config.unrestraint, values)
return [
{
"source": 'Bitwarden',
"errors": self.errors,
"warnings": self.warnings,
"values": values,
"options": {
"secret_manager": True,
}
}
]
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.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")
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 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
]
raise ConfigError(
_(
'several items found with name "{0}" in Bitwarden: "{1}"'
).format(key, '", "'.join(names))
)
value = get_value(key, type_, data[0])
if multiple:
return [data[0]["name"]], [value]
return value
def get_value( key_bitwarden: str, type_: str, data: dict) -> str:
try:
if type_ == "secret":
value = data["login"]["password"]
else:
value = data["login"]["username"]
except Exception as exc:
raise ConfigError(
_('unexpected datas "{0}" from Bitwarden: {2}').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
)
)
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

@ -0,0 +1,26 @@
"""
Silique (https://www.silique.fr)
Copyright (C) 2025
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, either version 3 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 Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from gettext import translation
from pathlib import Path
t = translation(
"rougail_user_data_bitwarden", str(Path(__file__).parent / "locale"), fallback=True
)
_ = t.gettext

View file

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

View file

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

View file

@ -0,0 +1,3 @@
{
"rougail.secret": "bitwarden_password"
}

View file

@ -0,0 +1,3 @@
{
"rougail.secret": "Ex4mpL3_P4ssw0rD"
}

View file

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

View file

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

View file

@ -0,0 +1,3 @@
{
"rougail.secret": null
}

View file

@ -0,0 +1,3 @@
{
"rougail.secret": "Ex4mpL3_P4ssw0rD"
}

View file

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

View file

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

View file

@ -0,0 +1,4 @@
{
"rougail.username": "bitwarden_username",
"rougail.secret": "bitwarden_password"
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,4 @@
{
"rougail.username": "up_secret_error_1",
"rougail.secret": "up_secret_error_pass_1"
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,4 @@
{
"rougail.username": "bitwarden_username",
"rougail.secret": "bitwarden_password"
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,8 @@
{
"rougail.leader.username": [
{
"rougail.leader.username": "example_login",
"rougail.leader.secret": "Ex4mpL3_P4ssw0rD"
}
]
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,12 @@
{
"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

@ -0,0 +1,8 @@
{
"rougail.leader.username": [
{
"rougail.leader.username": "example_login",
"rougail.leader.secret": "Ex4mpL3_P4ssw0rD"
}
]
}

View file

@ -0,0 +1,6 @@
{
"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\""
],
"warnings": []
}

View file

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

View file

@ -0,0 +1,3 @@
{
"rougail.secret": null
}

View file

@ -0,0 +1,3 @@
{
"rougail.secret": "Ex4mpL3_P4ssw0rD"
}

View file

@ -0,0 +1,6 @@
{
"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\""
],
"warnings": []
}

View file

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

View file

@ -0,0 +1,3 @@
{
"rougail.secret": null
}

View file

@ -0,0 +1,3 @@
{
"rougail.secret": "Ex4mpL3_P4ssw0rD"
}

View file

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

View file

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

View file

@ -0,0 +1,10 @@
{
"rougail.host": "test",
"rougail.project": "5_secret_calc",
"rougail.environment": "environment",
"rougail.service": "service",
"rougail.user": "user_1",
"rougail.secret": "bitwarden_password",
"rougail.dyn_user_1.secret": "bitwarden_password",
"rougail.dyn_user_2.secret": "bitwarden_password_2"
}

View file

@ -0,0 +1,10 @@
{
"rougail.host": "test",
"rougail.project": "5_secret_calc",
"rougail.environment": "environment",
"rougail.service": "service",
"rougail.user": "user_1",
"rougail.secret": "Ex4mpL3_P4ssw0rD",
"rougail.dyn_user_1.secret": "Ex4mpL3_P4ssw0rD",
"rougail.dyn_user_2.secret": "Ex4mpL3_P4ssw0rD"
}

View file

@ -0,0 +1,6 @@
{
"errors": [],
"warnings": [
"variable or family \"rougail.modified_variable_single\" does not exist, it will be ignored when loading from By Hand"
]
}

View file

@ -0,0 +1,12 @@
{
"rougail.host": "test",
"rougail.project": "5_secret_calc",
"rougail.environment": "environment",
"rougail.service": "service",
"rougail.modified_variable": [
"user_1",
"user_2"
],
"rougail.dyn_user_1.secret": "bitwarden_password",
"rougail.dyn_user_2.secret": "bitwarden_password_2"
}

View file

@ -0,0 +1,6 @@
{
"errors": [],
"warnings": [
"variable or family \"rougail.modified_variable\" does not exist, it will be ignored when loading from By Hand"
]
}

View file

@ -0,0 +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

@ -0,0 +1,6 @@
{
"errors": [],
"warnings": [
"variable or family \"rougail.modified_variable\" does not exist, it will be ignored when loading from By Hand"
]
}

View file

@ -0,0 +1,4 @@
{
"rougail.modified_variable_single": "user_2",
"rougail.secret": "bitwarden_password_2"
}

View file

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

View file

@ -0,0 +1,4 @@
{
"rougail.username": null,
"rougail.secret": "ET6RrpdZv5bNZu"
}

View file

@ -0,0 +1,12 @@
---
version: 1.1
secret:
description: the second variable
type: secret
secret_manager:
host: test
project: 1_secret
environment: environment
service: service
user: user

View file

@ -0,0 +1,12 @@
---
version: 1.1
secret:
description: the second variable
type: secret
secret_manager:
host: test
project: 1_secret_unknown
environment: environment
service: service
user: user

View file

@ -0,0 +1,22 @@
---
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

View file

@ -0,0 +1,22 @@
---
version: 1.1
username:
description: the username
type: unix_user
secret_manager:
host: test
project: 2_username_secret_invalid
environment: environment
service: service
user: UP SECRET 1
secret:
description: the secret
type: secret
secret_manager:
host: test
project: 2_username_secret_invalid
environment: environment
service: service
user: UP SECRET 1

View file

@ -0,0 +1,22 @@
---
version: 1.1
username:
description: the username
type: unix_user
secret_manager:
host: TEST
project: 2_USERNAME_SECRET_UPPER
environment: ENVIRONMENT
service: SERVICE
user: USER_1
secret:
description: the secret
type: secret
secret_manager:
host: TEST
project: 2_USERNAME_SECRET_UPPER
environment: ENVIRONMENT
service: SERVICE
user: USER_1

View file

@ -0,0 +1,25 @@
---
version: 1.1
leader:
type: leadership
username:
description: the username
type: unix_user
secret_manager:
host: test
project: 3_leadership_secret
environment: environment
service: service
user: user
secret:
description: the secret
type: secret
secret_manager:
host: test
project: 3_leadership_secret
environment: environment
service: service
user: user

View file

@ -0,0 +1,25 @@
---
version: 1.1
leader:
type: leadership
username:
description: the username
type: unix_user
secret_manager:
host: test
project: 3_leadership_secret_several
environment: environment
service: service
user: user
secret:
description: the secret
type: secret
secret_manager:
host: test
project: 3_leadership_secret_several
environment: environment
service: service
user: user

View file

@ -0,0 +1,12 @@
---
version: 1.1
secret:
description: the second variable
type: secret
secret_manager:
host: test
project: 4_several_secrets
environment: environment
service: service
user: user

View file

@ -0,0 +1,12 @@
---
version: 1.1
secret:
description: the second variable
type: secret
secret_manager:
host: test
project: 3_leadership_secret
environment: ENVIRONMENT
service: SERVICE
user: USER

View file

@ -0,0 +1,57 @@
---
version: 1.1
host:
description: hostname
default: test
project:
description: the project
default: 5_secret_calc
environment:
description: the environment
default: environment
service:
description: the service
default: service
user:
description: the user
default: user_1
secret:
description: the secret
type: secret
secret_manager:
host:
variable: _.host
project:
variable: _.project
environment:
variable: _.environment
service:
variable: _.service
user:
variable: _.user
dyn_{{ identifier }}:
dynamic:
- user_1
- user_2
secret:
description: the second secret
type: secret
secret_manager:
host:
variable: __.host
project:
variable: __.project
environment:
variable: __.environment
service:
variable: __.service
user:
type: identifier

View file

@ -0,0 +1,39 @@
---
version: 1.1
host:
description: hostname
default: test
project:
description: the project
default: 5_secret_calc
environment:
description: the environment
default: environment
service:
description: the service
default: service
modified_variable: [] # a variable modified
dyn_{{ identifier }}:
dynamic:
variable: _.modified_variable
secret:
description: the second secret
type: secret
secret_manager:
host:
variable: __.host
project:
variable: __.project
environment:
variable: __.environment
service:
variable: __.service
user:
type: identifier

View file

@ -0,0 +1,35 @@
---
version: 1.1
host:
description: hostname
default: test
project:
description: the project
default: 5_secret_calc
environment:
description: the environment
default: environment
service:
description: the service
default: service
modified_variable_single: # a variable modified
secret:
description: the second secret
type: secret
secret_manager:
host:
variable: _.host
project:
variable: _.project
environment:
variable: _.environment
service:
variable: _.service
user:
variable: _.modified_variable_single

View file

@ -0,0 +1,41 @@
%YAML 1.2
---
version: 1.1
host:
description: hostname
default: test
hidden: true
project:
description: the project
default: 5_secret_calc
hidden: true
environment:
description: the environment
default: environment
hidden: true
service:
description: the service
default: service
hidden: true
modified_variable_single: # a variable modified
secret:
description: the second secret
type: secret
secret_manager:
host:
variable: _.host
project:
variable: _.project
environment:
variable: _.environment
service:
variable: _.service
user:
variable: _.modified_variable_single
...

View file

@ -0,0 +1,22 @@
---
version: 1.1
username:
description: the username
type: unix_user
secret_manager:
host: test
project: 6_no_username
environment: environment
service: service
user: user_1
secret:
description: the secret
type: secret
secret_manager:
host: test
project: 6_no_username
environment: environment
service: service
user: user_1

View file

@ -0,0 +1,13 @@
---
version: 1.1
secret:
description: the secret variable
type: secret
multi: true
secret_manager:
host: test
project: 8_multi_variable
environment: environment
service: service
user: user

View file

@ -0,0 +1,12 @@
---
version: 1.1
secret:
description: the first variable
type: string
secret_manager:
host: test
project: 9_unknown_type
environment: environment
service: service
user: user

318
tests/test_load.py Normal file
View file

@ -0,0 +1,318 @@
import os
from pathlib import Path
from rougail import Rougail, RougailConfig
from rougail.error import DictConsistencyError
#########################
from rougail.user_data_bitwarden import RougailUserDataBitwarden as RougailUserData
from json import load, dump
#########################
from pytest import raises
from unittest import mock
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, 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]
if mock:
rougailconfig["bitwarden.mock_enable"] = True
rougail = Rougail(rougailconfig)
config = rougail.run()
config.property.read_write()
# loads variables in the tiramisu config
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_data(generated_user_data)
#expected output
config_dict = dict(config_to_dict(config.value.get()))
if not env:
base_filename = 'bitwarden.json'
else:
base_filename = 'bitwarden_env.json'
ok_file = Path('tests') / 'results' / test_dir.name / 'makedict' / (base_filename + '.' + command)
if not ok_file.is_file():
ok_file = Path('tests') / 'results' / test_dir.name / 'makedict' / base_filename
if not ok_file.is_file():
ok_file.parent.mkdir(parents=True, exist_ok=True)
with open(ok_file, 'a') as json_file:
dump(config_dict, json_file, indent=4)
with open(ok_file) as json_file:
expected = load(json_file)
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
if not errors_file.is_file():
errors_file.parent.mkdir(parents=True, exist_ok=True)
with open(errors_file, 'a') as json_file:
dump(errors, json_file, indent=4)
with open(errors_file) as json_file:
expected_errors = load(json_file)
assert expected_errors == errors, errors_file
#
assert expected == config_dict, ok_file
def test_structural_files_1_secret_rbw():
"tests the output"
_test_structural_files(test_dir / '1_secret', 'rbw')
def test_structural_files_1_secret_bw():
"tests the output"
_test_structural_files(test_dir / '1_secret', 'bw')
def test_structural_files_1_secret_unknown_rbw():
"tests the output"
_test_structural_files(test_dir / '1_secret_unknown', 'rbw')
def test_structural_files_1_secret_unknown_bw():
"tests the output"
_test_structural_files(test_dir / '1_secret_unknown', 'bw')
def test_structural_files_2_username_secret_rbw():
"tests the output"
_test_structural_files(test_dir / '2_username_secret', 'rbw')
def test_structural_files_2_username_secret_bw():
"tests the output"
_test_structural_files(test_dir / '2_username_secret', 'bw')
def test_structural_files_2_username_secret_upper_rbw():
"tests the output"
_test_structural_files(test_dir / '2_username_secret_upper', 'rbw')
def test_structural_files_2_username_secret_upper_bw():
"tests the output"
_test_structural_files(test_dir / '2_username_secret_upper', 'bw')
def test_structural_files_2_username_secret_invalid_rbw():
"tests the output"
_test_structural_files(test_dir / '2_username_secret_invalid', 'rbw')
def test_structural_files_2_username_secret_invalid_bw():
"tests the output"
_test_structural_files(test_dir / '2_username_secret_invalid', 'bw')
def test_structural_files_3_leadership_secret_rbw():
"tests the output"
_test_structural_files(test_dir / '3_leadership_secret', 'rbw')
def test_structural_files_3_leadership_secret_bw():
"tests the output"
_test_structural_files(test_dir / '3_leadership_secret', 'bw')
def test_structural_files_3_leadership_secret_several_rbw():
"tests the output"
_test_structural_files(test_dir / '3_leadership_secret_several', 'rbw')
def test_structural_files_3_leadership_secret_several_bw():
"tests the output"
_test_structural_files(test_dir / '3_leadership_secret_several', 'bw')
def test_structural_files_4_several_secrets_rbw():
"tests the output"
_test_structural_files(test_dir / '4_several_secrets', 'rbw')
def test_structural_files_4_several_secrets_bw():
"tests the output"
_test_structural_files(test_dir / '4_several_secrets', 'bw')
def test_structural_files_4_several_secrets_upper_rbw():
"tests the output"
_test_structural_files(test_dir / '4_several_secrets_upper', 'rbw')
def test_structural_files_4_several_secrets_upper_bw():
"tests the output"
_test_structural_files(test_dir / '4_several_secrets_upper', 'bw')
def test_structural_files_5_secret_calc_rbw():
"tests the output"
_test_structural_files(test_dir / '5_secret_calc', 'rbw')
def test_structural_files_5_secret_calc_bw():
"tests the output"
_test_structural_files(test_dir / '5_secret_calc', 'bw')
def test_structural_files_5_secret_calc_other_user_data_rbw():
"tests the output"
_test_structural_files(test_dir / '5_secret_calc_other_user_data', 'rbw', modified=True)
def test_structural_files_5_secret_calc_other_user_data2_rbw():
"tests the output"
_test_structural_files(test_dir / '5_secret_calc_other_user_data2', 'rbw', modified=True)
def test_structural_files_5_secret_calc_other_user_data3_rbw():
"tests the output"
_test_structural_files(test_dir / '5_secret_calc_other_user_data3', 'rbw', modified=True)
def test_structural_files_6_no_username_rbw():
"tests the output"
_test_structural_files(test_dir / '6_no_username', 'rbw')
def test_structural_files_6_no_username_bw():
"tests the output"
_test_structural_files(test_dir / '6_no_username', 'bw')
def test_structural_files_8_multi_variable_rbw():
"tests the output"
with raises(DictConsistencyError) as err:
_test_structural_files(test_dir / '8_multi_variable', 'rbw')
assert err.value.errno == 57
def test_structural_files_8_multi_variable_bw():
"tests the output"
with raises(DictConsistencyError) as err:
_test_structural_files(test_dir / '8_multi_variable', 'bw')
assert err.value.errno == 57
def test_structural_files_9_unknown_type_rbw():
"tests the output"
with raises(DictConsistencyError) as err:
_test_structural_files(test_dir / '9_unknown_type', 'rbw')
assert err.value.errno == 56
def test_structural_files_9_unknown_type_bw():
"tests the output"
with raises(DictConsistencyError) as err:
_test_structural_files(test_dir / '9_unknown_type', 'bw')
assert err.value.errno == 56
def test_structural_files_env_1_secret_rbw():
_test_structural_files(test_dir / '1_secret', 'rbw', env=True, mock=True)
def test_structural_files_env_1_secret_bw():
_test_structural_files(test_dir / '1_secret', 'bw', env=True, mock=True)
def test_structural_files_env_1_secret_unknown_rbw():
_test_structural_files(test_dir / '1_secret_unknown', 'rbw', env=True, mock=True)
def test_structural_files_env_1_secret_unknown_bw():
_test_structural_files(test_dir / '1_secret_unknown', 'bw', env=True, mock=True)
def test_structural_files_env_2_username_secret_rbw():
_test_structural_files(test_dir / '2_username_secret', 'rbw', env=True, mock=True)
def test_structural_files_env_2_username_secret_bw():
_test_structural_files(test_dir / '2_username_secret', 'bw', env=True, mock=True)
def test_structural_files_env_2_username_secret_upper_rbw():
_test_structural_files(test_dir / '2_username_secret_upper', 'rbw', env=True, mock=True)
def test_structural_files_env_2_username_secret_upper_bw():
_test_structural_files(test_dir / '2_username_secret_upper', 'bw', env=True, mock=True)
def test_structural_files_env_2_username_secret_invalid_rbw():
_test_structural_files(test_dir / '2_username_secret_invalid', 'rbw', env=True, mock=True)
def test_structural_files_env_2_username_secret_invalid_bw():
_test_structural_files(test_dir / '2_username_secret_invalid', 'bw', env=True, mock=True)
def test_structural_files_env_3_leadership_secret_rbw():
_test_structural_files(test_dir / '3_leadership_secret', 'rbw', env=True, mock=True)
def test_structural_files_env_3_leadership_secret_bw():
_test_structural_files(test_dir / '3_leadership_secret', 'bw', env=True, mock=True)
def test_structural_files_env_3_leadership_secret_several_rbw():
_test_structural_files(test_dir / '3_leadership_secret_several', 'rbw', env=True, mock=True)
def test_structural_files_env_3_leadership_secret_several_bw():
_test_structural_files(test_dir / '3_leadership_secret_several', 'bw', env=True, mock=True)
def test_structural_files_env_4_several_secrets_rbw():
_test_structural_files(test_dir / '4_several_secrets', 'rbw', env=True, mock=True)
def test_structural_files_env_4_several_secrets_bw():
_test_structural_files(test_dir / '4_several_secrets', 'bw', env=True, mock=True)
def test_structural_files_env_4_several_secrets_upper_rbw():
_test_structural_files(test_dir / '4_several_secrets_upper', 'rbw', env=True, mock=True)
def test_structural_files_env_4_several_secrets_upper_bw():
_test_structural_files(test_dir / '4_several_secrets_upper', 'bw', env=True, mock=True)
def test_structural_files_env_5_secret_calc_rbw():
_test_structural_files(test_dir / '5_secret_calc', 'rbw', env=True, mock=True)
def test_structural_files_env_5_secret_calc_bw():
_test_structural_files(test_dir / '5_secret_calc', 'bw', env=True, mock=True)
def test_structural_files_env_8_multi_variable_rbw():
with raises(DictConsistencyError) as err:
_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():
with raises(DictConsistencyError) as err:
_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():
with raises(DictConsistencyError) as err:
_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():
with raises(DictConsistencyError) as err:
_test_structural_files(test_dir / '9_unknown_type', 'bw', env=True, mock=True)
assert err.value.errno == 56