def login_user(event, _): """ Login a user :param event: event :return: 200 and a login cookie if successful, 401 otherwise """ # Parse the paramteres params = parse_qs(event["body"]) # Make sure that the request contains all needed data if "identifier" in params and "password" in params: # Try to login the user user = User() username = user.login_user(params["identifier"][0], params["password"][0]) if username: cookie_content = f"user={username}" cookie_content_signed = sign_cookie(cookie_content) expiration_date = datetime.now() + timedelta(days=30) cookie = f"{cookie_content_signed};SameSite=Strict;Path=/;Expires={get_cookie_date(expiration_date)};HttpOnly" return create_response( 200, "POST", username, { "Access-Control-Allow-Credentials": "true", "Set-Cookie": cookie }, ) return create_response(401, "POST")
def change_voted_message_and_redirect(event, _): """ Change the user's voted message and redirect. """ # Parse the parameters params = parse_qs(event["body"]) # Check if the user is logged in correctly username = get_logged_in_user(event) if not username: return create_response(400, "POST", "User not logged in correctly") # Set the voted message and redirect voted_params = dict(user=username, voted_message=None, voted_redirect=None) if "voted_redirect" in params and params["voted_redirect"]: voted_params["voted_redirect"] = params["voted_redirect"][0] if "voted_message" in params and params["voted_message"]: voted_params["voted_message"] = params["voted_message"][0] user = User() try: user.set_voted_message_and_redirect(**voted_params) except ValueError as error: return create_response(400, "POST", str(error)) return create_response(200, "POST")
def get_votes_for_project(event, _): """ Handle get votes request for a project :param event: event :return: votes data as JSON """ # Get all parameters username = event["pathParameters"]["user"] project = event["pathParameters"]["project"] # Check if the user stats are public or the user is logged in user = User() user_data = user.get_user_by_username(username) if user_data and (user_data["is_public"] or username == get_logged_in_user(event)): vote = Vote() votes_data = vote.get_votes_for_project(username, project) result = dict( single_voting_projects=user_data.get("single_voting_projects"), votes=votes_data, ) return create_response(200, body=json.dumps(result)) else: return create_response(400, "GET", "Invalid user")
def test_get_user_by_username(self): user = User() self.assertEqual(self.expected_users_data["user_1"], user.get_user_by_username("user_1")) self.assertEqual(self.expected_users_data["user_2"], user.get_user_by_username("user_2")) self.assertFalse(user.get_user_by_username("user_3"))
def test_change_account_public(self): user = User() data = user.get_user_by_username("user_1") self.assertTrue(data["is_public"]) event_change = dict( headers=dict( Cookie="user=user_1&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" ), body="0", ) self.assertEqual(200, change_account_public(event_change, None)["statusCode"]) data = user.get_user_by_username("user_1") self.assertFalse(data["is_public"]) event_change = dict( headers=dict( Cookie="user=user_1&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" ), body="1", ) self.assertEqual(200, change_account_public(event_change, None)["statusCode"]) data = user.get_user_by_username("user_1") self.assertTrue(data["is_public"]) # Wrong cookie event_2 = dict( headers=dict( Cookie="user=user_2&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" ), body="0", ) self.assertEqual(400, change_account_public(event_2, None)["statusCode"])
def get_user_data(event, _): """ Get the data for the logged in user. :param event: event :return: dict with the main data attributes for the logged in user """ # Check if the user is logged in correctly and return empty dict if not username = get_logged_in_user(event) if not username: return create_response(200, body=json.dumps(dict())) # Get the user data user = User() data = user.get_user_by_username(username) # Choose fileds to provide return create_response( 200, body=json.dumps({ key: data[key] for key in [ "user", "email", "is_public", "voted_message", "voted_redirect", ] }), )
def change_password(event, _): """ Change the password of the user :param event: event :return: 200 if the change was successful, 400 if there was a problem with the new password """ # Parse the parameters params = parse_qs(event["body"]) # Check if a password is missing or they don't match if ("newpassword" not in params or "newpassword2" not in params or params["newpassword"][0] != params["newpassword2"][0]): return create_response(400, "POST", "The two passwords don't match") # Check if the user is logged in correctly username = get_logged_in_user(event) if not username: return create_response(400, "POST", "User not logged in correctly") # Try to change the password user = User() try: user.update_user_password(username, params["newpassword"][0]) return create_response(200, "POST") except ValueError as error: return create_response(400, "POST", str(error))
def remove_single_voting_project(event, _): """ Remove a project from the user's single voting projects list """ project = event["body"] # Check if the project name is valid if not re.fullmatch(r"[a-z0-9_\.\-]{1,100}", project): return create_response(400, "POST", "Invalid project name") # Get the logged in user data user = User() username = get_logged_in_user(event) user_data = user.get_user_by_username(username) if not user_data: return create_response(400, "POST", "User not logged in correctly") # Update the list of single voting projects if "single_voting_projects" in user_data: if project in user_data["single_voting_projects"]: user_data["single_voting_projects"].remove(project) user.change_single_voting_projects( username, user_data["single_voting_projects"]) return create_response(200, "POST")
def add_vote_and_redirect(event, _): """ Handle add vote requests and redirect to a info page :param event: event :return: redirect to a page explaining that the vote was added """ # Save the vote do_vote(event, None) redirect_url = "/voted" # Find the user and determine the redirect page username = event["pathParameters"]["user"].lower() user = User() user_data = user.get_user_by_username(username) if user_data: # If the user has a redirect page configured if user_data["voted_redirect"]: redirect_url = user_data["voted_redirect"] elif user_data["voted_message"]: redirect_url = f"/voted?message={quote_plus(user_data['voted_message'])}" # Return response return create_response(302, additional_headers={"Location": redirect_url})
def test_update_user_last_active(self, _): user = User() # Test valid password hash user.update_user_last_active("user_1") self.assertEqual("9999", user.get_user_by_username("user_1")["last_active"]) # Test invalid user self.assertRaises(ValueError, user.update_user_last_active, "user_x")
def test_remove_single_voting_project(self): cookie = "user=user_1&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" user = User() # Positive case event = dict(headers=dict(Cookie=cookie), body="project_a",) self.assertEqual(200, remove_single_voting_project(event, None)["statusCode"]) self.assertEqual( ["project_b"], user.get_user_by_username("user_1")["single_voting_projects"], ) # Repeated case event = dict(headers=dict(Cookie=cookie), body="project_a",) self.assertEqual(200, remove_single_voting_project(event, None)["statusCode"]) self.assertEqual( ["project_b"], user.get_user_by_username("user_1")["single_voting_projects"], ) # Non existing case event = dict(headers=dict(Cookie=cookie), body="project_c",) self.assertEqual(200, remove_single_voting_project(event, None)["statusCode"]) self.assertEqual( ["project_b"], user.get_user_by_username("user_1")["single_voting_projects"], ) # Last case event = dict(headers=dict(Cookie=cookie), body="project_b",) self.assertEqual(200, remove_single_voting_project(event, None)["statusCode"]) self.assertEqual( [], user.get_user_by_username("user_1")["single_voting_projects"], ) # Empty case event = dict(headers=dict(Cookie=cookie), body="project_b",) self.assertEqual(200, remove_single_voting_project(event, None)["statusCode"]) self.assertEqual( [], user.get_user_by_username("user_1")["single_voting_projects"], ) # Wrong cookie event = dict( headers=dict( Cookie="user=user_2&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" ), body="project_d", ) self.assertEqual(400, remove_single_voting_project(event, None)["statusCode"]) self.assertNotIn("single_voting_projects", user.get_user_by_username("user_2")) # Invalid project name event = dict(headers=dict(Cookie=cookie), body="project_invalid_<>",) self.assertEqual(400, remove_single_voting_project(event, None)["statusCode"]) self.assertEqual( [], user.get_user_by_username("user_1")["single_voting_projects"], )
def test_get_user_by_email(self): user = User() self.assertEqual( self.expected_users_data["user_1"], user.get_user_by_email("*****@*****.**"), ) self.assertEqual( self.expected_users_data["user_2"], user.get_user_by_email("*****@*****.**"), ) self.assertFalse(user.get_user_by_username("*****@*****.**"))
def do_vote(event, _): """ Post a vote to the database :param event: event """ # Get all parameters username = event["pathParameters"]["user"].lower() project = event["pathParameters"]["project"].lower() topic = event["pathParameters"]["topic"].lower() log.debug("Voting for: %s, %s, %s", username, project, topic) # Check all parameters for validity and return if some of them is not valid if not re.fullmatch(r"[a-z0-9_\.\-]{3,30}", username): return if not re.fullmatch(r"[a-z0-9_\.\-]{1,100}", project): return if not re.fullmatch(r"[a-z0-9_\.\-]{1,100}", topic): return # Check if this IP address already voted for this topic log.debug(f"Event: {event}") ip_address = get_ip_address(event) vote_history = VoteHistory() if vote_history.check_ip_voted(username, get_topic_key(project, topic), ip_address): return # Check if the user has multiple voting for a project disabled user = User() user_data = user.get_user_by_username(username) # Stop if the user is not registered if not user_data: return # Stop ff the project is a single voting and the user already voted if ("single_voting_projects" in user_data and project in user_data["single_voting_projects"] and vote_history.check_ip_voted_project(username, project, ip_address)): return # Do the voting vote = Vote() vote.add_vote(username, project, topic) vote_history.add_vote_history(username, project, topic, ip_address)
def test_set_account_public(self): user = User() user.set_account_public("user_1", False) user.set_account_public("user_2", True) self.assertFalse(user.is_account_public("user_1")) self.assertTrue(user.is_account_public("user_2"))
def change_account_public(event, _): """ Change the public visibility of an account :param event: event :return: 200 if the change was successful, 400 otherwise """ # Check if the user is logged in correctly username = get_logged_in_user(event) if not username: return create_response(400, "POST", "User not logged in correctly") # Get the new value of the public option new_is_public = event["body"] == "1" # Change the user public setting user = User() user.set_account_public(username, new_is_public) return create_response(200, "POST")
def test_create_user(self, _, __): user = User() # Test creation of a valid user user.create_user("user_3", "*****@*****.**", "test3") self.assertEqual(self.expected_users_data["user_3"], user.get_user_by_username("user_3")) # Test invalid values self.assertRaises(ValueError, user.create_user, "user_4", "*****@*****.**", "a") self.assertRaises(ValueError, user.create_user, "u", "*****@*****.**", "test4") self.assertRaises(ValueError, user.create_user, "user_4", "user_4@gmail", "test4") # Test creation of duplicate username or e-mail self.assertRaises(ValueError, user.create_user, "user_2", "*****@*****.**", "test4") self.assertRaises(ValueError, user.create_user, "user_4", "*****@*****.**", "test4")
def test_update_user_password(self, _): user = User() # Test valid password hash user_1 = user.get_user_by_username("user_1") user_1[ "password_hash"] = "$2b$12$nChdB1EJj1DZbtJgNSOFz.fTxPXu565.ic3xtXJvjLf64F4ELnuXG" user.update_user_password("user_1", "test3") self.assertEqual(user_1, user.get_user_by_username("user_1")) # Test invalid password hash self.assertRaises(ValueError, user.update_user_password, "user_1", "") self.assertRaises(ValueError, user.update_user_password, "user_1", "a") self.assertRaises(ValueError, user.update_user_password, "user_1", "aa") self.assertRaises(ValueError, user.update_user_password, "user_1", "aaa") # Test invalid user self.assertRaises( ValueError, user.update_user_password, "user_x", "$2b$12$G/Kb.r3YAJbenM7Ul9gQXO6bIjMZtVAt1uY.nKZMQL.1i6L50LLTW", )
def test_add_single_voting_project(self): cookie = "user=user_1&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" cookie2 = "user=user_2&signature=$2b$12$VTqno3w/ALCThRCNZQO2wu91wRvULXLhcD.Q06tbRJW9wWBFSYMRG" user = User() # Positive case event = dict(headers=dict(Cookie=cookie), body="project_c",) self.assertEqual(200, add_single_voting_project(event, None)["statusCode"]) self.assertEqual( ["project_a", "project_b", "project_c"], user.get_user_by_username("user_1")["single_voting_projects"], ) # Repeated case event = dict(headers=dict(Cookie=cookie), body="project_c",) self.assertEqual(200, add_single_voting_project(event, None)["statusCode"]) self.assertEqual( ["project_a", "project_b", "project_c"], user.get_user_by_username("user_1")["single_voting_projects"], ) # Positive case (no single voting projects yet) event = dict(headers=dict(Cookie=cookie2), body="project_a",) self.assertEqual(200, add_single_voting_project(event, None)["statusCode"]) self.assertEqual( ["project_a"], user.get_user_by_username("user_2")["single_voting_projects"], ) # Wrong cookie event = dict( headers=dict( Cookie="user=user_2&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" ), body="project_d", ) self.assertEqual(400, add_single_voting_project(event, None)["statusCode"]) # Invalid project name event = dict(headers=dict(Cookie=cookie), body="project_invalid_<>",) self.assertEqual(400, add_single_voting_project(event, None)["statusCode"]) self.assertEqual( ["project_a", "project_b", "project_c"], user.get_user_by_username("user_1")["single_voting_projects"], )
def test_update_user_email(self): user = User() # Test valid email user_1 = user.get_user_by_username("user_1") user_1["email"] = "*****@*****.**" user.update_user_email("user_1", user_1["email"]) self.assertEqual(user_1, user.get_user_by_username("user_1")) # Test invalid email self.assertRaises(ValueError, user.update_user_email, "user_1", "") self.assertRaises(ValueError, user.update_user_email, "user_1", "aaaaaa") self.assertRaises(ValueError, user.update_user_email, "user_1", "bbbb@cccc") # Test invalid user self.assertRaises(ValueError, user.update_user_email, "user_x", "*****@*****.**")
def test_login_user(self, _): user = User() # Test correct identifier and password self.assertEqual("user_1", user.login_user("user_1", "test")) self.assertEqual("user_1", user.login_user("*****@*****.**", "test")) # Test wrong identifier or password self.assertFalse(user.login_user("user_1", "wrong_password")) self.assertFalse(user.login_user("user_1", "test2")) self.assertFalse(user.login_user("user_3", "test3")) self.assertFalse(user.login_user("*****@*****.**", "test3")) # Test last active password self.assertEqual("1111", user.get_user_by_username("user_2")["last_active"]) user.login_user("user_2", "test2") self.assertEqual("9999", user.get_user_by_username("user_2")["last_active"])
def test_change_voted_message_and_redirect(self): cookie = "user=user_1&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" # Correct empty event = dict( headers=dict(Cookie=cookie), body="voted_message=&voted_redirect=", ) self.assertEqual( 200, change_voted_message_and_redirect(event, None)["statusCode"] ) # Correct voted message event = dict( headers=dict(Cookie=cookie), body="voted_message=New voted message&voted_redirect=", ) self.assertEqual( 200, change_voted_message_and_redirect(event, None)["statusCode"] ) # Correct voted redirect event = dict( headers=dict(Cookie=cookie), body="voted_message=&voted_redirect=http://iwanttoreadmore.com/voted", ) self.assertEqual( 200, change_voted_message_and_redirect(event, None)["statusCode"] ) # Correct voted message and redirect event = dict( headers=dict(Cookie=cookie), body="voted_message=Some message&voted_redirect=http://iwanttoreadmore.com/voted", ) self.assertEqual( 200, change_voted_message_and_redirect(event, None)["statusCode"] ) user = User() user_data = user.get_user_by_username("user_1") self.assertEqual("Some message", user_data["voted_message"]) self.assertEqual( "http://iwanttoreadmore.com/voted", user_data["voted_redirect"] ) # Wrong cookie event = dict( headers=dict( Cookie="user=user_2&signature=$2b$12$oGAaQWkNrjCWI0ugg8Go8uZ1ld2828dTeTk2cE/WZAO2yOB4aUxQm" ), body="voted_message=New voted message&voted_redirect=", ) self.assertEqual( 400, change_voted_message_and_redirect(event, None)["statusCode"] ) # Invalid message event = dict( headers=dict(Cookie=cookie), body="voted_message=Invalid <message>&voted_redirect=", ) self.assertEqual( 400, change_voted_message_and_redirect(event, None)["statusCode"] ) # Invalid redirect event = dict( headers=dict(Cookie=cookie), body="voted_message=&voted_redirect=invalidurl", ) self.assertEqual( 400, change_voted_message_and_redirect(event, None)["statusCode"] )
def test_change_single_voting_projects(self): user = User() user.change_single_voting_projects("user_1", ["project_a"]) self.assertEqual( ["project_a"], user.get_user_by_username("user_1")["single_voting_projects"]) user.change_single_voting_projects( "user_1", ["project_a", "project_b", "project_c"]) self.assertEqual( ["project_a", "project_b", "project_c"], user.get_user_by_username("user_1")["single_voting_projects"], ) user.change_single_voting_projects("user_1", []) self.assertEqual( [], user.get_user_by_username("user_1")["single_voting_projects"], ) self.assertRaises(ValueError, user.change_single_voting_projects, "user_x", [])
def set_voted_message_and_redirect(self): user = User() # Positive cases voted message user.set_voted_message_and_redirect("user_1", "New voted message", None) user_data = user.get_user_by_username("user_1") self.assertEqual(user_data["voted_message"], "New voted message") self.assertEqual(user_data["voted_redirect"], None) user.set_voted_message_and_redirect("user_2", "Other new voted message", None) user_data = user.get_user_by_username("user_2") self.assertEqual(user_data["voted_message"], "Other new voted message") self.assertEqual(user_data["voted_redirect"], None) # Positive cases voted redirect user.set_voted_message_and_redirect( "user_1", None, "https://iwanttoreadmore.com/voted") user_data = user.get_user_by_username("user_1") self.assertEqual(user_data["voted_redirect"], "https://iwanttoreadmore.com/voted") self.assertEqual(user_data["voted_message"], None) # Positive cases both user.set_voted_message_and_redirect( "user_1", "Some message", "https://iwanttoreadmore.com/voted") user_data = user.get_user_by_username("user_1") self.assertEqual(user_data["voted_message"], "Some message") self.assertEqual(user_data["voted_redirect"], "https://iwanttoreadmore.com/voted") # Negative case user self.assertRaises(ValueError, user.set_voted_message_and_redirect, "user_X", "Message", None) # Negative cases voted message self.assertRaises( ValueError, user.set_voted_message_and_redirect, "user_1", "<b>Tags</b>", None, ) self.assertRaises(ValueError, user.set_voted_message_and_redirect, "user_1", "M" * 501, None) # Negative cases voted redirect user.set_voted_message_and_redirect( "user_2", None, "https://iwanttoreadmore.com/voted2") user_data = user.get_user_by_username("user_2") self.assertEqual(user_data["voted_redirect"], "https://iwanttoreadmore.com/voted2") self.assertEqual(user_data["voted_message"], None) self.assertRaises( ValueError, user.set_voted_message_and_redirect, "user_1", None, "nonurltext", ) self.assertRaises(ValueError, user.set_voted_message_and_redirect, "user_1", None, "a" * 501)
def test_is_account_public(self): user = User() self.assertTrue(user.is_account_public("user_1")) self.assertFalse(user.is_account_public("user_2"))