[platform] postponing plugin privacy dialog till the next start when installing plugins in headless mode (IDEA-259912)

GitOrigin-RevId: e80c71a52e4b1685f163bd860e8ac6aad0960bbf
This commit is contained in:
Roman Shevchenko
2021-11-10 22:51:33 +01:00
committed by intellij-monorepo-bot
parent b44eb87130
commit 609aa2e00a
5 changed files with 130 additions and 40 deletions

View File

@@ -73,4 +73,13 @@ failed.to.make.the.following.files.writable.error.message=Failed to make the fol
failed.to.make.file.writable.error.message=Failed to make {0} writable.
vfs.corruption.notification.title=File system caches corrupted
vfs.corruption.notification.text=Restart is required to rebuild corrupted caches
vfs.corruption.notification.text=Restart is required to rebuild corrupted caches
third.party.plugins.privacy.note.title=Third-Party Plugins Privacy Note
third.party.plugins.privacy.note.text=<html><body>The following plugins aren''t coming from JetBrains:<br><br>\
{0}<br><br>\
Using third-party plugins may involve a plugin vendor processing your personal data.<br>\
Please check the plugin vendor\u2019s documentation for details concerning personal data processing.<br><br>\
JetBrains is not responsible for any processing of your personal data by any third-party plugin vendors.</body></html>
third.party.plugins.privacy.note.accept=Accept
third.party.plugins.privacy.note.disable=Disable Plugins

View File

@@ -4,10 +4,9 @@ package com.intellij.ide.plugins;
import com.intellij.ReviseWhenPortedToJDK;
import com.intellij.core.CoreBundle;
import com.intellij.diagnostic.*;
import com.intellij.icons.AllIcons;
import com.intellij.ide.plugins.cl.PluginAwareClassLoader;
import com.intellij.ide.plugins.cl.PluginClassLoader;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginDescriptor;
@@ -17,6 +16,7 @@ import com.intellij.openapi.util.BuildNumber;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.io.NioFiles;
import com.intellij.openapi.util.text.HtmlChunk;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.PlatformUtils;
@@ -24,6 +24,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.lang.UrlClassLoader;
import org.jetbrains.annotations.*;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.lang.invoke.MethodHandles;
@@ -39,6 +40,7 @@ import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* See <a href="https://github.com/JetBrains/intellij-community/blob/master/docs/plugin.md">Plugin Model</a> documentation.
@@ -48,8 +50,8 @@ import java.util.stream.Stream;
public final class PluginManagerCore {
public static final @NonNls String META_INF = "META-INF/";
public static final PluginId CORE_ID = PluginId.getId("com.intellij");
public static final String CORE_PLUGIN_ID = "com.intellij";
public static final PluginId CORE_ID = PluginId.getId(CORE_PLUGIN_ID);
public static final PluginId JAVA_PLUGIN_ID = PluginId.getId("com.intellij.java");
static final PluginId JAVA_MODULE_ID = PluginId.getId("com.intellij.modules.java");
@@ -74,6 +76,9 @@ public final class PluginManagerCore {
private static final boolean IGNORE_DISABLED_PLUGINS = Boolean.getBoolean("idea.ignore.disabled.plugins");
private static final MethodType HAS_LOADED_CLASS_METHOD_TYPE = MethodType.methodType(boolean.class, String.class);
private static final String THIRD_PARTY_PLUGINS_FILE = "alien_plugins.txt";
private static volatile @Nullable Boolean ourThirdPartyPluginsNoteAccepted = null;
private static Reference<Map<PluginId, Set<String>>> brokenPluginVersions;
private static volatile @Nullable PluginSet pluginSet;
private static Map<PluginId, PluginLoadingError> pluginLoadingErrors;
@@ -338,13 +343,7 @@ public final class PluginManagerCore {
}
public static boolean isDevelopedByJetBrains(@NotNull PluginDescriptor plugin) {
String vendor = plugin.getVendor();
return isDevelopedByJetBrains(vendor) ||
(vendor == null && // a core plugin
!(plugin.getPluginClassLoader() instanceof PluginClassLoader) &&
plugin instanceof IdeaPluginDescriptorImpl &&
!((IdeaPluginDescriptorImpl)plugin).isUseIdeaClassLoader &&
ApplicationInfoEx.getInstanceEx().isVendorJetBrains());
return CORE_ID.equals(plugin.getPluginId()) || SPECIAL_IDEA_PLUGIN_ID.equals(plugin.getPluginId()) || isDevelopedByJetBrains(plugin.getVendor());
}
public static boolean isDevelopedByJetBrains(@Nullable String vendorString) {
@@ -799,6 +798,23 @@ public final class PluginManagerCore {
Collections.singletonList(CORE_ID + " (platform prefix: " + System.getProperty(PlatformUtils.PLATFORM_PREFIX_KEY) + ")"));
}
Collection<? extends IdeaPluginDescriptor> aliens = get3rdPartyPlugins(idMap);
if (!aliens.isEmpty()) {
if (GraphicsEnvironment.isHeadless()) {
getLogger().info("3rd-party plugin privacy note not accepted yet; disabling plugins for this headless session");
aliens.forEach(descriptor -> descriptor.setEnabled(false));
}
else if (!ask3rdPartyPluginsPrivacyConsent(aliens)) {
getLogger().info("3rd-party plugin privacy note declined; disabling plugins");
aliens.forEach(descriptor -> descriptor.setEnabled(false));
PluginEnabler.HEADLESS.disableById(aliens.stream().map(descriptor -> descriptor.getPluginId()).collect(Collectors.toSet()));
ourThirdPartyPluginsNoteAccepted = Boolean.FALSE;
}
else {
ourThirdPartyPluginsNoteAccepted = Boolean.TRUE;
}
}
PluginSetBuilder pluginSetBuilder = new PluginSetBuilder(loadingResult.getEnabledPlugins());
disableIncompatiblePlugins(pluginSetBuilder, idMap, pluginErrorsById);
pluginSetBuilder.checkPluginCycles(globalErrors);
@@ -837,6 +853,56 @@ public final class PluginManagerCore {
return new PluginManagerState(pluginSet, disabledRequired, disabledAfterInit);
}
@ApiStatus.Internal
static @Nullable Boolean isThirdPartyPluginsNoteAccepted() {
Boolean result = ourThirdPartyPluginsNoteAccepted;
ourThirdPartyPluginsNoteAccepted = null;
return result;
}
@ApiStatus.Internal
static synchronized void write3rdPartyPlugins(@NotNull Collection<? extends IdeaPluginDescriptor> aliens) {
Path file = Paths.get(PathManager.getConfigPath(), THIRD_PARTY_PLUGINS_FILE);
try {
NioFiles.createDirectories(file.getParent());
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
//noinspection SSBasedInspection
writePluginsList(aliens.stream().map(PluginDescriptor::getPluginId).collect(Collectors.toList()), writer);
}
}
catch (IOException e) {
getLogger().error(file.toString(), e);
}
}
private static Collection<IdeaPluginDescriptorImpl> get3rdPartyPlugins(Map<PluginId, IdeaPluginDescriptorImpl> descriptors) {
Path file = Paths.get(PathManager.getConfigPath(), THIRD_PARTY_PLUGINS_FILE);
if (Files.exists(file)) {
try {
List<String> ids = Files.readAllLines(file);
Files.delete(file);
return ids.stream().map(id -> descriptors.get(PluginId.getId(id))).filter(descriptor -> descriptor != null).collect(Collectors.toList());
}
catch (IOException e) {
getLogger().error(file.toString(), e);
}
}
return Collections.emptyList();
}
private static boolean ask3rdPartyPluginsPrivacyConsent(Iterable<? extends IdeaPluginDescriptor> descriptors) {
String title = CoreBundle.message("third.party.plugins.privacy.note.title");
String pluginList = StreamSupport.stream(descriptors.spliterator(), false)
.map(descriptor -> "&nbsp;&nbsp;&nbsp;" + descriptor.getName() + " (" + descriptor.getVendor() + ')')
.collect(Collectors.joining("<br>"));
String text = CoreBundle.message("third.party.plugins.privacy.note.text", pluginList);
String[] buttons = {CoreBundle.message("third.party.plugins.privacy.note.accept"), CoreBundle.message("third.party.plugins.privacy.note.disable")};
int choice = JOptionPane.showOptionDialog(null, text, title, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE,
AllIcons.General.WarningDialog, buttons, buttons[0]);
return choice == 0;
}
@SuppressWarnings("DuplicatedCode")
private static @Nullable Map<PluginId, List<IdeaPluginDescriptorImpl>> checkAndPut(@NotNull IdeaPluginDescriptorImpl descriptor,
@NotNull PluginId id,

View File

@@ -1002,13 +1002,6 @@ run.anything.context.separator.modules=Modules
run.anything.context.tooltip=Choose context the current command will be executed in
run.anything.accessible.name=Run anything
third.party.plugins.privacy.note.title=Third-Party Plugins Privacy Note
third.party.plugins.privacy.note.message=<html><body>Using third-party plugins may involve a plugin vendor processing your personal data.<br>\
Please check the plugin vendor\u2019s documentation for details concerning personal data processing.<br><br>\
JetBrains is not responsible for any processing of your personal data by any third-party plugin vendors.</body></html>
third.party.plugins.privacy.note.yes=Accept
third.party.plugins.privacy.note.no=Cancel
plugin.signature.not.signed=<html><body>The ''{0}'' plugin has not been digitally signed, and its authenticity cannot be verified. Installing or updating unsigned plugins may expose your system to risk.<br/><br/>Plugin details: Id: {1} Version: {2} {3}</body></html>
jetbrains.certificate.not.found=JetBrains certificate is not found.
jetbrains.certificate.vendor=Vendor: {0}

View File

@@ -1,10 +1,13 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.plugins;
import com.intellij.CommonBundle;
import com.intellij.core.CoreBundle;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.plugins.marketplace.statistics.PluginManagerUsageCollector;
import com.intellij.ide.plugins.marketplace.statistics.enums.DialogAcceptanceResultEnum;
import com.intellij.idea.Main;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationAction;
import com.intellij.notification.NotificationType;
@@ -44,6 +47,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public final class PluginManagerMain {
private PluginManagerMain() { }
@@ -298,31 +302,50 @@ public final class PluginManagerMain {
.notify(project);
}
public static boolean checkThirdPartyPluginsAllowed(@NotNull Iterable<? extends IdeaPluginDescriptor> descriptors) {
public static boolean checkThirdPartyPluginsAllowed(@NotNull Collection<? extends IdeaPluginDescriptor> descriptors) {
@SuppressWarnings("SSBasedInspection") Collection<? extends IdeaPluginDescriptor> aliens = descriptors.stream()
.filter(descriptor -> !(descriptor.isBundled() || PluginManagerCore.isDevelopedByJetBrains(descriptor)))
.collect(Collectors.toList());
if (aliens.isEmpty()) return true;
UpdateSettings updateSettings = UpdateSettings.getInstance();
if (updateSettings.isThirdPartyPluginsAllowed()) {
PluginManagerUsageCollector.thirdPartyAcceptanceCheck(DialogAcceptanceResultEnum.AUTO_ACCEPTED);
return true;
}
for (IdeaPluginDescriptor descriptor : descriptors) {
if (!PluginManagerCore.isDevelopedByJetBrains(descriptor)) {
String title = IdeBundle.message("third.party.plugins.privacy.note.title");
String message = IdeBundle.message("third.party.plugins.privacy.note.message");
String yesText = IdeBundle.message("third.party.plugins.privacy.note.yes");
String noText = IdeBundle.message("third.party.plugins.privacy.note.no");
if (Messages.showYesNoDialog(message, title, yesText, noText, Messages.getWarningIcon()) == Messages.YES) {
updateSettings.setThirdPartyPluginsAllowed(true);
PluginManagerUsageCollector.thirdPartyAcceptanceCheck(DialogAcceptanceResultEnum.ACCEPTED);
return true;
}
else {
PluginManagerUsageCollector.thirdPartyAcceptanceCheck(DialogAcceptanceResultEnum.DECLINED);
return false;
}
}
if (Main.isHeadless()) {
// postponing the dialog till the next start
PluginManagerCore.write3rdPartyPlugins(aliens);
return true;
}
return true;
String title = CoreBundle.message("third.party.plugins.privacy.note.title");
String pluginList = aliens.stream()
.map(descriptor -> "&nbsp;&nbsp;&nbsp;" + descriptor.getName() + " (" + descriptor.getVendor() + ')')
.collect(Collectors.joining("<br>"));
String message = CoreBundle.message("third.party.plugins.privacy.note.text", pluginList);
String yesText = CoreBundle.message("third.party.plugins.privacy.note.accept"), noText = CommonBundle.getCancelButtonText();
if (Messages.showYesNoDialog(message, title, yesText, noText, Messages.getWarningIcon()) == Messages.YES) {
updateSettings.setThirdPartyPluginsAllowed(true);
PluginManagerUsageCollector.thirdPartyAcceptanceCheck(DialogAcceptanceResultEnum.ACCEPTED);
return true;
}
else {
PluginManagerUsageCollector.thirdPartyAcceptanceCheck(DialogAcceptanceResultEnum.DECLINED);
return false;
}
}
@ApiStatus.Internal
public static void checkThirdPartyPluginsAllowed() {
Boolean noteAccepted = PluginManagerCore.isThirdPartyPluginsNoteAccepted();
if (noteAccepted == Boolean.TRUE) {
UpdateSettings.getInstance().setThirdPartyPluginsAllowed(true);
PluginManagerUsageCollector.thirdPartyAcceptanceCheck(DialogAcceptanceResultEnum.ACCEPTED);
}
else if (noteAccepted == Boolean.FALSE) {
PluginManagerUsageCollector.thirdPartyAcceptanceCheck(DialogAcceptanceResultEnum.DECLINED);
}
}
}

View File

@@ -8,10 +8,7 @@ import com.intellij.diagnostic.*
import com.intellij.diagnostic.StartUpMeasurer.Activities
import com.intellij.icons.AllIcons
import com.intellij.ide.*
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.ide.plugins.PluginSet
import com.intellij.ide.plugins.StartupAbortedException
import com.intellij.ide.plugins.*
import com.intellij.ide.ui.laf.darcula.DarculaLaf
import com.intellij.openapi.application.*
import com.intellij.openapi.application.ex.ApplicationEx
@@ -186,6 +183,8 @@ private fun startApp(app: ApplicationImpl,
addActivateAndWindowsCliListeners()
initAppActivity.end()
PluginManagerMain.checkThirdPartyPluginsAllowed()
if (starter.requiredModality == ApplicationStarter.NOT_IN_EDT) {
starter.main(args)
// no need to use pool once plugins are loaded