diff --git a/labconnect/main/auth_routes.py b/labconnect/main/auth_routes.py index fae2de91..c822ac30 100644 --- a/labconnect/main/auth_routes.py +++ b/labconnect/main/auth_routes.py @@ -1,44 +1,55 @@ 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 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 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") @@ -50,7 +61,7 @@ def saml_login(): or current_app.config["FRONTEND_URL"] == "http://127.0.0.1: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}") @@ -70,6 +81,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 +89,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 +100,63 @@ def saml_callback(): return {"errors": errors}, 500 +@main_blueprint.post("/register") +def registerUser(): + + # Gather the new user's information + json_data = request.get_json() + if not json_data: + abort(400) + + user = User( + 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_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"} + + @main_blueprint.post("/token") def tokenRoute(): if request.json is None or request.json.get("code", None) is None: @@ -108,13 +165,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/") diff --git a/labconnect/main/opportunity_routes.py b/labconnect/main/opportunity_routes.py index 37e47b9a..4d04eb6f 100644 --- a/labconnect/main/opportunity_routes.py +++ b/labconnect/main/opportunity_routes.py @@ -587,6 +587,47 @@ def getLabManagerOpportunityCards(rcs_id: str): # return cards +# 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() + + 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 + + @main_blueprint.get("/searchCourses/") def searchCourses(query: str): # Perform a search on Courses table by code and name using ILIKE for exact partial matches @@ -871,6 +912,16 @@ def editOpportunity(opportunity_id): 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