def test_user_display_last_active(): assert User(last_active=datetime(2020, 7, 10, 16, 34, 1, 1)).display_last_active == datetime( 2020, 7, 10, 16, 0, 0, 0) assert User(last_active=datetime(2025, 7, 10, 17, 59, 1, 1)).display_last_active == datetime( 2025, 7, 10, 17, 0, 0, 0) assert User(last_active=datetime(2020, 7, 10, 16, 0, 1, 1)).display_last_active == datetime( 2020, 7, 10, 16, 0, 0, 0) assert User(last_active=datetime(2020, 7, 10, 0, 0, 0, 0)).display_last_active == datetime( 2020, 7, 10, 0, 0, 0, 0)
def test_user_display_joined(): assert User( joined=datetime(2020, 7, 10, 16, 34, 1, 1)).display_joined == datetime( 2020, 7, 10, 16, 0, 0, 0) assert User( joined=datetime(2025, 7, 10, 16, 59, 1, 1)).display_joined == datetime( 2025, 7, 10, 16, 0, 0, 0) assert User( joined=datetime(2020, 7, 10, 16, 0, 1, 1)).display_joined == datetime( 2020, 7, 10, 16, 0, 0, 0) assert User( joined=datetime(2020, 7, 10, 0, 0, 0, 0)).display_joined == datetime( 2020, 7, 10, 0, 0, 0, 0)
def add_dummy_data(file_name): with session_scope(Session) as session: with open(file_name, "r") as file: users = json.loads(file.read()) for user in users: new_user = User( username=user["username"], email=user["email"], hashed_password=hash_password(user["password"]) if user["password"] else None, name=user["name"], city=user["city"], verification=user["verification"], community_standing=user["community_standing"], birthdate=date(year=user["birthdate"]["year"], month=user["birthdate"]["month"], day=user["birthdate"]["day"]), gender=user["gender"], languages="|".join(user["languages"]), occupation=user["occupation"], about_me=user["about_me"], about_place=user["about_place"], countries_visited="|".join(user["countries_visited"]), countries_lived="|".join(user["countries_lived"]), ) session.add(new_user)
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 generate_user(db, *_, **kwargs): """ Create a new user, return session token The user is detached from any session, and you can access its static attributes, but you can't modify it Use this most of the time """ auth = Auth(db) with session_scope(db) as session: # default args username = "******" + random_hex(16) user_opts = { "username": username, "email": f"{username}@dev.couchers.org", # password is just 'password' # this is hardcoded because the password is slow to hash (so would slow down tests otherwise) "hashed_password": b"$argon2id$v=19$m=65536,t=2,p=1$4cjGg1bRaZ10k+7XbIDmFg$tZG7JaLrkfyfO7cS233ocq7P8rf3znXR7SAfUt34kJg", "name": username.capitalize(), "city": "Testing city", "verification": 0.5, "community_standing": 0.5, "birthdate": date(year=2000, month=1, day=1), "gender": "N/A", "languages": "Testing language 1|Testing language 2", "occupation": "Tester", "about_me": "I test things", "about_place": "My place has a lot of testing paraphenelia", "countries_visited": "Testing country", "countries_lived": "Wonderland", # you need to make sure to update this logic to make sure the user is jailed/not on request "accepted_tos": 1, } for key, value in kwargs.items(): user_opts[key] = value user = User(**user_opts) session.add(user) # this expires the user, so now it's "dirty" session.commit() token = auth._create_session("Dummy context", session, user) # refresh it, undoes the expiry session.refresh(user) # allows detaches the user from the session, allowing its use outside this session session.expunge(user) return user, token
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 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 add_dummy_users(): try: logger.info(f"Adding dummy users") with session_scope() as session: with open("src/data/dummy_users.json", "r") as file: data = json.loads(file.read()) for user in data["users"]: new_user = User( username=user["username"], email=user["email"], hashed_password=hash_password(user["password"]) if user["password"] else None, name=user["name"], city=user["location"]["city"], geom=create_coordinate(user["location"]["lat"], user["location"]["lng"]), geom_radius=user["location"]["radius"], verification=user["verification"], community_standing=user["community_standing"], birthdate=date(year=user["birthdate"]["year"], month=user["birthdate"]["month"], day=user["birthdate"]["day"]), gender=user["gender"], languages="|".join(user["languages"]), occupation=user["occupation"], about_me=user["about_me"], about_place=user["about_place"], countries_visited="|".join(user["countries_visited"]), countries_lived="|".join(user["countries_lived"]), hosting_status=hostingstatus2sql[HostingStatus.Value( user["hosting_status"])] if "hosting_status" in user else None, ) session.add(new_user) session.commit() for username1, username2 in data["friendships"]: friend_relationship = FriendRelationship( from_user_id=get_user_by_field(session, username1).id, to_user_id=get_user_by_field(session, username2).id, status=FriendStatus.accepted, ) session.add(friend_relationship) session.commit() for reference in data["references"]: reference_type = (ReferenceType.HOSTED if reference["type"] == "hosted" else (ReferenceType.SURFED if reference["type"] == "surfed" else ReferenceType.FRIEND)) new_reference = Reference( from_user_id=get_user_by_field(session, reference["from"]).id, to_user_id=get_user_by_field(session, reference["to"]).id, reference_type=reference_type, text=reference["text"], rating=reference["rating"], was_safe=reference["was_safe"], ) session.add(new_reference) session.commit() for group_chat in data["group_chats"]: # Create the chat creator = group_chat["creator"] conversation = Conversation() session.add(conversation) chat = GroupChat( conversation=conversation, title=group_chat["title"], creator_id=get_user_by_field(session, creator).id, is_dm=group_chat["is_dm"], ) session.add(chat) for participant in group_chat["participants"]: subscription = GroupChatSubscription( user_id=get_user_by_field(session, participant["username"]).id, group_chat=chat, role=GroupChatRole.admin if participant["username"] == creator else GroupChatRole.participant, joined=parser.isoparse(participant["joined"]), ) session.add(subscription) for message in group_chat["messages"]: session.add( Message( message_type=MessageType.text, conversation=chat.conversation, author_id=get_user_by_field( session, message["author"]).id, time=parser.isoparse(message["time"]), text=message["message"], )) session.commit() except IntegrityError: logger.error("Failed to insert dummy users, is it already inserted?")
def test_user_birthdate(): FakeDate.today = classmethod(lambda cls: date(2019, 7, 5)) assert User(birthdate = date(1990, 7, 4)).age == 29 assert User(birthdate = date(1990, 7, 31)).age == 28 assert User(birthdate = date(1992, 2, 29)).age == 27
def SignupFlow(self, request, context): with session_scope() as session: if request.email_token: # the email token can either be for verification or just to find an existing signup flow = session.execute( select(SignupFlow). where(SignupFlow.email_verified == False).where( SignupFlow.email_token == request.email_token).where( SignupFlow.token_is_valid)).scalar_one_or_none() if flow: # find flow by email verification token and mark it as verified flow.email_verified = True flow.email_token = None flow.email_token_expiry = None session.flush() else: # just try to find the flow by flow token, no verification is done flow = session.execute( select(SignupFlow).where( SignupFlow.flow_token == request.email_token)).scalar_one_or_none() if not flow: context.abort(grpc.StatusCode.NOT_FOUND, errors.INVALID_TOKEN) else: if not request.flow_token: # fresh signup if not request.HasField("basic"): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.SIGNUP_FLOW_BASIC_NEEDED) # TODO: unique across both tables existing_user = session.execute( select(User).where(User.email == request.basic.email) ).scalar_one_or_none() if existing_user: context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.SIGNUP_FLOW_EMAIL_TAKEN) existing_flow = session.execute( select(SignupFlow).where( SignupFlow.email == request.basic.email)).scalar_one_or_none() if existing_flow: send_signup_email(existing_flow) session.commit() context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.SIGNUP_FLOW_EMAIL_STARTED_SIGNUP) if not is_valid_email(request.basic.email): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_EMAIL) if not is_valid_name(request.basic.name): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_NAME) flow_token = cookiesafe_secure_token() flow = SignupFlow( flow_token=flow_token, name=request.basic.name, email=request.basic.email, ) session.add(flow) session.flush() else: # not fresh signup flow = session.execute( select(SignupFlow).where( SignupFlow.flow_token == request.flow_token)).scalar_one_or_none() if not flow: context.abort(grpc.StatusCode.NOT_FOUND, errors.INVALID_TOKEN) if request.HasField("basic"): context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.SIGNUP_FLOW_BASIC_FILLED) # we've found and/or created a new flow, now sort out other parts if request.HasField("account"): if flow.account_is_filled: context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.SIGNUP_FLOW_ACCOUNT_FILLED) # check username validity if not is_valid_username(request.account.username): context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_USERNAME) if not self._username_available(request.account.username): context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.USERNAME_NOT_AVAILABLE) abort_on_invalid_password(request.account.password, context) hashed_password = hash_password(request.account.password) birthdate = parse_date(request.account.birthdate) if not birthdate or birthdate >= minimum_allowed_birthdate( ): context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.INVALID_BIRTHDATE) if not request.account.hosting_status: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.HOSTING_STATUS_REQUIRED) if request.account.lat == 0 and request.account.lng == 0: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.INVALID_COORDINATE) if not request.account.accept_tos: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.MUST_ACCEPT_TOS) flow.username = request.account.username flow.hashed_password = hashed_password flow.birthdate = birthdate flow.gender = request.account.gender flow.hosting_status = hostingstatus2sql[ request.account.hosting_status] flow.city = request.account.city flow.geom = create_coordinate(request.account.lat, request.account.lng) flow.geom_radius = request.account.radius flow.accepted_tos = TOS_VERSION session.flush() if request.HasField("feedback"): if flow.filled_feedback: context.abort(grpc.StatusCode.FAILED_PRECONDITION, errors.SIGNUP_FLOW_FEEDBACK_FILLED) form = request.feedback flow.filled_feedback = True flow.ideas = form.ideas flow.features = form.features flow.experience = form.experience flow.contribute = contributeoption2sql[form.contribute] flow.contribute_ways = form.contribute_ways flow.expertise = form.expertise session.flush() if request.HasField("accept_community_guidelines"): if not request.accept_community_guidelines.value: context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.MUST_ACCEPT_COMMUNITY_GUIDELINES) flow.accepted_community_guidelines = GUIDELINES_VERSION session.flush() # send verification email if needed if not flow.email_sent: send_signup_email(flow) session.flush() # finish the signup if done if flow.is_completed: user = User( name=flow.name, email=flow.email, username=flow.username, hashed_password=flow.hashed_password, birthdate=flow.birthdate, gender=flow.gender, hosting_status=flow.hosting_status, city=flow.city, geom=flow.geom, geom_radius=flow.geom_radius, accepted_tos=flow.accepted_tos, accepted_community_guidelines=flow. accepted_community_guidelines, onboarding_emails_sent=1, last_onboarding_email_sent=func.now(), ) session.add(user) form = ContributorForm( user=user, ideas=flow.ideas or None, features=flow.features or None, experience=flow.experience or None, contribute=flow.contribute or None, contribute_ways=flow.contribute_ways, expertise=flow.expertise or None, ) session.add(form) user.filled_contributor_form = form.is_filled session.delete(flow) session.commit() enforce_community_memberships_for_user(session, user) if form.is_filled: user.filled_contributor_form = True maybe_send_contributor_form_email(form) send_onboarding_email(user, email_number=1) token, expiry = create_session(context, session, user, False) context.send_initial_metadata([ ("set-cookie", create_session_cookie(token, expiry)), ]) return auth_pb2.SignupFlowRes(auth_res=_auth_res(user), ) else: return auth_pb2.SignupFlowRes( flow_token=flow.flow_token, need_account=not flow.account_is_filled, need_feedback=not flow.filled_feedback, need_verify_email=not flow.email_verified, need_accept_community_guidelines=flow. accepted_community_guidelines < GUIDELINES_VERSION, )
def add_dummy_users(): logger.info(f"Adding dummy users") with session_scope() as session: if session.execute(select( func.count()).select_from(User)).scalar_one() > 0: logger.info("Users not empty, not adding dummy users") return with open(SRC_DIR + "/data/dummy_users.json", "r") as f: data = json.loads(f.read()) for user in data["users"]: new_user = User( username=user["username"], email=user["email"], hashed_password=hash_password(user["password"]) if user["password"] else None, name=user["name"], city=user["location"]["city"], geom=create_coordinate(user["location"]["lat"], user["location"]["lng"]), geom_radius=user["location"]["radius"], community_standing=user["community_standing"], birthdate=date(year=user["birthdate"]["year"], month=user["birthdate"]["month"], day=user["birthdate"]["day"]), gender=user["gender"], occupation=user["occupation"], about_me=user["about_me"], about_place=user["about_place"], hosting_status=hostingstatus2sql[HostingStatus.Value( user["hosting_status"] if "hosting_status" in user else "HOSTING_STATUS_CANT_HOST")], new_notifications_enabled=True, accepted_tos=TOS_VERSION, accepted_community_guidelines=GUIDELINES_VERSION, ) session.add(new_user) session.flush() for language in user["languages"]: session.add( LanguageAbility(user_id=new_user.id, language_code=language[0], fluency=LanguageFluency[language[1]])) for region in user["regions_visited"]: session.add( RegionVisited(user_id=new_user.id, region_code=region)) for region in user["regions_lived"]: session.add( RegionLived(user_id=new_user.id, region_code=region)) session.commit() for username1, username2 in data["friendships"]: friend_relationship = FriendRelationship( from_user_id=session.execute( select(User).where( User.username == username1)).scalar_one().id, to_user_id=session.execute( select(User).where( User.username == username2)).scalar_one().id, status=FriendStatus.accepted, ) session.add(friend_relationship) session.commit() for reference in data["references"]: reference_type = (ReferenceType.hosted if reference["type"] == "hosted" else (ReferenceType.surfed if reference["type"] == "surfed" else ReferenceType.friend)) new_reference = Reference( from_user_id=session.execute( select(User).where( User.username == reference["from"])).scalar_one().id, to_user_id=session.execute( select(User).where( User.username == reference["to"])).scalar_one().id, reference_type=reference_type, text=reference["text"], rating=reference["rating"], was_appropriate=reference["was_appropriate"], ) session.add(new_reference) session.commit() for group_chat in data["group_chats"]: # Create the chat creator = group_chat["creator"] conversation = Conversation() session.add(conversation) chat = GroupChat( conversation=conversation, title=group_chat["title"], creator_id=session.execute( select(User).where( User.username == creator)).scalar_one().id, is_dm=group_chat["is_dm"], ) session.add(chat) for participant in group_chat["participants"]: subscription = GroupChatSubscription( user_id=session.execute( select(User).where( User.username == participant["username"])).scalar_one().id, group_chat=chat, role=GroupChatRole.admin if participant["username"] == creator else GroupChatRole.participant, joined=parser.isoparse(participant["joined"]), ) session.add(subscription) for message in group_chat["messages"]: session.add( Message( message_type=MessageType.text, conversation=chat.conversation, author_id=session.execute( select(User).where( User.username == message["author"])).scalar_one().id, time=parser.isoparse(message["time"]), text=message["message"], )) session.commit()