예제 #1
0
    def parse_signed_data(cls, signed_request, secret=facebook_settings.FACEBOOK_APP_SECRET):
        """
        Thanks to 
        http://stackoverflow.com/questions/3302946/how-to-base64-url-decode-in-python
        and
        http://sunilarora.org/parsing-signedrequest-parameter-in-python-bas
        """
        from open_facebook.utils import base64_url_decode_php_style

        l = signed_request.split(".", 2)
        encoded_sig = l[0]
        payload = l[1]
        from open_facebook.utils import json

        sig = base64_url_decode_php_style(encoded_sig)
        import hmac
        import hashlib

        data = json.loads(base64_url_decode_php_style(payload))

        if data.get("algorithm").upper() != "HMAC-SHA256":
            logger.error("Unknown algorithm")
            return None
        else:
            expected_sig = hmac.new(secret, msg=payload, digestmod=hashlib.sha256).digest()

        if sig != expected_sig:
            return None
        else:
            logger.debug("valid signed request received..")
            return data
예제 #2
0
파일: api.py 프로젝트: evrenesat/ganihomes
    def parse_signed_data(cls, signed_request, secret=facebook_settings.FACEBOOK_APP_SECRET):
        '''
        Thanks to
        http://stackoverflow.com/questions/3302946/how-to-base64-url-decode-in-python
        and
        http://sunilarora.org/parsing-signedrequest-parameter-in-python-bas
        '''
        from open_facebook.utils import base64_url_decode_php_style
        l = signed_request.split('.', 2)
        encoded_sig = l[0]
        payload = l[1]
        from open_facebook.utils import json
        sig = base64_url_decode_php_style(encoded_sig)
        import hmac
        import hashlib
        data = json.loads(base64_url_decode_php_style(payload))

        algo = data.get('algorithm').upper()
        if  algo != 'HMAC-SHA256':
            send_warning('Unknown algorithm we only support HMAC-SHA256 user asked for %s', algo)
            logger.error('Unknown algorithm')
            return None
        else:
            expected_sig = hmac.new(secret, msg=payload, digestmod=hashlib.sha256).digest()

        if sig != expected_sig:
            send_warning('Signature %s didnt match the expected signature %s', sig, expected_sig)
            return None
        else:
            logger.debug('valid signed request received..')
            return data
예제 #3
0
    def parse_signed_data(cls, signed_request,
                          secret=facebook_settings.FACEBOOK_APP_SECRET):
        '''
        Thanks to
        http://stackoverflow.com/questions/3302946/how-to-base64-url-decode-in-python
        and
        http://sunilarora.org/parsing-signedrequest-parameter-in-python-bas
        '''
        from open_facebook.utils import base64_url_decode_php_style
        l = signed_request.split('.', 2)
        encoded_sig = l[0]
        payload = l[1]
        from open_facebook.utils import json
        sig = base64_url_decode_php_style(encoded_sig)
        import hmac
        import hashlib
        data = json.loads(base64_url_decode_php_style(payload))

        algo = data.get('algorithm').upper()
        if  algo != 'HMAC-SHA256':
            send_warning('Unknown algorithm we only support HMAC-SHA256 ' \
                         'user asked for %s', algo)
            logger.error('Unknown algorithm')
            return None
        else:
            expected_sig = hmac.new(secret, msg=payload,
                                    digestmod=hashlib.sha256).digest()

        if sig != expected_sig:
            send_warning('Signature %s didnt match the expected ' \
                         'signature %s', sig, expected_sig)
            return None
        else:
            logger.debug('valid signed request received..')
            return data
예제 #4
0
    def test_oauth_errors(self):
        expires_response = '''{
          "error": {
            "type": "OAuthException",
            "message": "Session has expired at unix time SOME_TIME. The current unix time is SOME_TIME."
          }
        } '''
        changed_password_response = '''
        {
          "error": {
            "type": "OAuthException",
            "message": "The session has been invalidated because the user has changed the password."
          }
        }
        '''
        deauthorized_response = '''
        {
          "error": {
            "type": "OAuthException",
            "message": "Error validating access token: USER_ID has not authorized application APP_ID"
          }
        }
        '''
        loggedout_response = '''
        {
          "error": {
            "type": "OAuthException",
            "message": "Error validating access token: The session is invalid because the user logged out."
           }
        }
        '''
        responses = [
            expires_response, changed_password_response, deauthorized_response,
            loggedout_response
        ]
        response_objects = []
        for response_string in responses:
            response = json.loads(response_string)

            response_objects.append(response)

        from open_facebook import exceptions as open_facebook_exceptions
        for response in response_objects:
            oauth = False
            try:
                FacebookConnection.raise_error(response['error']['type'],
                                               response['error']['message'])
            except open_facebook_exceptions.OAuthException as e:
                oauth = True
            assert oauth, 'response %s didnt raise oauth error' % response
예제 #5
0
    def test_oauth_errors(self):
        expires_response = '''{
          "error": {
            "type": "OAuthException",
            "message": "Session has expired at unix time SOME_TIME. The current unix time is SOME_TIME."
          }
        } '''
        changed_password_response = '''
        {
          "error": {
            "type": "OAuthException",
            "message": "The session has been invalidated because the user has changed the password."
          }
        }
        '''
        deauthorized_response = '''
        {
          "error": {
            "type": "OAuthException",
            "message": "Error validating access token: USER_ID has not authorized application APP_ID"
          }
        }
        '''
        loggedout_response = '''
        {
          "error": {
            "type": "OAuthException",
            "message": "Error validating access token: The session is invalid because the user logged out."
           }
        }
        '''
        responses = [expires_response, changed_password_response,
                     deauthorized_response, loggedout_response]
        response_objects = []
        for response_string in responses:
            response = json.loads(response_string)

            response_objects.append(response)

        from open_facebook import exceptions as open_facebook_exceptions
        for response in response_objects:
            oauth = False
            try:
                FacebookConnection.raise_error(response['error']['type'],
                                               response['error']['message'])
            except open_facebook_exceptions.OAuthException, e:
                oauth = True
            assert oauth, 'response %s didnt raise oauth error' % response
예제 #6
0
    def test_non_oauth_errors(self):
        object_open_graph_error = """
        {"error":
            {"message": "(#3502) Object at URL http://www.fashiolista.com/my_style/list/441276/?og=active&utm_campaign=facebook_action_comment&utm_medium=facebook&utm_source=facebook has og:type of 'website'. The property 'list' requires an object of og:type 'fashiolista:list'. ",
            "code": 3502, "type": "OAuthException"
            }
        }
        """
        response = json.loads(object_open_graph_error)

        def test():
            FacebookConnection.raise_error(
                response["error"]["type"], response["error"]["message"], response["error"].get("code")
            )

        self.assertRaises(OpenGraphException, test)
예제 #7
0
    def test_non_oauth_errors(self):
        object_open_graph_error = '''
        {"error":
            {"message": "(#3502) Object at URL http://www.fashiolista.com/my_style/list/441276/?og=active&utm_campaign=facebook_action_comment&utm_medium=facebook&utm_source=facebook has og:type of 'website'. The property 'list' requires an object of og:type 'fashiolista:list'. ",
            "code": 3502, "type": "OAuthException"
            }
        }
        '''
        response = json.loads(object_open_graph_error)

        def test():
            FacebookConnection.raise_error(response['error']['type'],
                                           response['error']['message'],
                                           response['error'].get('code'))

        self.assertRaises(OpenGraphException, test)
 def parse_signed_data(cls, signed_request, secret=None):
     """Parse a ``signed_request`` from Facebook.
     
     Thanks to http://stackoverflow.com/questions/3302946/how-to-base64-url-decode-in-python
     and http://sunilarora.org/parsing-signedrequest-parameter-in-python-bas
     
     :param signed_request: The signed request to be verified.
         This is a string containing two dot-separated parts: signature
         and data. Each part is (PHP-style) base64-encoded.
         Signature is a HMAC-SHA256 signature of the data, using APP_SECRET
         as the key. Data is a JSON-encoded object.
     :param secret: The key to be used to verify signature.
         Defaults to ``FACEBOOK_APP_SECRET`` from settings.
     :returns: The decoded data object if signature is valid, else ``None``.
     """
     from open_facebook.utils import base64_url_decode_php_style
     import hmac
     import hashlib
     
     if not signed_request:
         return
     
     if secret is None:
         secret = facebook_settings.FACEBOOK_APP_SECRET
     
     enc_signature, enc_payload = signed_request.split('.', 1)
     signature, payload = map(base64_url_decode_php_style, (enc_signature, enc_payload))
     data = json.loads(payload)
     algo = data.get('algorithm').upper()
     
     try:
         if cls.verify_signature(enc_payload, secret, signature, algo):
             logger.debug("Received a valid signed request")
             return data
         else:
             logger.error("Invalid signed_data signature!")
             return
     except:
         logger.exception("Something went wrong while verifying the signed_request.")
예제 #9
0
                        response_file = e
                    else:
                        raise
                response = response_file.read().decode("utf8")
                break
            except (urllib2.HTTPError, urllib2.URLError), e:
                logger.warn("facebook encountered a timeout or error %s", unicode(e))
                attempts -= 1
                if not attempts:
                    raise
            finally:
                if response_file:
                    response_file.close()

        try:
            parsed_response = json.loads(response)
            logger.info("facebook send response %s" % parsed_response)
        except Exception, e:
            # using exception because we need to support multiple json libs :S
            parsed_response = QueryDict(response, True)
            logger.info("facebook send response %s" % parsed_response)

        if parsed_response and isinstance(parsed_response, dict):
            # of course we have two different syntaxes
            if parsed_response.get("error"):
                cls.raise_error(parsed_response["error"]["type"], parsed_response["error"]["message"])
            elif parsed_response.get("error_code"):
                cls.raise_error(parsed_response["error_code"], parsed_response["error_msg"])

        return parsed_response
예제 #10
0
 def get_share_dict(self):
     share_dict_string = self.share_dict
     share_dict = json.loads(share_dict_string)
     return share_dict
예제 #11
0
 def get_share_dict(self):
     share_dict_string = self.share_dict
     share_dict = json.loads(share_dict_string)
     return share_dict
예제 #12
0
                response = response_file.read().decode('utf8')
                break
            except (urllib2.HTTPError, urllib2.URLError), e:
                logger.warn('facebook encountered a timeout or error %s',
                            unicode(e))
                attempts -= 1
                if not attempts:
                    raise
            finally:
                if response_file:
                    response_file.close()
                if django_statsd:
                    django_statsd.stop('facebook.%s' % path)

        try:
            parsed_response = json.loads(response)
            logger.info('facebook send response %s' % parsed_response)
        except Exception, e:
            # using exception because we need to support multiple json libs :S
            parsed_response = QueryDict(response, True)
            logger.info('facebook send response %s' % parsed_response)

        if parsed_response and isinstance(parsed_response, dict):
            # of course we have two different syntaxes
            if parsed_response.get('error'):
                cls.raise_error(parsed_response['error']['type'],
                                parsed_response['error']['message'])
            elif parsed_response.get('error_code'):
                cls.raise_error(parsed_response['error_code'],
                                parsed_response['error_msg'])
예제 #13
0
    def _request(cls, url, post_data=None, timeout=REQUEST_TIMEOUT,
                 attempts=REQUEST_ATTEMPTS):
        # change fb__explicitly_shared to fb:explicitly_shared
        if post_data:
            post_data = dict(
                (k.replace('__', ':'), v) for k, v in post_data.items())

        logger.info('requesting url %s with post data %s', url, post_data)
        post_request = (post_data is not None or 'method=post' in url)

        if post_request and facebook_settings.FACEBOOK_READ_ONLY:
            logger.info('running in readonly mode')
            response = dict(id=123456789, setting_read_only=True)
            return response

        # nicely identify ourselves before sending the request
        opener = build_opener()
        opener.addheaders = [('User-agent', 'Open Facebook Python')]

        # get the statsd path to track response times with
        path = urlparse(url).path
        statsd_path = path.replace('.', '_')

        # give it a few shots, connection is buggy at times
        timeout_mp = 0
        while attempts:
            # gradually increase the timeout upon failure
            timeout_mp += 1
            extended_timeout = timeout * timeout_mp
            response_file = None
            encoded_params = encode_params(post_data) if post_data else None
            post_string = (urlencode(encoded_params)
                           if post_data else None)
            try:
                start_statsd('facebook.%s' % statsd_path)

                try:
                    response_file = opener.open(
                        url, post_string, timeout=extended_timeout)
                    response = response_file.read().decode('utf8')
                except (HTTPError,) as e:
                    response_file = e
                    response = response_file.read().decode('utf8')
                    # Facebook sents error codes for many of their flows
                    # we still want the json to allow for proper handling
                    msg_format = 'FB request, error type %s, code %s'
                    logger.warn(msg_format, type(e), getattr(e, 'code', None))
                    # detect if its a server or application error
                    server_error = cls.is_server_error(e, response)
                    if server_error:
                        # trigger a retry
                        raise URLError(
                            'Facebook is down %s' % response)
                break
            except (HTTPError, URLError, ssl.SSLError) as e:
                # These are often temporary errors, so we will retry before
                # failing
                error_format = 'Facebook encountered a timeout (%ss) or error %s'
                logger.warn(error_format, extended_timeout, unicode(e))
                attempts -= 1
                if not attempts:
                    # if we have no more attempts actually raise the error
                    error_instance = facebook_exceptions.convert_unreachable_exception(
                        e)
                    error_msg = 'Facebook request failed after several retries, raising error %s'
                    logger.warn(error_msg, error_instance)
                    raise error_instance
            finally:
                if response_file:
                    response_file.close()
                stop_statsd('facebook.%s' % statsd_path)

        # Faceboook response is either
        # Valid json
        # A string which is a querydict (a=b&c=d...etc)
        # A html page stating FB is having trouble (but that shouldnt reach
        # this part of the code)
        try:
            parsed_response = json.loads(response)
            logger.info('facebook send response %s' % parsed_response)
        except Exception as e:
            # using exception because we need to support multiple json libs :S
            parsed_response = QueryDict(response, True)
            logger.info('facebook send response %s' % parsed_response)

        if parsed_response and isinstance(parsed_response, dict):
            # of course we have two different syntaxes
            if parsed_response.get('error'):
                cls.raise_error(parsed_response['error']['type'],
                                parsed_response['error']['message'],
                                parsed_response['error'].get('code'))
            elif parsed_response.get('error_code'):
                cls.raise_error(parsed_response['error_code'],
                                parsed_response['error_msg'])

        return parsed_response
예제 #14
0
    def _request(cls,
                 url,
                 post_data=None,
                 timeout=REQUEST_TIMEOUT,
                 attempts=REQUEST_ATTEMPTS):
        # change fb__explicitly_shared to fb:explicitly_shared
        if post_data:
            post_data = dict(
                (k.replace('__', ':'), v) for k, v in post_data.items())

        logger.info('requesting url %s with post data %s', url, post_data)
        post_request = (post_data is not None or 'method=post' in url)

        if post_request and facebook_settings.FACEBOOK_READ_ONLY:
            logger.info('running in readonly mode')
            response = dict(id=123456789, setting_read_only=True)
            return response

        # nicely identify ourselves before sending the request
        opener = build_opener()
        opener.addheaders = [('User-agent', 'Open Facebook Python')]

        # get the statsd path to track response times with
        path = urlparse(url).path
        statsd_path = path.replace('.', '_')

        # give it a few shots, connection is buggy at times
        timeout_mp = 0
        while attempts:
            # gradually increase the timeout upon failure
            timeout_mp += 1
            extended_timeout = timeout * timeout_mp
            response_file = None
            encoded_params = encode_params(post_data) if post_data else None
            post_string = (urlencode(encoded_params) if post_data else None)
            try:
                start_statsd('facebook.%s' % statsd_path)

                try:
                    response_file = opener.open(url,
                                                post_string,
                                                timeout=extended_timeout)
                    response = response_file.read().decode('utf8')
                except (HTTPError, ) as e:
                    response_file = e
                    response = response_file.read().decode('utf8')
                    # Facebook sents error codes for many of their flows
                    # we still want the json to allow for proper handling
                    msg_format = 'FB request, error type %s, code %s'
                    logger.warn(msg_format, type(e), getattr(e, 'code', None))
                    # detect if its a server or application error
                    server_error = cls.is_server_error(e, response)
                    if server_error:
                        # trigger a retry
                        raise URLError('Facebook is down %s' % response)
                break
            except (HTTPError, URLError, ssl.SSLError) as e:
                # These are often temporary errors, so we will retry before
                # failing
                error_format = 'Facebook encountered a timeout (%ss) or error %s'
                logger.warn(error_format, extended_timeout, str(e))
                attempts -= 1
                if not attempts:
                    # if we have no more attempts actually raise the error
                    error_instance = facebook_exceptions.convert_unreachable_exception(
                        e)
                    error_msg = 'Facebook request failed after several retries, raising error %s'
                    logger.warn(error_msg, error_instance)
                    raise error_instance
            finally:
                if response_file:
                    response_file.close()
                stop_statsd('facebook.%s' % statsd_path)

        # Faceboook response is either
        # Valid json
        # A string which is a querydict (a=b&c=d...etc)
        # A html page stating FB is having trouble (but that shouldnt reach
        # this part of the code)
        try:
            parsed_response = json.loads(response)
            logger.info('facebook send response %s' % parsed_response)
        except Exception as e:
            # using exception because we need to support multiple json libs :S
            parsed_response = QueryDict(response, True)
            logger.info('facebook send response %s' % parsed_response)

        if parsed_response and isinstance(parsed_response, dict):
            # of course we have two different syntaxes
            if parsed_response.get('error'):
                cls.raise_error(parsed_response['error']['type'],
                                parsed_response['error']['message'],
                                parsed_response['error'].get('code'))
            elif parsed_response.get('error_code'):
                cls.raise_error(parsed_response['error_code'],
                                parsed_response['error_msg'])

        return parsed_response