Created
March 18, 2019 12:27
-
-
Save lainiwa/b617fc2df978116a7483437bdab2444c to your computer and use it in GitHub Desktop.
Import chromium passwords to passwordstore
This file contains hidden or 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
| """Import data to passwordstore from chromium.""" | |
| import os.path | |
| import re | |
| import sqlite3 | |
| import sys | |
| from subprocess import PIPE, Popen | |
| from urllib.parse import urlparse | |
| def get_db(path): | |
| """Get database connection and cursor.""" | |
| connection = sqlite3.connect(os.path.expanduser(path)) | |
| try: | |
| cursor = connection.execute('SELECT * FROM logins') | |
| except sqlite3.OperationalError: | |
| print('Could not open chromium DB. Did you close the browser first?', | |
| file=sys.stderr) | |
| exit(1) | |
| return connection, cursor | |
| def cursor_to_dicts(cursor, gets=None, renames=None): | |
| """Fetch data into a iterator of dictionaries.""" | |
| names = [x[0] for x in cursor.description] | |
| if renames is None: | |
| renames = dict() | |
| if gets is None: | |
| gets = names | |
| for values in cursor: | |
| yield { | |
| renames.get(name, name): value | |
| for name, value in zip(names, values) | |
| if name in gets | |
| } | |
| def pass_import_entry(path, data): | |
| """Import new password entry to passwordstore.""" | |
| proc = Popen(['pass', 'insert', '--multiline', path], stdin=PIPE, | |
| stdout=PIPE) | |
| proc.communicate(data.encode('utf8')) | |
| proc.wait() | |
| def pprint_row(row, data): | |
| """Print added entry with asterisk'ed passwords.""" | |
| max_url = max([len(row['url']) for row in data]) | |
| max_name = max([len(row['username_value']) for row in data]) | |
| max_pass = max([len(row['password_value']) for row in data]) | |
| fmt = f'%{max_url}s %{max_name}s %{max_pass}s' | |
| print(fmt % ( | |
| row['url'], | |
| row['username_value'], | |
| '*' * len(row['password_value']) | |
| )) | |
| # Connect to DB and get cursor | |
| connection, cursor = get_db('~/.config/chromium/Default/Login Data') | |
| # Get passwords with the desired additional fields | |
| data = cursor_to_dicts( | |
| cursor, ['signon_realm', 'password_value', 'username_value']) | |
| # Remove dupes | |
| data = [dict(t) for t in {tuple(row.items()) for row in data}] | |
| # Remove empty passwords | |
| data = [row for row in data if row['password_value'] != b''] | |
| # Change signon_realm into a "basic url" and a port | |
| for row in data: | |
| row['url'] = urlparse(row['signon_realm']).hostname | |
| port = urlparse(row['signon_realm']).port | |
| if port is not None: | |
| row['port'] = port | |
| del row['signon_realm'] | |
| # Remove www from urls | |
| data = [{ | |
| name: (re.sub('^www.', '', value) if name == 'url' else value) | |
| for name, value in row.items() | |
| } for row in data] | |
| # Sorted by website | |
| data = sorted(data, key=lambda row: row['url']) | |
| # Add passwords to www folder | |
| for row in data: | |
| pprint_row(row, data) | |
| lines = [ | |
| row['password_value'].decode('utf-8'), | |
| 'Username: ' + row['username_value'] | |
| ] | |
| if port in row: | |
| lines.append('Port: ' + row['port']) | |
| lines.append('') | |
| pass_import_entry('www/' + row['url'], '\n'.join(lines)) | |
| # Close all stuff | |
| connection.commit() | |
| connection.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment