From 9b925742dd02b082b5c5f67a3e69ef3222321532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=82=98=ED=98=95=EC=A7=84?= Date: Mon, 18 Aug 2025 22:12:32 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EB=9E=AD=EC=B2=B4=EC=9D=B8?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=A0=20API=20KEY=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/api_key_api.py | 17 ++++++++++++++++- app/schemas/api_key/decrypted_response_model.py | 7 +++++++ app/services/api_key_service.py | 10 ++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 app/schemas/api_key/decrypted_response_model.py diff --git a/app/api/api_key_api.py b/app/api/api_key_api.py index 7a26140..f9be6e2 100644 --- a/app/api/api_key_api.py +++ b/app/api/api_key_api.py @@ -4,6 +4,7 @@ from app.core.response import ResponseMessage from app.core.status import CommonCode from app.schemas.api_key.create_model import APIKeyCreate +from app.schemas.api_key.decrypted_response_model import DecryptedAPIKeyResponse from app.schemas.api_key.response_model import APIKeyResponse from app.schemas.api_key.update_model import APIKeyUpdate from app.services.api_key_service import APIKeyService, api_key_service @@ -31,7 +32,6 @@ def store_api_key( response_data = APIKeyResponse( id=created_api_key.id, service_name=created_api_key.service_name.value, - api_key_encrypted=created_api_key.api_key, created_at=created_api_key.created_at, updated_at=created_api_key.updated_at, ) @@ -86,6 +86,21 @@ def get_api_key_by_service_name( return ResponseMessage.success(value=response_data, code=CommonCode.SUCCESS_GET_API_KEY) +@router.get( + "/internal/decrypted/{serviceName}", + response_model=ResponseMessage[DecryptedAPIKeyResponse], + summary="[내부용] 복호화된 API KEY 조회", + description="내부 AI 서버와 같이, 신뢰된 서비스가 복호화된 API 키를 요청할 때 사용합니다. (외부 노출 금지)", + # include_in_schema=False, # Swagger 문서에 포함하지 않음 +) +def get_decrypted_api_key( + serviceName: LLMServiceEnum, service: APIKeyService = api_key_service_dependency +) -> ResponseMessage[DecryptedAPIKeyResponse]: + """서비스 이름을 기준으로 API Key를 복호화하여 반환합니다.""" + decrypted_key = service.get_decrypted_api_key(serviceName.value) + return ResponseMessage.success(value=DecryptedAPIKeyResponse(api_key=decrypted_key)) + + @router.put( "/modify/{serviceName}", response_model=ResponseMessage[APIKeyResponse], diff --git a/app/schemas/api_key/decrypted_response_model.py b/app/schemas/api_key/decrypted_response_model.py new file mode 100644 index 0000000..65c730e --- /dev/null +++ b/app/schemas/api_key/decrypted_response_model.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel, Field + + +class DecryptedAPIKeyResponse(BaseModel): + """Decrypted API Key 응답용 스키마""" + + api_key: str = Field(..., description="복호화된 실제 API Key") diff --git a/app/services/api_key_service.py b/app/services/api_key_service.py index 36b7492..88f1730 100644 --- a/app/services/api_key_service.py +++ b/app/services/api_key_service.py @@ -61,6 +61,16 @@ def get_api_key_by_service_name(self, service_name: str) -> APIKeyInDB: except sqlite3.Error as e: raise APIException(CommonCode.FAIL) from e + def get_decrypted_api_key(self, service_name: str) -> str: + """서비스 이름으로 암호화된 API Key를 조회하고 복호화하여 반환합니다.""" + api_key_in_db = self.get_api_key_by_service_name(service_name) + try: + decrypted_key = AES256.decrypt(api_key_in_db.api_key) + return decrypted_key + except Exception as e: + # 복호화 실패 시 서버 에러 발생 + raise APIException(CommonCode.FAIL_DECRYPT_API_KEY) from e + def update_api_key(self, service_name: str, key_data: APIKeyUpdate) -> APIKeyInDB: """서비스 이름에 해당하는 API Key를 수정합니다.""" key_data.validate_with_api_key() From 4b52c37f4518d6f5eb04edddb3d5ef60ab363eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=82=98=ED=98=95=EC=A7=84?= Date: Mon, 18 Aug 2025 22:20:20 +0900 Subject: [PATCH 2/3] =?UTF-8?q?chore:=20=EC=83=81=ED=83=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/api_key_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/api/api_key_api.py b/app/api/api_key_api.py index f9be6e2..6989a7b 100644 --- a/app/api/api_key_api.py +++ b/app/api/api_key_api.py @@ -98,7 +98,9 @@ def get_decrypted_api_key( ) -> ResponseMessage[DecryptedAPIKeyResponse]: """서비스 이름을 기준으로 API Key를 복호화하여 반환합니다.""" decrypted_key = service.get_decrypted_api_key(serviceName.value) - return ResponseMessage.success(value=DecryptedAPIKeyResponse(api_key=decrypted_key)) + return ResponseMessage.success( + value=DecryptedAPIKeyResponse(api_key=decrypted_key), code=CommonCode.SUCCESS_GET_API_KEY + ) @router.put( From b0e4af842037ab70ae10e20649de6a882183aed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=82=98=ED=98=95=EC=A7=84?= Date: Mon, 18 Aug 2025 22:21:55 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20path=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/api_key_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/api_key_api.py b/app/api/api_key_api.py index 6989a7b..0617237 100644 --- a/app/api/api_key_api.py +++ b/app/api/api_key_api.py @@ -87,7 +87,7 @@ def get_api_key_by_service_name( @router.get( - "/internal/decrypted/{serviceName}", + "/find/decrypted/{serviceName}", response_model=ResponseMessage[DecryptedAPIKeyResponse], summary="[내부용] 복호화된 API KEY 조회", description="내부 AI 서버와 같이, 신뢰된 서비스가 복호화된 API 키를 요청할 때 사용합니다. (외부 노출 금지)",