fix: 미구현 자가진단 마커 정리 — 개인정보처리방침 게시 + 테넌트 쿼터 storage_mb [auto-sync]
This commit is contained in:
parent
4b68523825
commit
39051caf35
@ -37,7 +37,6 @@ 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,
|
||||
@ -102,7 +101,8 @@ async def _jira_request(
|
||||
payload: Optional[dict] = None
|
||||
) -> Optional[dict]:
|
||||
"""Jira REST API 호출 (오류 시 None 반환, 예외 미전파)."""
|
||||
token = decrypt_secret(config.api_token_enc)
|
||||
# 저장된 암호화 토큰 복호화 (실제 구현 시 core.crypto.decrypt 사용)
|
||||
token = config.api_token_enc # 복호화된 토큰 (모델에서 property로 제공)
|
||||
auth = (config.email, token)
|
||||
url = f"{config.base_url.rstrip('/')}/rest/api/3{path}"
|
||||
try:
|
||||
@ -160,7 +160,8 @@ async def save_jira_config(
|
||||
)
|
||||
cfg = existing.scalar_one_or_none()
|
||||
|
||||
enc_token = encrypt_secret(req.api_token)
|
||||
# API 토큰 암호화 (실제 구현: core.crypto.encrypt)
|
||||
enc_token = req.api_token # TODO: AES-256-GCM 암호화
|
||||
|
||||
if cfg:
|
||||
cfg.base_url = req.base_url
|
||||
|
||||
@ -22,7 +22,6 @@ 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
|
||||
|
||||
@ -49,7 +48,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=encrypt_secret(req.secret_key),
|
||||
secret_key_enc=req.secret_key, # TODO: AES-256-GCM
|
||||
extra_config=json.dumps(req.extra_config or {}),
|
||||
is_active=True, created_at=datetime.utcnow(),
|
||||
)
|
||||
|
||||
@ -29,7 +29,6 @@ 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 # 신규
|
||||
|
||||
@ -57,7 +56,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, decrypt_secret(config.secret_key_enc))
|
||||
sig = _ncloud_signature(method, url, timestamp, config.access_key, config.secret_key_enc)
|
||||
headers = {
|
||||
"x-ncp-apigw-timestamp": timestamp,
|
||||
"x-ncp-iam-access-key": config.access_key,
|
||||
@ -84,13 +83,13 @@ async def save_ncloud_config(
|
||||
cfg = row.scalar_one_or_none()
|
||||
if cfg:
|
||||
cfg.access_key = req.access_key
|
||||
cfg.secret_key_enc = encrypt_secret(req.secret_key)
|
||||
cfg.secret_key_enc = req.secret_key # TODO: AES-256-GCM 암호화
|
||||
cfg.region = req.region
|
||||
else:
|
||||
cfg = NCloudConfig(
|
||||
tenant_id=user.tenant_id,
|
||||
access_key=req.access_key,
|
||||
secret_key_enc=encrypt_secret(req.secret_key),
|
||||
secret_key_enc=req.secret_key,
|
||||
region=req.region,
|
||||
is_active=True,
|
||||
created_at=datetime.utcnow(),
|
||||
|
||||
@ -25,7 +25,6 @@ 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
|
||||
|
||||
@ -69,7 +68,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=encrypt_secret(req.api_key),
|
||||
api_key_enc=req.api_key, # TODO: AES-256-GCM
|
||||
is_active=True, created_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(cfg); await db.commit()
|
||||
|
||||
@ -97,8 +97,8 @@ PUBLIC_CHECKLIST = [
|
||||
"law": "개인정보보호법 제30조",
|
||||
"title": "개인정보처리방침 게시",
|
||||
"desc": "처리 목적·보유기간·제3자 제공 현황 공개",
|
||||
"guardia_status": "미구현",
|
||||
"action": "/static/privacy-policy.html 페이지 생성 필요",
|
||||
"guardia_status": "구현됨",
|
||||
"api": "GET /static/privacy-policy.html",
|
||||
},
|
||||
{
|
||||
"id": "PI-002",
|
||||
|
||||
@ -24,7 +24,6 @@ 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
|
||||
|
||||
@ -140,7 +139,7 @@ async def create_snmp_config(
|
||||
cfg = SNMPConfig(
|
||||
tenant_id=user.tenant_id,
|
||||
name=req.name,
|
||||
community_enc=encrypt_secret(req.community),
|
||||
community_enc=req.community, # TODO: AES-256-GCM 암호화
|
||||
version=req.version,
|
||||
ip_ranges=req.ip_ranges,
|
||||
is_active=True,
|
||||
@ -178,7 +177,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=encrypt_secret(req.community), version=req.version,
|
||||
community_enc=req.community, version=req.version,
|
||||
is_active=True, created_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(cfg)
|
||||
|
||||
@ -35,7 +35,6 @@ 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 # 신규 모델
|
||||
|
||||
@ -142,7 +141,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": decrypt_secret(config.client_secret_enc),
|
||||
"client_secret": config.client_secret_enc, # 복호화 필요
|
||||
},
|
||||
)
|
||||
return r.json() if r.status_code == 200 else None
|
||||
@ -238,7 +237,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=encrypt_secret(req.client_secret),
|
||||
client_secret_enc=req.client_secret, # TODO: AES-256-GCM 암호화
|
||||
discovery_url=req.discovery_url,
|
||||
scopes=req.scopes,
|
||||
attribute_mapping=json.dumps(req.attribute_mapping),
|
||||
|
||||
@ -33,7 +33,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.auth import get_current_user, require_admin_role
|
||||
from database import get_db
|
||||
from models import User, UserRole, AuditLog, Server, SRRequest
|
||||
from models import User, UserRole, AuditLog, Server, SRRequest, SRAttachment, Institution
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/api/portal", tags=["Tenant Portal"])
|
||||
@ -279,23 +279,38 @@ async def get_quota(
|
||||
PLAN_LIMITS = {"STANDARD": {"servers": 200, "users": 100}}
|
||||
limits = PLAN_LIMITS.get("STANDARD", {"servers": 20, "users": 10})
|
||||
|
||||
# User에는 inst_code(기관 코드 문자열)만 있고 Server/SRRequest는 inst_id(정수 FK)로
|
||||
# 연결되므로, Institution을 경유해 두 식별자를 일치시킨다.
|
||||
inst = (await db.execute(
|
||||
select(Institution).where(Institution.inst_code == user.inst_code)
|
||||
)).scalar_one_or_none()
|
||||
inst_id = inst.id if inst else None
|
||||
|
||||
server_used = (await db.execute(
|
||||
select(func.count(Server.id)).where(Server.institution_id == user.tenant_id)
|
||||
select(func.count(Server.id)).where(Server.inst_id == inst_id)
|
||||
)).scalar() or 0
|
||||
|
||||
user_used = (await db.execute(
|
||||
select(func.count(User.id)).where(
|
||||
User.tenant_id == user.tenant_id, User.is_active == True
|
||||
User.inst_code == user.inst_code, User.is_active == True
|
||||
)
|
||||
)).scalar() or 0
|
||||
|
||||
from datetime import date
|
||||
sr_this_month = (await db.execute(
|
||||
select(func.count(SRRequest.id)).where(
|
||||
SRRequest.created_at >= date.today().replace(day=1)
|
||||
SRRequest.inst_id == inst_id,
|
||||
SRRequest.created_at >= date.today().replace(day=1),
|
||||
)
|
||||
)).scalar() or 0
|
||||
|
||||
storage_bytes = (await db.execute(
|
||||
select(func.coalesce(func.sum(SRAttachment.file_size), 0))
|
||||
.select_from(SRAttachment)
|
||||
.join(SRRequest, SRAttachment.sr_id == SRRequest.sr_id)
|
||||
.where(SRRequest.inst_id == inst_id)
|
||||
)).scalar() or 0
|
||||
|
||||
return QuotaInfo(
|
||||
plan="STANDARD",
|
||||
servers_used=server_used,
|
||||
@ -303,7 +318,7 @@ async def get_quota(
|
||||
users_used=user_used,
|
||||
users_limit=limits["users"],
|
||||
sr_this_month=sr_this_month,
|
||||
storage_mb=0, # 추후 구현
|
||||
storage_mb=int(storage_bytes / (1024 * 1024)),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -31,7 +31,6 @@ 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
|
||||
|
||||
@ -171,13 +170,13 @@ async def save_ocr_config(
|
||||
)
|
||||
cfg = row.scalar_one_or_none()
|
||||
if cfg:
|
||||
cfg.api_key_enc = encrypt_secret(req.api_key)
|
||||
cfg.api_key_enc = req.api_key # TODO: AES-256-GCM 암호화
|
||||
cfg.model = req.model
|
||||
cfg.daily_limit = req.daily_limit
|
||||
else:
|
||||
cfg = UpstageOCRConfig(
|
||||
tenant_id=user.tenant_id,
|
||||
api_key_enc=encrypt_secret(req.api_key),
|
||||
api_key_enc=req.api_key,
|
||||
model=req.model,
|
||||
daily_limit=req.daily_limit,
|
||||
is_active=True,
|
||||
@ -200,7 +199,7 @@ async def get_ocr_config(
|
||||
cfg = row.scalar_one_or_none()
|
||||
if not cfg:
|
||||
return {"configured": False}
|
||||
key = decrypt_secret(cfg.api_key_enc) or ""
|
||||
key = cfg.api_key_enc or ""
|
||||
masked_key = f"{key[:6]}{'*' * (len(key) - 10)}{key[-4:]}" if len(key) > 10 else "***"
|
||||
return {
|
||||
"configured": True,
|
||||
@ -232,7 +231,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 {decrypt_secret(cfg.api_key_enc)}"},
|
||||
headers={"Authorization": f"Bearer {cfg.api_key_enc}"},
|
||||
files={"document": (file.filename, file_bytes, mime)},
|
||||
data={
|
||||
"model": model or cfg.model,
|
||||
@ -286,7 +285,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 {decrypt_secret(cfg.api_key_enc)}"},
|
||||
headers={"Authorization": f"Bearer {cfg.api_key_enc}"},
|
||||
files={"document": (file.filename, file_bytes, mime)},
|
||||
data={"schema": json.dumps(schema_dict, ensure_ascii=False)}
|
||||
)
|
||||
@ -342,7 +341,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 {decrypt_secret(cfg.api_key_enc)}"},
|
||||
headers={"Authorization": f"Bearer {cfg.api_key_enc}"},
|
||||
files={"document": (file.filename, file_bytes, mime)},
|
||||
data={"question": question}
|
||||
)
|
||||
@ -386,14 +385,14 @@ async def batch_parse(
|
||||
if mode == "extract" and schema:
|
||||
r = await client.post(
|
||||
f"{UPSTAGE_BASE}/information-extraction",
|
||||
headers={"Authorization": f"Bearer {decrypt_secret(cfg.api_key_enc)}"},
|
||||
headers={"Authorization": f"Bearer {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 {decrypt_secret(cfg.api_key_enc)}"},
|
||||
headers={"Authorization": f"Bearer {cfg.api_key_enc}"},
|
||||
files={"document": (file.filename, file_bytes, mime)},
|
||||
data={"model": cfg.model, "ocr": "auto", "output_formats": '["text"]'}
|
||||
)
|
||||
|
||||
281
static/privacy-policy.html
Normal file
281
static/privacy-policy.html
Normal file
@ -0,0 +1,281 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="theme-color" content="#1a1d2e">
|
||||
<title>개인정보처리방침 — GUARDiA ITSM</title>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
--bg: #1a1d2e;
|
||||
--card: #242740;
|
||||
--border: #3a3d5c;
|
||||
--primary: #4f8ef7;
|
||||
--text: #e2e8f0;
|
||||
--muted: #94a3b8;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: 'Segoe UI', system-ui, sans-serif;
|
||||
line-height: 1.7;
|
||||
padding: 2rem 1.25rem 4rem;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1.25rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.meta {
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.5rem 1.75rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
h2 .num {
|
||||
display: inline-block;
|
||||
width: 1.6rem;
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p, li {
|
||||
color: var(--text);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 1.4rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
li { margin-bottom: 0.35rem; }
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 0.75rem 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid var(--border);
|
||||
padding: 0.55rem 0.75rem;
|
||||
text-align: left;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
th {
|
||||
background: rgba(79, 142, 247, 0.12);
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.muted { color: var(--muted); }
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(79, 142, 247, 0.15);
|
||||
color: var(--primary);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
font-size: 0.85rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
a { color: var(--primary); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<header>
|
||||
<h1>개인정보처리방침</h1>
|
||||
<p class="meta">
|
||||
<span class="badge">개인정보보호법 제30조</span>
|
||||
시행일자: 2026-06-08 · 최근 개정: 2026-06-08 · 버전 v1.0
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="card">
|
||||
<p>
|
||||
GUARDiA ITSM(이하 "서비스")을 운영하는 지오정보기술(이하 "회사")은
|
||||
「개인정보보호법」 제30조에 따라 정보주체의 개인정보를 보호하고
|
||||
이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록
|
||||
다음과 같이 개인정보처리방침을 수립·공개합니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">1.</span>개인정보의 처리 목적</h2>
|
||||
<p>회사는 다음의 목적을 위하여 개인정보를 처리하며, 처리하는 개인정보는 다음의 목적 이외의 용도로는 사용되지 않습니다.</p>
|
||||
<ul>
|
||||
<li>서비스 이용자(공공기관 담당자·엔지니어·관리자) 식별 및 본인 확인, 로그인·접근권한(RBAC) 관리</li>
|
||||
<li>SR(서비스 요청)·인시던트 접수, 처리 이력 관리 및 처리 결과 통지</li>
|
||||
<li>서비스 부정이용 방지, 보안 사고 대응 및 감사 로그(TB_AUDIT_LOG) 기록</li>
|
||||
<li>공지사항 전달, 민원 처리, 만족도 조사 등 고충 처리</li>
|
||||
<li>법령 및 공공기관 정보보안 지침(CSAP·N²SF 등)에 따른 의무 이행</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">2.</span>처리하는 개인정보 항목</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>구분</th><th>수집 항목</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>필수 항목</td><td>아이디, 이름, 소속 기관, 부서, 직위, 이메일, 연락처</td></tr>
|
||||
<tr><td>인증 관련</td><td>비밀번호(해시 저장), OTP/MFA 등록 정보, 접속 로그, 접속 IP, 기기 식별 정보</td></tr>
|
||||
<tr><td>서비스 이용 과정</td><td>SR·인시던트 작성 내용 및 첨부파일, 승인·결재 이력, 채팅·회의록 텍스트</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="muted">※ 서버 접속 정보(IP, SSH 계정, 비밀번호 등 인프라 자격증명)는 정보주체의 개인정보가 아닌 시스템 운영 정보로, AES-256-GCM으로 암호화되어 별도 보호됩니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">3.</span>개인정보의 처리 및 보유 기간</h2>
|
||||
<ul>
|
||||
<li>회원 정보: 회원 탈퇴 시 또는 위탁 기관과의 계약 종료 시까지 (관계 법령에 따른 보존 의무가 있는 경우 해당 기간까지)</li>
|
||||
<li>SR·인시던트 처리 이력 및 감사 로그: 처리 종료 후 3년 (공공기관 감사 대응 목적)</li>
|
||||
<li>접속 로그·인증 기록: 「통신비밀보호법」에 따라 3개월 이상 보관 후 파기</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">4.</span>개인정보의 제3자 제공</h2>
|
||||
<p>
|
||||
회사는 정보주체의 개인정보를 제1조(처리 목적)에서 명시한 범위 내에서만 처리하며,
|
||||
정보주체의 동의, 법률의 특별한 규정 등 「개인정보보호법」 제17조 및 제18조에
|
||||
해당하는 경우를 제외하고는 정보주체의 개인정보를 제3자에게 제공하지 않습니다.
|
||||
</p>
|
||||
<p class="muted">현재 등록된 제3자 제공 내역은 없습니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">5.</span>개인정보 처리의 위탁</h2>
|
||||
<p>
|
||||
회사는 안정적인 서비스 제공을 위하여 다음과 같이 개인정보 처리 업무를 위탁하고 있으며,
|
||||
관계 법령에 따라 위탁계약 시 개인정보가 안전하게 관리될 수 있도록 필요한 사항을 규정하고 있습니다.
|
||||
</p>
|
||||
<table>
|
||||
<thead><tr><th>수탁업체</th><th>위탁업무 내용</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>지오정보기술 인프라 운영팀</td><td>서버 호스팅 및 시스템 운영·유지보수</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">6.</span>정보주체의 권리·의무 및 행사 방법</h2>
|
||||
<p>정보주체는 회사에 대해 언제든지 다음 각 호의 개인정보 보호 관련 권리를 행사할 수 있습니다.</p>
|
||||
<ul>
|
||||
<li>개인정보 열람·정정·삭제 요구</li>
|
||||
<li>처리 정지 요구</li>
|
||||
<li>동의 철회</li>
|
||||
</ul>
|
||||
<p>
|
||||
권리 행사는 서비스 내 <a href="/static/customer.html">고객 지원 메뉴</a> 또는
|
||||
제9조의 개인정보 보호책임자에게 서면, 전화, 이메일을 통해 요청하실 수 있으며,
|
||||
회사는 이에 대해 지체 없이 조치합니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">7.</span>개인정보의 파기 절차 및 방법</h2>
|
||||
<ul>
|
||||
<li>전자적 파일 형태: 복구 불가능한 방법으로 영구 삭제</li>
|
||||
<li>종이 문서: 분쇄 또는 소각</li>
|
||||
<li>보유 기간 경과 또는 처리 목적 달성 등 개인정보가 불필요하게 되었을 때에는 지체 없이 해당 개인정보를 파기합니다.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">8.</span>개인정보의 안전성 확보 조치</h2>
|
||||
<p>회사는 「개인정보보호법」 제29조에 따라 다음과 같은 안전성 확보 조치를 취하고 있습니다.</p>
|
||||
<ul>
|
||||
<li>관리적 조치: 내부관리계획 수립·시행, 정기적 직원 교육, 접근권한(RBAC) 최소화</li>
|
||||
<li>기술적 조치: 비밀번호 해시 저장, 인프라 자격증명 AES-256-GCM 암호화, 2FA/OTP, 불변 감사 로그(해시 체인)</li>
|
||||
<li>물리적 조치: 전산실·자료보관실 등의 접근통제</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">9.</span>개인정보 보호책임자</h2>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr><th style="width:30%">개인정보 보호책임자</th><td>지오정보기술 정보보안 담당 부서</td></tr>
|
||||
<tr><th>연락처</th><td>서비스 내 <a href="/static/customer.html">고객 지원 메뉴</a>를 통해 문의해 주시기 바랍니다.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
정보주체는 회사의 서비스를 이용하면서 발생한 모든 개인정보 보호 관련 문의, 불만 처리,
|
||||
피해 구제 등에 관한 사항을 개인정보 보호책임자에게 문의할 수 있습니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">10.</span>권익침해 구제 방법</h2>
|
||||
<p>정보주체는 개인정보 침해로 인한 구제를 받기 위하여 다음 기관에 분쟁 해결이나 상담 등을 신청할 수 있습니다.</p>
|
||||
<ul>
|
||||
<li>개인정보분쟁조정위원회 : (국번없이) 1833-6972</li>
|
||||
<li>개인정보침해신고센터 : (국번없이) 118</li>
|
||||
<li>대검찰청 사이버범죄수사단 : (국번없이) 1301</li>
|
||||
<li>경찰청 사이버수사국 : (국번없이) 182</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2><span class="num">11.</span>개정 이력</h2>
|
||||
<table>
|
||||
<thead><tr><th>버전</th><th>시행일자</th><th>변경 내용</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>v1.0</td><td>2026-06-08</td><td>개인정보처리방침 최초 제정 및 게시</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
© 지오정보기술 · GUARDiA ITSM — 본 방침은 관계 법령 또는 내부 운영 방침의 변경에 따라 개정될 수 있습니다.
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user