/** * Matches the credentials against a given secret. * <p> * The secret is expected to be of type <em>hashed-password</em> as defined by * <a href="https://www.eclipse.org/hono/api/credentials-api/#hashed-password">Hono's Credentials API</a>. * * @param candidateSecret The secret to match against. * @return {@code true} if the credentials match the secret. */ @Override public boolean matchesCredentials(final JsonObject candidateSecret) { try { final EncodedPassword encodedPassword = EncodedPassword.fromHonoSecret(candidateSecret); return encodedPassword.matches(getPassword()); } catch (final IllegalArgumentException e) { if (LOG.isDebugEnabled()) { LOG.debug("cannot decode malformed Base64 encoded property", e); } return false; } catch (final ClassCastException e) { // one or more of the properties are not of expected type if (LOG.isDebugEnabled()) { LOG.debug("cannot process malformed candidate hashed-password secret returned by Credentials service [{}]", candidateSecret.encodePrettily()); } return false; } }
@Override protected Future<Device> doValidateCredentials( final UsernamePasswordCredentials deviceCredentials, final CredentialsObject credentialsOnRecord) { final Context currentContext = Vertx.currentContext(); if (currentContext == null) { return Future.failedFuture(new IllegalStateException("not running on vert.x Context")); } else { final Future<Device> result = Future.future(); currentContext.executeBlocking(blockingCodeHandler -> { log.debug("validating password hash on vert.x worker thread [{}]", Thread.currentThread().getName()); final boolean isValid = credentialsOnRecord.getCandidateSecrets().stream() .anyMatch(candidateSecret -> pwdEncoder.matches(deviceCredentials.getPassword(), candidateSecret)); if (isValid) { blockingCodeHandler.complete(new Device(deviceCredentials.getTenantId(), credentialsOnRecord.getDeviceId())); } else { blockingCodeHandler.fail(new ClientErrorException(HttpURLConnection.HTTP_UNAUTHORIZED, "bad credentials")); } }, false, result); return result; } } }
/** * Verifies that for single tenant mode, the tenant is automatically set to {@link Constants#DEFAULT_TENANT}. */ @Test public void testTenantFromUserSingleTenant() { final UsernamePasswordCredentials mqttUsernamePassword = UsernamePasswordCredentials.create(TEST_USER, TEST_PASSWORD, true); assertEquals(CredentialsConstants.SECRETS_TYPE_HASHED_PASSWORD, mqttUsernamePassword.getType()); assertEquals(Constants.DEFAULT_TENANT, mqttUsernamePassword.getTenantId()); assertEquals(TEST_USER, mqttUsernamePassword.getAuthId()); assertEquals(TEST_PASSWORD, mqttUsernamePassword.getPassword()); } }
/** * Verifies that in multi tenant mode, a username containing userId@tenantId leads to a correctly filled instance. */ @Test public void testTenantFromUserMultiTenant() { final UsernamePasswordCredentials mqttUsernamePassword = UsernamePasswordCredentials.create(TEST_USER_OTHER_TENANT, TEST_PASSWORD, false); assertEquals(CredentialsConstants.SECRETS_TYPE_HASHED_PASSWORD, mqttUsernamePassword.getType()); assertEquals(TEST_OTHER_TENANT, mqttUsernamePassword.getTenantId()); assertEquals(TEST_USER, mqttUsernamePassword.getAuthId()); assertEquals(TEST_PASSWORD, mqttUsernamePassword.getPassword()); }