diff --git a/README.adoc b/README.adoc
index 100dc23..a44babb 100644
--- a/README.adoc
+++ b/README.adoc
@@ -3,7 +3,7 @@
== HiveMQ Deny Wildcard Subscription Extension
*Extension Type*: Security
-*Version*: 4.0.0
+*Version*: 4.1.1
*License*: Apache License 2.0
@@ -21,7 +21,8 @@ This extension denies any subscription to the root wildcard topic. In general, r
No configuration needed, works out of the box.
=== First Steps
-After the extension is installed start HiveMQ. Now no client is allowed to subscribe to `#`.
+After the extension is installed start HiveMQ. Now no client is allowed to subscribe to `#`, `+/#`, `$share/group/#`
+or any other topic filter that would represent a root wildcard.
==== Need help?
diff --git a/pom.xml b/pom.xml
index 4ee3976..6f75ec3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
com.hivemq.extension
hivemq-deny-wildcard-extension
- 4.1.0
+ 4.1.1
HiveMQ Extension to deny top level wildcard subscription
2018
diff --git a/src/main/java/com/hivemq/extension/callbacks/DenyWildcardAuthorizer.java b/src/main/java/com/hivemq/extension/callbacks/DenyWildcardAuthorizer.java
index e4fc0ee..0b751c9 100644
--- a/src/main/java/com/hivemq/extension/callbacks/DenyWildcardAuthorizer.java
+++ b/src/main/java/com/hivemq/extension/callbacks/DenyWildcardAuthorizer.java
@@ -25,6 +25,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -41,7 +42,8 @@
public class DenyWildcardAuthorizer implements SubscriptionAuthorizer {
public static final DenyWildcardAuthorizer INSTANCE = new DenyWildcardAuthorizer();
- public static final String REASON_STRING = "Root wildcard subscription not supported.";
+ public static final String REASON_STRING = "Root wildcard subscriptions are not supported.";
+ private static final Pattern SHARED_SUBSCRIPTION_PATTERN = Pattern.compile("\\$share(/.*?/(.*))");
private DenyWildcardAuthorizer() {
}
@@ -52,6 +54,18 @@ private DenyWildcardAuthorizer() {
@Override
public void authorizeSubscribe(@NotNull final SubscriptionAuthorizerInput subscriptionAuthorizerInput, @NotNull final SubscriptionAuthorizerOutput subscriptionAuthorizerOutput) {
final String topicFilter = subscriptionAuthorizerInput.getSubscription().getTopicFilter();
+
+ if (topicFilter.startsWith("$share/")) {
+ final Matcher matcher = SHARED_SUBSCRIPTION_PATTERN.matcher(topicFilter);
+ if (matcher.matches()) {
+ final String subscriptionTopic = matcher.group(2);
+ if (StringUtils.containsOnly(subscriptionTopic, WILDCARD_CHARS)) {
+ logger.debug("Client {} tried to subscribe to an denied shared root wildcard topic filter '{}'", subscriptionAuthorizerInput.getClientInformation().getClientId(), topicFilter);
+ subscriptionAuthorizerOutput.failAuthorization(SubackReasonCode.NOT_AUTHORIZED, REASON_STRING);
+ }
+ }
+ }
+
if (StringUtils.containsOnly(topicFilter, WILDCARD_CHARS)) {
logger.debug("Client {} tried to subscribe to an denied root wildcard topic filter '{}'", subscriptionAuthorizerInput.getClientInformation().getClientId(), topicFilter);
subscriptionAuthorizerOutput.failAuthorization(SubackReasonCode.NOT_AUTHORIZED, REASON_STRING);
diff --git a/src/test/java/com/hivemq/extension/callbacks/DenyWildcardAuthorizerTest.java b/src/test/java/com/hivemq/extension/callbacks/DenyWildcardAuthorizerTest.java
index 486c1d7..29fcb76 100644
--- a/src/test/java/com/hivemq/extension/callbacks/DenyWildcardAuthorizerTest.java
+++ b/src/test/java/com/hivemq/extension/callbacks/DenyWildcardAuthorizerTest.java
@@ -63,61 +63,62 @@ public void test_denied_hashtag() {
}
@Test
- public void test_denied_plus() {
- when(input.getSubscription().getTopicFilter()).thenReturn("+");
+ public void test_denied_shared_hash() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("$share/group/#");
DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
verify(output).failAuthorization(SubackReasonCode.NOT_AUTHORIZED, DenyWildcardAuthorizer.REASON_STRING);
}
@Test
- public void test_denied_plus_slash() {
- when(input.getSubscription().getTopicFilter()).thenReturn("+/");
+ public void test_denied_hash() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("/#");
DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
verify(output).failAuthorization(SubackReasonCode.NOT_AUTHORIZED, DenyWildcardAuthorizer.REASON_STRING);
}
@Test
- public void test_denied_hashtag_slash() {
- when(input.getSubscription().getTopicFilter()).thenReturn("#/");
+ public void test_denied_shared_slash_hash() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("$share/group//#");
DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
verify(output).failAuthorization(SubackReasonCode.NOT_AUTHORIZED, DenyWildcardAuthorizer.REASON_STRING);
}
@Test
- public void test_denied_hashtag_plus() {
- when(input.getSubscription().getTopicFilter()).thenReturn("#/+");
+ public void test_denied_plus_hash() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("+/#");
DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
verify(output).failAuthorization(SubackReasonCode.NOT_AUTHORIZED, DenyWildcardAuthorizer.REASON_STRING);
}
@Test
- public void test_denied_plus_hashtag() {
- when(input.getSubscription().getTopicFilter()).thenReturn("+/#");
+ public void test_denied_shared_plus_hash() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("$share/group/+/#");
DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
verify(output).failAuthorization(SubackReasonCode.NOT_AUTHORIZED, DenyWildcardAuthorizer.REASON_STRING);
}
@Test
- public void test_denied_slash_plus() {
- when(input.getSubscription().getTopicFilter()).thenReturn("/+");
+ public void test_denied_plus_plus() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("+/+");
DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
verify(output).failAuthorization(SubackReasonCode.NOT_AUTHORIZED, DenyWildcardAuthorizer.REASON_STRING);
}
@Test
- public void test_denied_slash_hashtag() {
- when(input.getSubscription().getTopicFilter()).thenReturn("/#");
+ public void test_denied_shared_plus_plus() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("$share/group/+/+");
DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
verify(output).failAuthorization(SubackReasonCode.NOT_AUTHORIZED, DenyWildcardAuthorizer.REASON_STRING);
}
+
@Test
public void test_success() {
when(input.getSubscription().getTopicFilter()).thenReturn("topic");
@@ -134,6 +135,30 @@ public void test_success_non_root_hashtag() {
verify(output).authorizeSuccessfully();
}
+ @Test
+ public void test_success_shared_non_root_wildcard() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("$share/group/topic/#");
+ DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
+
+ verify(output).authorizeSuccessfully();
+ }
+
+ @Test
+ public void test_success_non_root_plus_wildcard() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("+/topic/#");
+ DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
+
+ verify(output).authorizeSuccessfully();
+ }
+
+ @Test
+ public void test_success_shared_non_root_plus_wildcard() {
+ when(input.getSubscription().getTopicFilter()).thenReturn("$share/group/+/topic/#");
+ DenyWildcardAuthorizer.INSTANCE.authorizeSubscribe(input, output);
+
+ verify(output).authorizeSuccessfully();
+ }
+
@Test
public void test_success_non_root_plus() {
when(input.getSubscription().getTopicFilter()).thenReturn("topic/+");