updates
This commit is contained in:
133
Backend/src/integrations/services/api_key_service.py
Normal file
133
Backend/src/integrations/services/api_key_service.py
Normal file
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
API key service for third-party access management.
|
||||
"""
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
import secrets
|
||||
import hashlib
|
||||
from ..models.api_key import APIKey
|
||||
from ...shared.config.logging_config import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
class APIKeyService:
|
||||
"""Service for managing API keys."""
|
||||
|
||||
@staticmethod
|
||||
def generate_api_key() -> tuple[str, str]:
|
||||
"""Generate a new API key and return (key, hash)."""
|
||||
# Generate key in format: hb_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
key = f"hb_{secrets.token_urlsafe(32)}"
|
||||
key_hash = hashlib.sha256(key.encode('utf-8')).hexdigest()
|
||||
key_prefix = key[:11] # "hb_" + 8 chars
|
||||
return key, key_hash, key_prefix
|
||||
|
||||
@staticmethod
|
||||
def create_api_key(
|
||||
db: Session,
|
||||
name: str,
|
||||
scopes: List[str],
|
||||
created_by: int,
|
||||
description: Optional[str] = None,
|
||||
rate_limit: int = 100,
|
||||
expires_at: Optional[datetime] = None
|
||||
) -> tuple[APIKey, str]:
|
||||
"""Create a new API key and return (api_key_model, plain_key)."""
|
||||
key, key_hash, key_prefix = APIKeyService.generate_api_key()
|
||||
|
||||
api_key = APIKey(
|
||||
name=name,
|
||||
key_hash=key_hash,
|
||||
key_prefix=key_prefix,
|
||||
scopes=scopes,
|
||||
rate_limit=rate_limit,
|
||||
created_by=created_by,
|
||||
description=description,
|
||||
expires_at=expires_at
|
||||
)
|
||||
|
||||
db.add(api_key)
|
||||
db.commit()
|
||||
db.refresh(api_key)
|
||||
|
||||
logger.info(f'API key created: {api_key.id} - {name}')
|
||||
return api_key, key # Return plain key only once
|
||||
|
||||
@staticmethod
|
||||
def verify_api_key(db: Session, api_key: str) -> Optional[APIKey]:
|
||||
"""Verify an API key and return the APIKey model if valid."""
|
||||
key_hash = hashlib.sha256(api_key.encode('utf-8')).hexdigest()
|
||||
|
||||
api_key_model = db.query(APIKey).filter(
|
||||
APIKey.key_hash == key_hash,
|
||||
APIKey.is_active == True
|
||||
).first()
|
||||
|
||||
if api_key_model and api_key_model.is_valid:
|
||||
# Update last used timestamp
|
||||
api_key_model.last_used_at = datetime.utcnow()
|
||||
db.commit()
|
||||
return api_key_model
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def revoke_api_key(db: Session, key_id: int) -> bool:
|
||||
"""Revoke an API key."""
|
||||
api_key = db.query(APIKey).filter(APIKey.id == key_id).first()
|
||||
if api_key:
|
||||
api_key.is_active = False
|
||||
db.commit()
|
||||
logger.info(f'API key {key_id} revoked')
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_user_api_keys(
|
||||
db: Session,
|
||||
created_by: int,
|
||||
active_only: bool = True
|
||||
) -> List[APIKey]:
|
||||
"""Get all API keys created by a user."""
|
||||
query = db.query(APIKey).filter(APIKey.created_by == created_by)
|
||||
|
||||
if active_only:
|
||||
query = query.filter(APIKey.is_active == True)
|
||||
|
||||
return query.order_by(APIKey.created_at.desc()).all()
|
||||
|
||||
@staticmethod
|
||||
def update_api_key(
|
||||
db: Session,
|
||||
key_id: int,
|
||||
name: Optional[str] = None,
|
||||
scopes: Optional[List[str]] = None,
|
||||
description: Optional[str] = None,
|
||||
rate_limit: Optional[int] = None,
|
||||
expires_at: Optional[datetime] = None
|
||||
) -> Optional[APIKey]:
|
||||
"""Update an API key."""
|
||||
api_key = db.query(APIKey).filter(APIKey.id == key_id).first()
|
||||
if not api_key:
|
||||
return None
|
||||
|
||||
if name is not None:
|
||||
api_key.name = name
|
||||
if scopes is not None:
|
||||
api_key.scopes = scopes
|
||||
if description is not None:
|
||||
api_key.description = description
|
||||
if rate_limit is not None:
|
||||
api_key.rate_limit = rate_limit
|
||||
if expires_at is not None:
|
||||
api_key.expires_at = expires_at
|
||||
|
||||
db.commit()
|
||||
db.refresh(api_key)
|
||||
|
||||
logger.info(f'API key updated: {api_key.id} - {api_key.name}')
|
||||
return api_key
|
||||
|
||||
api_key_service = APIKeyService()
|
||||
|
||||
Reference in New Issue
Block a user