diff --git a/src/gradescopeapi/api/api.py b/src/gradescopeapi/api/api.py index 6de512b..a3fb928 100644 --- a/src/gradescopeapi/api/api.py +++ b/src/gradescopeapi/api/api.py @@ -88,6 +88,15 @@ def login( raise HTTPException(status_code=404, detail=f"Account not found. Error {e}") +@app.post("/logout", name="logout") +def logout(gs_connection: GSConnection = Depends(get_gs_connection)): + """Log out from Gradescope and clear the connection state.""" + gs_connection.logout() + global account + account = None + return {"message": "Logout successful", "status_code": status.HTTP_200_OK} + + @app.post("/courses", response_model=dict[str, dict[str, Course]]) def get_courses(): """Get all courses for the user diff --git a/src/gradescopeapi/classes/_helpers/_login_helpers.py b/src/gradescopeapi/classes/_helpers/_login_helpers.py index 128fd15..8b6b2bd 100644 --- a/src/gradescopeapi/classes/_helpers/_login_helpers.py +++ b/src/gradescopeapi/classes/_helpers/_login_helpers.py @@ -61,3 +61,15 @@ def login_set_session_cookies( session.headers.update({"X-CSRF-Token": csrf_token}) return True return False + + +def logout_session( + session: requests.Session, + gradescope_base_url: str = DEFAULT_GRADESCOPE_BASE_URL, +) -> None: + """Call Gradescope /logout to terminate the session server-side.""" + GS_LOGOUT_ENDPOINT = f"{gradescope_base_url}/logout" + try: + session.get(GS_LOGOUT_ENDPOINT) + except Exception: + pass diff --git a/src/gradescopeapi/classes/connection.py b/src/gradescopeapi/classes/connection.py index 11c52c2..dbc339e 100644 --- a/src/gradescopeapi/classes/connection.py +++ b/src/gradescopeapi/classes/connection.py @@ -4,6 +4,7 @@ from gradescopeapi.classes._helpers._login_helpers import ( get_auth_token_init_gradescope_session, login_set_session_cookies, + logout_session, ) from gradescopeapi.classes.account import Account @@ -30,3 +31,10 @@ def login(self, email, password): self.account = Account(self.session, self.gradescope_base_url) else: raise ValueError("Invalid credentials.") + + def logout(self): + """Call Gradescope /logout to terminate the session and clear local state.""" + logout_session(self.session, self.gradescope_base_url) + self.session = requests.Session() + self.logged_in = False + self.account = None diff --git a/tests/test_connection.py b/tests/test_connection.py index 4ced67b..65920ef 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -6,6 +6,7 @@ from gradescopeapi.classes._helpers._login_helpers import ( get_auth_token_init_gradescope_session, login_set_session_cookies, + logout_session, ) # load .env file @@ -72,3 +73,9 @@ def test_login_set_session_cookies_incorrect_creds(): cookie_check = set(cookies.keys()).issuperset({"_gradescope_session"}) assert login_check and cookie_check + + +def test_logout_session(): + # create test session and call the helper (completes without raising) + test_session = requests.Session() + logout_session(test_session)