diff --git a/routers/jira_sync.py b/routers/jira_sync.py index a2627c2..e0b9b81 100644 --- a/routers/jira_sync.py +++ b/routers/jira_sync.py @@ -37,6 +37,7 @@ from sqlalchemy import select, desc from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user, require_admin_role +from core.crypto import encrypt_secret, decrypt_secret from database import get_db from models import ( User, SRRequest, SRStatus, @@ -101,8 +102,7 @@ async def _jira_request( payload: Optional[dict] = None ) -> Optional[dict]: """Jira REST API 호출 (오류 시 None 반환, 예외 미전파).""" - # 저장된 암호화 토큰 복호화 (실제 구현 시 core.crypto.decrypt 사용) - token = config.api_token_enc # 복호화된 토큰 (모델에서 property로 제공) + token = decrypt_secret(config.api_token_enc) auth = (config.email, token) url = f"{config.base_url.rstrip('/')}/rest/api/3{path}" try: @@ -160,8 +160,7 @@ async def save_jira_config( ) cfg = existing.scalar_one_or_none() - # API 토큰 암호화 (실제 구현: core.crypto.encrypt) - enc_token = req.api_token # TODO: AES-256-GCM 암호화 + enc_token = encrypt_secret(req.api_token) if cfg: cfg.base_url = req.base_url diff --git a/routers/multicloud.py b/routers/multicloud.py index 1ae735e..f5d591f 100644 --- a/routers/multicloud.py +++ b/routers/multicloud.py @@ -22,6 +22,7 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user, require_admin_role +from core.crypto import encrypt_secret from database import get_db from models import User, MultiCloudConfig @@ -48,7 +49,7 @@ async def add_provider(req: ProviderCreate, db: AsyncSession = Depends(get_db), tenant_id=user.tenant_id, name=req.name, provider_type=req.provider_type, region=req.region, access_key=req.access_key, - secret_key_enc=req.secret_key, # TODO: AES-256-GCM + secret_key_enc=encrypt_secret(req.secret_key), extra_config=json.dumps(req.extra_config or {}), is_active=True, created_at=datetime.utcnow(), ) diff --git a/routers/ncloud.py b/routers/ncloud.py index b6f1944..4ad4f7d 100644 --- a/routers/ncloud.py +++ b/routers/ncloud.py @@ -29,6 +29,7 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user, require_admin_role +from core.crypto import encrypt_secret, decrypt_secret from database import get_db from models import User, NCloudConfig # 신규 @@ -56,7 +57,7 @@ async def _ncloud_request(config: NCloudConfig, method: str, path: str, params: """NCloud API 호출.""" timestamp = str(int(datetime.utcnow().timestamp() * 1000)) url = f"{path}?{urlencode(params or {})}" if params else path - sig = _ncloud_signature(method, url, timestamp, config.access_key, config.secret_key_enc) + sig = _ncloud_signature(method, url, timestamp, config.access_key, decrypt_secret(config.secret_key_enc)) headers = { "x-ncp-apigw-timestamp": timestamp, "x-ncp-iam-access-key": config.access_key, @@ -83,13 +84,13 @@ async def save_ncloud_config( cfg = row.scalar_one_or_none() if cfg: cfg.access_key = req.access_key - cfg.secret_key_enc = req.secret_key # TODO: AES-256-GCM 암호화 + cfg.secret_key_enc = encrypt_secret(req.secret_key) cfg.region = req.region else: cfg = NCloudConfig( tenant_id=user.tenant_id, access_key=req.access_key, - secret_key_enc=req.secret_key, + secret_key_enc=encrypt_secret(req.secret_key), region=req.region, is_active=True, created_at=datetime.utcnow(), diff --git a/routers/public_api_hub.py b/routers/public_api_hub.py index 205b99f..6a95d02 100644 --- a/routers/public_api_hub.py +++ b/routers/public_api_hub.py @@ -25,6 +25,7 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user, require_admin_role +from core.crypto import encrypt_secret from database import get_db from models import User, PublicApiConfig @@ -68,7 +69,7 @@ async def register_api(req: ApiRegisterRequest, db: AsyncSession = Depends(get_d cfg = PublicApiConfig( tenant_id=user.tenant_id, api_id=req.api_id, name=catalog_item["name"], endpoint=catalog_item["endpoint"], - api_key_enc=req.api_key, # TODO: AES-256-GCM + api_key_enc=encrypt_secret(req.api_key), is_active=True, created_at=datetime.utcnow(), ) db.add(cfg); await db.commit() diff --git a/routers/snmp_discovery.py b/routers/snmp_discovery.py index f419651..10fc635 100644 --- a/routers/snmp_discovery.py +++ b/routers/snmp_discovery.py @@ -24,6 +24,7 @@ from sqlalchemy import select, desc from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user, require_admin_role +from core.crypto import encrypt_secret from database import get_db from models import User, SNMPConfig, SNMPDevice @@ -139,7 +140,7 @@ async def create_snmp_config( cfg = SNMPConfig( tenant_id=user.tenant_id, name=req.name, - community_enc=req.community, # TODO: AES-256-GCM 암호화 + community_enc=encrypt_secret(req.community), version=req.version, ip_ranges=req.ip_ranges, is_active=True, @@ -177,7 +178,7 @@ async def start_snmp_scan( # 기본 SNMP 설정 생성 cfg = SNMPConfig( tenant_id=user.tenant_id, name=f"scan_{datetime.utcnow().strftime('%Y%m%d%H%M')}", - community_enc=req.community, version=req.version, + community_enc=encrypt_secret(req.community), version=req.version, is_active=True, created_at=datetime.utcnow(), ) db.add(cfg) diff --git a/routers/sso_provider.py b/routers/sso_provider.py index 3e6cbf8..b9afad3 100644 --- a/routers/sso_provider.py +++ b/routers/sso_provider.py @@ -35,6 +35,7 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from core.auth import create_access_token, get_current_user, require_admin_role +from core.crypto import encrypt_secret, decrypt_secret from database import get_db from models import User, UserRole, SSOConfig, SSOSession # 신규 모델 @@ -141,7 +142,7 @@ async def _exchange_code(config: SSOConfig, code: str, redirect_uri: str) -> Opt "code": code, "redirect_uri": redirect_uri, "client_id": config.client_id, - "client_secret": config.client_secret_enc, # 복호화 필요 + "client_secret": decrypt_secret(config.client_secret_enc), }, ) return r.json() if r.status_code == 200 else None @@ -237,7 +238,7 @@ async def create_sso_config( idp_sso_url=req.idp_sso_url, idp_cert=req.idp_cert, client_id=req.client_id, - client_secret_enc=req.client_secret, # TODO: AES-256-GCM 암호화 + client_secret_enc=encrypt_secret(req.client_secret), discovery_url=req.discovery_url, scopes=req.scopes, attribute_mapping=json.dumps(req.attribute_mapping), diff --git a/routers/upstage_ocr.py b/routers/upstage_ocr.py index d4660e6..b15a64a 100644 --- a/routers/upstage_ocr.py +++ b/routers/upstage_ocr.py @@ -31,6 +31,7 @@ from sqlalchemy import select, func, desc from sqlalchemy.ext.asyncio import AsyncSession from core.auth import get_current_user, require_admin_role +from core.crypto import encrypt_secret, decrypt_secret from database import get_db from models import User, UpstageOCRConfig, OCRHistory @@ -170,13 +171,13 @@ async def save_ocr_config( ) cfg = row.scalar_one_or_none() if cfg: - cfg.api_key_enc = req.api_key # TODO: AES-256-GCM 암호화 + cfg.api_key_enc = encrypt_secret(req.api_key) cfg.model = req.model cfg.daily_limit = req.daily_limit else: cfg = UpstageOCRConfig( tenant_id=user.tenant_id, - api_key_enc=req.api_key, + api_key_enc=encrypt_secret(req.api_key), model=req.model, daily_limit=req.daily_limit, is_active=True, @@ -199,7 +200,7 @@ async def get_ocr_config( cfg = row.scalar_one_or_none() if not cfg: return {"configured": False} - key = cfg.api_key_enc or "" + key = decrypt_secret(cfg.api_key_enc) or "" masked_key = f"{key[:6]}{'*' * (len(key) - 10)}{key[-4:]}" if len(key) > 10 else "***" return { "configured": True, @@ -231,7 +232,7 @@ async def parse_document( async with httpx.AsyncClient(timeout=120) as client: r = await client.post( f"{UPSTAGE_BASE}/document-digitization", - headers={"Authorization": f"Bearer {cfg.api_key_enc}"}, + headers={"Authorization": f"Bearer {decrypt_secret(cfg.api_key_enc)}"}, files={"document": (file.filename, file_bytes, mime)}, data={ "model": model or cfg.model, @@ -285,7 +286,7 @@ async def extract_information( async with httpx.AsyncClient(timeout=120) as client: r = await client.post( f"{UPSTAGE_BASE}/information-extraction", - headers={"Authorization": f"Bearer {cfg.api_key_enc}"}, + headers={"Authorization": f"Bearer {decrypt_secret(cfg.api_key_enc)}"}, files={"document": (file.filename, file_bytes, mime)}, data={"schema": json.dumps(schema_dict, ensure_ascii=False)} ) @@ -341,7 +342,7 @@ async def document_qa( async with httpx.AsyncClient(timeout=120) as client: r = await client.post( f"{UPSTAGE_BASE}/document-qa", - headers={"Authorization": f"Bearer {cfg.api_key_enc}"}, + headers={"Authorization": f"Bearer {decrypt_secret(cfg.api_key_enc)}"}, files={"document": (file.filename, file_bytes, mime)}, data={"question": question} ) @@ -385,14 +386,14 @@ async def batch_parse( if mode == "extract" and schema: r = await client.post( f"{UPSTAGE_BASE}/information-extraction", - headers={"Authorization": f"Bearer {cfg.api_key_enc}"}, + headers={"Authorization": f"Bearer {decrypt_secret(cfg.api_key_enc)}"}, files={"document": (file.filename, file_bytes, mime)}, data={"schema": schema} ) else: r = await client.post( f"{UPSTAGE_BASE}/document-digitization", - headers={"Authorization": f"Bearer {cfg.api_key_enc}"}, + headers={"Authorization": f"Bearer {decrypt_secret(cfg.api_key_enc)}"}, files={"document": (file.filename, file_bytes, mime)}, data={"model": cfg.model, "ocr": "auto", "output_formats": '["text"]'} )