krz/yoshi-cli

Archived

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

Commit 598e71b5ea

598e71b5ea1392f25cca0290c5544ab1135c37a2

parent: 0bef69a049

Verified · cmc

cmc <hello@cleberg.net> · 2024-11-02 21:58 UTC

add pylint workflow

Layout: unified · split

.github/workflows/pylint.yml added +23
@@ -0,0 +1,23 @@
1name: Pylint
2
3on: [push]
4
5jobs:
6 build:
7 runs-on: ubuntu-latest
8 strategy:
9 matrix:
10 python-version: ["3.8", "3.9", "3.10"]
11 steps:
12 - uses: actions/checkout@v4
13 - name: Set up Python ${{ matrix.python-version }}
14 uses: actions/setup-python@v4
15 with:
16 python-version: ${{ matrix.python-version }}
17 - name: Install dependencies
18 run: |
19 python -m pip install --upgrade pip
20 pip install pylint pandas dash plotly.express
21 - name: Analysing the code with pylint
22 run: |
23 pylint -d R0801 $(git ls-files '*.py')
Account.py +16
@@ -1,7 +1,16 @@
1"""
2This script imports necessary modules for database interactions.
3
4Modules imported:
5 - database: A custom module providing database functionality.
6"""
7
1import database 8import database
2 9
3 10
4class Account: 11class Account:
12 """Represents a login account."""
13
5 def __init__(self, uuid: str, application: str, username: str, 14 def __init__(self, uuid: str, application: str, username: str,
6 password: str, url: str) -> None: 15 password: str, url: str) -> None:
7 self.uuid = uuid 16 self.uuid = uuid
@@ -11,6 +20,7 @@ class Account:
11 self.url = url 20 self.url = url
12 21
13 def display_account(self) -> None: 22 def display_account(self) -> None:
23 """Print the account details."""
14 print('ID:', self.uuid) 24 print('ID:', self.uuid)
15 print('Application:', self.application) 25 print('Application:', self.application)
16 print('Username:', self.username) 26 print('Username:', self.username)
@@ -18,9 +28,15 @@ class Account:
18 print('URL:', self.url) 28 print('URL:', self.url)
19 29
20 def save_account(self) -> None: 30 def save_account(self) -> None:
31 """Save the account details to the database."""
21 database.create_account( 32 database.create_account(
22 self.uuid, self.application, self.username, self.password, self.url) 33 self.uuid, self.application, self.username, self.password, self.url)
23 34
24 def delete_account(self) -> bool: 35 def delete_account(self) -> bool:
36 """Delete the account from the database.
37
38 Returns:
39 bool: True if the deletion was successful.
40 """
25 database.delete_account(self.uuid) 41 database.delete_account(self.uuid)
26 return True 42 return True
crypto.py +30 −15
@@ -1,30 +1,45 @@
1"""
2This module imports the Fernet symmetric encryption algorithm from the cryptography library.
3
4It allows for secure encryption and decryption of data using a secret key.
5"""
6
1from cryptography.fernet import Fernet 7from cryptography.fernet import Fernet
2 8
3vault_file = 'vault.sqlite' 9VAULT_FILE = 'vault.sqlite'
4 10
5 11
6def generate_key() -> bytes: 12def generate_key() -> bytes:
7 new_key = Fernet.generate_key() 13 """Generates a new encryption key."""
8 return new_key 14 return Fernet.generate_key()
9 15
10 16
11def load_key(key_file: str) -> bytes: 17def load_key(key_file: str) -> bytes:
12 return open(key_file, 'rb').read() 18 """
19 Loads an existing encryption key from the file.
20
21 Args:
22 key_file (str): Path to the key file.
23 """
24 with open(key_file, 'rb') as key:
25 return key.read()
13 26
14 27
15def encrypt(key, filename=vault_file) -> None: 28def encrypt(key: bytes, filename: str = VAULT_FILE) -> None:
29 """Encrypts the data in the specified file using the provided key."""
16 f = Fernet(key) 30 f = Fernet(key)
17 with open(filename, 'rb') as file: 31 with open(filename, 'rb') as vault:
18 file_data = file.read() 32 data = vault.read()
19 encrypted_data = f.encrypt(file_data) 33 encrypted_data = f.encrypt(data)
20 with open(filename, 'wb') as file: 34 with open(filename, 'wb') as vault:
21 file.write(encrypted_data) 35 vault.write(encrypted_data)
22 36
23 37
24def decrypt(key, filename=vault_file) -> None: 38def decrypt(key: bytes, filename: str = VAULT_FILE) -> None:
39 """Decrypts the data in the specified file using the provided key."""
25 f = Fernet(key) 40 f = Fernet(key)
26 with open(filename, 'rb') as file: 41 with open(filename, 'rb') as vault:
27 encrypted_data = file.read() 42 encrypted_data = vault.read()
28 decrypted_data = f.decrypt(encrypted_data) 43 decrypted_data = f.decrypt(encrypted_data)
29 with open(filename, 'wb') as file: 44 with open(filename, 'wb') as vault:
30 file.write(decrypted_data) 45 vault.write(decrypted_data)
database.py +45 −32
@@ -1,34 +1,41 @@
1# Import Python modules 1"""
2This module provides a basic interface for connecting to and interacting with a SQLite database.
3It includes functions for creating connections, executing queries, and retrieving results.
4"""
5
2import sqlite3 6import sqlite3
3import sys 7import sys
4import os 8import os
5 9
6# Specify the name of the vault database 10VAULT_DECRYPTED = 'vault.sqlite'
7vault_decrypted = 'vault.sqlite' 11VAULT_ENCRYPTED = 'vault.sqlite.aes'
8vault_encrypted = 'vault.sqlite.aes'
9 12
10 13
11# Create the accounts table inside the vault database
12def create_table() -> None: 14def create_table() -> None:
13 db_connection = sqlite3.connect(vault_decrypted) 15 """Create the accounts table within the vault database."""
16 db_connection = sqlite3.connect(VAULT_DECRYPTED)
14 cursor = db_connection.cursor() 17 cursor = db_connection.cursor()
15 cursor.execute( 18 cursor.execute(
16 ''' CREATE TABLE accounts (uuid text, application text, username text, password text, url text) ''') 19 ''' CREATE TABLE IF NOT EXISTS accounts (uuid text, application text,
20 username text, password text, url text) '''
21 )
17 db_connection.commit() 22 db_connection.commit()
18 db_connection.close() 23 db_connection.close()
19 24
20 25
21# Check if the account table exists within the database 26def check_table() -> bool:
22def table_check() -> bool: 27 """Check if the 'accounts' table exists within the vault database."""
23 check = False 28 check = False
24 db_connection = sqlite3.connect(vault_decrypted) 29 db_connection = sqlite3.connect(VAULT_DECRYPTED)
25 cursor = db_connection.cursor() 30 cursor = db_connection.cursor()
26 cursor.execute( 31 cursor.execute(
27 ''' SELECT count(name) FROM sqlite_master WHERE type='table' AND name='accounts' ''') 32 ''' SELECT count(name) FROM sqlite_master WHERE type='table'
33 AND name='accounts' '''
34 )
28 if cursor.fetchone()[0] != 1: 35 if cursor.fetchone()[0] != 1:
29 user_choice = input( 36 user_choice = input(
30 '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): ')
31 if user_choice == 'y' or user_choice == 'Y': 38 if user_choice.lower() == 'y':
32 create_table() 39 create_table()
33 check = True 40 check = True
34 else: 41 else:
@@ -40,44 +47,48 @@ def table_check() -> bool:
40 return check 47 return check
41 48
42 49
43# Add a new account to the database 50def add_account(uuid: str, application: str, username: str, password: str,
44def create_account(uuid: str, application: str, username: str, password: str,
45 url: str) -> None: 51 url: str) -> None:
46 db_connection = sqlite3.connect(vault_decrypted) 52 """Add a new account within the vault database."""
53 db_connection = sqlite3.connect(VAULT_DECRYPTED)
47 cursor = db_connection.cursor() 54 cursor = db_connection.cursor()
48 cursor.execute( 55 cursor.execute(
49 ''' INSERT INTO accounts VALUES (:uuid,:application,:username,:password,:url) ''', 56 ''' INSERT INTO accounts VALUES (:uuid,:application,:username,
50 {'uuid': uuid, 'application': application, 'username': username, 57 :password,:url) ''', {
51 'password': password, 'url': url}) 58 'uuid': uuid, 'application': application, 'username': username,
59 'password': password, 'url': url
60 }
61 )
52 db_connection.commit() 62 db_connection.commit()
53 db_connection.close() 63 db_connection.close()
54 64
55 65
56# Delete an account with a specified UUID
57def delete_account(uuid: str) -> None: 66def delete_account(uuid: str) -> None:
58 db_connection = sqlite3.connect(vault_decrypted) 67 """Delete an account within the vault database by its unique ID."""
68 db_connection = sqlite3.connect(VAULT_DECRYPTED)
59 cursor = db_connection.cursor() 69 cursor = db_connection.cursor()
60 cursor.execute( 70 cursor.execute(
61 ''' DELETE FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid}) 71 ''' DELETE FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid}
72 )
62 db_connection.commit() 73 db_connection.commit()
63 db_connection.close() 74 db_connection.close()
64 75
65 76
66# Find a specific account by UUID
67def find_account(uuid: str) -> list: 77def find_account(uuid: str) -> list:
68 db_connection = sqlite3.connect(vault_decrypted) 78 """Find an account within the vault database by its unique ID."""
79 db_connection = sqlite3.connect(VAULT_DECRYPTED)
69 cursor = db_connection.cursor() 80 cursor = db_connection.cursor()
70 cursor.execute( 81 cursor.execute(
71 ''' SELECT * FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid}) 82 ''' SELECT * FROM accounts WHERE uuid = :uuid ''', {'uuid': uuid}
83 )
72 account = cursor.fetchall() 84 account = cursor.fetchall()
73 db_connection.close() 85 db_connection.close()
74 return account 86 return account
75 87
76 88
77# Return all accounts found within the database table
78# The `accounts` variable is a list of tuples
79def find_accounts() -> list: 89def find_accounts() -> list:
80 db_connection = sqlite3.connect(vault_decrypted) 90 """Return all accounts stored within the vault database."""
91 db_connection = sqlite3.connect(VAULT_DECRYPTED)
81 cursor = db_connection.cursor() 92 cursor = db_connection.cursor()
82 cursor.execute(''' SELECT * FROM accounts ''') 93 cursor.execute(''' SELECT * FROM accounts ''')
83 accounts = cursor.fetchall() 94 accounts = cursor.fetchall()
@@ -86,22 +97,23 @@ def find_accounts() -> list:
86 97
87 98
88def update_account(field_name: str, new_value: str, uuid: str) -> None: 99def update_account(field_name: str, new_value: str, uuid: str) -> None:
100 """Update an account within the vault database by its unique ID."""
89 queries = { 101 queries = {
90 'application': 'UPDATE accounts SET application = :new_value WHERE uuid = :uuid', 102 'application': 'UPDATE accounts SET application = :new_value WHERE uuid = :uuid',
91 'username': 'UPDATE accounts SET username = :new_value WHERE uuid = :uuid', 103 'username': 'UPDATE accounts SET username = :new_value WHERE uuid = :uuid',
92 'password': 'UPDATE accounts SET password = :new_value WHERE uuid = :uuid', 104 'password': 'UPDATE accounts SET password = :new_value WHERE uuid = :uuid',
93 'url': 'UPDATE accounts SET url = :new_value WHERE uuid = :uuid' 105 'url': 'UPDATE accounts SET url = :new_value WHERE uuid = :uuid'
94 } 106 }
95 db_connection = sqlite3.connect(vault_decrypted) 107 db_connection = sqlite3.connect(VAULT_DECRYPTED)
96 cursor = db_connection.cursor() 108 cursor = db_connection.cursor()
97 cursor.execute(queries[field_name], 109 cursor.execute(queries[field_name], {'new_value': new_value, 'uuid': uuid})
98 {'new_value': new_value, 'uuid': uuid})
99 db_connection.commit() 110 db_connection.commit()
100 db_connection.close() 111 db_connection.close()
101 112
102 113
103def purge_table() -> None: 114def purge_table() -> None:
104 db_connection = sqlite3.connect(vault_decrypted) 115 """Purge the 'accounts' table within the vault database."""
116 db_connection = sqlite3.connect(VAULT_DECRYPTED)
105 cursor = db_connection.cursor() 117 cursor = db_connection.cursor()
106 cursor.execute(''' DROP TABLE accounts ''') 118 cursor.execute(''' DROP TABLE accounts ''')
107 db_connection.commit() 119 db_connection.commit()
@@ -109,4 +121,5 @@ def purge_table() -> None:
109 121
110 122
111def purge_database() -> None: 123def purge_database() -> None:
112 os.remove(vault_decrypted) 124 """Purge the entire vault database."""
125 os.remove(VAULT_DECRYPTED)
main.py +68 −19
@@ -1,3 +1,9 @@
1"""
2This script uses argparse to parse command line arguments.
3
4It imports the required modules and sets up a parser with basic options for demonstration purposes.
5"""
6
1import argparse 7import argparse
2import crypto 8import crypto
3import database 9import database
@@ -5,47 +11,87 @@ import process
5 11
6if __name__ == '__main__': 12if __name__ == '__main__':
7 parser = argparse.ArgumentParser( 13 parser = argparse.ArgumentParser(
8 description='Manage your username and passwords via a convenient CLI vault.') 14 description='Manage your username and passwords via a convenient CLI vault.'
15 )
9 16
10 # Top-level arguments 17 # Top-level arguments
11 group_one = parser.add_mutually_exclusive_group() 18 group_one = parser.add_mutually_exclusive_group()
12 group_one.add_argument( 19 group_one.add_argument(
13 '-n', '--new', help='Create a new account', action='store_true') 20 '-n', '--new',
21 help='Create a new account.',
22 action='store_true'
23 )
14 group_one.add_argument( 24 group_one.add_argument(
15 '-l', '--list', help='List all saved accounts', action='store_true') 25 '-l', '--list',
26 help='List all saved accounts.',
27 action='store_true'
28 )
16 group_one.add_argument( 29 group_one.add_argument(
17 '-e', '--edit', help='Edit a saved account', action='store_true') 30 '-e', '--edit',
31 help='Edit a saved account.',
32 action='store_true'
33 )
18 group_one.add_argument( 34 group_one.add_argument(
19 '-d', '--delete', help='Delete a saved account', action='store_true') 35 '-d', '--delete',
36 help='Delete a saved account.',
37 action='store_true'
38 )
20 group_one.add_argument( 39 group_one.add_argument(
21 '--purge', help='Purge all accounts and delete the vault', 40 '--purge',
22 action='store_true') 41 help=(
42 'Purge all accounts and delete the vault. '
43 '(Caution: this will irreversibly destroy your data.)'
44 ),
45 action='store_true'
46 )
23 group_one.add_argument( 47 group_one.add_argument(
24 '--encrypt', help='Encrypt the vault', action='store_true') 48 '--encrypt',
49 help='Encrypt the vault.',
50 action='store_true'
51 )
25 group_one.add_argument( 52 group_one.add_argument(
26 '--decrypt', help='Decrypt the vault', action='store_true') 53 '--decrypt',
54 help='Decrypt the vault.',
55 action='store_true'
56 )
27 57
28 # Encryption flags 58 # Encryption flags
29 group_two = parser.add_mutually_exclusive_group() 59 group_two = parser.add_mutually_exclusive_group()
30 group_two.add_argument( 60 group_two.add_argument(
31 '-g', '--generate', 61 '-g', '--generate',
32 help='When using the --encrypt option, generate a new encryption key', 62 help=(
33 action='store_true') 63 'When using the --encrypt option, generate a new encryption key.'
64 ),
65 action='store_true'
66 )
34 group_two.add_argument( 67 group_two.add_argument(
35 '-k', '--keyfile', 68 '-k', '--keyfile',
36 help='When using the --encrypt or --decrypt options, specify an existing key file path', 69 help='Path to existing key file.',
37 action='store', nargs=1, type=str) 70 action='store',
71 nargs=1,
72 type=str
73 )
38 74
39 # Edit flags 75 # Edit flags
40 group_three = parser.add_argument_group() 76 group_three = parser.add_argument_group()
41 group_three.add_argument( 77 group_three.add_argument(
42 '-u', '--uuid', 78 '-u', '--uuid',
43 help='When using the --edit or --delete options, provide the account UUID', 79 help=(
44 action='store', nargs=1, type=str) 80 'When using the --edit or --delete options, provide the account UUID.'
81 ),
82 action='store',
83 nargs=1,
84 type=str
85 )
45 group_three.add_argument( 86 group_three.add_argument(
46 '-f', '--field', 87 '-f', '--field',
47 help='When using the --edit option, provide the field to edit', 88 help=(
48 action='store', nargs=1, type=int) 89 'When using the --edit option, specify the field to edit (integer index).'
90 ),
91 action='store',
92 nargs=1,
93 type=int
94 )
49 95
50 args = parser.parse_args() 96 args = parser.parse_args()
51 97
@@ -59,7 +105,9 @@ if __name__ == '__main__':
59 if args.generate: 105 if args.generate:
60 key = crypto.generate_key() 106 key = crypto.generate_key()
61 print( 107 print(
62 'WRITE THIS KEY DOWN SOMEWHERE SAFE. YOU WILL NOT BE ABLE TO DECRYPT YOUR DATA WITHOUT IT!') 108 'WRITE THIS KEY DOWN SOMEWHERE SAFE. YOU WILL NOT BE ABLE TO DECRYPT '
109 'YOUR DATA WITHOUT IT!'
110 )
63 print(key.decode()) 111 print(key.decode())
64 print('\n') 112 print('\n')
65 else: 113 else:
@@ -81,4 +129,5 @@ if __name__ == '__main__':
81 process.purge_accounts() 129 process.purge_accounts()
82 else: 130 else:
83 raise TypeError( 131 raise TypeError(
84 'Please specify a command or use the --help flag for more information.') 132 'Please specify a command or use the --help flag for more information.'
133 )
process.py +124 −33
@@ -1,4 +1,29 @@
1from Account import Account 1"""
2Password Vault Manager
3
4This script provides various functions for managing password vaults.
5It allows users to create, list, edit and delete accounts.
6
7The `Account` class represents an individual account, with attributes for
8the application name, username, password, and URL. The database module is used
9to interact with the SQLite database file (`vault.sqlite`) that stores the
10accounts data.
11
12Functions:
13 generate_characters(n): generates a list of random characters
14 shuffle_characters(characters): shuffles the characters to create a password
15 generate_passphrase(n, sep): generates an XKCD-style passphrase with n words and separator
16 list_accounts(): lists all saved accounts in the database
17 delete_account(uuid): deletes an account by its UUID
18 purge_accounts(): purges the entire database (irreversible)
19 create_account(): creates a new account by prompting user for details
20 edit_account(uuid, edit_parameter): edits an existing account's details
21
22Usage:
23 Run this script in your terminal to access these functions.
24"""
25
26from account import Account
2import database 27import database
3from string import ascii_letters, punctuation, digits 28from string import ascii_letters, punctuation, digits
4import random 29import random
@@ -6,39 +31,70 @@ import uuid
6from prettytable import PrettyTable 31from prettytable import PrettyTable
7 32
8 33
9# Generate a list of characters
10def generate_characters(n: int) -> list: 34def generate_characters(n: int) -> list:
11 characters = list() 35 """
36 Generates a list of n random characters from the set of ASCII letters,
37 punctuation and digits.
38
39 Args:
40 n (int): The number of characters to generate
41
42 Returns:
43 list: A list of n random characters
44 """
45 characters = []
12 password_format = ascii_letters + punctuation + digits 46 password_format = ascii_letters + punctuation + digits
13 for x in range(n): 47 for _ in range(n):
14 characters.append(random.choice(password_format)) 48 characters.append(random.choice(password_format))
15 return characters 49 return characters
16 50
17 51
18# Randomly shuffle the characters
19def shuffle_characters(characters: list) -> str: 52def shuffle_characters(characters: list) -> str:
53 """
54 Shuffles the characters to create a password.
55
56 Args:
57 characters (list): The list of characters
58
59 Returns:
60 str: A string representation of the shuffled characters
61 """
20 random.shuffle(characters) 62 random.shuffle(characters)
21 character_string = ''.join(characters) 63 character_string = ''.join(characters)
22 return character_string 64 return character_string
23 65
24 66
25# Generate a combination of passphrases
26def generate_passphrase(n: int, sep: str) -> str: 67def generate_passphrase(n: int, sep: str) -> str:
68 """
69 Generates an XKCD-style passphrase with n words and separator.
70
71 Args:
72 n (int): The number of words to include
73 sep (str): The separator symbol
74
75 Returns:
76 str: A string representation of the passphrase
77 """
27 phrases = [] 78 phrases = []
28 lucky_number = random.choice(range(0, n)) 79 lucky_number = random.choice(range(0, n))
29 for x in range(0, n): 80 for _ in range(n):
30 line = random.choice(open('wordlist.txt').readlines()) 81 line = random.choice(open('wordlist.txt').readlines())
31 line = line.replace('\n', '') 82 line = line.replace('\n', '')
32 if x == lucky_number: 83 if _ == lucky_number:
33 phrases.append(line.strip().capitalize() + str(x)) 84 phrases.append(line.strip().capitalize() + str(_))
34 else: 85 else:
35 phrases.append(line.strip().capitalize()) 86 phrases.append(line.strip().capitalize())
36 passphrase = sep.join(phrases) 87 passphrase = sep.join(phrases)
37 return passphrase 88 return passphrase
38 89
39 90
40# List all saved accounts
41def list_accounts() -> None: 91def list_accounts() -> None:
92 """
93 Lists all saved accounts in the database.
94
95 Returns:
96 None
97 """
42 accounts = database.find_accounts() 98 accounts = database.find_accounts()
43 t = PrettyTable(['UUID', 'Application', 'Username', 'Password', 'URL']) 99 t = PrettyTable(['UUID', 'Application', 'Username', 'Password', 'URL'])
44 for account in accounts: 100 for account in accounts:
@@ -46,10 +102,18 @@ def list_accounts() -> None:
46 print(t) 102 print(t)
47 103
48 104
49# Delete a single account by UUID
50def delete_account(uuid: str) -> None: 105def delete_account(uuid: str) -> None:
106 """
107 Deletes an account by its UUID.
108
109 Args:
110 uuid (str): The UUID of the account to delete
111
112 Returns:
113 None
114 """
51 account_record = database.find_account(uuid) 115 account_record = database.find_account(uuid)
52 account: Account = Account(account_record[0][0], 116 account = Account(account_record[0][0],
53 account_record[0][1], 117 account_record[0][1],
54 account_record[0][2], 118 account_record[0][2],
55 account_record[0][3], 119 account_record[0][3],
@@ -58,52 +122,79 @@ def delete_account(uuid: str) -> None:
58 print('Account successfully deleted.') 122 print('Account successfully deleted.')
59 123
60 124
61# Purge the `accounts` table and `vault.sqlite` file
62def purge_accounts() -> None: 125def purge_accounts() -> None:
126 """
127 Purges the entire database (irreversible).
128
129 Returns:
130 None
131 """
63 check = input( 132 check = input(
64 'Are you absolutely sure you want to delete your password vault? This action is irreversible. (y/n): ') 133 'Are you absolutely sure you want to delete your password vault? This action is irreversible. (y/n): ')
65 if check: 134 if check.lower() == 'y':
66 database.purge_table() 135 database.purge_table()
67 database.purge_database() 136 database.purge_database()
68 print('The password vault has been purged. You may now exit or create a new one.') 137 print('The password vault has been purged. You may now exit or create a new one.')
69 138
70 139
71# Request user input and create an account
72def create_account() -> None: 140def create_account() -> None:
141 """
142 Creates a new account by prompting user for details.
143
144 Returns:
145 None
146 """
73 application_string = input('Please enter a name for this account: ') 147 application_string = input('Please enter a name for this account: ')
74 username_string = input('Please enter your username for this account: ') 148 username_string = input('Please enter your username for this account: ')
75 url_string = input('(Optional) Please enter a URL for this account: ') 149 url_string = input('(Optional) Please enter a URL for this account: ')
150
76 password_type = input( 151 password_type = input(
77 '''Do you want a random character password (p), an XKCD-style passphrase 152 '''Do you want a random character password (p), an XKCD-style passphrase
78(x), or a custom password (c)? (p|x|c): ''') 153(x), or a custom password (c)? (p|x|c): '''
79 if password_type == "x" or password_type == "xkcd": 154 )
155 if password_type not in ['p', 'x', 'c']:
156 print('Error: Invalid choice. Please choose p, x, or c.')
157 return
158
159 if password_type == 'x':
80 password_length = int( 160 password_length = int(
81 input('Please enter number of words to include (min. 2): ')) 161 input('Please enter number of words to include (min. 2): ')
82 password_separator = input( 162 )
83 'Please enter your desired separator symbol (_, -, ~, etc.: ')
84 if password_length < 3: 163 if password_length < 3:
85 print('Error: Your passphrase length must be at least 3 words.') 164 print('Error: Your passphrase length must be at least 3 words.')
86 else: 165 return
87 password_string = generate_passphrase( 166 password_separator = input(
88 password_length, password_separator) 167 'Please enter your desired separator symbol (_,-, ~, etc.): '
89 elif password_type == "p": 168 )
169 password_string = generate_passphrase(password_length, password_separator)
170 elif password_type == 'p':
90 password_length = int( 171 password_length = int(
91 input('Please enter your desired password length (min. 8): ')) 172 input('Please enter your desired password length (min. 8): ')
173 )
92 if password_length < 8: 174 if password_length < 8:
93 print('Error: Your password length must be at least 8 characters.') 175 print('Error: Your password length must be at least 8 characters.')
94 else: 176 return
95 password_characters = generate_characters(password_length) 177 password_string = generate_password(password_length)
96 password_string = shuffle_characters(password_characters)
97 else: 178 else:
98 password_string = input('Please enter your desired password: ') 179 password_string = input('Please enter your desired password: ')
180
99 account = Account(str(uuid.uuid4()), application_string, 181 account = Account(str(uuid.uuid4()), application_string,
100 username_string, password_string, url_string) 182 username_string, password_string, url_string)
101 account.save_account() 183 account.save_account()
102 print('Account saved to the vault. Use `--list` to see all saved accounts.') 184 print('Account saved to the vault. Use `--list` to see all saved accounts.')
103 185
104 186
105# Allow users to edit any account info except the UUID 187
106def edit_account(uuid: str, edit_parameter: int) -> None: 188def edit_account(uuid: str, edit_parameter: int) -> None:
189 """
190 Allow users to edit any account information except the UUID.
191
192 Args:
193 uuid (str): Unique identifier of the account.
194 edit_parameter (int): Parameter indicating which field to edit.
195 Valid values are 1 for application name, 2 for username,
196 3 for password, and 4 for URL.
197 """
107 if edit_parameter == 1: 198 if edit_parameter == 1:
108 field_name = 'application' 199 field_name = 'application'
109 new_value = input('Please enter your desired Application name: ') 200 new_value = input('Please enter your desired Application name: ')
@@ -113,10 +204,10 @@ def edit_account(uuid: str, edit_parameter: int) -> None:
113 elif edit_parameter == 3: 204 elif edit_parameter == 3:
114 field_name = 'password' 205 field_name = 'password'
115 type_check = input( 206 type_check = input(
116 'Do you want a new random password or to enter a custom password? (random/custom): ') 207 'Do you want a new random password or to enter a custom password? '
208 '(random/custom): ').lower()
117 if type_check == 'random': 209 if type_check == 'random':
118 password_length = int( 210 password_length = int(input('Please enter your desired password length: '))
119 input('Please enter your desired password length: '))
120 if password_length < 8: 211 if password_length < 8:
121 print('Error: Your password length must be at least 8 characters.') 212 print('Error: Your password length must be at least 8 characters.')
122 else: 213 else: