diff --git a/.gitignore b/.gitignore index e869bd6..12a75c6 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ +# main src/run/ logs/ +# google drive api +google_key.json + # Python - Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/requirements.txt b/requirements.txt index 1a2989f..7b350cf 100755 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ uvicorn[standard]==0.27.1 sqlalchemy==2.0.27 asyncpg==0.29.0 alembic==1.13.1 +google-api-python-client==2.143.0 # minor pyOpenSSL==24.0.0 # for generating cryptographically secure random numbers diff --git a/src/google_api/__init__.py b/src/google_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/google_api/constants.py b/src/google_api/constants.py new file mode 100644 index 0000000..f547817 --- /dev/null +++ b/src/google_api/constants.py @@ -0,0 +1,34 @@ +import os +import pathlib + +# this google account runs the google workspace for executives +GOOGLE_WORKSPACE_ACCOUNT = "csss@sfucsss.org" + +# any officer from the past 5 semesters has access to these +# TODO: ask the pres if we still want these rules, or not +FIVE_SEM_OFFICER_ACCESS = [ + "CSSS@SFU", +] + +EXECUTIVE_ACCESS = [ + "CSSS Gallery", + "CSSS@SFU", + "Deep-Exec", + "Exec_Photos", + "Private Gallery", +] + +# scopes are like permissions to google +GOOGLE_API_SCOPES = [ + # google drive permission + "https://www.googleapis.com/auth/drive" +] + +# TODO: make this into an enum, or something +GOOGLE_DRIVE_PERMISSION_ROLES = [ + "organizer", + "fileOrganizer", +] + +_this_file_path = pathlib.Path(__file__).parent.resolve() +SERVICE_ACCOUNT_KEY_PATH = str((_this_file_path / "../../google_key.json").resolve()) diff --git a/src/google_api/internals.py b/src/google_api/internals.py new file mode 100644 index 0000000..2246076 --- /dev/null +++ b/src/google_api/internals.py @@ -0,0 +1,68 @@ +# google workspace (shared drives) + google drive api + +from google.oauth2 import service_account +from googleapiclient.discovery import build + +from google_api.constants import GOOGLE_API_SCOPES, GOOGLE_WORKSPACE_ACCOUNT, SERVICE_ACCOUNT_KEY_PATH + +# TODO: understand how these work +credentials = service_account.Credentials.from_service_account_file( + filename=SERVICE_ACCOUNT_KEY_PATH, + scopes=GOOGLE_API_SCOPES +) +delegated_credentials = credentials.with_subject(GOOGLE_WORKSPACE_ACCOUNT) +service = build("drive", "v3", credentials=delegated_credentials) + +def _list_shared_drives() -> list: + return ( + service + .drives() + .list( + #pageSize = 50, + #q = "name contains 'CSSS'", + #useDomainAdminAccess = True, + ) + .execute() + ) + +def list_drive_permissions(drive_id: str) -> list: + return ( + service + .permissions() + .list( + fileId = drive_id, + # important to find the shared drive + supportsAllDrives = True, + fields = "*", + ) + .execute() + ) + +def create_drive_permission(drive_id: str, permission: dict): + return ( + service + .permissions() + .create( + fileId = drive_id, + + # TODO: update message + emailMessage = "You were just given permission to an SFU CSSS shared google drive!", + sendNotificationEmail = True, + supportsAllDrives = True, + + body=permission, + ) + .execute() + ) + +def delete_drive_permission(drive_id: str, permission_id: str): + return ( + service + .permissions() + .delete( + fileId = drive_id, + permissionId = permission_id, + supportsAllDrives = True, + ) + .execute() + ) diff --git a/src/officers/types.py b/src/officers/types.py index 7546991..12a2784 100644 --- a/src/officers/types.py +++ b/src/officers/types.py @@ -166,8 +166,8 @@ def serializable_dict(self): @staticmethod def from_data( - term: officers.tables.OfficerTerm, - officer_info: officers.tables.OfficerInfo, + term: OfficerTerm, + officer_info: OfficerInfo, include_private: bool, is_active: bool, ) -> OfficerData: diff --git a/src/utils.py b/src/utils.py index 5b56970..39758e1 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,3 +1,4 @@ +import re from datetime import datetime from officers.tables import OfficerInfo, OfficerTerm @@ -32,3 +33,6 @@ def is_valid_phone_number(phone_number: str) -> bool: len(phone_number) == 10 and phone_number.isnumeric() ) + +def is_valid_email(email: str): + return not re.match(r"^[^@]+@[^@]+\.[a-zA-Z]*$", email) diff --git a/tests/integration/test_google.py b/tests/integration/test_google.py new file mode 100644 index 0000000..5b41c59 --- /dev/null +++ b/tests/integration/test_google.py @@ -0,0 +1,25 @@ +from google_api import internals + + +def test__list_drives(): + """should not fail""" + drive_list = internals._list_shared_drives() + print(drive_list) + + drive_id = drive_list["drives"][0]["id"] + print(drive_id) + + permissions = internals.list_drive_permissions(drive_id) + print(permissions) + + # NOTE: this will raise an exception if the email address is a non-google account + """ + internals.create_drive_permission( + drive_id, + { + "type": "user", + "emailAddress": "tester_123591735013000019@gmail2.ca", + "role": "fileOrganizer", + } + ) + """