Skip to content

Commit bc80c66

Browse files
authored
Merge pull request #32 from IceTheDev2/change-automatically-change-an-invalid-number-to-the-closest-valid-number
Change automatically change an invalid number to the closest valid number
2 parents 687305c + 8b141b8 commit bc80c66

File tree

5 files changed

+115
-72
lines changed

5 files changed

+115
-72
lines changed

code/generate_password_gui.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import tkinter as tk
22

3-
import generate_password_logic
3+
import generate_password_logic as logic
44

55
title_font = 'Helvetica 24'
66
section_title_font = 'Helvetica 16'
@@ -15,11 +15,11 @@
1515
no_character_set_error = 'An error occurred. Try again with at least 1 character set.'
1616
double_error = 'An error occurred. Try again with at least 1 character set and a whole number between 4 and 100.'
1717

18-
def show_generate_password_frame(frame, done_btn_image) -> None:
18+
def create_generate_password_frame(frame, done_btn_image) -> None:
1919
'''
2020
Called upon starting the program,
2121
this function uses the Tkinter module to create a GUI frame to generate passwords with various options for customisation
22-
(length and character sets)
22+
(length and character sets),
2323
and serves as a hub for all other password generation functions.
2424
2525
Parameters
@@ -38,8 +38,6 @@ def show_generate_password_frame(frame, done_btn_image) -> None:
3838
password_label_4 = tk.Text(frame, width = password_width, height = password_height,
3939
borderwidth = password_border_width, font = password_font)
4040
password_labels = [password_label_1, password_label_2, password_label_3, password_label_4]
41-
42-
global copy_button
4341

4442
frame_title = tk.Label(frame, text = 'Generate password', font = title_font)
4543
frame_title.grid(column = 0, row = 1, columnspan = 2)
@@ -67,38 +65,47 @@ def show_generate_password_frame(frame, done_btn_image) -> None:
6765
punctuation_text = tk.Label(frame, text = 'Punctuation', font = description_font)
6866

6967
checkboxes = [lowercase_letters_checkbox, uppercase_letters_checkbox, digits_checkbox, punctuation_checkbox]
70-
checkboxes_text = [lowercase_letters_text, uppercase_letters_text, digits_text, punctuation_text]
68+
checkboxes_text_labels = [lowercase_letters_text, uppercase_letters_text, digits_text, punctuation_text]
7169

7270
for checkbox in checkboxes:
7371
checkbox.grid(column = 1, row = 5 + checkboxes.index(checkbox), pady = 8)
7472
checkbox.select()
7573

76-
for text in checkboxes_text:
77-
text.grid(column = 2, row = 5 + checkboxes_text.index(text), sticky = 'w')
74+
for text in checkboxes_text_labels:
75+
text.grid(column = 2, row = 5 + checkboxes_text_labels.index(text), sticky = 'w')
7876

7977
def create_password_labels(event) -> None:
8078
'''
8179
Called upon clicking the done button or pressing the ENTER key,
82-
this function validates the user input.
83-
If it is valid, this function uses the show_text function to display a password,
84-
else it uses the same function to display an adequate error given through the validate_input function of generate_password_logic.py
80+
this function calls determine_error and validate_character_sets of generate_password_logic,
81+
and then settles whether an error has occurred or not.
82+
If an error has occurred, the function displays said error
83+
(obtained through determine_error),
84+
and displays it on the screen through show_text.
85+
If an error has not occurred, the function calls generate_password of generate_password_logic.py to get 4 passwords,
86+
and calls the show_text function to display them to the user.
8587
8688
Parameters
8789
----------
8890
event:
8991
Necessary for initiating the function when pressing the ENTER key.
9092
'''
91-
text = generate_password_logic.validate_input(input_box.get(), lowercase_letters_var, uppercase_letters_var, digits_var, punctuation_var, no_character_set_error, input_box, double_error, invalid_input_error)
92-
93-
# Check if an error is NOT returned.
94-
if text == None:
93+
text = logic.determine_error(logic.validate_character_sets(lowercase_letters_var, uppercase_letters_var, digits_var, punctuation_var),
94+
input_box.get(), no_character_set_error, double_error, invalid_input_error)
95+
96+
if text == '':
9597
for password_label in password_labels:
96-
password_label.bind('<ButtonRelease>', lambda event: generate_password_logic.show_copy_button(event, copy_button))
98+
password_label.bind('<ButtonRelease>', lambda event: logic.show_copy_button(event, copy_button))
99+
100+
adapted_input = logic.adapt_input(input_box.get())
101+
input_box.delete(0, 'end')
102+
input_box.insert(1, adapted_input)
97103

98-
text = generate_password_logic.generate_password(input_box.get(), lowercase_letters_var, uppercase_letters_var, digits_var, punctuation_var)
104+
text = logic.generate_password(adapted_input, lowercase_letters_var, uppercase_letters_var, digits_var, punctuation_var)
99105
show_text(password_label, text)
100106
password_label.grid(column = 0, row = 5 + password_labels.index(password_label), pady = 10, padx = 10)
101107
else:
108+
input_box.delete(0, 'end')
102109
password_label_1.grid(column = 0, row = 5, padx = 10, pady = 10)
103110
show_text(password_label_1, text)
104111

@@ -111,13 +118,14 @@ def create_password_labels(event) -> None:
111118
done_btn.grid(column = 0, row = 4, columnspan = 2)
112119

113120
copy_button = tk.Menu(frame, tearoff = False)
114-
copy_button.add_command(label = 'Copy', command = lambda: generate_password_logic.copy_text(input_box, password_labels))
121+
copy_button.add_command(label = 'Copy', command = lambda: logic.copy_text(input_box, password_labels))
115122

116123
def show_text(label, text) -> None:
117124
'''
118125
Called by the create_password_labels function,
119126
this function updates the contents of the password_labels,
120-
by enabling the label, deleting its current contents, inserting the new text, and then disabling the label again.
127+
by enabling the label, deleting its current contents,
128+
inserting the new text, and then disabling the label again.
121129
122130
Parameters
123131
----------
@@ -135,7 +143,7 @@ def show_text(label, text) -> None:
135143
def select_input_box(event) -> None:
136144
'''
137145
Called whenever the tab is changed,
138-
this function focuses to the input box,
146+
this function focuses the keyboard to the input box,
139147
which allows the user to start typing immediately without having to click on the input box first.
140148
'''
141149
input_box.focus()

code/generate_password_logic.py

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,93 @@
11
import string
22
import secrets
3-
from pynput.keyboard import Key, Controller
43
import clipboard
4+
from pynput.keyboard import Key, Controller
55

66
keyboard = Controller()
77

8-
def validate_input(requested_password_length, lowercase_letters_var, uppercase_letters_var, digits_var, punctuation_var, no_character_set_error, input_box, double_error, invalid_input_error) -> str:
8+
def adapt_input(requested_password_length) -> int:
99
'''
10-
Called by the create_password_labels function,
11-
this function checks if a password can be generated.
12-
It first checks if the user has chosen any characters sets,
13-
then it checks if the user's chosen length is valid,
14-
and displays an adequate error.
10+
Called by the create_password_labels function
11+
(upon pressing the done button),
12+
this function first checks if any input has been given.
13+
It raises a ValueError if no input has been given,
14+
and if input has been given,
15+
it attempts to adapt the input to an integer between 4 and 100.
16+
If it succeeds, it returns the adapted input,
17+
if it fails, it raises a ValueError.
1518
1619
Parameters
1720
----------
18-
requested_password_length: int
19-
The length requested by the user.
20-
lowercase_letters_var: tkinter.IntVar()
21-
The variable used to check if the lowercase letters checkbox has been selected or not.
22-
uppercase_letters_var: tkinter.IntVar()
23-
The variable used to check if the upprcase letters checkbox has been selected or not.
24-
digits_var: tkinter.IntVar()
25-
The variable used to check if the digits checkbox has been selected or not.
26-
punctuation_var: tkinter.IntVar()
27-
The variable used to check if the punctuation checkbox has been selected or not.
21+
requested_password_length: str
22+
The input of the user.
23+
'''
24+
if requested_password_length == '':
25+
raise ValueError
26+
else:
27+
try:
28+
return max(min(abs(int(round(float(requested_password_length), 0))), 100), 4)
29+
except:
30+
raise ValueError
31+
32+
def determine_error(valid_character_set_bool, requested_password_length, no_character_set_error, double_error, invalid_input_error) -> str:
33+
'''
34+
Called by create_password_labels,
35+
(upon pressing the done button)
36+
this function retruns what error should be shown to the user:
37+
an invalid_input_error if a character set has been chosen, but the input is unadaptable.
38+
a no_character_set_error if no character set has been chosen, but the input is adaptable,
39+
or a double_error if no character set has been chosen, and the input is unadaptable.
40+
41+
Parameters
42+
----------
43+
valid_character_set_bool: boolean
44+
Whether or not at least one character set has been chosen, as determined by validate_character_sets.
45+
requested_password_length: str
46+
The input_box content.
2847
no_character_set_error: str
29-
The error used when no character set has been picked.
30-
input_box: tkinter.Entry()
31-
The input box used for the length of the password.
48+
'An error occurred. Try again with at least 1 character set.'
3249
double_error: str
33-
The error used when no character set has been picked and when the input is invalid.
50+
'An error occurred. Try again with at least 1 character set and a whole number between 4 and 100.'
3451
invalid_input_error: str
35-
The error used when the input is invalid.
52+
'An error occurred. Try again with a whole number between 4 and 100.'
3653
'''
37-
if lowercase_letters_var.get() == 0 and uppercase_letters_var.get() == 0 and digits_var.get() == 0 and punctuation_var.get() == 0:
54+
55+
if valid_character_set_bool:
56+
try:
57+
adapt_input(requested_password_length)
58+
return ''
59+
except:
60+
return invalid_input_error
61+
else:
3862
try:
39-
if 4 <= int(requested_password_length) <= 100:
40-
return no_character_set_error
41-
else:
42-
input_box.delete(0, 'end')
43-
return double_error
63+
adapt_input(requested_password_length)
64+
return no_character_set_error
4465
except:
45-
input_box.delete(0, 'end')
4666
return double_error
4767

48-
try:
49-
if not 4 <= int(requested_password_length) <= 100:
50-
input_box.delete(0, 'end')
51-
return invalid_input_error
52-
except:
53-
input_box.delete(0, 'end')
54-
return invalid_input_error
55-
68+
def validate_character_sets(lowercase_letters_var, uppercase_letters_var, digits_var, punctuation_var) -> str:
69+
'''
70+
Called by the create_password_labels function
71+
(upon pressing the done button),
72+
this function checks if at least one character set has been chosen by the user,
73+
and returns a boolean.
74+
75+
Parameters
76+
----------
77+
lowercase_letters_var: tkinter.IntVar()
78+
The variable of the lowercase letters checkbox.
79+
uppercase_letters_var: tkinter.IntVar()
80+
The variable of the uppercase letters checkbox.
81+
digits_var: tkinter.IntVar()
82+
The variable of the digits checkbox.
83+
punctuation_var: tkinter.IntVar()
84+
The variable of the punctuation checkbox.
85+
'''
86+
if lowercase_letters_var.get() == 0 and uppercase_letters_var.get() == 0 and digits_var.get() == 0 and punctuation_var.get() == 0:
87+
return False
88+
else:
89+
return True
90+
5691
def generate_password(requested_password_length, lowercase_letters_var, uppercase_letters_var, digits_var, punctuation_var) -> str:
5792
'''
5893
Called by the validate_input function,
@@ -96,9 +131,9 @@ def show_copy_button(event, copy) -> None:
96131
97132
Parameters
98133
----------
99-
event:
134+
event: tkinter.event
100135
Gets the coordinates of the mouse cursor when the user releases a mouse button on a password_label.
101-
copy:
136+
copy: tkinter.Menu()
102137
The copy button itself.
103138
'''
104139
copy.tk_popup(event.x_root, event.y_root - 30)

code/main.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import generate_password_gui
66
import password_strength_gui
77

8-
98
def main():
109
'''
1110
Called upon starting the program,
@@ -33,7 +32,7 @@ def main():
3332

3433
class GeneratePasswordFrame:
3534
'''
36-
A class that creates the "generate password" frame,
35+
A class that creates the 'generate password' frame,
3736
and adds it to the notebook previously created.
3837
3938
...
@@ -46,7 +45,7 @@ class GeneratePasswordFrame:
4645
generate_password_frame = tk.Frame(window)
4746
generate_password_frame.grid(column = 0, row = 0)
4847

49-
# Expand widgets to take up the entire window
48+
# Expand some widgets' rows and columns to take up the entire window
5049
generate_password_frame.grid_columnconfigure(0, weight = 1)
5150
generate_password_frame.grid_rowconfigure(0, weight = 1)
5251
generate_password_frame.grid_rowconfigure(1, weight = 1)
@@ -56,7 +55,7 @@ class GeneratePasswordFrame:
5655

5756
notebook.add(generate_password_frame, text = 'Generate password')
5857

59-
generate_password_gui.show_generate_password_frame(generate_password_frame, done_btn_image)
58+
generate_password_gui.create_generate_password_frame(generate_password_frame, done_btn_image)
6059

6160
class PasswordStrengthFrame:
6261
'''

code/password_strength_gui.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
title_font = 'Helvetica 24'
99
warning_font = 'Helvetica 16'
1010

11+
input_password_msg = 'Please input a password.'
12+
1113
copy_button_y_offest = 30
1214

13-
input_password_msg = 'Please input a password.'
1415

1516
def display_paste_button(event) -> None:
1617
'''
@@ -20,7 +21,7 @@ def display_paste_button(event) -> None:
2021
2122
Parameters
2223
----------
23-
event:
24+
event: tkinter.event
2425
Gets the coordinates of the mouse cursor when the user releases a mouse button on a password_label.
2526
'''
2627
paste.tk_popup(event.x_root, event.y_root - copy_button_y_offest)
@@ -72,7 +73,7 @@ def create_password_strength_frame(frame) -> None:
7273
Parameters
7374
----------
7475
frame: ttk.Frame
75-
The "password strength" frame
76+
The 'password strength' frame
7677
'''
7778
global paste
7879
paste = tk.Menu(frame, tearoff = False)

code/password_strength_logic.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
def check_password_strength(event, inputted_password, input_password_msg) -> list:
1111
'''
1212
Called upon pressing the done button,
13-
this function hosts defines several functions that check the prevalance, length, complexity and repetitiveness of an inputted password,
13+
this function defines several functions that check the prevalance, length, complexity and repetitiveness of an inputted password,
1414
and returns appropriate messages.
1515
It then calls these functions and returns a list of messages indicating the results of each check.
1616
@@ -29,7 +29,7 @@ def check_password_strength(event, inputted_password, input_password_msg) -> lis
2929
def check_if_password_is_common() -> None:
3030
'''
3131
Called by the check_password_strength function
32-
(upon pressing the done button),
32+
(as the user types),
3333
this function checks if the inputted password is in the 100,000 most used passwords (modified_common_password),
3434
and returns an appropiate message.
3535
It does this by counting the number of times an inputted_password is found in the SecLists list.
@@ -43,7 +43,7 @@ def check_if_password_is_common() -> None:
4343
def check_password_length() -> None:
4444
'''
4545
Called by the check_password_strength function
46-
(upon pressing the done button),
46+
(as the user types),
4747
this function categorises the inputted password as very weak, weak, good, or strong depending on its length,
4848
and returns a suitable message.
4949
'''
@@ -65,10 +65,10 @@ def check_password_length() -> None:
6565
def check_password_complexity() -> None:
6666
'''
6767
Called by the check_password_strength function
68-
(upon pressing the done button),
68+
(as the user types),
6969
this function checks how many of the following the inputted password is missing:
7070
lowercase letters, uppercase letters, digits, and punctuation,
71-
and return an adequate warning about them.
71+
and returns an adequate warning about them.
7272
'''
7373
missing_security_features_list = []
7474

@@ -128,7 +128,7 @@ def check_password_complexity() -> None:
128128
def check_for_patterns_in_password() -> None:
129129
'''
130130
Called by the check_password_strength function
131-
(upon pressing the done button),
131+
(as the user types),
132132
this function checks if there are any repeating characters in the inputted password,
133133
and returns an adequate message.
134134
'''

0 commit comments

Comments
 (0)