Advertisement
stuppid_bot

Untitled

Jan 21st, 2016
222
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.02 KB | None | 0 0
  1. # Модуль для авторизации Вконтакте. Существует 4 способа авторизации, но нам
  2. # интересны только два: прямая авторизация и авторизация клиентских приложений.
  3. # Данные (client id и client secret) от официальных приложений нагуглить
  4. # несложно (например, от Android client id: 2274003, client secret:
  5. # hHbZxrka2uZ6jB1inYsH), свое же Standalone приложение можно создать по ссылке
  6. # <https://vk.com/editapp?act=create>
  7. import logging
  8. import re
  9. import urllib.parse
  10.  
  11.  
  12. AUTH_BASE = "https://oauth.vk.com"
  13. REDIRECT_URI = AUTH_BASE + "/blank.html"
  14.  
  15.  
  16. class AuthMixin:
  17.     @property
  18.     def auth_url(self):
  19.         return urllib.parse.urljoin(AUTH_BASE, self.auth_path)
  20.  
  21.     def parse_hash(self, url):
  22.         data = dict(urllib.parse.parse_qsl(url.split("#")[1]))
  23.         if 'error' in data:
  24.             raise OAuthError(data['error'], data.get('error_description'))
  25.         self.client.new_access_token(
  26.             data['access_token'],
  27.             int(data['user_id']),
  28.             int(data['expires_in']),
  29.             data.get('secret')
  30.         )
  31.  
  32.  
  33. class DirectAuth(AuthMixin):
  34.     """Класс для прямой авторизации <https://vk.com/dev/auth_direct>
  35.  
  36.    Тут логика работы несколько другая чем в классе FectchVk. Этот класс
  37.    только набросок. Необходимо реализовать какой-то примитивный
  38.    графический интерфейс.
  39.    """
  40.     auth_path = 'token'
  41.  
  42.     def __init__(self,
  43.                  client,
  44.                  client_id,
  45.                  client_secret,
  46.                  scope=None,
  47.                  test_redirect_uri=None):
  48.         self.client = client
  49.         self.client_id = client_id
  50.         self.client_secret = client_secret
  51.         self.scope = scope
  52.         self.test_redirect_uri = test_redirect_uri
  53.         logger_name = '.'.join([__name__, self.__class__.__name__])
  54.         self.logger = logging.getLogger(logger_name)
  55.  
  56.     def authorize(self, username, password):
  57.         if not username or not password:
  58.             raise AuthError("Username and password are required")
  59.         params = {
  60.             'client_id': self.client_id,
  61.             'client_secret': self.client_secret,
  62.             'grant_type': 'password',
  63.             'username': username,
  64.             'password': password,
  65.             'v': self.client.api_version
  66.         }
  67.         if self.scope:
  68.             params['scope'] = self.scope
  69.         if self.test_redirect_uri:
  70.             params['test_redirect_uri']
  71.         self.request(params)
  72.  
  73.     def request(self, params):
  74.         self.logger.debug("Params: %s", params)
  75.         response = self.client.request(self.auth_url, params)
  76.         if 'captcha_img' in response:
  77.             params['captcha_sid'] = response.captcha_sid
  78.             self.handle_captcha(response.captcha_img, params)
  79.             return
  80.         if 'redirect_uri' in response:
  81.             self.handle_validation(response.redirect_uri)
  82.         # Прочие ошибки
  83.         if 'error' in response:
  84.             raise OAuthError(response.error, response.get('error_description'))
  85.         self.client.new_access_token(
  86.             response.access_token,
  87.             response.user_id,
  88.             response.expires_in,
  89.             response.get('secret')
  90.         )
  91.         self.logger.info("Successfully authorized")
  92.  
  93.     def handle_captcha(self, captcha_img, params):
  94.         # captcha_sid уже в params
  95.         # params['captcha_key'] = captcha_key
  96.         # можно еще обновить username и password, если допустим имеется
  97.         # графический интерфейс и поля для ввода логина, пароля и капчи
  98.         # одновременно
  99.         # По новой пробуем авторизоваться
  100.         # self.request(params)
  101.         raise AuthError("Captcha Required")
  102.  
  103.     def handle_validation(self, redirect_uri):
  104.         # Процедура валидации заключается в вводе цифр телефона (кроме префикса
  105.         # и двух последних), после чего мы попадем на страницу:
  106.         # {REDIRECT_URI}#access_token=ACCESS_TOKEN...
  107.         # либо на:
  108.         # {REDIRECT_URI}#error=...&error_description=...
  109.         # Парсим эту строку с помощью self.parse_hash
  110.         raise AuthError("Validation Required")
  111.  
  112.  
  113. class ClientAuth(AuthMixin):
  114.     """Осуществляет авторизацию Standalone приложения
  115.    <https://vk.com/dev/auth_mobile>"""
  116.     auth_path = 'authorize'
  117.  
  118.     def __init__(self,
  119.                  client,
  120.                  client_id,
  121.                  scope=None,
  122.                  display=None,
  123.                  redirect_uri=REDIRECT_URI):
  124.         self.client = client
  125.         self.session = self.client.session
  126.         self.client_id = client_id
  127.         self.scope = scope
  128.         self.redirect_uri = redirect_uri
  129.         self.display = display
  130.         logger_name = '.'.join([__name__, self.__class__.__name__])
  131.         self.logger = logging.getLogger(logger_name)
  132.  
  133.     def authorize(self, username, password):
  134.         if not username or not password:
  135.             raise AuthError("Username and password are required")
  136.         params = {
  137.             'client_id': self.client_id,
  138.             'redirect_uri': self.redirect_uri,
  139.             'response_type': 'token',
  140.             'v': self.client.api_version,
  141.         }
  142.         if self.scope:
  143.             params['scope'] = self.scope
  144.         if self.display:
  145.             params['display'] = self.display
  146.         r = self.session.post(self.auth_url, params)
  147.         action = self.get_action(r.text)
  148.         data = self.get_hiddens(r.text)
  149.         data['email'] = username
  150.         data['pass'] = password
  151.         self.submit_login(action, data)
  152.  
  153.     def submit_login(self, action, data):
  154.         """Эмилируем отправку формы."""
  155.         r = self.session.post(action, data)
  156.         self.logger.debug("Current URL: %s", r.url)
  157.         # Если уже авторизованы сразу перебросит на {REDIRECT_URI}#{PARAMS}
  158.         if '#' in r.url:
  159.             self.parse_hash(r.url)
  160.             return
  161.         # Либо попадем на страницу подтверждения с кнопочкой Allow
  162.         ga_match = re.search(
  163.             'https://login\.vk\.com/\?act=grant_access([^"]+)', r.text)
  164.         if ga_match:
  165.             ga_url = ga_match.group(0)
  166.             # Переходим по ссылке (эмулируем ее нажатие)
  167.             r = self.session.get(ga_url)
  168.             # Попадаем на {REDIRECT_URI}#{PARAMS}
  169.             self.parse_hash(r.url)
  170.             return
  171.         # Ищем капчу
  172.         captcha_match = re.search(
  173.             '([^"]+/captcha\.php\?sid=[^"]+)', r.text)
  174.         if captcha_match:
  175.             captcha_img, captcha_sid = captcha_match.groups()
  176.             # Вроде лишнее
  177.             # action = self.get_action()
  178.             # data = self.get_hiddens()
  179.             data['captcha_sid'] = captcha_sid
  180.             self.handle_captcha(captcha_img, action, data)
  181.             return
  182.         # Ищем ошибку
  183.         # display=page,popup
  184.         # <div class="oauth_error">Invalid login or password.</div>
  185.         # display=mobile
  186.         # <div class="service_msg service_msg_warning">Invalid login or
  187.         # password.</div>
  188.         error_match = re.search(
  189.             '(warning|error)">([^<]+)', r.text)
  190.         if error_match:
  191.             raise AuthError(error_match.group(2))
  192.         raise AuthError("WTF?")
  193.  
  194.     def handle_captcha(self, captcha_img, action, data):
  195.         # Выводи форму с капчей либо используем антигейт, а потом снова
  196.         # отправляем форму
  197.         # data['captcha_key'] = captcha_key
  198.         # self.submit_login(action, data)
  199.         raise AuthError("Captcha Required")
  200.  
  201.     def get_action(self, content):
  202.         match = re.search('action="([^"]+)', content)
  203.         return match.group(1)
  204.  
  205.     def get_hiddens(self, content):
  206.         """Возвращает значения скрытых полей на странице в виде словаря."""
  207.         matches = re.findall(
  208.             '(_origin|ip_h|lg_h|to|captcha_sid|expire)" value="([^"]+)',
  209.             content
  210.         )
  211.         return dict(matches)
  212.  
  213.  
  214. class AuthError(Exception):
  215.     pass
  216.  
  217.  
  218. class OAuthError(AuthError):
  219.     def __init__(self, name, description):
  220.         self.name = name
  221.         self.description = description
  222.  
  223.     def __str__(self):
  224.         if self.description:
  225.             return ": ".join([self.name, self.description])
  226.         return self.name
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement