def CompleteSignup(self, request, context): """ Completes user sign up by creating the user in question, then logs them in. TODO: nice error handling for dupe username/email? """ with session_scope(self._Session) 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) # should be in YYYY-MM-DD format try: birthdate = datetime.fromisoformat(request.birthdate) except ValueError: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_BIRTHDATE) # check email again if not is_valid_email(signup_token.email): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_EMAIL) # check username validity if not is_valid_username(request.username): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_USERNAME) # check name validity if not is_valid_name(request.name): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_NAME) if not request.hosting_status: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.HOSTING_STATUS_REQUIRED) if not self._username_available(request.username): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.USERNAME_NOT_AVAILABLE) user = User( email=signup_token.email, username=request.username, name=request.name, city=request.city, gender=request.gender, birthdate=birthdate, hosting_status=hostingstatus2sql[request.hosting_status], ) # happens in same transaction session.delete(signup_token) # enforces email/username uniqueness session.add(user) session.commit() token = self._create_session(context, session, user) return auth_pb2.AuthRes(token=token, jailed=user.is_jailed)
def CompleteSignup(self, request, context): """ Completes user sign up by creating the user in question, then logs them in. TODO: nice error handling for dupe username/email? """ 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.") # should be in YYYY/MM/DD format, will raise exception if can't parse birthdate = datetime.fromisoformat(request.birthdate) # check email again if not is_valid_email(signup_token.email): context.abort(grpc.StatusCode.INVALID_ARGUMENT, "Invalid email") # check username validity if not is_valid_username(request.username): context.abort(grpc.StatusCode.INVALID_ARGUMENT, "Invalid username") user = User( email=signup_token.email, username=request.username, name=request.name, city=request.city, gender=request.gender, birthdate=birthdate ) # happens in same transaction session.delete(signup_token) # enforces email/username uniqueness session.add(user) session.commit() token = self._create_session(session, user) return auth_pb2.AuthRes(token=token)
def Signup(self, request, context): """ First step of Signup flow. If the email is not a valid email (by regexp), returns INVALID_EMAIL If the email already exists, returns EMAIL_EXISTS. Otherwise, creates a signup token and sends an email, then returns SENT_SIGNUP_EMAIL. """ logging.debug(f"Signup with {request.email=}") if not is_valid_email(request.email): return auth_pb2.SignupRes(next_step=auth_pb2.SignupRes.SignupStep.INVALID_EMAIL) with session_scope(self._Session) as session: user = session.query(User).filter(User.email == request.email).one_or_none() if not user: token, expiry_text = new_signup_token(session, request.email) send_signup_email(request.email, token, expiry_text) return auth_pb2.SignupRes(next_step=auth_pb2.SignupRes.SignupStep.SENT_SIGNUP_EMAIL) else: return auth_pb2.SignupRes(next_step=auth_pb2.SignupRes.SignupStep.EMAIL_EXISTS)
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(self._Session) 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(self._Session) 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(self._Session) 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) session.add(send_email_changed_notification_email(user)) session.add( send_email_changed_confirmation_email(user, token, expiry_text)) # session autocommit return empty_pb2.Empty()
def CompleteSignup(self, request, context): """ Completes user sign up by creating the user in question, then logs them in. TODO: nice error handling for dupe username/email? """ 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) # check birthdate validity (YYYY-MM-DD format and in the past) try: birthdate = datetime.fromisoformat(request.birthdate) except ValueError: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_BIRTHDATE) if pytz.UTC.localize(birthdate) >= now(): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_BIRTHDATE) # check email again if not is_valid_email(signup_token.email): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_EMAIL) # check username validity if not is_valid_username(request.username): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_USERNAME) # check name validity if not is_valid_name(request.name): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_NAME) if not request.hosting_status: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.HOSTING_STATUS_REQUIRED) if not self._username_available(request.username): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.USERNAME_NOT_AVAILABLE) if request.lat == 0 and request.lng == 0: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_COORDINATE) user = User( email=signup_token.email, username=request.username, name=request.name, gender=request.gender, birthdate=birthdate, hosting_status=hostingstatus2sql[request.hosting_status], city=request.city, geom=create_coordinate(request.lat, request.lng), geom_radius=request.radius, accepted_tos=1 if request.accept_tos else 0, ) # happens in same transaction session.delete(signup_token) # enforces email/username uniqueness session.add(user) session.commit() token, expiry = self._create_session(context, session, user, False) context.send_initial_metadata( [ ("set-cookie", create_session_cookie(token, expiry)), ] ) return auth_pb2.AuthRes(jailed=user.is_jailed)
def test_is_valid_email(): assert is_valid_email("*****@*****.**") assert is_valid_email("*****@*****.**") assert not is_valid_email("test [email protected]") assert not is_valid_email("*****@*****.**") assert not is_valid_email("testemail@couchersorg")