Last active
August 11, 2017 14:20
-
-
Save fqxp/c2cfac38fc584dabcc72ff2ef4a09d9c to your computer and use it in GitHub Desktop.
Export LastPass passwords to pass (https://www.passwordstore.org/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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