krz/yoshi-cli

Archived

A password manager for the command line. cli password-manager terminal

Commit c8617ce8ae

c8617ce8aecb40ccf1d13c02dcd7d7e74e79b56b

parent: 1fc982100a

Unregistered key

cmc <hello@cleberg.net> · 2025-04-05 18:36 UTC
committer: <noreply@github.com>

migrate from pylint to ruff-action (#4)

* migrate from pylint to ruff-action

* temporarily disable push param

* Commit from GitHub Actions (Pylint)

* re-enable push param

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>

Layout: unified · split

.github/workflows/pylint.yml +20 −6
@@ -1,23 +1,37 @@
1name: Pylint 1name: Pylint
2 2
3on: [push] 3on:
4 push:
5 paths:
6 - '**.py'
4 7
5jobs: 8jobs:
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
5if __name__ == "__main__": 5if __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
11class Account: 11class 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
7import argparse 7import argparse
8from yoshi import crypto, database, process 8from yoshi import crypto, database, process
9 9
10
10def yoshi(): 11def 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
136if __name__ == "__main__": 122if __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
7from cryptography.fernet import Fernet 7from cryptography.fernet import Fernet
8 8
9VAULT_FILE = 'vault.sqlite' 9VAULT_FILE = "vault.sqlite"
10 10
11 11
12def generate_key() -> bytes: 12def 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
28def encrypt(key: bytes, filename: str = VAULT_FILE) -> None: 28def 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
38def decrypt(key: bytes, filename: str = VAULT_FILE) -> None: 38def 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
7import sys 7import sys
8import os 8import os
9 9
10VAULT_DECRYPTED = 'vault.sqlite' 10VAULT_DECRYPTED = "vault.sqlite"
11VAULT_ENCRYPTED = 'vault.sqlite.aes' 11VAULT_ENCRYPTED = "vault.sqlite.aes"
12 12
13 13
14def create_table() -> None: 14def 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
50def add_account(uuid: str, application: str, username: str, password: str, 51def 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:
99def update_account(field_name: str, new_value: str, uuid: str) -> None: 101def 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
32from yoshi import database 32from yoshi import database
33from yoshi.wordlist import WORDLIST 33from yoshi.wordlist import WORDLIST
34 34
35
35def generate_characters(n: int) -> list: 36def 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
125def purge_accounts() -> None: 128def 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
141def create_account() -> None: 147def 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
190def edit_account(account_uuid: str, edit_parameter: int) -> None: 200def 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.")