diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c639d6b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 HackRU + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deployment/config.prod.py b/deployment/config.prod.py index b49dedc..f7fe44b 100644 --- a/deployment/config.prod.py +++ b/deployment/config.prod.py @@ -47,7 +47,7 @@ class GOOGLE_CAL: datetime(int(os.getenv("PRODUCTION_END_YEAR", )), int(os.getenv("PRODUCTION_END_MONTH", )), int(os.getenv("PRODUCTION_END_DAY", )), int(os.getenv("PRODUCTION_END_HOUR", )), tzinfo=TIMEZONE)], # reopen during the event of [datetime(int(os.getenv("PRODUCTION_DAY_OF_START_YEAR", )), int(os.getenv("PRODUCTION_DAY_OF_START_MONTH", )), int(os.getenv("PRODUCTION_DAY_OF_START_DAY", )), int(os.getenv("PRODUCTION_DAY_OF_START_HOUR", )), tzinfo=TIMEZONE), - datetime(int(os.getenv("PRODUCTION_DAY_OF_END_YEAR", )), int(os.getenv("PRODUCTION_DAY_OF_START_MONTH", )), int(os.getenv("PRODUCTION_DAY_OF_END_DAY", )), int(os.getenv("PRODUCTION_DAY_OF_END_HOUR", )), tzinfo=TIMEZONE)] + datetime(int(os.getenv("PRODUCTION_DAY_OF_END_YEAR", )), int(os.getenv("PRODUCTION_DAY_OF_END_MONTH", )), int(os.getenv("PRODUCTION_DAY_OF_END_DAY", )), int(os.getenv("PRODUCTION_DAY_OF_END_HOUR", )), tzinfo=TIMEZONE)] ] AWS = { diff --git a/deployment/deploy_develop.sh b/deployment/deploy_develop.sh index 7ec8cb3..032aa6d 100644 --- a/deployment/deploy_develop.sh +++ b/deployment/deploy_develop.sh @@ -20,7 +20,9 @@ declare -a env_vars=( "DEVELOP_SLACK_API_TOKEN" "DEVELOP_SLACK_CHANNEL_ID" "DEVELOP_RESUME_BUCKET" - "DEVELOP_WAIVER_BUCKET") + "DEVELOP_WAIVER_BUCKET" + "DEVELOP_AWS_ACCESS_KEY_ID" + "DEVELOP_AWS_SECRET_ACCESS_KEY") echo "For loop" # Disabled environment variables: diff --git a/deployment/deploy_production.sh b/deployment/deploy_production.sh index 5d0865a..ee3a70e 100644 --- a/deployment/deploy_production.sh +++ b/deployment/deploy_production.sh @@ -35,7 +35,7 @@ declare -a env_vars=( "PRODUCTION_DAY_OF_START_DAY" "PRODUCTION_DAY_OF_START_HOUR" "PRODUCTION_DAY_OF_END_YEAR" - "PRODUCTION_DAY_OF_START_MONTH" + "PRODUCTION_DAY_OF_END_MONTH" "PRODUCTION_DAY_OF_END_DAY" "PRODUCTION_DAY_OF_END_HOUR") diff --git a/qr_generator/qr_requirements.txt b/qr_generator/qr_requirements.txt index 2fd3618..7dfe26d 100644 --- a/qr_generator/qr_requirements.txt +++ b/qr_generator/qr_requirements.txt @@ -1,3 +1,3 @@ qrcode==6.1 -Pillow==6.2.2 +Pillow==7.1.0 fpdf==1.7.2 \ No newline at end of file diff --git a/src/cal_announce.py b/src/cal_announce.py index 3415ca7..ebb1565 100644 --- a/src/cal_announce.py +++ b/src/cal_announce.py @@ -42,8 +42,8 @@ def refresh_cache(): # refresh cache token = config.SLACK_KEYS['token'] channel = config.SLACK_KEYS['channel'] - url = 'https://slack.com/api/channels.history' - params = {'token': token, 'channel': channel, 'count': num_messages} + url = 'https://slack.com/api/conversations.history' + params = {'token': token, 'channel': channel, 'limit': num_messages} result = requests.get(url, params) reply = result.json() if not reply.get('ok'): diff --git a/src/slack.py b/src/slack.py index b316a88..598bbb6 100644 --- a/src/slack.py +++ b/src/slack.py @@ -11,12 +11,37 @@ def create_error_response(err_msg: str): def process_slack_error(error_str: str): - if error_str in ["user_not_found", "user_not_visible", "user_disabled"]: + if error_str == "contains_invalid_user": return add_cors_headers( - {"statusCode": 403, "body": f"There was an error with the user id's provided: {error_str}"}) + {"statusCode": 403, "body": f"{error_str}: The id for one of the provided users were invalid"}) + elif error_str == "user_not_found": + return add_cors_headers( + {"statusCode": 403, "body": f"{error_str}: The id for both of the provided users were invalid"}) + elif error_str == "user_not_visible": + return add_cors_headers( + {"statusCode": 403, "body": f"{error_str}: The calling user is restricted from seeing the requested user"}) + elif error_str == "user_disabled": + return add_cors_headers( + {"statusCode": 403, "body": f"{error_str}: One or both user accounts are disabled"}) else: return create_error_response(f"Encountered a slack API error: {error_str}") +def get_slack_id(email): + # creates the link, payload and headers to make the request for the ID + api_link = r"https://slack.com/api/users.lookupByEmail" + slack_api_payload = {"token": config.SLACK_KEYS["token"], "email": f"{email}"} + slack_api_headers = {"Content-Type": "application/x-www-form-urlencoded"} + response = requests.post(url=api_link, data=slack_api_payload, headers=slack_api_headers) + if response.status_code != 200: + return + # fetches the json and examines it to determine if it was successful + response_json = response.json() + was_successful = response_json["ok"] + # in case of failure, error message is attached and returned back + if not was_successful: + return + # if everything goes well, fetches the necessary id to create the link + return response_json["user"]["id"] @ensure_schema({ "type": "object", @@ -36,13 +61,20 @@ def generate_dm_link(event, context, user=None): # ensures Slack Token is present in the config if "token" not in config.SLACK_KEYS or not config.SLACK_KEYS["token"]: return create_error_response("Slack API token not configured") - # fetches the slack id from their LCS profile this_slack_id = user.get("slack_id", None) # fetches the other user's slack id from their LCS profile other_slack_id = other_user.get("slack_id", None) # ensures both id's exist - if this_slack_id is None or other_slack_id is None: - return add_cors_headers({"statusCode": 403, "body": "Slack ID not present within LCS for the given user(s)"}) + if this_slack_id is None: + this_slack_id = get_slack_id(user.get("email", None)) + if this_slack_id is None: + return add_cors_headers({"statusCode": 403, "body": "Requester's Slack ID not present within LCS and email is not linked to a Slack ID"}) + if other_slack_id is None: + other_slack_id = get_slack_id(other_user.get("email", None)) + if other_slack_id is None: + return add_cors_headers({"statusCode": 403, "body": "Other user's Slack ID not present within LCS and email is not linked to a Slack ID"}) + + # creates the link, payload and headers to make the request api_link = r"https://slack.com/api/conversations.open" slack_api_payload = {"token": config.SLACK_KEYS["token"], "users": f"{this_slack_id},{other_slack_id}"} @@ -62,6 +94,6 @@ def generate_dm_link(event, context, user=None): creation_info = response_json["channel"] dm_id = creation_info["id"] server_id = creation_info["shared_team_ids"][0] - link_to_dm = f"https://apps.slack.com/client/{server_id}/{dm_id}" + link_to_dm = f"https://app.slack.com/client/{server_id}/{dm_id}" # returns the link and OK status code return add_cors_headers({"statusCode": 200, "body": {"slack_dm_link": link_to_dm}}) diff --git a/tests/test_slack.py b/tests/test_slack.py index 1c56192..aed5fbc 100644 --- a/tests/test_slack.py +++ b/tests/test_slack.py @@ -4,12 +4,14 @@ # credentials of the generator email = "generator@test.com" +email_linked = "email@mailmyrss.com" password = "temp_password" token = None slack_id = "U010WR7TMAQ" # credentials of the other user other_email = "other@test.com" +other_email_linked = "other_email@mailmyrss.com" other_password = "secure_password" other_slack_id = "U010WRFRJ7N" @@ -58,6 +60,16 @@ def setup_module(m): assert result["statusCode"] == 200 assert bool(config.SLACK_KEYS["token"]) + # create a dummy user with linked email to slack + result = authorize.create_user({"email": email_linked, "password": password}, {}) + assert result["statusCode"] == 200 + assert bool(config.SLACK_KEYS["token"]) + + # create a dummy user with linked email to slack + result = authorize.create_user({"email": other_email_linked, "password": other_password}, {}) + assert result["statusCode"] == 200 + assert bool(config.SLACK_KEYS["token"]) + # sets the slack id in the db documents db = util.coll("users") set_slack_id(email, slack_id) @@ -94,30 +106,48 @@ def test_misconfigured_slack_token(): config.SLACK_KEYS["token"] = slack_token -def test_missing_slack_id(): +def test_missing_slack_id_and_unlinked_email(): unset_slack_id(email) response = generate_dm_link(payload, {}) assert response["statusCode"] == 403 + assert response["body"] == "Requester's Slack ID not present within LCS and email is not linked to a Slack ID" set_slack_id(email, slack_id) unset_slack_id(other_email) response = generate_dm_link(payload, {}) assert response["statusCode"] == 403 + assert response["body"] == "Other user's Slack ID not present within LCS and email is not linked to a Slack ID" set_slack_id(other_email, other_slack_id) def test_invalid_slack_id(): set_slack_id(email, "bad slack id") response = generate_dm_link(payload, {}) - assert response["statusCode"] == 503 + assert response["statusCode"] == 403 + assert response["body"] == "contains_invalid_user: The id for one of the provided users were invalid" set_slack_id(email, slack_id) set_slack_id(other_email, "bad slack id") response = generate_dm_link(payload, {}) - assert response["statusCode"] == 503 + assert response["statusCode"] == 403 + assert response["body"] == "contains_invalid_user: The id for one of the provided users were invalid" + set_slack_id(other_email, other_slack_id) + + set_slack_id(email, "bad slack id") + set_slack_id(other_email, "bad slack id") + response = generate_dm_link(payload, {}) + assert response["statusCode"] == 403 + assert response["body"] == "user_not_found: The id for both of the provided users were invalid" + set_slack_id(email, slack_id) set_slack_id(other_email, other_slack_id) def test_valid_use(): response = generate_dm_link(payload, {}) assert response["statusCode"] == 200 + +def test_missing_ids_with_linked_emails(): + payload["other_email"] = other_email_linked + response = generate_dm_link(payload, {}) + assert response["statusCode"] == 200 +