forked from baron/baron-sso
앱 생성 개발자 권한 신청 안내 추가
This commit is contained in:
@@ -60,6 +60,7 @@ import { t } from "../../lib/i18n";
|
||||
import { resolveProfileRole } from "../../lib/role";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { fetchMe } from "../auth/authApi";
|
||||
import { resolveClientCreateAccess } from "./clientCreateAccess";
|
||||
import { ClientLogo } from "./components/ClientLogo";
|
||||
|
||||
type ClientSortKey = "application" | "id" | "type" | "status" | "createdAt";
|
||||
@@ -96,7 +97,8 @@ function ClientsPage() {
|
||||
} = useQuery({
|
||||
queryKey: ["developer-request", tenantId],
|
||||
queryFn: () => fetchDeveloperRequestStatus(tenantId),
|
||||
enabled: hasAccessToken && role === "user",
|
||||
enabled:
|
||||
hasAccessToken && (role === "user" || role === "tenant_member"),
|
||||
});
|
||||
const { data: tenants } = useQuery({
|
||||
queryKey: ["myTenants"],
|
||||
@@ -109,15 +111,14 @@ function ClientsPage() {
|
||||
enabled: hasAccessToken,
|
||||
});
|
||||
|
||||
const canCreateClient =
|
||||
(role !== "user" && role !== "tenant_member") ||
|
||||
requestStatus?.status === "approved";
|
||||
const isDeveloperRequestPending = requestStatus?.status === "pending";
|
||||
const createAccessState = resolveClientCreateAccess({
|
||||
role,
|
||||
requestStatus: requestStatus?.status,
|
||||
});
|
||||
const canCreateClient = createAccessState === "can_create";
|
||||
const isDeveloperRequestPending = createAccessState === "pending";
|
||||
const canRequestDeveloperAccess =
|
||||
role === "user" &&
|
||||
!isLoadingRequest &&
|
||||
!canCreateClient &&
|
||||
!isDeveloperRequestPending;
|
||||
createAccessState === "request_required" && !isLoadingRequest;
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [typeFilter, setTypeFilter] = useState("all");
|
||||
@@ -278,7 +279,54 @@ function ClientsPage() {
|
||||
<Plus className="h-4 w-4" />
|
||||
{t("ui.dev.clients.new", "새 클라이언트")}
|
||||
</Button>
|
||||
) : null
|
||||
) : isDeveloperRequestPending ? (
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
<p className="max-w-xs text-right text-sm text-muted-foreground">
|
||||
{t(
|
||||
"msg.dev.clients.create_pending_detail",
|
||||
"개발자 권한 신청을 검토 중입니다. 승인되면 연동 앱을 추가할 수 있습니다.",
|
||||
)}
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => navigate("/developer-requests")}
|
||||
>
|
||||
{t("ui.dev.nav.developer_request", "개발자 권한 신청")}
|
||||
</Button>
|
||||
</div>
|
||||
) : canRequestDeveloperAccess ? (
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
<p className="max-w-xs whitespace-pre-line text-right text-sm text-muted-foreground">
|
||||
{t(
|
||||
"msg.dev.clients.create_requires_request",
|
||||
"연동 앱을 생성할 권한이 없습니다.\n개발자 권한 신청을 요청한 뒤 승인 받아주세요.",
|
||||
).replaceAll("\\n", "\n")}
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => navigate("/developer-requests")}
|
||||
>
|
||||
{t("ui.dev.welcome.btn_request", "개발자 권한 신청")}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-end gap-2 text-right">
|
||||
<p className="max-w-xs text-sm text-muted-foreground">
|
||||
{t(
|
||||
"msg.dev.clients.create_forbidden_detail",
|
||||
"연동 앱을 생성할 권한이 없습니다. 관리자에게 개발자 권한 또는 적절한 RP 권한 부여를 요청해 주세요.",
|
||||
)}
|
||||
</p>
|
||||
<Button type="button" variant="outline" size="sm" disabled>
|
||||
<Plus className="h-4 w-4" />
|
||||
{t("ui.dev.clients.new", "새 클라이언트")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
55
devfront/src/features/clients/clientCreateAccess.test.ts
Normal file
55
devfront/src/features/clients/clientCreateAccess.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveClientCreateAccess } from "./clientCreateAccess";
|
||||
|
||||
describe("client create access", () => {
|
||||
it("allows privileged roles to create clients without developer request approval", () => {
|
||||
expect(
|
||||
resolveClientCreateAccess({
|
||||
role: "rp_admin",
|
||||
}),
|
||||
).toBe("can_create");
|
||||
});
|
||||
|
||||
it("requires a developer request for basic users without approval", () => {
|
||||
expect(
|
||||
resolveClientCreateAccess({
|
||||
role: "user",
|
||||
requestStatus: "none",
|
||||
}),
|
||||
).toBe("request_required");
|
||||
});
|
||||
|
||||
it("shows pending state while a developer request is under review", () => {
|
||||
expect(
|
||||
resolveClientCreateAccess({
|
||||
role: "tenant_member",
|
||||
requestStatus: "pending",
|
||||
}),
|
||||
).toBe("pending");
|
||||
});
|
||||
|
||||
it("allows client creation after developer request approval", () => {
|
||||
expect(
|
||||
resolveClientCreateAccess({
|
||||
role: "user",
|
||||
requestStatus: "approved",
|
||||
}),
|
||||
).toBe("can_create");
|
||||
});
|
||||
|
||||
it("routes cancelled or rejected requests back to requestable state", () => {
|
||||
expect(
|
||||
resolveClientCreateAccess({
|
||||
role: "user",
|
||||
requestStatus: "cancelled",
|
||||
}),
|
||||
).toBe("request_required");
|
||||
|
||||
expect(
|
||||
resolveClientCreateAccess({
|
||||
role: "user",
|
||||
requestStatus: "rejected",
|
||||
}),
|
||||
).toBe("request_required");
|
||||
});
|
||||
});
|
||||
44
devfront/src/features/clients/clientCreateAccess.ts
Normal file
44
devfront/src/features/clients/clientCreateAccess.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { DeveloperRequestStatus } from "../../lib/devApi";
|
||||
|
||||
export type ClientCreateAccessState =
|
||||
| "can_create"
|
||||
| "pending"
|
||||
| "request_required"
|
||||
| "forbidden";
|
||||
|
||||
type ResolveClientCreateAccessParams = {
|
||||
role: string;
|
||||
requestStatus?: DeveloperRequestStatus;
|
||||
};
|
||||
|
||||
function canSelfRequestDeveloperAccess(role: string) {
|
||||
return role === "user" || role === "tenant_member";
|
||||
}
|
||||
|
||||
export function resolveClientCreateAccess({
|
||||
role,
|
||||
requestStatus,
|
||||
}: ResolveClientCreateAccessParams): ClientCreateAccessState {
|
||||
if (!canSelfRequestDeveloperAccess(role)) {
|
||||
return "can_create";
|
||||
}
|
||||
|
||||
if (requestStatus === "approved") {
|
||||
return "can_create";
|
||||
}
|
||||
|
||||
if (requestStatus === "pending") {
|
||||
return "pending";
|
||||
}
|
||||
|
||||
if (
|
||||
requestStatus === "none" ||
|
||||
requestStatus === "rejected" ||
|
||||
requestStatus === "cancelled" ||
|
||||
typeof requestStatus === "undefined"
|
||||
) {
|
||||
return "request_required";
|
||||
}
|
||||
|
||||
return "forbidden";
|
||||
}
|
||||
Reference in New Issue
Block a user