Beispiel #1
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
Beispiel #2
0
    def SSO(self, request, context):
        # Protocol description: https://meta.discourse.org/t/official-single-sign-on-for-discourse-sso/13045
        with session_scope(self._Session) as session:
            sso = request.sso
            sig = request.sig

            logger.info(
                f"Doing SSO login for {context.user_id=}, {sso=}, {sig=}")

            # TODO: secrets management, this is from sso-test instance
            hmac_sec = "b26c7ff6aa391b6a2ba2c0ad18cc6eae40c1a72e5355f86b7b35a4200b514709"

            if not sso_check_hmac(sso, hmac_sec, sig):
                context.abort(grpc.StatusCode.UNAUTHENTICATED,
                              errors.SSO_SIGNATURE_FAILED)

            # grab data from the "sso" string
            decoded_sso = base64decode(unquote(sso))
            parsed_query_string = parse_qs(decoded_sso)

            logger.info(f"SSO {parsed_query_string=}")

            nonce = parsed_query_string["nonce"][0]
            return_sso_url = parsed_query_string["return_sso_url"][0]

            user = session.query(User).filter(User.id == context.user_id).one()

            payload = {
                "nonce": nonce,
                "email": user.email,
                "external_id": user.id,
                "username": user.username,
                "name": user.name,
                # "admin": False
            }

            logger.info(f"SSO {payload=}")

            encoded_payload = base64encode(urlencode(payload))
            payload_sig = sso_create_hmac(encoded_payload, hmac_sec)

            query_string = urlencode({
                "sso": encoded_payload,
                "sig": payload_sig
            })

            redirect_url = f"{return_sso_url}?{query_string}"
            logger.info(f"SSO {redirect_url=}")

            return sso_pb2.SSORes(redirect_url=redirect_url)
Beispiel #3
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
Beispiel #4
0
 def format_conversation(conversation_id):
     out = ""
     with session_scope() as session:
         messages = (session.execute(
             select(Message).where(
                 Message.conversation_id ==
                 conversation_id).order_by(
                     Message.id.asc())).scalars().all())
         for message in messages:
             out += f"Message {message.id} by {format_user(message.author)} at {message.time}\nType={message.message_type}, host_req_status_change={message.host_request_status_target}\n\n"
             out += str(message.text)
             out += "\n\n-----\n"
         out += "\n\n\n\n"
     return out
Beispiel #5
0
def unsubscribe(request, context):
    """
    Returns a response string or uses context.abort upon error
    """
    if not verify_hash_signature(message=request.payload,
                                 key=get_secret(UNSUBSCRIBE_KEY_NAME),
                                 sig=request.sig):
        context.abort(grpc.StatusCode.PERMISSION_DENIED,
                      errors.WRONG_SIGNATURE)
    payload = unsubscribe_pb2.UnsubscribePayload.FromString(request.payload)
    with session_scope() as session:
        user = session.execute(
            select(User).where(User.id == payload.user_id)).scalar_one()
        if payload.HasField("all"):
            logger.info(f"User {user.name} unsubscribing from all")
            # todo: some other system when out of preview
            user.new_notifications_enabled = False
            return "You've been unsubscribed from all non-security notifications"
        if payload.HasField("topic_action"):
            logger.info(f"User {user.name} unsubscribing from topic_action")
            topic = payload.topic_action.topic
            action = payload.topic_action.action
            topic_action = enum_from_topic_action[topic, action]
            # disable emails for this type
            settings.set_preference(session, user.id, topic_action,
                                    NotificationDeliveryType.email, False)
            return "You've been unsubscribed from all email notifications of that type"
        if payload.HasField("topic_key"):
            logger.info(f"User {user.name} unsubscribing from topic_key")
            topic = payload.topic_key.topic
            key = payload.topic_key.key
            # a bunch of manual stuff
            if topic == "chat":
                group_chat_id = int(key)
                subscription = session.execute(
                    select(GroupChatSubscription).where(
                        GroupChatSubscription.group_chat_id == group_chat_id).
                    where(GroupChatSubscription.user_id == user.id).where(
                        GroupChatSubscription.left ==
                        None)).scalar_one_or_none()

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

                subscription.muted_until = DATETIME_INFINITY
                return "That group chat has been muted."
            else:
                context.abort(grpc.StatusCode.UNIMPLEMENTED,
                              errors.CANT_UNSUB_TOPIC)
Beispiel #6
0
    def CreateCommunity(self, request, context):
        with session_scope() as session:
            geom = shape(json.loads(request.geojson))

            if geom.type != "MultiPolygon":
                context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                              errors.NO_MULTIPOLYGON)

            parent_node_id = request.parent_node_id if request.parent_node_id != 0 else None
            node = create_node(session, geom, parent_node_id)
            create_cluster(session, node.id, request.name, request.description,
                           context.user_id, request.admin_ids, True)

            return community_to_pb(node, context)
Beispiel #7
0
    def _delete_session(self, token):
        """
        Deletes the given session (practically logging the user out)

        Returns True if the session was found, False otherwise.
        """
        with session_scope(self._Session) as session:
            user_session = session.query(UserSession).filter(UserSession.token == token).one_or_none()
            if user_session:
                session.delete(user_session)
                session.commit()
                return True
            else:
                return False
Beispiel #8
0
def test_ChangeEmail_wrong_token(db, fast_passwords):
    password = random_hex()
    new_email = f"{random_hex()}@couchers.org.invalid"
    user, token = generate_user(hashed_password=hash_password(password))

    with account_session(token) as account:
        account.ChangeEmail(
            account_pb2.ChangeEmailReq(
                password=wrappers_pb2.StringValue(value=password),
                new_email=new_email,
            )
        )

    with session_scope() as session:
        user_updated = (
            session.query(User)
            .filter(User.id == user.id)
            .filter(User.new_email == new_email)
            .filter(User.new_email_token_created <= func.now())
            .filter(User.new_email_token_expiry >= func.now())
        ).one()

        token = user_updated.new_email_token

    with auth_api_session() as (auth_api, metadata_interceptor):
        with pytest.raises(grpc.RpcError) as e:
            res = auth_api.CompleteChangeEmail(
                auth_pb2.CompleteChangeEmailReq(
                    change_email_token="wrongtoken",
                )
            )
        assert e.value.code() == grpc.StatusCode.NOT_FOUND
        assert e.value.details() == errors.INVALID_TOKEN

    with session_scope() as session:
        user_updated2 = session.query(User).filter(User.id == user.id).one()
        assert user_updated2.email == user.email
Beispiel #9
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}")
Beispiel #10
0
def test_threads_num_responses(db):
    user1, token1 = generate_user()

    # Create a dummy Thread (should be replaced by pages later on)
    with session_scope() as session:
        dummy_thread = Thread()
        session.add(dummy_thread)
        session.flush()
        PARENT_THREAD_ID = pack_thread_id(database_id=dummy_thread.id, depth=0)

    with threads_session(token1) as api:
        # add some comments and replies to the dummy Thread
        bat_id = api.PostReply(
            threads_pb2.PostReplyReq(thread_id=PARENT_THREAD_ID,
                                     content="bat")).thread_id

        cat_id = api.PostReply(
            threads_pb2.PostReplyReq(thread_id=PARENT_THREAD_ID,
                                     content="cat")).thread_id

        dog_id = api.PostReply(
            threads_pb2.PostReplyReq(thread_id=PARENT_THREAD_ID,
                                     content="dog")).thread_id

        dogs = [
            api.PostReply(
                threads_pb2.PostReplyReq(thread_id=dog_id,
                                         content=animal)).thread_id
            for animal in ["hyena", "wolf", "prairie wolf"]
        ]
        cats = [
            api.PostReply(
                threads_pb2.PostReplyReq(thread_id=cat_id,
                                         content=animal)).thread_id
            for animal in ["cheetah", "lynx", "panther"]
        ]

        # test num_responses
        ret = api.GetThread(
            threads_pb2.GetThreadReq(thread_id=PARENT_THREAD_ID))
        assert ret.num_responses == 9

        ret = api.GetThread(threads_pb2.GetThreadReq(thread_id=dog_id))
        assert ret.num_responses == 3

        # test num_responses with page_size < num_responses
        ret = api.GetThread(
            threads_pb2.GetThreadReq(thread_id=PARENT_THREAD_ID, page_size=1))
        assert ret.num_responses == 9
Beispiel #11
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)
Beispiel #12
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))
Beispiel #13
0
 def SignupTokenInfo(self, request, context):
     """
     Returns the email for a given SignupToken (which will be shown on the UI on the singup form).
     """
     logging.debug(f"Signup token info for {request.signup_token=}")
     with session_scope(self._Session) as session:
         signup_token = session.query(SignupToken) \
             .filter(SignupToken.token == request.signup_token) \
             .filter(SignupToken.created <= func.now()) \
             .filter(SignupToken.expiry >= func.now()) \
             .one_or_none()
         if not signup_token:
             context.abort(grpc.StatusCode.NOT_FOUND, "Invalid token.")
         else:
             return auth_pb2.SignupTokenInfoRes(email=signup_token.email)
Beispiel #14
0
def process_send_email(payload):
    logger.info(f"Sending email with subject '{payload.subject}' to '{payload.recipient}'")
    # selects a "sender", which either prints the email to the logger or sends it out with SMTP
    sender = send_smtp_email if config.config["ENABLE_EMAIL"] else print_dev_email
    # the sender must return a models.Email object that can be added to the database
    email = sender(
        sender_name=payload.sender_name,
        sender_email=payload.sender_email,
        recipient=payload.recipient,
        subject=payload.subject,
        plain=payload.plain,
        html=payload.html,
    )
    with session_scope() as session:
        session.add(email)
Beispiel #15
0
def test_ChangePassword_normal_no_passwords(db, fast_passwords):
    # user has old password and called with empty body
    old_password = random_hex()
    user, token = generate_user(db,
                                hashed_password=hash_password(old_password))

    with account_session(db, 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(db) as session:
        updated_user = session.query(User).filter(User.id == user.id).one()
        assert updated_user.hashed_password == hash_password(old_password)
Beispiel #16
0
def test_DeleteUser(db):
    with session_scope() as session:
        super_user, super_token = generate_user(is_superuser=True)
        normal_user, normal_token = generate_user()

        with real_admin_session(super_token) as api:
            res = api.DeleteUser(
                admin_pb2.DeleteUserReq(user=normal_user.username))
        assert res.user_id == normal_user.id
        assert res.username == normal_user.username
        assert res.email == normal_user.email
        assert res.gender == normal_user.gender
        assert parse_date(res.birthdate) == normal_user.birthdate
        assert not res.banned
        assert res.deleted
Beispiel #17
0
def get_friend_relationship(user1, user2):
    with session_scope() as session:
        friend_relationship = (
            session.query(FriendRelationship)
            .filter(
                or_(
                    (FriendRelationship.from_user_id == user1.id and FriendRelationship.to_user_id == user2.id),
                    (FriendRelationship.from_user_id == user2.id and FriendRelationship.to_user_id == user1.id),
                )
            )
            .one_or_none()
        )

        session.expunge(friend_relationship)
        return friend_relationship
Beispiel #18
0
    def ChangeEmail(self, request, context):
        """
        Change the user's email address.

        A notification is sent to the old email, and a confirmation is sent to the new one.

        The user then has to click on the confirmation email which actually changes the emails
        """
        # check password first
        with session_scope() as session:
            user = session.query(User).filter(User.id == context.user_id).one()
            _check_password(user, "password", request, context)

        # not a valid email
        if not is_valid_email(request.new_email):
            context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                          errors.INVALID_EMAIL)

        # email already in use (possibly by this user)
        with session_scope() as session:
            if session.query(User).filter(
                    User.email == request.new_email).one_or_none():
                context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                              errors.INVALID_EMAIL)

        with session_scope() as session:
            user = session.query(User).filter(User.id == context.user_id).one()

            # otherwise we're good
            user.new_email = request.new_email
            token, expiry_text = set_email_change_token(session, user)

            send_email_changed_notification_email(user)
            send_email_changed_confirmation_email(user, token, expiry_text)
            # session autocommit
        return empty_pb2.Empty()
Beispiel #19
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."
Beispiel #20
0
    def GetGuides(self, request, context):
        with session_scope() as session:
            latest_pages = (session.query(
                func.max(PageVersion.id).label("id")).join(
                    Page, Page.id == PageVersion.page_id).filter(
                        Page.type == PageType.guide).group_by(
                            PageVersion.page_id).subquery())

            query = (session.query(
                PageVersion.page_id.label("id"),
                PageVersion.slug.label("slug"), PageVersion.geom).join(
                    latest_pages, latest_pages.c.id == PageVersion.id).filter(
                        PageVersion.geom != None))

            return _query_to_geojson_response(session, query)
Beispiel #21
0
    def RemoveGroupChatAdmin(self, request, context):
        with session_scope(self._Session) as session:
            your_subscription = (session.query(GroupChatSubscription).filter(
                GroupChatSubscription.group_chat_id == request.group_chat_id
            ).filter(GroupChatSubscription.user_id == context.user_id).filter(
                GroupChatSubscription.left == None).one_or_none())

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

            if request.user_id == context.user_id:
                # Race condition!
                other_admins_count = (session.query(
                    GroupChatSubscription.id).filter(
                        GroupChatSubscription.group_chat_id ==
                        request.group_chat_id).filter(
                            GroupChatSubscription.user_id != context.user_id
                        ).filter(
                            GroupChatSubscription.role == GroupChatRole.admin
                        ).filter(GroupChatSubscription.left == None).count())
                if not other_admins_count > 0:
                    context.abort(grpc.StatusCode.FAILED_PRECONDITION,
                                  errors.CANT_REMOVE_LAST_ADMIN)

            if your_subscription.role != GroupChatRole.admin:
                context.abort(grpc.StatusCode.PERMISSION_DENIED,
                              errors.ONLY_ADMIN_CAN_REMOVE_ADMIN)

            their_subscription = (session.query(GroupChatSubscription).filter(
                GroupChatSubscription.group_chat_id == request.group_chat_id
            ).filter(GroupChatSubscription.user_id == request.user_id).filter(
                GroupChatSubscription.left == None).filter(
                    GroupChatSubscription.role ==
                    GroupChatRole.admin).one_or_none())

            if not their_subscription:
                context.abort(grpc.StatusCode.FAILED_PRECONDITION,
                              errors.USER_NOT_ADMIN)

            their_subscription.role = GroupChatRole.participant

            _add_message_to_subscription(
                session,
                your_subscription,
                message_type=MessageType.user_removed_admin,
                target_id=request.user_id)

        return empty_pb2.Empty()
Beispiel #22
0
    def GetHostRequestUpdates(self, request, context):
        if request.only_sent and request.only_received:
            context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.HOST_REQUEST_SENT_OR_RECEIVED)

        with session_scope(self._Session) as session:
            if request.newest_message_id == 0:
                context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_MESSAGE)

            newest_message = session.query(Message).filter(Message.id == request.newest_message_id).one_or_none()

            pagination = request.number if request.number > 0 else PAGINATION_LENGTH

            query = (
                session.query(
                    Message,
                    HostRequest.status.label("host_request_status"),
                    HostRequest.conversation_id.label("host_request_id"),
                )
                .join(HostRequest, HostRequest.conversation_id == Message.conversation_id)
                .filter(Message.id > request.newest_message_id)
            )

            if request.only_sent:
                query = query.filter(HostRequest.from_user_id == context.user_id)
            elif request.only_received:
                query = query.filter(HostRequest.to_user_id == context.user_id)
            else:
                query = query.filter(
                    or_(HostRequest.to_user_id == context.user_id, HostRequest.from_user_id == context.user_id)
                )

            query = query.order_by(Message.id.asc()).limit(pagination + 1).all()

            no_more = len(query) <= pagination

            next_message_id = min(map(lambda m: m.Message.id if m else 1, query)) - 1 if len(query) > 0 else 0  # TODO

            return requests_pb2.GetHostRequestUpdatesRes(
                no_more=no_more,
                updates=[
                    requests_pb2.HostRequestUpdate(
                        host_request_id=result.host_request_id,
                        status=hostrequeststatus2api[result.host_request_status],
                        message=message_to_pb(result.Message),
                    )
                    for result in query[:pagination]
                ],
            )
Beispiel #23
0
def test_host_request_email(db):
    with session_scope(db) as session:
        from_user, api_token_from = generate_user(db)
        to_user, api_token_to = generate_user(db)
        from_date = "2020-01-01"
        to_date = "2020-01-05"

        conversation = Conversation()
        message = Message()
        message.conversation_id = conversation.id
        message.author_id = from_user.id
        message.text = random_hex(64)

        host_request = HostRequest(
            conversation_id=conversation.id,
            from_user=from_user,
            to_user=to_user,
            from_date=from_date,
            to_date=to_date,
            status=HostRequestStatus.pending,
            from_last_seen_message_id=message.id,
        )

        message_id = random_hex(64)

        @create_autospec
        def mock_send_email(sender_name, sender_email, recipient, subject,
                            plain, html):
            assert recipient == to_user.email
            assert "host request" in subject.lower()
            assert to_user.name in plain
            assert to_user.name in html
            assert from_user.name in plain
            assert from_user.name in html
            assert from_date in plain
            assert from_date in html
            assert to_date in plain
            assert to_date in html
            assert from_user.avatar_url not in plain
            assert from_user.avatar_url in html
            assert f"{config['BASE_URL']}/hostrequests/" in plain
            assert f"{config['BASE_URL']}/hostrequests/" in html
            return message_id

        with patch("couchers.email.send_email", mock_send_email) as mock:
            send_host_request_email(host_request)

        assert mock.call_count == 1
Beispiel #24
0
    def CreateHostRequest(self, request, context):
        with session_scope(self._Session) as session:
            if request.to_user_id == context.user_id:
                context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.CANT_REQUEST_SELF)
            # just to check the host exists
            host = session.query(User).filter(User.id == request.to_user_id).one_or_none()
            if not host:
                context.abort(grpc.StatusCode.NOT_FOUND, errors.USER_NOT_FOUND)

            if not is_valid_date(request.from_date) or not is_valid_date(request.to_date):
                context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_DATE)

            if request.from_date >= request.to_date:
                context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_TO)

            if request.to_date < least_current_date():
                context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_TO_BEFORE_TODAY)

            conversation = Conversation()
            session.add(conversation)
            session.flush()

            created_message = Message()
            created_message.conversation_id = conversation.id
            created_message.author_id = context.user_id
            created_message.message_type = MessageType.chat_created
            session.add(created_message)
            session.commit()

            message = Message()
            message.conversation_id = conversation.id
            message.author_id = context.user_id
            message.text = request.text
            message.message_type = MessageType.text
            session.add(message)
            session.flush()

            host_request = HostRequest()
            host_request.conversation_id = conversation.id
            host_request.from_user_id = context.user_id
            host_request.to_user_id = host.id
            host_request.from_date = request.from_date
            host_request.to_date = request.to_date
            host_request.status = HostRequestStatus.pending
            host_request.from_last_seen_message_id = message.id
            session.add(host_request)

            return requests_pb2.CreateHostRequestRes(host_request_id=host_request.conversation_id)
Beispiel #25
0
def test_login_email(db):
    user, api_token = generate_user()

    with session_scope() as session:
        login_token, expiry_text = new_login_token(session, user)

        with patch("couchers.email.queue_email") as mock:
            send_login_email(user, login_token, expiry_text)

        assert mock.call_count == 1
        (sender_name, sender_email, recipient, subject, plain,
         html), _ = mock.call_args
        assert recipient == user.email
        assert "login" in subject.lower()
        assert login_token.token in plain
        assert login_token.token in html
Beispiel #26
0
    def GetPlaces(self, request, context):
        with session_scope() as session:
            # need to do a subquery here so we get pages without a geom, not just versions without geom
            latest_pages = (session.query(
                func.max(PageVersion.id).label("id")).join(
                    Page, Page.id == PageVersion.page_id).filter(
                        Page.type == PageType.place).group_by(
                            PageVersion.page_id).subquery())

            query = (session.query(
                PageVersion.page_id.label("id"),
                PageVersion.slug.label("slug"), PageVersion.geom).join(
                    latest_pages, latest_pages.c.id == PageVersion.id).filter(
                        PageVersion.geom != None))

            return _query_to_geojson_response(session, query)
Beispiel #27
0
 def ListFriends(self, request, context):
     with session_scope() as session:
         rels = (
             session.query(FriendRelationship)
             .filter(
                 or_(
                     FriendRelationship.from_user_id == context.user_id,
                     FriendRelationship.to_user_id == context.user_id,
                 )
             )
             .filter(FriendRelationship.status == FriendStatus.accepted)
             .all()
         )
         return api_pb2.ListFriendsRes(
             user_ids=[rel.from_user.id if rel.from_user.id != context.user_id else rel.to_user.id for rel in rels],
         )
Beispiel #28
0
def delete_session(token):
    """
    Deletes the given session (practically logging the user out)

    Returns True if the session was found, False otherwise.
    """
    with session_scope() as session:
        user_session = session.execute(
            select(UserSession).where(UserSession.token == token).where(
                UserSession.is_valid)).scalar_one_or_none()
        if user_session:
            user_session.deleted = func.now()
            session.commit()
            return True
        else:
            return False
Beispiel #29
0
 def SignupTokenInfo(self, request, context):
     """
     Returns the email for a given SignupToken (which will be shown on the UI on the singup form).
     """
     logger.debug(f"Signup token info for {request.signup_token=}")
     with session_scope() as session:
         signup_token = (
             session.query(SignupToken)
             .filter(SignupToken.token == request.signup_token)
             .filter(SignupToken.is_valid)
             .one_or_none()
         )
         if not signup_token:
             context.abort(grpc.StatusCode.NOT_FOUND, errors.INVALID_TOKEN)
         else:
             return auth_pb2.SignupTokenInfoRes(email=signup_token.email)
Beispiel #30
0
    def SetLocation(self, request, context):
        with session_scope() as session:
            user = session.execute(
                select(User).where(User.id == context.user_id)).scalar_one()

            if request.lat == 0 and request.lng == 0:
                context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                              errors.INVALID_COORDINATE)

            user.city = request.city
            user.geom = create_coordinate(request.lat, request.lng)
            user.geom_radius = request.radius

            session.commit()

            return self._get_jail_info(user)