private HonoClientBasedAuthProvider<UsernamePasswordCredentials> getUsernamePasswordAuthProvider() { if (usernamePasswordAuthProvider == null) { usernamePasswordAuthProvider = new UsernamePasswordAuthProvider(credentialsServiceClient, config, tracer); } return usernamePasswordAuthProvider; }
/** * Verifies that the provider fails to authenticate a device when not * running on a vert.x Context. * * @param ctx The vert.x test context. */ @Test public void testAuthenticateRequiresVertxContext(final TestContext ctx) { provider.authenticate(deviceCredentials, null, ctx.asyncAssertFailure(e -> { ctx.assertTrue(e instanceof IllegalStateException); })); }
/** * Verifies that the provider fails to validate wrong credentials. * * @param ctx The vert.x test context. */ @Test public void testAuthenticateFailsForWrongCredentials(final TestContext ctx) { when(pwdEncoder.matches(eq("wrong_pwd"), any(JsonObject.class))).thenReturn(false); deviceCredentials = UsernamePasswordCredentials.create("device@DEFAULT_TENANT", "wrong_pwd", false); vertx.runOnContext(go -> { provider.authenticate(deviceCredentials, null, ctx.asyncAssertFailure(e -> { final ClientErrorException error = (ClientErrorException) e; ctx.assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, error.getErrorCode()); })); }); }
/** * Sets up the fixture. */ @Before public void setUp() { credentialsOnRecord = new CredentialsObject() .setAuthId("device") .setDeviceId("4711") .setType(CredentialsConstants.SECRETS_TYPE_HASHED_PASSWORD) .setEnabled(true); credentialsClient = mock(CredentialsClient.class); when(credentialsClient.get(anyString(), anyString(), any(JsonObject.class), any())).thenReturn(Future.succeededFuture(credentialsOnRecord)); credentialsServiceClient = mock(HonoClient.class); when(credentialsServiceClient.getOrCreateCredentialsClient(anyString())).thenReturn(Future.succeededFuture(credentialsClient)); pwdEncoder = mock(HonoPasswordEncoder.class); when(pwdEncoder.matches(anyString(), any(JsonObject.class))).thenReturn(true); provider = new UsernamePasswordAuthProvider(credentialsServiceClient, pwdEncoder, new ServiceConfigProperties(), NoopTracerFactory.create()); }
/** * Verifies that credentials validation fails if none of the secrets on record are * valid any more. * * @param ctx The vert.x test context. */ @Test public void testAuthenticateFailsIfNoSecretsAreValidAnymore(final TestContext ctx) { credentialsOnRecord.addSecret(CredentialsObject.emptySecret(null, Instant.now().minusSeconds(120))); vertx.runOnContext(go -> { provider.authenticate(deviceCredentials, null, ctx.asyncAssertFailure(t -> { // THEN authentication fails with a 401 client error ctx.assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, ((ClientErrorException) t).getErrorCode()); })); }); }
@Override public void doStart(final Future<Void> startFuture) { LOG.info("limiting size of inbound message payload to {} bytes", getConfig().getMaxPayloadSize()); if (!getConfig().isAuthenticationRequired()) { LOG.warn("authentication of devices turned off"); } checkPortConfiguration() .compose(ok -> { return CompositeFuture.all(bindSecureMqttServer(), bindInsecureMqttServer()); }).compose(t -> { if (usernamePasswordAuthProvider == null) { usernamePasswordAuthProvider = new UsernamePasswordAuthProvider(getCredentialsServiceClient(), getConfig()); } startFuture.complete(); }, startFuture); }
/** * Verifies that credentials validation fails if none of the secrets on record are * valid yet. * * @param ctx The vert.x test context. */ @Test public void testAuthenticateFailsIfNoSecretsAreValidYet(final TestContext ctx) { credentialsOnRecord.addSecret(CredentialsObject.emptySecret(Instant.now().plusSeconds(120), null)); vertx.runOnContext(go -> { provider.authenticate(deviceCredentials, null, ctx.asyncAssertFailure(t -> { // THEN authentication fails with a 401 client error ctx.assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, ((ClientErrorException) t).getErrorCode()); })); }); } }
@Override protected void addRoutes(final Router router) { if (getConfig().isAuthenticationRequired()) { final ChainAuthHandler authHandler = new HonoChainAuthHandler(); authHandler.append(new X509AuthHandler( Optional.ofNullable(clientCertAuthProvider).orElse( new X509AuthProvider(getCredentialsServiceClient(), getConfig())), getTenantServiceClient(), tracer)); authHandler.append(new HonoBasicAuthHandler( Optional.ofNullable(usernamePasswordAuthProvider).orElse( new UsernamePasswordAuthProvider(getCredentialsServiceClient(), getConfig())), getConfig().getRealm())); addTelemetryApiRoutes(router, authHandler); addEventApiRoutes(router, authHandler); addCommandResponseRoutes(router, authHandler); } else { LOG.warn("device authentication has been disabled"); LOG.warn("any device may publish data on behalf of all other devices"); addTelemetryApiRoutes(router, null); addEventApiRoutes(router, null); addCommandResponseRoutes(router, null); } }
/** * Verifies that the provider succeeds to validate matching credentials when * running on a vert.x Context. * * @param ctx The vert.x test context. */ @Test public void testAuthenticateSucceedsWhenRunningOnVertxContext(final TestContext ctx) { vertx.runOnContext(go -> { provider.authenticate(deviceCredentials, null, ctx.asyncAssertSuccess(device -> { ctx.assertEquals("4711", device.getDeviceId()); ctx.assertEquals("DEFAULT_TENANT", device.getTenantId()); })); }); }
@Override protected void addRoutes(final Router router) { if (getConfig().isAuthenticationRequired()) { final ChainAuthHandler authHandler = ChainAuthHandler.create(); authHandler.append(new X509AuthHandler( new TenantServiceBasedX509Authentication(getTenantServiceClient(), tracer), Optional.ofNullable(clientCertAuthProvider).orElse( new X509AuthProvider(getCredentialsServiceClient(), getConfig(), tracer)))); authHandler.append(new HonoBasicAuthHandler( Optional.ofNullable(usernamePasswordAuthProvider).orElse( new UsernamePasswordAuthProvider(getCredentialsServiceClient(), getConfig(), tracer)), getConfig().getRealm(), tracer)); addTelemetryApiRoutes(router, authHandler); addEventApiRoutes(router, authHandler); addCommandResponseRoutes(router, authHandler); } else { LOG.warn("device authentication has been disabled"); LOG.warn("any device may publish data on behalf of all other devices"); addTelemetryApiRoutes(router, null); addEventApiRoutes(router, null); addCommandResponseRoutes(router, null); } }
/** * Creates the default auth handler to use for authenticating devices. * <p> * This default implementation creates a {@link ChainAuthHandler} consisting of * an {@link X509AuthHandler} and a {@link ConnectPacketAuthHandler} instance. * <p> * Subclasses may either set the auth handler expicitly using * {@link #setAuthHandler(AuthHandler)} or override this method in order to * create a custom auth handler. * * @return The handler. */ protected AuthHandler<MqttContext> createAuthHandler() { return new ChainAuthHandler<MqttContext>() .append(new X509AuthHandler( new TenantServiceBasedX509Authentication(getTenantServiceClient(), tracer), new X509AuthProvider(getCredentialsServiceClient(), getConfig(), tracer))) .append(new ConnectPacketAuthHandler( new UsernamePasswordAuthProvider( getCredentialsServiceClient(), getConfig(), tracer))); }