-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathso.py
More file actions
312 lines (282 loc) · 14.3 KB
/
Copy pathso.py
File metadata and controls
312 lines (282 loc) · 14.3 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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
import xvfbwrapper
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from seleniumbase.common.exceptions import *
from selenium.common.exceptions import TimeoutException as SeleniumTimeoutException
from seleniumbase import SB
import time
import os
import sys
gmail_overide = "Jediweirdo@gmail.com"
password_overide = os.environ["GOOGLE_PASSWORD"]
debug_screenshots_path = "Debug Screenshots"
activate_attempted_solutions = {
"Using XPATHS (to text) instead of CSS Selectors": True
}
# Do not change this var unless you know what you're doing!
debug_folders = ["Login", "Errors", "Warnings"]
"""
Edit the values abover to your liking! Note that ONLY 1 solution in
activate_attempted_solutions can be attempted while the program is
running, and the program will still function if nothing is selected.
Here's a quick summary what each solution attempt does to the code:
Normal Login (Runs if everything is False):
Description:
Uses "div:contains("Some text")" to find the selected 2fa
button. Simple, yet to the point.
Result of Running:
All but "Offline Code"'s 2fa button are found by the
program, but when the program clicks on them, it will always go
to the "Offline Code" 2fa page, no matter which option the user
chooses
Using XPATHS (to text) instead of CSS Selectors:
Description:
Changes the code to use Full XPATHs obtained via the copy element
dropdown in Inspect Element instead of CSS Selectors. The XPATHS
lead directly to the <div> that contains the text describing the
2FA.
Result of Running: (Semi-Fix)
The program is only to finds the 8-digit Backup
Code and the Offline Device Code options, reguardless of if the
user has the other 2FA options enabled on their account
-----
As a quick reminder, my problem is that I can't figure out how to
click on the 2FA option the user selects when they use this program.
For the sake of simplicity, the only function you have to edit to
make this work is complete_tfa(), which should be located right
below this multi-line comment. The rest only exists for the purposes
of replroducing my problem without much work on your end. Good luck,
and thanks for reading!
"""
def complete_tfa(driver): # tfa stands for "Two Factor Authentication"
success_count = 0
tfa_options = {
"Tap Yes": "on your phone",
"SMS Code (Reccomended)": "verification code at",
"Authenticator": "verification code from",
"Backup Code": "8-digit backup codes",
"Offline Code": "even if it's offline"
}
tfa_options_text_xpath = {
"Tap Yes": "/html/body/div[1]/div[1]/div[2]/div/c-wiz/div/div[2]/div/div[1]/div/form/span/section[2]/div/div/section/div/div/div/ul/li[1]/div/div[2]/text()[2]",
"SMS Code (Reccomended)": "/html/body/div[1]/div[1]/div[2]/div/c-wiz/div/div[2]/div/div[1]/div/form/span/section[2]/div/div/section/div/div/div/ul/li[4]/div/div[2]/div[1]/text()",
"Authenticator": "/html/body/div[1]/div[1]/div[2]/div/c-wiz/div/div[2]/div/div[1]/div/form/span/section[2]/div/div/section/div/div/div/ul/li[3]/div/div[2]/text()[1]",
"Backup Code": "/html/body/div[1]/div[1]/div[2]/div/c-wiz/div/div[2]/div/div[1]/div/form/span/section[2]/div/div/section/div/div/div/ul/li[5]/div/div[2]",
"Offline Code": "/html/body/div[1]/div[1]/div[2]/div/c-wiz/div/div[2]/div/div[1]/div/form/span/section[2]/div/div/section/div/div/div/ul/li[2]/div/div[2]"
}
try:
driver.click(By.XPATH, "//*[@id='yDmH0d']/c-wiz/div/div[2]/div/div[2]/div[2]/div[2]/div/div/button/span")
except NoSuchElementException:
print("[WARNING] Couldn't find the 2FA Button! Expect either an error or for some 2FA attempts to be unaccessible from accidental overuse!")
driver.save_screenshot(f"{debug_screenshots_path}/Warnings/TimeoutException - Try Another Way Button.png")
else:
driver.save_screenshot(f"{debug_screenshots_path}/Login/2FA Button.png")
for tfa_type, query in tfa_options.copy().items():
if activate_attempted_solutions["Using XPATHS (to text) instead of CSS Selectors"]:
print(f"[XPATH] Checking for {tfa_type}...")
found_tfa_option = driver.is_element_clickable(By.XPATH, tfa_options_text_xpath[tfa_type])
else:
print(f"Checking for {tfa_type}...")
found_tfa_option = driver.is_element_clickable(f'div:contains("{query}")')
if found_tfa_option:
print(f"Found 2-Factor-Authentication Item {tfa_type}! Adding to the availiable pool...")
success_count += 1
tfa_option = f"{success_count}. {tfa_type}"
tfa_options[tfa_option] = tfa_options.pop(tfa_type) # Modifies the current key to include a number for the user to select in the terminal
else:
print(f"[WARNING] Looks like you never set up for {tfa_type}!")
# print(f"[INFO] {tfa_options}")
# NOTES:
# - If it finds 1 Valid 2FA option, it will automatically select it.
# - If there's more than 1 Valid 2FA option, it will prompt the user to select one via a while loop
# - If there's no Valid 2FA options, the program commits die
if success_count == 0:
print("[ERROR] Couldn't find any 2FA types!")
driver.save_screenshot(f"{debug_screenshots_path}/Errors/TimeoutException - No 2FA.png")
# driver.fail()
return False
elif success_count > 1:
print("Choose your 2FA method:")
for key in tfa_options.keys():
if key[0].isdigit(): # If the 1st char in the key is a digit then it's a successful option
print(key)
while True:
tfa_choice = input("Enter the number corresponding to your choice here: ")
if tfa_choice.strip().isdigit():
if int(tfa_choice.strip()) <= success_count:
tfa_choice = int(tfa_choice)
break
print("That's not a valid number! Go again:")
else:
print("You sorta only have one option, so we're gonna go with that...")
# How the program will find the 2FA option the user selected and clicks on it
for key in tfa_options.copy().keys():
if key[0].isdigit():
if int(key[0]) == tfa_choice:
if activate_attempted_solutions["Using XPATHS (to text) instead of CSS Selectors"]:
print(f"[XPATH] Attempting to click on {key}...")
driver.click(By.XPATH, tfa_options_text_xpath[key[key.find('.') + 2:]]) # Getting rid of the numbers for compatibility with the xpath varient of tfa_options
else:
driver.click(f'div:contains("{tfa_options[key]}")')
tfa_options[key] = True # Used as an identifier to redirect to the correct function
tfa_options[key[key.find('.') + 2:]] = tfa_options.pop(key) # Gets rid of the number added for the user to select
print(f"{key[0]}")
print(tfa_options)
time.sleep(3) # So the program wait for Google to go to the next page before screenshotting
# Redirects user to its corresponding function
if isinstance(tfa_options["Tap Yes"], bool):
return check_if_out_of_tfa(tap_yes_tfa(driver))
elif isinstance(tfa_options["SMS Code (Reccomended)"], bool):
return check_if_out_of_tfa(sms_tfadriver(driver))
elif isinstance(tfa_options["Authenticator"], bool):
return check_if_out_of_tfa(authenticator_tfa(driver))
elif isinstance(tfa_options["Backup Code"], bool):
return check_if_out_of_tfa(backup_code_tfa(driver))
elif isinstance(tfa_options["Offline Code"], bool):
return check_if_out_of_tfa(offline_code_tfa(driver))
else:
print("[ERROR] How did you break this so badly?!")
driver.save_screenshot(f"{debug_screenshots_path}/Errors/StupidCoder - Invalid 2fa Choice.png")
# driver.fail()
return False
# These are all basically empty, so don't worry much about them
def tap_yes_tfa(driver):
driver.save_screenshot(f"{debug_screenshots_path}/Login/2fa Chosen - Tap Yes")
return login_check(driver, "https://stackoverflow.com/users/login")
def sms_tfa(driver):
driver.save_screenshot(f"{debug_screenshots_path}/Login/2fa Chosen - SMS Message")
return login_check(driver, "https://stackoverflow.com/users/login")
def authenticator_tfa(driver, max_attempts = 3):
driver.save_screenshot(f"{debug_screenshots_path}/Login/2fa Chosen - Authenticator")
return login_check(driver, "https://stackoverflow.com/users/login")
def backup_code_tfa(driver):
driver.save_screenshot(f"{debug_screenshots_path}/Login/2fa Chosen - Backup Code")
return login_check(driver, "https://stackoverflow.com/users/login")
def offline_code_tfa(driver):
driver.save_screenshot(f"{debug_screenshots_path}/Login/2fa Chosen - Offline Code")
return login_check(driver, "https://stackoverflow.com/users/login")
def delete_old_debug_info():
if not os.path.exists(debug_screenshots_path):
print(f"The chosen Debug Screenshots Path ('{debug_screenshots_path}') doesn't exist on your PC! Did you change debug_screenshots_path?")
exit()
for folder in debug_folders:
if not os.path.exists(f"{debug_screenshots_path}/{folder}"):
print(f"Creating a screenshot debug folder stored at {debug_screenshots_path}/{folder}...")
os.mkdir(f"{debug_screenshots_path}/{folder}")
print("Deleting old news...")
for folders in os.listdir(debug_screenshots_path):
for files in os.listdir(f"{debug_screenshots_path}/{folders}"):
os.remove(f"{debug_screenshots_path}/{folders}/{files}")
def login_via_password():
delete_old_debug_info()
print("Alright, let's get you logged in.")
username = gmail_overide
password = password_overide
print("Creating the webdriver...")
with xvfbwrapper.Xvfb(), SB(uc=True, headed=True) as driver:
driver.open("https://stackoverflow.com/users/login") # Getting to the login page
print("Going to Google Login Page...")
# Get rid of the cookies button in the way of the Google Button
try:
driver.save_screenshot(f"{debug_screenshots_path}/Login/SO Login Page.png")
driver.click('//button[@id="onetrust-reject-all-handler"]')
driver.save_screenshot(f"{debug_screenshots_path}/Login/Cookieless SO Login Page.png")
except NoSuchElementException:
print("[WARNING] Couldn't Find Stuff in the Way!")
driver.save_screenshot(f"{debug_screenshots_path}/Warnings/NoSuchElementException - Cookies.png")
# Go to and click the Google login button
try:
driver.click('//*[@id="openid-buttons"]/button[1]')
except ElementClickInterceptedException:
print("[ERROR] Element is not clickable because something is in the way!")
return fail(str(error))
else:
driver.save_screenshot(f"{debug_screenshots_path}/Login/Google Email Login Page.png")
print("Entering Your Credentials...")
# Enters the email
try:
driver.type('//input[@id="identifierId"]', username)
except TimeoutException:
print("[ERROR] Could not find the username field!")
driver.save_screenshot(f"{debug_screenshots_path}/Errors/TimeoutException.png")
# driver.fail()
return False
else:
driver.save_screenshot(f"{debug_screenshots_path}/Login/Google Username Field.png")
# Presses Next
try:
driver.click('button:contains("Next")')
except TimeoutException:
print("[ERROR] Could not find the email submit button!")
driver.save_screenshot(f"{debug_screenshots_path}/Errors/TimeoutException.png")
# driver.fail()
return False
else:
driver.save_screenshot(f"{debug_screenshots_path}/Login/Google Password Field (Submitted Username).png")
# Enters the password
try:
driver.type("input[type='password']", password)
except (TimeoutException, ElementNotVisibleException)as e:
if str(type(e).__name__) == "ElementNotVisibleException":
print(f"[ERROR] '{gmail_overide}' is an Invalid Email! Did you change gmail_overide?")
driver.save_screenshot(f"{debug_screenshots_path}/Errors/ElementNotVisibleExeption - Bad email.png")
# driver.fail()
return False
else:
driver.save_screenshot(f"{debug_screenshots_path}/Errors/TimeoutException - Busted.png")
print(f"[ERROR] Could not find the password field! We probably got busted... Check in {debug_screenshots_path}/Errors/TimeoutException - Busted.png")
# driver.fail()
return False
# Presses Next
try:
driver.click('button:contains("Next")')
except (TimeoutException, NoSuchElementException) as e:
print("[ERROR] Couldn't find the password submit button!")
driver.save_screenshot(f"{debug_screenshots_path}/Errors/{type(e).__name__}.png")
# driver.fail()
return False
# Checks if the account has 2FA
logged_in = login_check(driver, "https://stackoverflow.com/")
if logged_in == True: # Success! Got to SO
print("Wow, I think we made it across the firewall! Maybe there's hope afterall...")
driver.save_screenshot(f"{debug_screenshots_path}/Login/Got Past the Google Guards.png")
# driver.fail()
return True
elif logged_in == "Redirected": # Redirected to 2FA Page
print("Login Successful, but it looks like you got 2FA enabled. We'll have to go through that manually!")
driver.save_screenshot(f"{debug_screenshots_path}/Login/2FA Enabled.png")
return complete_tfa(driver) # tfa stands for "Two Factor Authentication"
else: # It denied the password and didn't go anywhere
print(f"[ERROR] '{password_overide}' is a Bad Password! Did you change password_overide?")
del username, password
driver.save_screenshot(f"{debug_screenshots_path}/Errors/TimeoutException - Bad Password.png")
# driver.fail()
return False
def login_check(driver, target_url):
"""
What it does is simple-- it checks if the user is at the target URL or not.
Has 3 possible returns:
- True (got to target email)
- False (Never changed pages)
- "Redirected" (Redirected to a different page, but not the targeted one)
"""
starting_url = driver.get_current_url()
try:
WebDriverWait(driver, 10).until(lambda driver: target_url in driver.get_current_url())
return True
except SeleniumTimeoutException:
if starting_url != driver.get_current_url():
return "Redirected"
else:
return False
def check_if_out_of_tfa(logged_in):
if logged_in == "Redirected":
print("[ERROR] Wha..??? Where did it take you???")
driver.save_screenshot(f"{debug_screenshots_path}/Errors/WrongWarpException - Post 2FA.png")
drivr.fail()
return False
else:
return logged_in
# RUNTIME CODE
print(f"---------------------------------------\nLogged in? {login_via_password()}")