Exemplo n.º 1
0
    def GetBlockedUsers(self, request, context):
        with session_scope() as session:
            blocked_users = (session.execute(
                select(User).join(UserBlock,
                                  UserBlock.blocked_user_id == User.id).where(
                                      User.is_visible).where(
                                          UserBlock.blocking_user_id ==
                                          context.user_id)).scalars().all())

            return blocking_pb2.GetBlockedUsersRes(blocked_usernames=[
                blocked_user.username for blocked_user in blocked_users
            ], )
Exemplo n.º 2
0
def get_parent_node_at_location(session, shape):
    """
    Finds the smallest node containing the shape.

    Shape can be any PostGIS geo object, e.g. output from create_coordinate
    """

    # Fin the lowest Node (in the Node tree) that contains the shape. By construction of nodes, the area of a sub-node
    # must always be less than its parent Node, so no need to actually traverse the tree!
    return (session.execute(
        select(Node).where(func.ST_Contains(Node.geom, shape)).order_by(
            func.ST_Area(Node.geom))).scalars().first())
Exemplo n.º 3
0
def test_relationships_userblock_dot_user(db):
    user1, token1 = generate_user()
    user2, token2 = generate_user()

    make_user_block(user1, user2)

    with session_scope() as session:
        block = session.execute(
            select(UserBlock).where((UserBlock.blocking_user_id == user1.id) & (UserBlock.blocked_user_id == user2.id))
        ).scalar_one_or_none()
        assert block.blocking_user.username == user1.username
        assert block.blocked_user.username == user2.username
Exemplo n.º 4
0
    def GetHostRequestMessages(self, request, context):
        with session_scope() as session:
            host_request = session.execute(
                select(HostRequest).where(
                    HostRequest.conversation_id ==
                    request.host_request_id)).scalar_one_or_none()

            if not host_request:
                context.abort(grpc.StatusCode.NOT_FOUND,
                              errors.HOST_REQUEST_NOT_FOUND)

            if host_request.surfer_user_id != context.user_id and host_request.host_user_id != context.user_id:
                context.abort(grpc.StatusCode.NOT_FOUND,
                              errors.HOST_REQUEST_NOT_FOUND)

            pagination = request.number if request.number > 0 else DEFAULT_PAGINATION_LENGTH
            pagination = min(pagination, MAX_PAGE_SIZE)

            messages = (session.execute(
                select(Message).where(
                    Message.conversation_id ==
                    host_request.conversation_id).where(
                        or_(Message.id < request.last_message_id,
                            request.last_message_id == 0)).order_by(
                                Message.id.desc()).limit(pagination +
                                                         1)).scalars().all())

            no_more = len(messages) <= pagination

            last_message_id = min(
                map(lambda m: m.id if m else 1,
                    messages[:pagination])) if len(messages) > 0 else 0

            return requests_pb2.GetHostRequestMessagesRes(
                last_message_id=last_message_id,
                no_more=no_more,
                messages=[
                    message_to_pb(message) for message in messages[:pagination]
                ],
            )
Exemplo n.º 5
0
def test_media_upload(db):
    user, token = generate_user()

    media_bearer_token = random_hex(32)

    with api_session(token) as api:
        res = api.InitiateMediaUpload(empty_pb2.Empty())

    params = parse_qs(urlparse(res.upload_url).query)
    data = b64decode(params["data"][0])

    response = media_pb2.UploadRequest.FromString(data)
    key = response.key

    filename = random_hex(32)

    req = media_pb2.UploadConfirmationReq(key=key, filename=filename)

    with session_scope() as session:
        # make sure it exists
        assert (session.execute(
            select(InitiatedUpload).where(
                InitiatedUpload.key == key)).scalar_one_or_none() is not None)

    with media_session(media_bearer_token) as media:
        res = media.UploadConfirmation(req)

    with session_scope() as session:
        # make sure it exists
        assert (session.execute(
            select(Upload).where(Upload.key == key).where(
                Upload.filename == filename).where(
                    Upload.creator_user_id == user.id)).scalar_one()
                is not None)

    with session_scope() as session:
        # make sure it was deleted
        assert not session.execute(
            select(InitiatedUpload)).scalar_one_or_none()
Exemplo n.º 6
0
def test_banned_user_without_password(db):
    _quick_signup()

    with session_scope() as session:
        user = session.execute(select(User)).scalar_one()
        user.hashed_password = None

    with auth_api_session() as (auth_api, metadata_interceptor):
        reply = auth_api.Login(auth_pb2.LoginReq(user="******"))
    assert reply.next_step == auth_pb2.LoginRes.LoginStep.SENT_LOGIN_EMAIL

    with session_scope() as session:
        login_token = session.execute(select(LoginToken)).scalar_one().token

    with session_scope() as session:
        session.execute(select(User)).scalar_one().is_banned = True

    with auth_api_session() as (auth_api, metadata_interceptor):
        with pytest.raises(grpc.RpcError) as e:
            auth_api.CompleteTokenLogin(
                auth_pb2.CompleteTokenLoginReq(login_token=login_token))
        assert e.value.details() == "Your account is suspended."
Exemplo n.º 7
0
    def AcceptTOS(self, request, context):
        with session_scope() as session:
            user = session.execute(
                select(User).where(User.id == context.user_id)).scalar_one()

            if not request.accept:
                context.abort(grpc.StatusCode.FAILED_PRECONDITION,
                              errors.CANT_UNACCEPT_TOS)

            user.accepted_tos = TOS_VERSION
            session.commit()

            return self._get_jail_info(user)
Exemplo n.º 8
0
    def AcceptCommunityGuidelines(self, request, context):
        with session_scope() as session:
            user = session.execute(
                select(User).where(User.id == context.user_id)).scalar_one()

            if not request.accept:
                context.abort(grpc.StatusCode.FAILED_PRECONDITION,
                              errors.CANT_UNACCEPT_COMMUNITY_GUIDELINES)

            user.accepted_community_guidelines = GUIDELINES_VERSION
            session.commit()

            return self._get_jail_info(user)
Exemplo n.º 9
0
    def ListEventOccurrences(self, request, context):
        with session_scope() as session:
            page_size = min(MAX_PAGINATION_LENGTH, request.page_size
                            or MAX_PAGINATION_LENGTH)
            # the page token is a unix timestamp of where we left off
            page_token = dt_from_millis(int(
                request.page_token)) if request.page_token else now()
            occurrence = session.execute(
                select(EventOccurrence).where(EventOccurrence.id == request.
                                              event_id)).scalar_one_or_none()
            if not occurrence:
                context.abort(grpc.StatusCode.NOT_FOUND,
                              errors.EVENT_NOT_FOUND)

            occurrences = select(EventOccurrence).where(
                EventOccurrence.event_id == Event.id)

            if not request.past:
                occurrences = occurrences.where(
                    EventOccurrence.end_time > page_token -
                    timedelta(seconds=1)).order_by(
                        EventOccurrence.start_time.asc())
            else:
                occurrences = occurrences.where(
                    EventOccurrence.end_time < page_token +
                    timedelta(seconds=1)).order_by(
                        EventOccurrence.start_time.desc())

            occurrences = occurrences.limit(page_size + 1)
            occurrences = session.execute(occurrences).scalars().all()

            return events_pb2.ListEventOccurrencesRes(
                events=[
                    event_to_pb(session, occurrence, context)
                    for occurrence in occurrences[:page_size]
                ],
                next_page_token=str(millis_from_dt(occurrences[-1].end_time))
                if len(occurrences) > page_size else None,
            )
Exemplo n.º 10
0
    def UnblockUser(self, request, context):
        with session_scope() as session:
            blockee = session.execute(
                select(User).where(User.is_visible).where(
                    User.username == request.username)).scalar_one_or_none()

            if not blockee:
                context.abort(grpc.StatusCode.NOT_FOUND, errors.USER_NOT_FOUND)

            user_block = session.execute(
                select(UserBlock).where(
                    UserBlock.blocking_user_id == context.user_id).where(
                        UserBlock.blocked_user_id ==
                        blockee.id)).scalar_one_or_none()
            if not user_block:
                context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                              errors.USER_NOT_BLOCKED)

            session.delete(user_block)
            session.commit()

        return empty_pb2.Empty()
Exemplo n.º 11
0
def _search_pages(session, search_statement, title_only, next_rank, page_size,
                  context, include_places, include_guides):
    rank, snippet, execute_search_statement = _gen_search_elements(
        search_statement,
        title_only,
        next_rank,
        page_size,
        [PageVersion.title],
        [PageVersion.address],
        [],
        [PageVersion.content],
    )
    if not include_places and not include_guides:
        return []

    latest_pages = (select(func.max(PageVersion.id).label("id")).join(
        Page, Page.id == PageVersion.page_id).where(
            or_(
                (Page.type == PageType.place) if include_places else False,
                (Page.type == PageType.guide) if include_guides else False,
            )).group_by(PageVersion.page_id).subquery())

    pages = execute_search_statement(
        session,
        select(Page, rank,
               snippet).join(PageVersion, PageVersion.page_id == Page.id).join(
                   latest_pages, latest_pages.c.id == PageVersion.id),
    )

    return [
        search_pb2.Result(
            rank=rank,
            place=page_to_pb(page, context)
            if page.type == PageType.place else None,
            guide=page_to_pb(page, context)
            if page.type == PageType.guide else None,
            snippet=snippet,
        ) for page, rank, snippet in pages
    ]
Exemplo n.º 12
0
def handle_notification(notification_id):
    with session_scope() as session:
        notification = session.execute(
            select(Notification).where(
                Notification.id == notification_id)).scalar_one()

        # ignore this notification if the user hasn't enabled new notifications
        user = session.execute(
            select(User).where(User.id == notification.user_id)).scalar_one()
        if not user.new_notifications_enabled:
            logger.info(
                f"Skipping notification for {user} due to new notifications disabled"
            )
            return

        topic, action = notification.topic_action.unpack()
        delivery_types = get_notification_preference(session,
                                                     notification.user.id,
                                                     notification.topic_action)
        for delivery_type in delivery_types:
            logger.info(f"Should notify by {delivery_type}")
            if delivery_type == NotificationDeliveryType.email:
                # for emails we don't deliver straight up, wait until the email background worker gets around to it and handles deduplication
                session.add(
                    NotificationDelivery(
                        notification_id=notification.id,
                        delivered=None,
                        delivery_type=NotificationDeliveryType.email,
                    ))
            elif delivery_type == NotificationDeliveryType.push:
                # for push notifications, we send them straight away
                session.add(
                    NotificationDelivery(
                        notification_id=notification.id,
                        delivered=func.now(),
                        delivery_type=NotificationDeliveryType.push,
                    ))
                # todo
                logger.info("Supposed to send push notification")
Exemplo n.º 13
0
def test_full_delete_account_with_recovery(db):
    user, token = generate_user()
    user_id = user.id

    with account_session(token) as account:
        with pytest.raises(grpc.RpcError) as e:
            account.DeleteAccount(account_pb2.DeleteAccountReq())
        assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
        assert e.value.details() == errors.MUST_CONFIRM_ACCOUNT_DELETE

        # Check the right email is sent
        with patch("couchers.email.queue_email") as mock:
            account.DeleteAccount(account_pb2.DeleteAccountReq(confirm=True))
        mock.assert_called_once()
        (_, _, _, subject, _, _), _ = mock.call_args
        assert subject == "[TEST] Confirm your Couchers.org account deletion"

    with session_scope() as session:
        token_o = session.execute(select(AccountDeletionToken)).scalar_one()
        token = token_o.token

        user = session.execute(
            select(User).where(User.id == user_id)).scalar_one()
        assert token_o.user == user
        assert not user.is_deleted
        assert not user.undelete_token
        assert not user.undelete_until

    with auth_api_session() as (auth_api, metadata_interceptor):
        auth_api.ConfirmDeleteAccount(
            auth_pb2.ConfirmDeleteAccountReq(token=token, ))

    with session_scope() as session:
        assert not session.execute(
            select(AccountDeletionToken)).scalar_one_or_none()

    with session_scope() as session:
        user = session.execute(
            select(User).where(User.id == user_id)).scalar_one()
        assert user.is_deleted
        assert user.undelete_token
        assert user.undelete_until > now()

        undelete_token = user.undelete_token

    with auth_api_session() as (auth_api, metadata_interceptor):
        auth_api.RecoverAccount(
            auth_pb2.RecoverAccountReq(token=undelete_token, ))

    with session_scope() as session:
        assert not session.execute(
            select(AccountDeletionToken)).scalar_one_or_none()

        user = session.execute(
            select(User).where(User.id == user_id)).scalar_one()
        assert not user.is_deleted
        assert not user.undelete_token
        assert not user.undelete_until
Exemplo n.º 14
0
    def CreateDiscussion(self, request, context):
        if not request.title:
            context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                          errors.MISSING_DISCUSSION_TITLE)
        if not request.content:
            context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                          errors.MISSING_DISCUSSION_CONTENT)
        if not request.owner_community_id and not request.owner_group_id:
            context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                          errors.GROUP_OR_COMMUNITY_NOT_FOUND)

        with session_scope() as session:
            if request.WhichOneof("owner") == "owner_group_id":
                cluster = session.execute(
                    select(Cluster).where(~Cluster.is_official_cluster).where(
                        Cluster.id ==
                        request.owner_group_id)).scalar_one_or_none()
            elif request.WhichOneof("owner") == "owner_community_id":
                cluster = session.execute(
                    select(Cluster).where(
                        Cluster.parent_node_id ==
                        request.owner_community_id).where(
                            Cluster.is_official_cluster)).scalar_one_or_none()

            if not cluster:
                context.abort(grpc.StatusCode.NOT_FOUND,
                              errors.GROUP_OR_COMMUNITY_NOT_FOUND)

            discussion = Discussion(
                title=request.title,
                content=request.content,
                creator_user_id=context.user_id,
                owner_cluster=cluster,
                thread=Thread(),
            )
            session.add(discussion)
            session.commit()
            return discussion_to_pb(discussion, context)
Exemplo n.º 15
0
def test_process_send_onboarding_emails(db):
    # needs to get first onboarding email
    user1, token1 = generate_user(onboarding_emails_sent=0,
                                  last_onboarding_email_sent=None)

    process_send_onboarding_emails(empty_pb2.Empty())

    with session_scope() as session:
        assert (session.execute(
            select(func.count()).select_from(BackgroundJob).where(
                BackgroundJob.job_type ==
                BackgroundJobType.send_email)).scalar_one() == 1)

    # needs to get second onboarding email, but not yet
    user2, token2 = generate_user(onboarding_emails_sent=1,
                                  last_onboarding_email_sent=now() -
                                  timedelta(days=6))

    process_send_onboarding_emails(empty_pb2.Empty())

    with session_scope() as session:
        assert (session.execute(
            select(func.count()).select_from(BackgroundJob).where(
                BackgroundJob.job_type ==
                BackgroundJobType.send_email)).scalar_one() == 1)

    # needs to get second onboarding email
    user3, token3 = generate_user(onboarding_emails_sent=1,
                                  last_onboarding_email_sent=now() -
                                  timedelta(days=8))

    process_send_onboarding_emails(empty_pb2.Empty())

    with session_scope() as session:
        assert (session.execute(
            select(func.count()).select_from(BackgroundJob).where(
                BackgroundJob.job_type ==
                BackgroundJobType.send_email)).scalar_one() == 2)
Exemplo n.º 16
0
    def TransferEvent(self, request, context):
        with session_scope() as session:
            res = session.execute(
                select(Event, EventOccurrence).where(
                    EventOccurrence.id == request.event_id).where(
                        EventOccurrence.event_id == Event.id)).one_or_none()

            if not res:
                context.abort(grpc.StatusCode.NOT_FOUND,
                              errors.EVENT_NOT_FOUND)

            event, occurrence = res

            if not _can_edit_event(session, event, context.user_id):
                context.abort(grpc.StatusCode.PERMISSION_DENIED,
                              errors.EVENT_TRANSFER_PERMISSION_DENIED)

            if request.WhichOneof("new_owner") == "new_owner_group_id":
                cluster = session.execute(
                    select(Cluster).where(~Cluster.is_official_cluster).where(
                        Cluster.id ==
                        request.new_owner_group_id)).scalar_one_or_none()
            elif request.WhichOneof("new_owner") == "new_owner_community_id":
                cluster = session.execute(
                    select(Cluster).where(
                        Cluster.parent_node_id ==
                        request.new_owner_community_id).where(
                            Cluster.is_official_cluster)).scalar_one_or_none()

            if not cluster:
                context.abort(grpc.StatusCode.NOT_FOUND,
                              errors.GROUP_OR_COMMUNITY_NOT_FOUND)

            event.owner_user = None
            event.owner_cluster = cluster

            session.commit()
            return event_to_pb(session, occurrence, context)
Exemplo n.º 17
0
def test_ChangeEmail_tokens_two_hour_window(db):
    def two_hours_one_minute_in_future():
        return now() + timedelta(hours=2, minutes=1)

    def one_minute_ago():
        return now() - timedelta(minutes=1)

    new_email = f"{random_hex()}@couchers.org.invalid"
    user, token = generate_user(hashed_password=None)

    with account_session(token) as account:
        account.ChangeEmail(account_pb2.ChangeEmailReq(new_email=new_email, ))

    with session_scope() as session:
        user = session.execute(
            select(User).where(User.id == user.id)).scalar_one()
        old_email_token = user.old_email_token
        new_email_token = user.new_email_token

    with patch("couchers.servicers.auth.now", one_minute_ago):
        with auth_api_session() as (auth_api, metadata_interceptor):
            with pytest.raises(grpc.RpcError) as e:
                auth_api.ConfirmChangeEmail(
                    auth_pb2.ConfirmChangeEmailReq(
                        change_email_token=old_email_token, ))
            assert e.value.code() == grpc.StatusCode.NOT_FOUND
            assert e.value.details() == errors.INVALID_TOKEN

            with pytest.raises(grpc.RpcError) as e:
                auth_api.ConfirmChangeEmail(
                    auth_pb2.ConfirmChangeEmailReq(
                        change_email_token=new_email_token, ))
            assert e.value.code() == grpc.StatusCode.NOT_FOUND
            assert e.value.details() == errors.INVALID_TOKEN

    with patch("couchers.servicers.auth.now", two_hours_one_minute_in_future):
        with auth_api_session() as (auth_api, metadata_interceptor):
            with pytest.raises(grpc.RpcError) as e:
                auth_api.ConfirmChangeEmail(
                    auth_pb2.ConfirmChangeEmailReq(
                        change_email_token=old_email_token, ))
            assert e.value.code() == grpc.StatusCode.NOT_FOUND
            assert e.value.details() == errors.INVALID_TOKEN

            with pytest.raises(grpc.RpcError) as e:
                auth_api.ConfirmChangeEmail(
                    auth_pb2.ConfirmChangeEmailReq(
                        change_email_token=new_email_token, ))
            assert e.value.code() == grpc.StatusCode.NOT_FOUND
            assert e.value.details() == errors.INVALID_TOKEN
Exemplo n.º 18
0
def test_select_dot_where_users_visible(db):
    user1, token1 = generate_user()
    user2, token2 = generate_user(delete_user=True)
    user3, token3 = generate_user()
    user4, token4 = generate_user()

    make_user_block(user1, user3)
    make_user_block(user4, user1)

    context = _FakeContext(user1.id)
    with session_scope() as session:
        assert session.execute(
            select(func.count()).select_from(User).where_users_visible(
                context)).scalar_one() == 1
Exemplo n.º 19
0
 def ListNearbyUsers(self, request, context):
     with session_scope() as session:
         page_size = min(MAX_PAGINATION_LENGTH, request.page_size or MAX_PAGINATION_LENGTH)
         next_nearby_id = int(request.page_token) if request.page_token else 0
         node = session.execute(select(Node).where(Node.id == request.community_id)).scalar_one_or_none()
         if not node:
             context.abort(grpc.StatusCode.NOT_FOUND, errors.COMMUNITY_NOT_FOUND)
         nearbys = (
             session.execute(
                 select(User)
                 .where_users_visible(context)
                 .where(func.ST_Contains(node.geom, User.geom))
                 .where(User.id >= next_nearby_id)
                 .order_by(User.id)
                 .limit(page_size + 1)
             )
             .scalars()
             .all()
         )
         return communities_pb2.ListNearbyUsersRes(
             nearby_user_ids=[nearby.id for nearby in nearbys[:page_size]],
             next_page_token=str(nearbys[-1].id) if len(nearbys) > page_size else None,
         )
Exemplo n.º 20
0
def enforce_community_memberships_for_user(session, user):
    """
    Adds a given user to all the communities they belong in based on their location.
    """
    nodes = session.execute(
        select(Node).where(func.ST_Contains(Node.geom,
                                            user.geom))).scalars().all()
    for node in nodes:
        node.official_cluster.cluster_subscriptions.append(
            ClusterSubscription(
                user=user,
                role=ClusterRole.member,
            ))
    session.commit()
Exemplo n.º 21
0
def test_ChangePassword_add_no_passwords(db, fast_passwords):
    # user does not have an old password and called with empty body
    user, token = generate_user(hashed_password=None)

    with account_session(token) as account:
        with pytest.raises(grpc.RpcError) as e:
            account.ChangePassword(account_pb2.ChangePasswordReq())
        assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
        assert e.value.details() == errors.MISSING_BOTH_PASSWORDS

    with session_scope() as session:
        updated_user = session.execute(
            select(User).where(User.id == user.id)).scalar_one()
        assert updated_user.hashed_password == None
Exemplo n.º 22
0
def get_node_parents_recursively(session, node_id):
    """
    Gets the upwards hierarchy of parents, ordered by level, for a given node

    Returns SQLAlchemy rows of (node_id, parent_node_id, level, cluster)
    """
    parents = (select(Node.id, Node.parent_node_id,
                      literal(0).label("level")).where(Node.id == node_id).cte(
                          "parents", recursive=True))

    subquery = select(
        parents.union(
            select(Node.id, Node.parent_node_id,
                   (parents.c.level + 1).label("level")).join(
                       parents,
                       Node.id == parents.c.parent_node_id))).subquery()

    return session.execute(
        select(subquery,
               Cluster).join(Cluster,
                             Cluster.parent_node_id == subquery.c.id).where(
                                 Cluster.is_official_cluster).order_by(
                                     subquery.c.level.desc())).all()
Exemplo n.º 23
0
    def SetEventAttendance(self, request, context):
        with session_scope() as session:
            occurrence = session.execute(
                select(EventOccurrence).where(EventOccurrence.id == request.
                                              event_id)).scalar_one_or_none()

            if not occurrence:
                context.abort(grpc.StatusCode.NOT_FOUND,
                              errors.EVENT_NOT_FOUND)

            current_attendance = session.execute(
                select(EventOccurrenceAttendee).where(
                    EventOccurrenceAttendee.user_id == context.user_id).where(
                        EventOccurrenceAttendee.occurrence_id ==
                        occurrence.id)).scalar_one_or_none()

            if request.attendance_state == events_pb2.ATTENDANCE_STATE_NOT_GOING:
                if current_attendance:
                    session.delete(current_attendance)
                # if unset/not going, nothing to do!
            else:
                if current_attendance:
                    current_attendance.attendee_status = attendancestate2sql[
                        request.attendance_state]
                else:
                    # create new
                    attendance = EventOccurrenceAttendee(
                        user_id=context.user_id,
                        occurrence_id=occurrence.id,
                        attendee_status=attendancestate2sql[
                            request.attendance_state],
                    )
                    session.add(attendance)

            session.flush()

            return event_to_pb(session, occurrence, context)
Exemplo n.º 24
0
    def ListFriendRequests(self, request, context):
        # both sent and received
        with session_scope() as session:
            sent_requests = (session.execute(
                select(FriendRelationship).where_users_column_visible(
                    context, FriendRelationship.to_user_id).where(
                        FriendRelationship.from_user_id == context.user_id).
                where(FriendRelationship.status ==
                      FriendStatus.pending)).scalars().all())

            received_requests = (session.execute(
                select(FriendRelationship).where_users_column_visible(
                    context, FriendRelationship.from_user_id).where(
                        FriendRelationship.to_user_id == context.user_id).
                where(FriendRelationship.status ==
                      FriendStatus.pending)).scalars().all())

            return api_pb2.ListFriendRequestsRes(
                sent=[
                    api_pb2.FriendRequest(
                        friend_request_id=friend_request.id,
                        state=api_pb2.FriendRequest.FriendRequestStatus.
                        PENDING,
                        user_id=friend_request.to_user.id,
                        sent=True,
                    ) for friend_request in sent_requests
                ],
                received=[
                    api_pb2.FriendRequest(
                        friend_request_id=friend_request.id,
                        state=api_pb2.FriendRequest.FriendRequestStatus.
                        PENDING,
                        user_id=friend_request.from_user.id,
                        sent=False,
                    ) for friend_request in received_requests
                ],
            )
Exemplo n.º 25
0
    def LeaveGroupChat(self, request, context):
        with session_scope() as session:
            subscription = session.execute(
                select(GroupChatSubscription)
                .where(GroupChatSubscription.group_chat_id == request.group_chat_id)
                .where(GroupChatSubscription.user_id == context.user_id)
                .where(GroupChatSubscription.left == None)
            ).scalar_one_or_none()

            if not subscription:
                context.abort(grpc.StatusCode.NOT_FOUND, errors.CHAT_NOT_FOUND)

            if subscription.role == GroupChatRole.admin:
                other_admins_count = session.execute(
                    select(func.count())
                    .select_from(GroupChatSubscription)
                    .where(GroupChatSubscription.group_chat_id == request.group_chat_id)
                    .where(GroupChatSubscription.user_id != context.user_id)
                    .where(GroupChatSubscription.role == GroupChatRole.admin)
                    .where(GroupChatSubscription.left == None)
                ).scalar_one()
                participants_count = session.execute(
                    select(func.count())
                    .select_from(GroupChatSubscription)
                    .where(GroupChatSubscription.group_chat_id == request.group_chat_id)
                    .where(GroupChatSubscription.user_id != context.user_id)
                    .where(GroupChatSubscription.role == GroupChatRole.participant)
                    .where(GroupChatSubscription.left == None)
                ).scalar_one()
                if not (other_admins_count > 0 or participants_count == 0):
                    context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.LAST_ADMIN_CANT_LEAVE)

            _add_message_to_subscription(session, subscription, message_type=MessageType.user_left)

            subscription.left = func.now()

        return empty_pb2.Empty()
Exemplo n.º 26
0
    def ReportBug(self, request, context):
        if not config["BUG_TOOL_ENABLED"]:
            context.abort(grpc.StatusCode.UNAVAILABLE, "Bug tool disabled")

        repo = config["BUG_TOOL_GITHUB_REPO"]
        auth = (config["BUG_TOOL_GITHUB_USERNAME"],
                config["BUG_TOOL_GITHUB_TOKEN"])

        if context.user_id:
            with session_scope() as session:
                username = session.execute(
                    select(User.username).where(
                        User.id == context.user_id)).scalar_one()
                user_details = f"{username} ({context.user_id})"
        else:
            user_details = "<not logged in>"

        issue_title = request.subject
        issue_body = (f"Subject: {request.subject}\n"
                      f"Description:\n"
                      f"{request.description}\n"
                      f"\n"
                      f"Results:\n"
                      f"{request.results}\n"
                      f"\n"
                      f"Backend version: {self._version()}\n"
                      f"Frontend version: {request.frontend_version}\n"
                      f"User Agent: {request.user_agent}\n"
                      f"Page: {request.page}\n"
                      f"User: {user_details}")
        issue_labels = ["bug tool"]

        json_body = {
            "title": issue_title,
            "body": issue_body,
            "labels": issue_labels
        }

        r = requests.post(f"https://api.github.com/repos/{repo}/issues",
                          auth=auth,
                          json=json_body)
        if not r.status_code == 201:
            context.abort(grpc.StatusCode.INTERNAL, "Request failed")

        issue_number = r.json()["number"]

        return bugs_pb2.ReportBugRes(
            bug_id=f"#{issue_number}",
            bug_url=f"https://github.com/{repo}/issues/{issue_number}")
Exemplo n.º 27
0
    def ChangePhone(self, request, context):
        phone = request.phone
        # early quick validation
        if phone and not is_e164_format(phone):
            context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                          errors.INVALID_PHONE)

        with session_scope() as session:
            user = session.execute(
                select(User).where(User.id == context.user_id)).scalar_one()
            if not phone:
                user.phone = None
                user.phone_verification_verified = None
                user.phone_verification_token = None
                user.phone_verification_attempts = 0
                return empty_pb2.Empty()

            if not is_known_operator(phone):
                context.abort(grpc.StatusCode.UNIMPLEMENTED,
                              errors.UNRECOGNIZED_PHONE_NUMBER)

            if now(
            ) - user.phone_verification_sent < PHONE_REVERIFICATION_INTERVAL:
                context.abort(grpc.StatusCode.RESOURCE_EXHAUSTED,
                              errors.REVERIFICATION_TOO_EARLY)

            token = sms.generate_random_code()
            result = sms.send_sms(phone, sms.format_message(token))

            if result == "success":
                user.phone = phone
                user.phone_verification_verified = None
                user.phone_verification_token = token
                user.phone_verification_sent = now()
                user.phone_verification_attempts = 0

                notify(
                    user_id=user.id,
                    topic="phone_number",
                    key="",
                    action="change",
                    icon="wrench",
                    title=f"Your phone number was changed",
                    link=urls.account_settings_link(),
                )

                return empty_pb2.Empty()

        context.abort(grpc.StatusCode.UNIMPLEMENTED, result)
Exemplo n.º 28
0
def test_UnblockUser(db):
    user1, token1 = generate_user()
    user2, token2 = generate_user()
    make_user_block(user1, user2)

    with blocking_session(token1) as user_blocks:
        user_blocks.UnblockUser(blocking_pb2.UnblockUserReq(username=user2.username))

    with session_scope() as session:
        blocked_users = session.execute(select(UserBlock).where(UserBlock.blocking_user_id == user1.id)).scalars().all()
        assert len(blocked_users) == 0

    with blocking_session(token1) as user_blocks:
        with pytest.raises(grpc.RpcError) as e:
            user_blocks.UnblockUser(blocking_pb2.UnblockUserReq(username=user2.username))
        assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
        assert e.value.details() == errors.USER_NOT_BLOCKED

        # Test re-blocking
        user_blocks.BlockUser(blocking_pb2.BlockUserReq(username=user2.username))

    with session_scope() as session:
        blocked_users = session.execute(select(UserBlock).where(UserBlock.blocking_user_id == user1.id)).scalars().all()
        assert len(blocked_users) == 1
Exemplo n.º 29
0
def test_banned_user(db):
    _quick_signup()
    with auth_api_session() as (auth_api, metadata_interceptor):
        reply = auth_api.Login(auth_pb2.LoginReq(user="******"))
    assert reply.next_step == auth_pb2.LoginRes.LoginStep.NEED_PASSWORD

    with session_scope() as session:
        session.execute(select(User)).scalar_one().is_banned = True

    with auth_api_session() as (auth_api, metadata_interceptor):
        with pytest.raises(grpc.RpcError) as e:
            auth_api.Authenticate(
                auth_pb2.AuthReq(user="******",
                                 password="******"))
        assert e.value.details() == "Your account is suspended."
Exemplo n.º 30
0
def test_login_tokens_invalidate_after_use(db):
    _quick_signup()

    with session_scope() as session:
        user = session.execute(select(User)).scalar_one()
        user.hashed_password = None

    with auth_api_session() as (auth_api, metadata_interceptor):
        reply = auth_api.Login(auth_pb2.LoginReq(user="******"))
    assert reply.next_step == auth_pb2.LoginRes.LoginStep.SENT_LOGIN_EMAIL

    with session_scope() as session:
        login_token = session.execute(select(LoginToken)).scalar_one().token

    with auth_api_session() as (auth_api, metadata_interceptor):
        auth_api.CompleteTokenLogin(
            auth_pb2.CompleteTokenLoginReq(login_token=login_token))
    session_token = get_session_cookie_token(metadata_interceptor)

    with auth_api_session() as (auth_api, metadata_interceptor), pytest.raises(
            grpc.RpcError):
        # check we can't login again
        auth_api.CompleteTokenLogin(
            auth_pb2.CompleteTokenLoginReq(login_token=login_token))