def process_send_request_notifications(payload): """ Sends out email notifications for unseen messages in host requests (as surfer or host) """ logger.info(f"Sending out email notifications for unseen messages in host requests") with session_scope() as session: # requests where this user is surfing surfing_reqs = session.execute( select(User, HostRequest, func.max(Message.id)) .where(User.is_visible) .join(HostRequest, HostRequest.surfer_user_id == User.id) .join(Message, Message.conversation_id == HostRequest.conversation_id) .where(Message.id > HostRequest.surfer_last_seen_message_id) .where(Message.id > User.last_notified_request_message_id) .where(Message.time < now() - timedelta(minutes=5)) .where(Message.message_type == MessageType.text) .group_by(User, HostRequest) ).all() # where this user is hosting hosting_reqs = session.execute( select(User, HostRequest, func.max(Message.id)) .where(User.is_visible) .join(HostRequest, HostRequest.host_user_id == User.id) .join(Message, Message.conversation_id == HostRequest.conversation_id) .where(Message.id > HostRequest.host_last_seen_message_id) .where(Message.id > User.last_notified_request_message_id) .where(Message.time < now() - timedelta(minutes=5)) .where(Message.message_type == MessageType.text) .group_by(User, HostRequest) ).all() for user, host_request, max_message_id in surfing_reqs: user.last_notified_request_message_id = max(user.last_notified_request_message_id, max_message_id) session.commit() email.enqueue_email_from_template( user.email, "unseen_message_guest", template_args={ "user": user, "host_request": host_request, "host_request_link": urls.host_request_link_guest(), }, ) for user, host_request, max_message_id in hosting_reqs: user.last_notified_request_message_id = max(user.last_notified_request_message_id, max_message_id) session.commit() email.enqueue_email_from_template( user.email, "unseen_message_host", template_args={ "user": user, "host_request": host_request, "host_request_link": urls.host_request_link_host(), }, )
def test_list_host_requests_active_filter(db): user1, token1 = generate_user() user2, token2 = generate_user() today_plus_2 = (now() + timedelta(days=2)).strftime("%Y-%m-%d") today_plus_3 = (now() + timedelta(days=3)).strftime("%Y-%m-%d") with requests_session(token1) as api: request_id = api.CreateHostRequest( requests_pb2.CreateHostRequestReq( to_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1")).host_request_id api.RespondHostRequest( requests_pb2.RespondHostRequestReq( host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED)) with requests_session(token2) as api: res = api.ListHostRequests( requests_pb2.ListHostRequestsReq(only_received=True)) assert len(res.host_requests) == 1 res = api.ListHostRequests( requests_pb2.ListHostRequestsReq(only_active=True)) assert len(res.host_requests) == 0
def test_get_host_request(db): user1, token1 = generate_user() user2, token2 = generate_user() user3, token3 = generate_user() today_plus_2 = (now() + timedelta(days=2)).strftime("%Y-%m-%d") today_plus_3 = (now() + timedelta(days=3)).strftime("%Y-%m-%d") with requests_session(token1) as api: host_request_id = api.CreateHostRequest( requests_pb2.CreateHostRequestReq( to_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1")).host_request_id with pytest.raises(grpc.RpcError) as e: api.GetHostRequest( requests_pb2.GetHostRequestReq(host_request_id=999)) assert e.value.code() == grpc.StatusCode.NOT_FOUND api.SendHostRequestMessage( requests_pb2.SendHostRequestMessageReq( host_request_id=host_request_id, text="Test message 1")) res = api.GetHostRequest( requests_pb2.GetHostRequestReq(host_request_id=host_request_id)) assert res.latest_message.text.text == "Test message 1"
def ConfirmChangeEmail(self, request, context): with session_scope() as session: user_with_valid_token_from_old_email = session.execute( select(User).where( User.old_email_token == request.change_email_token).where( User.old_email_token_created <= now()).where( User.old_email_token_expiry >= now()) ).scalar_one_or_none() user_with_valid_token_from_new_email = session.execute( select(User).where( User.new_email_token == request.change_email_token).where( User.new_email_token_created <= now()).where( User.new_email_token_expiry >= now()) ).scalar_one_or_none() if user_with_valid_token_from_old_email: user = user_with_valid_token_from_old_email user.old_email_token = None user.old_email_token_created = None user.old_email_token_expiry = None user.need_to_confirm_via_old_email = False elif user_with_valid_token_from_new_email: user = user_with_valid_token_from_new_email user.new_email_token = None user.new_email_token_created = None user.new_email_token_expiry = None user.need_to_confirm_via_new_email = False else: context.abort(grpc.StatusCode.NOT_FOUND, errors.INVALID_TOKEN) # Using "___ is False" instead of "not ___" so that "None" doesn't pass if user.need_to_confirm_via_old_email is False and user.need_to_confirm_via_new_email is False: user.email = user.new_email user.new_email = None user.need_to_confirm_via_old_email = None user.need_to_confirm_via_new_email = None notify( user_id=user.id, topic="email_address", key="", action="change", icon="wrench", title=f"Your email was changed", link=urls.account_settings_link(), ) return auth_pb2.ConfirmChangeEmailRes( state=auth_pb2.EMAIL_CONFIRMATION_STATE_SUCCESS) elif user.need_to_confirm_via_old_email: return auth_pb2.ConfirmChangeEmailRes( state=auth_pb2. EMAIL_CONFIRMATION_STATE_REQUIRES_CONFIRMATION_FROM_OLD_EMAIL ) else: return auth_pb2.ConfirmChangeEmailRes( state=auth_pb2. EMAIL_CONFIRMATION_STATE_REQUIRES_CONFIRMATION_FROM_NEW_EMAIL )
def _check_occurrence_time_validity(start_time, end_time, context): if start_time < now(): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.EVENT_IN_PAST) if end_time < start_time: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.EVENT_ENDS_BEFORE_STARTS) if end_time - start_time > timedelta(days=7): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.EVENT_TOO_LONG) if start_time - now() > timedelta(days=365): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.EVENT_TOO_FAR_IN_FUTURE)
def VerifyPhone(self, request, context): if not sms.looks_like_a_code(request.token): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.WRONG_SMS_CODE) with session_scope() as session: user = session.execute( select(User).where(User.id == context.user_id)).scalar_one() if user.phone_verification_token is None: context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.NO_PENDING_VERIFICATION) if now() - user.phone_verification_sent > SMS_CODE_LIFETIME: context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.NO_PENDING_VERIFICATION) if user.phone_verification_attempts > SMS_CODE_ATTEMPTS: context.abort(grpc.StatusCode.RESOURCE_EXHAUSTED, errors.TOO_MANY_SMS_CODE_ATTEMPTS) if not verify_token(request.token, user.phone_verification_token): user.phone_verification_attempts += 1 session.commit() context.abort(grpc.StatusCode.NOT_FOUND, errors.WRONG_SMS_CODE) # Delete verifications from everyone else that has this number session.execute( update(User).where(User.phone == user.phone).where( User.id != context.user_id).values({ "phone_verification_verified": None, "phone_verification_attempts": 0, "phone_verification_token": None, "phone": None, }).execution_options(synchronize_session=False)) user.phone_verification_token = None user.phone_verification_verified = now() user.phone_verification_attempts = 0 notify( user_id=user.id, topic="phone_number", key="", action="verify", icon="wrench", title=f"Your phone number was verified", link=urls.account_settings_link(), ) return empty_pb2.Empty()
def set_email_change_token(session, user, hours=2): """ Make a new email change token that's valid for `hours` hours for this user Note: does not call session.commit() Returns token and expiry text """ token = urlsafe_secure_token() user.new_email_token = token user.new_email_token_created = now() user.new_email_token_expiry = now() + datetime.timedelta(hours=hours) return token, f"{hours} hours"
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)
def test_VerifyPhone(): user, token = generate_user() user_id = user.id with account_session(token) as account, api_session(token) as api: with pytest.raises(grpc.RpcError) as e: account.VerifyPhone(account_pb2.VerifyPhoneReq(token="123455")) assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION res = api.GetUser(api_pb2.GetUserReq(user=str(user_id))) assert res.verification == 0.0 with session_scope() as session: user = session.execute( select(User).where(User.id == user_id)).scalar_one() user.phone = "+46701740605" user.phone_verification_token = "111112" user.phone_verification_sent = now() account.VerifyPhone(account_pb2.VerifyPhoneReq(token="111112")) res = api.GetUser(api_pb2.GetUserReq(user=str(user_id))) assert res.verification == 1.0 # Phone number should finally show up on in your profile settings res = account.GetAccountInfo(empty_pb2.Empty()) assert res.phone == "+46701740605"
def ConfirmDeleteAccount(self, request, context): """ Confirm account deletion using account delete token """ with session_scope() as session: res = session.execute( select(User, AccountDeletionToken).join( AccountDeletionToken, AccountDeletionToken.user_id == User.id).where( AccountDeletionToken.token == request.token).where( AccountDeletionToken.is_valid)).one_or_none() if not res: context.abort(grpc.StatusCode.NOT_FOUND, errors.INVALID_TOKEN) user, account_deletion_token = res session.execute( delete(AccountDeletionToken).where( AccountDeletionToken.user_id == user.id)) undelete_days = 7 user.is_deleted = True user.undelete_until = now() + timedelta(days=undelete_days) user.undelete_token = urlsafe_secure_token() send_account_deletion_successful_email(user, undelete_days) return empty_pb2.Empty()
def test_purge_password_reset_tokens(db): user, api_token = generate_user() with session_scope() as session: password_reset_token = PasswordResetToken(token=urlsafe_secure_token(), user=user, expiry=now()) session.add(password_reset_token) assert session.execute( select(func.count()).select_from( PasswordResetToken)).scalar_one() == 1 queue_job(BackgroundJobType.purge_password_reset_tokens, empty_pb2.Empty()) process_job() with session_scope() as session: assert session.execute( select(func.count()).select_from( PasswordResetToken)).scalar_one() == 0 with session_scope() as session: assert (session.execute( select(func.count()).select_from(BackgroundJob).where( BackgroundJob.state == BackgroundJobState.completed)).scalar_one() == 1) assert (session.execute( select(func.count()).select_from(BackgroundJob).where( BackgroundJob.state != BackgroundJobState.completed)). scalar_one() == 0)
def InitiateMediaUpload(self, request, context): key = random_hex() created = now() expiry = created + timedelta(minutes=20) with session_scope() as session: upload = InitiatedUpload(key=key, created=created, expiry=expiry, initiator_user_id=context.user_id) session.add(upload) session.commit() req = media_pb2.UploadRequest( key=upload.key, type=media_pb2.UploadRequest.UploadType.IMAGE, created=Timestamp_from_datetime(upload.created), expiry=Timestamp_from_datetime(upload.expiry), max_width=2000, max_height=1600, ).SerializeToString() data = urlsafe_b64encode(req).decode("utf8") sig = urlsafe_b64encode(generate_hash_signature(req, config["MEDIA_SERVER_SECRET_KEY"])).decode("utf8") path = "upload?" + urlencode({"data": data, "sig": sig}) return api_pb2.InitiateMediaUploadRes( upload_url=urls.media_upload_url(path), expiry=Timestamp_from_datetime(expiry), )
def send_signup_email(flow): logger.info(f"Sending signup email to {flow.email=}:") # whether we've sent an email at all yet email_sent_before = flow.email_sent if flow.email_verified: # we just send a link to continue, not a verification link signup_link = urls.signup_link(token=flow.flow_token) elif flow.email_token and flow.token_is_valid: # if the verification email was sent and still is not expired, just resend the verification email signup_link = urls.signup_link(token=flow.email_token) else: # otherwise send a fresh email with new token token = urlsafe_secure_token() flow.email_verified = False flow.email_token = token flow.email_token_expiry = now() + SIGNUP_EMAIL_TOKEN_VALIDITY signup_link = urls.signup_link(token=flow.email_token) flow.email_sent = True logger.info(f"Link is: {signup_link}") template = "signup_verify" if not email_sent_before else "signup_continue" email.enqueue_email_from_template(flow.email, template, template_args={ "flow": flow, "signup_link": signup_link })
def test_account_deletion_successful_email(db): user, api_token = generate_user() with session_scope() as session: user_ = session.execute(select(User)).scalar_one() user.undelete_token = "token" user.undelete_until = now() user.is_deleted = True with patch("couchers.email.queue_email") as mock: send_account_deletion_successful_email(user, 7) assert mock.call_count == 1 (sender_name, sender_email, recipient, subject, plain, html), _ = mock.call_args assert recipient == user.email assert "account has been deleted" in subject.lower() unique_string = "You have successfully deleted your account from Couchers.org." assert unique_string in plain assert unique_string in html assert "7 days" in plain assert "7 days" in html url = f"{config['BASE_URL']}/recover-account?token={user.undelete_token}" assert url in plain assert url in html assert "*****@*****.**" in plain assert "*****@*****.**" in html
def ListAllEvents(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() occurrences = select(EventOccurrence).join( Event, Event.id == EventOccurrence.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.ListAllEventsRes( 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, )
def test_purge_account_deletion_tokens(db): user, api_token = generate_user() user2, api_token2 = generate_user() user3, api_token3 = generate_user() with session_scope() as session: """ 3 cases: 1) Token is valid 2) Token expired but account retrievable 3) Account is irretrievable (and expired) """ account_deletion_tokens = [ AccountDeletionToken(token=urlsafe_secure_token(), user=user, expiry=now() - timedelta(hours=2)), AccountDeletionToken(token=urlsafe_secure_token(), user=user2, expiry=now()), AccountDeletionToken(token=urlsafe_secure_token(), user=user3, expiry=now() + timedelta(hours=5)), ] for token in account_deletion_tokens: session.add(token) assert session.execute( select(func.count()).select_from( AccountDeletionToken)).scalar_one() == 3 queue_job(BackgroundJobType.purge_account_deletion_tokens, empty_pb2.Empty()) process_job() with session_scope() as session: assert session.execute( select(func.count()).select_from( AccountDeletionToken)).scalar_one() == 1 with session_scope() as session: assert (session.execute( select(func.count()).select_from(BackgroundJob).where( BackgroundJob.state == BackgroundJobState.completed)).scalar_one() == 1) assert (session.execute( select(func.count()).select_from(BackgroundJob).where( BackgroundJob.state != BackgroundJobState.completed)). scalar_one() == 0)
def test_ChangeEmail_has_password(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.execute( select(User).where(User.id == user.id)).scalar_one() assert user_updated.email == user.email assert user_updated.new_email == new_email assert user_updated.old_email_token is None assert not user_updated.old_email_token_created assert not user_updated.old_email_token_expiry assert not user_updated.need_to_confirm_via_old_email assert user_updated.new_email_token is not None assert user_updated.new_email_token_created <= now() assert user_updated.new_email_token_expiry >= now() assert user_updated.need_to_confirm_via_new_email token = user_updated.new_email_token with auth_api_session() as (auth_api, metadata_interceptor): res = auth_api.ConfirmChangeEmail( auth_pb2.ConfirmChangeEmailReq(change_email_token=token, )) assert res.state == auth_pb2.EMAIL_CONFIRMATION_STATE_SUCCESS with session_scope() as session: user = session.execute( select(User).where(User.id == user.id)).scalar_one() assert user.email == new_email assert user.new_email is None assert user.old_email_token is None assert user.old_email_token_created is None assert user.old_email_token_expiry is None assert not user.need_to_confirm_via_old_email assert user.new_email_token is None assert user.new_email_token_created is None assert user.new_email_token_expiry is None assert not user.need_to_confirm_via_new_email
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
def new_signup_token(session, email, hours=2): """ Make a signup token that's valid for `hours` hours Returns token and expiry text """ token = urlsafe_secure_token() signup_token = SignupToken(token=token, email=email, expiry=now() + timedelta(hours=hours)) session.add(signup_token) session.commit() return signup_token, f"{hours} hours"
def new_login_token(session, user, hours=2): """ Make a login token that's valid for `hours` hours Returns token and expiry text """ token = urlsafe_secure_token() login_token = LoginToken(token=token, user=user, expiry=now() + timedelta(hours=hours)) session.add(login_token) session.commit() return login_token, f"{hours} hours"
def new_password_reset_token(session, user, hours=2): """ Make a password reset token that's valid for `hours` hours Returns token and expiry text """ token = urlsafe_secure_token() password_reset_token = PasswordResetToken(token=token, user=user, expiry=now() + timedelta(hours=hours)) session.add(password_reset_token) session.commit() return password_reset_token, f"{hours} hours"
def test_get_page(db): user1, token1 = generate_user() user2, token2 = generate_user() with session_scope() as session: c_id = create_community(session, 0, 2, "Root node", [user1], [], None).id with pages_session(token1) as api: time_before_create = now() page_id = api.CreatePlace( pages_pb2.CreatePlaceReq( title="dummy title", content="dummy content", address="dummy address", location=pages_pb2.Coordinate( lat=1, lng=1, ), )).page_id with pages_session(token2) as api: time_before_get = now() res = api.GetPage(pages_pb2.GetPageReq(page_id=page_id)) assert res.title == "dummy title" assert res.content == "dummy content" assert res.address == "dummy address" assert res.location.lat == 1 assert res.location.lng == 1 assert res.slug == "dummy-title" assert time_before_create < to_aware_datetime( res.created) < time_before_get assert time_before_create < to_aware_datetime( res.last_edited) < time_before_get assert res.last_editor_user_id == user1.id assert res.creator_user_id == user1.id assert res.owner_user_id == user1.id assert not res.owner_community_id assert not res.owner_group_id assert res.editor_user_ids == [user1.id] assert not res.can_edit assert not res.can_moderate
def create_friend_reference(session, from_user_id, to_user_id, reference_age): reference = Reference( time=now() - reference_age, from_user_id=from_user_id, to_user_id=to_user_id, reference_type=ReferenceType.friend, text="Test friend request", rating=0.4, was_appropriate=True, ) session.add(reference) session.commit() return reference.id
def create_event(token, community_id, group_id, title, content, start_td): with events_session(token) as api: res = api.CreateEvent( events_pb2.CreateEventReq( title=title, content=content, offline_information=events_pb2.OfflineEventInformation( address="Near Null Island", lat=0.1, lng=0.2, ), start_time=Timestamp_from_datetime(now() + start_td), end_time=Timestamp_from_datetime(now() + start_td + timedelta(hours=2)), timezone="UTC", )) api.TransferEvent( events_pb2.TransferEventReq( event_id=res.event_id, new_owner_community_id=community_id, new_owner_group_id=group_id, ))
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)
def process_send_onboarding_emails(payload): """ Sends out onboarding emails """ logger.info(f"Sending out onboarding emails") with session_scope() as session: # first onboarding email users = ( session.execute(select(User).where(User.is_visible).where(User.onboarding_emails_sent == 0)).scalars().all() ) for user in users: send_onboarding_email(user, email_number=1) user.onboarding_emails_sent = 1 user.last_onboarding_email_sent = now() session.commit() # second onboarding email # sent after a week if the user has no profile or their "about me" section is less than 20 characters long users = ( session.execute( select(User) .where(User.is_visible) .where(User.onboarding_emails_sent == 1) .where(now() - User.last_onboarding_email_sent > timedelta(days=7)) .where(User.has_completed_profile == False) ) .scalars() .all() ) for user in users: send_onboarding_email(user, email_number=2) user.onboarding_emails_sent = 2 user.last_onboarding_email_sent = now() session.commit()
def test_create_page_place(db): user, token = generate_user() with session_scope() as session: c_id = create_community(session, 0, 2, "Root node", [user], [], None).id with pages_session(token) as api: time_before = now() res = api.CreatePlace( pages_pb2.CreatePlaceReq( title="dummy !#¤%&/-*' title", content="dummy content", address="dummy address", location=pages_pb2.Coordinate( lat=1, lng=1, ), )) assert res.title == "dummy !#¤%&/-*' title" assert res.type == pages_pb2.PAGE_TYPE_PLACE assert res.content == "dummy content" assert res.address == "dummy address" assert res.location.lat == 1 assert res.location.lng == 1 assert res.slug == "dummy-title" assert time_before < to_aware_datetime(res.created) < now() assert time_before < to_aware_datetime(res.last_edited) < now() assert res.last_editor_user_id == user.id assert res.creator_user_id == user.id assert res.owner_user_id == user.id assert not res.owner_community_id assert not res.owner_group_id assert res.editor_user_ids == [user.id] assert res.can_edit assert res.can_moderate
def create_host_reference(session, from_user_id, to_user_id, reference_age, *, surfing=True, host_request_id=None): if host_request_id: actual_host_request_id = host_request_id else: if surfing: actual_host_request_id = host_request_id or create_host_request( session, from_user_id, to_user_id, reference_age - timedelta(days=1)) else: actual_host_request_id = host_request_id or create_host_request( session, to_user_id, from_user_id, reference_age - timedelta(days=1)) host_request = session.query(HostRequest).filter( HostRequest.conversation_id == actual_host_request_id).one() other_reference = (session.query(Reference).filter( Reference.host_request_id == host_request.conversation_id).filter( Reference.to_user_id == from_user_id).one_or_none()) reference = Reference( time=now() - reference_age, from_user_id=from_user_id, host_request_id=host_request.conversation_id, text="Dummy reference", rating=0.5, was_appropriate=True, ) if host_request.from_user_id == from_user_id: reference.reference_type = ReferenceType.surfed reference.to_user_id = host_request.to_user_id assert from_user_id == host_request.from_user_id else: reference.reference_type = ReferenceType.hosted reference.to_user_id = host_request.from_user_id assert from_user_id == host_request.to_user_id session.add(reference) session.commit() return reference.id, actual_host_request_id
def Deauthenticate(self, request, context): """ Removes an active cookie session. """ token = parse_session_cookie(dict(context.invocation_metadata())) logger.info(f"Deauthenticate(token={token})") # if we had a token, try to remove the session if token: delete_session(token) # set the cookie to an empty string and expire immediately, should remove it from the browser context.send_initial_metadata([ ("set-cookie", create_session_cookie("", now())), ]) return empty_pb2.Empty()
def test_VerifyPhone_antibrute(): user, token = generate_user() user_id = user.id with account_session(token) as account, api_session(token) as api: with session_scope() as session: user = session.execute( select(User).where(User.id == user_id)).scalar_one() user.phone_verification_token = "111112" user.phone_verification_sent = now() user.phone = "+46701740605" for i in range(10): with pytest.raises(grpc.RpcError) as e: account.VerifyPhone(account_pb2.VerifyPhoneReq(token="123455")) if e.value.code() != grpc.StatusCode.NOT_FOUND: break assert e.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED