Skip to content

Commit 627e926

Browse files
committed
[#2819] Prevent tenants from having empty CA array
The (partial) unique index defined on the tenant's trusted-ca array is defined using an $exists expression on the trusted-ca property. This causes tenants with an empty trusted-ca array to also be indexed. The Tenant class' trustedCertificateAuthorities field has been annotated to only be included in its JSON representation when the collection is not empty. Fixes #2819 Conflicts: site/homepage/content/release-notes.md Signed-off-by: Kai Hudalla <[email protected]>
1 parent 244646f commit 627e926

File tree

4 files changed

+86
-4
lines changed

4 files changed

+86
-4
lines changed

services/device-registry-base/src/main/java/org/eclipse/hono/service/management/tenant/Tenant.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ public class Tenant {
7878
private ResourceLimits resourceLimits;
7979

8080
@JsonProperty(RegistryManagementConstants.FIELD_TRACING)
81-
@JsonInclude(Include.NON_EMPTY)
81+
@JsonInclude(Include.NON_NULL)
8282
private TenantTracingConfig tracing;
8383

8484
@JsonProperty(RegistryManagementConstants.FIELD_PAYLOAD_TRUSTED_CA)
85-
private List<TrustedCertificateAuthority> trustedCertificateAuthorities;
85+
@JsonInclude(Include.NON_EMPTY)
86+
private List<TrustedCertificateAuthority> trustedCertificateAuthorities = List.of();
8687

8788
@JsonProperty(RegistryManagementConstants.FIELD_REGISTRATION_LIMITS)
8889
@JsonInclude(Include.NON_DEFAULT)
@@ -118,7 +119,7 @@ public Tenant(final Tenant other) {
118119
this.registrationLimits = other.registrationLimits;
119120
this.tracing = other.tracing;
120121
if (Objects.nonNull(other.trustedCertificateAuthorities)) {
121-
this.trustedCertificateAuthorities = new ArrayList<>(other.trustedCertificateAuthorities);
122+
this.trustedCertificateAuthorities = List.copyOf(other.trustedCertificateAuthorities);
122123
}
123124
}
124125

@@ -399,7 +400,7 @@ public List<TrustedCertificateAuthority> getTrustedCertificateAuthorities() {
399400
*/
400401
public Tenant setTrustedCertificateAuthorities(final List<TrustedCertificateAuthority> trustedCertificateAuthorities) {
401402
if (trustedCertificateAuthorities != null) {
402-
this.trustedCertificateAuthorities = Collections.unmodifiableList(trustedCertificateAuthorities);
403+
this.trustedCertificateAuthorities = List.copyOf(trustedCertificateAuthorities);
403404
}
404405
return this;
405406
}

services/device-registry-base/src/test/java/org/eclipse/hono/service/tenant/AbstractTenantServiceTest.java

+46
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,52 @@ default void testUpdateTenantFailsForDuplicateCa(final VertxTestContext ctx) {
563563
}));
564564
}
565565

566+
/**
567+
* Verifies that setting an empty list of trusted CAs on multiple tenants does not result in a unique key
568+
* violation.
569+
*
570+
* @param ctx The vert.x test context.
571+
*/
572+
@Test
573+
default void testUpdateTenantPreventsEmptyCaArray(final VertxTestContext ctx) {
574+
575+
final var trustedCaOne = new TrustedCertificateAuthority();
576+
trustedCaOne.setSubjectDn("CN=one");
577+
trustedCaOne.setPublicKey("NOTAKEY".getBytes(StandardCharsets.UTF_8));
578+
579+
final var trustedCaTwo = new TrustedCertificateAuthority();
580+
trustedCaTwo.setSubjectDn("CN=two");
581+
trustedCaTwo.setPublicKey("NOTAKEY".getBytes(StandardCharsets.UTF_8));
582+
583+
// GIVEN two tenants with one CA configured each
584+
addTenant("tenantOne", new Tenant().setTrustedCertificateAuthorities(List.of(trustedCaOne)))
585+
.onFailure(ctx::failNow)
586+
.compose(ok -> addTenant("tenantTwo", new Tenant().setTrustedCertificateAuthorities(List.of(trustedCaTwo))))
587+
.onFailure(ctx::failNow)
588+
.compose(ok -> {
589+
// WHEN setting an empty list of trusted CAs on the first tenant
590+
final var updatedTenantOne = new Tenant().setTrustedCertificateAuthorities(List.of());
591+
return getTenantManagementService().updateTenant(
592+
"tenantOne",
593+
updatedTenantOne,
594+
Optional.empty(),
595+
NoopSpan.INSTANCE);
596+
})
597+
// THEN the update succeeds
598+
.onFailure(ctx::failNow)
599+
.compose(ok -> {
600+
// and WHEN setting an empty list of trusted CAs on the second tenant
601+
final var updatedTenantTwo = new Tenant().setTrustedCertificateAuthorities(List.of());
602+
return getTenantManagementService().updateTenant(
603+
"tenantTwo",
604+
updatedTenantTwo,
605+
Optional.empty(),
606+
NoopSpan.INSTANCE);
607+
})
608+
// THEN the update succeeds as well
609+
.onComplete(ctx.completing());
610+
}
611+
566612
/**
567613
* Verifies that a tenant is registered.
568614
*

site/homepage/content/release-notes.md

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ description = "Information about changes in recent Hono releases. Includes new f
2020
* The device registry implementations did not return a JSON object in a response to a failed request as specified
2121
in the Device Registry Management API. This has been fixed.
2222
* The tracing output in error scenarios has been improved in the Mongo DB based device registry.
23+
* The MongoDB based registry erroneously rejected requests that would result in multiple tenants having an empty
24+
set of trusted CAs. This has been fixed.
2325

2426
## 1.9.0
2527

tests/src/test/java/org/eclipse/hono/tests/registry/TenantManagementIT.java

+33
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,39 @@ public void testUpdateTenantSucceedsForConfigurationWithMissingTrustAnchorIds(fi
426426
}));
427427
}
428428

429+
/**
430+
* Verifies that setting an empty list of trusted CAs on multiple tenants does not result in a unique key
431+
* violation.
432+
*
433+
* @param context The Vert.x test context.
434+
*/
435+
@Test
436+
public void testUpdateTenantPreventsEmptyCaArray(final VertxTestContext context) {
437+
438+
final var tenantTwoId = getHelper().getRandomTenantId();
439+
final PublicKey publicKey = TenantApiTests.getRandomPublicKey();
440+
final TrustedCertificateAuthority trustAnchor1 = Tenants
441+
.createTrustAnchor("test-ca", "CN=test-dn-1", publicKey.getEncoded(), publicKey.getAlgorithm(),
442+
Instant.now(), Instant.now().plus(365, ChronoUnit.DAYS));
443+
final TrustedCertificateAuthority trustAnchor2 = Tenants
444+
.createTrustAnchor(null, "CN=test-dn-2", publicKey.getEncoded(), publicKey.getAlgorithm(),
445+
Instant.now(), Instant.now().plus(365, ChronoUnit.DAYS));
446+
447+
getHelper().registry.addTenant(tenantId, new Tenant().setTrustedCertificateAuthorities(List.of(trustAnchor1)))
448+
.compose(ok -> getHelper().registry.addTenant(
449+
tenantTwoId,
450+
new Tenant().setTrustedCertificateAuthorities(List.of(trustAnchor2))))
451+
.compose(ok -> getHelper().registry.updateTenant(
452+
tenantId,
453+
new Tenant().setTrustedCertificateAuthorities(List.of()),
454+
HttpURLConnection.HTTP_NO_CONTENT))
455+
.compose(ok -> getHelper().registry.updateTenant(
456+
tenantTwoId,
457+
new Tenant().setTrustedCertificateAuthorities(List.of()),
458+
HttpURLConnection.HTTP_NO_CONTENT))
459+
.onComplete(context.completing());
460+
}
461+
429462
/**
430463
* Verifies that the service rejects an update request for a non-existing tenant.
431464
*

0 commit comments

Comments
 (0)