Ejemplo n.º 1
0
    def send(self, graph=None, shared_explicitly=False):
        result = None
        # update the last attempt
        self.last_attempt = datetime.now()
        self.save()

        # see if the graph is enabled
        profile = try_get_profile(self.user)
        user_or_profile = get_instance_for_attribute(
            self.user, profile, 'access_token')
        graph = graph or user_or_profile.get_offline_graph()
        user_enabled = shared_explicitly or \
            (user_or_profile.facebook_open_graph and self.facebook_user_id)
        # start sharing
        if graph and user_enabled:
            graph_location = '%s/%s' % (
                self.facebook_user_id, self.action_domain)
            share_dict = self.get_share_dict()
            from open_facebook.exceptions import OpenFacebookException
            try:
                result = graph.set(graph_location, **share_dict)
                share_id = result.get('id')
                if not share_id:
                    error_message = 'No id in Facebook response, found %s for url %s with data %s' % (
                        result, graph_location, share_dict)
                    logger.error(error_message)
                    raise OpenFacebookException(error_message)
                self.share_id = share_id
                self.error_message = None
                self.completed_at = datetime.now()
                self.save()
            except OpenFacebookException as e:
                logger.warn(
                    'Open graph share failed, writing message %s' % str(e))
                self.error_message = repr(e)
                self.save()
                # maybe we need a new access token
                new_token_required = self.exception_requires_new_token(
                    e, graph)
                # verify that the token didnt change in the mean time
                user_or_profile = user_or_profile.__class__.objects.get(
                    id=user_or_profile.id)
                token_changed = graph.access_token != user_or_profile.access_token
                logger.info('new token required is %s and token_changed is %s',
                            new_token_required, token_changed)
                if new_token_required and not token_changed:
                    logger.info(
                        'a new token is required, setting the flag on the user or profile')
                    # time to ask the user for a new token
                    update_user_attributes(self.user, profile, dict(
                        new_token_required=True), save=True)

        elif not graph:
            self.error_message = 'no graph available'
            self.save()
        elif not user_enabled:
            self.error_message = 'user not enabled'
            self.save()

        return result
Ejemplo n.º 2
0
    def test_follow_og_share_error(self):
        from django_facebook.models import OpenGraphShare
        user_url = 'http://www.fashiolista.com/style/neni/'
        kwargs = dict(item=user_url)
        user = get_user_model().objects.all()[:1][0]
        profile = try_get_profile(user)
        user_or_profile = get_instance_for_attribute(
            user, profile, 'facebook_open_graph')
        user_or_profile.facebook_open_graph = True
        user_or_profile.save()

        from django.contrib.contenttypes.models import ContentType
        some_content_type = ContentType.objects.all()[:1][0]
        share = OpenGraphShare.objects.create(
            user_id=user.id,
            facebook_user_id=13123123,
            action_domain='fashiolista:follow',
            content_type=some_content_type,
            object_id=user.id,
        )
        share.set_share_dict(kwargs)
        share.save()
        from open_facebook.exceptions import FacebookUnreachable
        with mock.patch('open_facebook.api.OpenFacebook') as mocked:
            instance = mocked.return_value
            instance.set = Mock(side_effect=FacebookUnreachable('broken'))
            share.send(graph=instance)
            self.assertEqual(share.error_message, 'broken')
            self.assertFalse(share.completed_at)
Ejemplo n.º 3
0
 def profile_or_self(self):
     user_or_profile_model = get_model_for_attribute('facebook_id')
     user_model = get_user_model()
     if user_or_profile_model == user_model:
         return self
     else:
         return try_get_profile(self)
Ejemplo n.º 4
0
    def profile_authenticate(self, facebook_id=None, facebook_email=None):
        '''
        Authenticate the facebook user by id OR facebook_email
        We filter using an OR to allow existing members to connect with
        their facebook ID using email.

        :param facebook_id:
            Optional string representing the facebook id

        :param facebook_email:
            Optional string with the facebook email

        :return: The signed in :class:`User`.
        '''
        logger.info("PA01 Inside profile authentication")
        if facebook_id or facebook_email:
            profile_class = get_profile_model()
            logger.info("PA02 Profile class %s" % profile_class)
            profile_query = profile_class.objects.all().order_by('user')
            profile_query = profile_query.select_related('user')
            logger.info("PA03 Profile query %s" % profile_query)
            profile = None

            # filter on email or facebook id, two queries for better
            # queryplan with large data sets
            if facebook_id:
                profiles = profile_query.filter(facebook_id=facebook_id)[:1]
                logger.info("PA04 Profiles from facebook_id %s" % profiles)
                profile = profiles[0] if profiles else None
                logger.info("PA05 Profile %s" % profile)
            if profile is None and facebook_email:
                try:
                    profiles = profile_query.filter(
                        user__email__iexact=facebook_email)[:1]
                    logger.info("PA06 Profiles from email %s" % profiles)
                    profile = profiles[0] if profiles else None
                    logger.info("PA07 Profile %s" % profile)
                except DatabaseError:
                    logger.info("PA08 Database error")
                    try:
                        user = get_user_model(
                        ).objects.get(email=facebook_email)
                        logger.info("PA09 User from model %s" % user)
                    except get_user_model().DoesNotExist:
                        logger.info("PA10 No model")
                        user = None
                    profile = try_get_profile(user) if user else None
                    logger.info("PA11 Profile %s" % profile)

            if profile:
                # populate the profile cache while we're getting it anyway
                user = profile.user
                logger.info("PA07 User from profile %s" % user)
                user._profile = profile
                if facebook_settings.FACEBOOK_FORCE_PROFILE_UPDATE_ON_LOGIN:
                    logger.info("UA10 Require user update")
                    user.fb_update_required = True
                return user
Ejemplo n.º 5
0
 def remove(self, graph=None):
     if not self.share_id:
         raise ValueError('Can only delete shares which have an id')
     # see if the graph is enabled
     profile = try_get_profile(self.user)
     graph = graph or profile.get_offline_graph()
     response = None
     if graph:
         response = graph.delete(self.share_id)
         self.removed_at = datetime.now()
         self.save()
     return response
Ejemplo n.º 6
0
def _add_current_user_id(graph, user):
    '''
    set the current user id, convenient if you want to make sure you
    fb session and user belong together
    '''
    if graph:
        graph.current_user_id = None

        if user.is_authenticated():
            profile = try_get_profile(user)
            facebook_id = get_user_attribute(user, profile, 'facebook_id')
            if facebook_id:
                graph.current_user_id = facebook_id
Ejemplo n.º 7
0
 def test_gender_matching(self):
     request = RequestMock().get('/')
     request.session = {}
     request.user = AnonymousUser()
     graph = get_persistent_graph(request, access_token='paul')
     converter = FacebookUserConverter(graph)
     base_data = converter.facebook_profile_data()
     self.assertEqual(base_data['gender'], 'male')
     data = converter.facebook_registration_data()
     self.assertEqual(data['gender'], 'm')
     action, user = connect_user(self.request, facebook_graph=graph)
     profile = try_get_profile(user)
     gender = get_user_attribute(user, profile, 'gender')
     self.assertEqual(gender, 'm')
Ejemplo n.º 8
0
    def test_follow_og_share_error(self):
        user_url = 'http://www.fashiolista.com/style/neni/'
        kwargs = dict(item=user_url)
        user = get_user_model().objects.all()[:1][0]
        profile = try_get_profile(user)
        user_or_profile = get_instance_for_attribute(
            user, profile, 'facebook_open_graph')
        user_or_profile.facebook_open_graph = True
        user_or_profile.save()

        some_content_type = ContentType.objects.all()[:1][0]
        share = OpenGraphShare.objects.create(
            user_id=user.id,
            facebook_user_id=13123123,
            action_domain='fashiolista:follow',
            content_type=some_content_type,
            object_id=user.id,
        )
        share.set_share_dict(kwargs)
        share.save()
        update_user_attributes(user, profile, dict(new_token_required=False))
        from open_facebook.exceptions import FacebookUnreachable, OAuthException
        with mock.patch('open_facebook.api.OpenFacebook') as mocked:
            instance = mocked.return_value
            instance.set = Mock(side_effect=FacebookUnreachable('broken'))
            share.send(graph=instance)
            self.assertEqual(share.error_message, 'broken')
            self.assertFalse(share.completed_at)
            user = get_user_model().objects.get(id=user.id)
            if profile:
                profile = get_profile_model().objects.get(id=profile.id)
            new_token_required = get_user_attribute(
                user, profile, 'new_token_required')
            self.assertEqual(new_token_required, False)

        # now try with an oauth exception
        return
        # Can't figure out how to test this
        with mock.patch('open_facebook.api.OpenFacebook') as mocked:
            instance = mocked.return_value
            instance.set = Mock(side_effect=OAuthException('permissions'))
            share.send(graph=instance)
            self.assertEqual(share.error_message, 'permissions')
            self.assertFalse(share.completed_at)
            user = get_user_model().objects.get(id=user.id)
            if profile:
                profile = get_profile_model().objects.get(id=profile.id)
            new_token_required = get_user_attribute(
                user, profile, 'new_token_required')
            self.assertEqual(new_token_required, True)
Ejemplo n.º 9
0
def disconnect(request):
    '''
    Removes Facebook from the users profile
    And redirects to the specified next page
    '''
    if request.method == 'POST':
        logger.info("You have disconnected your Facebook profile.")
        messages.info(
            request, _("You have disconnected your Facebook profile."))
        profile = try_get_profile(request.user)
        profile.disconnect_facebook()
        profile.save()
    response = next_redirect(request)
    return response
Ejemplo n.º 10
0
    def remove(self, graph=None):
        if not self.share_id:
            raise ValueError('Can only delete shares which have an id')
        # see if the graph is enabled
        profile = try_get_profile(self.user)
        user_or_profile = get_instance_for_attribute(
            self.user, profile, 'access_token')
        graph = graph or user_or_profile.get_offline_graph()

        response = None
        if graph:
            response = graph.delete(self.share_id)
            self.removed_at = datetime.now()
            self.save()
        return response
Ejemplo n.º 11
0
    def update(self, data, graph=None):
        '''
        Update the share with the given data
        '''
        profile = try_get_profile(self.user)
        user_or_profile = get_instance_for_attribute(
            self.user, profile, 'access_token')
        graph = graph or user_or_profile.get_offline_graph()

        # update the share dict so a retry will do the right thing
        # just in case we fail the first time
        shared = self.update_share_dict(data)
        self.save()

        # broadcast the change to facebook
        if self.share_id:
            return graph.set(self.share_id, **shared)
Ejemplo n.º 12
0
def _add_current_user_id(graph, user):
    '''
    set the current user id, convenient if you want to make sure you
    fb session and user belong together
    '''
    logger.info("ACUI01 add user id")
    if graph:
        logger.info("ACUI02 graph")
        graph.current_user_id = None

        if user.is_authenticated():
            logger.info("ACUI03 user is authenticated, try get profile")
            profile = try_get_profile(user)
            facebook_id = get_user_attribute(user, profile, 'facebook_id')
            logger.info("ACUI04 facebook_id %s" % facebook_id)
            if facebook_id:
                graph.current_user_id = facebook_id
Ejemplo n.º 13
0
    def profile_authenticate(self, facebook_id=None, facebook_email=None):
        '''
        Authenticate the facebook user by id OR facebook_email
        We filter using an OR to allow existing members to connect with
        their facebook ID using email.

        :param facebook_id:
            Optional string representing the facebook id

        :param facebook_email:
            Optional string with the facebook email

        :return: The signed in :class:`User`.
        '''
        if facebook_id or facebook_email:
            profile_class = get_profile_model()
            profile_query = profile_class.objects.all().order_by('user')
            profile_query = profile_query.select_related('user')
            profile = None

            # filter on email or facebook id, two queries for better
            # queryplan with large data sets
            if facebook_id:
                profiles = profile_query.filter(facebook_id=facebook_id)[:1]
                profile = profiles[0] if profiles else None
            if profile is None and facebook_email:
                try:
                    profiles = profile_query.filter(
                        user__email__iexact=facebook_email)[:1]
                    profile = profiles[0] if profiles else None
                except DatabaseError:
                    try:
                        user = get_user_model(
                        ).objects.get(email=facebook_email)
                    except get_user_model().DoesNotExist:
                        user = None
                    profile = try_get_profile(user) if user else None

            if profile:
                # populate the profile cache while we're getting it anyway
                user = profile.user
                user._profile = profile
                if facebook_settings.FACEBOOK_FORCE_PROFILE_UPDATE_ON_LOGIN:
                    user.fb_update_required = True
                return user
Ejemplo n.º 14
0
    def update(self, data, graph=None):
        '''
        Update the share with the given data
        '''
        result = None
        profile = try_get_profile(self.user)
        graph = graph or profile.get_offline_graph()

        # update the share dict so a retry will do the right thing
        # just in case we fail the first time
        shared = self.update_share_dict(data)
        self.save()

        # broadcast the change to facebook
        if self.share_id:
            result = graph.set(self.share_id, **shared)

        return result
Ejemplo n.º 15
0
def disconnect(request):
    '''
    Removes Facebook from the users profile
    And redirects to the specified next page
    '''
    if request.method == 'POST':
        messages.info(
            request, _("You have disconnected your Facebook profile."))

        user = request.user
        profile = try_get_profile(user)
        user_or_profile = get_instance_for_attribute(user, profile,
            'access_token')

        user_or_profile.disconnect_facebook()
        user_or_profile.save()
    response = next_redirect(request)
    return response
Ejemplo n.º 16
0
def _update_access_token(user, graph):
    """
    Conditionally updates the access token in the database
    """
    profile = try_get_profile(user)
    model_or_profile = get_instance_for_attribute(user, profile, "access_token")
    # store the access token for later usage if the profile model supports it
    if model_or_profile:
        # update if not equal to the current token
        new_token = graph.access_token != model_or_profile.access_token
        token_message = "a new" if new_token else "the same"
        logger.info("found %s token %s", token_message, graph.access_token[:10])
        if new_token:
            logger.info("access token changed, updating now")
            model_or_profile.update_access_token(graph.access_token)
            model_or_profile.save()
            # see if we can extend the access token
            # this runs in a task, after extending the token we fire an event
            model_or_profile.extend_access_token()
Ejemplo n.º 17
0
def _update_access_token(user, graph):
    '''
    Conditionally updates the access token in the database
    '''
    profile = try_get_profile(user)
    model_or_profile = get_instance_for_attribute(
        user, profile, 'access_token')
    # store the access token for later usage if the profile model supports it
    if model_or_profile:
        # update if not equal to the current token
        new_token = graph.access_token != model_or_profile.access_token
        token_message = 'a new' if new_token else 'the same'
        logger.info('found %s token', token_message)
        if new_token:
            logger.info('access token changed, updating now')
            model_or_profile.access_token = graph.access_token
            model_or_profile.save()
            # see if we can extend the access token
            # this runs in a task, after extending the token we fire an event
            model_or_profile.extend_access_token()
Ejemplo n.º 18
0
 def check_django_facebook_user(self, request, facebook_id, access_token):
     logger.info("CDFU01 Checking django facebook user...")
     try:
         current_user = try_get_profile(request.user)
         logger.info("CDFU02 got user profile %s" % current_user)
     except:
         logger.info("CDFU03 failed to get user profile")
         if request.user.is_authenticated():
             logger.info("CDFU04 user is authenticated user_id = %s" % request.user.id)
             if FacebookUserProfile.objects.filter(facebook_id=facebook_id).exists():
                 logger.info("CDFU05 FacebookUserProfile exists facebook_id = %s" % facebook_id)
                 messages.info(request, "There is already an account on this site using that login")
                 request.session["facebook_share_not_allowed"] = True
                 return False
             else:
                 logger.info("CDFU06 create new FacebookUserProfile for user_id = %s" % request.user.id)
                 current_user = FacebookUserProfile.objects.create(user=request.user)
                 current_user.facebook_id = facebook_id
                 current_facebook_id = facebook_id
         else:
             logger.info("CDFU07 user is not authenticated")
             current_facebook_id = None
     else:
         current_facebook_id = current_user.facebook_id
         logger.info("CDFU08 current user is user with facebook_id = %s" % current_facebook_id)
     if not current_facebook_id or current_facebook_id != facebook_id:
         logger.info("CDFU09 Wrong or no facebook_id")
         logout(request)
         # clear possible caches
         if hasattr(request, "facebook"):
             del request.facebook
             logger.info("CDFU10 Clear request facebook")
         if request.session.get("graph", None):
             del request.session["graph"]
             logger.info("CDFU11 Clear graph in session")
     else:
         # save last access_token to make sure we always have the most
         # recent one
         current_user.access_token = access_token
         current_user.save()
         logger.info("CDFU12 Save last access_token")
Ejemplo n.º 19
0
    def test_auth_backend(self):
        # the auth backend
        backend = FacebookBackend()
        facebook = get_facebook_graph(access_token='new_user')
        action, user = connect_user(self.request, facebook_graph=facebook)
        facebook_email = user.email
        profile = try_get_profile(user)
        user_or_profile = get_instance_for_attribute(
            user, profile, 'facebook_id')
        facebook_id = user_or_profile.facebook_id
        auth_user = backend.authenticate(facebook_email=facebook_email)

        #I GOT IT WORKING!!!!!!!!!!
        self.client.login(facebook_email=facebook_email)


        # response = self.client.get('/login/?testing=True')
        response = self.client.get('/login/')

        self.assertTemplateUsed(response, )
        template_names = []
        for template in response.templates:
            template_names.append(template.name)
        print template_names



        # logger.info('%s %s %s', auth_user.email, user.email, facebook_email)
        self.assertEqual(auth_user, user)

        auth_user = backend.authenticate(facebook_id=facebook_id)
        self.assertEqual(auth_user, user)

        auth_user = backend.authenticate(facebook_id=facebook_id,
                                         facebook_email=facebook_email)
        self.assertEqual(auth_user, user)

        auth_user = backend.authenticate()
        self.assertIsNone(auth_user)
Ejemplo n.º 20
0
    def setUp(self):
        FacebookTest.setUp(self)
        user_url = 'http://www.fashiolista.com/style/neni/'
        kwargs = dict(item=user_url)
        user = get_user_model().objects.all()[:1][0]
        profile = try_get_profile(user)
        user_or_profile = get_instance_for_attribute(
            user, profile, 'facebook_open_graph')
        user_or_profile.facebook_open_graph = True
        user_or_profile.save()

        some_content_type = ContentType.objects.all()[:1][0]
        share = OpenGraphShare.objects.create(
            user_id=user.id,
            facebook_user_id=13123123,
            action_domain='fashiolista:follow',
            content_type=some_content_type,
            object_id=user.id,
        )
        share.set_share_dict(kwargs)
        share.save()
        self.share = share
        self.share_details = user, profile, share
Ejemplo n.º 21
0
    def test_auth_backend(self):
        # the auth backend
        backend = FacebookBackend()
        facebook = get_facebook_graph(access_token='new_user')
        action, user = connect_user(self.request, facebook_graph=facebook)
        facebook_email = user.email
        profile = try_get_profile(user)
        user_or_profile = get_instance_for_attribute(
            user, profile, 'facebook_id')
        facebook_id = user_or_profile.facebook_id
        auth_user = backend.authenticate(facebook_email=facebook_email)
        logger.info('%s %s %s', auth_user.email, user.email, facebook_email)
        self.assertEqual(auth_user, user)

        auth_user = backend.authenticate(facebook_id=facebook_id)
        self.assertEqual(auth_user, user)

        auth_user = backend.authenticate(facebook_id=facebook_id,
                                         facebook_email=facebook_email)
        self.assertEqual(auth_user, user)

        auth_user = backend.authenticate()
        self.assertIsNone(auth_user)
Ejemplo n.º 22
0
    def test_update_access_token(self):
        request = RequestMock().get('/')
        request.session = {}
        request.user = AnonymousUser()
        graph = get_persistent_graph(request, access_token='paul')
        action, user = connect_user(self.request, facebook_graph=graph)
        first_user_id = user.id

        # new token required should start out as False
        profile = try_get_profile(user)
        new_token_required = get_user_attribute(
            user, profile, 'new_token_required')
        self.assertEqual(new_token_required, False)

        # we manually set it to true
        update_user_attributes(
            user, profile, dict(new_token_required=True), save=True)
        if profile:
            profile = get_profile_model().objects.get(id=profile.id)
        user = get_user_model().objects.get(id=user.id)
        new_token_required = get_user_attribute(
            user, profile, 'new_token_required')
        self.assertEqual(new_token_required, True)

        # another update should however set it back to False
        request.facebook = None
        graph = get_facebook_graph(request, access_token='paul2')
        logger.info('and the token is %s', graph.access_token)
        action, user = connect_user(self.request, facebook_graph=graph)
        user = get_user_model().objects.get(id=user.id)
        self.assertEqual(user.id, first_user_id)
        if profile:
            profile = get_profile_model().objects.get(id=profile.id)
        user = get_user_model().objects.get(id=user.id)
        new_token_required = get_user_attribute(
            user, profile, 'new_token_required')
        self.assertEqual(new_token_required, False)
Ejemplo n.º 23
0
    def send(self, graph=None):
        result = None
        # update the last attempt
        self.last_attempt = datetime.now()
        self.save()

        # see if the graph is enabled
        profile = try_get_profile(self.user)
        user_or_profile = get_instance_for_attribute(
            self.user, profile, 'access_token')
        graph = graph or user_or_profile.get_offline_graph()
        user_enabled = user_or_profile.facebook_open_graph and self.facebook_user_id

        # start sharing
        if graph and user_enabled:
            graph_location = '%s/%s' % (
                self.facebook_user_id, self.action_domain)
            share_dict = self.get_share_dict()
            from open_facebook.exceptions import OpenFacebookException
            try:
                result = graph.set(graph_location, **share_dict)
                share_id = result.get('id')
                if not share_id:
                    error_message = 'No id in Facebook response, found %s for url %s with data %s' % (
                        result, graph_location, share_dict)
                    logger.error(error_message)
                    raise OpenFacebookException(error_message)
                self.share_id = share_id
                self.error_message = None
                self.completed_at = datetime.now()
                self.save()
            except OpenFacebookException, e:
                logger.warn(
                    'Open graph share failed, writing message %s' % e.message)
                self.error_message = unicode(e)
                self.save()
Ejemplo n.º 24
0
def connect_user(request, access_token=None, facebook_graph=None, connect_facebook=False):
    '''
    Given a request either

    - (if authenticated) connect the user
    - login
    - register
    '''
    user = None
    graph = facebook_graph or get_facebook_graph(request, access_token)

    converter = get_instance_for('user_conversion', graph)

    assert converter.is_authenticated()
    facebook_data = converter.facebook_profile_data()
    force_registration = request.POST.get('force_registration') or \
        request.GET.get('force_registration') or \
        request.POST.get('force_registration_hard') or \
        request.GET.get('force_registration_hard')

    logger.debug('force registration is set to %s', force_registration)
    if connect_facebook and request.user.is_authenticated() and not force_registration:
        # we should only allow connect if users indicate they really want to connect
        # only when the request.CONNECT_FACEBOOK = 1
        # if this isn't present we just do a login
        action = CONNECT_ACTIONS.CONNECT
        # default behaviour is not to overwrite old data
        user = _connect_user(request, converter, overwrite=True)
    else:
        email = facebook_data.get('email', False)
        email_verified = facebook_data.get('verified', False)
        kwargs = {}
        if email and email_verified:
            kwargs = {'facebook_email': email}
        auth_user = authenticate(facebook_id=facebook_data['id'], **kwargs)
        if auth_user and not force_registration:
            action = CONNECT_ACTIONS.LOGIN

            # Has the user registered without Facebook, using the verified FB
            # email address?
            # It is after all quite common to use email addresses for usernames
            update = getattr(auth_user, 'fb_update_required', False)
            profile = try_get_profile(auth_user)
            current_facebook_id = get_user_attribute(
                auth_user, profile, 'facebook_id')
            if not current_facebook_id:
                update = True
            # login the user
            user = _login_user(request, converter, auth_user, update=update)
        else:
            action = CONNECT_ACTIONS.REGISTER
            # when force registration is active we should remove the old
            # profile
            try:
                user = _register_user(request, converter,
                                      remove_old_connections=force_registration)
            except facebook_exceptions.AlreadyRegistered as e:
                # in Multithreaded environments it's possible someone beats us to
                # the punch, in that case just login
                logger.info(
                    'parallel register encountered, slower thread is doing a login')
                auth_user = authenticate(
                    facebook_id=facebook_data['id'], **kwargs)
                if not auth_user:
                    # We don't have a valid user so raise
                    raise e
                action = CONNECT_ACTIONS.LOGIN
                user = _login_user(request, converter, auth_user, update=False)

    _update_likes_and_friends(request, user, converter)

    _update_access_token(user, graph)

    logger.info('connect finished with action %s', action)

    return action, user
Ejemplo n.º 25
0
def _update_user(user, facebook, overwrite=True):
    '''
    Updates the user and his/her profile with the data from facebook
    '''
    # if you want to add fields to ur user model instead of the
    # profile thats fine
    # partial support (everything except raw_data and facebook_id is included)
    facebook_data = facebook.facebook_registration_data(username=False)
    facebook_fields = ['facebook_name', 'facebook_profile_url', 'gender',
                       'date_of_birth', 'about_me', 'website_url', 'first_name', 'last_name']

    profile = try_get_profile(user)
    # which attributes to update
    attributes_dict = {}

    # send the signal that we're updating
    signals.facebook_pre_update.send(sender=get_user_model(), user=user,
                                     profile=profile, facebook_data=facebook_data)

    # set the facebook id and make sure we are the only user with this id
    current_facebook_id = get_user_attribute(user, profile, 'facebook_id')
    facebook_id_changed = facebook_data['facebook_id'] != current_facebook_id
    overwrite_allowed = overwrite or not current_facebook_id

    # update the facebook id and access token
    facebook_id_overwritten = False
    if facebook_id_changed and overwrite_allowed:
        # when not overwriting we only update if there is no
        # profile.facebook_id
        logger.info('profile facebook id changed from %s to %s',
                    repr(facebook_data['facebook_id']),
                    repr(current_facebook_id))
        attributes_dict['facebook_id'] = facebook_data['facebook_id']
        facebook_id_overwritten = True

    if facebook_id_overwritten:
        _remove_old_connections(facebook_data['facebook_id'], user.id)

    # update all fields on both user and profile
    for f in facebook_fields:
        facebook_value = facebook_data.get(f, False)
        current_value = get_user_attribute(user, profile, f, None)
        if facebook_value and not current_value:
            attributes_dict[f] = facebook_value

    # write the raw data in case we missed something
    serialized_fb_data = json.dumps(facebook.facebook_profile_data())
    current_raw_data = get_user_attribute(user, profile, 'raw_data')
    if current_raw_data != serialized_fb_data:
        attributes_dict['raw_data'] = serialized_fb_data

    image_url = facebook_data['image']
    # update the image if we are allowed and have to
    if facebook_settings.FACEBOOK_STORE_LOCAL_IMAGE:
        image_field = get_user_attribute(user, profile, 'image', True)
        if not image_field:
            image_name, image_file = _update_image(
                facebook_data['facebook_id'], image_url)
            image_field.save(image_name, image_file)

    # save both models if they changed
    update_user_attributes(user, profile, attributes_dict)
    if getattr(user, '_fb_is_dirty', False):
        user.save()
    if getattr(profile, '_fb_is_dirty', False):
        profile.save()

    signals.facebook_post_update.send(sender=get_user_model(),
                                      user=user, profile=profile, facebook_data=facebook_data)

    return user
Ejemplo n.º 26
0
def connect_user(request,
                 access_token=None,
                 facebook_graph=None,
                 connect_facebook=False):
    '''
    Given a request either

    - (if authenticated) connect the user
    - login
    - register
    '''
    user = None
    graph = facebook_graph or get_facebook_graph(request, access_token)

    converter = get_instance_for('user_conversion', graph)

    assert converter.is_authenticated()
    facebook_data = converter.facebook_profile_data()
    force_registration = request.REQUEST.get('force_registration') or\
        request.REQUEST.get('force_registration_hard')

    logger.debug('force registration is set to %s', force_registration)
    if connect_facebook and request.user.is_authenticated(
    ) and not force_registration:
        # we should only allow connect if users indicate they really want to connect
        # only when the request.CONNECT_FACEBOOK = 1
        # if this isn't present we just do a login
        action = CONNECT_ACTIONS.CONNECT
        # default behaviour is not to overwrite old data
        user = _connect_user(request, converter, overwrite=True)
    else:
        email = facebook_data.get('email', False)
        email_verified = facebook_data.get('verified', False)
        kwargs = {}
        if email and email_verified:
            kwargs = {'facebook_email': email}
        auth_user = authenticate(facebook_id=facebook_data['id'], **kwargs)
        if auth_user and not force_registration:
            action = CONNECT_ACTIONS.LOGIN

            # Has the user registered without Facebook, using the verified FB
            # email address?
            # It is after all quite common to use email addresses for usernames
            update = getattr(auth_user, 'fb_update_required', False)
            profile = try_get_profile(auth_user)
            current_facebook_id = get_user_attribute(auth_user, profile,
                                                     'facebook_id')
            if not current_facebook_id:
                update = True
            # login the user
            user = _login_user(request, converter, auth_user, update=update)
        else:
            action = CONNECT_ACTIONS.REGISTER
            # when force registration is active we should remove the old
            # profile
            try:
                user = _register_user(
                    request,
                    converter,
                    remove_old_connections=force_registration)
            except facebook_exceptions.AlreadyRegistered, e:
                # in Multithreaded environments it's possible someone beats us to
                # the punch, in that case just login
                logger.info(
                    'parallel register encountered, slower thread is doing a login'
                )
                auth_user = authenticate(facebook_id=facebook_data['id'],
                                         **kwargs)
                action = CONNECT_ACTIONS.LOGIN
                user = _login_user(request, converter, auth_user, update=False)
Ejemplo n.º 27
0
def connect_user(request, access_token=None, facebook_graph=None):
    """
    Given a request either

    - (if authenticated) connect the user
    - login
    - register
    """
    user = None
    graph = facebook_graph or get_facebook_graph(request, access_token)

    converter = get_instance_for("user_conversion", graph)

    assert converter.is_authenticated()
    facebook_data = converter.facebook_profile_data()
    force_registration = request.REQUEST.get("force_registration") or request.REQUEST.get("force_registration_hard")

    connect_facebook = to_bool(request.REQUEST.get("connect_facebook"))

    backend = get_registration_backend()

    logger.debug("force registration is set to %s", force_registration)
    if connect_facebook and request.user.is_authenticated() and not force_registration:
        # we should only allow connect if users indicate they really want to connect
        # only when the request.CONNECT_FACEBOOK = 1
        # if this isn't present we just do a login
        action = CONNECT_ACTIONS.CONNECT
        user = _connect_user(request, converter)
        if backend.is_user_banned(user):
            raise PermissionDenied()
    else:
        email = facebook_data.get("email", False)
        email_verified = facebook_data.get("verified", False)
        kwargs = {}
        if email and email_verified:
            kwargs = {"facebook_email": email}
        auth_user = authenticate(facebook_id=facebook_data["id"], **kwargs)
        if backend.is_user_banned(auth_user):
            raise PermissionDenied()
        if auth_user and not force_registration:
            action = CONNECT_ACTIONS.LOGIN

            # Has the user registered without Facebook, using the verified FB
            # email address?
            # It is after all quite common to use email addresses for usernames
            update = getattr(auth_user, "fb_update_required", False)
            profile = try_get_profile(auth_user)
            current_facebook_id = get_user_attribute(auth_user, profile, "facebook_id")
            if not current_facebook_id:
                update = True
            # login the user
            user = _login_user(request, converter, auth_user, update=update)
        else:
            action = CONNECT_ACTIONS.REGISTER
            # when force registration is active we should remove the old
            # profile
            try:
                user = _register_user(request, converter, remove_old_connections=force_registration)
            except facebook_exceptions.AlreadyRegistered, e:
                # in Multithreaded environments it's possible someone beats us to
                # the punch, in that case just login
                logger.info("parallel register encountered, slower thread is doing a login")
                auth_user = authenticate(facebook_id=facebook_data["id"], **kwargs)
                action = CONNECT_ACTIONS.LOGIN
                user = _login_user(request, converter, auth_user, update=False)
Ejemplo n.º 28
0
def get_facebook_graph(request=None, access_token=None, redirect_uri=None, raise_=False):
    '''
    given a request from one of these
    - js authentication flow (signed cookie)
    - facebook app authentication flow (signed cookie)
    - facebook oauth redirect (code param in url)
    - mobile authentication flow (direct access_token)
    - offline access token stored in user profile

    returns a graph object

    redirect path is the path from which you requested the token
    for some reason facebook needs exactly this uri when converting the code
    to a token
    falls back to the current page without code in the request params
    specify redirect_uri if you are not posting and recieving the code
    on the same page
    '''
    # this is not a production flow, but very handy for testing
    if not access_token and request.REQUEST.get('access_token'):
        access_token = request.REQUEST['access_token']
    # should drop query params be included in the open facebook api,
    # maybe, weird this...
    from open_facebook import OpenFacebook, FacebookAuthorization
    from django.core.cache import cache
    expires = None
    if hasattr(request, 'facebook') and request.facebook:
        graph = request.facebook
        _add_current_user_id(graph, request.user)
        return graph

    # parse the signed request if we have it
    signed_data = None
    if request:
        signed_request_string = request.REQUEST.get('signed_data')
        if signed_request_string:
            logger.info('Got signed data from facebook')
            signed_data = parse_signed_request(signed_request_string)
        if signed_data:
            logger.info('We were able to parse the signed data')

    # the easy case, we have an access token in the signed data
    if signed_data and 'oauth_token' in signed_data:
        access_token = signed_data['oauth_token']

    if not access_token:
        # easy case, code is in the get
        code = request.REQUEST.get('code')
        if code:
            logger.info('Got code from the request data')

        if not code:
            # signed request or cookie leading, base 64 decoding needed
            cookie_name = 'fbsr_%s' % facebook_settings.FACEBOOK_APP_ID
            cookie_data = request.COOKIES.get(cookie_name)

            if cookie_data:
                signed_request_string = cookie_data
                if signed_request_string:
                    logger.info('Got signed data from cookie')
                signed_data = parse_signed_request(signed_request_string)
                if signed_data:
                    logger.info('Parsed the cookie data')
                # the javascript api assumes a redirect uri of ''
                redirect_uri = ''

            if signed_data:
                # parsed data can fail because of signing issues
                if 'oauth_token' in signed_data:
                    logger.info('Got access_token from parsed data')
                    # we already have an active access token in the data
                    access_token = signed_data['oauth_token']
                else:
                    logger.info('Got code from parsed data')
                    # no access token, need to use this code to get one
                    code = signed_data.get('code', None)

        if not access_token:
            if code:
                cache_key = hash_key('convert_code_%s' % code)
                access_token = cache.get(cache_key)
                if not access_token:
                    # exchange the code for an access token
                    # based on the php api
                    # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php
                    # create a default for the redirect_uri
                    # when using the javascript sdk the default
                    # should be '' an empty string
                    # for other pages it should be the url
                    if not redirect_uri:
                        redirect_uri = ''

                    # we need to drop signed_data, code and state
                    redirect_uri = cleanup_oauth_url(redirect_uri)

                    try:
                        logger.info(
                            'trying to convert the code with redirect uri: %s',
                            redirect_uri)
                        # This is realy slow, that's why it's cached
                        token_response = FacebookAuthorization.convert_code(
                            code, redirect_uri=redirect_uri)
                        expires = token_response.get('expires')
                        access_token = token_response['access_token']
                        # would use cookies instead, but django's cookie setting
                        # is a bit of a mess
                        cache.set(cache_key, access_token, 60 * 60 * 2)
                    except (open_facebook_exceptions.OAuthException, open_facebook_exceptions.ParameterException), e:
                        # this sometimes fails, but it shouldnt raise because
                        # it happens when users remove your
                        # permissions and then try to reauthenticate
                        logger.warn('Error when trying to convert code %s',
                                    unicode(e))
                        if raise_:
                            raise
                        else:
                            return None
            elif request.user.is_authenticated():
                # support for offline access tokens stored in the users profile
                profile = try_get_profile(request.user)
                access_token = get_user_attribute(
                    request.user, profile, 'access_token')
                if not access_token:
                    if raise_:
                        message = 'Couldnt find an access token in the request or the users profile'
                        raise open_facebook_exceptions.OAuthException(message)
                    else:
                        return None
            else:
                if raise_:
                    message = 'Couldnt find an access token in the request or cookies'
                    raise open_facebook_exceptions.OAuthException(message)
                else:
                    return None
Ejemplo n.º 29
0
    def send(self, graph=None, shared_explicitly=False):
        result = None
        # update the last attempt
        self.last_attempt = datetime.now()
        self.save()

        # see if the graph is enabled
        profile = try_get_profile(self.user)
        user_or_profile = get_instance_for_attribute(self.user, profile,
                                                     'access_token')
        graph = graph or user_or_profile.get_offline_graph()
        user_enabled = shared_explicitly or \
            (user_or_profile.facebook_open_graph
             and self.facebook_user_id)
        # start sharing
        if graph and user_enabled:
            graph_location = '%s/%s' % (self.facebook_user_id,
                                        self.action_domain)
            share_dict = self.get_share_dict()
            from open_facebook.exceptions import OpenFacebookException
            try:
                result = graph.set(graph_location, **share_dict)
                share_id = result.get('id')
                if not share_id:
                    error_message = 'No id in Facebook response, found %s for url %s with data %s' % (
                        result, graph_location, share_dict)
                    logger.error(error_message)
                    raise OpenFacebookException(error_message)
                self.share_id = share_id
                self.error_message = None
                self.completed_at = datetime.now()
                self.save()
            except OpenFacebookException as e:
                logger.warn('Open graph share failed, writing message %s' %
                            str(e))
                self.error_message = repr(e)
                self.save()
                # maybe we need a new access token
                new_token_required = self.exception_requires_new_token(
                    e, graph)
                # verify that the token didnt change in the mean time
                user_or_profile = user_or_profile.__class__.objects.get(
                    id=user_or_profile.id)
                token_changed = graph.access_token != user_or_profile.access_token
                logger.info('new token required is %s and token_changed is %s',
                            new_token_required, token_changed)
                if new_token_required and not token_changed:
                    logger.info(
                        'a new token is required, setting the flag on the user or profile'
                    )
                    # time to ask the user for a new token
                    update_user_attributes(self.user,
                                           profile,
                                           dict(new_token_required=True),
                                           save=True)

        elif not graph:
            self.error_message = 'no graph available'
            self.save()
        elif not user_enabled:
            self.error_message = 'user not enabled'
            self.save()

        return result
Ejemplo n.º 30
0
def _update_user(user, facebook, overwrite=True):
    '''
    Updates the user and his/her profile with the data from facebook
    '''
    # if you want to add fields to ur user model instead of the
    # profile thats fine
    # partial support (everything except raw_data and facebook_id is included)
    facebook_data = facebook.facebook_registration_data(username=False)
    facebook_fields = [
        'facebook_name', 'facebook_profile_url', 'gender', 'date_of_birth',
        'about_me', 'website_url', 'first_name', 'last_name'
    ]

    profile = try_get_profile(user)
    # which attributes to update
    attributes_dict = {}

    # send the signal that we're updating
    signals.facebook_pre_update.send(sender=get_user_model(),
                                     user=user,
                                     profile=profile,
                                     facebook_data=facebook_data)

    # set the facebook id and make sure we are the only user with this id
    current_facebook_id = get_user_attribute(user, profile, 'facebook_id')
    facebook_id_changed = facebook_data['facebook_id'] != current_facebook_id
    overwrite_allowed = overwrite or not current_facebook_id

    # update the facebook id and access token
    facebook_id_overwritten = False
    if facebook_id_changed and overwrite_allowed:
        # when not overwriting we only update if there is no
        # profile.facebook_id
        logger.info('profile facebook id changed from %s to %s',
                    repr(facebook_data['facebook_id']),
                    repr(current_facebook_id))
        attributes_dict['facebook_id'] = facebook_data['facebook_id']
        facebook_id_overwritten = True

    if facebook_id_overwritten:
        _remove_old_connections(facebook_data['facebook_id'], user.id)

    # update all fields on both user and profile
    for f in facebook_fields:
        facebook_value = facebook_data.get(f, False)
        current_value = get_user_attribute(user, profile, f, None)
        if facebook_value and not current_value:
            attributes_dict[f] = facebook_value

    # write the raw data in case we missed something
    serialized_fb_data = json.dumps(facebook.facebook_profile_data())
    current_raw_data = get_user_attribute(user, profile, 'raw_data')
    if current_raw_data != serialized_fb_data:
        attributes_dict['raw_data'] = serialized_fb_data

    image_url = facebook_data['image']
    # update the image if we are allowed and have to
    if facebook_settings.FACEBOOK_STORE_LOCAL_IMAGE:
        image_field = get_user_attribute(user, profile, 'image', True)
        if not image_field:
            image_name, image_file = _update_image(
                facebook_data['facebook_id'], image_url)
            image_field.save(image_name, image_file)

    # save both models if they changed
    update_user_attributes(user, profile, attributes_dict)
    if getattr(user, '_fb_is_dirty', False):
        user.save()
    if getattr(profile, '_fb_is_dirty', False):
        profile.save()

    signals.facebook_post_update.send(sender=get_user_model(),
                                      user=user,
                                      profile=profile,
                                      facebook_data=facebook_data)

    return user
Ejemplo n.º 31
0
def connect_user(request, access_token=None, facebook_graph=None, connect_facebook=False):
    '''
    Given a request either

    - (if authenticated) connect the user
    - login
    - register
    '''
    logger.info('CU01: Start data access_token %s' % access_token)
    logger.info('CU02: Start data facebook_graph %s' % facebook_graph)
    logger.info('CU03: Connect facebook %s' % connect_facebook)
    user = None
    graph = facebook_graph or get_facebook_graph(request, access_token)

    converter = get_instance_for('user_conversion', graph)

    assert converter.is_authenticated()
    facebook_data = converter.facebook_profile_data()
    force_registration = request.REQUEST.get('force_registration') or\
        request.REQUEST.get('force_registration_hard')
    logger.info('CU04: Force registration %s' % force_registration)

    if connect_facebook and request.user.is_authenticated() and not force_registration:
        logger.info('CU05: User is authenticated %s' % request.user.is_authenticated())
        # we should only allow connect if users indicate they really want to connect
        # only when the request.CONNECT_FACEBOOK = 1
        # if this isn't present we just do a login
        action = CONNECT_ACTIONS.CONNECT
        logger.info('CU06: Action %s' % action)
        # default behaviour is not to overwrite old data
        user = _connect_user(request, converter, overwrite=True)
    else:
        logger.info('CU07: Bad day')
        email = facebook_data.get('email', False)
        logger.info('CU08: Email %s' % email)
        email_verified = facebook_data.get('verified', False)
        kwargs = {}
        if email and email_verified:
            logger.info('CU09: Facebook email added to kwargs')
            kwargs = {'facebook_email': email}
        # social-auth support
        # trying to find social user with given email
        logger.info('CU10: Social auth support')
        social_user = get_user_model().objects.filter(email=email)
        logger.info('CU11: Social users found %s' % social_user)
        social_user = social_user[0] if social_user else None
        logger.info('CU12: Social user %s' % social_user)
        if social_user and UserSocialAuth.objects.filter(user__id=social_user.id).exists():
            logger.info('CU13: Social user exists')
            try:
                current_user_profile = try_get_profile(social_user)
                logger.info('CU14: Got profile for social user %s' % current_user_profile)
            except:
                logger.info('CU15: No profile')
                profile_model = get_profile_model()
                logger.info('CU16: Profile model %s' % profile_model)
                profile_model.objects.create(user=social_user)
                logger.info('CU17: Profile object created')
        auth_user = authenticate(facebook_id=facebook_data['id'], **kwargs)
        logger.info('CU19: Aunthenticated user %s ' % auth_user)
        if auth_user and not force_registration:
            referer_url = request.META.get('HTTP_REFERER', None)
            if referer_url:
                urlparsed = urlparse(referer_url)
                is_facebook = urlparsed.netloc.endswith('facebook.com')
            else:
                is_facebook = False
            logger.info('CU32 is_facebook: %s, referer_url: %s'%(is_facebook, referer_url))
            if auth_user.is_client() or auth_user.is_subclient() and not is_facebook:
                action = CONNECT_ACTIONS.CLIENT_REDIRECT
                logger.info('CU31: Client or subclient detected. Action: CLIENT_REDIRECT')
                return action, auth_user
            action = CONNECT_ACTIONS.LOGIN
            logger.info('CU20: Action %s' % action)
            # Has the user registered without Facebook, using the verified FB
            # email address?
            # It is after all quite common to use email addresses for usernames
            update = getattr(auth_user, 'fb_update_required', False)
            logger.info('CU21: Update %s' % update)
            profile = try_get_profile(auth_user)
            logger.info('CU22: Got profile %s' % profile)
            current_facebook_id = get_user_attribute(
                auth_user, profile, 'facebook_id')
            logger.info('CU23: Current facebook_id %s' % current_facebook_id)
            if not current_facebook_id:
                update = True
            # login the user
            user = _login_user(request, converter, auth_user, update=update)
        else:
            action = CONNECT_ACTIONS.REGISTER
            logger.info('CU24: Action %s' % action)
            # when force registration is active we should remove the old
            # profile
            try:
                user = _register_user(request, converter,
                                      remove_old_connections=force_registration)
                logger.info('CU25: User registered %s' % user)
            except facebook_exceptions.AlreadyRegistered, e:
                # in Multithreaded environments it's possible someone beats us to
                # the punch, in that case just login
                logger.info(
                    'CU26: parallel register encountered, slower thread is doing a login')
                auth_user = authenticate(
                    facebook_id=facebook_data['id'], **kwargs)
                logger.info('CU27: Auth user %s' % auth_user)
                if not auth_user:
                    logger.info('CU28: No auth user')
                    # We don't have a valid user so raise
                    raise e
                logger.info('CU29: Login user')
                action = CONNECT_ACTIONS.LOGIN
                user = _login_user(request, converter, auth_user, update=False)
Ejemplo n.º 32
0
 def save(self, *args, **kwargs):
     if self.user and not self.facebook_user_id:
         profile = try_get_profile(self.user)
         self.facebook_user_id = get_user_attribute(
             self.user, profile, 'facebook_id')
     return BaseModel.save(self, *args, **kwargs)
Ejemplo n.º 33
0
def get_facebook_graph(request=None, access_token=None, redirect_uri=None, raise_=False):
    '''
    given a request from one of these
    - js authentication flow (signed cookie)
    - facebook app authentication flow (signed cookie)
    - facebook oauth redirect (code param in url)
    - mobile authentication flow (direct access_token)
    - offline access token stored in user profile

    returns a graph object

    redirect path is the path from which you requested the token
    for some reason facebook needs exactly this uri when converting the code
    to a token
    falls back to the current page without code in the request params
    specify redirect_uri if you are not posting and recieving the code
    on the same page
    '''
    # this is not a production flow, but very handy for testing
    if not access_token and request.REQUEST.get('access_token'):
        access_token = request.REQUEST['access_token']
    # should drop query params be included in the open facebook api,
    # maybe, weird this...
    from open_facebook import OpenFacebook, FacebookAuthorization
    from django.core.cache import cache
    expires = None
    if hasattr(request, 'facebook') and request.facebook:
        graph = request.facebook
        _add_current_user_id(graph, request.user)
        return graph

    # parse the signed request if we have it
    signed_data = None
    if request:
        signed_request_string = request.REQUEST.get('signed_data')
        if signed_request_string:
            logger.info('Got signed data from facebook')
            signed_data = parse_signed_request(signed_request_string)
        if signed_data:
            logger.info('We were able to parse the signed data')

    # the easy case, we have an access token in the signed data
    if signed_data and 'oauth_token' in signed_data:
        access_token = signed_data['oauth_token']

    if not access_token:
        # easy case, code is in the get
        code = request.REQUEST.get('code')
        if code:
            logger.info('Got code from the request data')

        if not code:
            # signed request or cookie leading, base 64 decoding needed
            cookie_name = 'fbsr_%s' % facebook_settings.FACEBOOK_APP_ID
            cookie_data = request.COOKIES.get(cookie_name)

            if cookie_data:
                signed_request_string = cookie_data
                if signed_request_string:
                    logger.info('Got signed data from cookie')
                signed_data = parse_signed_request(signed_request_string)
                if signed_data:
                    logger.info('Parsed the cookie data')
                # the javascript api assumes a redirect uri of ''
                redirect_uri = ''

            if signed_data:
                # parsed data can fail because of signing issues
                if 'oauth_token' in signed_data:
                    logger.info('Got access_token from parsed data')
                    # we already have an active access token in the data
                    access_token = signed_data['oauth_token']
                else:
                    logger.info('Got code from parsed data')
                    # no access token, need to use this code to get one
                    code = signed_data.get('code', None)

        if not access_token:
            if code:
                cache_key = hash_key('convert_code_%s' % code)
                access_token = cache.get(cache_key)
                if not access_token:
                    # exchange the code for an access token
                    # based on the php api
                    # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php
                    # create a default for the redirect_uri
                    # when using the javascript sdk the default
                    # should be '' an empty string
                    # for other pages it should be the url
                    if not redirect_uri:
                        redirect_uri = ''

                    # we need to drop signed_data, code and state
                    redirect_uri = cleanup_oauth_url(redirect_uri)

                    try:
                        logger.info(
                            'trying to convert the code with redirect uri: %s',
                            redirect_uri)
                        # This is realy slow, that's why it's cached
                        token_response = FacebookAuthorization.convert_code(
                            code, redirect_uri=redirect_uri)
                        expires = token_response.get('expires')
                        access_token = token_response['access_token']
                        # would use cookies instead, but django's cookie setting
                        # is a bit of a mess
                        cache.set(cache_key, access_token, 60 * 60 * 2)
                    except (open_facebook_exceptions.OAuthException, open_facebook_exceptions.ParameterException) as e:
                        # this sometimes fails, but it shouldnt raise because
                        # it happens when users remove your
                        # permissions and then try to reauthenticate
                        logger.warn('Error when trying to convert code %s',
                                    unicode(e))
                        if raise_:
                            raise
                        else:
                            return None
            elif request.user.is_authenticated():
                # support for offline access tokens stored in the users profile
                profile = try_get_profile(request.user)
                access_token = get_user_attribute(
                    request.user, profile, 'access_token')
                if not access_token:
                    if raise_:
                        message = 'Couldnt find an access token in the request or the users profile'
                        raise open_facebook_exceptions.OAuthException(message)
                    else:
                        return None
            else:
                if raise_:
                    message = 'Couldnt find an access token in the request or cookies'
                    raise open_facebook_exceptions.OAuthException(message)
                else:
                    return None

    graph = OpenFacebook(access_token, signed_data, expires=expires)
    # add user specific identifiers
    if request:
        _add_current_user_id(graph, request.user)

    return graph
Ejemplo n.º 34
0
def connect_user(request, access_token=None, facebook_graph=None, connect_facebook=False):
    '''
    Given a request either

    - (if authenticated) connect the user
    - login
    - register
    '''
    user = None
    graph = facebook_graph or get_facebook_graph(request, access_token)

    converter = get_instance_for('user_conversion', graph)

    assert converter.is_authenticated()
    facebook_data = converter.facebook_profile_data()
    logger.debug('facebook_data %s', facebook_data)
    
    force_registration = request.POST.get('force_registration') or \
        request.GET.get('force_registration') or \
        request.POST.get('force_registration_hard') or \
        request.GET.get('force_registration_hard')

    logger.debug('force registration is set to %s', force_registration)
    if connect_facebook and request.user.is_authenticated() and not force_registration:
        # we should only allow connect if users indicate they really want to connect
        # only when the request.CONNECT_FACEBOOK = 1
        # if this isn't present we just do a login
        action = CONNECT_ACTIONS.CONNECT
        # default behaviour is not to overwrite old data
        user = _connect_user(request, converter, overwrite=True)
    else:
        name = facebook_data.get('name', None)
        email = facebook_data.get('email', False)
        #email = False
        email_verified = facebook_data.get('verified')
        if not email_verified:
            logger.info('overriding email_verified to true. we dont care about this flag.')
            email_verified = True
        kwargs = {}
        logger.debug('email, email_verified: %s %s', email, email_verified)
        if email and email_verified:
            kwargs = {'facebook_email': email}

        logger.debug('authenticating %s', facebook_data['id'])
        auth_user = authenticate(facebook_id=facebook_data['id'], **kwargs)
        logger.debug('auth_user, force_registration: %s %s', auth_user, force_registration)
        if auth_user and not force_registration:
            action = CONNECT_ACTIONS.LOGIN

            # Has the user registered without Facebook, using the verified FB
            # email address?
            # It is after all quite common to use email addresses for usernames
            update = getattr(auth_user, 'fb_update_required', False)
            profile = try_get_profile(auth_user)
            current_facebook_id = get_user_attribute(
                auth_user, profile, 'facebook_id')
            if not current_facebook_id:
                update = True
            # login the user
            user = _login_user(request, converter, auth_user, update=update)
        else:
            logger.info('going for _register_user')
            
            if not email:
                logger.info('raising AccountNotVerifiedException')
                raise facebook_exceptions.AccountNotVerifiedException(name)


            action = CONNECT_ACTIONS.REGISTER
            # when force registration is active we should remove the old
            # profile
            try:
                user = _register_user(request, converter,
                                      remove_old_connections=force_registration)
            except facebook_exceptions.AlreadyRegistered as e:
                # in Multithreaded environments it's possible someone beats us to
                # the punch, in that case just login
                logger.info(
                    'parallel register encountered, slower thread is doing a login')
                auth_user = authenticate(
                    facebook_id=facebook_data['id'], **kwargs)
                if not auth_user:
                    # We don't have a valid user so raise
                    raise e
                action = CONNECT_ACTIONS.LOGIN
                user = _login_user(request, converter, auth_user, update=False)

    _update_likes_and_friends(request, user, converter)

    _update_access_token(user, graph)

    logger.info('connect finished with action %s', action)

    return action, user