115 lines
5.5 KiB
Python
115 lines
5.5 KiB
Python
"""GRC 자동화 — Governance Risk Compliance"""
|
|
from __future__ import annotations
|
|
import json, logging
|
|
from datetime import datetime
|
|
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query
|
|
from pydantic import BaseModel
|
|
from core.auth import get_current_user, require_admin_role
|
|
from models import User
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix="/api/grc", tags=["GRC 자동화"])
|
|
|
|
_policies: list[dict] = []
|
|
_risks: list[dict] = []
|
|
_pid = 0; _rid = 0
|
|
|
|
class PolicyCreate(BaseModel):
|
|
title: str; category: str = "security"; content: str = ""; version: str = "1.0"
|
|
|
|
class RiskCreate(BaseModel):
|
|
title: str; likelihood: int = 3; impact: int = 3; mitigation: str = ""
|
|
|
|
@router.get("/policies")
|
|
async def list_policies(category: str = Query(None), user: User = Depends(get_current_user)):
|
|
items = _policies if not category else [p for p in _policies if p["category"] == category]
|
|
return {"total": len(items), "items": items}
|
|
|
|
@router.post("/policies")
|
|
async def create_policy(body: PolicyCreate, user: User = Depends(require_admin_role)):
|
|
global _pid; _pid += 1
|
|
draft = body.content
|
|
if not draft:
|
|
try:
|
|
import httpx
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
r = await c.post("http://localhost:11434/api/generate", json={
|
|
"model": "llama3", "stream": False,
|
|
"prompt": f"다음 IT 보안 정책을 한국어로 3개 항목으로 작성하라: {body.title}"})
|
|
draft = r.json().get("response", f"{body.title} 정책 초안")
|
|
except Exception:
|
|
draft = f"{body.title}: 정책 내용을 입력하세요."
|
|
p = {"id": _pid, "title": body.title, "category": body.category,
|
|
"content": draft, "version": body.version, "status": "draft",
|
|
"created_by": user.username, "created_at": datetime.utcnow().isoformat()}
|
|
_policies.append(p); return p
|
|
|
|
@router.put("/policies/{pid}")
|
|
async def update_policy(pid: int, body: PolicyCreate, user: User = Depends(require_admin_role)):
|
|
for p in _policies:
|
|
if p["id"] == pid:
|
|
p.update({"title": body.title, "category": body.category,
|
|
"content": body.content, "version": body.version}); return p
|
|
raise HTTPException(404, "정책 없음")
|
|
|
|
@router.get("/risk-matrix")
|
|
async def risk_matrix(user: User = Depends(get_current_user)):
|
|
m = {"critical": [], "high": [], "medium": [], "low": []}
|
|
for r in _risks:
|
|
s = r["risk_score"]
|
|
if s >= 20: m["critical"].append(r)
|
|
elif s >= 12: m["high"].append(r)
|
|
elif s >= 6: m["medium"].append(r)
|
|
else: m["low"].append(r)
|
|
return {"total": len(_risks), "matrix": m}
|
|
|
|
@router.post("/risk-assessment")
|
|
async def create_risk(body: RiskCreate, user: User = Depends(require_admin_role)):
|
|
global _rid; _rid += 1
|
|
s = body.likelihood * body.impact
|
|
lv = "CRITICAL" if s >= 20 else "HIGH" if s >= 12 else "MEDIUM" if s >= 6 else "LOW"
|
|
r = {"id": _rid, "title": body.title, "likelihood": body.likelihood,
|
|
"impact": body.impact, "risk_score": s, "level": lv,
|
|
"mitigation": body.mitigation, "status": "open",
|
|
"created_at": datetime.utcnow().isoformat()}
|
|
_risks.append(r); return r
|
|
|
|
@router.get("/compliance")
|
|
async def compliance_summary(user: User = Depends(get_current_user)):
|
|
crit = sum(1 for r in _risks if r.get("level") == "CRITICAL")
|
|
score = max(0, 100 - crit * 10)
|
|
return {"policies": {"total": len(_policies), "active": sum(1 for p in _policies if p.get("status") == "active")},
|
|
"risks": {"total": len(_risks), "critical": crit},
|
|
"compliance_score": score,
|
|
"grade": "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "D"}
|
|
|
|
@router.post("/audit-report")
|
|
async def audit_report(standard: str = Query("CSAP"), user: User = Depends(require_admin_role)):
|
|
crit = sum(1 for r in _risks if r.get("level") == "CRITICAL")
|
|
score = max(0, 100 - crit * 10)
|
|
return {"standard": standard, "generated_at": datetime.utcnow().isoformat(),
|
|
"summary": {"total_policies": len(_policies), "total_risks": len(_risks),
|
|
"critical_risks": crit, "compliance_score": score},
|
|
"findings": [f"CRITICAL 리스크 {crit}건", f"{standard} 준수율 {score}%"],
|
|
"recommendations": ["CRITICAL 리스크 즉시 완화 조치 필요"] if crit else ["컴플라이언스 상태 양호"]}
|
|
|
|
@router.get("/templates")
|
|
async def policy_templates(user: User = Depends(get_current_user)):
|
|
return {"templates": [
|
|
{"id": 1, "name": "SSH root 접속 금지 정책", "category": "security"},
|
|
{"id": 2, "name": "비밀번호 90일 주기 변경 정책", "category": "access"},
|
|
{"id": 3, "name": "미사용 계정 30일 비활성화 정책", "category": "access"},
|
|
{"id": 4, "name": "서버 패치 30일 내 적용 정책", "category": "operation"},
|
|
{"id": 5, "name": "백업 7일 보관 확인 정책", "category": "operation"},
|
|
]}
|
|
|
|
@router.get("/dashboard")
|
|
async def grc_dashboard(user: User = Depends(get_current_user)):
|
|
crit = sum(1 for r in _risks if r.get("level") == "CRITICAL")
|
|
score = max(0, 100 - crit * 10)
|
|
return {"compliance_score": score,
|
|
"grade": "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "D",
|
|
"policies": len(_policies), "risks": len(_risks), "critical_risks": crit,
|
|
"recent_policies": _policies[-3:][::-1],
|
|
"top_risks": sorted(_risks, key=lambda x: x["risk_score"], reverse=True)[:3]}
|