Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public final class ManagementHelper {

public static final SimpleString HDR_CERT_SUBJECT_DN = SimpleString.of("_AMQ_CertSubjectDN");

public static final SimpleString HDR_CERT_UPN = SimpleString.of("_AMQ_CertUPN");

public static final SimpleString HDR_CHECK_TYPE = SimpleString.of("_AMQ_CheckType");

public static final SimpleString HDR_SESSION_NAME = SimpleString.of("_AMQ_SessionName");
Expand Down
5 changes: 5 additions & 0 deletions artemis-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@
<artifactId>mockserver-client-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.File;
import java.net.URL;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
Expand All @@ -43,6 +44,7 @@
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerQueuePlugin;
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerResourcePlugin;
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin;
import org.apache.activemq.artemis.core.settings.impl.AuthenticationCacheKeyConfig;
import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration;
import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration;
Expand Down Expand Up @@ -1565,4 +1567,8 @@ default boolean isUsingDatabasePersistence() {
void setFederationDownstreamAuthorization(List<String> roles);

Configuration addFederationDownstreamAuthorization(String role);

Configuration setAuthenticationCacheKey(EnumSet<AuthenticationCacheKeyConfig> authenticationCacheKey);

EnumSet<AuthenticationCacheKeyConfig> getAuthenticationCacheKey();
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@
import java.io.StringWriter;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -132,6 +135,7 @@
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerResourcePlugin;
import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.core.settings.impl.AuthenticationCacheKeyConfig;
import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
import org.apache.activemq.artemis.json.JsonArrayBuilder;
import org.apache.activemq.artemis.json.JsonObject;
Expand Down Expand Up @@ -172,6 +176,8 @@ public class ConfigurationImpl extends javax.security.auth.login.Configuration i

public static final JournalType DEFAULT_JOURNAL_TYPE = JournalType.ASYNCIO;

public static final EnumSet<AuthenticationCacheKeyConfig> DEFAULT_AUTHENTICATION_CACHE_KEY = EnumSet.of(AuthenticationCacheKeyConfig.USER, AuthenticationCacheKeyConfig.PASS, AuthenticationCacheKeyConfig.TLS_SUBJECT_DN);

public static final String PROPERTY_CLASS_SUFFIX = ".class";

public static final String REDACTED = "**redacted**";
Expand Down Expand Up @@ -491,6 +497,8 @@ public class ConfigurationImpl extends javax.security.auth.login.Configuration i

private Map<String, JaasAppConfiguration> jaasConfigs = new ConcurrentHashMap<>();

private EnumSet<AuthenticationCacheKeyConfig> authenticationCacheKey = EnumSet.copyOf(DEFAULT_AUTHENTICATION_CACHE_KEY);

/**
* Parent folder for all data folders.
*/
Expand Down Expand Up @@ -646,7 +654,7 @@ public void parsePrefixedProperties(Properties properties, String prefix) throws

@Override
public void parsePrefixedProperties(Object target, String name, Properties properties, String prefix) throws Exception {
Map<String, Object> beanProperties = new LinkedHashMap<>();
Map<String, String> beanProperties = new LinkedHashMap<>();
final Checksum checksum = new Adler32();
synchronized (properties) {
String key = null;
Expand Down Expand Up @@ -706,7 +714,7 @@ public AppConfigurationEntry[] getAppConfigurationEntry(String realm) {
}
}

public void populateWithProperties(final Object target, final String propsId, Map<String, Object> beanProperties) throws InvocationTargetException, IllegalAccessException {
public void populateWithProperties(final Object target, final String propsId, Map<String, String> beanProperties) throws InvocationTargetException, IllegalAccessException {
CollectionAutoFillPropertiesUtil autoFillCollections = new CollectionAutoFillPropertiesUtil(getBrokerPropertiesRemoveValue(beanProperties));
BeanUtilsBean beanUtils = new BeanUtilsBean(new ConvertUtilsBean(), autoFillCollections) {

Expand Down Expand Up @@ -1004,15 +1012,17 @@ public <T> T convert(Class<T> type, Object value) {

Map<String, String> errors = new LinkedHashMap<>();
// Loop through the property name/value pairs to be set
for (final Map.Entry<String, ? extends Object> entry : beanProperties.entrySet()) {
for (final Map.Entry<String, String> entry : beanProperties.entrySet()) {
// Identify the property name and value(s) to be assigned
final String name = entry.getKey();
try {
if (logger.isDebugEnabled()) {
logger.debug("set property target={}, name = {}, value = {}", target.getClass(), name, entry.getValue());
}
// Perform the assignment for this property
beanUtils.setProperty(target, name, entry.getValue());
// Perform the assignment for this property with special handling for EnumSet
if (!handleEnumSet(target, name, entry.getValue())) {
beanUtils.setProperty(target, name, entry.getValue());
}
} catch (InvocationTargetException invocationTargetException) {
logger.trace("failed to populate property with key: {}", name, invocationTargetException);
Throwable toLog = invocationTargetException;
Expand All @@ -1028,6 +1038,59 @@ public <T> T convert(Class<T> type, Object value) {
updateApplyStatus(propsId, errors);
}

/*
* Since an EnumSet relies on parameterized typing BeanUtils can't handle them directly. Therefore, we need to handle
* them manually.
*/
private boolean handleEnumSet(Object target, String name, String value) throws IllegalAccessException {
boolean result = false;
Field field = getField(target.getClass(), name);
if (field != null && EnumSet.class.isAssignableFrom(field.getType())) {
// Extract the <E> from EnumSet<E>
Class<? extends Enum> enumClass = getEnumClassFromField(field);
if (enumClass != null) {
EnumSet<?> enumSet = convertToEnumSet(enumClass, value);
field.setAccessible(true);
field.set(target, enumSet);
result = true;
}
}
return result;
}

private static Class<? extends Enum> getEnumClassFromField(Field field) {
if (field.getGenericType() instanceof ParameterizedType parameterizedType) {
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if (actualTypeArguments.length > 0 && actualTypeArguments[0] instanceof Class) {
return (Class<? extends Enum>) actualTypeArguments[0];
}
}
return null;
}

private static <E extends Enum<E>> EnumSet<E> convertToEnumSet(Class<E> enumClass, String csv) {
if (csv == null || csv.trim().isEmpty()) {
return EnumSet.noneOf(enumClass);
}

return Arrays.stream(csv.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(s -> Enum.valueOf(enumClass, s))
.collect(Collectors.toCollection(() -> EnumSet.noneOf(enumClass)));
}

private static Field getField(Class<?> clazz, String fieldName) {
while (clazz != null) {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
return null;
}

@Override
public void exportAsProperties(File file) throws Exception {
try (FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8)) {
Expand Down Expand Up @@ -1299,17 +1362,17 @@ private synchronized void updateReadPropertiesStatus(String propsId, long alder3
this.jsonStatus = JsonUtil.mergeAndUpdate(jsonStatus, jsonObjectBuilder.build());
}

private String getBrokerPropertiesKeySurround(Map<String, Object> propertiesToApply) {
private String getBrokerPropertiesKeySurround(Map<String, String> propertiesToApply) {
if (propertiesToApply.containsKey(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY)) {
return String.valueOf(propertiesToApply.remove(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY));
return propertiesToApply.remove(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY);
} else {
return System.getProperty(getSystemPropertyPrefix() + ActiveMQDefaultConfiguration.BROKER_PROPERTIES_KEY_SURROUND_PROPERTY, getBrokerPropertiesKeySurround());
}
}

private String getBrokerPropertiesRemoveValue(Map<String, Object> propertiesToApply) {
private String getBrokerPropertiesRemoveValue(Map<String, String> propertiesToApply) {
if (propertiesToApply.containsKey(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY)) {
return String.valueOf(propertiesToApply.remove(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY));
return propertiesToApply.remove(ActiveMQDefaultConfiguration.BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY);
} else {
return System.getProperty(getSystemPropertyPrefix() + ActiveMQDefaultConfiguration.BROKER_PROPERTIES_REMOVE_VALUE_PROPERTY, getBrokerPropertiesRemoveValue());
}
Expand Down Expand Up @@ -3576,6 +3639,17 @@ public Configuration addFederationDownstreamAuthorization(String role) {
return this;
}

@Override
public Configuration setAuthenticationCacheKey(EnumSet<AuthenticationCacheKeyConfig> authenticationCacheKey) {
this.authenticationCacheKey = authenticationCacheKey;
return this;
}

@Override
public EnumSet<AuthenticationCacheKeyConfig> getAuthenticationCacheKey() {
return authenticationCacheKey;
}

// extend property utils with ability to auto-fill and locate from collections
// collection entries are identified by the name() property
private static class CollectionAutoFillPropertiesUtil extends PropertyUtilsBean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -106,6 +107,7 @@
import org.apache.activemq.artemis.core.server.routing.policies.PolicyFactoryResolver;
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.core.settings.impl.AuthenticationCacheKeyConfig;
import org.apache.activemq.artemis.core.settings.impl.DeletionPolicy;
import org.apache.activemq.artemis.core.settings.impl.DiskFullMessagePolicy;
import org.apache.activemq.artemis.core.settings.impl.PageFullMessagePolicy;
Expand Down Expand Up @@ -399,6 +401,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {

private static final String MQTT_SUBSCRIPTION_PERSISTENCE_ENABLED = "mqtt-subscription-persistence-enabled";

private static final String AUTHENTICATION_CACHE_KEY = "authentication-cache-key";

private boolean validateAIO = false;

private boolean printPageMaxSizeUsed = false;
Expand Down Expand Up @@ -516,6 +520,8 @@ public void parseMainConfig(final Element e, final Configuration config) throws

config.setMqttSubscriptionPersistenceEnabled(getBoolean(e, MQTT_SUBSCRIPTION_PERSISTENCE_ENABLED, config.isMqttSubscriptionPersistenceEnabled()));

parseAuthenticationCacheKey(e, config);

config.setGlobalMaxSizePercentOfJvmMaxMemory(getInteger(e, GLOBAL_MAX_SIZE_PERCENT_JVM_MAX_MEM, config.getGlobalMaxSizePercentOfJvmMaxMemory(), GT_ZERO));

long globalMaxSize = getTextBytesAsLongBytes(e, GLOBAL_MAX_SIZE, -1, MINUS_ONE_OR_GT_ZERO);
Expand Down Expand Up @@ -949,6 +955,26 @@ public void parseMainConfig(final Element e, final Configuration config) throws
}
}

private static void parseAuthenticationCacheKey(Element e, Configuration config) {
NodeList authenticationCachKeyNodes = e.getElementsByTagName(AUTHENTICATION_CACHE_KEY);

EnumSet<AuthenticationCacheKeyConfig> authenticationCachKey = EnumSet.noneOf(AuthenticationCacheKeyConfig.class);

if (authenticationCachKeyNodes.getLength() > 0) {
NodeList parts = authenticationCachKeyNodes.item(0).getChildNodes();

for (int i = 0; i < parts.getLength(); i++) {
if ("part".equalsIgnoreCase(parts.item(i).getNodeName())) {
String part = getTrimmedTextContent(parts.item(i));
authenticationCachKey.add(AuthenticationCacheKeyConfig.valueOf(part));
}
}
} else {
authenticationCachKey = ConfigurationImpl.DEFAULT_AUTHENTICATION_CACHE_KEY;
}
config.setAuthenticationCacheKey(authenticationCachKey);
}

private void parseLockCoordinator(final Element lockCoordinatorElement, final Configuration mainConfig) throws Exception {
String name = lockCoordinatorElement.getAttribute("name");
String lockId = getString(lockCoordinatorElement, "lock-id", name, NO_CHECK);
Expand Down
Loading