rougail-user-data-bitwarden/src/rougail/user_data_bitwarden/data.py

96 lines
4.1 KiB
Python
Raw Normal View History

2025-02-05 11:30:51 +01:00
"""
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 rougail.error import ExtentionError
from .i18n import _
class RougailUserDataBitwarden:
force_apply_user_data = True
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
user_data = rougailconfig['step.user_data']
self.rougailconfig = rougailconfig
if 'bitwarden' not in user_data:
raise ExtentionError(_('"bitwarden" is not set in step.user_data'))
self.errors = []
self.warnings = []
def run(self):
self.set_passwords(self.config)
return {'errors': self.errors,
'warnings': self.warnings,
}
def set_passwords(self, optiondescription):
for option in optiondescription:
if option.isoptiondescription():
self.set_passwords(option)
elif option.owner.isdefault() and option.type() == 'password':
key_bitwarden = option.value.get()
if not isinstance(key_bitwarden, str):
self.errors.append(_('the default value for "{0}" must be the Bitwarden password name').format(option.path()))
continue
try:
cpe = run(["bw", "list", "items", "--search", key_bitwarden, '--nointeraction'], capture_output=True)
except Exception as exc:
self.errors.append(_('cannot execute the "bw" commandline from Bitwarden for "{0}": {1}').format(option.path(), exc))
continue
out = cpe.stdout.decode('utf8')
err = cpe.stderr.decode('utf8')
if cpe.returncode != 0 or err:
self.errors.append(_('cannot get password "{0}" from Bitwarden for "{1}": {2} ({3})').format(key_bitwarden, option.path(), err, cpe.returncode))
continue
try:
data = loads(out)
except Exception as exc:
self.errors.append(_('cannot load password "{0}" from Bitwarden for "{1}": {2}').format(key_bitwarden, option.path(), exc))
continue
if not data:
self.errors.append(_('cannot find password "{0}" from Bitwarden for "{1}"').format(key_bitwarden, option.path()))
continue
if len(data) != 1:
names = [d["name"] for d in data]
self.errors.append(_('several passwords found with name "{0}" from Bitwarden for "{1}": "{2}"').format(key_bitwarden, option.path(), "\", \"".join(names)))
continue
try:
option.value.set(data[0]['login']['password'])
except Exception as exc:
self.errors.append(_('unexpected password "{0}" from Bitwarden for "{1}": {2}').format(key_bitwarden, option.path(), exc))
continue