fix: rbac implementation general issues (local squash)
This commit is contained in:
@@ -18,6 +18,7 @@ export function HostManager({
|
||||
isTopbarOpen,
|
||||
initialTab = "host_viewer",
|
||||
hostConfig,
|
||||
_updateTimestamp,
|
||||
rightSidebarOpen = false,
|
||||
rightSidebarWidth = 400,
|
||||
}: HostManagerProps): React.ReactElement {
|
||||
@@ -36,20 +37,39 @@ export function HostManager({
|
||||
const ignoreNextHostConfigChangeRef = useRef<boolean>(false);
|
||||
const lastProcessedHostIdRef = useRef<number | undefined>(undefined);
|
||||
|
||||
// Sync state when tab is updated externally (via updateTab or addTab)
|
||||
useEffect(() => {
|
||||
if (initialTab) {
|
||||
setActiveTab(initialTab);
|
||||
}
|
||||
}, [initialTab]);
|
||||
// Always sync on timestamp changes
|
||||
if (_updateTimestamp !== undefined) {
|
||||
// Update activeTab if initialTab has changed
|
||||
if (initialTab && initialTab !== activeTab) {
|
||||
setActiveTab(initialTab);
|
||||
}
|
||||
|
||||
// Update editingHost when hostConfig changes
|
||||
useEffect(() => {
|
||||
if (hostConfig) {
|
||||
setEditingHost(hostConfig);
|
||||
setActiveTab("add_host");
|
||||
lastProcessedHostIdRef.current = hostConfig.id;
|
||||
// Update editingHost if hostConfig has changed
|
||||
if (hostConfig && hostConfig.id !== editingHost?.id) {
|
||||
setEditingHost(hostConfig);
|
||||
lastProcessedHostIdRef.current = hostConfig.id;
|
||||
} else if (!hostConfig && editingHost) {
|
||||
// Clear editingHost if hostConfig is now undefined
|
||||
setEditingHost(null);
|
||||
}
|
||||
|
||||
// Clear editingCredential if switching away from add_credential
|
||||
if (initialTab !== "add_credential" && editingCredential) {
|
||||
setEditingCredential(null);
|
||||
}
|
||||
} else {
|
||||
// Initial mount - set state from props
|
||||
if (initialTab) {
|
||||
setActiveTab(initialTab);
|
||||
}
|
||||
if (hostConfig) {
|
||||
setEditingHost(hostConfig);
|
||||
lastProcessedHostIdRef.current = hostConfig.id;
|
||||
}
|
||||
}
|
||||
}, [hostConfig?.id]);
|
||||
}, [_updateTimestamp, initialTab, hostConfig?.id]);
|
||||
|
||||
const handleEditHost = (host: SSHHost) => {
|
||||
setEditingHost(host);
|
||||
|
||||
@@ -1465,6 +1465,7 @@ export function HostManagerEditor({
|
||||
<Tabs
|
||||
value={authTab}
|
||||
onValueChange={(value) => {
|
||||
if (editingHost?.isShared) return;
|
||||
const newAuthType = value as
|
||||
| "password"
|
||||
| "key"
|
||||
@@ -1478,25 +1479,29 @@ export function HostManagerEditor({
|
||||
<TabsList className="bg-button border border-edge-medium">
|
||||
<TabsTrigger
|
||||
value="password"
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
|
||||
disabled={editingHost?.isShared}
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{t("hosts.password")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="key"
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
|
||||
disabled={editingHost?.isShared}
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{t("hosts.key")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="credential"
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
|
||||
disabled={editingHost?.isShared}
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{t("hosts.credential")}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="none"
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium"
|
||||
disabled={editingHost?.isShared}
|
||||
className="bg-button data-[state=active]:bg-elevated data-[state=active]:border data-[state=active]:border-edge-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{t("hosts.none")}
|
||||
</TabsTrigger>
|
||||
@@ -1709,26 +1714,34 @@ export function HostManagerEditor({
|
||||
name="credentialId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<CredentialSelector
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
onCredentialSelect={(credential) => {
|
||||
if (
|
||||
credential &&
|
||||
!form.getValues(
|
||||
"overrideCredentialUsername",
|
||||
)
|
||||
) {
|
||||
form.setValue(
|
||||
"username",
|
||||
credential.username,
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("hosts.credentialDescription")}
|
||||
</FormDescription>
|
||||
{editingHost?.isShared ? (
|
||||
<div className="text-sm text-muted-foreground p-3 bg-base border border-edge-medium rounded-md">
|
||||
{t("hosts.cannotChangeAuthAsSharedUser")}
|
||||
</div>
|
||||
) : (
|
||||
<CredentialSelector
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
onCredentialSelect={(credential) => {
|
||||
if (
|
||||
credential &&
|
||||
!form.getValues(
|
||||
"overrideCredentialUsername",
|
||||
)
|
||||
) {
|
||||
form.setValue(
|
||||
"username",
|
||||
credential.username,
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!editingHost?.isShared && (
|
||||
<FormDescription>
|
||||
{t("hosts.credentialDescription")}
|
||||
</FormDescription>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@@ -3769,7 +3782,7 @@ export function HostManagerEditor({
|
||||
</ScrollArea>
|
||||
<footer className="shrink-0 w-full pb-0">
|
||||
<Separator className="p-0.25" />
|
||||
{!(editingHost?.permissionLevel === "view") && (
|
||||
{!editingHost?.isShared && (
|
||||
<Button className="translate-y-2" type="submit" variant="outline">
|
||||
{editingHost
|
||||
? editingHost.id
|
||||
|
||||
@@ -76,10 +76,8 @@ interface User {
|
||||
is_admin: boolean;
|
||||
}
|
||||
|
||||
const PERMISSION_LEVELS = [
|
||||
{ value: "view", labelKey: "rbac.view" },
|
||||
{ value: "manage", labelKey: "rbac.manage" },
|
||||
];
|
||||
// Only view permission is supported (manage removed due to encryption constraints)
|
||||
const PERMISSION_LEVELS = [{ value: "view", labelKey: "rbac.view" }];
|
||||
|
||||
export function HostSharingTab({
|
||||
hostId,
|
||||
@@ -430,26 +428,12 @@ export function HostSharingTab({
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
{/* Permission Level */}
|
||||
{/* Permission Level - Always "view" (read-only) */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="permission-level">
|
||||
{t("rbac.permissionLevel")}
|
||||
</Label>
|
||||
<Select
|
||||
value={permissionLevel || "use"}
|
||||
onValueChange={(v) => setPermissionLevel(v || "use")}
|
||||
>
|
||||
<SelectTrigger id="permission-level">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{PERMISSION_LEVELS.map((level) => (
|
||||
<SelectItem key={level.value} value={level.value}>
|
||||
{t(level.labelKey)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label>{t("rbac.permissionLevel")}</Label>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{t("rbac.view")} - {t("rbac.viewDesc")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Expiration */}
|
||||
@@ -496,7 +480,6 @@ export function HostSharingTab({
|
||||
<TableHead>{t("rbac.permissionLevel")}</TableHead>
|
||||
<TableHead>{t("rbac.grantedBy")}</TableHead>
|
||||
<TableHead>{t("rbac.expires")}</TableHead>
|
||||
<TableHead>{t("rbac.accessCount")}</TableHead>
|
||||
<TableHead className="text-right">
|
||||
{t("common.actions")}
|
||||
</TableHead>
|
||||
@@ -506,7 +489,7 @@ export function HostSharingTab({
|
||||
{loading ? (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={7}
|
||||
colSpan={6}
|
||||
className="text-center text-muted-foreground"
|
||||
>
|
||||
{t("common.loading")}
|
||||
@@ -515,7 +498,7 @@ export function HostSharingTab({
|
||||
) : accessList.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={7}
|
||||
colSpan={6}
|
||||
className="text-center text-muted-foreground"
|
||||
>
|
||||
{t("rbac.noAccessRecords")}
|
||||
@@ -582,7 +565,6 @@ export function HostSharingTab({
|
||||
t("rbac.never")
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>{access.accessCount}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user