the middle of the idiots

This commit is contained in:
2025-10-24 16:29:40 -05:00
parent 6a58e19b10
commit 721301c779
2472 changed files with 237076 additions and 418 deletions

View File

@@ -0,0 +1,116 @@
"""
Job application management service
"""
from typing import List, Optional
from sqlalchemy.orm import Session
from ..models import Application, JobPosting, Resume
from ..config.settings import settings
def create_application(db: Session, user_id: int, job_posting_id: int, resume_id: int,
cover_letter: str = None, tenant_id: int = None) -> Application:
"""
Create a new job application
"""
# Verify that the resume belongs to the user
resume = db.query(Resume).filter(
Resume.id == resume_id,
Resume.user_id == user_id
).first()
if not resume:
raise ValueError("Resume does not belong to user")
# In a multi-tenant setup, we should verify tenant access to the job posting
if settings.MULTI_TENANT_ENABLED and tenant_id:
job_posting = db.query(JobPosting).filter(
JobPosting.id == job_posting_id,
JobPosting.tenant_id == tenant_id
).first()
if not job_posting:
raise ValueError("Job posting not found in tenant")
db_application = Application(
user_id=user_id,
job_posting_id=job_posting_id,
resume_id=resume_id,
cover_letter=cover_letter
)
db.add(db_application)
db.commit()
db.refresh(db_application)
return db_application
def get_user_applications(db: Session, user_id: int) -> List[Application]:
"""
Get all applications for a specific user
"""
return db.query(Application).filter(Application.user_id == user_id).all()
def get_job_applications(db: Session, job_posting_id: int, tenant_id: int = None) -> List[Application]:
"""
Get all applications for a specific job posting within a tenant
"""
query = db.query(Application).join(JobPosting).filter(Application.job_posting_id == job_posting_id)
if settings.MULTI_TENANT_ENABLED and tenant_id:
query = query.filter(JobPosting.tenant_id == tenant_id)
return query.all()
def get_application_by_id(db: Session, application_id: int, user_id: int = None,
job_posting_id: int = None) -> Optional[Application]:
"""
Get a specific application by ID with optional user or job constraints
"""
query = db.query(Application).filter(Application.id == application_id)
if user_id:
query = query.filter(Application.user_id == user_id)
if job_posting_id:
query = query.filter(Application.job_posting_id == job_posting_id)
return query.first()
def update_application_status(db: Session, application_id: int, status: str,
user_id: int = None, job_posting_id: int = None) -> Optional[Application]:
"""
Update application status
"""
query = db.query(Application).filter(Application.id == application_id)
if user_id:
query = query.filter(Application.user_id == user_id)
if job_posting_id:
query = query.filter(Application.job_posting_id == job_posting_id)
db_application = query.first()
if not db_application:
return None
db_application.status = status
db.commit()
db.refresh(db_application)
return db_application
def delete_application(db: Session, application_id: int, user_id: int = None) -> bool:
"""
Delete an application
"""
query = db.query(Application).filter(Application.id == application_id)
if user_id:
query = query.filter(Application.user_id == user_id)
db_application = query.first()
if not db_application:
return False
db.delete(db_application)
db.commit()
return True

View File

@@ -0,0 +1,108 @@
"""
Authentication service with OIDC and social media support
"""
from datetime import datetime, timedelta
from typing import Optional
from jose import jwt
from fastapi import HTTPException, status
from sqlalchemy.orm import Session
from ..config.settings import settings
from ..models import User
from ..database import SessionLocal
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
"""Create a JWT access token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt
def verify_token(token: str) -> dict:
"""Verify a JWT token and return the payload"""
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
return payload
except jwt.JWTError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
def authenticate_user(db: Session, username: str, password: str) -> Optional[User]:
"""Authenticate a user with username and password"""
user = db.query(User).filter(User.username == username).first()
if not user or not verify_password_correct(password, user.hashed_password):
return None
if not user.is_active:
return None
return user
async def get_oidc_config():
"""Get OIDC configuration"""
return {
"issuer": settings.OIDC_ISSUER,
"authorization_endpoint": f"{settings.OIDC_ISSUER}/authorize" if settings.OIDC_ISSUER else None,
"token_endpoint": f"{settings.OIDC_ISSUER}/token" if settings.OIDC_ISSUER else None,
"userinfo_endpoint": f"{settings.OIDC_ISSUER}/userinfo" if settings.OIDC_ISSUER else None,
"jwks_uri": f"{settings.OIDC_ISSUER}/.well-known/jwks.json" if settings.OIDC_ISSUER else None,
"client_id": settings.OIDC_CLIENT_ID,
"redirect_uri": settings.OIDC_REDIRECT_URI,
}
async def handle_oidc_callback(code: str) -> User:
"""
Handle OIDC callback and return or create a user
This is a simplified implementation - in a real app you'd exchange the code
for tokens and fetch user info from the OIDC provider
"""
# In a real implementation, you would:
# 1. Exchange the authorization code for access/id tokens
# 2. Validate the ID token
# 3. Fetch user info from the userinfo endpoint
# 4. Create or update the user in your database
# 5. Return the user object
# For now, return a placeholder - this would be replaced with real OIDC logic
raise NotImplementedError("OIDC callback handling not fully implemented")
async def handle_social_login(provider: str, access_token: str) -> User:
"""
Handle social media login (Google, Facebook, etc.)
This is a simplified implementation
"""
# In a real implementation, you would:
# 1. Validate the access token with the social provider
# 2. Fetch user info from the provider's API
# 3. Create or update the user in your database
# 4. Return the user object
# For now, return a placeholder - this would be replaced with real social login logic
raise NotImplementedError("Social login handling not fully implemented")
def hash_password(password: str) -> str:
"""
Hash a password using bcrypt
"""
from ..utils.security import get_password_hash
return get_password_hash(password)
def verify_password_correct(plain_password: str, hashed_password: str) -> bool:
"""
Verify a plain password against its hash
"""
from ..utils.security import verify_password as verify_password_util
return verify_password_util(plain_password, hashed_password)

View File

@@ -0,0 +1,100 @@
"""
Resume management service
"""
from typing import List, Optional
from sqlalchemy.orm import Session
from ..models import Resume, User
from ..config.settings import settings
def create_resume(db: Session, user_id: int, title: str, content: str, tenant_id: int) -> Resume:
"""
Create a new resume for a user
"""
db_resume = Resume(
user_id=user_id,
title=title,
content=content,
tenant_id=tenant_id
)
db.add(db_resume)
db.commit()
db.refresh(db_resume)
return db_resume
def get_user_resumes(db: Session, user_id: int, tenant_id: int = None) -> List[Resume]:
"""
Get all resumes for a specific user
"""
query = db.query(Resume).filter(Resume.user_id == user_id)
if settings.MULTI_TENANT_ENABLED and tenant_id:
query = query.filter(Resume.tenant_id == tenant_id)
return query.all()
def get_resume_by_id(db: Session, resume_id: int, user_id: int, tenant_id: int = None) -> Optional[Resume]:
"""
Get a specific resume by ID for a user
"""
query = db.query(Resume).filter(
Resume.id == resume_id,
Resume.user_id == user_id
)
if settings.MULTI_TENANT_ENABLED and tenant_id:
query = query.filter(Resume.tenant_id == tenant_id)
return query.first()
def update_resume(db: Session, resume_id: int, user_id: int, tenant_id: int = None, title: str = None, content: str = None) -> Optional[Resume]:
"""
Update a resume
"""
query = db.query(Resume).filter(
Resume.id == resume_id,
Resume.user_id == user_id
)
if settings.MULTI_TENANT_ENABLED and tenant_id:
query = query.filter(Resume.tenant_id == tenant_id)
db_resume = query.first()
if not db_resume:
return None
if title is not None:
db_resume.title = title
if content is not None:
db_resume.content = content
db.commit()
db.refresh(db_resume)
return db_resume
def delete_resume(db: Session, resume_id: int, user_id: int, tenant_id: int = None) -> bool:
"""
Delete a resume
"""
query = db.query(Resume).filter(
Resume.id == resume_id,
Resume.user_id == user_id
)
if settings.MULTI_TENANT_ENABLED and tenant_id:
query = query.filter(Resume.tenant_id == tenant_id)
db_resume = query.first()
if not db_resume:
return False
db.delete(db_resume)
db.commit()
return True

View File

@@ -0,0 +1,49 @@
"""
Multi-tenant service for managing tenant-specific operations
"""
from typing import Optional
from fastapi import Request
from sqlalchemy.orm import Session
from ..models import Tenant
from ..config.settings import settings
def get_current_tenant_id(request: Request) -> Optional[int]:
"""
Get the current tenant ID from the request
"""
return getattr(request.state, 'tenant_id', None)
def verify_tenant_access(request: Request, db: Session, resource_tenant_id: int) -> bool:
"""
Verify that the current tenant has access to a resource
"""
if not settings.MULTI_TENANT_ENABLED:
return True # If multi-tenancy is disabled, allow access
current_tenant_id = get_current_tenant_id(request)
return current_tenant_id == resource_tenant_id
def check_tenant_isolation(request: Request, db: Session, model_class, id: int) -> bool:
"""
Check if a specific instance of a model belongs to the current tenant
"""
if not settings.MULTI_TENANT_ENABLED:
return True
current_tenant_id = get_current_tenant_id(request)
# Assuming the model has a tenant_id attribute
instance = db.query(model_class).filter(model_class.id == id).first()
if not instance:
return False
# This is a generic approach - in practice you'd need to handle specific model types
if hasattr(instance, 'tenant_id'):
return instance.tenant_id == current_tenant_id
else:
# For models that aren't tenant-specific, allow access
return True