This is an automated email from the ASF dual-hosted git repository. jsedding pushed a commit to branch issus/SLING-11741-config-plugin in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-base.git
commit 82f057e9601c8e4fd4769f6fe97a7ecdd3eca032 Author: Julian Sedding <jsedd...@apache.org> AuthorDate: Wed Sep 20 09:59:22 2023 +0200 untested - refactored to ConfigurationPlugin --- pom.xml | 31 ++- .../sling/jcr/base/internal/AllowListFragment.java | 64 +++--- .../BackwardsCompatibilityConfigurationPlugin.java | 73 +++++++ .../jcr/base/internal/ConfigurationUpdater.java | 236 --------------------- .../jcr/base/internal/LoginAdminAllowList.java | 22 +- ...kwardsCompatibilityConfigurationPluginTest.java | 164 ++++++++++++++ .../base/internal/ConfigurationUpdaterTest.java | 175 --------------- 7 files changed, 306 insertions(+), 459 deletions(-) diff --git a/pom.xml b/pom.xml index b2e04d2..a33ddae 100644 --- a/pom.xml +++ b/pom.xml @@ -146,32 +146,45 @@ <scope>test</scope> </dependency> <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>5.10.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.vintage</groupId> + <artifactId>junit-vintage-engine</artifactId> + <version>5.10.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <version>2.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.sling</groupId> - <artifactId>org.apache.sling.testing.osgi-mock</artifactId> - <version>3.3.6</version> + <artifactId>org.apache.sling.testing.osgi-mock.junit4</artifactId> + <version>3.3.8</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.sling</groupId> - <artifactId>org.apache.sling.testing.sling-mock</artifactId> - <version>3.4.4</version> + <artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId> + <version>3.4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.testing.jcr-mock</artifactId> - <version>1.6.6</version> + <version>1.6.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> - <artifactId>mockito-all</artifactId> - <version>1.9.5</version> + <artifactId>mockito-core</artifactId> + <version>4.11.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java b/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java index 3716931..f1c3203 100644 --- a/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java +++ b/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java @@ -18,9 +18,14 @@ */ package org.apache.sling.jcr.base.internal; +import org.osgi.service.cm.ConfigurationPlugin; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @@ -31,38 +36,43 @@ import java.util.Set; import static java.util.Arrays.asList; -@ObjectClassDefinition( - name = "Apache Sling Login Admin Allow List Configuration Fragment", - description = "This list of Bundle Symbolic Names is added to the list of bundles which are allowed " + - "to use Administrative Login. The full list is built in a modular way out of all such configuration fragments." -) -@interface Configuration { - - @AttributeDefinition( - name = "Name", - description = "Optional name to disambiguate configurations." - ) - String allowlist_name() default "[unnamed]"; - - @AttributeDefinition( - name = "Allow listed BSNs", - description = "A list of bundle symbolic names allowed to use loginAdministrative()." - ) - String[] allowlist_bundles(); - - @SuppressWarnings("unused") - String webconsole_configurationFactory_nameHint() default "{allowlist.name}: [{allowlist.bundles}]"; -} - @Component( - configurationPid = AllowListFragment.FACTORY_PID, - configurationPolicy = ConfigurationPolicy.REQUIRE, - service = AllowListFragment.class + service = AllowListFragment.class, + configurationPid = { + AllowListFragment.FACTORY_PID, + BackwardsCompatibilityConfigurationPlugin.WHITELIST_FRAGMENT_PID + }, + configurationPolicy = ConfigurationPolicy.REQUIRE ) -@Designate(ocd = Configuration.class, factory = true) +//@Designate(ocd = AllowListFragment.Configuration.class, factory = true) public class AllowListFragment { public static final String FACTORY_PID = "org.apache.sling.jcr.base.LoginAdminAllowList.fragment"; + + + @ObjectClassDefinition( + name = "Apache Sling Login Admin Allow List Configuration Fragment", + description = "This list of Bundle Symbolic Names is added to the list of bundles which are allowed " + + "to use Administrative Login. The full list is built in a modular way out of all such configuration fragments." + ) + public @interface Configuration { + + @AttributeDefinition( + name = "Name", + description = "Optional name to disambiguate configurations." + ) + String allowlist_name() default "[unnamed]"; + + @AttributeDefinition( + name = "Allow listed BSNs", + description = "A list of bundle symbolic names allowed to use loginAdministrative()." + ) + String[] allowlist_bundles(); + + @SuppressWarnings("unused") + String webconsole_configurationFactory_nameHint() default "{allowlist.name}: [{allowlist.bundles}]"; + } + private final String name; private final Set<String> bundles; diff --git a/src/main/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPlugin.java b/src/main/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPlugin.java new file mode 100644 index 0000000..72de01e --- /dev/null +++ b/src/main/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPlugin.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.jcr.base.internal; + +import org.osgi.framework.ServiceReference; +import org.osgi.service.cm.ConfigurationPlugin; +import org.osgi.service.component.annotations.Component; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +/** + * This class is updating configuration and configuration property names to use + * more inclusive language. + * See https://issues.apache.org/jira/browse/SLING-11741 + */ +@Component( + service = ConfigurationPlugin.class, + property = { + ConfigurationPlugin.CM_TARGET + "=" + BackwardsCompatibilityConfigurationPlugin.LOGIN_ADMIN_WHITELIST_PID, + ConfigurationPlugin.CM_TARGET + "=" + BackwardsCompatibilityConfigurationPlugin.WHITELIST_FRAGMENT_PID, + } +) +public class BackwardsCompatibilityConfigurationPlugin implements ConfigurationPlugin { + + static final String LOGIN_ADMIN_WHITELIST_PID = "org.apache.sling.jcr.base.internal.LoginAdminWhitelist"; + private static final Map<String, String> LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE = new HashMap<>(); + static { + LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.put("whitelist.bypass", "allowlist.bypass"); + LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.put("whitelist.bundles.regexp", "allowlist.bundles.regexp"); + } + + static final String WHITELIST_FRAGMENT_PID = "org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment"; + private static final Map<String, String> FRAGMENT_PROPS_TO_REPLACE = new HashMap<>(); + + static { + FRAGMENT_PROPS_TO_REPLACE.put("whitelist.name", "allowlist.name"); + FRAGMENT_PROPS_TO_REPLACE.put("whitelist.bundles", "allowlist.bundles"); + } + + @Override + public void modifyConfiguration(ServiceReference<?> serviceReference, Dictionary<String, Object> dictionary) { + FRAGMENT_PROPS_TO_REPLACE.forEach(renameKey(dictionary)); + LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.forEach(renameKey(dictionary)); + } + + private static BiConsumer<? super String, ? super String> renameKey(Dictionary<String, Object> dictionary) { + return (oldKey, newKey) -> { + final Object value = dictionary.remove(oldKey); + if (value != null && dictionary.get(newKey) == null) { + dictionary.put(newKey, value); + } + }; + } +} diff --git a/src/main/java/org/apache/sling/jcr/base/internal/ConfigurationUpdater.java b/src/main/java/org/apache/sling/jcr/base/internal/ConfigurationUpdater.java deleted file mode 100644 index 3b32ed7..0000000 --- a/src/main/java/org/apache/sling/jcr/base/internal/ConfigurationUpdater.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.sling.jcr.base.internal; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Dictionary; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; - -import org.osgi.framework.Constants; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.cm.Configuration; -import org.osgi.service.cm.ConfigurationAdmin; -import org.osgi.service.cm.ConfigurationEvent; -import org.osgi.service.cm.ConfigurationListener; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class is updating configuration and configuration property names to use - * more inclusive language. - * See https://issues.apache.org/jira/browse/SLING-11741 - */ -@Component(service = {ConfigurationListener.class, ConfigurationUpdater.class}) -public class ConfigurationUpdater implements ConfigurationListener { - - static final String LOGIN_ADMIN_WHITELIST_PID = "org.apache.sling.jcr.base.internal.LoginAdminWhitelist"; - static final String LOGIN_ADMIN_ALLOWLIST_PID = LoginAdminAllowList.PID; - private static final Map<String, String> LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE = new HashMap<>(); - static { - LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.put("whitelist.bypass", "allowlist.bypass"); - LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.put("whitelist.bundles.regexp", "allowlist.bundles.regexp"); - } - - private static final String WHITELIST_FRAGMENT_PID = "org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment"; - private static final String ALLOWLIST_FRAGMENT_PID = AllowListFragment.FACTORY_PID; - private static final Map<String, String> FRAGMENT_PROPS_TO_REPLACE = new HashMap<>(); - - static { - FRAGMENT_PROPS_TO_REPLACE.put("whitelist.name", "allowlist.name"); - FRAGMENT_PROPS_TO_REPLACE.put("whitelist.bundles", "allowlist.bundles"); - } - - protected final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final List<Updater> configurationUpdaterList = new ArrayList<>(); - - private final ConfigurationAdmin configurationAdmin; - - @Activate - public ConfigurationUpdater(@Reference ConfigurationAdmin configurationAdmin) { - this.configurationAdmin = configurationAdmin; - configurationUpdaterList.add(new PidConfigurationUpdater(LOGIN_ADMIN_WHITELIST_PID, LOGIN_ADMIN_ALLOWLIST_PID, LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE)); - configurationUpdaterList.add(new FactoryPidConfigurationUpdater(WHITELIST_FRAGMENT_PID, ALLOWLIST_FRAGMENT_PID, FRAGMENT_PROPS_TO_REPLACE)); - - configurationUpdaterList.forEach(configurationUpdater -> configurationUpdater.updateProps()); - } - - @Override - public void configurationEvent(final ConfigurationEvent event) { - if ( event.getType() == ConfigurationEvent.CM_UPDATED ) { - configurationUpdaterList.forEach(configurationUpdater -> { - configurationUpdater.updateProps(event); - }); - } - } - - /** - * Encode the value for the ldap filter: \, *, (, and ) should be escaped. - */ - private static String encode(final String value) { - return value.replace("\\", "\\\\") - .replace("*", "\\*") - .replace("(", "\\(") - .replace(")", "\\)"); - } - - protected abstract class Updater { - - protected final String oldPid; - protected final String newPid; - protected final Map<String, String> propsToReplace; - - public Updater(final String oldPid, final String newPid, final Map<String, String> propsToReplace) { - this.oldPid = oldPid; - this.newPid = newPid; - this.propsToReplace = propsToReplace; - } - - protected abstract void updateProps(ConfigurationEvent event); - - protected abstract void updateProps(); - - protected abstract Configuration createConfiguration(String oldPid) throws IOException; - - /** - * Update a configuration - */ - protected void updateProps(final Configuration sourceConfig, ConfigurationAdmin configurationAdmin) { - final Dictionary<String, Object> sourceProps = sourceConfig.getProperties(); - final Dictionary<String, Object> targetProps = new Hashtable<>(); - for(final String name : Collections.list(sourceProps.keys())) { - targetProps.put(this.propsToReplace.getOrDefault(name, name), sourceProps.get(name)); - } - try { - final Configuration cfg = this.createConfiguration(sourceConfig.getPid()); - if (cfg == null) { - logger.warn("Not updating configuration with PID {} to new configuration with PID {} as the new one already exists."+ - "Please update your configurations. See https://sling.apache.org/documentation/the-sling-engine/service-authentication.html for more information.", - sourceConfig.getPid(), this.newPid); - } else { - cfg.update(targetProps); - sourceConfig.delete(); - logger.warn("Updated configuration with PID {} to new configuration with PID {}. "+ - "Please update your configurations. See https://sling.apache.org/documentation/the-sling-engine/service-authentication.html for more information.", - sourceConfig.getPid(), cfg.getPid()); - } - } catch (final IOException e) { - logger.warn("Failed to update configuration with PID {}", sourceConfig.getPid(), e); - } - } - } - - private class PidConfigurationUpdater extends Updater { - - public PidConfigurationUpdater(final String oldPid, final String newPid, final Map<String, String> propsToReplace) { - super(oldPid, newPid, propsToReplace); - } - - @Override - protected void updateProps(final ConfigurationEvent event) { - if (this.oldPid.equals(event.getPid())) { - this.updateProps(); - } - } - - @Override - protected Configuration createConfiguration(final String oldPid) throws IOException { - final String filter = String.format("(%s=%s)", Constants.SERVICE_PID, encode(this.newPid)); - try { - final Configuration[] configs = configurationAdmin.listConfigurations(filter); - if (configs != null && configs.length > 0) { - return null; - } - } catch (final IOException | InvalidSyntaxException e) { - // ignore - } - return configurationAdmin.getConfiguration(newPid, null); - } - - @Override - protected void updateProps() { - final String filter = String.format("(%s=%s)", Constants.SERVICE_PID, encode(this.oldPid)); - try { - final Configuration[] configs = configurationAdmin.listConfigurations(filter); - if (configs != null && configs.length > 0) { - this.updateProps(configs[0], configurationAdmin); - } - } catch (final IOException | InvalidSyntaxException e) { - logger.error("Failed to retrieve configuration for PID: {}. Configuration is not updated to PID {}.", - oldPid, newPid, e); - } - } - } - - private class FactoryPidConfigurationUpdater extends Updater { - - public FactoryPidConfigurationUpdater(final String oldPid, final String newPid, final Map<String, String> propsToReplace) { - super(oldPid, newPid, propsToReplace); - } - - @Override - protected void updateProps(final ConfigurationEvent event) { - if (this.oldPid.equals(event.getFactoryPid())) { - this.updateProps(); - } - } - - @Override - protected void updateProps() { - final String filter = String.format("(%s=%s)", ConfigurationAdmin.SERVICE_FACTORYPID, encode(this.oldPid)); - try { - final Configuration[] configs = configurationAdmin.listConfigurations(filter); - if ( configs != null) { - for (final Configuration sourceConfig : configs) { - this.updateProps(sourceConfig, configurationAdmin); - } - } - } catch (final IOException | InvalidSyntaxException e) { - logger.error("Failed to list configurations for filter: {}.", filter, e); - } - } - - @Override - protected Configuration createConfiguration(final String oldFullPid) throws IOException { - final String prefix = this.oldPid.concat("~"); - if (oldFullPid.startsWith(prefix)) { - final String name = oldFullPid.substring(prefix.length()); - final String filter = String.format("(%s=%s~%s)", Constants.SERVICE_PID, encode(this.newPid), encode(name)); - try { - final Configuration[] configs = configurationAdmin.listConfigurations(filter); - if (configs != null && configs.length > 0) { - return null; - } - } catch (final IOException | InvalidSyntaxException e) { - // ignore - } - return configurationAdmin.getFactoryConfiguration(newPid, name, null); - } - return configurationAdmin.createFactoryConfiguration(newPid, null); - } - } -} diff --git a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java index f3aaa7b..b1e86c5 100644 --- a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java +++ b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java @@ -26,6 +26,7 @@ import java.util.regex.Pattern; import org.apache.sling.jcr.api.SlingRepository; import org.osgi.framework.Bundle; +import org.osgi.service.cm.ConfigurationPlugin; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Modified; @@ -47,20 +48,17 @@ import static org.apache.sling.commons.osgi.PropertiesUtil.toStringArray; * use the loginAdministrative method. */ @Component(service = LoginAdminAllowList.class, - configurationPid = LoginAdminAllowList.PID, - reference = { - // ConfigurationUpdater is a required dependency to make sure that configurations are - // updated before this component is activated - @Reference( - name = "ConfigurationUpdater", - service = ConfigurationUpdater.class, + configurationPid = { LoginAdminAllowList.PID, BackwardsCompatibilityConfigurationPlugin.LOGIN_ADMIN_WHITELIST_PID }, + // BackwardsCompatibilityConfigurationPlugin is a required dependency to make + // sure that configurations are adjusted when this component is activated + reference = @Reference( + name = "backwardsCompatibilityConfigurationPlugin", + service = ConfigurationPlugin.class, + target = "(component.name=org.apache.sling.jcr.base.internal.BackwardsCompatibilityConfigurationPlugin)", cardinality = ReferenceCardinality.MANDATORY - ) - } -) -@Designate( - ocd = LoginAdminAllowListConfiguration.class + ) ) +// @Designate(ocd = LoginAdminAllowListConfiguration.class) public class LoginAdminAllowList { public static final String PID = "org.apache.sling.jcr.base.LoginAdminAllowList"; diff --git a/src/test/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPluginTest.java b/src/test/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPluginTest.java new file mode 100644 index 0000000..ea8914f --- /dev/null +++ b/src/test/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPluginTest.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.jcr.base.internal; + +import java.io.IOException; +import java.util.Dictionary; +import java.util.Hashtable; + +import org.apache.felix.utils.collections.DictionaryAsMap; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class BackwardsCompatibilityConfigurationPluginTest { + + private final BackwardsCompatibilityConfigurationPlugin plugin = + new BackwardsCompatibilityConfigurationPlugin(); + + @Test + public void whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured() { + + final Dictionary<String, Object> properties = new Hashtable<>(); + properties.put("whitelist.bypass", "whitelistBypassValue"); + properties.put("whitelist.bundles.regexp", "whitelistBundlesValue"); + properties.put("prop", "value"); + + plugin.modifyConfiguration(mock(ServiceReference.class), properties); + + assertThat(new DictionaryAsMap<>(properties), Matchers.allOf( + Matchers.hasEntry("allowlist.bypass", "whitelistBypassValue"), + Matchers.hasEntry("allowlist.bundles.regexp", "whitelistBundlesValue"), + Matchers.hasEntry("prop", "value"), + Matchers.not(Matchers.hasKey("whitelist.bypass")), + Matchers.not(Matchers.hasKey("whitelist.bundles.regexp")) + )); + } + + @Test + public void testUpdateProps_whenNewConfigExists() { + final Dictionary<String, Object> properties = new Hashtable<>(); + properties.put("allowlist.bypass", "allowListBypassValue"); + properties.put("allowlist.bundles.regexp", "allowListBundlesValue"); + properties.put("prop", "value"); + + plugin.modifyConfiguration(mock(ServiceReference.class), properties); + + assertThat(new DictionaryAsMap<>(properties), Matchers.allOf( + Matchers.hasEntry("allowlist.bypass", "allowListBypassValue"), + Matchers.hasEntry("allowlist.bundles.regexp", "allowListBundlesValue"), + Matchers.hasEntry("prop", "value"), + Matchers.not(Matchers.hasKey("whitelist.bypass")), + Matchers.not(Matchers.hasKey("whitelist.bundles.regexp")) + )); + } + + + @Test + public void preferNewConfigWhenOldAndNewConfigExists() { + final Dictionary<String, Object> properties = new Hashtable<>(); + properties.put("allowlist.bypass", "allowListBypassValue"); + properties.put("allowlist.bundles.regexp", "allowListBundlesValue"); + properties.put("whitelist.bypass", "whitelistBypassValue"); + properties.put("whitelist.bundles.regexp", "whitelistBundlesValue"); + properties.put("prop", "value"); + + plugin.modifyConfiguration(mock(ServiceReference.class), properties); + + assertThat(new DictionaryAsMap<>(properties), Matchers.allOf( + Matchers.hasEntry("allowlist.bypass", "allowListBypassValue"), + Matchers.hasEntry("allowlist.bundles.regexp", "allowListBundlesValue"), + Matchers.hasEntry("prop", "value"), + Matchers.not(Matchers.hasKey("whitelist.bypass")), + Matchers.not(Matchers.hasKey("whitelist.bundles.regexp")) + )); + } + + @Test + public void mergePartialConfigs() { + final Dictionary<String, Object> properties = new Hashtable<>(); + properties.put("allowlist.bypass", "allowListBypassValue"); + properties.put("whitelist.bypass", "whitelistBypassValue"); + properties.put("whitelist.bundles.regexp", "whitelistBundlesValue"); + properties.put("prop", "value"); + + plugin.modifyConfiguration(mock(ServiceReference.class), properties); + + assertThat(new DictionaryAsMap<>(properties), Matchers.allOf( + Matchers.hasEntry("allowlist.bypass", "allowListBypassValue"), + Matchers.hasEntry("allowlist.bundles.regexp", "whitelistBundlesValue"), + Matchers.hasEntry("prop", "value"), + Matchers.not(Matchers.hasKey("whitelist.bypass")), + Matchers.not(Matchers.hasKey("whitelist.bundles.regexp")) + )); + } + + + @Test + public void testUpdatePropsForNamedFactoryPid_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured() throws InvalidSyntaxException, IOException { + final ConfigurationAdmin mockConfigurationAdmin = mock(ConfigurationAdmin.class); + final Configuration mockSourceConfiguration = mock(Configuration.class); + final Configuration mockTargetConfiguration = mock(Configuration.class); + final Dictionary<String, Object> sourceProperties = new Hashtable<>(); + sourceProperties.put("whitelist.name", "whitelistNameValue"); + sourceProperties.put("whitelist.bundles", "whitelistBundleValue"); + sourceProperties.put("prop", "value"); + when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties); + when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment"); + when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~foo"); + when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new Configuration[] {mockSourceConfiguration}); + when(mockConfigurationAdmin.getFactoryConfiguration(AllowListFragment.FACTORY_PID, "foo", null)).thenReturn(mockTargetConfiguration); + + final Dictionary<String, Object> expectedProperties = new Hashtable<>(); + expectedProperties.put("allowlist.name", "whitelistNameValue"); + expectedProperties.put("allowlist.bundles", "whitelistBundleValue"); + expectedProperties.put("prop", "value"); + + } + + @Test + public void testUpdatePropsForOldFactoryPid_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured() throws InvalidSyntaxException, IOException { + final ConfigurationAdmin mockConfigurationAdmin = mock(ConfigurationAdmin.class); + final Configuration mockSourceConfiguration = mock(Configuration.class); + final Configuration mockTargetConfiguration = mock(Configuration.class); + final Dictionary<String, Object> sourceProperties = new Hashtable<>(); + sourceProperties.put("whitelist.name", "whitelistNameValue"); + sourceProperties.put("whitelist.bundles", "whitelistBundleValue"); + sourceProperties.put("prop", "value"); + when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties); + when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment"); + when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment-randomid"); + when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new Configuration[] {mockSourceConfiguration}); + when(mockConfigurationAdmin.createFactoryConfiguration(AllowListFragment.FACTORY_PID, null)).thenReturn(mockTargetConfiguration); + + final Dictionary<String, Object> expectedProperties = new Hashtable<>(); + expectedProperties.put("allowlist.name", "whitelistNameValue"); + expectedProperties.put("allowlist.bundles", "whitelistBundleValue"); + expectedProperties.put("prop", "value"); + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/jcr/base/internal/ConfigurationUpdaterTest.java b/src/test/java/org/apache/sling/jcr/base/internal/ConfigurationUpdaterTest.java deleted file mode 100644 index 047501a..0000000 --- a/src/test/java/org/apache/sling/jcr/base/internal/ConfigurationUpdaterTest.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.sling.jcr.base.internal; - -import java.io.IOException; -import java.util.Dictionary; -import java.util.Hashtable; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.runners.MockitoJUnitRunner; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.service.cm.Configuration; -import org.osgi.service.cm.ConfigurationAdmin; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class ConfigurationUpdaterTest { - - @Test - public void testUpdateProps_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured() throws IOException, InvalidSyntaxException { - final ConfigurationAdmin mockConfigurationAdmin = mock(ConfigurationAdmin.class); - final Configuration mockSourceConfiguration = mock(Configuration.class); - final Configuration mockTargetConfiguration = mock(Configuration.class); - final Dictionary<String, Object> sourceProperties = new Hashtable<>(); - sourceProperties.put("whitelist.bypass", "whitelistNameValue"); - sourceProperties.put("whitelist.bundles.regexp", "whitelistBundleValue"); - sourceProperties.put("prop", "value"); - when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties); - - when(mockConfigurationAdmin.listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)")).thenReturn(new Configuration[] {mockSourceConfiguration}); - when(mockConfigurationAdmin.getConfiguration(LoginAdminAllowList.PID, null)).thenReturn(mockTargetConfiguration); - - final Dictionary<String, Object> expectedProperties = new Hashtable<>(); - expectedProperties.put("allowlist.bypass", "whitelistNameValue"); - expectedProperties.put("allowlist.bundles.regexp", "whitelistBundleValue"); - expectedProperties.put("prop", "value"); - - new ConfigurationUpdater(mockConfigurationAdmin); - - verify(mockConfigurationAdmin).listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)"); - verify(mockConfigurationAdmin).getConfiguration(LoginAdminAllowList.PID, null); - final ArgumentCaptor<Dictionary> targetPropertiesCaptor = ArgumentCaptor.forClass(Dictionary.class); - verify(mockTargetConfiguration).update(targetPropertiesCaptor.capture()); - assertEquals(expectedProperties, targetPropertiesCaptor.getValue()); - verify(mockSourceConfiguration).delete(); - } - - @Test - public void testUpdateProps_whenNewConfigExists() throws IOException, InvalidSyntaxException { - final ConfigurationAdmin mockConfigurationAdmin = mock(ConfigurationAdmin.class); - final Configuration mockSourceConfiguration = mock(Configuration.class); - final Configuration mockTargetConfiguration = mock(Configuration.class); - final Dictionary<String, Object> sourceProperties = new Hashtable<>(); - sourceProperties.put("whitelist.bypass", "whitelistNameValue"); - sourceProperties.put("whitelist.bundles.regexp", "whitelistBundleValue"); - sourceProperties.put("prop", "value"); - when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties); - - when(mockConfigurationAdmin.listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)")).thenReturn(new Configuration[] {mockSourceConfiguration}); - when(mockConfigurationAdmin.listConfigurations("(service.pid=" + LoginAdminAllowList.PID + ")")).thenReturn(new Configuration[] {mockTargetConfiguration}); - - new ConfigurationUpdater(mockConfigurationAdmin); - - verify(mockConfigurationAdmin).listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)"); - verify(mockConfigurationAdmin).listConfigurations("(service.pid=" + LoginAdminAllowList.PID + ")"); - verify(mockConfigurationAdmin).listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)"); - verifyNoMoreInteractions(mockConfigurationAdmin); - } - - @Test - public void testUpdatePropsForNamedFactoryPid_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured() throws InvalidSyntaxException, IOException { - final ConfigurationAdmin mockConfigurationAdmin = mock(ConfigurationAdmin.class); - final Configuration mockSourceConfiguration = mock(Configuration.class); - final Configuration mockTargetConfiguration = mock(Configuration.class); - final Dictionary<String, Object> sourceProperties = new Hashtable<>(); - sourceProperties.put("whitelist.name", "whitelistNameValue"); - sourceProperties.put("whitelist.bundles", "whitelistBundleValue"); - sourceProperties.put("prop", "value"); - when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties); - when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment"); - when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~foo"); - when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new Configuration[] {mockSourceConfiguration}); - when(mockConfigurationAdmin.getFactoryConfiguration(AllowListFragment.FACTORY_PID, "foo", null)).thenReturn(mockTargetConfiguration); - - final Dictionary<String, Object> expectedProperties = new Hashtable<>(); - expectedProperties.put("allowlist.name", "whitelistNameValue"); - expectedProperties.put("allowlist.bundles", "whitelistBundleValue"); - expectedProperties.put("prop", "value"); - - new ConfigurationUpdater(mockConfigurationAdmin); - - verify(mockConfigurationAdmin).listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)"); - verify(mockConfigurationAdmin).getFactoryConfiguration(AllowListFragment.FACTORY_PID, "foo", null); - final ArgumentCaptor<Dictionary> targetPropertiesCaptor = ArgumentCaptor.forClass(Dictionary.class); - verify(mockTargetConfiguration).update(targetPropertiesCaptor.capture()); - assertEquals(expectedProperties, targetPropertiesCaptor.getValue()); - verify(mockSourceConfiguration).delete(); - } - - @Test - public void testUpdateNamedFactoryConfig_whenNewConfigExists() throws IOException, InvalidSyntaxException { - final ConfigurationAdmin mockConfigurationAdmin = mock(ConfigurationAdmin.class); - final Configuration mockSourceConfiguration = mock(Configuration.class); - final Configuration mockTargetConfiguration = mock(Configuration.class); - final Dictionary<String, Object> sourceProperties = new Hashtable<>(); - sourceProperties.put("whitelist.bypass", "whitelistNameValue"); - sourceProperties.put("whitelist.bundles.regexp", "whitelistBundleValue"); - sourceProperties.put("prop", "value"); - when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties); - when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment"); - when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~foo"); - - when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new Configuration[] {mockSourceConfiguration}); - when(mockConfigurationAdmin.listConfigurations("(service.pid=" + AllowListFragment.FACTORY_PID + "~foo)")).thenReturn(new Configuration[] {mockTargetConfiguration}); - - new ConfigurationUpdater(mockConfigurationAdmin); - - verify(mockConfigurationAdmin).listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)"); - verify(mockConfigurationAdmin).listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)"); - verify(mockConfigurationAdmin).listConfigurations("(service.pid=" + AllowListFragment.FACTORY_PID + "~foo)"); - verifyNoMoreInteractions(mockConfigurationAdmin); - } - - @Test - public void testUpdatePropsForOldFactoryPid_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured() throws InvalidSyntaxException, IOException { - final ConfigurationAdmin mockConfigurationAdmin = mock(ConfigurationAdmin.class); - final Configuration mockSourceConfiguration = mock(Configuration.class); - final Configuration mockTargetConfiguration = mock(Configuration.class); - final Dictionary<String, Object> sourceProperties = new Hashtable<>(); - sourceProperties.put("whitelist.name", "whitelistNameValue"); - sourceProperties.put("whitelist.bundles", "whitelistBundleValue"); - sourceProperties.put("prop", "value"); - when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties); - when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment"); - when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment-randomid"); - when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new Configuration[] {mockSourceConfiguration}); - when(mockConfigurationAdmin.createFactoryConfiguration(AllowListFragment.FACTORY_PID, null)).thenReturn(mockTargetConfiguration); - - final Dictionary<String, Object> expectedProperties = new Hashtable<>(); - expectedProperties.put("allowlist.name", "whitelistNameValue"); - expectedProperties.put("allowlist.bundles", "whitelistBundleValue"); - expectedProperties.put("prop", "value"); - - new ConfigurationUpdater(mockConfigurationAdmin); - - verify(mockConfigurationAdmin).listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)"); - verify(mockConfigurationAdmin).createFactoryConfiguration(AllowListFragment.FACTORY_PID, null); - final ArgumentCaptor<Dictionary> targetPropertiesCaptor = ArgumentCaptor.forClass(Dictionary.class); - verify(mockTargetConfiguration).update(targetPropertiesCaptor.capture()); - assertEquals(expectedProperties, targetPropertiesCaptor.getValue()); - verify(mockSourceConfiguration).delete(); - } -} \ No newline at end of file