use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testDeviceConnectionIsClosedOnDeviceOrTenantChangeNotification.
private <T extends AbstractNotification> void testDeviceConnectionIsClosedOnDeviceOrTenantChangeNotification(final T notification) {
// GIVEN an AMQP adapter
givenAnAdapter(properties);
final Promise<Void> startPromise = Promise.promise();
adapter.doStart(startPromise);
assertThat(startPromise.future().succeeded()).isTrue();
@SuppressWarnings("unchecked") final ArgumentCaptor<Handler<io.vertx.core.eventbus.Message<T>>> notificationHandlerCaptor = getEventBusConsumerHandlerArgumentCaptor((NotificationType<T>) notification.getType());
// with an enabled tenant
givenAConfiguredTenant(TEST_TENANT_ID, true);
// WHEN a device connects
final Device authenticatedDevice = new Device(TEST_TENANT_ID, TEST_DEVICE);
final Record record = new RecordImpl();
record.set(AmqpAdapterConstants.KEY_CLIENT_DEVICE, Device.class, authenticatedDevice);
final ProtonConnection deviceConnection = mock(ProtonConnection.class);
when(deviceConnection.attachments()).thenReturn(record);
when(deviceConnection.getRemoteContainer()).thenReturn("deviceContainer");
adapter.onConnectRequest(deviceConnection);
final ArgumentCaptor<Handler<AsyncResult<ProtonConnection>>> openHandler = VertxMockSupport.argumentCaptorHandler();
verify(deviceConnection).openHandler(openHandler.capture());
openHandler.getValue().handle(Future.succeededFuture(deviceConnection));
// that wants to receive commands
final CommandConsumer commandConsumer = mock(CommandConsumer.class);
when(commandConsumer.close(any())).thenReturn(Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_PRECON_FAILED)));
when(commandConsumerFactory.createCommandConsumer(eq(TEST_TENANT_ID), eq(TEST_DEVICE), VertxMockSupport.anyHandler(), any(), any())).thenReturn(Future.succeededFuture(commandConsumer));
final String sourceAddress = getCommandEndpoint();
final ProtonSender sender = getSender(sourceAddress);
adapter.handleRemoteSenderOpenForCommands(deviceConnection, sender);
// THEN the connection count is incremented
verify(metrics).incrementConnections(TEST_TENANT_ID);
// AND WHEN a notification is sent about the tenant/device having been deleted or disabled
sendViaEventBusMock(notification, notificationHandlerCaptor.getValue());
// THEN the device connection is closed
verify(deviceConnection).close();
// and the connection count is decremented
verify(metrics).decrementConnections(TEST_TENANT_ID);
// and the adapter has closed the command consumer
verify(commandConsumer).close(any());
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testConnectionFailsIfTenantLevelConnectionLimitIsExceeded.
/**
* Verifies that the connection is rejected as the connection limit for
* the given tenant is exceeded.
*/
@Test
public void testConnectionFailsIfTenantLevelConnectionLimitIsExceeded() {
// GIVEN an AMQP adapter that requires devices to authenticate
properties.setAuthenticationRequired(true);
givenAnAdapter(properties);
// WHEN the connection limit for the given tenant exceeds
when(resourceLimitChecks.isConnectionLimitReached(any(TenantObject.class), any(SpanContext.class))).thenReturn(Future.succeededFuture(Boolean.TRUE));
// WHEN a device connects
final Device authenticatedDevice = new Device(TEST_TENANT_ID, TEST_DEVICE);
final Record record = new RecordImpl();
record.set(AmqpAdapterConstants.KEY_CLIENT_DEVICE, Device.class, authenticatedDevice);
final ProtonConnection deviceConnection = mock(ProtonConnection.class);
when(deviceConnection.attachments()).thenReturn(record);
adapter.onConnectRequest(deviceConnection);
@SuppressWarnings("unchecked") final ArgumentCaptor<Handler<AsyncResult<ProtonConnection>>> openHandler = ArgumentCaptor.forClass(Handler.class);
verify(deviceConnection).openHandler(openHandler.capture());
openHandler.getValue().handle(Future.succeededFuture(deviceConnection));
// THEN the adapter does not accept the incoming connection request.
final ArgumentCaptor<ErrorCondition> errorConditionCaptor = ArgumentCaptor.forClass(ErrorCondition.class);
verify(deviceConnection).setCondition(errorConditionCaptor.capture());
assertEquals(AmqpError.UNAUTHORIZED_ACCESS, errorConditionCaptor.getValue().getCondition());
verify(metrics).reportConnectionAttempt(ConnectionAttemptOutcome.TENANT_CONNECTIONS_EXCEEDED, TEST_TENANT_ID, null);
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testMessageLimitExceededForADownstreamMessage.
private void testMessageLimitExceededForADownstreamMessage(final VertxTestContext ctx, final Message message, final Consumer<Void> postUploadAssertions) {
final ProtonDelivery delivery = mock(ProtonDelivery.class);
// AT LEAST ONCE
when(delivery.remotelySettled()).thenReturn(false);
final AmqpContext amqpContext = AmqpContext.fromMessage(delivery, message, span, null);
// GIVEN an AMQP adapter
givenAnAdapter(properties);
givenATelemetrySenderForAnyTenant();
// which is enabled for a tenant with exceeded message limit
when(resourceLimitChecks.isMessageLimitReached(any(TenantObject.class), anyLong(), any(SpanContext.class))).thenReturn(Future.succeededFuture(Boolean.TRUE));
// WHEN a device uploads a message to the adapter with AT_LEAST_ONCE delivery semantics
adapter.onMessageReceived(amqpContext).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
// THEN the message limit is exceeded
assertThat(((ClientErrorException) t).getErrorCode()).isEqualTo(HttpUtils.HTTP_TOO_MANY_REQUESTS);
// AND the client receives a corresponding REJECTED disposition
verify(delivery).disposition(argThat(s -> {
if (s instanceof Rejected) {
return AmqpError.RESOURCE_LIMIT_EXCEEDED.equals(((Rejected) s).getError().getCondition());
} else {
return false;
}
}), eq(true));
// AND
postUploadAssertions.accept(null);
});
ctx.completeNow();
}));
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testConnectionFailsIfAdapterIsDisabled.
/**
* Verifies that the connection is rejected as the adapter is disabled.
*/
@Test
public void testConnectionFailsIfAdapterIsDisabled() {
// GIVEN an AMQP adapter that requires devices to authenticate
properties.setAuthenticationRequired(true);
givenAnAdapter(properties);
// AND given a tenant for which the AMQP Adapter is disabled
givenAConfiguredTenant(TEST_TENANT_ID, false);
// WHEN a device connects
final Device authenticatedDevice = new Device(TEST_TENANT_ID, TEST_DEVICE);
final Record record = new RecordImpl();
record.set(AmqpAdapterConstants.KEY_CLIENT_DEVICE, Device.class, authenticatedDevice);
record.set(AmqpAdapterConstants.KEY_TLS_CIPHER_SUITE, String.class, "BUMLUX_CIPHER");
final ProtonConnection deviceConnection = mock(ProtonConnection.class);
when(deviceConnection.attachments()).thenReturn(record);
adapter.onConnectRequest(deviceConnection);
@SuppressWarnings("unchecked") final ArgumentCaptor<Handler<AsyncResult<ProtonConnection>>> openHandler = ArgumentCaptor.forClass(Handler.class);
verify(deviceConnection).openHandler(openHandler.capture());
openHandler.getValue().handle(Future.succeededFuture(deviceConnection));
// THEN the adapter does not accept the incoming connection request.
final ArgumentCaptor<ErrorCondition> errorConditionCaptor = ArgumentCaptor.forClass(ErrorCondition.class);
verify(deviceConnection).setCondition(errorConditionCaptor.capture());
assertEquals(AmqpError.UNAUTHORIZED_ACCESS, errorConditionCaptor.getValue().getCondition());
verify(metrics).reportConnectionAttempt(ConnectionAttemptOutcome.ADAPTER_DISABLED, TEST_TENANT_ID, "BUMLUX_CIPHER");
}
use of org.eclipse.hono.auth.Device in project hono by eclipse.
the class VertxBasedAmqpProtocolAdapterTest method testUploadTelemetryWithAtMostOnceDeliverySemantics.
/**
* Verifies that a request to upload a pre-settled telemetry message results
* in the downstream sender not waiting for the consumer's acknowledgment.
*
* @param ctx The vert.x test context.
*/
@Test
public void testUploadTelemetryWithAtMostOnceDeliverySemantics(final VertxTestContext ctx) {
// GIVEN an AMQP adapter with a configured server
givenAnAdapter(properties);
// sending of downstream telemetry message succeeds
givenATelemetrySenderForAnyTenant();
// which is enabled for a tenant
final TenantObject tenantObject = givenAConfiguredTenant(TEST_TENANT_ID, true);
// IF a device sends a 'fire and forget' telemetry message
final ProtonDelivery delivery = mock(ProtonDelivery.class);
when(delivery.remotelySettled()).thenReturn(true);
final Buffer payload = Buffer.buffer("payload");
final String to = ResourceIdentifier.from(TelemetryConstants.TELEMETRY_ENDPOINT, TEST_TENANT_ID, TEST_DEVICE).toString();
adapter.onMessageReceived(AmqpContext.fromMessage(delivery, getFakeMessage(to, payload), span, null)).onComplete(ctx.succeeding(d -> {
ctx.verify(() -> {
// THEN the adapter has forwarded the message downstream
assertTelemetryMessageHasBeenSentDownstream(QoS.AT_MOST_ONCE, TEST_TENANT_ID, TEST_DEVICE, "text/plain");
// and acknowledged the message to the device
verify(delivery).disposition(any(Accepted.class), eq(true));
// and has reported the telemetry message
verify(metrics).reportTelemetry(eq(EndpointType.TELEMETRY), eq(TEST_TENANT_ID), eq(tenantObject), eq(ProcessingOutcome.FORWARDED), eq(MetricsTags.QoS.AT_MOST_ONCE), eq(payload.length()), any());
});
ctx.completeNow();
}));
}
Aggregations