Fixed issue #415 by adding a configuration flag to force subscription re-authentication on client connect
This commit is contained in:
@@ -46,6 +46,7 @@ public final class BrokerConstants {
|
||||
public static final String KEY_STORE_PASSWORD_PROPERTY_NAME = "key_store_password";
|
||||
public static final String KEY_MANAGER_PASSWORD_PROPERTY_NAME = "key_manager_password";
|
||||
public static final String ALLOW_ANONYMOUS_PROPERTY_NAME = "allow_anonymous";
|
||||
public static final String REAUTHORIZE_SUBSCRIPTIONS_ON_CONNECT = "reauthorize_subscriptions_on_connect";
|
||||
public static final String ALLOW_ZERO_BYTE_CLIENT_ID_PROPERTY_NAME = "allow_zero_byte_client_id";
|
||||
public static final String ACL_FILE_PROPERTY_NAME = "acl_file";
|
||||
public static final String AUTHORIZATOR_CLASS_NAME = "authorizator_class";
|
||||
|
||||
@@ -17,11 +17,15 @@
|
||||
package io.moquette.server.netty;
|
||||
|
||||
import io.moquette.spi.impl.ProtocolProcessor;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.codec.mqtt.*;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -92,7 +96,14 @@ public class NettyMQTTHandler extends ChannelInboundHandlerAdapter {
|
||||
break;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
ctx.fireExceptionCaught(ex);
|
||||
//ctx.fireExceptionCaught(ex);
|
||||
LOG.error("Error processing protocol message: {}", messageType, ex);
|
||||
ctx.channel().close().addListener(new ChannelFutureListener() {
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) {
|
||||
LOG.info("Closed client channel due to exception in processing");
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
ReferenceCountUtil.release(msg);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package io.moquette.spi.impl;
|
||||
import io.moquette.connections.IConnectionsManager;
|
||||
import io.moquette.interception.InterceptHandler;
|
||||
import io.moquette.interception.messages.InterceptAcknowledgedMessage;
|
||||
import io.moquette.persistence.MemorySessionStore;
|
||||
import io.moquette.server.ConnectionDescriptor;
|
||||
import io.moquette.server.ConnectionDescriptorStore;
|
||||
import io.moquette.server.netty.AutoFlushHandler;
|
||||
@@ -104,9 +105,9 @@ public class ProtocolProcessor {
|
||||
private ConcurrentMap<RunningSubscription, SubscriptionState> subscriptionInCourse;
|
||||
|
||||
private ISubscriptionsDirectory subscriptions;
|
||||
// private ISubscriptionsStore subscriptionStore;
|
||||
private boolean allowAnonymous;
|
||||
private boolean allowZeroByteClientId;
|
||||
private boolean reauthorizeSubscriptionsOnConnect;
|
||||
private IAuthorizator m_authorizator;
|
||||
|
||||
private IMessagesStore m_messagesStore;
|
||||
@@ -131,16 +132,19 @@ public class ProtocolProcessor {
|
||||
|
||||
public void init(ISubscriptionsDirectory subscriptions, IMessagesStore storageService, ISessionsStore sessionsStore,
|
||||
IAuthenticator authenticator, boolean allowAnonymous, IAuthorizator authorizator,
|
||||
BrokerInterceptor interceptor, SessionsRepository sessionsRepository) {
|
||||
BrokerInterceptor interceptor, SessionsRepository sessionsRepository,
|
||||
boolean reauthorizeSubscriptionsOnConnect) {
|
||||
init(subscriptions, storageService, sessionsStore, authenticator, allowAnonymous, false,
|
||||
authorizator, interceptor, sessionsRepository);
|
||||
authorizator, interceptor, sessionsRepository, reauthorizeSubscriptionsOnConnect);
|
||||
}
|
||||
|
||||
public void init(ISubscriptionsDirectory subscriptions, IMessagesStore storageService, ISessionsStore sessionsStore,
|
||||
IAuthenticator authenticator, boolean allowAnonymous, boolean allowZeroByteClientId,
|
||||
IAuthorizator authorizator, BrokerInterceptor interceptor, SessionsRepository sessionsRepository) {
|
||||
IAuthorizator authorizator, BrokerInterceptor interceptor, SessionsRepository sessionsRepository,
|
||||
boolean reauthorizeSubscriptionsOnConnect) {
|
||||
init(new ConnectionDescriptorStore(), subscriptions, storageService, sessionsStore,
|
||||
authenticator, allowAnonymous, allowZeroByteClientId, authorizator, interceptor, sessionsRepository);
|
||||
authenticator, allowAnonymous, allowZeroByteClientId, authorizator, interceptor, sessionsRepository,
|
||||
reauthorizeSubscriptionsOnConnect);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,13 +168,15 @@ public class ProtocolProcessor {
|
||||
void init(IConnectionsManager connectionDescriptors, ISubscriptionsDirectory subscriptions,
|
||||
IMessagesStore storageService, ISessionsStore sessionsStore, IAuthenticator authenticator,
|
||||
boolean allowAnonymous, boolean allowZeroByteClientId, IAuthorizator authorizator,
|
||||
BrokerInterceptor interceptor, SessionsRepository sessionsRepository) {
|
||||
BrokerInterceptor interceptor, SessionsRepository sessionsRepository,
|
||||
boolean reauthorizeSubscriptionsOnConnect) {
|
||||
LOG.debug("Initializing MQTT protocol processor...");
|
||||
this.connectionDescriptors = connectionDescriptors;
|
||||
this.subscriptionInCourse = new ConcurrentHashMap<>();
|
||||
this.m_interceptor = interceptor;
|
||||
this.subscriptions = subscriptions;
|
||||
this.allowAnonymous = allowAnonymous;
|
||||
this.reauthorizeSubscriptionsOnConnect = reauthorizeSubscriptionsOnConnect;
|
||||
this.allowZeroByteClientId = allowZeroByteClientId;
|
||||
m_authorizator = authorizator;
|
||||
if (LOG.isDebugEnabled()) {
|
||||
@@ -202,7 +208,8 @@ public class ProtocolProcessor {
|
||||
public void processConnect(Channel channel, MqttConnectMessage msg) {
|
||||
MqttConnectPayload payload = msg.payload();
|
||||
String clientId = payload.clientIdentifier();
|
||||
LOG.debug("Processing CONNECT message. CId={}, username={}", clientId, payload.userName());
|
||||
final String username = payload.userName();
|
||||
LOG.debug("Processing CONNECT message. CId={}, username={}", clientId, username);
|
||||
|
||||
if (msg.variableHeader().version() != MqttVersion.MQTT_3_1.protocolLevel()
|
||||
&& msg.variableHeader().version() != MqttVersion.MQTT_3_1_1.protocolLevel()) {
|
||||
@@ -221,14 +228,14 @@ public class ProtocolProcessor {
|
||||
|
||||
channel.writeAndFlush(badId).addListener(FIRE_EXCEPTION_ON_FAILURE);
|
||||
channel.close().addListener(CLOSE_ON_FAILURE);
|
||||
LOG.error("The MQTT client ID cannot be empty. Username={}", payload.userName());
|
||||
LOG.error("The MQTT client ID cannot be empty. Username={}", username);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generating client id.
|
||||
clientId = UUID.randomUUID().toString().replace("-", "");
|
||||
LOG.info("Client has connected with a server generated identifier. CId={}, username={}", clientId,
|
||||
payload.userName());
|
||||
username);
|
||||
}
|
||||
|
||||
if (!login(channel, msg, clientId)) {
|
||||
@@ -248,6 +255,9 @@ public class ProtocolProcessor {
|
||||
|
||||
initializeKeepAliveTimeout(channel, msg, clientId);
|
||||
storeWillMessage(msg, clientId);
|
||||
if (!cleanSession && reauthorizeSubscriptionsOnConnect) {
|
||||
reauthorizeOnExistingSubscriptions(clientId, username);
|
||||
}
|
||||
if (!sendAck(descriptor, msg, clientId)) {
|
||||
channel.close().addListener(CLOSE_ON_FAILURE);
|
||||
return;
|
||||
@@ -274,7 +284,22 @@ public class ProtocolProcessor {
|
||||
channel.close().addListener(CLOSE_ON_FAILURE);
|
||||
}
|
||||
|
||||
LOG.info("Connected client <{}> with login <{}>", clientId, payload.userName());
|
||||
LOG.info("Connected client <{}> with login <{}>", clientId, username);
|
||||
}
|
||||
|
||||
private void reauthorizeOnExistingSubscriptions(String clientId, String username) {
|
||||
if (!m_sessionsStore.contains(clientId)) {
|
||||
return;
|
||||
}
|
||||
final Collection<Subscription> clientSubscriptions = m_sessionsStore.subscriptionStore()
|
||||
.listClientSubscriptions(clientId);
|
||||
for (Subscription sub : clientSubscriptions) {
|
||||
final Topic topicToReauthorize = sub.getTopicFilter();
|
||||
final boolean readAuthorized = m_authorizator.canRead(topicToReauthorize, username, clientId);
|
||||
if (!readAuthorized) {
|
||||
subscriptions.removeSubscription(topicToReauthorize, clientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupAutoFlusher(Channel channel, int flushIntervalMs) {
|
||||
@@ -634,6 +659,9 @@ public class ProtocolProcessor {
|
||||
LOG.trace("Removing saved subscriptions. CId={}", descriptor.clientID);
|
||||
final ClientSession session = this.sessionsRepository.sessionForClient(clientID);
|
||||
session.wipeSubscriptions();
|
||||
for (Subscription existingSub : session.getSubscriptions()) {
|
||||
this.subscriptions.removeSubscription(existingSub.getTopicFilter(), clientID);
|
||||
}
|
||||
LOG.trace("Saved subscriptions have been removed. CId={}", descriptor.clientID);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -168,13 +168,12 @@ public class ProtocolProcessorBootstrapper {
|
||||
connectionDescriptors = new ConnectionDescriptorStore();
|
||||
|
||||
LOG.info("Initializing MQTT protocol processor...");
|
||||
boolean allowAnonymous = Boolean
|
||||
.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ANONYMOUS_PROPERTY_NAME, "true"));
|
||||
boolean allowZeroByteClientId = Boolean
|
||||
.parseBoolean(props.getProperty(BrokerConstants.ALLOW_ZERO_BYTE_CLIENT_ID_PROPERTY_NAME, "false"));
|
||||
boolean allowAnonymous = props.boolProp(BrokerConstants.ALLOW_ANONYMOUS_PROPERTY_NAME, true);
|
||||
boolean allowZeroByteClientId = props.boolProp(BrokerConstants.ALLOW_ZERO_BYTE_CLIENT_ID_PROPERTY_NAME, false);
|
||||
boolean reauthorizeSubscriptionsOnConnect = props.boolProp(BrokerConstants.REAUTHORIZE_SUBSCRIPTIONS_ON_CONNECT, false);
|
||||
m_processor.init(connectionDescriptors, subscriptions, messagesStore, m_sessionsStore, authenticator,
|
||||
allowAnonymous, allowZeroByteClientId, authorizator, interceptor,
|
||||
sessionsRepository);
|
||||
sessionsRepository, reauthorizeSubscriptionsOnConnect);
|
||||
return m_processor;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user