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
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)
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
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
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)
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)
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
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
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}")
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
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_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))
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)
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)
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)
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
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
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()
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."
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)
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()
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] ], )
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
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)
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 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)
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], )
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
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)
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)