Skip to content

Instantly share code, notes, and snippets.

@fqxp
Last active August 11, 2017 14:20
Show Gist options
  • Save fqxp/c2cfac38fc584dabcc72ff2ef4a09d9c to your computer and use it in GitHub Desktop.
Save fqxp/c2cfac38fc584dabcc72ff2ef4a09d9c to your computer and use it in GitHub Desktop.
Export LastPass passwords to pass (https://www.passwordstore.org/)
#!/usr/bin/env python3
#
# Prerequisites:
# * Install lpass command (ArchLinux: lastpass-cli package)
# and run `lpass login` once
# * or export passwords from Lastpass in CVS format.
# * Create a file `.lastpass2pass.blacklist` in your $HOME and optionally
# enter group names to be blacklisted, one per line.
#
# Usage:
# 1. If lpass is installed, simply run `lastpass2pass`.
#
# If you exported the passwords to `PASSWORDS.CSV`, run `lastpass2pass <PASSWORDS.CSV`.
#
# 2. Voilà!
from collections import Counter
import csv
import os.path
import subprocess
import sys
from urllib.parse import urlparse
BLACKLIST_GROUPS = list(map(str.strip, open(os.path.expanduser('~/.lastpass2pass.blacklist')).readlines()))
def open_export_pipe():
if len(sys.argv) > 1:
filename = sys.argv[1]
return sys.stdin
else:
return subprocess.Popen(['lpass', 'export'], stdout=subprocess.PIPE, encoding='utf-8').stdout
def read_lastpass_csv(fd):
reader = csv.DictReader(fd)
for entry in reader:
yield {
'path': build_path(entry),
'content': build_content(entry),
'group': entry['grouping']
}
def build_path(entry):
parsed_url = urlparse(entry['url'])
if parsed_url.netloc not in ('', 'sn'):
path = [entry['grouping'], 'www', parsed_url.netloc]
else:
path = [entry['grouping'], 'other', entry['name']]
if entry['username'].strip():
path.append(entry['username'])
return '/'.join(path)
def build_content(entry):
return '''%(password)s
name: %(name)s
url: %(url)s
username: %(username)s
extra: %(extra)s
''' % entry
def filter_blacklisted(pass_entries):
return [
pass_entry
for pass_entry in pass_entries
if pass_entry['group'] not in BLACKLIST_GROUPS
]
def warn_duplicates(pass_entries):
counter = Counter(pass_entry['path'] for pass_entry in pass_entries)
duplicate_paths = (path for path, count in counter.items() if count > 1)
for path in duplicate_paths:
print('WARNING: Duplicate path "%s"' % path)
def build_pass_dict(pass_entries):
result = dict()
for pass_entry in pass_entries:
result[pass_entry['path']] = pass_entry['content']
return result
def insert_into_pass(path, content):
process = subprocess.Popen(
['pass', 'insert', '--force', '--multiline', bytes(path, 'utf-8')],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
process.communicate(bytes(content, 'utf-8'))
if process.returncode != 0:
print ('WARNING: Couldn’t insert password %s' % path)
if __name__ == '__main__':
pass_entries = filter_blacklisted((read_lastpass_csv(open_export_pipe())))
warn_duplicates(pass_entries)
pass_entries_dict = build_pass_dict(pass_entries)
i = 0
for path, content in pass_entries_dict.items():
i += 1
sys.stdout.write('Inserting passwords ... (%d of %d)\r' % (i, len(pass_entries_dict)))
insert_into_pass(path, content)
print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment