mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 01:03:40 +00:00
Fix clipboard permissions apparent inconsistency (#1509)
This commit is contained in:
parent
25177ab6ba
commit
99914cf059
|
@ -86,7 +86,10 @@ export default function useTdpClientCanvas(props: Props) {
|
||||||
|
|
||||||
// Default TdpClientEvent.TDP_CLIPBOARD_DATA handler.
|
// Default TdpClientEvent.TDP_CLIPBOARD_DATA handler.
|
||||||
const onClipboardData = async (clipboardData: ClipboardData) => {
|
const onClipboardData = async (clipboardData: ClipboardData) => {
|
||||||
if (clipboardSharingEnabled && document.hasFocus() && clipboardData.data) {
|
if (
|
||||||
|
clipboardData.data &&
|
||||||
|
(await shouldTryClipboardRW(clipboardSharingEnabled))
|
||||||
|
) {
|
||||||
navigator.clipboard.writeText(clipboardData.data);
|
navigator.clipboard.writeText(clipboardData.data);
|
||||||
let digest = await Sha256Digest(clipboardData.data, encoder.current);
|
let digest = await Sha256Digest(clipboardData.data, encoder.current);
|
||||||
latestClipboardDigest.current = digest;
|
latestClipboardDigest.current = digest;
|
||||||
|
@ -224,9 +227,8 @@ export default function useTdpClientCanvas(props: Props) {
|
||||||
// on the remote machine.
|
// on the remote machine.
|
||||||
const onContextMenu = () => false;
|
const onContextMenu = () => false;
|
||||||
|
|
||||||
const sendLocalClipboardToRemote = (cli: TdpClient) => {
|
const sendLocalClipboardToRemote = async (cli: TdpClient) => {
|
||||||
// We must check that the DOM is focused or navigator.clipboard.readText throws an error.
|
if (await shouldTryClipboardRW(clipboardSharingEnabled)) {
|
||||||
if (clipboardSharingEnabled && document.hasFocus()) {
|
|
||||||
navigator.clipboard.readText().then(text => {
|
navigator.clipboard.readText().then(text => {
|
||||||
Sha256Digest(text, encoder.current).then(digest => {
|
Sha256Digest(text, encoder.current).then(digest => {
|
||||||
if (text && digest !== latestClipboardDigest.current) {
|
if (text && digest !== latestClipboardDigest.current) {
|
||||||
|
@ -284,3 +286,46 @@ type Props = {
|
||||||
clipboardSharingEnabled: boolean;
|
clipboardSharingEnabled: boolean;
|
||||||
setWarnings: Dispatch<SetStateAction<NotificationItem[]>>;
|
setWarnings: Dispatch<SetStateAction<NotificationItem[]>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be called before any system clipboard read/write operation.
|
||||||
|
*
|
||||||
|
* @param clipboardSharingEnabled true if clipboard sharing is enabled by RBAC
|
||||||
|
*/
|
||||||
|
async function shouldTryClipboardRW(
|
||||||
|
clipboardSharingEnabled: boolean
|
||||||
|
): Promise<boolean> {
|
||||||
|
return (
|
||||||
|
clipboardSharingEnabled &&
|
||||||
|
document.hasFocus() && // if document doesn't have focus, clipboard r/w will throw an uncatchable error
|
||||||
|
!(await isBrowserClipboardDenied()) // don't try r/w if either permission is denied
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if either 'clipboard-read' or `clipboard-write' are 'denied',
|
||||||
|
* false otherwise.
|
||||||
|
*
|
||||||
|
* This is used as a check before reading from or writing to the clipboard,
|
||||||
|
* because we only want to do so when *both* read and write permissions are
|
||||||
|
* granted (or if either one is 'prompt', which will cause the browser to
|
||||||
|
* prompt the user to specify). This is because Chromium browsers default to
|
||||||
|
* granting clipboard-write permissions, and only allow the user to toggle
|
||||||
|
* clipboard-read. However the prompt makes it seem like the user is granting
|
||||||
|
* or denying all clipboard permissions, which can lead to an awkward UX where
|
||||||
|
* a user has explicitly denied clipboard permissions at the browser level,
|
||||||
|
* but is still getting the remote clipboard contents synced to their local machine.
|
||||||
|
*
|
||||||
|
* By calling this function before any read or write transaction, we ensure we're
|
||||||
|
* complying with the user's explicit intention towards our use of their clipboard.
|
||||||
|
*/
|
||||||
|
async function isBrowserClipboardDenied(): Promise<boolean> {
|
||||||
|
const readPromise = navigator.permissions.query({
|
||||||
|
name: 'clipboard-read' as PermissionName,
|
||||||
|
});
|
||||||
|
const writePromise = navigator.permissions.query({
|
||||||
|
name: 'clipboard-write' as PermissionName,
|
||||||
|
});
|
||||||
|
const [readPerm, writePerm] = await Promise.all([readPromise, writePromise]);
|
||||||
|
return readPerm.state === 'denied' || writePerm.state === 'denied';
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue