From 90b8160844f2e348f6d298ca7f5505ae5658f992 Mon Sep 17 00:00:00 2001 From: youssefr26 Date: Fri, 8 Nov 2024 16:13:12 -0500 Subject: [PATCH 1/6] Added registration for new users. --- labconnect/main/auth_routes.py | 58 +++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/labconnect/main/auth_routes.py b/labconnect/main/auth_routes.py index ca9b7d41..ea42d38b 100644 --- a/labconnect/main/auth_routes.py +++ b/labconnect/main/auth_routes.py @@ -2,7 +2,7 @@ from uuid import uuid4 from flask import current_app, make_response, redirect, request -from flask_jwt_extended import create_access_token +from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required from onelogin.saml2.auth import OneLogin_Saml2_Auth from labconnect import db @@ -14,31 +14,36 @@ temp_codes = {} -def generate_temporary_code(user_email: str) -> str: +def generate_temporary_code(user_email: str, registered: bool) -> str: # Generate a unique temporary code code = str(uuid4()) expires_at = datetime.now() + timedelta(seconds=5) # expires in 5 seconds - temp_codes[code] = {"email": user_email, "expires_at": expires_at} + temp_codes[code] = { + "email": user_email, + "expires_at": expires_at, + "registered": registered, + } return code -def validate_code_and_get_user_email(code: str) -> str | None: +def validate_code_and_get_user_email(code: str) -> tuple[str | None, bool | None]: token_data = temp_codes.get(code, {}) if not token_data: return None user_email = token_data.get("email", None) expire = token_data.get("expires_at", None) + registered = token_data.get("registered", None) if user_email and expire and expire > datetime.now(): # If found, delete the code to prevent reuse del temp_codes[code] - return user_email + return user_email, registered elif expire: # If the code has expired, delete it del temp_codes[code] - return None + return None, None @main_blueprint.get("/login") @@ -70,6 +75,7 @@ def saml_callback(): errors = auth.get_errors() if not errors: + registered = True user_info = auth.get_attributes() # user_id = auth.get_nameid() @@ -77,22 +83,10 @@ def saml_callback(): # User doesn't exist, create a new user if data is None: - - # TODO: add data - user = User( - # email=email, - # first_name=first_name, - # last_name=last_name, - # preferred_name=json_request_data.get("preferred_name", None), - # class_year=class_year, - ) - - db.session.add(user) - db.session.commit() - + registered = False # Generate JWT # token = create_access_token(identity=[user_id, datetime.now()]) - code = generate_temporary_code(user_info["email"][0]) + code = generate_temporary_code(user_info["email"][0], registered) # Send the JWT to the frontend return redirect(f"{current_app.config['FRONTEND_URL']}/callback/?code={code}") @@ -100,6 +94,26 @@ def saml_callback(): return {"errors": errors}, 500 +@main_blueprint.post("/register") +@jwt_required() +def registerUser(): + user_id = get_jwt_identity() + + user = db.session.execute(db.select(User).where(User.email == user_id)) + + # Gather the new user's information + json_data = request.get_json() + user.first_name = json_data.get("first_name") + user.last_name = json_data.get("last_name") + user.preferred_name = json_data.get("preferred_name") + user.class_year = json_data.get("class_year") + user.profile_picture = json_data.get("profile_pictures") + user.website = json_data.get("website") + user.description = json_data.get("description") + + return {"msg": "Information added"} + + @main_blueprint.post("/token") def tokenRoute(): if request.json is None or request.json.get("code", None) is None: @@ -108,13 +122,13 @@ def tokenRoute(): code = request.json["code"] if code is None: return {"msg": "Missing code in request"}, 400 - user_email = validate_code_and_get_user_email(code) + user_email, registered = validate_code_and_get_user_email(code) if user_email is None: return {"msg": "Invalid code"}, 400 token = create_access_token(identity=[user_email, datetime.now()]) - return {"token": token} + return {"token": token, "registered": registered} @main_blueprint.get("/metadata/") From 3c2d641792dfde3cd13b17bed9fe58dfed6af403 Mon Sep 17 00:00:00 2001 From: youssefr26 Date: Fri, 8 Nov 2024 17:14:01 -0500 Subject: [PATCH 2/6] Added route to search for lab_managers and updated edit and create opportunities to add new leads. Also attempted to fix registration. --- labconnect/main/auth_routes.py | 25 ++++++----- labconnect/main/opportunity_routes.py | 62 +++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/labconnect/main/auth_routes.py b/labconnect/main/auth_routes.py index ea42d38b..5eb05707 100644 --- a/labconnect/main/auth_routes.py +++ b/labconnect/main/auth_routes.py @@ -97,21 +97,24 @@ def saml_callback(): @main_blueprint.post("/register") @jwt_required() def registerUser(): - user_id = get_jwt_identity() - user = db.session.execute(db.select(User).where(User.email == user_id)) + user_id = get_jwt_identity() # Gather the new user's information json_data = request.get_json() - user.first_name = json_data.get("first_name") - user.last_name = json_data.get("last_name") - user.preferred_name = json_data.get("preferred_name") - user.class_year = json_data.get("class_year") - user.profile_picture = json_data.get("profile_pictures") - user.website = json_data.get("website") - user.description = json_data.get("description") - - return {"msg": "Information added"} + user = User( + email=user_id, + first_name=json_data.get("first_name"), + last_name=json_data.get("last_name"), + preferred_name=json_data.get("preferred_name"), + class_year=json_data.get("class_year"), + profile_picture=json_data.get("profile_pictures"), + website=json_data.get("website"), + description=json_data.get("description"), + ) + db.session.add(user) + db.session.commit() + return {"msg": "New user added"} @main_blueprint.post("/token") diff --git a/labconnect/main/opportunity_routes.py b/labconnect/main/opportunity_routes.py index 9d69a212..7d01a1fb 100644 --- a/labconnect/main/opportunity_routes.py +++ b/labconnect/main/opportunity_routes.py @@ -599,6 +599,48 @@ def getLabManagerOpportunityCards(rcs_id: str): # abort(500) +# function to search for lab managers +@main_blueprint.get("/searchLabManagers/") +def searchLabManagers(query: str): + # Perform a search on User table by first name, last name, or email using ILIKE for exact partial matches + stmt = ( + db.select(User) + .join(LabManager, User.lab_manager_id == LabManager.id) + .where( + ( + User.first_name.ilike( + f"%{query}%" + ) # Case-insensitive partial match on first_name + ) + | ( + User.last_name.ilike( + f"%{query}%" + ) # Case-insensitive partial match on last_name + ) + | ( + User.email.ilike( + f"%{query}%" + ) # Case-insensitive partial match on email + ) + ) + ) + + results = db.session.execute(stmt).scalars().all() + + # Format results as JSON + lab_managers = [ + { + "lab_manager_id": user.lab_manager_id, + "first_name": user.first_name, + "last_name": user.last_name, + "email": user.email, + } + for user in results + ] + + return {"lab_managers": lab_managers}, 200 + + # functions to create/edit/delete opportunities @main_blueprint.post("/createOpportunity") @jwt_required() @@ -688,6 +730,16 @@ def createOpportunity(): db.session.add(newOpportunity) + # Add the selected managers to the Leads table + if "lab_manager_ids" in data: + for lab_manager_id in data["lab_manager_ids"]: + lead = Leads( + lab_manager_id=lab_manager_id, opportunity_id=newOpportunity.id + ) + db.session.add(lead) + + db.session.commit() # Commit all changes + return {"data": "Opportunity Created"} @@ -845,6 +897,16 @@ def editOpportunity(opportunity_id): # Commit all changes to the database db.session.commit() + # Add the updated list of managers + if "lab_manager_ids" in data: + for lab_manager_id in data["lab_manager_ids"]: + new_lead = Leads( + lab_manager_id=lab_manager_id, opportunity_id=opportunity_id + ) + db.session.add(new_lead) + + db.session.commit() # Commit all changes + return {"data": "Opportunity Updated"}, 200 From a900f5f2f424767595d7d24471334730fd63d8a4 Mon Sep 17 00:00:00 2001 From: youssefr26 Date: Tue, 12 Nov 2024 16:23:46 -0500 Subject: [PATCH 3/6] Clean up Deepsource error. --- labconnect/main/auth_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labconnect/main/auth_routes.py b/labconnect/main/auth_routes.py index 5eb05707..7824313d 100644 --- a/labconnect/main/auth_routes.py +++ b/labconnect/main/auth_routes.py @@ -55,7 +55,7 @@ def saml_login(): and current_app.config["FRONTEND_URL"] == "http://localhost:3000" ): # Generate JWT - code = generate_temporary_code("test@rpi.edu") + code = generate_temporary_code("test@rpi.edu", True) # Send the JWT to the frontend return redirect(f"{current_app.config['FRONTEND_URL']}/callback/?code={code}") From f8d12ed5efce926a1091240b1e6af5763c8d3e2f Mon Sep 17 00:00:00 2001 From: youssefr26 Date: Fri, 15 Nov 2024 16:04:23 -0500 Subject: [PATCH 4/6] Updated register route --- labconnect/main/auth_routes.py | 62 ++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/labconnect/main/auth_routes.py b/labconnect/main/auth_routes.py index 7824313d..2b901d4e 100644 --- a/labconnect/main/auth_routes.py +++ b/labconnect/main/auth_routes.py @@ -1,13 +1,19 @@ from datetime import datetime, timedelta from uuid import uuid4 -from flask import current_app, make_response, redirect, request +from flask import current_app, make_response, redirect, request, abort from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required from onelogin.saml2.auth import OneLogin_Saml2_Auth from labconnect import db from labconnect.helpers import prepare_flask_request -from labconnect.models import User +from labconnect.models import ( + User, + UserCourses, + UserDepartments, + UserMajors, + ManagementPermissions, +) from . import main_blueprint @@ -95,25 +101,59 @@ def saml_callback(): @main_blueprint.post("/register") -@jwt_required() def registerUser(): - user_id = get_jwt_identity() - # Gather the new user's information json_data = request.get_json() + if not json_data: + abort(400) + user = User( - email=user_id, + email=json_data.get("email"), first_name=json_data.get("first_name"), last_name=json_data.get("last_name"), - preferred_name=json_data.get("preferred_name"), - class_year=json_data.get("class_year"), - profile_picture=json_data.get("profile_pictures"), - website=json_data.get("website"), - description=json_data.get("description"), + preferred_name=json_data.get("preferred_name", ""), + class_year=json_data.get("class_year", ""), + profile_picture=json_data.get( + "profile_picture", "https://www.svgrepo.com/show/206842/professor.svg" + ), + website=json_data.get("website", ""), + description=json_data.get("description", ""), ) db.session.add(user) db.session.commit() + + # Add UserDepartments if provided + if json_data.get("departments"): + for department_id in json_data["departments"]: + user_department = UserDepartments( + user_id=user.id, department_id=department_id + ) + db.session.add(user_department) + + # Additional auxiliary records (majors, courses, etc.) + if json_data.get("majors"): + for major_id in json_data["majors"]: + user_major = UserMajors(user_id=user.id, major_id=major_id) + db.session.add(user_major) + # Add Courses if provided + if json_data.get("courses"): + for course_id in json_data["courses"]: + user_course = UserCourses(user_id=user.id, course_id=course_id) + db.session.add(user_course) + + # Add ManagementPermissions if provided + if json_data.get("permissions"): + permissions = json_data["permissions"] + management_permissions = ManagementPermissions( + user_id=user.id, + super_admin=permissions.get("super_admin", False), + admin=permissions.get("admin", False), + moderator=permissions.get("moderator", False), + ) + db.session.add(management_permissions) + + db.session.commit() return {"msg": "New user added"} From 473ff8f9d90001d179bc87fca3855085501fe0fd Mon Sep 17 00:00:00 2001 From: youssefr26 Date: Fri, 15 Nov 2024 16:11:26 -0500 Subject: [PATCH 5/6] Fixed Deepsource errors --- labconnect/main/auth_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labconnect/main/auth_routes.py b/labconnect/main/auth_routes.py index 2b901d4e..d065ce1e 100644 --- a/labconnect/main/auth_routes.py +++ b/labconnect/main/auth_routes.py @@ -2,7 +2,7 @@ from uuid import uuid4 from flask import current_app, make_response, redirect, request, abort -from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required +from flask_jwt_extended import create_access_token from onelogin.saml2.auth import OneLogin_Saml2_Auth from labconnect import db From 69d45aedb49ba9c752e4678dd9e3f2ba8b5fe3cc Mon Sep 17 00:00:00 2001 From: Rafael Cenzano <32753063+RafaelCenzano@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:29:41 -0500 Subject: [PATCH 6/6] fix missing code --- labconnect/main/opportunity_routes.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/labconnect/main/opportunity_routes.py b/labconnect/main/opportunity_routes.py index 423be5bd..4d04eb6f 100644 --- a/labconnect/main/opportunity_routes.py +++ b/labconnect/main/opportunity_routes.py @@ -609,7 +609,13 @@ def searchLabManagers(query: str): User.email.ilike( f"%{query}%" ) # Case-insensitive partial match on email - lab_managers = [ + ) + ) + ) + + results = db.session.execute(stmt).scalars().all() + + lab_managers = [ { "lab_manager_id": user.lab_manager_id, "first_name": user.first_name, @@ -621,6 +627,7 @@ def searchLabManagers(query: str): return {"lab_managers": lab_managers}, 200 + @main_blueprint.get("/searchCourses/") def searchCourses(query: str): # Perform a search on Courses table by code and name using ILIKE for exact partial matches @@ -726,7 +733,7 @@ def createOpportunity(): db.session.add(newYear) db.session.commit() - + return {"data": "Opportunity Created", "id": newOpportunity.id}, 200