def Login(self, request, context): """ Does the first step of the Login flow. The user is searched for using their id, username, or email. If the user does not exist, returns a LOGIN_NO_SUCH_USER. If the user has a password, returns NEED_PASSWORD. If the user exists but does not have a password, generates a login token, send it in the email and returns SENT_LOGIN_EMAIL. """ logging.debug(f"Attempting login for {request.user=}") with session_scope(self._Session) as session: # Gets user by one of id/username/email or None if not found user = get_user_by_field(session, request.user) if user: if user.hashed_password is not None: logging.debug(f"Found user with password") return auth_pb2.LoginRes(next_step=auth_pb2.LoginRes.LoginStep.NEED_PASSWORD) else: logging.debug(f"Found user without password, sending login email") login_token, expiry_text = new_login_token(session, user) send_login_email(user, login_token, expiry_text) return auth_pb2.LoginRes(next_step=auth_pb2.LoginRes.LoginStep.SENT_LOGIN_EMAIL) else: # user not found logging.debug(f"Didn't find user") return auth_pb2.LoginRes(next_step=auth_pb2.LoginRes.LoginStep.INVALID_USER)
def Login(self, request, context): """ Does the first step of the Login flow. The user is searched for using their id, username, or email. If the user does not exist or has been deleted, throws a NOT_FOUND rpc error. If the user has a password, returns NEED_PASSWORD. If the user exists but does not have a password, generates a login token, send it in the email and returns SENT_LOGIN_EMAIL. """ logger.debug(f"Attempting login for {request.user=}") with session_scope() as session: # if the user is banned, they can get past this but get an error later in login flow user = session.execute( select(User).where_username_or_email(request.user).where( ~User.is_deleted)).scalar_one_or_none() if user: if user.has_password: logger.debug(f"Found user with password") return auth_pb2.LoginRes( next_step=auth_pb2.LoginRes.LoginStep.NEED_PASSWORD) else: logger.debug( f"Found user without password, sending login email") send_login_email(session, user) return auth_pb2.LoginRes( next_step=auth_pb2.LoginRes.LoginStep.SENT_LOGIN_EMAIL) else: # user not found logger.debug(f"Didn't find user") context.abort(grpc.StatusCode.NOT_FOUND, errors.USER_NOT_FOUND)
def test_login_email_full(db): user, api_token = generate_user() user_email = user.email with session_scope() as session: login_token, expiry_text = new_login_token(session, user) send_login_email(user, login_token, expiry_text) token = login_token.token def mock_print_dev_email(sender_name, sender_email, recipient, subject, plain, html): assert recipient == user.email assert "login" in subject.lower() assert login_token.token in plain assert login_token.token in html return print_dev_email(sender_name, sender_email, recipient, subject, plain, html) with patch("couchers.jobs.handlers.print_dev_email", mock_print_dev_email): process_job() with session_scope() as session: assert session.query(BackgroundJob).filter( BackgroundJob.state == BackgroundJobState.completed).count() == 1 assert session.query(BackgroundJob).filter( BackgroundJob.state != BackgroundJobState.completed).count() == 0
def test_login_email_full(db): user, api_token = generate_user() user_email = user.email with session_scope() as session: login_token = send_login_email(session, user) def mock_print_dev_email(sender_name, sender_email, recipient, subject, plain, html): assert recipient == user.email assert "login" in subject.lower() assert login_token.token in plain assert login_token.token in html return print_dev_email(sender_name, sender_email, recipient, subject, plain, html) with patch("couchers.jobs.handlers.print_dev_email", mock_print_dev_email): process_job() 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_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
def test_login_email(db): user, api_token = generate_user(db) message_id = random_hex(64) with session_scope(db) as session: login_token, expiry_text = new_login_token(session, user) @create_autospec def mock_send_email(sender_name, sender_email, recipient, subject, plain, html): assert recipient == user.email assert "login" in subject.lower() assert login_token.token in plain assert login_token.token in html return message_id with patch("couchers.email.send_email", mock_send_email) as mock: send_login_email(user, login_token, expiry_text) assert mock.call_count == 1