示例#1
0
def user_from_facebook_auth_response(auth_response, initial_user=None, is_test=False):
    if 'accessToken' in auth_response:
        facebook_access_token = auth_response['accessToken']
    else:
        facebook_access_token = auth_response
    fb_user = fb_user_from_facebook_access_token(facebook_access_token)
    facebook_id = fb_user['id']
    try:
        linked_facebook = LinkedFacebookAccount.objects.filter(facebook_id=facebook_id).select_related(
            'user__profile').first()
        if not linked_facebook:
            raise LinkedFacebookAccount.DoesNotExist()
        save_linked_facebook(linked_facebook.user, facebook_access_token, fb_user, linked_facebook=linked_facebook)
        user = linked_facebook.user
        location = initial_user and initial_user.get('location')
        if location:
            location_controller.update_profile_location(user.ap, location)
    except LinkedFacebookAccount.DoesNotExist:
        debug_logger.debug('LinkedFacebookAccount.DoesNotExist for facebook_id %s' % facebook_id)
        if 'email' not in fb_user:
            dev_msg = 'Facebook user has no email: %s' % json.dumps(fb_user)
            debug_logger.error(dev_msg)
            raise ShoutitBadRequest(message=FB_LINK_ERROR_EMAIL, developer_message=dev_msg)
        user = user_controller.auth_with_facebook(fb_user, initial_user, is_test)
        try:
            save_linked_facebook(user, facebook_access_token, fb_user)
        except (ValidationError, IntegrityError) as e:
            raise ShoutitBadRequest(message=FB_LINK_ERROR_TRY_AGAIN, developer_message=str(e))

    return user
示例#2
0
    def can_promote(self, shout, user):
        if user.credit < self.credits:
            raise ShoutitBadRequest(
                _("You don't have enough Shoutit Credit for this action"))

        if shout.promotions.exists():
            raise ShoutitBadRequest(_('This Shout is already promoted'))
示例#3
0
def facebook_graph_object(graph_path, params):
    graph_url = FB_GRAPH_URL + graph_path
    try:
        response = requests.get(graph_url, params=params, timeout=20)
        response_data = response.json()
        error = response_data.get('error')
        if response.status_code != 200 or error:
            dev_msg = error.get('message') if isinstance(error, dict) else str(response_data)
            dev_msg = "Facebook Graph error: %s" % dev_msg
            raise ShoutitBadRequest(message=FB_ERROR_TRY_AGAIN, developer_message=dev_msg)
        return response_data
    except requests.RequestException as e:
        dev_msg = "Facebook Graph error: %s" % str(e)
        debug_logger.error(dev_msg)
        raise ShoutitBadRequest(message=FB_ERROR_TRY_AGAIN, developer_message=dev_msg)
示例#4
0
def auth_with_gplus(gplus_user, credentials, initial_user=None, is_test=False):
    email = gplus_user.get('emails')[0].get('value').lower()
    name = gplus_user.get('name', {})
    first_name = name.get('givenName')
    last_name = name.get('familyName')
    username = initial_user.get('username')
    gplus_id = gplus_user.get('id')
    gender = gplus_user.get('gender')
    profile_fields = {}
    location = {}
    if initial_user:
        if initial_user.get('location'):
            location = initial_user.get('location')
            location_controller.add_predefined_city(location)
        elif initial_user.get('ip'):
            location = location_controller.from_ip(initial_user.get('ip'))
    profile_fields.update(location)
    profile_fields.update({'gender': gender})

    try:
        # Todo: Check!
        # in rare cases when clients send multiple requests and while the first one is still in progress
        # (post save isn't yet completed i.e. no profile yet) the second one passes this check and breaks
        # in the coming lines of updating profile location as there is no profile. The result of such call can be even
        # worse as two users will be created
        user = User.objects.get(email=email)
        debug_logger.debug(
            'Found user: {} with same email of gplus_user: {}'.format(
                user, gplus_id))
        if location:
            location_controller.update_profile_location(user.profile,
                                                        location,
                                                        add_pc=False)
    except User.DoesNotExist:
        user = create_user(email=email,
                           first_name=first_name,
                           last_name=last_name,
                           username=username,
                           is_activated=True,
                           profile_fields=profile_fields,
                           is_test=is_test)

    if not user.is_activated:
        user.activate()

    if not user.profile.gender and gender:
        user.profile.update(gender=gender)

    credentials_json = credentials.to_json()
    try:
        LinkedGoogleAccount.objects.create(user=user,
                                           credentials_json=credentials_json,
                                           gplus_id=gplus_id)
    except IntegrityError as e:
        raise ShoutitBadRequest(
            message=_("Could not access your Google account, try again later"),
            developer_message=str(e))
    image_url = gplus_user['image']['url'].split('?')[0]
    media_controller.set_profile_media(user.profile, 'image', image_url)
    return user
示例#5
0
    def video_identity(self, request):
        """
        Retrieve video client identity to make a call
        <pre><code>
        GET: /twilio/video_identity?profile=username
        </code></pre>

        ###REQUIRES AUTH

        ###Response
        <pre><code>
        {
          "identity": "7c6ca4737db3447f936037374473e61f"
        }
        </code></pre>

        Returns 403 if the call is not allowed.
        ---
        omit_serializer: true
        parameters:
            - name: profile
              description: Profile username
              paramType: query
        """
        # Todo: Check whether calling user is allowed to do this call or not
        # Todo: Move the logic to Serializer

        other_username = request.query_params.get('profile')
        if not other_username:
            raise RequiredParameter('profile')
        if other_username == request.user.username:
            raise InvalidParameter('profile', "You can't call your self")

        try:
            other_user = User.objects.get(username=other_username)
        except User.DoesNotExist:
            msg = _("Profile with username '%(username)s' does not exist") % {
                'username': other_username
            }
            raise InvalidParameter('profile', message=msg)

        if hasattr(other_user, 'video_client'):
            video_client = other_user.video_client
        else:
            # Create video client for the other user
            try:
                video_client = create_video_client(other_user)
            except (ValidationError, IntegrityError) as e:
                msg = _("Error calling %(name)s") % {
                    'name': other_username.name
                }
                raise ShoutitBadRequest(message=msg,
                                        developer_message=unicode(e))

        # Notify the other user
        notifications_controller.notify_user_of_incoming_video_call(
            user=other_user, caller=request.user)

        res = {'identity': video_client.identity}
        return Response(res)
示例#6
0
    def call(self, request, *args, **kwargs):
        """
        Get the mobile of this Shout
        ##Response
        <pre><code>
        {
            "mobile": "01701700555"
        }
        </code></pre>

        This endpoint will be throttled to prevent multiple requests from same client in short time.
        ---
        omit_serializer: true
        omit_parameters:
            - form
        """
        user = request.user
        profile = user.ap
        shout = self.get_object()
        mobile = shout.mobile if shout.is_mobile_set else None
        if not mobile:
            raise ShoutitBadRequest(_("No mobile to be called"))
        track_properties = {
            'api_client': getattr(request, 'api_client', None),
            'api_version': request.version,
            'mp_country_code': profile.country,
            '$region': profile.state,
            '$city': profile.city,
            'shout_id': shout.pk,
            'shout_country': shout.get_country_display(),
            'shout_region': shout.state,
            'shout_city': shout.city,
        }
        mixpanel_controller.track(user.pk, 'show_mobile', track_properties)
        return Response({'mobile': mobile})
示例#7
0
def unlink_facebook_user(user, strict=True):
    """
    Deleted the user's LinkedFacebookAccount
    """
    linked_account = LinkedFacebookAccount.objects.filter(user=user)
    if linked_account.exists():
        linked_account.delete()
    elif strict:
        debug_logger.warning("User: %s, tried to unlink non-existing facebook account" % user)
        raise ShoutitBadRequest(FB_LINK_ERROR_NO_LINK)
示例#8
0
def fb_page_from_facebook_page_id(facebook_page_id, facebook_access_token):
    params = {
        'fields': 'access_token,category,name,id,perms',
        'access_token': facebook_access_token
    }
    accounts = facebook_graph_object('/me/accounts', params)['data']
    for account in accounts:
        if account['id'] == facebook_page_id:
            return account
    raise ShoutitBadRequest(_("Couldn't find the specified Facebook Page"))
示例#9
0
    def get_object(self):
        value = self.kwargs.get(self.lookup_field)
        try:
            uuid.UUID(value)
        except:
            raise ShoutitBadRequest(
                message=_("Resource not found"),
                developer_message="'%s' is not a valid id" % value,
                reason=ERROR_REASON.INVALID_IDENTIFIER)

        return super(UUIDViewSetMixin, self).get_object()
示例#10
0
def link_facebook_account(user, facebook_access_token):
    """
    Add LinkedFacebookAccount to user
    """
    fb_user = fb_user_from_facebook_access_token(facebook_access_token)
    facebook_id = fb_user['id']
    linked_page_ids = []

    # Check whether the Facebook account is already linked
    try:
        la = LinkedFacebookAccount.objects.get(facebook_id=facebook_id)
        debug_logger.warning('User %s tried to link already linked facebook account id: %s.' % (user, facebook_id))
        if la.user != user:
            raise ShoutitBadRequest(_("Facebook account is already linked to somebody else's profile"))
        linked_page_ids = list(la.pages.values_list('facebook_id', flat=True))  # copy the list
    except LinkedFacebookAccount.DoesNotExist:
        pass

    # Unlink previous Facebook account
    unlink_facebook_user(user, strict=False)

    # Link Facebook account
    try:
        linked_facebook = save_linked_facebook(user, facebook_access_token, fb_user)
    except (ValidationError, IntegrityError) as e:
        debug_logger.error("LinkedFacebookAccount creation error: %s" % str(e))
        raise ShoutitBadRequest(message=FB_LINK_ERROR_TRY_AGAIN, developer_message=str(e))

    # Link Facebook Pages if any existed in the previous LinkedFacebookAccount
    for facebook_page_id in linked_page_ids:
        link_facebook_page(linked_facebook, facebook_page_id)

    # Activate the user if not yet activated
    if not user.is_activated:
        user.notify = False
        user.activate()
示例#11
0
    def listen(self, request, *args, **kwargs):
        """
        Start/Stop listening to a Profile
        ###REQUIRES AUTH
        ###Start listening
        <pre><code>
        POST: /users/{username}/listen
        </code></pre>

        ###Stop listening
        <pre><code>
        DELETE: /users/{username}/listen
        </code></pre>
        ---
        omit_serializer: true
        omit_parameters:
            - form
        """
        user = self.get_object()
        ap = user.ap
        api_client = getattr(request, 'api_client', None)

        if request.user == user:
            raise ShoutitBadRequest(_("You can't listen to your self"))

        if request.method == 'POST':
            listen_controller.listen_to_object(request.user,
                                               ap,
                                               api_client=api_client,
                                               api_version=request.version)
            msg = _("You started listening to shouts from %(name)s") % {
                'name': user.name
            }
        else:
            listen_controller.stop_listening_to_object(request.user, ap)
            msg = _("You stopped listening to shouts from %(name)s") % {
                'name': user.name
            }

        data = {'success': msg, 'new_listeners_count': user.listeners_count}
        return Response(data=data, status=status.HTTP_202_ACCEPTED)
示例#12
0
def extend_token(short_lived_token):
    exchange_url = FB_GRAPH_ACCESS_TOKEN_URL
    params = {
        'client_id': settings.FACEBOOK_APP_ID,
        'client_secret': settings.FACEBOOK_APP_SECRET,
        'grant_type': "fb_exchange_token",
        'fb_exchange_token': short_lived_token
    }
    try:
        response = requests.get(exchange_url, params=params, timeout=20)
        if response.status_code != 200:
            raise ValueError("Invalid access token: %s" % response.content)
        response_params = dict(urlparse.parse_qsl(response.content))
        access_token = response_params.get('access_token')
        if not access_token:
            raise ValueError('`access_token` not in response: %s' % response.content)
        expires = response_params.get('expires')
        # Sometimes Facebook doesn't return expiry, assume 60 days
        response_params['expires'] = expires or SIXTY_DAYS
    except (requests.RequestException, ValueError) as e:
        debug_logger.error("Facebook token extend error: %s" % str(e))
        raise ShoutitBadRequest(message=FB_ERROR_TRY_AGAIN, developer_message=str(e))
    return response_params
示例#13
0
    def video_auth(self, request):
        """
        Create a video chat endpoint.
        ###REQUIRES AUTH
        ###Response
        <pre><code>
        {
          "token": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCIsICJjdHkiOiAidHdpbGlvLWZwYTt2PTEifQ.eyJpc3MiOiAiU0s3MDFlYzE",
          "identity": "7c6ca4737db3447f936037374473e61f"
        }
        </code></pre>
        ---
        """
        # Todo: Move the logic to Serializer
        user = request.user
        try:
            video_client = user.video_client

            # Check whether client has an expired token
            if timezone.now() > video_client.expires_at:
                video_client.delete()
                raise ValueError()
        except (AttributeError, ValueError):
            try:
                video_client = create_video_client(user)
            except (ValidationError, IntegrityError) as e:
                msg = _("Couldn't authorize you to make video calls")
                raise ShoutitBadRequest(message=msg,
                                        developer_message=unicode(e))

        # Return token info
        res = OrderedDict([('identity', video_client.identity),
                           ('token', video_client.token),
                           ('expires_at', video_client.expires_at_unix)])
        debug_logger.debug("Authorized %s to use Twilio with identity: %s" %
                           (user, video_client.identity))
        return Response(res)
示例#14
0
 def has_permission(self, request, view):
     if settings.ENFORCE_SECURE and not request.is_secure():
         raise ShoutitBadRequest(message=_("Secure connection is required"), reason=ERROR_REASON.INSECURE_CONNECTION)
     return True
示例#15
0
    def reply(self, request, *args, **kwargs):
        """
        Send message to the owner about this Shout
        ###REQUIRES AUTH
        ###Request
        <pre><code>
        {
            "text": "text goes here",
            "attachments": [
                {
                    "shout": {
                        "id": ""
                    }
                },
                {
                    "profile": {
                        "id": ""
                    }
                },
                {
                    "location": {
                        "latitude": 12.345,
                        "longitude": 12.345
                    }
                },
                {
                    "images": [], // list of image urls
                    "videos": [] // list of {Video Object}s
                }
            ]
        }
        </code></pre>

        Either `text`, `attachments` or both has to be provided. Images and videos are to be compressed and uploaded before submitting. CDN urls should be sent.
        ---
        serializer: MessageSerializer
        omit_parameters:
            - form
        parameters:
            - name: body
              paramType: body
        """
        shout = self.get_object()
        if request.user == shout.owner:
            raise ShoutitBadRequest(
                _("You can not start a conversation about your own shout"))
        context = {
            'request': request,
            'conversation': None,
            'to_users': [shout.owner],
            'about': shout
        }
        serializer = MessageSerializer(data=request.data,
                                       partial=True,
                                       context=context)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        headers = self.get_success_message_headers(serializer.data)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=headers)
示例#16
0
    def chat(self, request, *args, **kwargs):
        """
        Start or continue chatting (conversation whose its `type` is `chat`) with the Profile
        ###REQUIRES AUTH
        > Profile can only message its Listeners, or someone whom it already has an existing conversation with.
        ###Request
        <pre><code>
        {
            "text": "text goes here",
            "attachments": [
                {
                    "shout": {
                        "id": ""
                    }
                },
                {
                    "profile": {
                        "id": ""
                    }
                },
                {
                    "location": {
                        "latitude": 12.345,
                        "longitude": 12.345
                    }
                },
                {
                    "images": [], // list of image urls
                    "videos": [] // list of {Video Object}s
                }
            ]
        }
        </code></pre>

        Either `text`, `attachments` or both has to be provided. Images and videos are to be compressed and uploaded before submitting. CDN urls should be sent.
        ---
        response_serializer: MessageSerializer
        omit_parameters:
            - form
        parameters:
            - name: body
              paramType: body
        """
        # Todo: move validation to the serializer
        user = self.get_object()
        logged_user = request.user
        if logged_user == user:
            raise ShoutitBadRequest(
                _("You can not start a conversation with your self"))
        if not (message_controller.conversation_exist(
                users=[user, logged_user]) or user.is_listening(logged_user)):
            raise ShoutitBadRequest(
                _("You can only start a conversation with your listeners"))
        context = {
            'request': request,
            'conversation': None,
            'to_users': [user]
        }
        serializer = MessageSerializer(data=request.data,
                                       partial=False,
                                       context=context)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        headers = self.get_success_message_headers(serializer.data)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=headers)
示例#17
0
    def to_internal_value(self, data):
        user = self.context['request'].user
        if self.instance.is_owner(user):
            raise ShoutitBadRequest(_("You can not like your own shouts"))

        return {'user': user}