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:
@@ -1549,6 +1549,29 @@ async function resolveHostCredentials(
|
|||||||
host: Record<string, unknown>,
|
host: Record<string, unknown>,
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
try {
|
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) {
|
if (host.credentialId && host.userId) {
|
||||||
const credentialId = host.credentialId as number;
|
const credentialId = host.credentialId as number;
|
||||||
const userId = host.userId as string;
|
const userId = host.userId as string;
|
||||||
|
|||||||
@@ -1888,6 +1888,8 @@
|
|||||||
"cannotRemoveSystemRole": "Cannot remove system role",
|
"cannotRemoveSystemRole": "Cannot remove system role",
|
||||||
"cannotShareWithSelf": "Cannot share host with yourself",
|
"cannotShareWithSelf": "Cannot share host with yourself",
|
||||||
"noCustomRolesToAssign": "No custom roles available. System roles are auto-assigned.",
|
"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",
|
"auditLogs": "Audit Logs",
|
||||||
"viewAuditLogs": "View Audit Logs",
|
"viewAuditLogs": "View Audit Logs",
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
|
|||||||
@@ -1749,6 +1749,8 @@
|
|||||||
"cannotRemoveSystemRole": "无法移除系统角色",
|
"cannotRemoveSystemRole": "无法移除系统角色",
|
||||||
"cannotShareWithSelf": "不能与自己共享主机",
|
"cannotShareWithSelf": "不能与自己共享主机",
|
||||||
"noCustomRolesToAssign": "没有可用的自定义角色。系统角色已自动分配。",
|
"noCustomRolesToAssign": "没有可用的自定义角色。系统角色已自动分配。",
|
||||||
|
"credentialSharingWarning": "不支持共享使用凭据认证的主机",
|
||||||
|
"credentialSharingWarningDescription": "此主机使用凭据认证。由于凭据是按用户加密的无法共享,共享用户将无法连接。请为计划共享的主机使用密码或密钥认证。",
|
||||||
"auditLogs": "审计日志",
|
"auditLogs": "审计日志",
|
||||||
"viewAuditLogs": "查看审计日志",
|
"viewAuditLogs": "查看审计日志",
|
||||||
"action": "操作",
|
"action": "操作",
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ import {
|
|||||||
shareHost,
|
shareHost,
|
||||||
getHostAccess,
|
getHostAccess,
|
||||||
revokeHostAccess,
|
revokeHostAccess,
|
||||||
|
getSSHHostById,
|
||||||
type Role,
|
type Role,
|
||||||
type AccessRecord,
|
type AccessRecord,
|
||||||
|
type SSHHost,
|
||||||
} from "@/ui/main-axios.ts";
|
} from "@/ui/main-axios.ts";
|
||||||
|
|
||||||
interface HostSharingTabProps {
|
interface HostSharingTabProps {
|
||||||
@@ -79,6 +81,7 @@ export function HostSharingTab({
|
|||||||
const [accessList, setAccessList] = React.useState<AccessRecord[]>([]);
|
const [accessList, setAccessList] = React.useState<AccessRecord[]>([]);
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
const [currentUserId, setCurrentUserId] = React.useState<string>("");
|
const [currentUserId, setCurrentUserId] = React.useState<string>("");
|
||||||
|
const [hostData, setHostData] = React.useState<SSHHost | null>(null);
|
||||||
|
|
||||||
// Load roles
|
// Load roles
|
||||||
const loadRoles = React.useCallback(async () => {
|
const loadRoles = React.useCallback(async () => {
|
||||||
@@ -124,13 +127,27 @@ export function HostSharingTab({
|
|||||||
}
|
}
|
||||||
}, [hostId]);
|
}, [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(() => {
|
React.useEffect(() => {
|
||||||
loadRoles();
|
loadRoles();
|
||||||
loadUsers();
|
loadUsers();
|
||||||
if (!isNewHost) {
|
if (!isNewHost) {
|
||||||
loadAccessList();
|
loadAccessList();
|
||||||
|
loadHostData();
|
||||||
}
|
}
|
||||||
}, [loadRoles, loadUsers, loadAccessList, isNewHost]);
|
}, [loadRoles, loadUsers, loadAccessList, loadHostData, isNewHost]);
|
||||||
|
|
||||||
// Load current user ID
|
// Load current user ID
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -235,6 +252,17 @@ export function HostSharingTab({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<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 */}
|
{/* Share Form */}
|
||||||
<div className="space-y-4 border rounded-lg p-4">
|
<div className="space-y-4 border rounded-lg p-4">
|
||||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user