Skip to content

Commit

Permalink
Suspicious Login Attempt
Browse files Browse the repository at this point in the history
- Manage Suspicious Login Attempts
- Refactor Error Codes
  • Loading branch information
davidwickerhf committed Oct 28, 2020
1 parent 359b512 commit 04f1e97
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 18 deletions.
58 changes: 50 additions & 8 deletions instaclient/client/instaclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,21 @@ def __init__(self, driver_type: int=CHROMEDRIVER, host:int=LOCAHOST):
chrome_options = webdriver.ChromeOptions()
chrome_options.binary_location = os.environ.get("GOOGLE_CHROME_BIN")
chrome_options.add_argument('--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1')
chrome_options.add_argument("window-size=525,950")
chrome_options.add_argument("--window-size=720,1280")
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-gpu")
self.driver = webdriver.Chrome(executable_path=os.environ.get("CHROMEDRIVER_PATH"), chrome_options=chrome_options)
elif host == self.LOCAHOST:
# Running locally
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1')
chrome_options.add_argument("window-size=525,950")
chrome_options.add_argument("--window-size=720,1280")
#chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-gpu")
self.driver = webdriver.Chrome(executable_path='instaclient/drivers/chromedriver.exe', chrome_options=chrome_options)
else:
raise InvaildHostError(host)
Expand Down Expand Up @@ -136,11 +141,17 @@ def login(self, username:str, password:str, check_user:bool=True):
self.password = None
raise InvaildPasswordError(password)

# Detect Suspicious Login Attempt Dialogue
send_code = self.__find_buttons(EC.presence_of_element_located((By.XPATH, Paths.SEND_CODE)), wait_time=3)
if send_code:
send_code.click()
raise SuspisciousLoginAttemptError()

# Detect 2FS
scode_input = self.__check_existence(EC.presence_of_element_located((By.XPATH, Paths.SECURITY_CODE)), wait_time=3)
scode_input = self.__check_existence(EC.presence_of_element_located((By.XPATH, Paths.VERIFICATION_CODE)), wait_time=3)
if scode_input:
# 2F Auth is enabled, request security code
raise SecurityCodeNecessary()
raise VerificationCodeNecessary()
else:
self.logged_in = True

Expand All @@ -156,9 +167,40 @@ def login(self, username:str, password:str, check_user:bool=True):
self.dismiss_dialogue()
return self.logged_in


@insta_method
def input_security_code(self, code:int or str):
code = str(code)
if len(code) < 6:
raise InvalidSecurityCodeError()
elif not code.isdigit():
raise InvalidSecurityCodeError()

scode_input:WebElement = self.__find_element(EC.presence_of_element_located((By.XPATH, Paths.SECURITY_CODE_INPUT)), wait_time=4)
scode_btn = self.__find_element(EC.presence_of_element_located((By.XPATH, Paths.SECURITY_CODE_BTN)), wait_time=4)
scode_input.send_keys(code)
time.sleep(0.5)
scode_btn.click()

# Detect Error
form_error = self.__find_element(EC.presence_of_element_located((By.XPATH, Paths.INVALID_CODE)), wait_time=3)
if form_error:
# Invalid Code
scode_input.clear()
raise InvalidSecurityCodeError()

self.logged_in = True
self.dismiss_dialogue()
return self.logged_in







@insta_method
def input_verification_code(self, code:int or str):
"""
Complete login procedure started with `InstaClient.login()` and insert 2FA security code. Sets `instaclient.logged_in` to True if login was successful.
Expand All @@ -171,7 +213,7 @@ def input_security_code(self, code:int or str):
Returns:
bool: Returns True if login was successful
"""
scode_input: WebElement = self.__find_element(EC.presence_of_element_located((By.XPATH, Paths.SECURITY_CODE)), wait_time=4)
scode_input: WebElement = self.__find_element(EC.presence_of_element_located((By.XPATH, Paths.VERIFICATION_CODE)), wait_time=4)
scode_input.send_keys(code)
scode_btn: WebElement = self.__find_element(EC.element_to_be_clickable((By.XPATH, Paths.SECURITY_CODE_BTN)), wait_time=5)
time.sleep(1)
Expand All @@ -182,7 +224,7 @@ def input_security_code(self, code:int or str):
# Code is Wrong
# Clear input field
scode_input.clear()
raise InvalidSecurityCodeError()
raise InvalidVerificationCodeError()
else:
# Auth Correct
self.logged_in = True
Expand Down Expand Up @@ -447,7 +489,7 @@ def nav_user_dm(self, user:str, check_user:bool=True):
def is_valid_user(self, user, nav_to_user=True):
if nav_to_user:
self.driver.get(ClientUrls.NAV_USER.format(user))
element = self.__check_existence(EC.presence_of_element_located((By.XPATH, Paths.PAGE_NOT_FOUND)), wait_time=3)
element = self.__check_existence(EC.presence_of_element_located((By.XPATH, Paths.PAGE_NOT_FOUND)), wait_time=8)
if element:
# User does not exist
self.driver.get(ClientUrls.HOME_URL)
Expand Down
8 changes: 7 additions & 1 deletion instaclient/client/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ class Paths:
LOGIN_BTN = '//button[@class="sqdOP L3NKy y3zKF "]/div'
USERNAME_INPUT = '//input[@name="username"]'
PASSWORD_INPUT = '//input[@name="password"]'
SECURITY_CODE = '//input[@name="verificationCode"]'
# Suscpicious Activity Dialogue
SEND_CODE = '//button[@class="_5f5mN jIbKX KUBKM yZn4P "]'
SECURITY_CODE_INPUT = '//input[@name="security_code" or @class="_281Ls zyHYP"]'
INPUT_CODE_BTN = '//button[@class="_5f5mN jIbKX KUBKM yZn4P "]'
INVALID_CODE = '//div[@class="_3_2jD" and @id="form_error"]'
# 2FA Verification
VERIFICATION_CODE = '//input[@name="verificationCode"]'
SECURITY_CODE_BTN = '//button[@class="sqdOP L3NKy y3zKF "]'
NO_NOTIFICATIONS_BTN = '/html/body/div[4]/div/div/div/div[3]/button[2]'
# Nav to User Procedure
Expand Down
16 changes: 14 additions & 2 deletions instaclient/errors/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,28 @@ def __init__(self, password:str):
super().__init__(message='The password used to attempt login is incorrect. Check the password.')


class SecurityCodeNecessary(InstaClientError):
class VerificationCodeNecessary(InstaClientError):
"""Raised if security code is necessary to log in"""
def __init__(self):
super().__init__(message='The 2FA security code is required. The Security Code has been sent to the user\'s phone number or Authenticator App.')


class SuspisciousLoginAttemptError(InstaClientError):
"""Raised if security code is necessary to log in"""
def __init__(self):
super().__init__(message='The provided security code is invalid.')


class InvalidSecurityCodeError(InstaClientError):
"""Raised if security code inputted by the user is invalid"""
def __init__(self):
super().__init__(message='The used security code is invalid. Please try entering the code correctly or ask the user to input one of their backup codes')
super().__init__(message='The used security code is invalid.')


class InvalidVerificationCodeError(InstaClientError):
"""Raised if security code inputted by the user is invalid"""
def __init__(self):
super().__init__(message='The used verification code is invalid. Please try entering the code correctly or ask the user to input one of their backup codes')


class InvaildTagError(InstaClientError):
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
setup(
name = 'instaclient', # How you named your package folder (MyLib)
packages = find_packages(exclude=['tests']), # Chose the same as "name"
version = '1.5.3', # Start with a small number and increase it with every change you make
version = '1.6.1', # Start with a small number and increase it with every change you make
license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository
description = 'Instagram client built with Python 3.8 and the Selenium package.',
long_description=README,
long_description_content_type="text/markdown",
author = 'David Wicker', # Type in your name
author_email = '[email protected]', # Type in your E-Mail
url = 'https://github.com/wickerdevs/py-instaclient', # Provide either the link to your github or to your website
download_url = 'https://github.com/wickerdevs/py-instaclient/archive/v1.5.3.tar.gz', # I explain this later on
download_url = 'https://github.com/wickerdevs/py-instaclient/archive/v1.6.1.tar.gz', # I explain this later on
keywords = ['INSTAGRAM', 'BOT', 'INSTAGRAM BOT', 'INSTAGRAM CLIENT'], # Keywords that define your package best
install_requires=[ # I get to this in a second
'selenium',
Expand Down
10 changes: 5 additions & 5 deletions tests/tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from instaclient.errors.common import InvalidUserError, InvalidSecurityCodeError, PrivateAccountError, SecurityCodeNecessary
from instaclient.errors.common import InvalidUserError, InvalidVerificationCodeError, PrivateAccountError, VerificationCodeNecessary
import unittest
from instaclient import InstaClient

Expand All @@ -25,13 +25,13 @@ def test_login(self):
"""
try:
response = self.client.login()
except SecurityCodeNecessary:
except VerificationCodeNecessary:
code = input('Enter the 2FA security code sent to your phone or generated by your Authenticator App:')
try:
response = self.client.input_security_code(code)
except InvalidSecurityCodeError:
response = self.client.input_verification_code(code)
except InvalidVerificationCodeError:
code = input('The security code you entered is invalid. Please try again: ')
response = self.client.input_security_code(code)
response = self.client.input_verification_code(code)
self.assertEqual(response, True, 'Should be True (connected)')

def test_search_tag(self):
Expand Down

0 comments on commit 04f1e97

Please sign in to comment.