1
0
forked from baron/baron-sso

애플리케이션(RP) 관리 기능 구현 및 Ory Keto 권한 연동

This commit is contained in:
2026-02-04 14:56:16 +09:00
parent bf469b1eb4
commit 066ea86f46
14 changed files with 232 additions and 48 deletions

View File

@@ -11,6 +11,10 @@ import TenantDetailPage from "../features/tenants/routes/TenantDetailPage";
import TenantListPage from "../features/tenants/routes/TenantListPage";
import { TenantProfilePage } from "../features/tenants/routes/TenantProfilePage";
import { TenantSchemaPage } from "../features/tenants/routes/TenantSchemaPage";
import TenantRelyingPartyListPage from "../features/tenants/routes/TenantRelyingPartyListPage";
import TenantRelyingPartyCreatePage from "../features/tenants/routes/TenantRelyingPartyCreatePage";
import TenantRelyingPartyDetailPage from "../features/tenants/routes/TenantRelyingPartyDetailPage";
import RelyingPartyListPage from "../features/relying-parties/RelyingPartyListPage";
import UserCreatePage from "../features/users/UserCreatePage";
import UserDetailPage from "../features/users/UserDetailPage";
import UserListPage from "../features/users/UserListPage";
@@ -28,6 +32,7 @@ export const router = createBrowserRouter(
{ path: "users", element: <UserListPage /> },
{ path: "users/new", element: <UserCreatePage /> },
{ path: "users/:id", element: <UserDetailPage /> },
{ path: "relying-parties", element: <RelyingPartyListPage /> },
{ path: "tenants", element: <TenantListPage /> },
{ path: "tenants/new", element: <TenantCreatePage /> },
{
@@ -36,6 +41,9 @@ export const router = createBrowserRouter(
children: [
{ index: true, element: <TenantProfilePage /> },
{ path: "schema", element: <TenantSchemaPage /> },
{ path: "relying-parties", element: <TenantRelyingPartyListPage /> },
{ path: "relying-parties/new", element: <TenantRelyingPartyCreatePage /> },
{ path: "relying-parties/:id", element: <TenantRelyingPartyDetailPage /> },
],
},
{ path: "api-keys", element: <ApiKeyListPage /> },

View File

@@ -9,6 +9,7 @@ import {
ShieldHalf,
Sun,
Users,
Share2,
} from "lucide-react";
import { useEffect, useState } from "react";
import { NavLink, Outlet } from "react-router-dom";
@@ -19,6 +20,7 @@ const navItems = [
{ label: "Tenant Dashboard", to: "/dashboard", icon: ShieldHalf },
{ label: "Tenants", to: "/tenants", icon: Building2 },
{ label: "Users", to: "/users", icon: Users },
{ label: "Applications", to: "/relying-parties", icon: Share2 },
{ label: "API Keys (M2M)", to: "/api-keys", icon: Key },
{ label: "Audit Logs", to: "/audit-logs", icon: NotebookTabs },
{ label: "Auth Guard", to: "/auth", icon: KeyRound },

View File

@@ -70,6 +70,16 @@ function TenantDetailPage() {
>
Schema
</Link>
<Link
to={`/tenants/${tenantId}/relying-parties`}
className={`px-4 py-2 text-sm font-medium ${
location.pathname.includes("/relying-parties")
? "border-b-2 border-blue-500 text-blue-600"
: "text-gray-500 hover:text-gray-700"
}`}
>
Relying Parties
</Link>
</div>
{/* Outlet for nested routes */}

View File

@@ -17,6 +17,7 @@ import { Label } from "../../components/ui/label";
import {
createUser,
fetchTenants,
fetchTenant,
type UserCreateRequest,
type UserCreateResponse,
} from "../../lib/adminApi";

View File

@@ -17,6 +17,7 @@ import { Label } from "../../components/ui/label";
import {
fetchUser,
fetchTenants,
fetchTenant,
updateUser,
type UserUpdateRequest,
} from "../../lib/adminApi";

View File

@@ -252,3 +252,70 @@ export async function updateUser(userId: string, payload: UserUpdateRequest) {
export async function deleteUser(userId: string) {
await apiClient.delete(`/v1/admin/users/${userId}`);
}
// Relying Party Management
export type RelyingParty = {
clientId: string;
tenantId: string;
name: string;
description: string;
createdAt: string;
updatedAt: string;
};
export type HydraClientReq = {
client_id?: string;
client_name: string;
client_secret?: string;
redirect_uris: string[];
scope?: string;
token_endpoint_auth_method?: string;
grant_types?: string[];
response_types?: string[];
metadata?: Record<string, any>;
};
export async function fetchRelyingParties(tenantId: string) {
const { data } = await apiClient.get<RelyingParty[]>(
`/v1/admin/tenants/${tenantId}/relying-parties`,
);
return data;
}
export async function fetchAllRelyingParties() {
const { data } = await apiClient.get<RelyingParty[]>(
"/v1/admin/relying-parties",
);
return data;
}
export async function createRelyingParty(
tenantId: string,
payload: HydraClientReq,
) {
const { data } = await apiClient.post<RelyingParty>(
`/v1/admin/tenants/${tenantId}/relying-parties`,
payload,
);
return data;
}
export async function fetchRelyingParty(id: string) {
const { data } = await apiClient.get<{
relyingParty: RelyingParty;
oauth2Config: HydraClientReq;
}>(`/v1/admin/relying-parties/${id}`);
return data;
}
export async function updateRelyingParty(id: string, payload: HydraClientReq) {
const { data } = await apiClient.put<RelyingParty>(
`/v1/admin/relying-parties/${id}`,
payload,
);
return data;
}
export async function deleteRelyingParty(id: string) {
await apiClient.delete(`/v1/admin/relying-parties/${id}`);
}