Merge pull request #28 from jle64/bitwarden

Add support for using rbw.
This commit is contained in:
Jonathan Lestrelin 2021-09-28 00:43:29 +00:00 committed by GitHub
commit 92fac7865a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 68 deletions

View File

@ -1,8 +1,8 @@
A search provider for GNOME Shell that adds support for searching in zx2c4/[pass](https://www.passwordstore.org/).
A search provider for GNOME Shell that adds support for searching passwords in zx2c4/[pass](https://www.passwordstore.org/) or in the [rbw](https://github.com/doy/rbw) Bitwarden/Vaultwarden client.
Names of passwords will show up in GNOME Shell searches, choosing one will copy the corresponding content to the clipboard.
Supports OTP, fields and can use GPaste.
Can use the [GPaste](https://github.com/Keruspe/GPaste) clipboard manager, supports OTP and fields (pass only, requires GPaste).
![Sreencapture](misc/screencapture.gif)
@ -29,11 +29,12 @@ dnf install gnome-pass-search-provider
## Manual
Ensure that python>=3.5 as well as the dbus, gobject, fuzzywuzzy Python modules are installed. They should all be packaged under python-name or python3-name depending on your distribution.
Ensure that python>=3.7 as well as the dbus, gobject and fuzzywuzzy Python modules are installed. They should all be packaged under python-name or python3-name depending on your distribution.
Clone this repository and run the installation script as root:
```
git clone git@github.com:jle64/gnome-shell-pass-search-provider.git
git clone https://github.com/jle64/gnome-pass-search-provider.git
cd gnome-pass-search-provider
sudo ./install.sh
```
@ -62,6 +63,10 @@ To copy the pin start the search with `:pin` and for the username with `:user`.
The [pass-otp](https://github.com/tadfisher/pass-otp) extension is supported. Searches starting with `otp` will copy the otp token to the clipboard.
# Bitwarden/Vaultwarden
If [rbw](https://github.com/doy/rbw) is installed, it can be used instead of pass by prefixing a search with `bw`. Non prefixed searches will still go through pass if present.
# Environment variables
If you are configuring `pass` through environment variables, such as `PASSWORD_STORE_DIR`, make sure to set them in a way that will propagate to the search provider executable, not just in your shell.

View File

@ -51,6 +51,7 @@ class SearchPassService(dbus.service.Object):
bus_name = "org.gnome.Pass.SearchProvider"
_object_path = "/" + bus_name.replace(".", "/")
use_bw = False
def __init__(self):
self.session_bus = dbus.SessionBus()
@ -66,7 +67,14 @@ class SearchPassService(dbus.service.Object):
@dbus.service.method(in_signature="as", out_signature="as", **sbn)
def GetInitialResultSet(self, terms):
return self.get_result_set(terms)
try:
if terms[0] == "bw":
self.use_bw = True
return self.get_bw_result_set(terms)
except NameError:
pass
self.use_bw = False
return self.get_pass_result_set(terms)
@dbus.service.method(in_signature="as", out_signature="aa{sv}", **sbn)
def GetResultMetas(self, ids):
@ -81,13 +89,31 @@ class SearchPassService(dbus.service.Object):
@dbus.service.method(in_signature="asas", out_signature="as", **sbn)
def GetSubsearchResultSet(self, previous_results, new_terms):
return self.get_result_set(new_terms)
if self.use_bw:
return self.get_bw_result_set(new_terms)
else:
return self.get_pass_result_set(new_terms)
@dbus.service.method(in_signature="asu", terms="as", timestamp="u", **sbn)
def LaunchSearch(self, terms, timestamp):
pass
def get_result_set(self, terms):
def get_bw_result_set(self, terms):
name = "".join(terms[1:])
password_list = subprocess.check_output(
["rbw", "list"], stderr=subprocess.STDOUT, universal_newlines=True
).split("\n")[:-1]
results = [
e[0]
for e in process.extract(
name, password_list, limit=5, scorer=fuzz.partial_ratio
)
]
return results
def get_pass_result_set(self, terms):
if terms[0] == "otp":
field = terms[0]
elif terms[0].startswith(":"):
@ -123,31 +149,56 @@ class SearchPassService(dbus.service.Object):
return results
def send_password_to_gpaste(self, base_args, name, field=None):
try:
gpaste = self.session_bus.get_object(
"org.gnome.GPaste.Daemon", "/org/gnome/GPaste"
)
gpaste = self.session_bus.get_object(
"org.gnome.GPaste.Daemon", "/org/gnome/GPaste"
)
output = subprocess.check_output(
base_args + [name], stderr=subprocess.STDOUT, universal_newlines=True
output = subprocess.check_output(
base_args + [name], stderr=subprocess.STDOUT, universal_newlines=True
)
if field is not None:
match = re.search(
fr"^{field}:\s*(?P<value>.+?)$", output, flags=re.I | re.M
)
if field is not None:
match = re.search(
fr"^{field}:\s*(?P<value>.+?)$", output, flags=re.I | re.M
)
if match:
password = match.group("value")
else:
raise RuntimeError(
f"The field {field} was not found in " + "the pass file."
)
if match:
password = match.group("value")
else:
password = output.split("\n", 1)[0]
try:
gpaste.AddPassword(name, password, dbus_interface="org.gnome.GPaste1")
except dbus.DBusException:
gpaste.AddPassword(name, password, dbus_interface="org.gnome.GPaste2")
raise RuntimeError(f"The field {field} was not found in the pass file.")
else:
password = output.split("\n", 1)[0]
try:
gpaste.AddPassword(name, password, dbus_interface="org.gnome.GPaste1")
except dbus.DBusException:
gpaste.AddPassword(name, password, dbus_interface="org.gnome.GPaste2")
def send_password_to_native_clipboard(self, base_args, name, field=None):
if field is not None or self.use_bw:
raise RuntimeError("This feature requires GPaste.")
result = subprocess.run(base_args + ["-c", name])
if result.returncode:
raise RuntimeError(
f"Error while running pass: got return code {result.returncode}."
)
def send_password_to_clipboard(self, name):
field = None
if self.use_bw:
base_args = ["rbw", "get"]
elif name.startswith("otp "):
base_args = ["pass", "otp", "code"]
name = name[4:]
else:
base_args = ["pass", "show"]
if name.startswith(":"):
field, name = name.split(" ", 1)
field = field[1:]
try:
try:
self.send_password_to_gpaste(base_args, name, field)
except dbus.DBusException:
# We couldn't join GPaste over D-Bus, use native clipboard
self.send_password_to_native_clipboard(base_args, name, field)
if "otp" in base_args:
self.notify("Copied OTP password to clipboard:", body=f"<b>{name}</b>")
elif field is not None:
@ -156,45 +207,8 @@ class SearchPassService(dbus.service.Object):
)
else:
self.notify("Copied password to clipboard:", body=f"<b>{name}</b>")
except subprocess.CalledProcessError as e:
self.notify("Failed to copy password!", body=e.output, error=True)
except RuntimeError as e:
self.notify("Failed to copy field!", body=e.output, error=True)
def send_password_to_native_clipboard(self, base_args, name, field=None):
if field is not None:
self.notify(
"Cannot copy field values.",
body="This feature requires GPaste.",
error=True,
)
return
pass_cmd = subprocess.run(base_args + ["-c", name])
if pass_cmd.returncode:
self.notify("Failed to copy password!", error=True)
elif "otp" in base_args:
self.notify("Copied OTP password to clipboard:", body=f"<b>{name}</b>")
else:
self.notify("Copied password to clipboard:", body=f"<b>{name}</b>")
def send_password_to_clipboard(self, name):
field = None
if name.startswith("otp "):
base_args = ["pass", "otp", "code"]
name = name[4:]
else:
base_args = ["pass", "show"]
if name.startswith(":"):
field, name = name.split(" ", 1)
field = field[1:]
try:
self.send_password_to_gpaste(base_args, name, field)
except dbus.DBusException:
# We couldn't join GPaste over D-Bus,
# use pass native clipboard copy
self.send_password_to_native_clipboard(base_args, name, field)
except (subprocess.CalledProcessError, FileNotFoundError, RuntimeError) as e:
self.notify("Failed to copy password or field!", body=str(e), error=True)
def notify(self, message, body="", error=False):
try:

View File

@ -16,7 +16,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-build
%global debug_package %{nil}
%description
A Gnome Shell search provider for zx2c4/pass (passwordstore.org) that sends passwords to clipboard (or GPaste).
A Gnome Shell passwords search provider for zx2c4/pass (passwordstore.org) and Bitwarden/Vaultwarden that sends passwords to clipboard (or GPaste).
%prep
%setup -q -n %{name}-%{version}