private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { String userName = msg.payload().userName(); String clientIdentifier = msg.payload().clientIdentifier(); if (StringUtils.isEmpty(userName)) { // ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD)); // ctx.close(); ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD)); connected = false; } else { boolean login = deviceSessionCtx.login(new DeviceTokenCredentials(userName)); if (!login) { ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_NOT_AUTHORIZED)); connected = false; } else { MemoryMetaPool.registerClienId(clientIdentifier, ctx.channel()); ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); connected = true; checkGatewaySession(); } // } } }
/** * Invoked when a client sends its <em>CONNECT</em> packet. * <p> * Authenticates the client (if required) and registers handlers for processing * messages published by the client. * * @param endpoint The MQTT endpoint representing the client. */ final void handleEndpointConnection(final MqttEndpoint endpoint) { LOG.debug("connection request from client [clientId: {}]", endpoint.clientIdentifier()); if (!isConnected()) { endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); LOG.debug("connection request from client [clientId: {}] rejected: {}", endpoint.clientIdentifier(), MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); } else { if (getConfig().isAuthenticationRequired()) { handleEndpointConnectionWithAuthentication(endpoint); } else { handleEndpointConnectionWithoutAuthentication(endpoint); } } }
/** * Verifies that an adapter that is configured to require devices to authenticate, * rejects connections from devices not providing any credentials. */ @Test public void testEndpointHandlerRejectsUnauthenticatedDevices() { // GIVEN an adapter that does require devices to authenticate MqttServer server = getMqttServer(false); AbstractVertxBasedMqttProtocolAdapter<ProtocolAdapterProperties> adapter = getAdapter(server); forceClientMocksToConnected(); // WHEN a device connects without providing any credentials MqttEndpoint endpoint = mock(MqttEndpoint.class); adapter.handleEndpointConnection(endpoint); // THEN the connection is refused verify(endpoint).reject(MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD); }
private String generateClientId(ChannelHandlerContext ctx, boolean cleanSession) { if (cleanSession) { if (Settings.INSTANCE.getBoolean("mqttserver.acceptEmptyClientId", true)) { return "Lannister_" + Long.toString(ClusterDataFactory.INSTANCE.createIdGenerator("clientIdGenerator").newId()); // [MQTT-3.1.3-6],[MQTT-3.1.3-7] } else { sendNoneAcceptMessage(ctx, MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED); return null; } } else { sendNoneAcceptMessage(ctx, MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED); // [MQTT-3.1.3-8] return null; } }
@Test public void testCONNECTION_REFUSED_SERVER_UNAVAILABLE() throws Exception { ServiceChecker prev = Plugins.INSTANCE.put(ServiceChecker.class, new ServiceChecker() { @Override public Plugin clone() { return this; } @Override public boolean isServiceAvailable() { return false; } }); MqttConnAckMessage ret = executeNormalChannelRead0(TestUtil.newClientId(), true, null); Assert.assertEquals(ret.variableHeader().connectReturnCode(), MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); Plugins.INSTANCE.put(ServiceChecker.class, prev); }
@Test public void testCONNECTION_REFUSED_NOT_AUTHORIZED() throws Exception { Authorizer prev = Plugins.INSTANCE.put(Authorizer.class, new Authorizer() { @Override public Plugin clone() { return this; } @Override public boolean isAuthorized(String clientId, String username) { return false; } }); MqttConnAckMessage ret = executeNormalChannelRead0(TestUtil.newClientId(), true, null); Assert.assertEquals(ret.variableHeader().connectReturnCode(), MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED); Plugins.INSTANCE.put(Authorizer.class, prev); }
@Test public void testLive() throws Exception { ConnectOptions options = new ConnectOptions(); options.clientId(TestUtil.newClientId()); MqttClient client = new MqttClient("mqtt://localhost:" + Settings.INSTANCE.mqttPort()); MqttConnectReturnCode ret = client.connectOptions(options).connect(); Assert.assertEquals(MqttConnectReturnCode.CONNECTION_ACCEPTED, ret); Assert.assertTrue(client.isConnected()); HttpClient httpClient = new HttpClient( "http://localhost:" + Settings.INSTANCE.httpPort() + "/api/sessions?filter=live"); HttpResponse res = httpClient.get(); Assert.assertEquals(HttpResponseStatus.OK, res.status()); Assert.assertEquals(new Integer(1), JsonPath.read(res.content().toString(CharsetUtil.UTF_8), "$.length()")); client.disconnect(true); Assert.assertFalse(client.isConnected()); }
@Test public void testWillToNullOnNormalDisconnect() throws Exception { String willTopic = "will"; String message = "ASTALAVISTA"; String clientId = TestUtil.newClientId(); ConnectOptions options = new ConnectOptions(); options.clientId(clientId); options.will( new Message(-1, willTopic, null, message.getBytes(CharsetUtil.UTF_8), MqttQoS.AT_LEAST_ONCE, false)); options.cleanSession(false); MqttClient client0 = new MqttClient("mqtt://localhost:" + Settings.INSTANCE.mqttPort()); MqttConnectReturnCode ret = client0.connectOptions(options).connect(); Assert.assertEquals(MqttConnectReturnCode.CONNECTION_ACCEPTED, ret); Assert.assertTrue(client0.isConnected()); Assert.assertTrue(Session.NEXUS.get(clientId).will() != null && Session.NEXUS.get(clientId).will().topicName().equals(willTopic)); client0.disconnect(true); Thread.sleep(100); Assert.assertNull(Session.NEXUS.get(clientId).will()); }
/** * Used for calling the connect handler when the server replies to the request * * @param msg connection response message */ void handleConnack(MqttConnAckMessage msg) { synchronized (this.connection) { this.isConnected = msg.code() == MqttConnectReturnCode.CONNECTION_ACCEPTED; if (this.connectHandler != null) { if (msg.code() == MqttConnectReturnCode.CONNECTION_ACCEPTED) { this.connectHandler.handle(Future.succeededFuture(msg)); } else { MqttConnectionException exception = new MqttConnectionException(msg.code()); log.error(String.format("Connection refused by the server - code: %s", msg.code())); this.connectHandler.handle(Future.failedFuture(exception)); } } } }
@Test public void refusedBadUsernamePassword(TestContext context) { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; try { MemoryPersistence persistence = new MemoryPersistence(); MqttConnectOptions options = new MqttConnectOptions(); options.setUserName("wrong_username"); options.setPassword("wrong_password".toCharArray()); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "12345", persistence); client.connect(options); context.fail(); } catch (MqttException e) { context.assertTrue(e.getReasonCode() == MqttException.REASON_CODE_FAILED_AUTHENTICATION); } }
@Test public void refusedUnacceptableProtocolVersion(TestContext context) { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION; try { MemoryPersistence persistence = new MemoryPersistence(); MqttConnectOptions options = new MqttConnectOptions(); // trying the old 3.1 options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "12345", persistence); client.connect(options); context.fail(); } catch (MqttException e) { context.assertTrue(e.getReasonCode() == MqttException.REASON_CODE_INVALID_PROTOCOL_VERSION); } }
@Test public void connectionAlreadyAccepted(TestContext context) throws Exception { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_ACCEPTED; MemoryPersistence persistence = new MemoryPersistence(); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "12345", persistence); client.connect(); try { // try to accept a connection already accepted this.endpoint.accept(false); context.fail(); } catch (IllegalStateException e) { // Ok } }
@Test public void refusedClientIdZeroBytes(TestContext context) { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED; try { MemoryPersistence persistence = new MemoryPersistence(); MqttConnectOptions options = new MqttConnectOptions(); options.setCleanSession(false); options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "", persistence); client.connect(options); context.fail(); } catch (MqttException e) { context.assertTrue(e.getReasonCode() == MqttException.REASON_CODE_INVALID_CLIENT_ID); context.assertNotNull(rejection); } }
/** * Verifies that a connection attempt from a device is refused if the adapter is not * connected to all of the services it depends on. */ @Test public void testEndpointHandlerFailsWithoutConnect() { // GIVEN an endpoint MqttEndpoint endpoint = mock(MqttEndpoint.class); MqttServer server = getMqttServer(false); AbstractVertxBasedMqttProtocolAdapter<ProtocolAdapterProperties> adapter = getAdapter(server); adapter.handleEndpointConnection(endpoint); verify(endpoint).reject(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); }
public static MqttConnAckMessage connack(MqttConnectReturnCode returnCode, boolean sessionPresent) { MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 2); MqttConnAckVariableHeader variableHeader = new MqttConnAckVariableHeader(returnCode, sessionPresent); return new MqttConnAckMessage(fixedHeader, variableHeader); }
private boolean filterPlugins(ChannelHandlerContext ctx, MqttConnectMessage msg) { String clientId = msg.payload().clientIdentifier(); String userName = msg.variableHeader().hasUserName() ? msg.payload().userName() : null; String password = msg.variableHeader().hasPassword() ? msg.payload().password() : null; if (!Plugins.INSTANCE.get(ServiceChecker.class).isServiceAvailable()) { sendNoneAcceptMessage(ctx, MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); return false; } if (!Plugins.INSTANCE.get(Authenticator.class).isValid(clientId)) { sendNoneAcceptMessage(ctx, MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED); // [MQTT-3.1.3-9] return false; } if (!Plugins.INSTANCE.get(Authenticator.class).isValid(clientId, userName, password)) { sendNoneAcceptMessage(ctx, MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD); return false; } if (!Plugins.INSTANCE.get(Authorizer.class).isAuthorized(clientId, userName)) { sendNoneAcceptMessage(ctx, MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED); return false; } return true; }
private void sendNoneAcceptMessage(ChannelHandlerContext ctx, MqttConnectReturnCode returnCode) { assert returnCode != MqttConnectReturnCode.CONNECTION_ACCEPTED; MqttConnAckMessage msg = MqttMessageFactory.connack(returnCode, false); // [MQTT-3.2.2-4] ctx.channel().writeAndFlush(msg).addListener(f -> { Plugins.INSTANCE.get(ConnectEventListener.class).connectHandled(new ConnectEventArgs() { @Override public String clientId() { return null; } @Override public IMessage will() { return null; } @Override public Boolean cleanSession() { return null; } @Override public MqttConnectReturnCode returnCode() { return returnCode; } }); ctx.channel().disconnect().addListener(ChannelFutureListener.CLOSE).addListener(fs -> // [MQTT-3.2.2-5],[MQTT-3.1.4-5] Plugins.INSTANCE.get(DisconnectEventListener.class).disconnected(new AbnormalDisconnectEventArgs())); }); }
@Test public void testNonCleanSessionWithoutClientId() throws Exception { // [MQTT-3.1.3-3] ConnectOptions options = new ConnectOptions(); options.cleanSession(false); MqttClient client = new MqttClient("mqtt://localhost:" + Settings.INSTANCE.mqttPort()); MqttConnectReturnCode ret = client.connectOptions(options).receiver(null).connect(); Assert.assertEquals(MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED, ret); }
@Test public void testCleanSessionWithoutClientIdReturnFalse() throws Exception { Settings.INSTANCE.setProperty("mqttserver.acceptEmptyClientId", "false"); MqttConnAckMessage ret = executeNormalChannelRead0("", true, null); Assert.assertEquals(MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED, ret.variableHeader().connectReturnCode()); Settings.INSTANCE.setProperty("mqttserver.acceptEmptyClientId", "true"); }
@Test public void nonCleanSession() throws Exception { String clientId = TestUtil.newClientId(); MqttConnAckMessage ret = executeNormalChannelRead0(clientId, false, null); Assert.assertEquals(ret.variableHeader().connectReturnCode(), MqttConnectReturnCode.CONNECTION_ACCEPTED); }
public MqttEndpointImpl accept(boolean sessionPresent) { if (this.isConnected) { throw new IllegalStateException("Connection already accepted"); } return this.connack(MqttConnectReturnCode.CONNECTION_ACCEPTED, sessionPresent); }
public MqttEndpointImpl reject(MqttConnectReturnCode returnCode) { if (returnCode == MqttConnectReturnCode.CONNECTION_ACCEPTED) { throw new IllegalArgumentException("Need to use the 'accept' method for accepting connection"); } // sessionPresent flag has no meaning in this case, the network connection will be closed return this.connack(returnCode, false); }
@Test public void connackNotOk(TestContext context) { Async async = context.async(); Async asyncServer = context.async(); Vertx vertx = Vertx.vertx(); MqttServer server = MqttServer.create(vertx); server.endpointHandler(endpoint -> { endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); }); server.listen(MqttServerOptions.DEFAULT_PORT, done -> { if (done.succeeded()) { asyncServer.complete(); } else { context.fail(); } }); asyncServer.await(); MqttClient client = MqttClient.create(vertx); client.closeHandler(v -> { // when server replies with "negative" CONNACK, this handler should not be called // the failure is just part of the connectHandler context.fail(); }); client.connect(MqttClientOptions.DEFAULT_PORT, MqttClientOptions.DEFAULT_HOST, c -> { assertTrue(c.failed()); assertTrue(c.cause() instanceof MqttConnectionException); MqttConnectionException connEx = (MqttConnectionException) c.cause(); assertEquals(connEx.code(), MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); assertFalse(client.isConnected()); async.complete(); }); async.await(); }
@Test public void accepted(TestContext context) { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_ACCEPTED; try { MemoryPersistence persistence = new MemoryPersistence(); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "12345", persistence); client.connect(); } catch (MqttException e) { context.fail(e); } }
@Test public void acceptedClientIdAutoGenerated(TestContext context) { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_ACCEPTED; try { MemoryPersistence persistence = new MemoryPersistence(); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "", persistence); client.connect(); } catch (MqttException e) { context.fail(e); } }
@Test public void refusedIdentifierRejected(TestContext context) { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED; try { MemoryPersistence persistence = new MemoryPersistence(); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "12345", persistence); client.connect(); context.fail(); } catch (MqttException e) { context.assertTrue(e.getReasonCode() == MqttException.REASON_CODE_INVALID_CLIENT_ID); } }
@Test public void refusedServerUnavailable(TestContext context) { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE; try { MemoryPersistence persistence = new MemoryPersistence(); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "12345", persistence); client.connect(); context.fail(); } catch (MqttException e) { context.assertTrue(e.getReasonCode() == MqttException.REASON_CODE_BROKER_UNAVAILABLE); } }
@Test public void refusedNotAuthorized(TestContext context) { this.expectedReturnCode = MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; try { MemoryPersistence persistence = new MemoryPersistence(); MqttClient client = new MqttClient(String.format("tcp://%s:%d", MQTT_SERVER_HOST, MQTT_SERVER_PORT), "12345", persistence); client.connect(); context.fail(); } catch (MqttException e) { context.assertTrue(e.getReasonCode() == MqttException.REASON_CODE_NOT_AUTHORIZED); } }
@Override protected void endpointHandler(MqttEndpoint endpoint) { MqttConnectReturnCode returnCode = this.expectedReturnCode; switch (this.expectedReturnCode) { case CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD: returnCode = (endpoint.auth().userName().equals(MQTT_USERNAME) && endpoint.auth().password().equals(MQTT_PASSWORD)) ? MqttConnectReturnCode.CONNECTION_ACCEPTED : MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; break; case CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION: returnCode = endpoint.protocolVersion() == MqttConnectOptions.MQTT_VERSION_3_1_1 ? MqttConnectReturnCode.CONNECTION_ACCEPTED : MqttConnectReturnCode.CONNECTION_REFUSED_UNACCEPTABLE_PROTOCOL_VERSION; break; } log.info("return code = " + returnCode); if (returnCode == MqttConnectReturnCode.CONNECTION_ACCEPTED) { log.info("client id = " + endpoint.clientIdentifier()); endpoint.accept(false); } else { endpoint.reject(returnCode); } this.endpoint = endpoint; }
private MqttConnAckMessage createMqttConnAckMsg(MqttConnectReturnCode returnCode) { MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(CONNACK, false, AT_MOST_ONCE, false, 0); MqttConnAckVariableHeader mqttConnAckVariableHeader = new MqttConnAckVariableHeader(returnCode, true); return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader); }
private ConnectAcknowledgeMessage(boolean dup, int qos, boolean retain, int keepAliveTime, MqttConnectReturnCode returnCode) { setMessageType(MESSAGE_TYPE); this.dup = dup; this.qos = qos; this.retain = retain; this.keepAliveTime = keepAliveTime; this.returnCode = returnCode; }
/** * Handles the connect packet. See spec for details on each of parameters. */ synchronized void connect(String cId, String username, byte[] passwordInBytes, boolean will, byte[] willMessage, String willTopic, boolean willRetain, int willQosLevel, boolean cleanSession) throws Exception { String clientId = validateClientId(cId, cleanSession); if (clientId == null) { session.getProtocolHandler().sendConnack(MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED); session.getProtocolHandler().disconnect(true); return; } session.setSessionState(getSessionState(clientId)); String password = passwordInBytes == null ? null : new String(passwordInBytes, CharsetUtil.UTF_8); ServerSessionImpl serverSession = createServerSession(username, password); serverSession.start(); session.setServerSession(serverSession); session.setIsClean(cleanSession); if (will) { isWill = true; this.willMessage = ByteBufAllocator.DEFAULT.buffer(willMessage.length); this.willMessage.writeBytes(willMessage); this.willQoSLevel = willQosLevel; this.willRetain = willRetain; this.willTopic = willTopic; } session.getConnection().setConnected(true); session.start(); session.getConnection().setClientID(clientId); session.getProtocolHandler().sendConnack(MqttConnectReturnCode.CONNECTION_ACCEPTED); }
static void assertEqualsConnAck(String msg, MqttConnectReturnCode expectedCode, Object connAck) { assertTrue("connAck is not an instance of ConnAckMessage", connAck instanceof MqttConnAckMessage); MqttConnAckMessage connAckMsg = (MqttConnAckMessage) connAck; if (msg == null) assertEquals(expectedCode, connAckMsg.variableHeader().connectReturnCode()); else assertEquals(msg, expectedCode, connAckMsg.variableHeader().connectReturnCode()); }
public ConnAck(MqttConnectReturnCode returnCode, boolean sessionPresent) { this.returnCode = returnCode; this.sessionPresent = sessionPresent; }
public MqttConnectReturnCode getReturnCode() { return returnCode; }
public void setReturnCode(MqttConnectReturnCode returnCode) { this.returnCode = returnCode; }
private void handleEndpointConnectionWithAuthentication(final MqttEndpoint endpoint) { if (endpoint.auth() == null) { LOG.debug("connection request from device [clientId: {}] rejected: {}", endpoint.clientIdentifier(), "device did not provide credentials in CONNECT packet"); endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD); } else { final DeviceCredentials credentials = getCredentials(endpoint.auth()); if (credentials == null) { LOG.debug("connection request from device [clientId: {}] rejected: {}", endpoint.clientIdentifier(), "device provided malformed credentials in CONNECT packet"); endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD); } else { getCredentialsAuthProvider().authenticate(credentials, attempt -> { if (attempt.failed()) { LOG.debug("cannot authenticate device [tenant-id: {}, auth-id: {}]: {}", credentials.getTenantId(), credentials.getAuthId(), attempt.cause().getMessage()); if (ServerErrorException.class.isInstance(attempt.cause())) { // credentials service might not be available (yet) endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE); } else { // validation of credentials has failed endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED); } } else { final Device authenticatedDevice = attempt.result(); LOG.debug("successfully authenticated device [tenant-id: {}, auth-id: {}, device-id: {}]", authenticatedDevice.getTenantId(), credentials.getAuthId(), authenticatedDevice.getDeviceId()); onAuthenticationSuccess(endpoint, authenticatedDevice); } }); } } }