From 5c15137254c9a231cc3b2ce517e91b2669c1b288 Mon Sep 17 00:00:00 2001 From: Kartheeswaran Kalidass Date: Wed, 24 Nov 2021 08:52:20 +0100 Subject: [PATCH 1/2] [#2825] Make use of auth-id template to get credentials Signed-off-by: Kartheeswaran Kalidass --- .../AbstractCredentialsService.java | 8 +++--- .../util/DeviceRegistryUtils.java | 26 +++++++++++++++++++ .../AbstractCredentialsServiceTest.java | 4 +-- .../jdbc/impl/CredentialsServiceImpl.java | 5 ++-- .../MongoDbBasedCredentialsService.java | 9 ++++--- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/credentials/AbstractCredentialsService.java b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/credentials/AbstractCredentialsService.java index 72d89ed60b..57b4e147d8 100644 --- a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/credentials/AbstractCredentialsService.java +++ b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/service/credentials/AbstractCredentialsService.java @@ -19,10 +19,10 @@ import org.eclipse.hono.client.ServiceInvocationException; import org.eclipse.hono.deviceregistry.service.tenant.NoopTenantInformationService; import org.eclipse.hono.deviceregistry.service.tenant.TenantInformationService; -import org.eclipse.hono.deviceregistry.service.tenant.TenantKey; import org.eclipse.hono.deviceregistry.util.DeviceRegistryUtils; import org.eclipse.hono.service.credentials.CredentialsService; import org.eclipse.hono.service.management.device.DeviceAndGatewayAutoProvisioner; +import org.eclipse.hono.service.management.tenant.Tenant; import org.eclipse.hono.tracing.TracingHelper; import org.eclipse.hono.util.CacheDirective; import org.eclipse.hono.util.Constants; @@ -98,7 +98,7 @@ public final Future stop() { /** * Get credentials for a device. * - * @param tenant The tenant key object. + * @param tenant The tenant information. * @param key The credentials key object. * @param clientContext Optional bag of properties that can be used to identify the device. * @param span The active OpenTracing span for this operation. @@ -106,7 +106,7 @@ public final Future stop() { * @throws NullPointerException if any of the parameters other than client context are {@code null}. */ protected abstract Future> processGet( - TenantKey tenant, + Tenant tenant, CredentialKey key, JsonObject clientContext, Span span); @@ -153,7 +153,7 @@ public final Future> get( .compose(tenant -> { LOG.trace("retrieving credentials by auth-id"); return processGet( - TenantKey.from(tenantId), + tenant, CredentialKey.from(tenantId, authId, type), clientContext, span) diff --git a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/util/DeviceRegistryUtils.java b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/util/DeviceRegistryUtils.java index fce48b0730..e596f210fb 100644 --- a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/util/DeviceRegistryUtils.java +++ b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/util/DeviceRegistryUtils.java @@ -37,6 +37,7 @@ import org.eclipse.hono.tracing.TracingHelper; import org.eclipse.hono.util.CacheDirective; import org.eclipse.hono.util.CredentialsConstants; +import org.eclipse.hono.util.IdentityTemplate; import org.eclipse.hono.util.RegistryManagementConstants; import org.eclipse.hono.util.TenantConstants; import org.eclipse.hono.util.TenantObject; @@ -343,6 +344,31 @@ public static boolean matchesWithClientContext(final JsonObject credential, fina } + /** + * todo. + * + * @param credential The credential object to match. + * @param tenant The tenant information. + * @return todo. + */ + public static JsonObject applyAuthIdTemplate(final JsonObject credential, final Tenant tenant) { + Objects.requireNonNull(credential); + + final String type = credential.getString(RegistryManagementConstants.FIELD_TYPE); + if (!CredentialsConstants.SECRETS_TYPE_X509_CERT.equals(type)) { + return credential; + } + + final String subjectDN = credential.getString(RegistryManagementConstants.FIELD_AUTH_ID); + final Optional authIdTemplate = tenant.getAuthIdTemplate(subjectDN); + authIdTemplate + .map(IdentityTemplate::new) + .map(t -> t.apply(subjectDN)) + .map(generatedAuthId -> credential.put(RegistryManagementConstants.FIELD_AUTH_ID, generatedAuthId)); + + return credential; + } + /** * Returns a regex expression based on the given filter value which can be used by the device registry * implementations to support wildcard matching during search operations. diff --git a/services/device-registry-base/src/test/java/org/eclipse/hono/deviceregistry/service/credentials/AbstractCredentialsServiceTest.java b/services/device-registry-base/src/test/java/org/eclipse/hono/deviceregistry/service/credentials/AbstractCredentialsServiceTest.java index 9ba7d8b083..7c304bfc93 100644 --- a/services/device-registry-base/src/test/java/org/eclipse/hono/deviceregistry/service/credentials/AbstractCredentialsServiceTest.java +++ b/services/device-registry-base/src/test/java/org/eclipse/hono/deviceregistry/service/credentials/AbstractCredentialsServiceTest.java @@ -21,10 +21,10 @@ import java.util.concurrent.TimeUnit; import org.eclipse.hono.client.util.MessagingClientProvider; -import org.eclipse.hono.deviceregistry.service.tenant.TenantKey; import org.eclipse.hono.service.management.credentials.CredentialsManagementService; import org.eclipse.hono.service.management.device.DeviceAndGatewayAutoProvisioner; import org.eclipse.hono.service.management.device.DeviceManagementService; +import org.eclipse.hono.service.management.tenant.Tenant; import org.eclipse.hono.util.CacheDirective; import org.eclipse.hono.util.CredentialsConstants; import org.eclipse.hono.util.CredentialsResult; @@ -59,7 +59,7 @@ public void testGetCredentialsPreservesOriginalErrorStatus(final VertxTestContex final AbstractCredentialsService credentialsService = new AbstractCredentialsService() { @Override - protected Future> processGet(final TenantKey tenant, final CredentialKey key, + protected Future> processGet(final Tenant tenant, final CredentialKey key, final JsonObject clientContext, final Span span) { return Future.succeededFuture(CredentialsResult.from(HttpURLConnection.HTTP_BAD_GATEWAY)); } diff --git a/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/CredentialsServiceImpl.java b/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/CredentialsServiceImpl.java index cf8fcc734f..8b430cdd2e 100644 --- a/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/CredentialsServiceImpl.java +++ b/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/CredentialsServiceImpl.java @@ -26,9 +26,9 @@ import org.eclipse.hono.deviceregistry.jdbc.config.DeviceServiceProperties; import org.eclipse.hono.deviceregistry.service.credentials.AbstractCredentialsService; import org.eclipse.hono.deviceregistry.service.credentials.CredentialKey; -import org.eclipse.hono.deviceregistry.service.tenant.TenantKey; import org.eclipse.hono.deviceregistry.util.DeviceRegistryUtils; import org.eclipse.hono.service.base.jdbc.store.device.TableAdapterStore; +import org.eclipse.hono.service.management.tenant.Tenant; import org.eclipse.hono.util.Constants; import org.eclipse.hono.util.CredentialsConstants; import org.eclipse.hono.util.CredentialsResult; @@ -60,7 +60,7 @@ public CredentialsServiceImpl(final TableAdapterStore store, final DeviceService @Override protected Future> processGet( - final TenantKey tenant, + final Tenant tenant, final CredentialKey key, final JsonObject clientContext, final Span span) { @@ -77,6 +77,7 @@ protected Future> processGet( final var secrets = result.getCredentials() .stream() .map(JsonObject::mapFrom) + .map(c -> DeviceRegistryUtils.applyAuthIdTemplate(c, tenant)) .filter(filter(key.getType(), key.getAuthId())) .filter(credential -> DeviceRegistryUtils.matchesWithClientContext(credential, clientContext)) .flatMap(c -> c.getJsonArray(CredentialsConstants.FIELD_SECRETS) diff --git a/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedCredentialsService.java b/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedCredentialsService.java index d5ea99b6dd..1cf3845119 100644 --- a/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedCredentialsService.java +++ b/services/device-registry-mongodb/src/main/java/org/eclipse/hono/deviceregistry/mongodb/service/MongoDbBasedCredentialsService.java @@ -22,8 +22,8 @@ import org.eclipse.hono.deviceregistry.mongodb.model.CredentialsDao; import org.eclipse.hono.deviceregistry.service.credentials.AbstractCredentialsService; import org.eclipse.hono.deviceregistry.service.credentials.CredentialKey; -import org.eclipse.hono.deviceregistry.service.tenant.TenantKey; import org.eclipse.hono.deviceregistry.util.DeviceRegistryUtils; +import org.eclipse.hono.service.management.tenant.Tenant; import org.eclipse.hono.util.CacheDirective; import org.eclipse.hono.util.CredentialsConstants; import org.eclipse.hono.util.CredentialsResult; @@ -87,7 +87,7 @@ private CacheDirective getCacheDirective(final String type) { */ @Override protected Future> processGet( - final TenantKey tenant, + final Tenant tenant, final CredentialKey key, final JsonObject clientContext, final Span span) { @@ -96,7 +96,8 @@ protected Future> processGet( Objects.requireNonNull(key); Objects.requireNonNull(span); - return dao.getByAuthIdAndType(tenant.getTenantId(), key.getAuthId(), key.getType(), span.context()) + //todo to make use of the tenant to apply the auth-id template in case of X509 certificate credential. + return dao.getByAuthIdAndType(key.getTenantId(), key.getAuthId(), key.getType(), span.context()) .map(dto -> { LOG.trace("found credentials matching criteria"); final var json = JsonObject.mapFrom(dto.getCredentials().get(0)); @@ -109,7 +110,7 @@ protected Future> processGet( credential, getCacheDirective(key.getType()))) .orElseThrow(() -> new ClientErrorException( - tenant.getTenantId(), + key.getTenantId(), HttpURLConnection.HTTP_NOT_FOUND, "no matching credentials on record")); }) From 5b11c3e26c37bf86422f311ad07451f76fd974d0 Mon Sep 17 00:00:00 2001 From: Kartheeswaran Kalidass Date: Wed, 24 Nov 2021 18:29:52 +0100 Subject: [PATCH 2/2] Use issuer DN instead of subject DN from client certificate Signed-off-by: Kartheeswaran Kalidass --- .../util/DeviceRegistryUtils.java | 38 +++++++++++++------ .../jdbc/impl/CredentialsServiceImpl.java | 2 +- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/util/DeviceRegistryUtils.java b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/util/DeviceRegistryUtils.java index e596f210fb..5779df2a58 100644 --- a/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/util/DeviceRegistryUtils.java +++ b/services/device-registry-base/src/main/java/org/eclipse/hono/deviceregistry/util/DeviceRegistryUtils.java @@ -28,6 +28,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.security.auth.x500.X500Principal; + import org.eclipse.hono.auth.HonoPasswordEncoder; import org.eclipse.hono.client.ClientErrorException; import org.eclipse.hono.service.management.credentials.CommonCredential; @@ -349,23 +351,37 @@ public static boolean matchesWithClientContext(final JsonObject credential, fina * * @param credential The credential object to match. * @param tenant The tenant information. + * @param clientContext todo. * @return todo. */ - public static JsonObject applyAuthIdTemplate(final JsonObject credential, final Tenant tenant) { + public static JsonObject applyAuthIdTemplate(final JsonObject credential, final Tenant tenant, + final JsonObject clientContext) { Objects.requireNonNull(credential); final String type = credential.getString(RegistryManagementConstants.FIELD_TYPE); - if (!CredentialsConstants.SECRETS_TYPE_X509_CERT.equals(type)) { - return credential; + if (CredentialsConstants.SECRETS_TYPE_X509_CERT.equals(type)) { + // TODO this is just to show that that issuer DN is taken from the client context for draft purpose and not + // final implementation + if (clientContext != null && !clientContext.isEmpty()) { + final byte[] bytes = clientContext.getBinary(CredentialsConstants.FIELD_CLIENT_CERT); + if (bytes != null) { + try { + final CertificateFactory factory = CertificateFactory.getInstance("X.509"); + final X509Certificate cert = (X509Certificate) factory + .generateCertificate(new ByteArrayInputStream(bytes)); + final String issuerDN = cert.getIssuerX500Principal().getName(X500Principal.RFC2253); + final String subjectDN = credential.getString(RegistryManagementConstants.FIELD_AUTH_ID); + tenant.getAuthIdTemplate(issuerDN) + .map(IdentityTemplate::new) + .map(t -> t.apply(subjectDN)) + .map(generatedAuthId -> credential.put(RegistryManagementConstants.FIELD_AUTH_ID, + generatedAuthId)); + } catch (final CertificateException e) { + // TODO. + } + } + } } - - final String subjectDN = credential.getString(RegistryManagementConstants.FIELD_AUTH_ID); - final Optional authIdTemplate = tenant.getAuthIdTemplate(subjectDN); - authIdTemplate - .map(IdentityTemplate::new) - .map(t -> t.apply(subjectDN)) - .map(generatedAuthId -> credential.put(RegistryManagementConstants.FIELD_AUTH_ID, generatedAuthId)); - return credential; } diff --git a/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/CredentialsServiceImpl.java b/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/CredentialsServiceImpl.java index 8b430cdd2e..fe8ebc52ce 100644 --- a/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/CredentialsServiceImpl.java +++ b/services/device-registry-jdbc/src/main/java/org/eclipse/hono/deviceregistry/jdbc/impl/CredentialsServiceImpl.java @@ -77,7 +77,7 @@ protected Future> processGet( final var secrets = result.getCredentials() .stream() .map(JsonObject::mapFrom) - .map(c -> DeviceRegistryUtils.applyAuthIdTemplate(c, tenant)) + .map(c -> DeviceRegistryUtils.applyAuthIdTemplate(c, tenant, clientContext)) .filter(filter(key.getType(), key.getAuthId())) .filter(credential -> DeviceRegistryUtils.matchesWithClientContext(credential, clientContext)) .flatMap(c -> c.getJsonArray(CredentialsConstants.FIELD_SECRETS)