Commit c8617ce8ae
Unregistered key
Layout: unified · split
.github/workflows/pylint.yml +20 −6
| @@ -1,23 +1,37 @@ | |||
| 1 | name: Pylint | 1 | name: Pylint |
| 2 | 2 | ||
| 3 | on: [push] | 3 | on: |
| 4 | push: | ||
| 5 | paths: | ||
| 6 | - '**.py' | ||
| 4 | 7 | ||
| 5 | jobs: | 8 | jobs: |
| 6 | build: | 9 | build: |
| 7 | runs-on: ubuntu-latest | 10 | runs-on: ubuntu-latest |
| 8 | strategy: | 11 | strategy: |
| 9 | matrix: | 12 | matrix: |
| 10 | python-version: ["3.8", "3.9", "3.10"] | 13 | python-version: ["3.x"] |
| 11 | steps: | 14 | steps: |
| 12 | - uses: actions/checkout@v4 | 15 | - uses: actions/checkout@v4 |
| 13 | - name: Set up Python ${{ matrix.python-version }} | 16 | - name: Set up Python ${{ matrix.python-version }} |
| 14 | uses: actions/setup-python@v4 | 17 | uses: actions/setup-python@v5 |
| 15 | with: | 18 | with: |
| 16 | python-version: ${{ matrix.python-version }} | 19 | python-version: ${{ matrix.python-version }} |
| 20 | ref: ${{ github.event.pull_request.head.ref }} | ||
| 17 | - name: Install dependencies | 21 | - name: Install dependencies |
| 18 | run: | | 22 | run: | |
| 19 | python -m pip install --upgrade pip | 23 | python -m pip install --upgrade pip |
| 20 | pip install pylint cryptography prettytable | 24 | pip install cryptography prettytable |
| 21 | - name: Analysing the code with pylint | 25 | - name: Install Ruff |
| 26 | uses: astral-sh/ruff-action@v3.2.2 | ||
| 27 | - name: Ruff Actions | ||
| 22 | run: | | 28 | run: | |
| 23 | pylint -d R0801 $(git ls-files '*.py') | 29 | ruff check --fix |
| 30 | ruff format | ||
| 31 | - name: Add and Commit | ||
| 32 | uses: EndBug/add-and-commit@v9 | ||
| 33 | env: | ||
| 34 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| 35 | with: | ||
| 36 | default_author: github_actions | ||
| 37 | pathspec_error_handling: ignore\ No newline at end of file | ||
yoshi/__main__.py +1
| @@ -4,4 +4,5 @@ Entry point for the yoshi cli app. | |||
| 4 | 4 | ||
| 5 | if __name__ == "__main__": | 5 | if __name__ == "__main__": |
| 6 | from yoshi.cli import yoshi | 6 | from yoshi.cli import yoshi |
| 7 | |||
| 7 | yoshi() | 8 | yoshi() |
yoshi/account.py +15 −9
| @@ -11,9 +11,14 @@ from yoshi import database | |||
| 11 | class Account: | 11 | class Account: |
| 12 | """Represents a login account.""" | 12 | """Represents a login account.""" |
| 13 | 13 | ||
| 14 | def __init__(self, uuid: str, application: str, #pylint: disable=R0913,R0917 | 14 | def __init__( |
| 15 | username: str, #pylint: disable=R0913,R0917 | 15 | self, |
| 16 | password: str, url: str) -> None: #pylint: disable=R0913,R0917 | 16 | uuid: str, |
| 17 | application: str, # pylint: disable=R0913,R0917 | ||
| 18 | username: str, # pylint: disable=R0913,R0917 | ||
| 19 | password: str, | ||
| 20 | url: str, | ||
| 21 | ) -> None: # pylint: disable=R0913,R0917 | ||
| 17 | self.uuid = uuid | 22 | self.uuid = uuid |
| 18 | self.application = application | 23 | self.application = application |
| 19 | self.username = username | 24 | self.username = username |
| @@ -22,16 +27,17 @@ class Account: | |||
| 22 | 27 | ||
| 23 | def display_account(self) -> None: | 28 | def display_account(self) -> None: |
| 24 | """Print the account details.""" | 29 | """Print the account details.""" |
| 25 | print('ID:', self.uuid) | 30 | print("ID:", self.uuid) |
| 26 | print('Application:', self.application) | 31 | print("Application:", self.application) |
| 27 | print('Username:', self.username) | 32 | print("Username:", self.username) |
| 28 | print('Password:', self.password) | 33 | print("Password:", self.password) |
| 29 | print('URL:', self.url) | 34 | print("URL:", self.url) |
| 30 | 35 | ||
| 31 | def save_account(self) -> None: | 36 | def save_account(self) -> None: |
| 32 | """Save the account details to the database.""" | 37 | """Save the account details to the database.""" |
| 33 | database.add_account( | 38 | database.add_account( |
| 34 | self.uuid, self.application, self.username, self.password, self.url) | 39 | self.uuid, self.application, self.username, self.password, self.url |
| 40 | ) | ||
| 35 | 41 | ||
| 36 | def delete_account(self) -> bool: | 42 | def delete_account(self) -> bool: |
| 37 | """Delete the account from the database. | 43 | """Delete the account from the database. |
yoshi/cli.py +38 −52
| @@ -7,91 +7,76 @@ It imports the required modules and sets up a parser with basic options for demo | |||
| 7 | import argparse | 7 | import argparse |
| 8 | from yoshi import crypto, database, process | 8 | from yoshi import crypto, database, process |
| 9 | 9 | ||
| 10 | |||
| 10 | def yoshi(): | 11 | def yoshi(): |
| 11 | """ | 12 | """ |
| 12 | CLI entry point logic, used to parse user inputs. | 13 | CLI entry point logic, used to parse user inputs. |
| 13 | """ | 14 | """ |
| 14 | parser = argparse.ArgumentParser( | 15 | parser = argparse.ArgumentParser( |
| 15 | description='Manage your username and passwords via a convenient CLI vault.' | 16 | description="Manage your username and passwords via a convenient CLI vault." |
| 16 | ) | 17 | ) |
| 17 | 18 | ||
| 18 | # Top-level arguments | 19 | # Top-level arguments |
| 19 | group_one = parser.add_mutually_exclusive_group() | 20 | group_one = parser.add_mutually_exclusive_group() |
| 20 | group_one.add_argument( | 21 | group_one.add_argument( |
| 21 | '-n', '--new', | 22 | "-n", "--new", help="Create a new account.", action="store_true" |
| 22 | help='Create a new account.', | ||
| 23 | action='store_true' | ||
| 24 | ) | 23 | ) |
| 25 | group_one.add_argument( | 24 | group_one.add_argument( |
| 26 | '-l', '--list', | 25 | "-l", "--list", help="List all saved accounts.", action="store_true" |
| 27 | help='List all saved accounts.', | ||
| 28 | action='store_true' | ||
| 29 | ) | 26 | ) |
| 30 | group_one.add_argument( | 27 | group_one.add_argument( |
| 31 | '-e', '--edit', | 28 | "-e", "--edit", help="Edit a saved account.", action="store_true" |
| 32 | help='Edit a saved account.', | ||
| 33 | action='store_true' | ||
| 34 | ) | 29 | ) |
| 35 | group_one.add_argument( | 30 | group_one.add_argument( |
| 36 | '-d', '--delete', | 31 | "-d", "--delete", help="Delete a saved account.", action="store_true" |
| 37 | help='Delete a saved account.', | ||
| 38 | action='store_true' | ||
| 39 | ) | 32 | ) |
| 40 | group_one.add_argument( | 33 | group_one.add_argument( |
| 41 | '--purge', | 34 | "--purge", |
| 42 | help=( | 35 | help=( |
| 43 | 'Purge all accounts and delete the vault. ' | 36 | "Purge all accounts and delete the vault. " |
| 44 | '(Caution: this will irreversibly destroy your data.)' | 37 | "(Caution: this will irreversibly destroy your data.)" |
| 45 | ), | 38 | ), |
| 46 | action='store_true' | 39 | action="store_true", |
| 47 | ) | ||
| 48 | group_one.add_argument( | ||
| 49 | '--encrypt', | ||
| 50 | help='Encrypt the vault.', | ||
| 51 | action='store_true' | ||
| 52 | ) | ||
| 53 | group_one.add_argument( | ||
| 54 | '--decrypt', | ||
| 55 | help='Decrypt the vault.', | ||
| 56 | action='store_true' | ||
| 57 | ) | 40 | ) |
| 41 | group_one.add_argument("--encrypt", help="Encrypt the vault.", action="store_true") | ||
| 42 | group_one.add_argument("--decrypt", help="Decrypt the vault.", action="store_true") | ||
| 58 | 43 | ||
| 59 | # Encryption flags | 44 | # Encryption flags |
| 60 | group_two = parser.add_mutually_exclusive_group() | 45 | group_two = parser.add_mutually_exclusive_group() |
| 61 | group_two.add_argument( | 46 | group_two.add_argument( |
| 62 | '-g', '--generate', | 47 | "-g", |
| 63 | help=( | 48 | "--generate", |
| 64 | 'When using the --encrypt option, generate a new encryption key.' | 49 | help=("When using the --encrypt option, generate a new encryption key."), |
| 65 | ), | 50 | action="store_true", |
| 66 | action='store_true' | ||
| 67 | ) | 51 | ) |
| 68 | group_two.add_argument( | 52 | group_two.add_argument( |
| 69 | '-k', '--keyfile', | 53 | "-k", |
| 70 | help='Path to existing key file.', | 54 | "--keyfile", |
| 71 | action='store', | 55 | help="Path to existing key file.", |
| 56 | action="store", | ||
| 72 | nargs=1, | 57 | nargs=1, |
| 73 | type=str | 58 | type=str, |
| 74 | ) | 59 | ) |
| 75 | 60 | ||
| 76 | # Edit flags | 61 | # Edit flags |
| 77 | group_three = parser.add_argument_group() | 62 | group_three = parser.add_argument_group() |
| 78 | group_three.add_argument( | 63 | group_three.add_argument( |
| 79 | '-u', '--uuid', | 64 | "-u", |
| 80 | help=( | 65 | "--uuid", |
| 81 | 'When using the --edit or --delete options, provide the account UUID.' | 66 | help=("When using the --edit or --delete options, provide the account UUID."), |
| 82 | ), | 67 | action="store", |
| 83 | action='store', | ||
| 84 | nargs=1, | 68 | nargs=1, |
| 85 | type=str | 69 | type=str, |
| 86 | ) | 70 | ) |
| 87 | group_three.add_argument( | 71 | group_three.add_argument( |
| 88 | '-f', '--field', | 72 | "-f", |
| 73 | "--field", | ||
| 89 | help=( | 74 | help=( |
| 90 | 'When using the --edit option, specify the field to edit (integer index).' | 75 | "When using the --edit option, specify the field to edit (integer index)." |
| 91 | ), | 76 | ), |
| 92 | action='store', | 77 | action="store", |
| 93 | nargs=1, | 78 | nargs=1, |
| 94 | type=int | 79 | type=int, |
| 95 | ) | 80 | ) |
| 96 | 81 | ||
| 97 | args = parser.parse_args() | 82 | args = parser.parse_args() |
| @@ -100,22 +85,22 @@ def yoshi(): | |||
| 100 | if args.keyfile: | 85 | if args.keyfile: |
| 101 | key = crypto.load_key(args.keyfile[0]) | 86 | key = crypto.load_key(args.keyfile[0]) |
| 102 | else: | 87 | else: |
| 103 | key = input('Please enter your decryption key: ') | 88 | key = input("Please enter your decryption key: ") |
| 104 | crypto.decrypt(key) | 89 | crypto.decrypt(key) |
| 105 | elif args.encrypt: | 90 | elif args.encrypt: |
| 106 | if args.generate: | 91 | if args.generate: |
| 107 | key = crypto.generate_key() | 92 | key = crypto.generate_key() |
| 108 | print( | 93 | print( |
| 109 | 'WRITE THIS KEY DOWN SOMEWHERE SAFE. YOU WILL NOT BE ABLE TO DECRYPT ' | 94 | "WRITE THIS KEY DOWN SOMEWHERE SAFE. YOU WILL NOT BE ABLE TO DECRYPT " |
| 110 | 'YOUR DATA WITHOUT IT!' | 95 | "YOUR DATA WITHOUT IT!" |
| 111 | ) | 96 | ) |
| 112 | print(key.decode()) | 97 | print(key.decode()) |
| 113 | print('\n') | 98 | print("\n") |
| 114 | else: | 99 | else: |
| 115 | if args.keyfile: | 100 | if args.keyfile: |
| 116 | key = crypto.load_key(args.keyfile[0]) | 101 | key = crypto.load_key(args.keyfile[0]) |
| 117 | else: | 102 | else: |
| 118 | key = input('Please enter your encryption key: ') | 103 | key = input("Please enter your encryption key: ") |
| 119 | crypto.encrypt(key) | 104 | crypto.encrypt(key) |
| 120 | elif database.check_table(): | 105 | elif database.check_table(): |
| 121 | if args.new: | 106 | if args.new: |
| @@ -130,8 +115,9 @@ def yoshi(): | |||
| 130 | process.purge_accounts() | 115 | process.purge_accounts() |
| 131 | else: | 116 | else: |
| 132 | raise TypeError( | 117 | raise TypeError( |
| 133 | 'Please specify a command or use the --help flag for more information.' | 118 | "Please specify a command or use the --help flag for more information." |
| 134 | ) | 119 | ) |
| 135 | 120 | ||
| 121 | |||
| 136 | if __name__ == "__main__": | 122 | if __name__ == "__main__": |
| 137 | yoshi() | 123 | yoshi() |
yoshi/crypto.py +6 −6
| @@ -6,7 +6,7 @@ It allows for secure encryption and decryption of data using a secret key. | |||
| 6 | 6 | ||
| 7 | from cryptography.fernet import Fernet | 7 | from cryptography.fernet import Fernet |
| 8 | 8 | ||
| 9 | VAULT_FILE = 'vault.sqlite' | 9 | VAULT_FILE = "vault.sqlite" |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | def generate_key() -> bytes: | 12 | def generate_key() -> bytes: |
| @@ -21,25 +21,25 @@ def load_key(key_file: str) -> bytes: | |||
| 21 | Args: | 21 | Args: |
| 22 | key_file (str): Path to the key file. | 22 | key_file (str): Path to the key file. |
| 23 | """ | 23 | """ |
| 24 | with open(key_file, 'rb') as key: | 24 | with open(key_file, "rb") as key: |
| 25 | return key.read() | 25 | return key.read() |
| 26 | 26 | ||
| 27 | 27 | ||
| 28 | def encrypt(key: bytes, filename: str = VAULT_FILE) -> None: | 28 | def encrypt(key: bytes, filename: str = VAULT_FILE) -> None: |
| 29 | """Encrypts the data in the specified file using the provided key.""" | 29 | """Encrypts the data in the specified file using the provided key.""" |
| 30 | f = Fernet(key) | 30 | f = Fernet(key) |
| 31 | with open(filename, 'rb') as vault: | 31 | with open(filename, "rb") as vault: |
| 32 | data = vault.read() | 32 | data = vault.read() |
| 33 | encrypted_data = f.encrypt(data) | 33 | encrypted_data = f.encrypt(data) |
| 34 | with open(filename, 'wb') as vault: | 34 | with open(filename, "wb") as vault: |
| 35 | vault.write(encrypted_data) | 35 | vault.write(encrypted_data) |
| 36 | 36 | ||
| 37 | 37 | ||
| 38 | def decrypt(key: bytes, filename: str = VAULT_FILE) -> None: | 38 | def decrypt(key: bytes, filename: str = VAULT_FILE) -> None: |
| 39 | """Decrypts the data in the specified file using the provided key.""" | 39 | """Decrypts the data in the specified file using the provided key.""" |
| 40 | f = Fernet(key) | 40 | f = Fernet(key) |
| 41 | with open(filename, 'rb') as vault: | 41 | with open(filename, "rb") as vault: |
| 42 | encrypted_data = vault.read() | 42 | encrypted_data = vault.read() |
| 43 | decrypted_data = f.decrypt(encrypted_data) | 43 | decrypted_data = f.decrypt(encrypted_data) |
| 44 | with open(filename, 'wb') as vault: | 44 | with open(filename, "wb") as vault: |
| 45 | vault.write(decrypted_data) | 45 | vault.write(decrypted_data) |
yoshi/database.py +31 −29
| @@ -7,8 +7,8 @@ import sqlite3 | |||
| 7 | import sys | 7 | import sys |
| 8 | import os | 8 | import os |
| 9 | 9 | ||
| 10 | VAULT_DECRYPTED = 'vault.sqlite' | 10 | VAULT_DECRYPTED = "vault.sqlite" |
| 11 | VAULT_ENCRYPTED = 'vault.sqlite.aes' | 11 | VAULT_ENCRYPTED = "vault.sqlite.aes" |
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | def create_table() -> None: | 14 | def create_table() -> None: |
| @@ -16,8 +16,8 @@ def create_table() -> None: | |||
| 16 | db_connection = sqlite3.connect(VAULT_DECRYPTED) | 16 | db_connection = sqlite3.connect(VAULT_DECRYPTED) |
| 17 | cursor = db_connection.cursor() | 17 | cursor = db_connection.cursor() |
| 18 | cursor.execute( | 18 | cursor.execute( |
| 19 | ''' CREATE TABLE IF NOT EXISTS accounts (uuid text, application text, | 19 | """ CREATE TABLE IF NOT EXISTS accounts (uuid text, application text, |
| 20 | username text, password text, url text) ''' | 20 | username text, password text, url text) """ |
| 21 | ) | 21 | ) |
| 22 | db_connection.commit() | 22 | db_connection.commit() |
| 23 | db_connection.close() | 23 | db_connection.close() |
| @@ -29,17 +29,18 @@ def check_table() -> bool: | |||
| 29 | db_connection = sqlite3.connect(VAULT_DECRYPTED) | 29 | db_connection = sqlite3.connect(VAULT_DECRYPTED) |
| 30 | cursor = db_connection.cursor() | 30 | cursor = db_connection.cursor() |
| 31 | cursor.execute( | 31 | cursor.execute( |
| 32 | ''' SELECT count(name) FROM sqlite_master WHERE type='table' | 32 | """ SELECT count(name) FROM sqlite_master WHERE type='table' |
| 33 | AND name='accounts' ''' | 33 | AND name='accounts' """ |
| 34 | ) | 34 | ) |
| 35 | if cursor.fetchone()[0] != 1: | 35 | if cursor.fetchone()[0] != 1: |
| 36 | user_choice = input( | 36 | user_choice = input( |
| 37 | 'Password vault does not exist. Would you like to create it now? (y/n): ') | 37 | "Password vault does not exist. Would you like to create it now? (y/n): " |
| 38 | if user_choice.lower() == 'y': | 38 | ) |
| 39 | if user_choice.lower() == "y": | ||
| 39 | create_table() | 40 | create_table() |
| 40 | check = True | 41 | check = True |
| 41 | else: | 42 | else: |
| 42 | sys.exit('Program aborted upon user request.') | 43 | sys.exit("Program aborted upon user request.") |
| 43 | else: | 44 | else: |
| 44 | check = True | 45 | check = True |
| 45 | db_connection.commit() | 46 | db_connection.commit() |
| @@ -47,17 +48,22 @@ def check_table() -> bool: | |||
| 47 | return check | 48 | return check |
| 48 | 49 | ||
| 49 | 50 | ||
| 50 | def add_account(uuid: str, application: str, username: str, password: str, | 51 | def add_account( |
| 51 | url: str) -> None: | 52 | uuid: str, application: str, username: str, password: str, url: str |
| 53 | ) -> None: | ||
| 52 | """Add a new account within the vault database.""" | 54 | """Add a new account within the vault database.""" |
| 53 | db_connection = sqlite3.connect(VAULT_DECRYPTED) | 55 | db_connection = sqlite3.connect(VAULT_DECRYPTED) |
| 54 | cursor = db_connection.cursor() | 56 | cursor = db_connection.cursor() |
| 55 | cursor.execute( | 57 | cursor.execute( |
| 56 | ''' INSERT INTO accounts VALUES (:uuid,:application,:username, | 58 | """ INSERT INTO accounts VALUES (:uuid,:application,:username, |
| 57 | :password,:url) ''', { | 59 | :password,:url) """, |
| 58 | 'uuid': uuid, 'application': application, 'username': username, | 60 | { |
| 59 | 'password': password, 'url': url | 61 | "uuid": uuid, |
| 60 | } | 62 | "application": application, |
| 63 | "username": username, | ||
| 64 | "password": password, | ||
| 65 | "url": url, | ||
| 66 | }, | ||
| 61 | ) | 67 | ) |
| 62 | db_connection.commit() | 68 | db_connection.commit() |
| 63 | db_connection.close() | 69 | db_connection.close() |
| @@ -67,9 +73,7 @@ def delete_account(uuid: str) -> None: | |||
| 67 | """Delete an account within the vault database by its unique ID.""" | 73 | """Delete an account within the vault database by its unique ID.""" |
| 68 | db_connection = sqlite3.connect(VAULT_DECRYPTED) | 74 | db_connection = sqlite3.connect(VAULT_DECRYPTED) |
| 69 | cursor = db_connection.cursor() | 75 | cursor = db_connection.cursor() |
| 70 | cursor.execute( | 76 | cursor.execute(""" DELETE FROM accounts WHERE uuid = :uuid """, {"uuid": uuid}) |
| 71 | ''' DELETE FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid} | ||
| 72 | ) | ||
| 73 | db_connection.commit() | 77 | db_connection.commit() |
| 74 | db_connection.close() | 78 | db_connection.close() |
| 75 | 79 | ||
| @@ -78,9 +82,7 @@ def find_account(uuid: str) -> list: | |||
| 78 | """Find an account within the vault database by its unique ID.""" | 82 | """Find an account within the vault database by its unique ID.""" |
| 79 | db_connection = sqlite3.connect(VAULT_DECRYPTED) | 83 | db_connection = sqlite3.connect(VAULT_DECRYPTED) |
| 80 | cursor = db_connection.cursor() | 84 | cursor = db_connection.cursor() |
| 81 | cursor.execute( | 85 | cursor.execute(""" SELECT * FROM accounts WHERE uuid = :uuid """, {"uuid": uuid}) |
| 82 | ''' SELECT * FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid} | ||
| 83 | ) | ||
| 84 | account = cursor.fetchall() | 86 | account = cursor.fetchall() |
| 85 | db_connection.close() | 87 | db_connection.close() |
| 86 | return account | 88 | return account |
| @@ -90,7 +92,7 @@ def find_accounts() -> list: | |||
| 90 | """Return all accounts stored within the vault database.""" | 92 | """Return all accounts stored within the vault database.""" |
| 91 | db_connection = sqlite3.connect(VAULT_DECRYPTED) | 93 | db_connection = sqlite3.connect(VAULT_DECRYPTED) |
| 92 | cursor = db_connection.cursor() | 94 | cursor = db_connection.cursor() |
| 93 | cursor.execute(''' SELECT * FROM accounts ''') | 95 | cursor.execute(""" SELECT * FROM accounts """) |
| 94 | accounts = cursor.fetchall() | 96 | accounts = cursor.fetchall() |
| 95 | db_connection.close() | 97 | db_connection.close() |
| 96 | return accounts | 98 | return accounts |
| @@ -99,14 +101,14 @@ def find_accounts() -> list: | |||
| 99 | def update_account(field_name: str, new_value: str, uuid: str) -> None: | 101 | def update_account(field_name: str, new_value: str, uuid: str) -> None: |
| 100 | """Update an account within the vault database by its unique ID.""" | 102 | """Update an account within the vault database by its unique ID.""" |
| 101 | queries = { | 103 | queries = { |
| 102 | 'application': 'UPDATE accounts SET application = :new_value WHERE uuid = :uuid', | 104 | "application": "UPDATE accounts SET application = :new_value WHERE uuid = :uuid", |
| 103 | 'username': 'UPDATE accounts SET username = :new_value WHERE uuid = :uuid', | 105 | "username": "UPDATE accounts SET username = :new_value WHERE uuid = :uuid", |
| 104 | 'password': 'UPDATE accounts SET password = :new_value WHERE uuid = :uuid', | 106 | "password": "UPDATE accounts SET password = :new_value WHERE uuid = :uuid", |
| 105 | 'url': 'UPDATE accounts SET url = :new_value WHERE uuid = :uuid' | 107 | "url": "UPDATE accounts SET url = :new_value WHERE uuid = :uuid", |
| 106 | } | 108 | } |
| 107 | db_connection = sqlite3.connect(VAULT_DECRYPTED) | 109 | db_connection = sqlite3.connect(VAULT_DECRYPTED) |
| 108 | cursor = db_connection.cursor() | 110 | cursor = db_connection.cursor() |
| 109 | cursor.execute(queries[field_name], {'new_value': new_value, 'uuid': uuid}) | 111 | cursor.execute(queries[field_name], {"new_value": new_value, "uuid": uuid}) |
| 110 | db_connection.commit() | 112 | db_connection.commit() |
| 111 | db_connection.close() | 113 | db_connection.close() |
| 112 | 114 | ||
| @@ -115,7 +117,7 @@ def purge_table() -> None: | |||
| 115 | """Purge the 'accounts' table within the vault database.""" | 117 | """Purge the 'accounts' table within the vault database.""" |
| 116 | db_connection = sqlite3.connect(VAULT_DECRYPTED) | 118 | db_connection = sqlite3.connect(VAULT_DECRYPTED) |
| 117 | cursor = db_connection.cursor() | 119 | cursor = db_connection.cursor() |
| 118 | cursor.execute(''' DROP TABLE accounts ''') | 120 | cursor.execute(""" DROP TABLE accounts """) |
| 119 | db_connection.commit() | 121 | db_connection.commit() |
| 120 | db_connection.close() | 122 | db_connection.close() |
| 121 | 123 | ||
yoshi/process.py +58 −47
| @@ -32,6 +32,7 @@ from yoshi.account import Account | |||
| 32 | from yoshi import database | 32 | from yoshi import database |
| 33 | from yoshi.wordlist import WORDLIST | 33 | from yoshi.wordlist import WORDLIST |
| 34 | 34 | ||
| 35 | |||
| 35 | def generate_characters(n: int) -> list: | 36 | def generate_characters(n: int) -> list: |
| 36 | """ | 37 | """ |
| 37 | Generates a list of n random characters from the set of ASCII letters, | 38 | Generates a list of n random characters from the set of ASCII letters, |
| @@ -61,7 +62,7 @@ def shuffle_characters(characters: list) -> str: | |||
| 61 | str: A string representation of the shuffled characters | 62 | str: A string representation of the shuffled characters |
| 62 | """ | 63 | """ |
| 63 | random.shuffle(characters) | 64 | random.shuffle(characters) |
| 64 | character_string = ''.join(characters) | 65 | character_string = "".join(characters) |
| 65 | return character_string | 66 | return character_string |
| 66 | 67 | ||
| 67 | 68 | ||
| @@ -96,7 +97,7 @@ def list_accounts() -> None: | |||
| 96 | None | 97 | None |
| 97 | """ | 98 | """ |
| 98 | accounts = database.find_accounts() | 99 | accounts = database.find_accounts() |
| 99 | t = PrettyTable(['UUID', 'Application', 'Username', 'Password', 'URL']) | 100 | t = PrettyTable(["UUID", "Application", "Username", "Password", "URL"]) |
| 100 | for account in accounts: | 101 | for account in accounts: |
| 101 | t.add_row([account[0], account[1], account[2], account[3], account[4]]) | 102 | t.add_row([account[0], account[1], account[2], account[3], account[4]]) |
| 102 | print(t) | 103 | print(t) |
| @@ -113,13 +114,15 @@ def delete_account(account_uuid: str) -> None: | |||
| 113 | None | 114 | None |
| 114 | """ | 115 | """ |
| 115 | account_record = database.find_account(account_uuid) | 116 | account_record = database.find_account(account_uuid) |
| 116 | account = Account(account_record[0][0], | 117 | account = Account( |
| 117 | account_record[0][1], | 118 | account_record[0][0], |
| 118 | account_record[0][2], | 119 | account_record[0][1], |
| 119 | account_record[0][3], | 120 | account_record[0][2], |
| 120 | account_record[0][4]) | 121 | account_record[0][3], |
| 122 | account_record[0][4], | ||
| 123 | ) | ||
| 121 | if account.delete_account(): | 124 | if account.delete_account(): |
| 122 | print('Account successfully deleted.') | 125 | print("Account successfully deleted.") |
| 123 | 126 | ||
| 124 | 127 | ||
| 125 | def purge_accounts() -> None: | 128 | def purge_accounts() -> None: |
| @@ -130,12 +133,15 @@ def purge_accounts() -> None: | |||
| 130 | None | 133 | None |
| 131 | """ | 134 | """ |
| 132 | check = input( | 135 | check = input( |
| 133 | '''Are you absolutely sure you want to delete your password vault? | 136 | """Are you absolutely sure you want to delete your password vault? |
| 134 | This action is irreversible. (y/n): ''') | 137 | This action is irreversible. (y/n): """ |
| 135 | if check.lower() == 'y': | 138 | ) |
| 139 | if check.lower() == "y": | ||
| 136 | database.purge_table() | 140 | database.purge_table() |
| 137 | database.purge_database() | 141 | database.purge_database() |
| 138 | print('The password vault has been purged. You may now exit or create a new one.') | 142 | print( |
| 143 | "The password vault has been purged. You may now exit or create a new one." | ||
| 144 | ) | ||
| 139 | 145 | ||
| 140 | 146 | ||
| 141 | def create_account() -> None: | 147 | def create_account() -> None: |
| @@ -145,46 +151,50 @@ def create_account() -> None: | |||
| 145 | Returns: | 151 | Returns: |
| 146 | None | 152 | None |
| 147 | """ | 153 | """ |
| 148 | application_string = input('Please enter a name for this account: ') | 154 | application_string = input("Please enter a name for this account: ") |
| 149 | username_string = input('Please enter your username for this account: ') | 155 | username_string = input("Please enter your username for this account: ") |
| 150 | url_string = input('(Optional) Please enter a URL for this account: ') | 156 | url_string = input("(Optional) Please enter a URL for this account: ") |
| 151 | 157 | ||
| 152 | password_type = input( | 158 | password_type = input( |
| 153 | '''Do you want a random character password (p), an XKCD-style passphrase | 159 | """Do you want a random character password (p), an XKCD-style passphrase |
| 154 | (x), or a custom password (c)? (p|x|c): ''' | 160 | (x), or a custom password (c)? (p|x|c): """ |
| 155 | ) | 161 | ) |
| 156 | if password_type not in ['p', 'x', 'c']: | 162 | if password_type not in ["p", "x", "c"]: |
| 157 | print('Error: Invalid choice. Please choose p, x, or c.') | 163 | print("Error: Invalid choice. Please choose p, x, or c.") |
| 158 | return | 164 | return |
| 159 | 165 | ||
| 160 | if password_type == 'x': | 166 | if password_type == "x": |
| 161 | password_length = int( | 167 | password_length = int( |
| 162 | input('Please enter number of words to include (min. 2): ') | 168 | input("Please enter number of words to include (min. 2): ") |
| 163 | ) | 169 | ) |
| 164 | if password_length < 3: | 170 | if password_length < 3: |
| 165 | print('Error: Your passphrase length must be at least 3 words.') | 171 | print("Error: Your passphrase length must be at least 3 words.") |
| 166 | return | 172 | return |
| 167 | password_separator = input( | 173 | password_separator = input( |
| 168 | 'Please enter your desired separator symbol (_,-, ~, etc.): ' | 174 | "Please enter your desired separator symbol (_,-, ~, etc.): " |
| 169 | ) | 175 | ) |
| 170 | password_string = generate_passphrase(password_length, password_separator) | 176 | password_string = generate_passphrase(password_length, password_separator) |
| 171 | elif password_type == 'p': | 177 | elif password_type == "p": |
| 172 | password_length = int( | 178 | password_length = int( |
| 173 | input('Please enter your desired password length (min. 8): ') | 179 | input("Please enter your desired password length (min. 8): ") |
| 174 | ) | 180 | ) |
| 175 | if password_length < 8: | 181 | if password_length < 8: |
| 176 | print('Error: Your password length must be at least 8 characters.') | 182 | print("Error: Your password length must be at least 8 characters.") |
| 177 | return | 183 | return |
| 178 | password_characters = generate_characters(password_length) | 184 | password_characters = generate_characters(password_length) |
| 179 | password_string = shuffle_characters(password_characters) | 185 | password_string = shuffle_characters(password_characters) |
| 180 | else: | 186 | else: |
| 181 | password_string = input('Please enter your desired password: ') | 187 | password_string = input("Please enter your desired password: ") |
| 182 | 188 | ||
| 183 | account = Account(str(uuid.uuid4()), application_string, | 189 | account = Account( |
| 184 | username_string, password_string, url_string) | 190 | str(uuid.uuid4()), |
| 191 | application_string, | ||
| 192 | username_string, | ||
| 193 | password_string, | ||
| 194 | url_string, | ||
| 195 | ) | ||
| 185 | account.save_account() | 196 | account.save_account() |
| 186 | print('Account saved to the vault. Use `--list` to see all saved accounts.') | 197 | print("Account saved to the vault. Use `--list` to see all saved accounts.") |
| 187 | |||
| 188 | 198 | ||
| 189 | 199 | ||
| 190 | def edit_account(account_uuid: str, edit_parameter: int) -> None: | 200 | def edit_account(account_uuid: str, edit_parameter: int) -> None: |
| @@ -197,29 +207,30 @@ def edit_account(account_uuid: str, edit_parameter: int) -> None: | |||
| 197 | Valid values are 1 for application name, 2 for username, | 207 | Valid values are 1 for application name, 2 for username, |
| 198 | 3 for password, and 4 for URL. | 208 | 3 for password, and 4 for URL. |
| 199 | """ | 209 | """ |
| 200 | field_name, new_value = '' | 210 | field_name, new_value = "" |
| 201 | if edit_parameter == 1: | 211 | if edit_parameter == 1: |
| 202 | field_name = 'application' | 212 | field_name = "application" |
| 203 | new_value = input('Please enter your desired Application name: ') | 213 | new_value = input("Please enter your desired Application name: ") |
| 204 | elif edit_parameter == 2: | 214 | elif edit_parameter == 2: |
| 205 | field_name = 'username' | 215 | field_name = "username" |
| 206 | new_value = input('Please enter your desired username: ') | 216 | new_value = input("Please enter your desired username: ") |
| 207 | elif edit_parameter == 3: | 217 | elif edit_parameter == 3: |
| 208 | field_name = 'password' | 218 | field_name = "password" |
| 209 | type_check = input( | 219 | type_check = input( |
| 210 | 'Do you want a new random password or to enter a custom password? ' | 220 | "Do you want a new random password or to enter a custom password? " |
| 211 | '(random/custom): ').lower() | 221 | "(random/custom): " |
| 212 | if type_check == 'random': | 222 | ).lower() |
| 213 | password_length = int(input('Please enter your desired password length: ')) | 223 | if type_check == "random": |
| 224 | password_length = int(input("Please enter your desired password length: ")) | ||
| 214 | if password_length < 8: | 225 | if password_length < 8: |
| 215 | print('Error: Your password length must be at least 8 characters.') | 226 | print("Error: Your password length must be at least 8 characters.") |
| 216 | else: | 227 | else: |
| 217 | password_characters = generate_characters(password_length) | 228 | password_characters = generate_characters(password_length) |
| 218 | new_value = shuffle_characters(password_characters) | 229 | new_value = shuffle_characters(password_characters) |
| 219 | else: | 230 | else: |
| 220 | new_value = input('Please enter your desired password: ') | 231 | new_value = input("Please enter your desired password: ") |
| 221 | elif edit_parameter == 4: | 232 | elif edit_parameter == 4: |
| 222 | field_name = 'url' | 233 | field_name = "url" |
| 223 | new_value = input('Please enter your desired URL: ') | 234 | new_value = input("Please enter your desired URL: ") |
| 224 | database.update_account(field_name, new_value, account_uuid) | 235 | database.update_account(field_name, new_value, account_uuid) |
| 225 | print('Account successfully updated.') | 236 | print("Account successfully updated.") |