diff --git a/src/main/java/com/hivemq/configuration/entity/MqttConfigEntity.java b/src/main/java/com/hivemq/configuration/entity/MqttConfigEntity.java
index 46798a13d..653088f8c 100644
--- a/src/main/java/com/hivemq/configuration/entity/MqttConfigEntity.java
+++ b/src/main/java/com/hivemq/configuration/entity/MqttConfigEntity.java
@@ -15,6 +15,7 @@
*/
package com.hivemq.configuration.entity;
+import com.hivemq.configuration.entity.mqtt.AllowDollarTopicsConfigEntity;
import com.hivemq.configuration.entity.mqtt.KeepAliveConfigEntity;
import com.hivemq.configuration.entity.mqtt.MessageExpiryConfigEntity;
import com.hivemq.configuration.entity.mqtt.PacketsConfigEntity;
@@ -47,6 +48,9 @@ public class MqttConfigEntity {
@XmlElementRef(required = false)
private @NotNull RetainedMessagesConfigEntity retainedMessagesConfigEntity = new RetainedMessagesConfigEntity();
+ @XmlElementRef(required = false)
+ private @NotNull AllowDollarTopicsConfigEntity allowDollarTopicsEntity = new AllowDollarTopicsConfigEntity();
+
@XmlElementRef(required = false)
private @NotNull WildcardSubscriptionsConfigEntity wildcardSubscriptionsConfigEntity =
new WildcardSubscriptionsConfigEntity();
@@ -100,6 +104,11 @@ public class MqttConfigEntity {
return retainedMessagesConfigEntity;
}
+ public @NotNull AllowDollarTopicsConfigEntity getAllowDollarTopicConfigEntity() {
+ return allowDollarTopicsEntity;
+ }
+
+
public @NotNull WildcardSubscriptionsConfigEntity getWildcardSubscriptionsConfigEntity() {
return wildcardSubscriptionsConfigEntity;
}
diff --git a/src/main/java/com/hivemq/configuration/entity/mqtt/AllowDollarTopicsConfigEntity.java b/src/main/java/com/hivemq/configuration/entity/mqtt/AllowDollarTopicsConfigEntity.java
new file mode 100644
index 000000000..66945027e
--- /dev/null
+++ b/src/main/java/com/hivemq/configuration/entity/mqtt/AllowDollarTopicsConfigEntity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019-present HiveMQ GmbH
+ *
+ * Licensed 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 com.hivemq.configuration.entity.mqtt;
+
+import com.hivemq.configuration.entity.EnabledEntity;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Marcel Bartholet
+ * @since 2025.1
+ */
+@XmlRootElement(name = "dollar-topic-publishing")
+@XmlAccessorType(XmlAccessType.NONE)
+@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
+public class AllowDollarTopicsConfigEntity extends EnabledEntity {
+}
diff --git a/src/main/java/com/hivemq/configuration/entity/mqtt/MqttConfigurationDefaults.java b/src/main/java/com/hivemq/configuration/entity/mqtt/MqttConfigurationDefaults.java
index 38c5356a3..b91119846 100644
--- a/src/main/java/com/hivemq/configuration/entity/mqtt/MqttConfigurationDefaults.java
+++ b/src/main/java/com/hivemq/configuration/entity/mqtt/MqttConfigurationDefaults.java
@@ -33,6 +33,7 @@ public class MqttConfigurationDefaults {
public static final long MAX_EXPIRY_INTERVAL_DEFAULT = UnsignedDataTypes.UNSIGNED_INT_MAX_VALUE + 1;
public static final boolean RETAINED_MESSAGES_ENABLED_DEFAULT = true;
+ public static final boolean ALLOW_DOLLAR_TOPICS_DEFAULT = false;
public static final QoS MAXIMUM_QOS_DEFAULT = QoS.EXACTLY_ONCE;
diff --git a/src/main/java/com/hivemq/configuration/reader/MqttConfigurator.java b/src/main/java/com/hivemq/configuration/reader/MqttConfigurator.java
index da94bf3f6..472f91cdc 100644
--- a/src/main/java/com/hivemq/configuration/reader/MqttConfigurator.java
+++ b/src/main/java/com/hivemq/configuration/reader/MqttConfigurator.java
@@ -52,6 +52,9 @@ void setMqttConfig(@NotNull final MqttConfigEntity mqttConfigEntity) {
mqttConfigurationService.setRetainedMessagesEnabled(mqttConfigEntity.getRetainedMessagesConfigEntity()
.isEnabled());
+ mqttConfigurationService.setAllowDollarTopicsEnabled(mqttConfigEntity.getAllowDollarTopicConfigEntity()
+ .isEnabled());
+
mqttConfigurationService.setWildcardSubscriptionsEnabled(mqttConfigEntity.getWildcardSubscriptionsConfigEntity()
.isEnabled());
mqttConfigurationService.setSubscriptionIdentifierEnabled(mqttConfigEntity.getSubscriptionIdentifierConfigEntity()
diff --git a/src/main/java/com/hivemq/configuration/service/InternalConfigurations.java b/src/main/java/com/hivemq/configuration/service/InternalConfigurations.java
index 8f0c22ef0..2dbd89cd4 100644
--- a/src/main/java/com/hivemq/configuration/service/InternalConfigurations.java
+++ b/src/main/java/com/hivemq/configuration/service/InternalConfigurations.java
@@ -394,8 +394,6 @@ public class InternalConfigurations {
public static final AtomicInteger INTERVAL_BETWEEN_CLEANUP_JOBS_SEC = new AtomicInteger(4);
- public static final AtomicBoolean MQTT_ALLOW_DOLLAR_TOPICS = new AtomicBoolean(false);
-
public static final AtomicInteger MQTT_EVENT_EXECUTOR_THREAD_COUNT =
new AtomicInteger(AVAILABLE_PROCESSORS_TIMES_TWO);
diff --git a/src/main/java/com/hivemq/configuration/service/MqttConfigurationService.java b/src/main/java/com/hivemq/configuration/service/MqttConfigurationService.java
index 33ce1600a..d83170118 100644
--- a/src/main/java/com/hivemq/configuration/service/MqttConfigurationService.java
+++ b/src/main/java/com/hivemq/configuration/service/MqttConfigurationService.java
@@ -96,6 +96,11 @@ public int getIndex() {
*/
boolean retainedMessagesEnabled();
+ /**
+ * @return true if publishing to dollar topics is enabled, else false. Default false
+ */
+ boolean allowDollarTopicsEnabled();
+
/**
* @return true if wildcard subscriptions are enabled, else false. Default true
*/
@@ -150,6 +155,8 @@ public int getIndex() {
void setMaxMessageExpiryInterval(final long maxMessageExpiryInterval);
void setRetainedMessagesEnabled(final boolean enabled);
+
+ void setAllowDollarTopicsEnabled(final boolean enabled);
void setWildcardSubscriptionsEnabled(final boolean enabled);
diff --git a/src/main/java/com/hivemq/configuration/service/impl/MqttConfigurationServiceImpl.java b/src/main/java/com/hivemq/configuration/service/impl/MqttConfigurationServiceImpl.java
index 9a0978dc8..219758136 100644
--- a/src/main/java/com/hivemq/configuration/service/impl/MqttConfigurationServiceImpl.java
+++ b/src/main/java/com/hivemq/configuration/service/impl/MqttConfigurationServiceImpl.java
@@ -33,6 +33,7 @@
import static com.hivemq.configuration.entity.mqtt.MqttConfigurationDefaults.MAXIMUM_QOS_DEFAULT;
import static com.hivemq.configuration.entity.mqtt.MqttConfigurationDefaults.MAX_EXPIRY_INTERVAL_DEFAULT;
import static com.hivemq.configuration.entity.mqtt.MqttConfigurationDefaults.MAX_QUEUED_MESSAGES_DEFAULT;
+import static com.hivemq.configuration.entity.mqtt.MqttConfigurationDefaults.ALLOW_DOLLAR_TOPICS_DEFAULT;
import static com.hivemq.configuration.entity.mqtt.MqttConfigurationDefaults.QUEUED_MESSAGES_STRATEGY_DEFAULT;
import static com.hivemq.configuration.entity.mqtt.MqttConfigurationDefaults.RETAINED_MESSAGES_ENABLED_DEFAULT;
import static com.hivemq.configuration.entity.mqtt.MqttConfigurationDefaults.SERVER_RECEIVE_MAXIMUM_DEFAULT;
@@ -64,6 +65,8 @@ public class MqttConfigurationServiceImpl implements MqttConfigurationService {
private final AtomicBoolean retainedMessagesEnabled = new AtomicBoolean(RETAINED_MESSAGES_ENABLED_DEFAULT);
+ private final AtomicBoolean allowDollarTopics = new AtomicBoolean(ALLOW_DOLLAR_TOPICS_DEFAULT);
+
private final AtomicBoolean wildcardSubscriptionsEnabled =
new AtomicBoolean(WILDCARD_SUBSCRIPTIONS_ENABLED_DEFAULT);
@@ -112,6 +115,9 @@ public boolean retainedMessagesEnabled() {
return retainedMessagesEnabled.get();
}
+ @Override
+ public boolean allowDollarTopicsEnabled() { return allowDollarTopics.get(); }
+
@Override
public boolean wildcardSubscriptionsEnabled() {
return wildcardSubscriptionsEnabled.get();
@@ -189,6 +195,12 @@ public void setRetainedMessagesEnabled(final boolean enabled) {
this.retainedMessagesEnabled.set(enabled);
}
+ @Override
+ public void setAllowDollarTopicsEnabled(final boolean enabled) {
+ log.debug("Setting allow dollar topics enabled to {}", enabled);
+ this.allowDollarTopics.set(enabled);
+ }
+
@Override
public void setWildcardSubscriptionsEnabled(final boolean enabled) {
log.debug("Setting wildcard subscriptions enabled to {}", enabled);
diff --git a/src/main/java/com/hivemq/extensions/handler/PluginAuthorizerServiceImpl.java b/src/main/java/com/hivemq/extensions/handler/PluginAuthorizerServiceImpl.java
index 3b379293a..935df29b0 100644
--- a/src/main/java/com/hivemq/extensions/handler/PluginAuthorizerServiceImpl.java
+++ b/src/main/java/com/hivemq/extensions/handler/PluginAuthorizerServiceImpl.java
@@ -15,12 +15,22 @@
*/
package com.hivemq.extensions.handler;
+import com.codahale.metrics.MetricRegistry;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import com.hivemq.bootstrap.ClientConnection;
import com.hivemq.bootstrap.ClientConnectionContext;
+import com.hivemq.configuration.ConfigurationBootstrap;
+import com.hivemq.configuration.info.SystemInformation;
+import com.hivemq.configuration.info.SystemInformationImpl;
+import com.hivemq.configuration.ioc.ConfigurationModule;
+import com.hivemq.configuration.reader.MqttConfigurator;
+import com.hivemq.configuration.service.FullConfigurationService;
+import com.hivemq.configuration.service.MqttConfigurationService;
+import com.hivemq.configuration.service.impl.ConfigurationServiceImpl;
+import com.hivemq.configuration.service.impl.MqttConfigurationServiceImpl;
import com.hivemq.extension.sdk.api.annotations.NotNull;
import com.hivemq.extension.sdk.api.auth.parameter.AuthorizerProviderInput;
import com.hivemq.extension.sdk.api.client.parameter.ServerInformation;
@@ -63,8 +73,6 @@
import java.util.List;
import java.util.Map;
-import static com.hivemq.configuration.service.InternalConfigurations.MQTT_ALLOW_DOLLAR_TOPICS;
-
/**
* @author Florian Limpöck
* @since 4.1.0
@@ -92,7 +100,8 @@ public PluginAuthorizerServiceImpl(
final @NotNull HiveMQExtensions hiveMQExtensions,
final @NotNull MqttServerDisconnector mqttServerDisconnector,
final @NotNull IncomingPublishService incomingPublishService,
- final @NotNull IncomingSubscribeService incomingSubscribeService) {
+ final @NotNull IncomingSubscribeService incomingSubscribeService,
+ final @NotNull MqttConfigurationService mqttConfigService) {
this.authorizers = authorizers;
this.asyncer = asyncer;
@@ -102,7 +111,7 @@ public PluginAuthorizerServiceImpl(
this.mqttServerDisconnector = mqttServerDisconnector;
this.extensionPriorityComparator = new ExtensionPriorityComparator(hiveMQExtensions);
this.incomingSubscribeService = incomingSubscribeService;
- this.allowDollarTopics = MQTT_ALLOW_DOLLAR_TOPICS.get();
+ this.allowDollarTopics = mqttConfigService.allowDollarTopicsEnabled();
}
public void authorizePublish(final @NotNull ChannelHandlerContext ctx, final @NotNull PUBLISH msg) {
diff --git a/src/main/resources/config.xsd b/src/main/resources/config.xsd
index ebb038aec..208a08d8e 100644
--- a/src/main/resources/config.xsd
+++ b/src/main/resources/config.xsd
@@ -198,6 +198,17 @@
+
+
+
+
+
+ Default: false
+
+
+
+
+
diff --git a/src/test/java/com/hivemq/configuration/reader/ConfigFileReaderTest.java b/src/test/java/com/hivemq/configuration/reader/ConfigFileReaderTest.java
index ac43f22b6..b69dd6f1b 100644
--- a/src/test/java/com/hivemq/configuration/reader/ConfigFileReaderTest.java
+++ b/src/test/java/com/hivemq/configuration/reader/ConfigFileReaderTest.java
@@ -99,6 +99,8 @@ public void verify_mqtt_default_values() {
.getMaxInterval());
verify(mqttConfigurationService).setRetainedMessagesEnabled(defaultMqttValues.getRetainedMessagesConfigEntity()
.isEnabled());
+ verify(mqttConfigurationService).setAllowDollarTopicsEnabled(defaultMqttValues.getAllowDollarTopicConfigEntity()
+ .isEnabled());
verify(mqttConfigurationService).setWildcardSubscriptionsEnabled(defaultMqttValues.getWildcardSubscriptionsConfigEntity()
.isEnabled());
verify(mqttConfigurationService).setMaximumQos(QoS.valueOf(defaultMqttValues.getQoSConfigEntity().getMaxQos()));
diff --git a/src/test/java/com/hivemq/extensions/handler/PluginAuthorizerServiceImplTest.java b/src/test/java/com/hivemq/extensions/handler/PluginAuthorizerServiceImplTest.java
index acb2a39de..2fbdfb610 100644
--- a/src/test/java/com/hivemq/extensions/handler/PluginAuthorizerServiceImplTest.java
+++ b/src/test/java/com/hivemq/extensions/handler/PluginAuthorizerServiceImplTest.java
@@ -20,6 +20,7 @@
import com.hivemq.bootstrap.ClientConnection;
import com.hivemq.bootstrap.ClientConnectionContext;
import com.hivemq.common.shutdown.ShutdownHooks;
+import com.hivemq.configuration.service.MqttConfigurationService;
import com.hivemq.extension.sdk.api.annotations.NotNull;
import com.hivemq.extension.sdk.api.client.parameter.ServerInformation;
import com.hivemq.extension.sdk.api.services.auth.provider.AuthorizerProvider;
@@ -97,6 +98,7 @@ public class PluginAuthorizerServiceImplTest {
private final @NotNull EventLog eventLog = mock(EventLog.class);
private final @NotNull IncomingPublishService incomingPublishService = mock(IncomingPublishService.class);
private final @NotNull PublishFlushHandler publishFlushHandler = mock(PublishFlushHandler.class);
+ private final @NotNull MqttConfigurationService mqttConfigService = mock(MqttConfigurationService.class);
private @NotNull PluginTaskExecutor executor;
private @NotNull EmbeddedChannel channel;
@@ -139,7 +141,8 @@ public void setUp() throws Exception {
hiveMQExtensions,
mqttServerDisconnector,
incomingPublishService,
- incomingSubscribeService);
+ incomingSubscribeService,
+ mqttConfigService);
eventsHandler = new CollectUserEventsHandler<>(AuthorizeWillResultEvent.class);
channel.pipeline().addLast(eventsHandler);