diff --git a/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/api/ZenithConnectPersistenceException.java b/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/api/ZenithConnectPersistenceException.java index d2459e62d26e..1f8c2f881b21 100644 --- a/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/api/ZenithConnectPersistenceException.java +++ b/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/api/ZenithConnectPersistenceException.java @@ -22,10 +22,18 @@ package org.opennms.features.zenithconnect.persistence.api; public class ZenithConnectPersistenceException extends Exception { + private boolean attemptedToAddDuplicate; + public ZenithConnectPersistenceException() { super(); } + public ZenithConnectPersistenceException(boolean attemptedToAddDuplicate) { + super(); + + this.attemptedToAddDuplicate = attemptedToAddDuplicate; + } + public ZenithConnectPersistenceException(String message) { super(message); } @@ -33,4 +41,8 @@ public ZenithConnectPersistenceException(String message) { public ZenithConnectPersistenceException(String message, Throwable e) { super(message, e); } + + public boolean isAttemptedToAddDuplicate() { + return attemptedToAddDuplicate; + } } diff --git a/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/api/ZenithConnectPersistenceService.java b/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/api/ZenithConnectPersistenceService.java index 24ed0fe0275e..373147f2bdaf 100644 --- a/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/api/ZenithConnectPersistenceService.java +++ b/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/api/ZenithConnectPersistenceService.java @@ -32,10 +32,22 @@ public interface ZenithConnectPersistenceService { /** * Add a new registration. - * Currently we only support a single registration. + * Currently we only support a single registration, so this will replace any registration that + * already exists. * Returns the added registration, including an id and createTimeMs. + * @param preventDuplicates If true, will check to see if the given registration appears to be + * a duplicate of an existing registration (same systemId and same accessToken or refreshToken). + * If so, will throw an exception. This is to prevent e.g. the UI from sending multiple + * duplicate add requests. */ - ZenithConnectRegistration addRegistration(ZenithConnectRegistration registration) throws ZenithConnectPersistenceException; + ZenithConnectRegistration addRegistration(ZenithConnectRegistration registration, boolean preventDuplicates) + throws ZenithConnectPersistenceException; + + /** + * Add a new registration, ignoring preventDuplicates. + */ + ZenithConnectRegistration addRegistration(ZenithConnectRegistration registration) + throws ZenithConnectPersistenceException; /** * Update an existing registration. The given id and registration.id must match an existing registration. diff --git a/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/impl/ZenithConnectPersistenceServiceImpl.java b/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/impl/ZenithConnectPersistenceServiceImpl.java index 22c838a5c52e..371ba0b05b1b 100644 --- a/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/impl/ZenithConnectPersistenceServiceImpl.java +++ b/features/zenith-connect/persistence/src/main/java/org/opennms/features/zenithconnect/persistence/impl/ZenithConnectPersistenceServiceImpl.java @@ -59,7 +59,12 @@ public ZenithConnectRegistrations getRegistrations() throws ZenithConnectPersist } /** {@inheritDoc} */ - public ZenithConnectRegistration addRegistration(ZenithConnectRegistration registration) throws ZenithConnectPersistenceException { + public ZenithConnectRegistration addRegistration(ZenithConnectRegistration registration, boolean preventDuplicates) + throws ZenithConnectPersistenceException { + if (preventDuplicates && isDuplicateRegistration(registration)) { + throw new ZenithConnectPersistenceException(true); + } + registration.id = UUID.randomUUID().toString(); registration.createTimeMs = Instant.now().toEpochMilli(); @@ -69,6 +74,11 @@ public ZenithConnectRegistration addRegistration(ZenithConnectRegistration regis return registration; } + public ZenithConnectRegistration addRegistration(ZenithConnectRegistration registration) + throws ZenithConnectPersistenceException { + return addRegistration(registration, false); + } + /** {@inheritDoc} */ public void updateRegistration(String id, ZenithConnectRegistration registration) throws ZenithConnectPersistenceException { if (Strings.isNullOrEmpty(id) || @@ -136,4 +146,31 @@ private void setRegistrations(ZenithConnectRegistrations registrations) throws Z throw new ZenithConnectPersistenceException("Could not serialize Zenith Connect registrations", e); } } + + private boolean isDuplicateRegistration(ZenithConnectRegistration registration) + throws ZenithConnectPersistenceException { + ZenithConnectRegistrations existingRegistrations = getRegistrationsImpl(); + ZenithConnectRegistration existingRegistration = existingRegistrations.first(); + + if (existingRegistration == null) { + return false; + } + + boolean systemIdsMatch = registration.systemId != null && existingRegistration.systemId != null && + registration.systemId.equals(existingRegistration.systemId); + + if (systemIdsMatch) { + boolean accessTokenMatches = registration.accessToken != null && + existingRegistration.accessToken != null && + registration.accessToken.equals(existingRegistration.accessToken); + + boolean refreshTokenMatches = registration.refreshToken != null && + existingRegistration.refreshToken != null && + registration.refreshToken.equals(existingRegistration.refreshToken); + + return accessTokenMatches || refreshTokenMatches; + } + + return false; + } } diff --git a/features/zenith-connect/persistence/src/test/java/org/opennms/features/zenithconnect/persistence/impl/ZenithConnectPersistenceServiceImplIT.java b/features/zenith-connect/persistence/src/test/java/org/opennms/features/zenithconnect/persistence/impl/ZenithConnectPersistenceServiceImplIT.java index 2f0bf5a6e6da..e4e862da8f34 100644 --- a/features/zenith-connect/persistence/src/test/java/org/opennms/features/zenithconnect/persistence/impl/ZenithConnectPersistenceServiceImplIT.java +++ b/features/zenith-connect/persistence/src/test/java/org/opennms/features/zenithconnect/persistence/impl/ZenithConnectPersistenceServiceImplIT.java @@ -83,6 +83,38 @@ public void testAddAndGetRegistrations() throws ZenithConnectPersistenceExceptio assertThat(createdRegistration.createTimeMs, greaterThanOrEqualTo(currentTime)); } + @Test + public void testAddDuplicateRegistrations() throws ZenithConnectPersistenceException { + // add a registration + var registration = createDefaultRegistration(); + long currentTime = Instant.now().toEpochMilli(); + persistenceService.addRegistration(registration); + + // make sure it was added correctly + ZenithConnectRegistrations registrations = persistenceService.getRegistrations(); + assertNotNull(registrations); + + ZenithConnectRegistration createdRegistration = registrations.first(); + assertRegistrationFieldsEqual(registration, createdRegistration); + + assertThat(createdRegistration.id, not(emptyString())); + assertThat(createdRegistration.id, matchesPattern(UUID_REGEX)); + assertThat(createdRegistration.createTimeMs, greaterThanOrEqualTo(currentTime)); + + // Now add it again, should fail with specific exception and flag + var duplicate = createDefaultRegistration(); + ZenithConnectPersistenceException exception = null; + + try { + persistenceService.addRegistration(duplicate, true); + } catch (ZenithConnectPersistenceException e) { + exception = e; + } + + assertNotNull("Adding duplicate to persistenceService.addRegistration should throw a ZenithConnectPersistenceException.", exception); + assertTrue("Adding duplicate to persistenceService.addRegistration should set attemptedToAddDuplicate.", exception.isAttemptedToAddDuplicate()); + } + @Test public void testUpdateRegistration() throws ZenithConnectPersistenceException { // add a registration diff --git a/features/zenith-connect/rest/src/main/java/org/opennms/features/zenithconnect/rest/impl/DefaultZenithConnectRestService.java b/features/zenith-connect/rest/src/main/java/org/opennms/features/zenithconnect/rest/impl/DefaultZenithConnectRestService.java index b7df17a0fcb5..52de3dd69c4b 100644 --- a/features/zenith-connect/rest/src/main/java/org/opennms/features/zenithconnect/rest/impl/DefaultZenithConnectRestService.java +++ b/features/zenith-connect/rest/src/main/java/org/opennms/features/zenithconnect/rest/impl/DefaultZenithConnectRestService.java @@ -46,7 +46,11 @@ public DefaultZenithConnectRestService(ZenithConnectPersistenceService persisten this.persistenceService = persistenceService; } - /** {@inheritDoc} */ + /** + * Get all registrations. + * Currently, there is only one registration at a time, so this will return a + * ZenithConnectRegistrations object with a single object in the registrations list. + */ @Override public Response getRegistrations() { try { @@ -58,19 +62,31 @@ public Response getRegistrations() { } } - /** {@inheritDoc} */ + /** + * Add the given registration. + * This will throw a 400 Bad Request if the request is a duplicate of an existing registration. + * Throws a 500 Server Error if the request is malformed, or there was an error updating the database. + */ @Override public Response addRegistration(ZenithConnectRegistration registration) { try { - var newRegistration = persistenceService.addRegistration(registration); + var newRegistration = persistenceService.addRegistration(registration, true); return Response.status(Response.Status.CREATED).entity(newRegistration).build(); } catch (ZenithConnectPersistenceException e) { - LOG.error("Could not add registration: {}.", e.getMessage(), e); + LOG.error("Could not add registration: {}. Attempted to add duplicate: {}", + e.getMessage(), e.isAttemptedToAddDuplicate(), e); + + if (e.isAttemptedToAddDuplicate()) { + return Response.status(Response.Status.BAD_REQUEST).build(); + } + return Response.serverError().build(); } } - /** {@inheritDoc} */ + /** + * Update an existing registration. The id and registration.id must match an existing registration. + */ @Override public Response updateRegistration(String id, ZenithConnectRegistration registration) { try { diff --git a/ui/src/components/ZenithConnect/ZenithConnectRegister.vue b/ui/src/components/ZenithConnect/ZenithConnectRegister.vue index f5ca5a3bbfa0..816f8ecb1591 100644 --- a/ui/src/components/ZenithConnect/ZenithConnectRegister.vue +++ b/ui/src/components/ZenithConnect/ZenithConnectRegister.vue @@ -11,31 +11,39 @@
Result | -System ID | -Display Name | -Access Token | -Refresh Token | - - -
---|---|---|---|---|
-
-
- Success
-
-
- Failed
- |
- {{ zenithConnectStore.registerResponse?.nmsSystemId }} | -{{ zenithConnectStore.registerResponse?.nmsDisplayName }} | -
-
- {{ ellipsify(zenithConnectStore.registerResponse?.accessToken ?? '', 30) }}
-
- |
-
-
- {{ ellipsify(zenithConnectStore.registerResponse?.refreshToken ?? '', 30) }}
-
- |
-
Result | +System ID | +Display Name | +Access Token | +Refresh Token | + + +
---|---|---|---|---|
+
+
+ Success
+
+
+ Failed
+ |
+ {{ zenithConnectStore.currentRegistration?.systemId }} | +{{ zenithConnectStore.currentRegistration?.displayName }} | +
+
+ {{ ellipsify(zenithConnectStore.currentRegistration?.accessToken ?? '', 30) }}
+
+ |
+
+
+ {{ ellipsify(zenithConnectStore.currentRegistration?.refreshToken ?? '', 30) }}
+
+ |
+