Commits

Hadrien David  committed 812c227 Merge

Automated merge with ssh://bitbucket.org/Ludia/pyramid_facebook

  • Participants
  • Parent commits 9b12819, df4a4f8

Comments (0)

Files changed (12)

+* add includeme for any sub module to uniform configuration
+* facebook auth policy does not rely anymore on context for authentication.
+* add an ``CanvasRequested`` event triggered when a identified user request
+  canvas.
+
 0.1.127
 -------
 

File pyramid_facebook/__init__.py

 # -*- coding: utf-8 -*-
 import logging
-import pprint
 
-from pyramid.authorization import ACLAuthorizationPolicy
-
-from pyramid_facebook.security import FacebookAuthenticationPolicy
 
 log = logging.getLogger(__name__)
 
 
     ``pyramid_facebook`` setup:
 
-    * ACL Authorization with :class:`~pyramid.authorization.ACLAuthorizationPolicy`
-      using :meth:`config.set_authorization_policy <pyramid.config.Configurator.set_authorization_policy>`
-    * Authentication with :class:`~pyramid_facebook.security.FacebookAuthenticationPolicy`
-      using :meth:`config.set_authentication_policy <pyramid.config.Configurator.set_authentication_policy>`
+    * ACL Authorization with :class:`~pyramid.authorization
+      .ACLAuthorizationPolicy` using :meth:`config.set_authorization_policy
+      <pyramid.config.Configurator.set_authorization_policy>`
+    * Authentication with :class:`~pyramid_facebook.security
+      .FacebookAuthenticationPolicy` using :meth:`config
+      .set_authentication_policy <pyramid.config.Configurator
+      .set_authentication_policy>`
 
     """
     settings = config.registry.settings
-    log.debug('Configuration settings: %s', pprint.pformat(settings))
-
-    config.set_authentication_policy(FacebookAuthenticationPolicy())
-    config.set_authorization_policy(ACLAuthorizationPolicy())
 
     path = '/%s' % settings['facebook.namespace']
 
-    config.include('pyramid_facebook.auth',      route_prefix=path)
-    config.include('pyramid_facebook.canvas',    route_prefix=path)
-    config.include('pyramid_facebook.credits',   route_prefix=path)
+    config.include('pyramid_facebook.lib', route_prefix=path)
+    config.include('pyramid_facebook.security', route_prefix=path)
+    config.include('pyramid_facebook.auth', route_prefix=path)
+    config.include('pyramid_facebook.canvas', route_prefix=path)
+    config.include('pyramid_facebook.credits', route_prefix=path)
     config.include('pyramid_facebook.real_time', route_prefix=path)
     config.commit()

File pyramid_facebook/canvas.py

 import logging
 import urllib
 
+from pyramid.events import ContextFound, subscriber
 from pyramid.response import Response
-from pyramid.view import view_config, view_defaults
-from pyramid.httpexceptions import HTTPForbidden, HTTPFound
+from pyramid.view import view_config
+from pyramid.httpexceptions import HTTPForbidden
 
-from pyramid_facebook.lib import Base, js_redirect_tpl
-from pyramid_facebook.security import ViewCanvas, Authenticate
+from pyramid_facebook.events import CanvasRequested
+from pyramid_facebook.lib import js_redirect_tpl
+from pyramid_facebook.security import ViewCanvas, SignedRequestContext
 
 log = logging.getLogger(__name__)
 
     raise NotImplementedError()
 
 
+@subscriber(
+    ContextFound,
+    matched_route='facebook_canvas',
+    expected_context=SignedRequestContext,
+    )
+def on_canvas(event):
+    event.request.registry.notify(CanvasRequested)

File pyramid_facebook/events.py

 class RefundedOrder(Base):
     "Event sent when a user got refunded for an order."
 
+
 class PlacedItemOrder(Base):
     "Event sent when a user placed an item order."
 
         """:returns: Request body as a dict. Raised an error if body is json
             decoding fails."""
         return self.request.json_body
+
+
+class UserSignedRequestParsed(Base):
+    """Event sent each time a signed request with user information is
+    successfully parsed.
+    """
+
+
+class CanvasRequested(Base):
+    """Event sent when an identified user requests the application canvas.
+    """

File pyramid_facebook/lib.py

 </html>"""
 
 
+def includeme(config):
+    config.add_subscriber_predicate('matched_route',
+                                    MatchedRouteEventPredicate)
+    config.add_subscriber_predicate('expected_context', ContextEventPredicate)
+
+
 class Base(object):
     "Base class for views and events"
     def __init__(self, context, request):
     :param secret_key: Facebook application' secret key.
     :param data: a dictionary of data to sign.
     :return: Signed request as defined by `Facebook documentation
-             <http://developers.facebook.com/docs/authentication/signed_request/>`_
+             <http://developers.facebook.com/docs/authentication/
+             signed_request/>`_
 
     """
     data = data.copy()
 
 def request_params_predicate(*required_param_keys, **required_params):
     """Custom predicates to check if required parameter are in request
-    parameters. Read :ref:`custom route predicates <pyramid:custom_route_predicates>`
+    parameters. Read :ref:`custom route predicates <pyramid:
+    custom_route_predicates>`
     for more info::
 
         # /example?param1&param2=321
             )
     """
     required = set(required_param_keys)
+
     def predicate(info, request):
         if not required.issubset(set(request.params.keys())):
             return False
     """Custom predicate which checks if a parameter is present with possible
     values being one in list values.
 
-    :param params: A dictionary structured as `dict(param_name=(value1, value2))`
+    :param params: A dictionary structured as
+                   `dict(param_name=(value1, value2))`
     """
     names = set(params.keys())
+
     def predicate(info, request):
         if not names.issubset(request.params):
             return False
-        if [request.params[n] for n in names if request.params[n] not in params[n]]:
+        if [request.params[n]
+            for n in names if request.params[n] not in params[n]]:
             return False
         return True
     return predicate
 
 
 def headers_predicate(*header_names, **headers):
-    """Custom predicate which check that `header_names` and  `headers` name/value
-    pairs are in `request.headers`.
+    """Custom predicate which check that `header_names` and  `headers`
+    name/value pairs are in `request.headers`.
     """
     def predicate(info, request):
         if [_ for _ in header_names if _ not in request.headers]:
             return False
-        if [(k, v) for k, v in headers.iteritems() if k not in request.headers or
-            request.headers[k] != v]:
+        if [(k, v) for k, v in headers.iteritems()
+            if k not in request.headers or request.headers[k] != v]:
             return False
         return True
     return predicate
+
+
+class MatchedRouteEventPredicate(object):
+
+    def __init__(self, route_to_match, config):
+        self.route_to_match = route_to_match
+
+    def text(self):
+        return 'route_to_match = %s' % self.route_to_match
+
+    phash = text
+
+    def __call__(self, event):
+        return event.request.matched_route.name == self.route_to_match
+
+
+class ContextEventPredicate(object):
+
+    def __init__(self, expected_context_cls, config):
+        self.expected_context_cls = expected_context_cls
+
+    def text(self):
+        return 'expected_context_cls = %r' % self.expected_context_cls
+
+    phash = text
+
+    def __call__(self, event):
+        return isinstance(event.request.context, self.expected_context_cls)

File pyramid_facebook/security.py

 from facepy import FacepyError, GraphAPI, SignedRequest
 from facepy.exceptions import SignedRequestError
 
+from pyramid.authorization import ACLAuthorizationPolicy
 from pyramid.authentication import CallbackAuthenticationPolicy
 from pyramid.decorator import reify
 from pyramid.security import Allow
 
+from pyramid_facebook.events import UserSignedRequestParsed
+
 log = logging.getLogger(__name__)
 
 ViewCanvas = 'view_canvas'
 
 FacebookUser = 'facebook-user'
 RegisteredUser = 'registered-user'
-AdminUser = 'admin_user'
-XHubSigned = 'x-hub-signed' # real time updates https://developers.facebook.com/docs/reference/api/realtime/
+AdminUser = 'admin-user'
+# real time updates https://developers.facebook.com/docs/reference/api/realtime
+XHubSigned = 'x-hub-signed'
+
+
+def includeme(config):
+    config.set_authentication_policy(FacebookAuthenticationPolicy())
+    config.set_authorization_policy(ACLAuthorizationPolicy())
 
 
 class SignedRequestContext(object):
     def __init__(self, request):
         self.request = request
 
-    def unauthenticated_userid(self):
-        try:
-            return int(self.facebook_data["user_id"])
-        except KeyError:
-            # user_id not in facebook_data => user has not authorized app
-            log.debug('User has not authorized app.')
-        except ValueError:
-            log.warn('Invalid user id %r', self.facebook_data["user_id"])
-        except TypeError:
-            # in case signature is malformated. fb_data inexists
-            # no need to log as it is already done in __init__
-            pass
-        return None
-
-    def effective_principals(self):
-        if self.unauthenticated_userid():
-            return [FacebookUser]
-        return []
-
-    @reify
+    @property
     def facebook_data(self):
         """Contains facebook data provided in ``signed_request`` parameter
-        decrypted with :meth:`SignedRequest.parse <facepy.SignedRequest.parse>`.
+        decrypted with :meth:`SignedRequest.parse <facepy.SignedRequest.parse>`
         """
-        if self.request.params.get('signed_request'):
-            try:
-                return SignedRequest.parse(
-                    self.request.params['signed_request'],
-                    self.request.registry.settings['facebook.secret_key'],
-                    )
-            except SignedRequestError as e:
-                log.warn(
-                    '%r with signature: %s',
-                    e,
-                    self.request.params['signed_request']
-                    )
-        return None
+        try:
+            return self._facebook_data
+        except AttributeError:
+            return None
+
+    @facebook_data.setter
+    def facebook_data(self, value):
+        self._facebook_data = value
 
     @reify
     def user(self):
     @reify
     def order_details(self):
         """Order details received in `facebook credits callback for payment
-        status updates <http://developers.facebook.com/docs/credits/callback/#payments_status_update>`_."""
+        status updates <http://developers.facebook.com/docs/credits/callback/
+        #payments_status_update>`_."""
         return json.loads(
             self.facebook_data['credits']['order_details']
             )
     def earned_currency_data(self):
         """Modified field received in `facebook credits callback for payment
         status update for earned app currency
-        <http://developers.facebook.com/docs/credits/callback/#payments_status_update_earn_app_currency>`_."""
+        <http://developers.facebook.com/docs/credits/callback/
+        #payments_status_update_earn_app_currency>`_."""
         data = self.item['data']
         if data:
             try:
 
 
 class AdminContext(AccessTokenContext):
-    """Context which defines principals as facebook application id list that user
-    administrates."""
+    """Context which defines principals as facebook application id list that
+    user administrates.
+    """
 
     def __init__(self, request):
         super(AdminContext, self).__init__(request)
         self.principals = None
         self.__acl__ = (
-            (Allow, request.registry.settings['facebook.app_id'], UpdateSubscription),
+            (Allow, request.registry.settings['facebook.app_id'],
+             UpdateSubscription),
             )
 
     def effective_principals(self):
             self.principals = super(AdminContext, self).effective_principals()
             # check what apps user owns.
             try:
-                accounts = GraphAPI(self.access_token).get('me/accounts', retry=0)
+                accounts = GraphAPI(self.access_token).get('me/accounts',
+                                                           retry=0)
                 self.principals.extend([a['id'] for a in accounts['data']])
             except FacepyError as e:
                 log.warn('Get accounts failed: %r', e)
 
 class FacebookAuthenticationPolicy(CallbackAuthenticationPolicy):
 
+    unauthenticated_methods = None
+    effective_principals_methods = None
+
+    def __init__(self, *args, **kwargs):
+        super(FacebookAuthenticationPolicy, self).__init__(*args, **kwargs)
+
+        self.unauthenticated_methods = {
+            SignedRequestContext: self._parse_signed_request,
+            FacebookCreditsContext: self._parse_signed_request,
+            AccessTokenContext: self._validate_access_token,
+
+            RealTimeNotificationContext: lambda req: None,
+            }
+
+        self.effective_principals_methods = {
+            SignedRequestContext: self._get_principals_from_signed_request,
+            FacebookCreditsContext: self._get_principals_from_signed_request,
+            AccessTokenContext: self._get_principals_from_access_token,
+            RealTimeNotificationContext: self._get_principals_from_signature,
+            }
+
     def unauthenticated_userid(self, request):
         try:
-            return request.context.unauthenticated_userid()
-        except AttributeError:
+            method = self.unauthenticated_methods[request.context.__class__]
+        except KeyError:
             return None
+        else:
+            return method(request)
 
     def effective_principals(self, request):
-        p = super(FacebookAuthenticationPolicy, self).effective_principals(request)
+        cls = FacebookAuthenticationPolicy
+        p = super(cls, self).effective_principals(request)
+        context = request.context
+        method = self.effective_principals_methods[context.__class__]
         try:
-            p.extend(request.context.effective_principals())
+            p.extend(method(request))
         except AttributeError:
-            pass
+            log.debug('AttributeError', exc_info=True)
         return p
 
     def remember(self, request, principal, **kwargs):
     def callback(self, user_id, request):
         # by default, having a user id means that user is registered
         return [RegisteredUser]
+
+    def _parse_signed_request(self, request):
+        context = request.context
+        if 'signed_request' not in request.params:
+            return None
+        context.facebook_data = None
+        try:
+            context.facebook_data = SignedRequest.parse(
+                request.params['signed_request'],
+                request.registry.settings['facebook.secret_key'],
+                )
+        except SignedRequestError:
+            log.warn(
+                'SignedRequestError with signature: %s',
+                request.params['signed_request'],
+                exc_info=True
+                )
+            return None
+        try:
+            user_id = int(context.facebook_data["user_id"])
+        except KeyError:
+            # user_id not in facebook_data => user has not authorized app
+            log.debug('User has not authorized app.')
+        except ValueError:
+            log.warn('Invalid user id %r', context.facebook_data["user_id"])
+        else:
+            request.registry.notify(
+                UserSignedRequestParsed(request.context, request)
+                )
+            return user_id
+        return None
+
+    def _validate_access_token(self, request):
+        return request.context.unauthenticated_userid()
+
+    def _get_principals_from_signed_request(self, request):
+        try:
+            if request.context.facebook_data['user_id']:
+                return [FacebookUser]
+        except TypeError:
+            pass
+        return []
+
+    def _get_principals_from_access_token(self, request):
+        return request.context.effective_principals()
+
+    def _get_principals_from_signature(self, request):
+        return request.context.effective_principals()

File pyramid_facebook/tests/functional/__init__.py

     @property
     def permissive_app(self):
         if TestView._permissive_app is None:
-            from pyramid_facebook import auth, canvas, credits, real_time
+            from pyramid_facebook import auth, canvas, credits, real_time, lib
             from pyramid_facebook.tests import conf
             namespace = conf['facebook.namespace']
             config = Configurator(settings=conf)
+            config.include(lib)
             config.include(auth, route_prefix=namespace)
             config.include(canvas, route_prefix=namespace)
             config.include(credits, route_prefix=namespace)

File pyramid_facebook/tests/functional/test_credits.py

             'method': 'payments_get_items'
             }
         params.update(credits_data)
-
         result = app.post(
             '/%s/credits' % conf['facebook.namespace'],
             params,

File pyramid_facebook/tests/unittests/test_pyramid_facebook.py

 
 import mock
 
+
 class TestPyramidFacebook(unittest.TestCase):
 
-    @mock.patch('pyramid_facebook.FacebookAuthenticationPolicy')
-    @mock.patch('pyramid_facebook.ACLAuthorizationPolicy')
-    def test_includeme(self, m_acl_policy, m_fb_policy):
+    def test_includeme(self):
         settings = {
             'facebook.namespace': 'namespace',
             'facebook.secret_key': 'secret_key'
         from pyramid_facebook import includeme
         self.assertIsNone(includeme(config))
 
-        # now we check if everything went as expected:
-        m_fb_policy.assert_called_once_with()
-        m_acl_policy.assert_called_once_with()
-
-        config.set_authentication_policy.assert_called_once_with(
-            m_fb_policy.return_value
-            )
-        config.set_authorization_policy.assert_called_once_with(
-            m_acl_policy.return_value
-            )
-
-        self.assertEqual(4, config.include.call_count)
+        self.assertEqual(6, config.include.call_count)
         prefix = '/namespace'
 
         self.assertEqual(
-            mock.call('pyramid_facebook.auth', route_prefix=prefix),
+            mock.call('pyramid_facebook.lib', route_prefix=prefix),
             config.include.call_args_list[0]
             )
 
         self.assertEqual(
+            mock.call('pyramid_facebook.security', route_prefix=prefix),
+            config.include.call_args_list[1]
+            )
+        self.assertEqual(
+            mock.call('pyramid_facebook.auth', route_prefix=prefix),
+            config.include.call_args_list[2]
+            )
+
+        self.assertEqual(
             mock.call('pyramid_facebook.canvas', route_prefix=prefix),
-            config.include.call_args_list[1]
+            config.include.call_args_list[3]
             )
 
         self.assertEqual(
             mock.call('pyramid_facebook.credits', route_prefix=prefix),
-            config.include.call_args_list[2]
+            config.include.call_args_list[4]
             )
 
         self.assertEqual(
             mock.call('pyramid_facebook.real_time', route_prefix=prefix),
-            config.include.call_args_list[3]
+            config.include.call_args_list[5]
             )
 
-    @mock.patch('pyramid_facebook.FacebookAuthenticationPolicy')
-    @mock.patch('pyramid_facebook.ACLAuthorizationPolicy')
-    def test_includeme_exception(self, m_acl_policy, m_fb_policy):
+    def test_includeme_exception(self):
         from pyramid_facebook import includeme
         bad_settings = {}
 

File pyramid_facebook/tests/unittests/test_security.py

 
 from facepy.exceptions import OAuthError
 
+from pyramid import testing
+from pyramid.config import Configurator
 from pyramid.security import Allow
 
+from pyramid_facebook.security import SignedRequestContext
 from pyramid_facebook.tests import conf
 
 TEST_USER = 123
             }
         )
 
+
 def _get_request(signed_request=None):
     request = mock.MagicMock()
-    signed_request = signed_request if signed_request else _get_signed_request()
+    signed_request = (signed_request
+                      if signed_request else _get_signed_request())
     request.params = {'signed_request': signed_request}
     request.registry.settings = conf.copy()
     return request
 
 
+class TestIncludeme(unittest.TestCase):
+
+    @mock.patch('pyramid_facebook.security.ACLAuthorizationPolicy')
+    @mock.patch('pyramid_facebook.security.FacebookAuthenticationPolicy')
+    def test_includeme(self, FacebookAuthenticationPolicy,
+                       ACLAuthorizationPolicy):
+        from pyramid_facebook.security import includeme
+
+        config = mock.MagicMock(spec=Configurator)
+
+        includeme(config)
+
+        config.set_authentication_policy.assert_called_once_with(
+            FacebookAuthenticationPolicy.return_value
+            )
+        config.set_authorization_policy.assert_called_once_with(
+            ACLAuthorizationPolicy.return_value
+            )
+        FacebookAuthenticationPolicy.assert_called_once_with()
+        ACLAuthorizationPolicy.assert_called_once_with()
+
+
 class TestFacebookCreditsContext(unittest.TestCase):
 
     @mock.patch('pyramid_facebook.security.json')
         ctx = FacebookCreditsContext(_get_request())
         ctx.facebook_data = {
             'credits': {
-                'order_details':json.dumps({
+                'order_details': json.dumps({
                     'items': [
                         {'data': '{"modified": "earned_currency_data"}'}
                         ]
         # with Invalid json data:
         ctx.facebook_data = {
             'credits': {
-                'order_details':json.dumps({
+                'order_details': json.dumps({
                     'items': [
                         {'data': '{INVALID JSON "}'}
                         ]
         ctx = FacebookCreditsContext(_get_request())
         ctx.facebook_data = mock.MagicMock()
 
-        self.assertEqual(ctx.facebook_data["credits"]["order_info"], ctx.order_info)
+        self.assertEqual(ctx.facebook_data["credits"]["order_info"],
+                         ctx.order_info)
 
     def test_item(self):
         from pyramid_facebook.security import FacebookCreditsContext
         ctx = FacebookCreditsContext(_get_request())
         ctx.facebook_data = mock.MagicMock()
-        ctx.facebook_data.__getitem__.return_value.__getitem__.return_value = json.dumps(
-            {'items': [{'title': 'an item'}]}
-            )
+        t = json.dumps({'items': [{'title': 'an item'}]})
+        ctx.facebook_data.__getitem__.return_value.__getitem__.return_value = t
 
         self.assertEqual(
             {'title': 'an item'},
 class TestSignedRequestContext(unittest.TestCase):
 
     def test_init(self):
-        from pyramid_facebook.security import(
+        from pyramid_facebook.security import (
             SignedRequestContext,
             ViewCanvas,
             Authenticate,
             RegisteredUser,
             )
 
-        request = _get_request()
-
-        ctx = SignedRequestContext(request)
-
         self.assertEqual(
             [
                 (Allow, FacebookUser, Authenticate),
                 (Allow, RegisteredUser, ViewCanvas),
                 ],
-            ctx.__acl__
-            )
-        self.assertEqual(
-            {u'user_id': 123, u'algorithm': u'HMAC-SHA256', u'user': {u'country': u'ca'}},
-            ctx.facebook_data
-            )
-        self.assertEqual({u'country': u'ca'}, ctx.user)
-        self.assertEqual(u'ca', ctx.user_country)
-
-    def test_init_malformated_signed_request(self):
-        from pyramid_facebook.security import SignedRequestContext
-        request = _get_request(signed_request='malformated')
-
-        ctx = SignedRequestContext(request)
-        self.assertIsNone(ctx.unauthenticated_userid())
-
-    def test_init_no_user_id(self):
-        from pyramid_facebook.lib import encrypt_signed_request
-        from pyramid_facebook.security import SignedRequestContext
-        request = _get_request(
-            encrypt_signed_request(
-                conf['facebook.secret_key'],
-                {'no_user_id': '-_-'})
+            SignedRequestContext.__acl__
             )
 
-        ctx = SignedRequestContext(request)
-        self.assertIsNone(ctx.unauthenticated_userid())
+        SignedRequestContext(None)
 
-        self.assertEqual([], ctx.effective_principals())
+    def test_facebook_data_not_set(self):
+        from pyramid_facebook.security import SignedRequestContext
+        sr = SignedRequestContext(None)
+        self.assertEqual(None, sr.facebook_data)
 
-    def test_user_id_attribute_error(self):
+    def test_facebook_data_property(self):
         from pyramid_facebook.security import SignedRequestContext
-        request = mock.MagicMock()
-        request.params.get.return_value = None
-
-        context = SignedRequestContext(request) # to raise AttributeError
-
-        self.assertIsNone(context.unauthenticated_userid())
-
-    def test_extract_user_id__error(self):
-        from pyramid_facebook.security import SignedRequestContext
-
-        request = _get_request(_get_signed_request('not a numeric value'))
-
-        ctx = SignedRequestContext(request)
-
-        self.assertIsNone(ctx.unauthenticated_userid())
+        sr = SignedRequestContext(None)
+        sr.facebook_data = 'something'
+        self.assertEqual('something', sr.facebook_data)
 
     def test_repr(self):
         from pyramid_facebook.security import SignedRequestContext
 
         ctx = SignedRequestContext(_get_request())
+        ctx.facebook_data = 'something'
 
         self.assertEqual(
-            "<SignedRequestContext facebook_data={u'user_id': 123, u'user': {u'country': u'ca'}, u'algorithm': u'HMAC-SHA256'}>",
+            "<SignedRequestContext facebook_data='something'>",
             ctx.__repr__()
             )
 
+    def test_user(self):
+        from pyramid_facebook.security import SignedRequestContext
+        sr = SignedRequestContext(None)
+        sr.facebook_data = {'user': 12345}
+        self.assertEqual(12345, sr.user)
+
+    def test_country(self):
+        from pyramid_facebook.security import SignedRequestContext
+        sr = SignedRequestContext(None)
+        sr.facebook_data = {'user': {'country': 'FR'}}
+        self.assertEqual('FR', sr.user_country)
+
 
 class TestFacebookAuthenticationPolicy(unittest.TestCase):
 
-    def test_unauthenticated_userid(self):
+    def setUp(self):
+        self.config = testing.setUp()
+
+    def tearDown(self):
+        testing.tearDown()
+
+    def _get_request(self, context_cls=SignedRequestContext):
+        request = testing.DummyRequest()
+        request.params = {'signed_request': mock.MagicMock(spec=str)}
+        request.registry.settings = {
+            'facebook.secret_key': 'a secret',
+            }
+        request.context = mock.MagicMock(spec=SignedRequestContext)
+        return request
+
+    @mock.patch('pyramid_facebook.security.SignedRequest')
+    def test_parse_signed_request(self, SignedRequest):
         from pyramid_facebook.security import FacebookAuthenticationPolicy
-        request = mock.Mock()
+        request = self._get_request()
+        SignedRequest.parse.return_value = {u'user_id': 123}
+        fap = FacebookAuthenticationPolicy()
+        result = fap._parse_signed_request(request)
 
-        fap = FacebookAuthenticationPolicy()
-        self.assertEqual(
-            request.context.unauthenticated_userid(),
-            fap.unauthenticated_userid(request)
+        SignedRequest.parse.assert_called_once_with(
+            request.params['signed_request'],
+            request.registry.settings['facebook.secret_key'],
             )
+        self.assertEqual(123, result)
 
     def test_unauthenticated_userid_attribute_error(self):
         from pyramid_facebook.security import FacebookAuthenticationPolicy
-        request = mock.Mock()
-        request.context.unauthenticated_userid.side_effect = AttributeError('no!')
+        request = self._get_request()
 
         fap = FacebookAuthenticationPolicy()
         self.assertIsNone(fap.unauthenticated_userid(request))
 
-    def test_effective_principals(self):
-        from pyramid_facebook.security import FacebookAuthenticationPolicy, RegisteredUser
-        request = mock.Mock()
-        request.context.unauthenticated_userid.return_value = 123
-        request.context.effective_principals.return_value = ['user']
+    @mock.patch('pyramid_facebook.security.SignedRequest')
+    def test_effective_principals(self, SignedRequest):
+        from pyramid_facebook.security import (
+            FacebookAuthenticationPolicy,
+            RegisteredUser,
+            FacebookUser,
+            )
+        request = self._get_request()
+        SignedRequest.parse.return_value = {u'user_id': 123}
         fap = FacebookAuthenticationPolicy()
         self.assertEqual(
-            ['system.Everyone', 'system.Authenticated', 123, RegisteredUser, 'user'],
+            ['system.Everyone', 'system.Authenticated', 123, RegisteredUser,
+             FacebookUser],
             fap.effective_principals(request)
             )
 
     def test_effective_principals_attribute_error(self):
         from pyramid_facebook.security import FacebookAuthenticationPolicy
-        request = mock.Mock()
-        request.context.unauthenticated_userid.side_effect = AttributeError('no!')
-        request.context.effective_principals.side_effect = AttributeError('no!')
+        request = self._get_request()
         fap = FacebookAuthenticationPolicy()
-        self.assertEqual(['system.Everyone'], fap.effective_principals(request))
-
+        self.assertEqual(['system.Everyone'],
+                         fap.effective_principals(request))
 
     def test_remember(self):
         from pyramid_facebook.security import FacebookAuthenticationPolicy
+        request = self._get_request()
 
         fap = FacebookAuthenticationPolicy()
 
-        request = mock.MagicMock()
         principal = "principal"
         kwargs = {"a": "a_val", "b": "b_val"}
 
         # mecanism, user is authenticated on each request
         self.assertListEqual([], fap.remember(request, principal, **kwargs))
 
-
     def test_forget(self):
         from pyramid_facebook.security import FacebookAuthenticationPolicy
+        request = self._get_request()
 
         fap = FacebookAuthenticationPolicy()
 
-        request = mock.MagicMock()
-
         # FacebookAuthenticationPolicy does not support a remember/forget
         # mecanism, user is authenticated on each request
         self.assertListEqual([], fap.forget(request))
         request = mock.Mock()
         atc = AccessTokenContext(request)
 
-        result = atc.user_dict
+        atc.user_dict
 
         self.assertEqual(12345, atc.unauthenticated_userid())
         self.assertEqual([FacebookUser], atc.effective_principals())
         m_graph_api.assert_called_once_with(request.params.get.return_value)
         m_graph_api.return_value.get.assert_called_once_with('me', retry=0)
 
-
     @mock.patch('pyramid_facebook.security.GraphAPI')
     def test_user_dict_facepy_error(self, m_graph_api):
         from pyramid_facebook.security import AccessTokenContext
-        m_graph_api.return_value.get.side_effect = OAuthError(code=190, message='OAuth Error')
+        oe = OAuthError(code=190, message='OAuth Error')
+        m_graph_api.return_value.get.side_effect = oe
         request = mock.Mock()
 
         atc = AccessTokenContext(request)
     @mock.patch('pyramid_facebook.security.GraphAPI')
     def test_effective_principals(self, m_graph_api):
         from pyramid_facebook.security import AdminContext, UpdateSubscription
-        m_graph_api.return_value.get.return_value = dict(data=[dict(id=12345), dict(id=54321)])
+        rv = dict(data=[dict(id=12345), dict(id=54321)])
+        m_graph_api.return_value.get.return_value = rv
         request = mock.MagicMock()
 
         ac = AdminContext(request)
             )
 
         self.assertEqual(
-            ((Allow, request.registry.settings.__getitem__.return_value, UpdateSubscription),),
+            ((Allow, request.registry.settings.__getitem__.return_value,
+              UpdateSubscription),),
             ac.__acl__
             )
 
         self.assertEqual([12345, 54321], result)
 
-
     @mock.patch('pyramid_facebook.security.GraphAPI')
     def test_effective_principals_facepy_error(self, m_graph_api):
         from pyramid_facebook.security import AdminContext
-        m_graph_api.return_value.get.side_effect = OAuthError(code=190, message='OAuth Error')
+        oe = OAuthError(code=190, message='OAuth Error')
+        m_graph_api.return_value.get.side_effect = oe
         request = mock.MagicMock()
 
         ac = AdminContext(request)
         request = mock.MagicMock()
         request.headers = {'X-Hub-Signature': 'sha1=signature'}
 
-        m_hmac.new.return_value.hexdigest.return_value = 'not the same signature'
+        m_hmac.new.return_value.hexdigest.return_value = 'not same signature'
 
         rtnc = RealTimeNotificationContext(request)
         result = rtnc.effective_principals()
 description-file = README.rst
 author = Hadrien David
 author_email = hadrien@ectobal.com
-requires-dist = 
-	pyramid
+requires-dist =
+	pyramid>=1.4
 	pyramid_debugtoolbar
 	requests
 	sphinx
 [nosetests]
 match = ^test
 nocapture = 1
-cover-package = pyramid_facebook
+cover-package = pyramid_rest
+cover-inclusive = 1
+with-xcoverage = 1
+with-xunit = 1
 cover-erase = 1
 verbosity = 3
 with-id = 1
-with-xcoverage = 1
-with-xunit = 1
-
+with-yanc = 1
 
 setup_requires = [
     'd2to1',
+    ]
+
+tests_require = [
     'nose',
     'nosexcover',
     'coverage',
     'mock',
     'webtest',
+    'yanc',
     ]
 
 setuptools.setup(
     setup_requires=setup_requires,
+    tests_require=tests_require,
     d2to1=True,
-    test_suite = "nose.collector",
+    test_suite="nose.collector",
     package_data={'pyramid_facebook': [
         ]},
     paster_plugins=['pyramid'],