summaryrefslogtreecommitdiff
path: root/yoshi/process.py
blob: 155f9b09ffecad41dbeb28159b7ab7d051513baa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
"""
Password Vault Manager

This script provides various functions for managing password vaults.
It allows users to create, list, edit and delete accounts.

The `Account` class represents an individual account, with attributes for
the application name, username, password, and URL. The database module is used
to interact with the SQLite database file (`vault.sqlite`) that stores the
accounts data.

Functions:
    generate_characters(n): generates a list of random characters
    shuffle_characters(characters): shuffles the characters to create a password
    generate_passphrase(n, sep): generates an XKCD-style passphrase with n words and separator
    list_accounts(): lists all saved accounts in the database
    delete_account(uuid): deletes an account by its UUID
    purge_accounts(): purges the entire database (irreversible)
    create_account(): creates a new account by prompting user for details
    edit_account(uuid, edit_parameter): edits an existing account's details

Usage:
    Run this script in your terminal to access these functions.
"""

from string import ascii_letters, punctuation, digits
import random
import uuid
from prettytable import PrettyTable
from yoshi.account import Account
import yoshi.database as database


def generate_characters(n: int) -> list:
    """
    Generates a list of n random characters from the set of ASCII letters,
    punctuation and digits.

    Args:
        n (int): The number of characters to generate

    Returns:
        list: A list of n random characters
    """
    characters = []
    password_format = ascii_letters + punctuation + digits
    for _ in range(n):
        characters.append(random.choice(password_format))
    return characters


def shuffle_characters(characters: list) -> str:
    """
    Shuffles the characters to create a password.

    Args:
        characters (list): The list of characters

    Returns:
        str: A string representation of the shuffled characters
    """
    random.shuffle(characters)
    character_string = ''.join(characters)
    return character_string


def generate_passphrase(n: int, sep: str) -> str:
    """
    Generates an XKCD-style passphrase with n words and separator.

    Args:
        n (int): The number of words to include
        sep (str): The separator symbol

    Returns:
        str: A string representation of the passphrase
    """
    phrases = []
    lucky_number = random.choice(range(0, n))
    for _ in range(n):
        with open('wordlist.txt', 'r', encoding='utf-8') as file:
            line = random.choice(file.readlines())
        line = line.replace('\n', '')
        if _ == lucky_number:
            phrases.append(line.strip().capitalize() + str(_))
        else:
            phrases.append(line.strip().capitalize())
    passphrase = sep.join(phrases)
    return passphrase


def list_accounts() -> None:
    """
    Lists all saved accounts in the database.

    Returns:
        None
    """
    accounts = database.find_accounts()
    t = PrettyTable(['UUID', 'Application', 'Username', 'Password', 'URL'])
    for account in accounts:
        t.add_row([account[0], account[1], account[2], account[3], account[4]])
    print(t)


def delete_account(account_uuid: str) -> None:
    """
    Deletes an account by its UUID.

    Args:
        account_uuid (str): The UUID of the account to delete

    Returns:
        None
    """
    account_record = database.find_account(account_uuid)
    account = Account(account_record[0][0],
                               account_record[0][1],
                               account_record[0][2],
                               account_record[0][3],
                               account_record[0][4])
    if account.delete_account():
        print('Account successfully deleted.')


def purge_accounts() -> None:
    """
    Purges the entire database (irreversible).

    Returns:
        None
    """
    check = input(
        '''Are you absolutely sure you want to delete your password vault?
        This action is irreversible. (y/n): ''')
    if check.lower() == 'y':
        database.purge_table()
        database.purge_database()
        print('The password vault has been purged. You may now exit or create a new one.')


def create_account() -> None:
    """
    Creates a new account by prompting user for details.

    Returns:
        None
    """
    application_string = input('Please enter a name for this account: ')
    username_string = input('Please enter your username for this account: ')
    url_string = input('(Optional) Please enter a URL for this account: ')

    password_type = input(
        '''Do you want a random character password (p), an XKCD-style passphrase
(x), or a custom password (c)? (p|x|c): '''
    )
    if password_type not in ['p', 'x', 'c']:
        print('Error: Invalid choice. Please choose p, x, or c.')
        return

    if password_type == 'x':
        password_length = int(
            input('Please enter number of words to include (min. 2): ')
        )
        if password_length < 3:
            print('Error: Your passphrase length must be at least 3 words.')
            return
        password_separator = input(
            'Please enter your desired separator symbol (_,-, ~, etc.): '
        )
        password_string = generate_passphrase(password_length, password_separator)
    elif password_type == 'p':
        password_length = int(
            input('Please enter your desired password length (min. 8): ')
        )
        if password_length < 8:
            print('Error: Your password length must be at least 8 characters.')
            return
        password_characters = generate_characters(password_length)
        password_string = shuffle_characters(password_characters)
    else:
        password_string = input('Please enter your desired password: ')

    account = Account(str(uuid.uuid4()), application_string,
                      username_string, password_string, url_string)
    account.save_account()
    print('Account saved to the vault. Use `--list` to see all saved accounts.')



def edit_account(account_uuid: str, edit_parameter: int) -> None:
    """
    Allow users to edit any account information except the UUID.

    Args:
        account_uuid (str): Unique identifier of the account.
        edit_parameter (int): Parameter indicating which field to edit.
            Valid values are 1 for application name, 2 for username,
            3 for password, and 4 for URL.
    """
    field_name, new_value = ''
    if edit_parameter == 1:
        field_name = 'application'
        new_value = input('Please enter your desired Application name: ')
    elif edit_parameter == 2:
        field_name = 'username'
        new_value = input('Please enter your desired username: ')
    elif edit_parameter == 3:
        field_name = 'password'
        type_check = input(
            'Do you want a new random password or to enter a custom password? '
            '(random/custom): ').lower()
        if type_check == 'random':
            password_length = int(input('Please enter your desired password length: '))
            if password_length < 8:
                print('Error: Your password length must be at least 8 characters.')
            else:
                password_characters = generate_characters(password_length)
                new_value = shuffle_characters(password_characters)
        else:
            new_value = input('Please enter your desired password: ')
    elif edit_parameter == 4:
        field_name = 'url'
        new_value = input('Please enter your desired URL: ')
    database.update_account(field_name, new_value, account_uuid)
    print('Account successfully updated.')