1
0
forked from baron/baron-sso
Files
baron-sso/adminfront/src/features/auth/components/PermissionChecker.tsx

189 lines
6.5 KiB
TypeScript

import { useMutation } from "@tanstack/react-query";
import { CheckCircle2, XCircle } from "lucide-react";
import { useState } from "react";
import { Button } from "../../../components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "../../../components/ui/card";
import { Input } from "../../../components/ui/input";
import { Label } from "../../../components/ui/label";
import apiClient from "../../../lib/apiClient";
import { t } from "../../../lib/i18n";
type CheckPermissionResponse = {
allowed: boolean;
query: {
namespace: string;
object: string;
relation: string;
subject: string;
};
};
function PermissionChecker() {
const [namespace, setNamespace] = useState("Tenant");
const [object, setObject] = useState("");
const [relation, setRelation] = useState("manage");
const [subject, setSubject] = useState("");
const checkMutation = useMutation({
mutationFn: async () => {
const { data } = await apiClient.get<CheckPermissionResponse>(
"/v1/admin/debug/check-permission",
{
params: { namespace, object, relation, subject },
},
);
return data;
},
});
const result = checkMutation.data;
return (
<Card className="border-primary/20 bg-[var(--color-panel)]">
<CardHeader>
<CardTitle className="text-lg font-bold">
{t("ui.admin.auth_guard.checker.title", "ReBAC permission checker")}
</CardTitle>
<CardDescription>
{t(
"ui.admin.auth_guard.checker.description",
"Check in real time whether a subject has access to a resource through Ory Keto.",
)}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<div className="space-y-2">
<Label>
{t("ui.admin.auth_guard.checker.namespace.label", "Namespace")}
</Label>
<select
id="permission-checker-namespace"
name="permission-checker-namespace"
value={namespace}
onChange={(e) => setNamespace(e.target.value)}
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
>
<option value="Tenant">
{t("ui.admin.auth_guard.checker.namespace.tenant", "Tenant")}
</option>
<option value="TenantGroup">
{t(
"ui.admin.auth_guard.checker.namespace.tenant_group",
"TenantGroup",
)}
</option>
<option value="RelyingParty">
{t(
"ui.admin.auth_guard.checker.namespace.relying_party",
"RelyingParty",
)}
</option>
<option value="System">
{t("ui.admin.auth_guard.checker.namespace.system", "System")}
</option>
</select>
</div>
<div className="space-y-2">
<Label>
{t("ui.admin.auth_guard.checker.relation", "Relation")}
</Label>
<Input
placeholder={t(
"ui.admin.auth_guard.checker.relation_placeholder",
"view, manage, admins...",
)}
value={relation}
onChange={(e) => setRelation(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>
{t("ui.admin.auth_guard.checker.object_id", "Object ID")}
</Label>
<Input
placeholder={t(
"ui.admin.auth_guard.checker.object_id_placeholder",
"Tenant UUID, etc.",
)}
value={object}
onChange={(e) => setObject(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>
{t("ui.admin.auth_guard.checker.subject", "Subject (User:ID)")}
</Label>
<Input
placeholder={t(
"ui.admin.auth_guard.checker.subject_placeholder",
"User:uuid or Namespace:ID#Relation",
)}
value={subject}
onChange={(e) => setSubject(e.target.value)}
/>
</div>
</div>
<div className="flex justify-center">
<Button
onClick={() => checkMutation.mutate()}
disabled={!object || !subject || checkMutation.isPending}
className="w-full px-12 md:w-auto"
>
{checkMutation.isPending
? t("ui.admin.auth_guard.checker.checking", "Checking...")
: t("ui.admin.auth_guard.checker.check", "Check permission")}
</Button>
</div>
{checkMutation.isSuccess && result && (
<div
className={`flex flex-col items-center justify-center gap-3 rounded-xl border-2 p-6 animate-in zoom-in duration-300 ${
result.allowed
? "border-green-500/50 bg-green-500/10 text-green-600"
: "border-destructive/50 bg-destructive/10 text-destructive"
}`}
>
{result.allowed ? (
<>
<CheckCircle2 size={48} />
<div className="text-lg font-bold">
{t("ui.admin.auth_guard.checker.allowed", "Access ALLOWED")}
</div>
<p className="text-center text-sm opacity-80">
{t(
"ui.admin.auth_guard.checker.allowed_description",
"The subject has access to the requested resource, including inherited permissions.",
)}
</p>
</>
) : (
<>
<XCircle size={48} />
<div className="text-lg font-bold">
{t("ui.admin.auth_guard.checker.denied", "Access DENIED")}
</div>
<p className="text-center text-sm opacity-80">
{t(
"ui.admin.auth_guard.checker.denied_description",
"The subject does not have access to the requested resource.",
)}
</p>
</>
)}
</div>
)}
</CardContent>
</Card>
);
}
export default PermissionChecker;