diff --git a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.kt b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.kt index 7c1197878a2c..3f8cf1913022 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.kt +++ b/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.kt @@ -11,6 +11,7 @@ import com.intellij.ide.plugins.PluginManagerCore.getPluginSet import com.intellij.ide.plugins.PluginManagerCore.isDisabled import com.intellij.ide.plugins.PluginManagerCore.loadedPlugins import com.intellij.ide.plugins.PluginManagerCore.processAllNonOptionalDependencies +import com.intellij.ide.plugins.cl.PluginAwareClassLoader import com.intellij.ide.plugins.cl.PluginClassLoader import com.intellij.idea.AppMode import com.intellij.openapi.application.PathManager @@ -20,6 +21,7 @@ import com.intellij.openapi.extensions.PluginDescriptor import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.project.Project import com.intellij.openapi.util.BuildNumber +import com.intellij.openapi.util.IntellijInternalApi import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.text.HtmlChunk import com.intellij.ui.IconManager @@ -213,11 +215,17 @@ object PluginManagerCore { fun isPlatformClass(fqn: String): Boolean = fqn.startsWith("java.") || fqn.startsWith("javax.") || fqn.startsWith("kotlin.") || fqn.startsWith("groovy.") - private fun isVendorItemTrusted(vendorItem: String): Boolean = - if (vendorItem.isEmpty()) false - else isVendorJetBrains(vendorItem) || - vendorItem == ApplicationInfoImpl.getShadowInstance().companyName || - vendorItem == ApplicationInfoImpl.getShadowInstance().shortCompanyName + @ApiStatus.Internal + fun isVendorItemTrusted(vendorItem: String): Boolean { + return if (vendorItem.isBlank()) { + false + } + else { + isVendorJetBrains(vendorItem) + || vendorItem == ApplicationInfoImpl.getShadowInstance().companyName + || vendorItem == ApplicationInfoImpl.getShadowInstance().shortCompanyName + } + } @JvmStatic fun isVendorTrusted(vendor: String): Boolean = @@ -993,7 +1001,7 @@ fun pluginRequiresUltimatePluginButItsDisabled(plugin: PluginId, pluginMap: Map< } @ApiStatus.Internal -fun pluginRequiresUltimatePlugin(plugin: PluginId, +fun pluginRequiresUltimatePlugin(plugin: PluginId, pluginMap: Map, contentModuleMap: Map, ): Boolean { @@ -1014,3 +1022,21 @@ fun pluginRequiresUltimatePlugin(rootDescriptor: IdeaPluginDescriptorImpl, } } } + +@ApiStatus.Internal +@IntellijInternalApi +fun isPlatformOrJetBrainsBundled(aClass: Class<*>): Boolean { + val classLoader = aClass.classLoader + when { + classLoader is PluginAwareClassLoader -> { + val plugin = classLoader.pluginDescriptor + return plugin.isBundled && PluginManagerCore.isDevelopedByJetBrains(plugin) + } + PluginManagerCore.isRunningFromSources() -> { + return true + } + else -> { + return PluginUtils.getPluginDescriptorIfIdeaClassLoaderIsUsed(aClass) == null + } + } +} diff --git a/platform/extensions/api-dump.txt b/platform/extensions/api-dump.txt index 98561f1b2fb9..91404fd2e51b 100644 --- a/platform/extensions/api-dump.txt +++ b/platform/extensions/api-dump.txt @@ -28,6 +28,7 @@ f:com.intellij.openapi.extensions.DefaultPluginDescriptor - com.intellij.openapi.extensions.PluginDescriptor - (com.intellij.openapi.extensions.PluginId):V - (com.intellij.openapi.extensions.PluginId,java.lang.ClassLoader):V +- (com.intellij.openapi.extensions.PluginId,java.lang.ClassLoader,java.lang.String):V - (java.lang.String):V - getCategory():java.lang.String - getChangeNotes():java.lang.String diff --git a/platform/extensions/src/com/intellij/openapi/extensions/DefaultPluginDescriptor.java b/platform/extensions/src/com/intellij/openapi/extensions/DefaultPluginDescriptor.java index 8c36a7c87682..6cad7df96acd 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/DefaultPluginDescriptor.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/DefaultPluginDescriptor.java @@ -10,20 +10,24 @@ import java.util.Date; public final class DefaultPluginDescriptor implements PluginDescriptor { private final @NotNull PluginId myPluginId; private final ClassLoader myPluginClassLoader; + private final String myVendor; public DefaultPluginDescriptor(@NotNull String pluginId) { - myPluginId = PluginId.getId(pluginId); - myPluginClassLoader = null; + this(PluginId.getId(pluginId), null); } public DefaultPluginDescriptor(@NotNull PluginId pluginId) { - myPluginId = pluginId; - myPluginClassLoader = null; + this(pluginId, null); } public DefaultPluginDescriptor(@NotNull PluginId pluginId, @Nullable ClassLoader pluginClassLoader) { + this(pluginId, pluginClassLoader, null); + } + + public DefaultPluginDescriptor(@NotNull PluginId pluginId, @Nullable ClassLoader pluginClassLoader, @Nullable String vendor) { myPluginId = pluginId; myPluginClassLoader = pluginClassLoader; + myVendor = vendor; } @Override @@ -78,7 +82,7 @@ public final class DefaultPluginDescriptor implements PluginDescriptor { @Override public @Nullable String getVendor() { - return null; + return myVendor; } @Override diff --git a/platform/settings-sync-core/resources/intellij.settingsSync.core.xml b/platform/settings-sync-core/resources/intellij.settingsSync.core.xml index 80b7b3a2810a..ba30877c1448 100644 --- a/platform/settings-sync-core/resources/intellij.settingsSync.core.xml +++ b/platform/settings-sync-core/resources/intellij.settingsSync.core.xml @@ -46,7 +46,10 @@ - + + + diff --git a/platform/settings-sync-core/resources/messages/SettingsSyncBundle.properties b/platform/settings-sync-core/resources/messages/SettingsSyncBundle.properties index c1e7201aa97a..7bcce10d7f89 100644 --- a/platform/settings-sync-core/resources/messages/SettingsSyncBundle.properties +++ b/platform/settings-sync-core/resources/messages/SettingsSyncBundle.properties @@ -90,7 +90,7 @@ plugins.bundled=Bundled plugins subcategory.config.link=Configure #temporary message settings.jba.plugin.required.text=Please download JetBrains "Backup and Sync" plugin from the Plugins page -settings.jba.plugin.required.title=Plugin download required +settings.jba.plugin.required.title=Plugin Download Required settings.jba.plugin.download=Downloading plugins settings.category.ui.editor.font=Editor font diff --git a/platform/settings-sync-core/src/com/intellij/settingsSync/core/SettingsProvider.kt b/platform/settings-sync-core/src/com/intellij/settingsSync/core/SettingsProvider.kt index 947ec134f902..081660fc99f3 100644 --- a/platform/settings-sync-core/src/com/intellij/settingsSync/core/SettingsProvider.kt +++ b/platform/settings-sync-core/src/com/intellij/settingsSync/core/SettingsProvider.kt @@ -6,7 +6,7 @@ import com.intellij.util.concurrency.annotations.RequiresBackgroundThread import org.jetbrains.annotations.ApiStatus /** - * Allows to store custom settings in the settings sync, if these settings can't be collected and applied via the standard + * Allows storing custom settings in the settings sync, if these settings can't be collected and applied via the standard * [PersistentStateComponent] mechanism. * * @param T the structure class holding the settings. @@ -16,7 +16,8 @@ import org.jetbrains.annotations.ApiStatus interface SettingsProvider { companion object { - val SETTINGS_PROVIDER_EP = ExtensionPointName.create>("com.intellij.settingsSync.settingsProvider") + val SETTINGS_PROVIDER_EP: ExtensionPointName> = + ExtensionPointName.create("com.intellij.settingsSync.settingsProvider") } /** @@ -64,7 +65,7 @@ interface SettingsProvider { * * @param newer The state of the settings which was made later. * Usually, if there is a real conflict in the settings between the local and the cloud modifications (when the same property is set - * to different values), then the newer version should be performed, because that value was set by the user more recently. + * to different values), then the newer version should be performed. The user set that value more recently. * * @return the resulting state which will be used as the conflict resolution and will be recorded to the settings sync and propagated * both to the local IDE and to the cloud (and then to other machines). diff --git a/platform/settings-sync-core/src/com/intellij/settingsSync/core/auth/SettingsSyncAuthService.kt b/platform/settings-sync-core/src/com/intellij/settingsSync/core/auth/SettingsSyncAuthService.kt index 4aa244f245c6..df025f09d59f 100644 --- a/platform/settings-sync-core/src/com/intellij/settingsSync/core/auth/SettingsSyncAuthService.kt +++ b/platform/settings-sync-core/src/com/intellij/settingsSync/core/auth/SettingsSyncAuthService.kt @@ -1,15 +1,23 @@ package com.intellij.settingsSync.core.auth -import com.intellij.settingsSync.core.SettingsSyncStatusTracker import com.intellij.settingsSync.core.communicator.SettingsSyncUserData +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls import java.awt.Component import javax.swing.Icon +/** + * This is an internal extension that requires an explicit license agreement with JetBrains s.r.o. for plugins. + * Only IDE-bundled plugins are allowed to implement it. + * + * Contact https://platform.jetbrains.com/ for details. + * You may not use this extension until it is unlocked in the platform for your plugin. + */ +@ApiStatus.Internal interface SettingsSyncAuthService { /** - * short, self-explanatory and unique code name of the provider. May or may not match the - * @see com.intellij.settingsSync.communicator.SettingsSyncCommunicatorProvider#getProviderCode() + * short, self-explanatory and unique code name of the provider. May or may not match the provider code. + * @see com.intellij.settingsSync.core.communicator.SettingsSyncCommunicatorProvider#getProviderCode() */ val providerCode: String @@ -26,7 +34,7 @@ interface SettingsSyncAuthService { /** * Provides a function/action responsible for the logout procedure or navigates a user to the place where they can log out themselves. * The method must call `SettingsSyncEvents.getInstance().fireLoginStateChanged()` in order to propagate the changed state. - * If function is null, logout link in the UI is not visible + * If the function is null, a logout link in the UI is not visible */ val logoutFunction: (suspend (Component?) -> Unit)? get() = null @@ -62,6 +70,6 @@ interface SettingsSyncAuthService { val message: @Nls String, val actionTitle: @Nls String, val actionDescription: @Nls String? = null, - val action: suspend (Component?) -> Unit + val action: suspend (Component?) -> Unit, ) } \ No newline at end of file diff --git a/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/RemoteCommunicatorHolder.kt b/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/RemoteCommunicatorHolder.kt index 08a20c515511..f369a6d1809a 100644 --- a/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/RemoteCommunicatorHolder.kt +++ b/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/RemoteCommunicatorHolder.kt @@ -1,3 +1,5 @@ +@file:OptIn(IntellijInternalApi::class) + package com.intellij.settingsSync.core.communicator import com.intellij.icons.AllIcons @@ -16,6 +18,7 @@ import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.ui.Messages import com.intellij.openapi.updateSettings.impl.PluginDownloader import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.IntellijInternalApi import com.intellij.platform.ide.progress.ModalTaskOwner import com.intellij.platform.ide.progress.TaskCancellation import com.intellij.platform.ide.progress.withModalProgress @@ -141,7 +144,7 @@ object RemoteCommunicatorHolder : SettingsSyncEventListener { fun getAvailableProviders(): List { val extensionList = arrayListOf() - extensionList.addAll(SettingsSyncCommunicatorProvider.PROVIDER_EP.extensionList.filter { it.isAvailable() }) + extensionList.addAll(getAvailableSyncProviders()) if (extensionList.find { it.providerCode == DEFAULT_PROVIDER_CODE } == null) { extensionList.add(DelegatingDefaultCommunicatorProvider) } @@ -242,7 +245,7 @@ object RemoteCommunicatorHolder : SettingsSyncEventListener { return null } - val defaultProvider = SettingsSyncCommunicatorProvider.PROVIDER_EP.extensionList.find { it.providerCode == DEFAULT_PROVIDER_CODE } + val defaultProvider = getAvailableSyncProviders().find { it.providerCode == DEFAULT_PROVIDER_CODE } ?: return null DelegatingDefaultCommunicatorProvider.delegate = defaultProvider return defaultProvider.authService.login(parentComponent) diff --git a/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/SettingsSyncCommunicatorBean.kt b/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/SettingsSyncCommunicatorBean.kt new file mode 100644 index 000000000000..1a9587bf16a4 --- /dev/null +++ b/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/SettingsSyncCommunicatorBean.kt @@ -0,0 +1,48 @@ +@file:OptIn(IntellijInternalApi::class) + +package com.intellij.settingsSync.core.communicator + +import com.intellij.ide.plugins.PluginManagerCore +import com.intellij.openapi.extensions.ExtensionPoint +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.extensions.RequiredElement +import com.intellij.openapi.util.IntellijInternalApi +import com.intellij.serviceContainer.BaseKeyedLazyInstance +import com.intellij.util.xmlb.annotations.Attribute +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.TestOnly + +@ApiStatus.Internal +open class SettingsSyncCommunicatorBean : BaseKeyedLazyInstance() { + @Attribute("implementation") + @JvmField + @RequiredElement + var implementation: String = "" + + override fun getImplementationClassName(): String = implementation +} + +private val PROVIDER_EP: ExtensionPointName = + ExtensionPointName.create("com.intellij.settingsSync.communicatorProvider") + +@Suppress("UNCHECKED_CAST") +@TestOnly +@ApiStatus.Internal +fun getSyncProviderPoint(): ExtensionPoint { + return PROVIDER_EP.point +} + +@ApiStatus.Internal +fun getAvailableSyncProviders(): List { + return PROVIDER_EP.extensionList + .filter { + val plugin = it.pluginDescriptor + val vendorName = plugin.vendor ?: plugin.organization ?: "" + + plugin.isBundled + || PluginManagerCore.isDevelopedByJetBrains(plugin) + || PluginManagerCore.isVendorItemTrusted(vendorName) + } + .map { bean -> bean.instance } + .filter { it.isAvailable() } +} \ No newline at end of file diff --git a/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/SettingsSyncCommunicatorProvider.kt b/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/SettingsSyncCommunicatorProvider.kt index 5b3e3a1a8f0b..b477ff8cb865 100644 --- a/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/SettingsSyncCommunicatorProvider.kt +++ b/platform/settings-sync-core/src/com/intellij/settingsSync/core/communicator/SettingsSyncCommunicatorProvider.kt @@ -1,9 +1,19 @@ package com.intellij.settingsSync.core.communicator -import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.util.IntellijInternalApi import com.intellij.settingsSync.core.SettingsSyncRemoteCommunicator import com.intellij.settingsSync.core.auth.SettingsSyncAuthService +import org.jetbrains.annotations.ApiStatus +/** + * This is an internal extension that requires an explicit license agreement with JetBrains s.r.o. for plugins. + * Only IDE-bundled plugins are allowed to implement it. + * + * Contact https://platform.jetbrains.com/ for details. + * You may not use this extension until it is unlocked in the platform for your plugin. + */ +@ApiStatus.Internal +@IntellijInternalApi interface SettingsSyncCommunicatorProvider { /** @@ -27,7 +37,7 @@ interface SettingsSyncCommunicatorProvider { /** * Used in the select provider dialog. - * a Pair: + * A pair contains: * * link text, for instance: Learn more * * actual link itself, for instance: https://www.jetbrains.com/help/idea/sharing-your-ide-settings.html */ @@ -40,14 +50,9 @@ interface SettingsSyncCommunicatorProvider { fun createCommunicator(userId: String): SettingsSyncRemoteCommunicator? /** - * Indicates whether provider is available. Allows to control provider availability inside the plugin + * Indicates whether a provider is available. Allows controlling provider availability inside the plugin */ fun isAvailable(): Boolean = true - - companion object { - @JvmField - val PROVIDER_EP = ExtensionPointName.create("com.intellij.settingsSync.communicatorProvider") - } } data class SettingsSyncUserData( @@ -55,5 +60,5 @@ data class SettingsSyncUserData( val providerCode: String, val name: String?, val email: String?, - val printableName: String? = null + val printableName: String? = null, ) \ No newline at end of file diff --git a/platform/settings-sync-core/src/com/intellij/settingsSync/core/config/SettingsSyncConfigurable.kt b/platform/settings-sync-core/src/com/intellij/settingsSync/core/config/SettingsSyncConfigurable.kt index 6a880c204f8f..1531cd098f97 100644 --- a/platform/settings-sync-core/src/com/intellij/settingsSync/core/config/SettingsSyncConfigurable.kt +++ b/platform/settings-sync-core/src/com/intellij/settingsSync/core/config/SettingsSyncConfigurable.kt @@ -1,3 +1,5 @@ +@file:OptIn(IntellijInternalApi::class) + package com.intellij.settingsSync.core.config @@ -27,6 +29,7 @@ import com.intellij.openapi.ui.popup.ListSeparator import com.intellij.openapi.ui.popup.PopupStep import com.intellij.openapi.ui.popup.util.BaseListPopupStep import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.IntellijInternalApi import com.intellij.platform.ide.progress.ModalTaskOwner import com.intellij.platform.ide.progress.TaskCancellation import com.intellij.platform.ide.progress.runWithModalProgressBlocking @@ -37,6 +40,7 @@ import com.intellij.settingsSync.core.auth.SettingsSyncAuthService.PendingUserAc import com.intellij.settingsSync.core.communicator.RemoteCommunicatorHolder import com.intellij.settingsSync.core.communicator.SettingsSyncCommunicatorProvider import com.intellij.settingsSync.core.communicator.SettingsSyncUserData +import com.intellij.settingsSync.core.communicator.getAvailableSyncProviders import com.intellij.settingsSync.core.config.SettingsSyncEnabler.State import com.intellij.settingsSync.core.statistics.SettingsSyncEventsStatistics import com.intellij.ui.RelativeFont @@ -128,11 +132,15 @@ internal class SettingsSyncConfigurable(private val coroutineScope: CoroutineSco val authService = userProviderHolder?.let { RemoteCommunicatorHolder.getProvider(userProviderHolder.providerCode) } ?.authService syncPanelHolder.crossSyncSupported.set(authService?.crossSyncSupported() ?: true) val infoRow = row { + @Suppress("DialogTitleCapitalization") text(message("settings.sync.info.message")) - SettingsSyncCommunicatorProvider.PROVIDER_EP.extensionList.firstOrNull { it.isAvailable() && it.learnMoreLinkPair != null }?.also { - val linkPair = it.learnMoreLinkPair!! - browserLink(linkPair.first, linkPair.second) - } + getAvailableSyncProviders() + .firstOrNull { it.learnMoreLinkPair != null } + ?.also { + val linkPair = it.learnMoreLinkPair!! + @Suppress("HardCodedStringLiteral") + browserLink(linkPair.first, linkPair.second) + } } rowsRange { @@ -698,7 +706,7 @@ internal class SettingsSyncConfigurable(private val coroutineScope: CoroutineSco } } - // triggers fake action, which causes SettingEditor to update and check if configurable was modified + // triggers a fake action, which causes SettingEditor to update and check if configurable was modified // must be called on EDT private fun triggerUpdateConfigurable() { val dumbAwareAction = DumbAwareAction.create(Consumer { _: AnActionEvent? -> diff --git a/platform/settings-sync-core/tests/com/intellij/settingsSync/core/MockRemoteCommunicator.kt b/platform/settings-sync-core/tests/com/intellij/settingsSync/core/MockRemoteCommunicator.kt index 16c9ce048597..fde72739fc00 100644 --- a/platform/settings-sync-core/tests/com/intellij/settingsSync/core/MockRemoteCommunicator.kt +++ b/platform/settings-sync-core/tests/com/intellij/settingsSync/core/MockRemoteCommunicator.kt @@ -1,6 +1,9 @@ +@file:OptIn(IntellijInternalApi::class) + package com.intellij.settingsSync.core import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.util.IntellijInternalApi import com.intellij.openapi.util.io.FileUtil import com.intellij.settingsSync.core.auth.SettingsSyncAuthService import com.intellij.settingsSync.core.communicator.SettingsSyncCommunicatorProvider @@ -163,9 +166,10 @@ internal class MockRemoteCommunicator(override val userId: String) : AbstractSer internal class MockCommunicatorProvider ( private val remoteCommunicator: SettingsSyncRemoteCommunicator, override val authService: SettingsSyncAuthService, + private val code: String? = null ): SettingsSyncCommunicatorProvider { override val providerCode: String - get() = MOCK_CODE + get() = code ?: MOCK_CODE override fun createCommunicator(userId: String): SettingsSyncRemoteCommunicator? = remoteCommunicator } diff --git a/platform/settings-sync-core/tests/com/intellij/settingsSync/core/SettingsSyncTestBase.kt b/platform/settings-sync-core/tests/com/intellij/settingsSync/core/SettingsSyncTestBase.kt index 702ab5b04536..b575bdfa1c01 100644 --- a/platform/settings-sync-core/tests/com/intellij/settingsSync/core/SettingsSyncTestBase.kt +++ b/platform/settings-sync-core/tests/com/intellij/settingsSync/core/SettingsSyncTestBase.kt @@ -1,12 +1,18 @@ +@file:OptIn(IntellijInternalApi::class) + package com.intellij.settingsSync.core +import com.intellij.ide.plugins.PluginManagerCore.VENDOR_JETBRAINS import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.impl.ApplicationImpl +import com.intellij.openapi.components.ComponentManager import com.intellij.openapi.diagnostic.logger -import com.intellij.settingsSync.core.communicator.RemoteCommunicatorHolder -import com.intellij.settingsSync.core.communicator.SettingsSyncCommunicatorProvider -import com.intellij.settingsSync.core.communicator.SettingsSyncUserData +import com.intellij.openapi.extensions.DefaultPluginDescriptor +import com.intellij.openapi.extensions.PluginDescriptor +import com.intellij.openapi.extensions.PluginId +import com.intellij.openapi.util.IntellijInternalApi +import com.intellij.settingsSync.core.communicator.* import com.intellij.testFramework.common.DEFAULT_TEST_TIMEOUT import com.intellij.testFramework.common.timeoutRunBlocking import com.intellij.testFramework.junit5.TestApplication @@ -29,13 +35,11 @@ import kotlin.time.Duration internal val TIMEOUT_UNIT = TimeUnit.SECONDS +private val LOG = logger() + @TestApplication internal abstract class SettingsSyncTestBase { - companion object { - val LOG = logger() - } - protected lateinit var application: ApplicationImpl protected lateinit var configDir: Path protected lateinit var remoteCommunicator: MockRemoteCommunicator @@ -61,11 +65,11 @@ internal abstract class SettingsSyncTestBase { else { MockRemoteCommunicator("mockUser").apply {this.isConnected = true } } - val providerEP = SettingsSyncCommunicatorProvider.PROVIDER_EP.point + val providerEP = getSyncProviderPoint() if (providerEP.extensions.size > 0) { LOG.warn("SettingsSyncCommunicatorProvider.PROVIDER_EP is not empty: ${providerEP.extensions.toList()}") - providerEP.extensions.forEach { - LOG.warn("Unregistering extension: ${it.javaClass.name}") + for (it in providerEP.extensions) { + LOG.warn("Unregistering extension: ${it.instance.javaClass.name}") providerEP.unregisterExtension(it) } } @@ -76,7 +80,21 @@ internal abstract class SettingsSyncTestBase { remoteCommunicator, authService ) - providerEP.registerExtension(mockCommunicatorProvider, disposable) + providerEP.registerExtension(object : SettingsSyncCommunicatorBean() { + init { + this.pluginDescriptor = DefaultPluginDescriptor( + PluginId.getId("com.intellij.settingsSync"), + SettingsSyncTestBase::class.java.getClassLoader(), + VENDOR_JETBRAINS + ) + } + + override fun createInstance( + componentManager: ComponentManager, + pluginDescriptor: PluginDescriptor, + ): SettingsSyncCommunicatorProvider = mockCommunicatorProvider + }, disposable) + SettingsSyncLocalSettings.getInstance().providerCode = mockCommunicatorProvider.providerCode SettingsSyncLocalSettings.getInstance().userId = DUMMY_USER_ID @@ -149,7 +167,6 @@ internal abstract class SettingsSyncTestBase { } } - internal fun CountDownLatch.wait(): Boolean { return this.await(getDefaultTimeoutInSeconds(), TIMEOUT_UNIT) } diff --git a/platform/settings-sync-core/tests/com/intellij/settingsSync/core/SettingsSyncVendorsTest.kt b/platform/settings-sync-core/tests/com/intellij/settingsSync/core/SettingsSyncVendorsTest.kt new file mode 100644 index 000000000000..dddf10efab16 --- /dev/null +++ b/platform/settings-sync-core/tests/com/intellij/settingsSync/core/SettingsSyncVendorsTest.kt @@ -0,0 +1,78 @@ +@file:OptIn(IntellijInternalApi::class) + +package com.intellij.settingsSync.core + +import com.intellij.ide.plugins.PluginManagerCore.VENDOR_JETBRAINS +import com.intellij.openapi.components.ComponentManager +import com.intellij.openapi.extensions.DefaultPluginDescriptor +import com.intellij.openapi.extensions.PluginDescriptor +import com.intellij.openapi.extensions.PluginId +import com.intellij.openapi.util.IntellijInternalApi +import com.intellij.settingsSync.core.communicator.SettingsSyncCommunicatorBean +import com.intellij.settingsSync.core.communicator.SettingsSyncCommunicatorProvider +import com.intellij.settingsSync.core.communicator.getAvailableSyncProviders +import com.intellij.settingsSync.core.communicator.getSyncProviderPoint +import com.intellij.testFramework.UsefulTestCase.assertSize +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class SettingSyncVendorsTest : SettingsSyncTestBase() { + @BeforeEach + fun setupProviders() { + val providerPoint = getSyncProviderPoint() + + providerPoint.registerExtension(object : SettingsSyncCommunicatorBean() { + init { + this.pluginDescriptor = DefaultPluginDescriptor( + PluginId.getId("com.intellij.allowed.sync.provider"), + SettingsSyncTestBase::class.java.getClassLoader(), + VENDOR_JETBRAINS + ) + } + + override fun createInstance( + componentManager: ComponentManager, + pluginDescriptor: PluginDescriptor, + ): SettingsSyncCommunicatorProvider = MockCommunicatorProvider( + remoteCommunicator, + authService, + "ALLOWED_PROVIDER" + ) + }, disposable) + + providerPoint.registerExtension(object : SettingsSyncCommunicatorBean() { + init { + this.pluginDescriptor = DefaultPluginDescriptor( + PluginId.getId("com.intellij.3rd.party.provider"), + SettingsSyncTestBase::class.java.getClassLoader(), + "YourCompany" + ) + } + + override fun createInstance( + componentManager: ComponentManager, + pluginDescriptor: PluginDescriptor, + ): SettingsSyncCommunicatorProvider = MockCommunicatorProvider( + remoteCommunicator, + authService, + "UNAUTHORIZED_PROVIDER" + ) + }, disposable) + } + + @Test + fun testUnauthorizedProviderIsExcluded() { + val extensions = getSyncProviderPoint().extensionList + assertSize(3, extensions) // precondition, everything registered + + val availableSyncProviders = getAvailableSyncProviders() + assertSize(2, availableSyncProviders) + + val codes = availableSyncProviders.map { it.providerCode } + + assertTrue(codes.contains("ALLOWED_PROVIDER"), "ALLOWED_PROVIDER must be present in the list") + assertFalse(codes.contains("UNAUTHORIZED_PROVIDER"), "UNAUTHORIZED_PROVIDER must be absent from the list") + } +} diff --git a/platform/statistics/src/com/intellij/internal/statistic/DeviceIdManager.java b/platform/statistics/src/com/intellij/internal/statistic/DeviceIdManager.java index 414c2f903c48..9bf57a8d600d 100644 --- a/platform/statistics/src/com/intellij/internal/statistic/DeviceIdManager.java +++ b/platform/statistics/src/com/intellij/internal/statistic/DeviceIdManager.java @@ -1,7 +1,6 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.internal.statistic; -import com.intellij.internal.statistic.utils.PluginInfoDetectorKt; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.diagnostic.Logger; @@ -23,6 +22,8 @@ import java.util.Locale; import java.util.UUID; import java.util.prefs.Preferences; +import static com.intellij.ide.plugins.PluginManagerCoreKt.isPlatformOrJetBrainsBundled; + public final class DeviceIdManager { private static final Logger LOG = Logger.getInstance(DeviceIdManager.class); @@ -60,7 +61,7 @@ public final class DeviceIdManager { if (token == null) { throw new InvalidDeviceIdTokenException("Cannot access base device id from unknown class"); } - else if (!PluginInfoDetectorKt.isPlatformOrJetBrainsBundled(token.getClass())) { + else if (!isPlatformOrJetBrainsBundled(token.getClass())) { throw new InvalidDeviceIdTokenException("Cannot access base device id from " + token.getClass().getName()); } } diff --git a/platform/statistics/src/com/intellij/internal/statistic/utils/PluginInfoDetector.kt b/platform/statistics/src/com/intellij/internal/statistic/utils/PluginInfoDetector.kt index cc1ba1b798d5..1b268da17afd 100644 --- a/platform/statistics/src/com/intellij/internal/statistic/utils/PluginInfoDetector.kt +++ b/platform/statistics/src/com/intellij/internal/statistic/utils/PluginInfoDetector.kt @@ -27,22 +27,6 @@ fun getPluginInfo(aClass: Class<*>): PluginInfo { } } -internal fun isPlatformOrJetBrainsBundled(aClass: Class<*>): Boolean { - val classLoader = aClass.classLoader - when { - classLoader is PluginAwareClassLoader -> { - val plugin = classLoader.pluginDescriptor - return plugin.isBundled && PluginManagerCore.isDevelopedByJetBrains(plugin) - } - PluginManagerCore.isRunningFromSources() -> { - return true - } - else -> { - return PluginUtils.getPluginDescriptorIfIdeaClassLoaderIsUsed(aClass) == null - } - } -} - @ApiStatus.Internal fun hasStandardExceptionPrefix(className: String): Boolean = className.startsWith("java.") || className.startsWith("javax.") || diff --git a/plugins/settings-sync/jba/src/com/intellij/settingsSync/jba/auth/JBAAuthService.kt b/plugins/settings-sync/jba/src/com/intellij/settingsSync/jba/auth/JBAAuthService.kt index 98d183602fbc..91a079f35f0e 100644 --- a/plugins/settings-sync/jba/src/com/intellij/settingsSync/jba/auth/JBAAuthService.kt +++ b/plugins/settings-sync/jba/src/com/intellij/settingsSync/jba/auth/JBAAuthService.kt @@ -1,13 +1,11 @@ package com.intellij.settingsSync.jba.auth import com.intellij.CommonBundle -import com.intellij.icons.AllIcons import com.intellij.ide.plugins.PluginManagerCore import com.intellij.idea.AppMode import com.intellij.openapi.actionSystem.* import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.actionSystem.ex.ActionUtil.performAction -import com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAwareWithCallbacks import com.intellij.openapi.application.EDT import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.asContextElement @@ -48,13 +46,10 @@ import javax.swing.event.HyperlinkEvent import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException +private val LOG = logger() +private const val JBA_USER_ID = "jba" + internal class JBAAuthService(private val cs: CoroutineScope) : SettingsSyncAuthService { - - companion object { - private val LOG = logger() - private const val JBA_USER_ID = "jba" - } - @Volatile private var invalidatedIdToken: String? = null