Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Модуль для авторизации Вконтакте. Существует 4 способа авторизации, но нам
- # интересны только два: прямая авторизация и авторизация клиентских приложений.
- # Данные (client id и client secret) от официальных приложений нагуглить
- # несложно (например, от Android client id: 2274003, client secret:
- # hHbZxrka2uZ6jB1inYsH), свое же Standalone приложение можно создать по ссылке
- # <https://vk.com/editapp?act=create>
- import logging
- import re
- import urllib.parse
- AUTH_BASE = "https://oauth.vk.com"
- REDIRECT_URI = AUTH_BASE + "/blank.html"
- class AuthMixin:
- @property
- def auth_url(self):
- return urllib.parse.urljoin(AUTH_BASE, self.auth_path)
- def parse_hash(self, url):
- data = dict(urllib.parse.parse_qsl(url.split("#")[1]))
- if 'error' in data:
- raise OAuthError(data['error'], data.get('error_description'))
- self.client.new_access_token(
- data['access_token'],
- int(data['user_id']),
- int(data['expires_in']),
- data.get('secret')
- )
- class DirectAuth(AuthMixin):
- """Класс для прямой авторизации <https://vk.com/dev/auth_direct>
- Тут логика работы несколько другая чем в классе FectchVk. Этот класс
- только набросок. Необходимо реализовать какой-то примитивный
- графический интерфейс.
- """
- auth_path = 'token'
- def __init__(self,
- client,
- client_id,
- client_secret,
- scope=None,
- test_redirect_uri=None):
- self.client = client
- self.client_id = client_id
- self.client_secret = client_secret
- self.scope = scope
- self.test_redirect_uri = test_redirect_uri
- logger_name = '.'.join([__name__, self.__class__.__name__])
- self.logger = logging.getLogger(logger_name)
- def authorize(self, username, password):
- if not username or not password:
- raise AuthError("Username and password are required")
- params = {
- 'client_id': self.client_id,
- 'client_secret': self.client_secret,
- 'grant_type': 'password',
- 'username': username,
- 'password': password,
- 'v': self.client.api_version
- }
- if self.scope:
- params['scope'] = self.scope
- if self.test_redirect_uri:
- params['test_redirect_uri']
- self.request(params)
- def request(self, params):
- self.logger.debug("Params: %s", params)
- response = self.client.request(self.auth_url, params)
- if 'captcha_img' in response:
- params['captcha_sid'] = response.captcha_sid
- self.handle_captcha(response.captcha_img, params)
- return
- if 'redirect_uri' in response:
- self.handle_validation(response.redirect_uri)
- # Прочие ошибки
- if 'error' in response:
- raise OAuthError(response.error, response.get('error_description'))
- self.client.new_access_token(
- response.access_token,
- response.user_id,
- response.expires_in,
- response.get('secret')
- )
- self.logger.info("Successfully authorized")
- def handle_captcha(self, captcha_img, params):
- # captcha_sid уже в params
- # params['captcha_key'] = captcha_key
- # можно еще обновить username и password, если допустим имеется
- # графический интерфейс и поля для ввода логина, пароля и капчи
- # одновременно
- # По новой пробуем авторизоваться
- # self.request(params)
- raise AuthError("Captcha Required")
- def handle_validation(self, redirect_uri):
- # Процедура валидации заключается в вводе цифр телефона (кроме префикса
- # и двух последних), после чего мы попадем на страницу:
- # {REDIRECT_URI}#access_token=ACCESS_TOKEN...
- # либо на:
- # {REDIRECT_URI}#error=...&error_description=...
- # Парсим эту строку с помощью self.parse_hash
- raise AuthError("Validation Required")
- class ClientAuth(AuthMixin):
- """Осуществляет авторизацию Standalone приложения
- <https://vk.com/dev/auth_mobile>"""
- auth_path = 'authorize'
- def __init__(self,
- client,
- client_id,
- scope=None,
- display=None,
- redirect_uri=REDIRECT_URI):
- self.client = client
- self.session = self.client.session
- self.client_id = client_id
- self.scope = scope
- self.redirect_uri = redirect_uri
- self.display = display
- logger_name = '.'.join([__name__, self.__class__.__name__])
- self.logger = logging.getLogger(logger_name)
- def authorize(self, username, password):
- if not username or not password:
- raise AuthError("Username and password are required")
- params = {
- 'client_id': self.client_id,
- 'redirect_uri': self.redirect_uri,
- 'response_type': 'token',
- 'v': self.client.api_version,
- }
- if self.scope:
- params['scope'] = self.scope
- if self.display:
- params['display'] = self.display
- r = self.session.post(self.auth_url, params)
- action = self.get_action(r.text)
- data = self.get_hiddens(r.text)
- data['email'] = username
- data['pass'] = password
- self.submit_login(action, data)
- def submit_login(self, action, data):
- """Эмилируем отправку формы."""
- r = self.session.post(action, data)
- self.logger.debug("Current URL: %s", r.url)
- # Если уже авторизованы сразу перебросит на {REDIRECT_URI}#{PARAMS}
- if '#' in r.url:
- self.parse_hash(r.url)
- return
- # Либо попадем на страницу подтверждения с кнопочкой Allow
- ga_match = re.search(
- 'https://login\.vk\.com/\?act=grant_access([^"]+)', r.text)
- if ga_match:
- ga_url = ga_match.group(0)
- # Переходим по ссылке (эмулируем ее нажатие)
- r = self.session.get(ga_url)
- # Попадаем на {REDIRECT_URI}#{PARAMS}
- self.parse_hash(r.url)
- return
- # Ищем капчу
- captcha_match = re.search(
- '([^"]+/captcha\.php\?sid=[^"]+)', r.text)
- if captcha_match:
- captcha_img, captcha_sid = captcha_match.groups()
- # Вроде лишнее
- # action = self.get_action()
- # data = self.get_hiddens()
- data['captcha_sid'] = captcha_sid
- self.handle_captcha(captcha_img, action, data)
- return
- # Ищем ошибку
- # display=page,popup
- # <div class="oauth_error">Invalid login or password.</div>
- # display=mobile
- # <div class="service_msg service_msg_warning">Invalid login or
- # password.</div>
- error_match = re.search(
- '(warning|error)">([^<]+)', r.text)
- if error_match:
- raise AuthError(error_match.group(2))
- raise AuthError("WTF?")
- def handle_captcha(self, captcha_img, action, data):
- # Выводи форму с капчей либо используем антигейт, а потом снова
- # отправляем форму
- # data['captcha_key'] = captcha_key
- # self.submit_login(action, data)
- raise AuthError("Captcha Required")
- def get_action(self, content):
- match = re.search('action="([^"]+)', content)
- return match.group(1)
- def get_hiddens(self, content):
- """Возвращает значения скрытых полей на странице в виде словаря."""
- matches = re.findall(
- '(_origin|ip_h|lg_h|to|captcha_sid|expire)" value="([^"]+)',
- content
- )
- return dict(matches)
- class AuthError(Exception):
- pass
- class OAuthError(AuthError):
- def __init__(self, name, description):
- self.name = name
- self.description = description
- def __str__(self):
- if self.description:
- return ": ".join([self.name, self.description])
- return self.name
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement