-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
293 lines (224 loc) · 9.47 KB
/
Copy pathmain.py
File metadata and controls
293 lines (224 loc) · 9.47 KB
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
"""
Random Password Generator
--------------------------
A menu-driven console application that generates cryptographically
secure random passwords made up of letters and numbers.
DecodeLabs Python Programming Internship - Project 3
Built using only the Python Standard Library:
- secrets : cryptographically secure random selection
- string : predefined character set constants
No external packages, GUI frameworks, databases, or file storage
are used. Nothing is stored permanently; all data lives only for
the duration of a single program run.
"""
import string
import secrets
# --------------------------------------------------------------------------
# Configuration / Constants
# --------------------------------------------------------------------------
DEFAULT_PASSWORD_LENGTH = 12
MIN_PASSWORD_LENGTH = 4
MAX_PASSWORD_LENGTH = 128
CHARACTER_POOL = string.ascii_letters + string.digits
# --------------------------------------------------------------------------
# Input Validation
# --------------------------------------------------------------------------
def get_valid_length(prompt_message, current_length):
"""
Prompt the user for a password length and validate the input.
Rejects empty input, non-numeric values, zero, and negative
numbers. Also enforces a sane upper bound so the program stays
responsive. The function loops until valid input is given, or
the user submits nothing meaningful more than once in a row is
still handled gracefully (no crash).
Returns:
int: A validated password length, or the current_length
unchanged if the user cancels by typing 'c'.
"""
while True:
raw_input_value = input(prompt_message).strip()
# Allow the user to cancel out of changing the length.
if raw_input_value.lower() == "c":
print("Cancelled. Length unchanged.\n")
return current_length
# Reject empty input.
if raw_input_value == "":
print("Input cannot be empty. Please enter a number.\n")
continue
# Reject non-numeric values (also protects against decimals
# like "8.5" since int() would raise ValueError on those too).
try:
length_value = int(raw_input_value)
except ValueError:
print("Invalid input. Please enter a whole number.\n")
continue
# Reject zero and negative numbers.
if length_value <= 0:
print("Password length must be a positive number.\n")
continue
# Reject unreasonably large values.
if length_value < MIN_PASSWORD_LENGTH:
print(f"Password length must be at least {MIN_PASSWORD_LENGTH}.\n")
continue
if length_value > MAX_PASSWORD_LENGTH:
print(f"Password length cannot exceed {MAX_PASSWORD_LENGTH}.\n")
continue
return length_value
def get_valid_count(prompt_message):
"""
Prompt the user for how many passwords to generate at once.
Rejects empty input, non-numeric values, zero, and negative
numbers, and caps the count to keep output readable.
Returns:
int: A validated count of passwords to generate.
"""
while True:
raw_input_value = input(prompt_message).strip()
if raw_input_value == "":
print("Input cannot be empty. Please enter a number.\n")
continue
try:
count_value = int(raw_input_value)
except ValueError:
print("Invalid input. Please enter a whole number.\n")
continue
if count_value <= 0:
print("Count must be a positive number.\n")
continue
if count_value > 50:
print("Please request 50 or fewer passwords at a time.\n")
continue
return count_value
# --------------------------------------------------------------------------
# Core Password Generation Logic
# --------------------------------------------------------------------------
def generate_password(password_length):
"""
Generate a single random password using letters and numbers.
Uses secrets.choice() rather than the random module because
secrets draws from the operating system's cryptographically
secure entropy source, making the output suitable for security
related purposes such as account passwords.
String characters are collected in a list and joined once at
the end with ''.join(), avoiding the inefficiency of repeatedly
concatenating immutable strings inside a loop.
Args:
password_length (int): Number of characters in the password.
Returns:
str: The generated password.
"""
password_characters = [secrets.choice(CHARACTER_POOL) for _ in range(password_length)]
return "".join(password_characters)
def generate_multiple_passwords(password_length, how_many):
"""
Generate several random passwords of the same length.
Args:
password_length (int): Number of characters per password.
how_many (int): Number of passwords to generate.
Returns:
list[str]: A list of generated passwords.
"""
return [generate_password(password_length) for _ in range(how_many)]
# --------------------------------------------------------------------------
# Menu Actions
# --------------------------------------------------------------------------
def handle_generate_password(current_length):
"""Generate and display a single password using the current settings."""
password = generate_password(current_length)
print(f"\nGenerated Password: {password}\n")
def handle_change_length(current_length):
"""Prompt for and return a new password length."""
print(f"\nCurrent password length: {current_length}")
new_length = get_valid_length(
"Enter new password length (or 'c' to cancel): ", current_length
)
if new_length != current_length:
print(f"Password length updated to {new_length}.\n")
return new_length
def handle_generate_multiple(current_length):
"""Generate and display several passwords using the current settings."""
count = get_valid_count("How many passwords would you like to generate? ")
passwords = generate_multiple_passwords(current_length, count)
print(f"\nGenerated {count} Password(s):")
for index, password in enumerate(passwords, start=1):
print(f" {index}. {password}")
print()
def handle_view_settings(current_length):
"""Display the current configuration of the password generator."""
print("\nCurrent Settings")
print("-----------------")
print(f"Password Length : {current_length}")
print(f"Character Pool : Letters (A-Z, a-z) and Numbers (0-9)")
print(f"Pool Size : {len(CHARACTER_POOL)} characters\n")
def handle_reset_settings():
"""Reset settings back to the application default and return it."""
print(f"\nSettings have been reset to default (length = {DEFAULT_PASSWORD_LENGTH}).\n")
return DEFAULT_PASSWORD_LENGTH
# --------------------------------------------------------------------------
# Menu Display
# --------------------------------------------------------------------------
def display_menu():
"""Print the main menu options."""
print("=" * 40)
print(" RANDOM PASSWORD GENERATOR")
print("=" * 40)
print("1. Generate Password")
print("2. Change Password Length")
print("3. Generate Multiple Passwords")
print("4. View Current Settings")
print("5. Reset Settings")
print("6. Exit")
print("=" * 40)
def get_menu_choice():
"""
Prompt for and validate the user's menu selection.
Rejects empty input and non-numeric values, and re-prompts on
any choice outside the valid menu range. Never crashes on bad
input.
Returns:
int: A validated menu choice between 1 and 6.
"""
while True:
raw_choice = input("Select an option (1-6): ").strip()
if raw_choice == "":
print("Input cannot be empty. Please choose an option.\n")
continue
try:
choice_value = int(raw_choice)
except ValueError:
print("Invalid input. Please enter a number between 1 and 6.\n")
continue
if choice_value < 1 or choice_value > 6:
print("Please choose a number between 1 and 6.\n")
continue
return choice_value
# --------------------------------------------------------------------------
# Main Application Loop
# --------------------------------------------------------------------------
def main():
"""Run the interactive password generator until the user exits."""
current_length = DEFAULT_PASSWORD_LENGTH
print("Welcome to the Random Password Generator!")
print(f"Default password length is set to {DEFAULT_PASSWORD_LENGTH}.\n")
while True:
display_menu()
choice = get_menu_choice()
print()
if choice == 1:
handle_generate_password(current_length)
elif choice == 2:
current_length = handle_change_length(current_length)
elif choice == 3:
handle_generate_multiple(current_length)
elif choice == 4:
handle_view_settings(current_length)
elif choice == 5:
current_length = handle_reset_settings()
elif choice == 6:
print("Thank you for using the Random Password Generator. Goodbye!")
break
if __name__ == "__main__":
try:
main()
except (KeyboardInterrupt, EOFError):
print("\n\nProgram interrupted. Goodbye!")