Bug 573950 - Secure storage provider compatibility break
- change the org.eclipse.equinox.security extension schema
to allow an optional "obsoletes" attribute to the provider
element to allow specifying an id that is being replaced by this
provider
- modify org.eclipse.equinox.security.linux fragment.xml to
note that linuxkeystoreintegrationjna obsoletes
org.eclipse.equinox.security.linuxkeystoreintegration provider
- modify PasswordProviderSelector.findAvailableModules() method
to look for "obsoletes" attribute if the id doesn't match the
specified non-null id
- add new passwordProvidersFind() method to InternalExchangeUtils to
allow testing of the "obsoletes" attribute
- add new ObsoletesTest test to AllSecurityTests
- avoid master password regenerate if secure store created
with obsolete module
- bump up versions appropriately
Change-Id: I89ca3252c96d1069617661d6081f04711cf3eabc
Signed-off-by: Jeff Johnston <jjohnstn@redhat.com>
Signed-off-by: Julien Dehaudt <julien.dehaudt@st.com>
Reviewed-on: https://git.eclipse.org/r/c/equinox/rt.equinox.bundles/+/181421
Tested-by: Equinox Bot <equinox-bot@eclipse.org>
Reviewed-by: Alexander Kurtakov <akurtako@redhat.com>
diff --git a/bundles/org.eclipse.equinox.security.linux/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.security.linux/META-INF/MANIFEST.MF
index b1cc95b..987f446 100644
--- a/bundles/org.eclipse.equinox.security.linux/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.security.linux/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: %fragmentName
Bundle-SymbolicName: org.eclipse.equinox.security.linux;singleton:=true
-Bundle-Version: 1.0.100.qualifier
+Bundle-Version: 1.0.200.qualifier
Bundle-Vendor: %providerName
Fragment-Host: org.eclipse.equinox.security;bundle-version="[1.0.0,2.0.0)"
Bundle-RequiredExecutionEnvironment: JavaSE-11
diff --git a/bundles/org.eclipse.equinox.security.linux/fragment.properties b/bundles/org.eclipse.equinox.security.linux/fragment.properties
index b0c9c1f..3215da8 100644
--- a/bundles/org.eclipse.equinox.security.linux/fragment.properties
+++ b/bundles/org.eclipse.equinox.security.linux/fragment.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2017 IBM Corporation and others.
+# Copyright (c) 2017, 2021 IBM Corporation and others.
#
# This program and the accompanying materials
# are made available under the terms of the Eclipse Public License 2.0
diff --git a/bundles/org.eclipse.equinox.security.linux/fragment.xml b/bundles/org.eclipse.equinox.security.linux/fragment.xml
index 971743f..d91ed1d 100644
--- a/bundles/org.eclipse.equinox.security.linux/fragment.xml
+++ b/bundles/org.eclipse.equinox.security.linux/fragment.xml
@@ -8,11 +8,11 @@
<provider
class="org.eclipse.equinox.internal.security.linux.LinuxPasswordProvider"
description="%providerDescription"
+ obsoletes="org.eclipse.equinox.security.linuxkeystoreintegration"
priority="5">
<hint
value="AutomaticPasswordGeneration">
</hint>
</provider>
</extension>
-
-</fragment>
+ </fragment>
diff --git a/bundles/org.eclipse.equinox.security.linux/pom.xml b/bundles/org.eclipse.equinox.security.linux/pom.xml
index 0b3c85c..e42cfb0 100644
--- a/bundles/org.eclipse.equinox.security.linux/pom.xml
+++ b/bundles/org.eclipse.equinox.security.linux/pom.xml
@@ -20,7 +20,7 @@
</parent>
<groupId>org.eclipse.equinox</groupId>
<artifactId>org.eclipse.equinox.security.linux</artifactId>
- <version>1.0.100-SNAPSHOT</version>
+ <version>1.0.200-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
<build>
diff --git a/bundles/org.eclipse.equinox.security.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.security.tests/META-INF/MANIFEST.MF
index 4e53ef1..fd52e82 100644
--- a/bundles/org.eclipse.equinox.security.tests/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.security.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: Equinox security tests
Bundle-SymbolicName: org.eclipse.equinox.security.tests;singleton:=true
-Bundle-Version: 1.2.0.qualifier
+Bundle-Version: 1.2.100.qualifier
Bundle-Activator: org.eclipse.equinox.internal.security.tests.SecurityTestsActivator
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-Vendor: Eclipse.org
diff --git a/bundles/org.eclipse.equinox.security.tests/pom.xml b/bundles/org.eclipse.equinox.security.tests/pom.xml
index c015e8f..04e68ee 100644
--- a/bundles/org.eclipse.equinox.security.tests/pom.xml
+++ b/bundles/org.eclipse.equinox.security.tests/pom.xml
@@ -19,7 +19,7 @@
</parent>
<groupId>org.eclipse.equinox</groupId>
<artifactId>org.eclipse.equinox.security.tests</artifactId>
- <version>1.2.0-SNAPSHOT</version>
+ <version>1.2.100-SNAPSHOT</version>
<packaging>eclipse-test-plugin</packaging>
<properties>
<testClass>org.eclipse.equinox.security.tests.AllSecurityTests</testClass>
diff --git a/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/ObsoletesTest.java b/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/ObsoletesTest.java
new file mode 100644
index 0000000..86610b1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/ObsoletesTest.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Red Hat Inc. 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:
+ * Red Hat Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.security.tests.storage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+import org.eclipse.equinox.internal.security.storage.friends.InternalExchangeUtils;
+import org.eclipse.equinox.internal.security.storage.friends.PasswordProviderDescription;
+import org.eclipse.equinox.internal.security.tests.SecurityTestsActivator;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class ObsoletesTest {
+
+ final private static String LINUX_BUNDLE = "org.eclipse.equinox.security.linux";
+
+ @Before
+ public void setUp() {
+ org.junit.Assume.assumeTrue(hasBundle(LINUX_BUNDLE));
+ }
+
+ @Test
+ public void testObsoletes() {
+ List<PasswordProviderDescription> descs = InternalExchangeUtils
+ .passwordProvidersFind("org.eclipse.equinox.security.linuxkeystoreintegration");
+ assertNotNull(descs);
+ assertEquals(1, descs.size());
+ PasswordProviderDescription desc = descs.get(0);
+ assertEquals("org.eclipse.equinox.security.linuxkeystoreintegrationjna", desc.getId());
+ }
+
+ static private boolean hasBundle(String symbolicID) {
+ BundleContext context = SecurityTestsActivator.getDefault().getBundleContext();
+ Bundle[] bundles = context.getBundles();
+ for (Bundle bundle : bundles) {
+ String bundleName = bundle.getSymbolicName();
+ if (!symbolicID.equals(bundleName))
+ continue;
+ int bundleState = bundle.getState();
+ return (bundleState != Bundle.INSTALLED) && (bundleState != Bundle.UNINSTALLED);
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/security/tests/AllSecurityTests.java b/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/security/tests/AllSecurityTests.java
index 8e03b09..7e84a7f 100644
--- a/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/security/tests/AllSecurityTests.java
+++ b/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/security/tests/AllSecurityTests.java
@@ -26,7 +26,7 @@
*/
@RunWith(Suite.class)
@SuiteClasses({ Base64Test.class, DetectPBECiphersTest.class, SlashEncodeTest.class, DefaultPreferencesTest.class,
- DynamicPreferencesTest.class, WinPreferencesTest.class })
+ DynamicPreferencesTest.class, ObsoletesTest.class, WinPreferencesTest.class })
public class AllSecurityTests {
// see @SuiteClasses
}
diff --git a/bundles/org.eclipse.equinox.security/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.security/META-INF/MANIFEST.MF
index 9fa41ba..d347f75 100644
--- a/bundles/org.eclipse.equinox.security/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.security/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.equinox.security;singleton:=true
-Bundle-Version: 1.3.600.qualifier
+Bundle-Version: 1.3.700.qualifier
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Bundle-Activator: org.eclipse.equinox.internal.security.auth.AuthPlugin
diff --git a/bundles/org.eclipse.equinox.security/pom.xml b/bundles/org.eclipse.equinox.security/pom.xml
index 9162149..ab6dc3b 100644
--- a/bundles/org.eclipse.equinox.security/pom.xml
+++ b/bundles/org.eclipse.equinox.security/pom.xml
@@ -19,7 +19,7 @@
</parent>
<groupId>org.eclipse.equinox</groupId>
<artifactId>org.eclipse.equinox.security</artifactId>
- <version>1.3.600-SNAPSHOT</version>
+ <version>1.3.700-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
<build>
diff --git a/bundles/org.eclipse.equinox.security/schema/secureStorage.exsd b/bundles/org.eclipse.equinox.security/schema/secureStorage.exsd
index df73e0e..d23f7f6 100644
--- a/bundles/org.eclipse.equinox.security/schema/secureStorage.exsd
+++ b/bundles/org.eclipse.equinox.security/schema/secureStorage.exsd
@@ -44,7 +44,7 @@
</appInfo>
</annotation>
</attribute>
- </complexType>
+ </complexType>
</element>
<element name="provider">
@@ -79,6 +79,13 @@
</appInfo>
</annotation>
</attribute>
+ <attribute name="obsoletes" type="string">
+ <annotation>
+ <documentation>
+ Full id of any old secure storage provider this extension obsoletes.
+ </documentation>
+ </annotation>
+ </attribute>
</complexType>
</element>
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderModuleExt.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderModuleExt.java
index 947657d..12ef11c 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderModuleExt.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderModuleExt.java
@@ -24,16 +24,22 @@
final private PasswordProvider providerModule;
final private String moduleID;
+ final private String obsoleteID;
- public PasswordProviderModuleExt(PasswordProvider module, String moduleID) {
+ public PasswordProviderModuleExt(PasswordProvider module, String moduleID, String obsoleteID) {
this.providerModule = module;
this.moduleID = moduleID;
+ this.obsoleteID = obsoleteID;
}
public String getID() {
return moduleID;
}
+ public String getObsoleteID() {
+ return obsoleteID;
+ }
+
public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType) {
return providerModule.getPassword(container, passwordType);
}
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderSelector.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderSelector.java
index 329e1b1..1971bc8 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderSelector.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderSelector.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2018 IBM Corporation and others.
+ * Copyright (c) 2008, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -38,6 +38,7 @@
final private static String STORAGE_MODULE = "provider";//$NON-NLS-1$
final private static String MODULE_PRIORITY = "priority";//$NON-NLS-1$
final private static String MODULE_DESCRIPTION = "description";//$NON-NLS-1$
+ final private static String OBSOLETES_ID = "obsoletes"; //$NON-NLS-1$
final private static String CLASS_NAME = "class";//$NON-NLS-1$
final private static String HINTS_NAME = "hint";//$NON-NLS-1$
final private static String HINT_VALUE = "value";//$NON-NLS-1$
@@ -46,16 +47,18 @@
public class ExtStorageModule {
public String moduleID;
+ public String obsoleteID;
public IConfigurationElement element;
public int priority;
public String name;
public String description;
public List<String> hints;
- public ExtStorageModule(String id, IConfigurationElement element, int priority, String name, String description, List<String> hints) {
+ public ExtStorageModule(String id, String obsoleteID, IConfigurationElement element, int priority, String name, String description, List<String> hints) {
super();
this.element = element;
this.moduleID = id;
+ this.obsoleteID = obsoleteID;
this.priority = priority;
this.name = name;
this.description = description;
@@ -99,16 +102,25 @@
if (moduleID == null) // IDs on those extensions are mandatory; if not specified, ignore the extension
continue;
moduleID = moduleID.toLowerCase();
+ boolean isFound = true;
if (expectedID != null && !expectedID.equals(moduleID))
- continue;
+ isFound = false;
IConfigurationElement[] elements = extension.getConfigurationElements();
if (elements.length == 0)
continue;
IConfigurationElement element = elements[0]; // only one module is allowed per extension
if (!STORAGE_MODULE.equals(element.getName())) {
+ if (!isFound) // don't bother issue error message if id doesn't match
+ continue;
reportError(SecAuthMessages.unexpectedConfigElement, element.getName(), element, null);
continue;
}
+ String obsoletes = element.getAttribute(OBSOLETES_ID);
+ if (!isFound) {
+ // check if old id has been replaced by newer one (Bug 573950)
+ if (obsoletes == null || !expectedID.equals(obsoletes))
+ continue;
+ }
String attribute = element.getAttribute(MODULE_PRIORITY);
int priority = -1;
if (attribute != null) {
@@ -140,7 +152,7 @@
} catch (CoreException e) {
continue;
}
- allAvailableModules.add(new ExtStorageModule(moduleID, element, priority, name, description, suppliedHints));
+ allAvailableModules.add(new ExtStorageModule(moduleID, obsoletes, element, priority, name, description, suppliedHints));
}
Collections.sort(allAvailableModules, (o1, o2) -> {
@@ -178,7 +190,7 @@
if (!(clazz instanceof PasswordProvider))
continue;
- PasswordProviderModuleExt result = new PasswordProviderModuleExt((PasswordProvider) clazz, module.moduleID);
+ PasswordProviderModuleExt result = new PasswordProviderModuleExt((PasswordProvider) clazz, module.moduleID, module.obsoleteID);
// cache the result
synchronized (modules) {
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesRoot.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesRoot.java
index 102f7c0..9d07500 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesRoot.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesRoot.java
@@ -236,6 +236,7 @@
PasswordProviderModuleExt moduleExt = PasswordProviderSelector.getInstance().findStorageModule(moduleID);
String key = moduleExt.getID();
+ String obsoleteKey = moduleExt.getObsoleteID();
PasswordExt passwordExt = null;
boolean validPassword = false;
boolean setupPasswordRecovery = false;
@@ -258,6 +259,9 @@
// is there password verification string already?
SecurePreferences node = node(PASSWORD_VERIFICATION_NODE);
+ if (obsoleteKey != null && node.hasKey(obsoleteKey)) {
+ key = obsoleteKey;
+ }
boolean newPassword = !node.hasKey(key);
int passwordType = newPassword ? PasswordProvider.CREATE_NEW_PASSWORD : 0;
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/InternalExchangeUtils.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/InternalExchangeUtils.java
index 087f704..4129d95 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/InternalExchangeUtils.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/InternalExchangeUtils.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2008, 2018 IBM Corporation and others.
+ * Copyright (c) 2008, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -60,6 +60,21 @@
}
/**
+ * Gathers list of available password providers with specified id. Note: this method does not try
+ * to instantiate providers, hence, providers listed as available by this method
+ * might fail on instantiation and not be available for the actual use.
+ * @return available password providers as described in extensions
+ */
+ static public List<PasswordProviderDescription> passwordProvidersFind(String id) {
+ List<ExtStorageModule> availableModules = PasswordProviderSelector.getInstance().findAvailableModules(id);
+ List<PasswordProviderDescription> result = new ArrayList<>(availableModules.size());
+ for (ExtStorageModule module : availableModules) {
+ result.add(new PasswordProviderDescription(module.name, module.moduleID, module.priority, module.description, module.hints));
+ }
+ return result;
+ }
+
+ /**
* Clears cached information from password providers.
*/
static public void passwordProvidersReset() {