feat(auth): Keycloak role 기반 관리자 판별과 도구 카탈로그 개편
- auth.js: realm/client role 추출 → roles·isAdmin 세션 저장 - config.js: ADMIN_ROLE + 도구 URL 8종 환경변수화 - sites.js: 개발4 + 인프라4(adminOnly) 재구성, toolsForUser(isAdmin) - .project-env.example: ADMIN_ROLE·도구 URL 항목 추가 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
src/auth.js
10
src/auth.js
@@ -42,10 +42,20 @@ export async function handleCallback(req) {
|
||||
nonce,
|
||||
});
|
||||
const claims = tokenSet.claims();
|
||||
// Keycloak realm 역할: realm_access.roles. (클라이언트 역할은 resource_access[client].roles)
|
||||
const realmRoles = (claims.realm_access && claims.realm_access.roles) || [];
|
||||
const clientRoles =
|
||||
(claims.resource_access &&
|
||||
claims.resource_access[config.oidc.clientId] &&
|
||||
claims.resource_access[config.oidc.clientId].roles) ||
|
||||
[];
|
||||
const roles = [...new Set([...realmRoles, ...clientRoles])];
|
||||
return {
|
||||
sabun: claims.preferred_username, // 사번
|
||||
name: claims.name || claims.given_name || claims.preferred_username,
|
||||
email: claims.email,
|
||||
roles,
|
||||
isAdmin: roles.includes(config.adminRole),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,21 @@ export const config = {
|
||||
// redirect 는 baseUrl + /auth/callback 로 구성
|
||||
},
|
||||
|
||||
// 관리자 판별용 Keycloak 역할 이름. 이 role 을 가진 사용자에게만 인프라 도구 노출.
|
||||
adminRole: process.env.ADMIN_ROLE || "admin",
|
||||
|
||||
// 도구(사이트) URL — 폐쇄망/도메인 변경 대비 환경변수로 덮어쓰기 가능.
|
||||
tools: {
|
||||
coder: process.env.CODER_URL || "https://coder.bokdev.in/api/v2/users/oidc/callback",
|
||||
gitea: process.env.GITEA_URL || "https://gitea.bokdev.in/user/oauth2/keycloak",
|
||||
kubero: process.env.KUBERO_URL || "https://kubero.bokdev.in",
|
||||
harbor: process.env.HARBOR_URL || "https://harbor.bokdev.in",
|
||||
openeverest: process.env.OPENEVEREST_URL || "https://openeverest.bokdev.in",
|
||||
minio: process.env.MINIO_URL || "https://minioc.bokdev.in",
|
||||
keycloak: process.env.KEYCLOAK_URL || "https://keycloak.bokdev.in/admin",
|
||||
litellm: process.env.LITELLM_URL || "https://litellm.bokdev.in",
|
||||
},
|
||||
|
||||
// 포털 표시 이름 (변경 가능)
|
||||
brand: process.env.PORTAL_BRAND || "AI DEV",
|
||||
};
|
||||
|
||||
45
src/sites.js
45
src/sites.js
@@ -1,29 +1,30 @@
|
||||
// 포털에 모아둘 사이트 카탈로그. 카테고리: 개발 핵심 / AI·데이터.
|
||||
// icon 은 이모지(추가 의존성 없이 GitHub 감성 카드에 표시).
|
||||
// 대시보드 "개발 도구" 카탈로그.
|
||||
//
|
||||
// url: 각 앱의 'OIDC 로그인 바로 시작' 엔드포인트를 쓰면, Keycloak SSO 세션이 있을 때
|
||||
// 로그인 폼 없이 즉시 통과(자동 로그인)된다. 포털도 같은 Keycloak realm 으로 로그인하므로
|
||||
// 포털 로그인 후 이 링크를 누르면 바로 들어간다.
|
||||
// - Coder: /api/v2/users/oidc/callback (code 없으면 keycloak authorize 로 307)
|
||||
// - Gitea: /user/oauth2/keycloak (auth source 명=keycloak, 307 로 keycloak authorize)
|
||||
// SSO 미지원(Kubero=oauth2 미완성, Harbor 등)은 기본 URL 유지.
|
||||
export const siteGroups = [
|
||||
{
|
||||
group: "개발 핵심",
|
||||
sites: [
|
||||
{ name: "Coder", url: "https://coder.bokdev.in/api/v2/users/oidc/callback", icon: "💻", desc: "웹 개발 워크스페이스 (VS Code + AI CLI)", sso: true },
|
||||
{ name: "Gitea", url: "https://gitea.bokdev.in/user/oauth2/keycloak", icon: "🍵", desc: "사내 Git 저장소", sso: true },
|
||||
{ name: "Kubero", url: "https://kubero.bokdev.in", icon: "🚀", desc: "앱 배포 PaaS (*.apps.bokdev.in)" },
|
||||
{ name: "Harbor", url: "https://harbor.bokdev.in", icon: "📦", desc: "컨테이너 이미지 레지스트리" },
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "AI · 데이터",
|
||||
sites: [
|
||||
{ name: "LiteLLM", url: "https://litellm.bokdev.in", icon: "🤖", desc: "AI 게이트웨이 (모델 키)" },
|
||||
{ name: "Chat", url: "https://chat.bokdev.in", icon: "💬", desc: "사내 챗 (Ollama)" },
|
||||
{ name: "OpenEverest", url: "https://openeverest.bokdev.in",icon: "🗄️", desc: "DBaaS (DB 프로비저닝)" },
|
||||
{ name: "MinIO", url: "https://minioc.bokdev.in", icon: "🪣", desc: "오브젝트 스토리지 콘솔" },
|
||||
],
|
||||
},
|
||||
// SSO 미지원(Kubero/Harbor 등)은 기본 URL 유지.
|
||||
//
|
||||
// adminOnly: true 인 항목은 관리자(role=admin)에게만 렌더(숨김, disabled 아님).
|
||||
// icon: 이모지(폐쇄망에서도 외부 의존 없이 표시). desc: 카드 한 줄 설명.
|
||||
import { config } from "./config.js";
|
||||
|
||||
export const sites = [
|
||||
// --- 개발 핵심 (모두에게) ---
|
||||
{ key: "coder", name: "Coder", desc: "클라우드 IDE", url: config.tools.coder, icon: "💻", sso: true },
|
||||
{ key: "gitea", name: "Gitea", desc: "Git 저장소", url: config.tools.gitea, icon: "🍵", sso: true },
|
||||
{ key: "kubero", name: "Kubero", desc: "배포 (PaaS)", url: config.tools.kubero, icon: "🚀" },
|
||||
{ key: "harbor", name: "Harbor", desc: "컨테이너 레지스트리", url: config.tools.harbor, icon: "📦" },
|
||||
// --- 인프라/운영 (관리자 전용) ---
|
||||
{ key: "openeverest", name: "OpenEverest", desc: "DB 콘솔", url: config.tools.openeverest, icon: "🗄️", adminOnly: true },
|
||||
{ key: "minio", name: "MinIO", desc: "오브젝트 스토리지", url: config.tools.minio, icon: "🪣", adminOnly: true },
|
||||
{ key: "keycloak", name: "Keycloak", desc: "계정 / SSO 관리", url: config.tools.keycloak, icon: "🔑", adminOnly: true },
|
||||
{ key: "litellm", name: "LiteLLM", desc: "LLM 게이트웨이", url: config.tools.litellm, icon: "🤖", adminOnly: true },
|
||||
];
|
||||
|
||||
// 역할에 맞는 도구만 추려서 반환. 일반 사용자는 adminOnly 카드를 아예 받지 않는다.
|
||||
export function toolsForUser(isAdmin) {
|
||||
return sites.filter((s) => isAdmin || !s.adminOnly);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user