Add option to copy other field values.

This commit is contained in:
J. Nathanael Philipp 2019-09-13 00:17:19 +02:00
parent e275953813
commit 706bee9e36
No known key found for this signature in database
GPG Key ID: A5CFB223570629B4

View File

@ -26,17 +26,15 @@
# Author: Luke Macken <lmacken@redhat.com> # Author: Luke Macken <lmacken@redhat.com>
from fuzzywuzzy import process, fuzz from fuzzywuzzy import process, fuzz
from os import getenv from gi.repository import GLib
from os import walk from os import getenv, walk
from os.path import expanduser from os.path import expanduser, join as path_join
from os.path import join as path_join
import re
import subprocess
import dbus import dbus
import dbus.glib import dbus.glib
import dbus.service import dbus.service
from gi.repository import GLib import re
import subprocess
# Convenience shorthand for declaring dbus interface methods. # Convenience shorthand for declaring dbus interface methods.
# s.b.n. -> search_bus_name # s.b.n. -> search_bus_name
@ -52,7 +50,6 @@ class SearchPassService(dbus.service.Object):
""" """
bus_name = 'org.gnome.Pass.SearchProvider' bus_name = 'org.gnome.Pass.SearchProvider'
_object_path = '/' + bus_name.replace('.', '/') _object_path = '/' + bus_name.replace('.', '/')
def __init__(self): def __init__(self):
@ -72,7 +69,8 @@ class SearchPassService(dbus.service.Object):
@dbus.service.method(in_signature='as', out_signature='aa{sv}', **sbn) @dbus.service.method(in_signature='as', out_signature='aa{sv}', **sbn)
def GetResultMetas(self, ids): def GetResultMetas(self, ids):
return [dict(id=id, name=id, gicon='dialog-password') for id in ids] return [dict(id=id, name=id[1:] if id.startswith(':') else id,
gicon='dialog-password') for id in ids]
@dbus.service.method(in_signature='asas', out_signature='as', **sbn) @dbus.service.method(in_signature='asas', out_signature='as', **sbn)
def GetSubsearchResultSet(self, previous_results, new_terms): def GetSubsearchResultSet(self, previous_results, new_terms):
@ -84,14 +82,15 @@ class SearchPassService(dbus.service.Object):
def get_result_set(self, terms): def get_result_set(self, terms):
if terms[0] == 'otp': if terms[0] == 'otp':
otp = True field = terms[0]
elif terms[0].startswith(':'):
field = terms[0][1:]
terms = terms[1:] terms = terms[1:]
else: else:
otp = False field = None
name = ''.join(terms) name = ''.join(terms)
password_list = [] password_list = []
for root, dirs, files in walk(self.password_store): for root, dirs, files in walk(self.password_store):
dir_path = root[len(self.password_store) + 1:] dir_path = root[len(self.password_store) + 1:]
@ -106,16 +105,28 @@ class SearchPassService(dbus.service.Object):
results = [e[0] for e in process.extract(name, password_list, limit=5, results = [e[0] for e in process.extract(name, password_list, limit=5,
scorer=fuzz.partial_ratio)] scorer=fuzz.partial_ratio)]
if otp: if field == 'otp':
results = [f'otp {r}' for r in results] results = [f'otp {r}' for r in results]
elif field is not None:
results = [f':{field} {r}' for r in results]
return results return results
def send_password_to_gpaste(self, base_args, name): def send_password_to_gpaste(self, base_args, name, field=None):
try: try:
pass_output = subprocess.check_output(base_args + [name], output = subprocess.check_output(base_args + [name],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
universal_newlines=True) universal_newlines=True)
password = pass_output.split('\n', 1)[0] 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} not found in pass' +
' file.')
else:
password = output.split('\n', 1)[0]
self.session_bus.get_object( self.session_bus.get_object(
'org.gnome.GPaste.Daemon', 'org.gnome.GPaste.Daemon',
'/org/gnome/GPaste' '/org/gnome/GPaste'
@ -128,14 +139,23 @@ class SearchPassService(dbus.service.Object):
if 'otp' in base_args: if 'otp' in base_args:
self.notify('Copied OTP password to clipboard:', self.notify('Copied OTP password to clipboard:',
body=f'<b>{name}</b>') body=f'<b>{name}</b>')
elif field is not None:
self.notify(f'Copied field {field} to clipboard:',
body=f'<b>{name}</b>')
else: else:
self.notify('Copied password to clipboard:', self.notify('Copied password to clipboard:',
body=f'<b>{name}</b>') body=f'<b>{name}</b>')
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as e:
self.notify('Failed to copy password!', body=error.output, self.notify('Failed to copy password!', body=e.output, error=True)
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(f'Cannot copy field values.',
body='This feature requires GPaste.', error=True)
return
def send_password_to_native_clipboard(self, base_args, name):
pass_cmd = subprocess.run(base_args + ['-c', name]) pass_cmd = subprocess.run(base_args + ['-c', name])
if pass_cmd.returncode: if pass_cmd.returncode:
self.notify('Failed to copy password!', error=True) self.notify('Failed to copy password!', error=True)
@ -146,18 +166,22 @@ class SearchPassService(dbus.service.Object):
self.notify('Copied password to clipboard:', body=f'<b>{name}</b>') self.notify('Copied password to clipboard:', body=f'<b>{name}</b>')
def send_password_to_clipboard(self, name): def send_password_to_clipboard(self, name):
field = None
if name.startswith('otp '): if name.startswith('otp '):
base_args = ['pass', 'otp', 'code'] base_args = ['pass', 'otp', 'code']
name = name[4:] name = name[4:]
else: else:
base_args = ['pass', 'show'] 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) self.send_password_to_gpaste(base_args, name, field)
except dbus.DBusException: except dbus.DBusException:
# We couldn't join GPaste over D-Bus, # We couldn't join GPaste over D-Bus,
# use pass native clipboard copy # use pass native clipboard copy
self.send_password_to_native_clipboard(base_args, name) self.send_password_to_native_clipboard(base_args, name, field)
def notify(self, message, body='', error=False): def notify(self, message, body='', error=False):
try: try: