From f216b6c22eb6eb697218f22e9b6dac3df61d1316 Mon Sep 17 00:00:00 2001 From: Simeon Andreev Date: Mon, 30 Mar 2026 11:28:44 +0300 Subject: [PATCH] Add API for restricting operations for sensitive files This change adds the following API: * org.eclipse.core.resources.IWorkspace.isRestrictedContentEnabled() * org.eclipse.core.resources.IWorkspace.setRestrictedContentEnabled(boolean) * org.eclipse.core.resources.IFile.isContentRestricted() * org.eclipse.core.resources.IFile.setContentRestricted(boolean) The goal of this API is to allow Eclipse-based IDEs to restrict certain platform functionality on a per-file basis, for sensitive files. To restrict access to sensitive files, at runtime call: 1. IWorkspace.setRestrictedContentEnabled(true) 2. For each sensitive file, call: IFile.setContentRestricted(true) Neither the IWorkspace nor the IFile flags are persisted, to enable restricted handling these flags must be set for each session. The restrictions will apply to following functionality: * File search * Storing file history Potentially more platform functionality will be restricted, if the workspace has restricted handling enabled. See: https://github.com/eclipse-platform/eclipse.platform/issues/2588 --- .../META-INF/MANIFEST.MF | 2 +- .../eclipse/core/internal/resources/File.java | 17 +++++ .../core/internal/resources/Workspace.java | 12 +++ .../src/org/eclipse/core/resources/IFile.java | 29 +++++++ .../eclipse/core/resources/IWorkspace.java | 33 ++++++++ .../resources/AllInternalResourcesTests.java | 1 + .../resources/RestrictedFileTests.java | 76 +++++++++++++++++++ 7 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/resources/RestrictedFileTests.java diff --git a/resources/bundles/org.eclipse.core.resources/META-INF/MANIFEST.MF b/resources/bundles/org.eclipse.core.resources/META-INF/MANIFEST.MF index 6a011fa99a9..bcfaaeb9ae8 100644 --- a/resources/bundles/org.eclipse.core.resources/META-INF/MANIFEST.MF +++ b/resources/bundles/org.eclipse.core.resources/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.core.resources; singleton:=true -Bundle-Version: 3.23.300.qualifier +Bundle-Version: 3.24.0.qualifier Bundle-Activator: org.eclipse.core.resources.ResourcesPlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java index 54a72e30207..ba99d0e9328 100644 --- a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java @@ -41,6 +41,7 @@ import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceStatus; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; @@ -61,6 +62,12 @@ */ public class File extends Resource implements IFile { + /** + * Session property used to mark a file as containing restricted content + */ + private static final QualifiedName RESTRICTED_CONTENT = new QualifiedName(ResourcesPlugin.PI_RESOURCES, + "restrictedContent"); //$NON-NLS-1$ + protected File(IPath path, Workspace container) { super(path, container); } @@ -670,4 +677,14 @@ public String getLineSeparator(boolean checkParent) throws CoreException { return checkParent ? getProject().getDefaultLineSeparator() : null; } + @Override + public boolean isContentRestricted() throws CoreException { + return workspace.isRestrictedContentEnabled() + && getSessionProperty(RESTRICTED_CONTENT) != null; + } + + @Override + public void setContentRestricted(boolean restricted) throws CoreException { + setSessionProperty(RESTRICTED_CONTENT, restricted ? Boolean.TRUE : null); + } } diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java index 690cb8fe72e..2c902dc2bb0 100644 --- a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/Workspace.java @@ -271,6 +271,8 @@ public class Workspace extends PlatformObject implements IWorkspace, ICoreConsta */ protected IFileModificationValidator validator = null; + private volatile boolean enableRestrictedContent; + /** * Data structure for holding the multi-part outcome of * IWorkspace.computeProjectBuildConfigOrder. @@ -2945,4 +2947,14 @@ private void createMultiple(ConcurrentMap filesToCreate, int updat subMonitor.done(); } } + + @Override + public boolean isRestrictedContentEnabled() { + return enableRestrictedContent; + } + + @Override + public void setRestrictedContentEnabled(boolean enabled) { + this.enableRestrictedContent = enabled; + } } diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IFile.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IFile.java index 65864100f6b..121f24172c6 100644 --- a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IFile.java +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IFile.java @@ -53,6 +53,7 @@ * @noextend This interface is not intended to be extended by clients. */ public interface IFile extends IResource, IEncodedStorage, IAdaptable { + /** * Character encoding constant (value 0) which identifies * files that have an unknown character encoding scheme. @@ -1414,4 +1415,32 @@ public default String readString() throws CoreException { public default String getLineSeparator(boolean checkParent) throws CoreException { return getProject().getDefaultLineSeparator(); } + + /** + * Returns whether the current file is marked as containing restricted + * (sensitive) content, where some IDE functionality related to the file content + * might be limited. The file must exist. + * + * @return whether the current file is marked as containing sensitive content. + * This flag is not persisted and is {@code false} by default. + * @throws CoreException if the file doesn't exist or if its session properties + * cannot be read + * @since 3.24 + */ + boolean isContentRestricted() throws CoreException; + + /** + * Marks the current file as containing restricted (sensitive) content. Some IDE + * functionality related to the file content might be limited as long as the + * file is marked as restricted. This flag is not persisted and is {@code false} + * by default. The file must exist. + * + * @param restricted true if the file should be a marked + * restricted, false if the file should be + * unmarked + * @throws CoreException if the file doesn't exist or if its session properties + * cannot be read + * @since 3.24 + */ + void setContentRestricted(boolean restricted) throws CoreException; } diff --git a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IWorkspace.java b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IWorkspace.java index 3c8b42deef4..e95c3f8a03a 100644 --- a/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IWorkspace.java +++ b/resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/IWorkspace.java @@ -1861,4 +1861,37 @@ public default void write(Map contentMap, boolean force, boolean e.getKey().write(e.getValue(), force, derived, keepHistory, subMon.split(1)); } } + + /** + * Returns whether certain IDE functionality should be disabled for restricted + * (sensitive) files. Examples are file history and search. Default is + * false. + *

+ * Restricted files are marked with: {@link IFile#setContentRestricted(boolean)} + *

+ * + * @return true if restricted (sensitive) files handling is enabled + * by the IDE, false otherwise + * @see IFile#setContentRestricted(boolean) + * @see IFile#isContentRestricted() + * @since 3.24 + */ + boolean isRestrictedContentEnabled(); + + /** + * Specifies whether certain IDE functionality should be disabled for restricted + * (sensitive) files. Examples are file history and search. This flag is not + * persisted and must be set for every new session that requires file + * restrictions. + *

+ * Restricted files are marked with: {@link IFile#setContentRestricted(boolean)} + *

+ * + * @param enabled true to enable restricted (sensitive) files + * handling, false otherwise + * @see IFile#setContentRestricted(boolean) + * @see IFile#isContentRestricted() + * @since 3.24 + */ + void setRestrictedContentEnabled(boolean enabled); } diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/resources/AllInternalResourcesTests.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/resources/AllInternalResourcesTests.java index 013b69e6e5b..fdfee9a0a0c 100644 --- a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/resources/AllInternalResourcesTests.java +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/resources/AllInternalResourcesTests.java @@ -32,6 +32,7 @@ ResourceInfoTest.class, // WorkspaceConcurrencyTest.class, // WorkspacePreferencesTest.class, // + RestrictedFileTests.class, // }) public class AllInternalResourcesTests { } diff --git a/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/resources/RestrictedFileTests.java b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/resources/RestrictedFileTests.java new file mode 100644 index 00000000000..a0b38f1c3c7 --- /dev/null +++ b/resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/internal/resources/RestrictedFileTests.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2026 Simeon Andreev and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Simeon Andreev - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.tests.internal.resources; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.tests.resources.util.WorkspaceResetExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(WorkspaceResetExtension.class) +public class RestrictedFileTests { + + @Test + public void testRestrictedFile() throws Exception { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot root = workspace.getRoot(); + IProject project = root.getProject(RestrictedFileTests.class.getSimpleName()); + try { + assertFalse(workspace.isRestrictedContentEnabled(), + "Expected file content to not be restricted by default"); + workspace.setRestrictedContentEnabled(true); + + project.create(null); + project.open(null); + + IFile file1 = project.getFile("test1.txt"); + IFile file2 = project.getFile("test2.txt"); + + try { + file1.isContentRestricted(); + fail("Should not work on not existing files"); + } catch (CoreException e) { + // expected, file should not exist + } + + file1.create("line 1".getBytes(), IResource.FORCE, null); + file2.create("line 1".getBytes(), IResource.FORCE, null); + + assertFalse(file1.isContentRestricted(), "Expected file to not be restricted"); + assertFalse(file2.isContentRestricted(), "Expected file to not be restricted"); + + file1.setContentRestricted(true); + assertTrue(file1.isContentRestricted(), "Expected file to be restricted"); + assertFalse(file2.isContentRestricted(), "Expected file to not be restricted"); + + workspace.setRestrictedContentEnabled(false); + + assertFalse(file1.isContentRestricted(), "Expected file to not be restricted"); + assertFalse(file2.isContentRestricted(), "Expected file to not be restricted"); + } finally { + workspace.setRestrictedContentEnabled(false); + project.delete(true, null); + } + } +}