Repository: couchers-org/couchers
Issue: No couch request future date limits

Subject: No couch request future date limits
Description:
You can send a couch request for a very distant future date, and with an unlimited stay duration (i.e. span between from and to date).

Steps:
Send a couch request with from and to dates decades in the future.

Results:
Expected: some time cap for how long in advance you can request a couch (e.g. 1 year) and how long the stay can be (e.g. 1 year)

Backend version: bf2ff87
Frontend version: bf2ff87
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36
Page: https://app.couchers.org/
User ID: 77

Changed files:
app/backend/src/tests/test_requests.py, app/backend/src/couchers/servicers/requests.py, app/backend/src/couchers/errors.py

diff --git a/app/backend/src/couchers/errors.py b/app/backend/src/couchers/errors.py
index beb89852a..565e50b33 100644
--- a/app/backend/src/couchers/errors.py
+++ b/app/backend/src/couchers/errors.py
@@ -13,7 +13,9 @@ CANT_REQUEST_SELF = "Can't request hosting from yourself."
 CANT_UNACCEPT_TOS = "You cannot revoke acceptance of the TOS"
 CANT_UNSEE_MESSAGES = "You can't unsee messages."
 CHAT_NOT_FOUND = "Couldn't find that chat."
+DATE_FROM_AFTER_ONE_YEAR = "The start date must be within one year from today."
 DATE_FROM_AFTER_TO = "From date can't be after to date."
+DATE_TO_AFTER_ONE_YEAR = "You cannot request to stay with someone for longer than one year."
 DATE_TO_BEFORE_TODAY = "To date must be today or later."
 DIRECT_MESSAGE_ONLY_FRIENDS = "You can only send a message to friends."
 FRIEND_REQUEST_NOT_FOUND = "Couldn't find that friend request."
diff --git a/app/backend/src/couchers/servicers/requests.py b/app/backend/src/couchers/servicers/requests.py
index 776944aff..49aeac3ea 100644
--- a/app/backend/src/couchers/servicers/requests.py
+++ b/app/backend/src/couchers/servicers/requests.py
@@ -1,5 +1,6 @@
 import datetime
 import logging
+from datetime import date, timedelta
 
 import grpc
 from google.protobuf import empty_pb2
@@ -74,6 +75,16 @@ class Requests(requests_pb2_grpc.RequestsServicer):
             if request.to_date < least_current_date():
                 context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_TO_BEFORE_TODAY)
 
+            today = date.fromisoformat(largest_current_date())
+            today_plus_one_year = today.replace(year=today.year + 1).isoformat()
+            if request.from_date > today_plus_one_year:
+                context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_FROM_AFTER_ONE_YEAR)
+
+            from_date = date.fromisoformat(request.from_date)
+            from_date_plus_one_year = (from_date.replace(year=from_date.year + 1)).isoformat()
+            if request.to_date > from_date_plus_one_year:
+                context.abort(grpc.StatusCode.INVALID_ARGUMENT, errors.DATE_TO_AFTER_ONE_YEAR)
+
             conversation = Conversation()
             session.add(conversation)
             session.flush()
diff --git a/app/backend/src/tests/test_requests.py b/app/backend/src/tests/test_requests.py
index 6ea9d2912..2ff5d31d6 100644
--- a/app/backend/src/tests/test_requests.py
+++ b/app/backend/src/tests/test_requests.py
@@ -1,4 +1,4 @@
-from datetime import timedelta
+from datetime import date, timedelta
 
 import grpc
 import pytest
@@ -84,6 +84,34 @@ def test_create_request(db):
             == "Test request"
         )
 
+    today = now().date()
+    today_plus_one_year = today.replace(year=today.year + 1)
+    today_plus_one_year_plus_2 = (today_plus_one_year + timedelta(days=2)).isoformat()
+    today_plus_one_year_plus_3 = (today_plus_one_year + timedelta(days=3)).isoformat()
+    with pytest.raises(grpc.RpcError) as e:
+        api.CreateHostRequest(
+            requests_pb2.CreateHostRequestReq(
+                to_user_id=user2.id,
+                from_date=today_plus_one_year_plus_2,
+                to_date=today_plus_one_year_plus_3,
+                text="Test from date after one year",
+            )
+        )
+    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+    assert e.value.details() == errors.DATE_FROM_AFTER_ONE_YEAR
+
+    with pytest.raises(grpc.RpcError) as e:
+        api.CreateHostRequest(
+            requests_pb2.CreateHostRequestReq(
+                to_user_id=user2.id,
+                from_date=today_plus_2,
+                to_date=today_plus_one_year_plus_3,
+                text="Test to date one year after from date",
+            )
+        )
+    assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+    assert e.value.details() == errors.DATE_TO_AFTER_ONE_YEAR
+
 
 def add_message(db, text, author_id, conversation_id):
     with session_scope() as session: