Skip to content

Commit

Permalink
Implement the callback for the SignalR connection state. Fix some bug…
Browse files Browse the repository at this point in the history
…s around connecting to the A2A signalr service and mark the A2A Persist event listener APIs that take a thumbprint as not supported. Connecting and authenticating to the A2A signalr service is handled differently than signalr on the core service. The core service authenticates using an access token via restsharp which is able to use the windows certificate store. It then uses Okhttp with the access token to connect to the signalr service. Connecting to the A2A signalr service is done entirely using okhttp and passing a certificate. Okhttp can't pull a certificate out of the windows certificate store by thumbprint. The problem is difference in the authentication methods when using Okhttp.
  • Loading branch information
bnicholesdell committed Jul 20, 2023
1 parent 61366e4 commit 3c818e6
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1887,6 +1887,8 @@ public static ISafeguardEventListener getPersistentA2AEventListener(List<char[]>
}

/**
* NOT SUPPORTED
*
* Get a persistent A2A event listener. The handler passed in
* will be registered for the AssetAccountPasswordUpdated event,
* which is the only one supported in A2A. Uses a client
Expand All @@ -1913,6 +1915,9 @@ public static ISafeguardEventListener getPersistentA2AEventListener(List<char[]>
HostnameVerifier validationCallback, Integer apiVersion)
throws ObjectDisposedException, ArgumentException, SafeguardForJavaException {

throw new SafeguardForJavaException("Not supported. This function is not available for Java.");

/*
int version = DEFAULTAPIVERSION;
if (apiVersion != null) {
version = apiVersion;
Expand All @@ -1930,9 +1935,12 @@ public static ISafeguardEventListener getPersistentA2AEventListener(List<char[]>
return new PersistentSafeguardA2AEventListener(
new SafeguardA2AContext(networkAddress, version, false,
thumbprint, validationCallback), apiKeys, handler);
*/
}

/**
* NOT SUPPORTED
*
* Get a persistent A2A event listener. The handler passed in
* will be registered for the AssetAccountPasswordUpdated event,
* which is the only one supported in A2A. Uses a client
Expand All @@ -1959,6 +1967,9 @@ public static ISafeguardEventListener getPersistentA2AEventListener(List<char[]>
Integer apiVersion, Boolean ignoreSsl)
throws ObjectDisposedException, ArgumentException, SafeguardForJavaException {

throw new SafeguardForJavaException("Not supported. This function is not available for Java.");

/*
int version = DEFAULTAPIVERSION;
if (apiVersion != null) {
version = apiVersion;
Expand All @@ -1981,6 +1992,7 @@ public static ISafeguardEventListener getPersistentA2AEventListener(List<char[]>
return new PersistentSafeguardA2AEventListener(
new SafeguardA2AContext(networkAddress, version, ignoreSsl,
thumbprint, null), apiKeys, handler);
*/
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.oneidentity.safeguard.safeguardjava.data;

/** <summary>
* Connection state of the Safeguard event listener.
*/
public enum SafeguardEventListenerState
{
/**
* Event listener connected.
*/
Connected,
/**
* Event listener disconnected.
*/
Disconnected
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ public interface ISafeguardEventListener
*/
void registerEventHandler(String eventName, ISafeguardEventHandler handler) throws ObjectDisposedException;

/**
* Set an event listener callback that will be called each time the connection
* state changes of the event listener.
*
* @param eventListenerStateCallback Callback method.
*/
void SetEventListenerStateCallback(ISafeguardEventListenerStateCallback eventListenerStateCallback);

/**
* Start listening for Safeguard events in a background thread.
* @throws ObjectDisposedException Object has already been disposed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.oneidentity.safeguard.safeguardjava.event;

import com.oneidentity.safeguard.safeguardjava.data.SafeguardEventListenerState;

/**
* A callback that will be called whenever the event listener connection state Changes.
*/
public interface ISafeguardEventListenerStateCallback {
/**
* Handles an incoming event listener connection change.
*
* @param eventListenerState New connection state of the event listener.
*/
void onEventListenerStateChange(SafeguardEventListenerState eventListenerState);
}

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public abstract class PersistentSafeguardEventListenerBase implements ISafeguard

private SafeguardEventListener eventListener;
private final EventHandlerRegistry eventHandlerRegistry = new EventHandlerRegistry();
private ISafeguardEventListenerStateCallback eventListenerStateCallback;

private Thread reconnectThread = null;
boolean isCancellationRequested = false;
Expand All @@ -28,6 +29,12 @@ public void registerEventHandler(String eventName, ISafeguardEventHandler handle
this.eventHandlerRegistry.registerEventHandler(eventName, handler);
}

@Override
public void SetEventListenerStateCallback(ISafeguardEventListenerStateCallback eventListenerStateCallback)
{
this.eventListenerStateCallback = eventListenerStateCallback;
}

protected abstract SafeguardEventListener reconnectEventListener() throws ObjectDisposedException, SafeguardForJavaException, ArgumentException;

class PersistentReconnectAndStartHandler implements IDisconnectHandler {
Expand Down Expand Up @@ -56,6 +63,7 @@ public void run() {
"Attempting to connect and start internal event listener.");
eventListener = reconnectEventListener();
eventListener.setEventHandlerRegistry(eventHandlerRegistry);
eventListener.SetEventListenerStateCallback(eventListenerStateCallback);
eventListener.start();
eventListener.setDisconnectHandler(new PersistentReconnectAndStartHandler());
break;
Expand All @@ -67,6 +75,7 @@ public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException ex1) {
isCancellationRequested = true;
}
}
}
Expand All @@ -78,11 +87,10 @@ public void run() {
this.reconnectThread.start();
this.reconnectThread.join();
} catch (InterruptedException ex1) {
isCancellationRequested = true;
}

if (isCancellationRequested)
this.reconnectThread = null;

this.reconnectThread = null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import com.microsoft.signalr.Action1;
import com.microsoft.signalr.OnClosedCallback;
import com.oneidentity.safeguard.safeguardjava.data.CertificateContext;
import com.oneidentity.safeguard.safeguardjava.data.SafeguardEventListenerState;
import com.oneidentity.safeguard.safeguardjava.exceptions.ArgumentException;
import com.oneidentity.safeguard.safeguardjava.exceptions.ObjectDisposedException;
import com.oneidentity.safeguard.safeguardjava.exceptions.SafeguardEventListenerDisconnectedException;
import com.oneidentity.safeguard.safeguardjava.exceptions.SafeguardForJavaException;
import io.reactivex.rxjava3.core.Single;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
Expand Down Expand Up @@ -57,6 +59,7 @@ public class SafeguardEventListener implements ISafeguardEventListener, AutoClos

private EventHandlerRegistry eventHandlerRegistry;
private IDisconnectHandler disconnectHandler;
private ISafeguardEventListenerStateCallback eventListenerStateCallback;

private HubConnection signalrConnection = null;
private static final String NOTIFICATION_HUB = "signalr";
Expand Down Expand Up @@ -107,8 +110,8 @@ public SafeguardEventListener(String eventUrl, String clientCertificatePath, cha
this.clientCertificate = new CertificateContext(certificateAlias, clientCertificatePath, null, certificatePassword);
this.apiKeys = new ArrayList<>();
for (char[] key : apiKeys)
apiKeys.add(key.clone());
if (apiKeys.isEmpty())
this.apiKeys.add(key.clone());
if (this.apiKeys.isEmpty())
throw new ArgumentException("The apiKeys parameter must include at least one item");
}

Expand All @@ -122,8 +125,8 @@ public SafeguardEventListener(String eventUrl, CertificateContext clientCertific
this.clientCertificate = clientCertificate.cloneObject();
this.apiKeys = new ArrayList<>();
for (char[] key : apiKeys)
apiKeys.add(key.clone());
if (apiKeys.isEmpty())
this.apiKeys.add(key.clone());
if (this.apiKeys.isEmpty())
throw new ArgumentException("The apiKeys parameter must include at least one item");
}

Expand All @@ -149,6 +152,12 @@ public void registerEventHandler(String eventName, ISafeguardEventHandler handle
}
eventHandlerRegistry.registerEventHandler(eventName, handler);
}

@Override
public void SetEventListenerStateCallback(ISafeguardEventListenerStateCallback eventListenerStateCallback)
{
this.eventListenerStateCallback = eventListenerStateCallback;
}

@Override
public void start() throws ObjectDisposedException, SafeguardForJavaException, SafeguardEventListenerDisconnectedException {
Expand Down Expand Up @@ -185,6 +194,7 @@ public void invoke(Exception exception){
// Start the connection
try{
signalrConnection.start().blockingAwait();
CallEventListenerStateCallback(SafeguardEventListenerState.Connected);
}
catch(Exception error) {
throw new SafeguardForJavaException(String.format("Failed to start signalr connection: %s", error.getMessage()), error);
Expand Down Expand Up @@ -252,8 +262,22 @@ private void handleDisconnect() throws SafeguardEventListenerDisconnectedExcepti
return;
}
Logger.getLogger(EventHandlerRegistry.class.getName()).log(Level.WARNING, "SignalR disconnect detected, calling handler...");
CallEventListenerStateCallback(SafeguardEventListenerState.Disconnected);
disconnectHandler.func();
}

private void CallEventListenerStateCallback(SafeguardEventListenerState newState)
{
if (eventListenerStateCallback != null) {
try {
eventListenerStateCallback.onEventListenerStateChange(newState);
}
catch(Exception e)
{
// Just in case the user's callback function throws an exception.
}
}
}

private void cleanupConnection() {
try {
Expand Down Expand Up @@ -306,11 +330,15 @@ private void ConfigureHttpClientBuilder(Builder builder)
}

KeyManager[] km = null;
if(clientCertificate != null){
if(clientCertificate != null &&
(clientCertificate.getCertificateData() != null || clientCertificate.getCertificatePath() != null)){

// If we have a client certificate, set it into the KeyStore/KeyManager
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream inputStream = new ByteArrayInputStream(clientCertificate.getCertificateData());
InputStream inputStream = clientCertificate.getCertificatePath() != null ? new FileInputStream(clientCertificate.getCertificatePath())
: new ByteArrayInputStream(clientCertificate.getCertificateData());

keyStore.load(inputStream, clientCertificate.getCertificatePassword());

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static void main(String[] args) {
ISafeguardSessionsConnection sessionConnection = null;
ISafeguardA2AContext a2aContext = null;
ISafeguardEventListener eventListener = null;
ISafeguardEventListener a2aEventListener = null;

boolean done = false;
SafeguardTests tests = new SafeguardTests();
Expand Down Expand Up @@ -97,39 +98,55 @@ public static void main(String[] args) {
tests.safeguardTestEventListener(eventListener);
break;
case 19:
eventListener = tests.safeguardDisconnectEventListener(eventListener);
a2aEventListener = tests.safeguardA2AEventListenerByCertificate();
break;
case 20:
tests.safeguardListBackups(connection);
a2aEventListener = tests.safeguardA2AEventListenerByThumbprint();
break;
case 21:
tests.safeguardTestBackupDownload(connection);
tests.safeguardTestA2AEventListener(a2aEventListener);
break;
case 22:
tests.safeguardTestBackupUpload(connection);
{
if (eventListener != null) {
eventListener = tests.safeguardDisconnectEventListener(eventListener);
}
if (a2aEventListener != null) {
a2aEventListener = tests.safeguardDisconnectEventListener(a2aEventListener);
}
}
break;
case 23:
sessionConnection = tests.safeguardSessionsConnection();
tests.safeguardListBackups(connection);
break;
case 24:
tests.safeguardSessionsApi(sessionConnection);
tests.safeguardTestBackupDownload(connection);
break;
case 25:
tests.safeguardSessionsFileUpload(sessionConnection);
tests.safeguardTestBackupUpload(connection);
break;
case 26:
tests.safeguardSessionsStreamUpload(sessionConnection);
sessionConnection = tests.safeguardSessionsConnection();
break;
case 27:
tests.safeguardSessionTestRecordingDownload(sessionConnection);
tests.safeguardSessionsApi(sessionConnection);
break;
case 28:
tests.safeguardTestJoinSps(connection, sessionConnection);
tests.safeguardSessionsFileUpload(sessionConnection);
break;
case 29:
tests.safeguardTestManagementConnection(connection);
tests.safeguardSessionsStreamUpload(sessionConnection);
break;
case 30:
tests.safeguardSessionTestRecordingDownload(sessionConnection);
break;
case 31:
tests.safeguardTestJoinSps(connection, sessionConnection);
break;
case 32:
tests.safeguardTestManagementConnection(connection);
break;
case 33:
tests.safeguardTestAnonymousConnection(connection);
break;
default:
Expand Down Expand Up @@ -168,18 +185,21 @@ private static Integer displayMenu() {
System.out.println ("\t16. Event Listener by keystore");
System.out.println ("\t17. Event Listener by thumbprint");
System.out.println ("\t18. Test Event Listener");
System.out.println ("\t19. Disconnect Event Listener");
System.out.println ("\t20. Test List Backups");
System.out.println ("\t21. Test Download Backup File");
System.out.println ("\t22. Test Upload Backup File");
System.out.println ("\t23. Test SPS Connection");
System.out.println ("\t24. Test SPS API");
System.out.println ("\t25. Test SPS Firmware Upload");
System.out.println ("\t26. Test Stream Upload");
System.out.println ("\t27. Test Session Recording Download");
System.out.println ("\t28. Test Join SPS");
System.out.println ("\t29. Test Management Interface API");
System.out.println ("\t30. Test Anonymous Connection");
System.out.println ("\t19. A2A Event Listener by certificate file");
System.out.println ("\t20. A2A Event Listener by thumbprint");
System.out.println ("\t21. Test A2A Event Listener");
System.out.println ("\t22. Disconnect Event Listener");
System.out.println ("\t23. Test List Backups");
System.out.println ("\t24. Test Download Backup File");
System.out.println ("\t25. Test Upload Backup File");
System.out.println ("\t26. Test SPS Connection");
System.out.println ("\t27. Test SPS API");
System.out.println ("\t28. Test SPS Firmware Upload");
System.out.println ("\t29. Test Stream Upload");
System.out.println ("\t30. Test Session Recording Download");
System.out.println ("\t31. Test Join SPS");
System.out.println ("\t32. Test Management Interface API");
System.out.println ("\t33. Test Anonymous Connection");

System.out.println ("\t99. Exit");

Expand Down
Loading

0 comments on commit 3c818e6

Please sign in to comment.