fix: Prevent credential sharing errors for shared hosts

- Skip credential resolution for shared hosts with credential authentication
  to prevent decryption errors (credentials are encrypted per-user)
- Add warning alert in sharing tab when host uses credential authentication
- Inform users that shared users cannot connect to credential-based hosts
- Add translations for credential sharing warning (EN/ZH)

This prevents authentication failures when sharing hosts configured
with credential authentication while maintaining security by keeping
credentials isolated per user.
This commit is contained in:
ZacharyZcR
2025-12-15 03:44:25 +08:00
parent dd56108e8a
commit 67cc3adcc4
4 changed files with 56 additions and 1 deletions

View File

@@ -1549,6 +1549,29 @@ async function resolveHostCredentials(
host: Record<string, unknown>,
): Promise<Record<string, unknown>> {
try {
// Skip credential resolution for shared hosts
// Shared users cannot access the owner's encrypted credentials
if (host.isShared && host.credentialId) {
sshLogger.info(
`Skipping credential resolution for shared host ${host.id} with credentialId ${host.credentialId}`,
{
operation: "resolve_host_credentials_shared",
hostId: host.id as number,
isShared: host.isShared,
},
);
// Return host without resolving credentials
// The frontend should handle credential auth for shared hosts differently
const result = { ...host };
if (host.key_password !== undefined) {
if (result.keyPassword === undefined) {
result.keyPassword = host.key_password;
}
delete result.key_password;
}
return result;
}
if (host.credentialId && host.userId) {
const credentialId = host.credentialId as number;
const userId = host.userId as string;

View File

@@ -1888,6 +1888,8 @@
"cannotRemoveSystemRole": "Cannot remove system role",
"cannotShareWithSelf": "Cannot share host with yourself",
"noCustomRolesToAssign": "No custom roles available. System roles are auto-assigned.",
"credentialSharingWarning": "Credential Authentication Not Supported for Sharing",
"credentialSharingWarningDescription": "This host uses credential-based authentication. Shared users will not be able to connect because credentials are encrypted per-user and cannot be shared. Please use password or key-based authentication for hosts you intend to share.",
"auditLogs": "Audit Logs",
"viewAuditLogs": "View Audit Logs",
"action": "Action",

View File

@@ -1749,6 +1749,8 @@
"cannotRemoveSystemRole": "无法移除系统角色",
"cannotShareWithSelf": "不能与自己共享主机",
"noCustomRolesToAssign": "没有可用的自定义角色。系统角色已自动分配。",
"credentialSharingWarning": "不支持共享使用凭据认证的主机",
"credentialSharingWarningDescription": "此主机使用凭据认证。由于凭据是按用户加密的无法共享,共享用户将无法连接。请为计划共享的主机使用密码或密钥认证。",
"auditLogs": "审计日志",
"viewAuditLogs": "查看审计日志",
"action": "操作",

View File

@@ -39,8 +39,10 @@ import {
shareHost,
getHostAccess,
revokeHostAccess,
getSSHHostById,
type Role,
type AccessRecord,
type SSHHost,
} from "@/ui/main-axios.ts";
interface HostSharingTabProps {
@@ -79,6 +81,7 @@ export function HostSharingTab({
const [accessList, setAccessList] = React.useState<AccessRecord[]>([]);
const [loading, setLoading] = React.useState(false);
const [currentUserId, setCurrentUserId] = React.useState<string>("");
const [hostData, setHostData] = React.useState<SSHHost | null>(null);
// Load roles
const loadRoles = React.useCallback(async () => {
@@ -124,13 +127,27 @@ export function HostSharingTab({
}
}, [hostId]);
// Load host data
const loadHostData = React.useCallback(async () => {
if (!hostId) return;
try {
const host = await getSSHHostById(hostId);
setHostData(host);
} catch (error) {
console.error("Failed to load host data:", error);
setHostData(null);
}
}, [hostId]);
React.useEffect(() => {
loadRoles();
loadUsers();
if (!isNewHost) {
loadAccessList();
loadHostData();
}
}, [loadRoles, loadUsers, loadAccessList, isNewHost]);
}, [loadRoles, loadUsers, loadAccessList, loadHostData, isNewHost]);
// Load current user ID
React.useEffect(() => {
@@ -235,6 +252,17 @@ export function HostSharingTab({
return (
<div className="space-y-6">
{/* Credential Authentication Warning */}
{hostData?.authType === "Credential" && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>{t("rbac.credentialSharingWarning")}</AlertTitle>
<AlertDescription>
{t("rbac.credentialSharingWarningDescription")}
</AlertDescription>
</Alert>
)}
{/* Share Form */}
<div className="space-y-4 border rounded-lg p-4">
<h3 className="text-lg font-semibold flex items-center gap-2">