From 93b83ba3f161a384239d6f13548442a7e1438785 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 7 Apr 2026 22:36:20 -0700 Subject: [PATCH 1/2] feat(secrets): allow admins to view and edit workspace secret values --- .../credentials/credentials-manager.tsx | 74 +++++++++++-------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx index e87f0ee5cf..9a3f7cd34d 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx @@ -127,10 +127,11 @@ interface WorkspaceVariableRowProps { renamingKey: string | null pendingKeyValue: string hasCredential: boolean - isAdmin: boolean + canEdit: boolean onRenameStart: (key: string) => void onPendingKeyChange: (value: string) => void onRenameEnd: (key: string, value: string) => void + onValueChange: (key: string, value: string) => void onDelete: (key: string) => void onViewDetails: (envKey: string) => void } @@ -141,17 +142,18 @@ function WorkspaceVariableRow({ renamingKey, pendingKeyValue, hasCredential, - isAdmin, + canEdit, onRenameStart, onPendingKeyChange, onRenameEnd, + onValueChange, onDelete, onViewDetails, }: WorkspaceVariableRowProps) { const [valueFocused, setValueFocused] = useState(false) const maskedValueStyle = - isAdmin && !valueFocused ? ({ WebkitTextSecurity: 'disc' } as React.CSSProperties) : undefined + canEdit && !valueFocused ? ({ WebkitTextSecurity: 'disc' } as React.CSSProperties) : undefined return (
@@ -167,18 +169,24 @@ function WorkspaceVariableRow({ autoCapitalize='off' spellCheck='false' readOnly - onFocus={(e) => e.target.removeAttribute('readOnly')} + onFocus={(e) => { + if (canEdit) e.target.removeAttribute('readOnly') + }} className='h-9' />
onValueChange(envKey, e.target.value)} readOnly - onFocus={() => { - if (isAdmin) setValueFocused(true) + onFocus={(e) => { + if (canEdit) { + setValueFocused(true) + e.target.removeAttribute('readOnly') + } }} onBlur={() => { - if (isAdmin) setValueFocused(false) + if (canEdit) setValueFocused(false) }} autoComplete='off' autoCorrect='off' @@ -195,14 +203,16 @@ function WorkspaceVariableRow({ > Details - - - - - Delete secret - + {canEdit && ( + + + + + Delete secret + + )}
) } @@ -316,7 +326,7 @@ export function CredentialsManager() { const { data: workspacePermissions } = useWorkspacePermissionsQuery(workspaceId || null) const queryClient = useQueryClient() - const isAdmin = useMemo(() => { + const isWorkspaceAdmin = useMemo(() => { const userId = session?.user?.id if (!userId || !workspacePermissions?.users) return false const currentUser = workspacePermissions.users.find((user) => user.userId === userId) @@ -791,6 +801,10 @@ export function CredentialsManager() { [pendingKeyValue, renamingKey] ) + const handleWorkspaceValueChange = useCallback((key: string, value: string) => { + setWorkspaceVars((prev) => ({ ...prev, [key]: value })) + }, []) + const handleDeleteWorkspaceVar = useCallback((key: string) => { setWorkspaceVars((prev) => { const next = { ...prev } @@ -1536,25 +1550,27 @@ export function CredentialsManager() { renamingKey={renamingKey} pendingKeyValue={pendingKeyValue} hasCredential={envKeyToCredential.has(key)} - isAdmin={isAdmin} + canEdit={isWorkspaceAdmin} onRenameStart={setRenamingKey} onPendingKeyChange={setPendingKeyValue} onRenameEnd={handleWorkspaceKeyRename} + onValueChange={handleWorkspaceValueChange} onDelete={handleDeleteWorkspaceVar} onViewDetails={(envKey) => handleViewDetails(envKey, 'env_workspace')} /> ))} - {(searchTerm.trim() - ? filteredNewWorkspaceRows - : newWorkspaceRows.map((row, index) => ({ row, originalIndex: index })) - ).map(({ row, originalIndex }) => ( - - ))} + {isWorkspaceAdmin && + (searchTerm.trim() + ? filteredNewWorkspaceRows + : newWorkspaceRows.map((row, index) => ({ row, originalIndex: index })) + ).map(({ row, originalIndex }) => ( + + ))}
)} From 72d4e72250c76a162ea6fc5b43d217786465ae31 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 7 Apr 2026 23:26:57 -0700 Subject: [PATCH 2/2] fix(secrets): cross-browser masking and grid layout for non-admin users --- .../components/credentials/credentials-manager.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx index 9a3f7cd34d..cf2f07519d 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/credentials/credentials-manager.tsx @@ -152,9 +152,6 @@ function WorkspaceVariableRow({ }: WorkspaceVariableRowProps) { const [valueFocused, setValueFocused] = useState(false) - const maskedValueStyle = - canEdit && !valueFocused ? ({ WebkitTextSecurity: 'disc' } as React.CSSProperties) : undefined - return (
onValueChange(envKey, e.target.value)} readOnly onFocus={(e) => { @@ -188,11 +186,11 @@ function WorkspaceVariableRow({ onBlur={() => { if (canEdit) setValueFocused(false) }} + name={`workspace_env_value_${envKey}_${Math.random()}`} autoComplete='off' autoCorrect='off' autoCapitalize='off' spellCheck='false' - style={maskedValueStyle} className='h-9' /> - {canEdit && ( + {canEdit ? (