diff --git a/src/main/java/com/oneidentity/safeguard/safeguardjava/ISafeguardConnection.java b/src/main/java/com/oneidentity/safeguard/safeguardjava/ISafeguardConnection.java index 5d23049..9fbe0d7 100644 --- a/src/main/java/com/oneidentity/safeguard/safeguardjava/ISafeguardConnection.java +++ b/src/main/java/com/oneidentity/safeguard/safeguardjava/ISafeguardConnection.java @@ -149,6 +149,17 @@ FullResponse JoinSps(ISafeguardSessionsConnection spsConnection, String certific */ ISafeguardEventListener getPersistentEventListener() throws ObjectDisposedException, SafeguardForJavaException; + /** + * Returns a Safeguard API connection tailored to work with the Safeguard management service + * service. To invoke methods on the Management service, call this method first and use the + * returned ISafeguardConnection. Applicable options such as SSL validation will be inherited + * from the original ISafeguardConnection. + * + * @param networkAddress Network address. + * @return Reusable Safeguard API connection. + */ + ISafeguardConnection GetManagementServiceConnection(String networkAddress); + /** * Call Safeguard API to invalidate current access token and clear its value from * the connection. In order to continue using the connection you will need to call diff --git a/src/main/java/com/oneidentity/safeguard/safeguardjava/PersistentSafeguardConnection.java b/src/main/java/com/oneidentity/safeguard/safeguardjava/PersistentSafeguardConnection.java index d782b2f..50c432b 100644 --- a/src/main/java/com/oneidentity/safeguard/safeguardjava/PersistentSafeguardConnection.java +++ b/src/main/java/com/oneidentity/safeguard/safeguardjava/PersistentSafeguardConnection.java @@ -72,6 +72,11 @@ public String invokeMethodCsv(Service service, Method method, String relativeUrl public SafeguardEventListener getEventListener() throws ObjectDisposedException, ArgumentException { return _connection.getEventListener(); } + + @Override + public ISafeguardConnection GetManagementServiceConnection(String networkAddress) { + return _connection.GetManagementServiceConnection(networkAddress); + } @Override public ISafeguardEventListener getPersistentEventListener() throws ObjectDisposedException, SafeguardForJavaException { diff --git a/src/main/java/com/oneidentity/safeguard/safeguardjava/SafeguardConnection.java b/src/main/java/com/oneidentity/safeguard/safeguardjava/SafeguardConnection.java index 89d9851..21dae62 100644 --- a/src/main/java/com/oneidentity/safeguard/safeguardjava/SafeguardConnection.java +++ b/src/main/java/com/oneidentity/safeguard/safeguardjava/SafeguardConnection.java @@ -200,6 +200,11 @@ public ISafeguardEventListener getPersistentEventListener() throw new SafeguardForJavaException("Unable to create persistent event listener from " + this.authenticationMechanism.getClass().getName()); } + @Override + public ISafeguardConnection GetManagementServiceConnection(String networkAddress) { + return new SafeguardManagementServiceConnection(authenticationMechanism, networkAddress); + } + @Override public void logOut() throws ObjectDisposedException { @@ -238,7 +243,7 @@ static void logResponseDetails(FullResponse fullResponse) Logger.getLogger(SafeguardConnection.class.getName()).log(Level.FINEST, " Body size: {0}", msg); } - RestClient getClientForService(Service service) throws SafeguardForJavaException { + protected RestClient getClientForService(Service service) throws SafeguardForJavaException { switch (service) { case Core: return coreClient; diff --git a/src/main/java/com/oneidentity/safeguard/safeguardjava/SafeguardManagementServiceConnection.java b/src/main/java/com/oneidentity/safeguard/safeguardjava/SafeguardManagementServiceConnection.java new file mode 100644 index 0000000..f3764df --- /dev/null +++ b/src/main/java/com/oneidentity/safeguard/safeguardjava/SafeguardManagementServiceConnection.java @@ -0,0 +1,49 @@ +package com.oneidentity.safeguard.safeguardjava; + +import com.oneidentity.safeguard.safeguardjava.authentication.IAuthenticationMechanism; +import com.oneidentity.safeguard.safeguardjava.authentication.ManagementServiceAuthenticator; +import com.oneidentity.safeguard.safeguardjava.data.FullResponse; +import com.oneidentity.safeguard.safeguardjava.data.Service; +import com.oneidentity.safeguard.safeguardjava.event.ISafeguardEventListener; +import com.oneidentity.safeguard.safeguardjava.exceptions.SafeguardForJavaException; +import com.oneidentity.safeguard.safeguardjava.restclient.RestClient; + +class SafeguardManagementServiceConnection extends SafeguardConnection { + + private final IAuthenticationMechanism authenticationMechanism; + + private final RestClient managementClient; + + public SafeguardManagementServiceConnection(IAuthenticationMechanism parentAuthenticationMechanism, String networkAddress) { + + super(parentAuthenticationMechanism); + authenticationMechanism = new ManagementServiceAuthenticator(parentAuthenticationMechanism, networkAddress); + + String safeguardManagementUrl = String.format("https://%s/service/management/v%d", + this.authenticationMechanism.getNetworkAddress(), this.authenticationMechanism.getApiVersion()); + managementClient = new RestClient(safeguardManagementUrl, authenticationMechanism.isIgnoreSsl(), authenticationMechanism.getValidationCallback()); + } + + public FullResponse JoinSps(ISafeguardSessionsConnection spsConnection, String certificateChain, String sppAddress) + throws SafeguardForJavaException { + throw new SafeguardForJavaException("Management connection cannot be used to join SPS."); + } + + public ISafeguardEventListener GetEventListener() throws SafeguardForJavaException { + throw new SafeguardForJavaException("Management connection does not support event listeners."); + } + + public ISafeguardEventListener GetPersistentEventListener() throws SafeguardForJavaException { + throw new SafeguardForJavaException("Management connection does not support event listeners."); + } + + @Override + protected RestClient getClientForService(Service service) throws SafeguardForJavaException { + + if (service == Service.Management) { + return managementClient; + } + throw new SafeguardForJavaException(String.format("%s service cannot be invoked with a management connection.", service.name())); + } + +} diff --git a/src/main/java/com/oneidentity/safeguard/safeguardjava/authentication/AnonymousAuthenticator.java b/src/main/java/com/oneidentity/safeguard/safeguardjava/authentication/AnonymousAuthenticator.java index 3951f51..d77ea39 100644 --- a/src/main/java/com/oneidentity/safeguard/safeguardjava/authentication/AnonymousAuthenticator.java +++ b/src/main/java/com/oneidentity/safeguard/safeguardjava/authentication/AnonymousAuthenticator.java @@ -53,7 +53,7 @@ protected char[] getRstsTokenInternal() throws SafeguardForJavaException { @Override public boolean hasAccessToken() { - return true; + return false; } @Override diff --git a/src/main/java/com/oneidentity/safeguard/safeguardjava/authentication/ManagementServiceAuthenticator.java b/src/main/java/com/oneidentity/safeguard/safeguardjava/authentication/ManagementServiceAuthenticator.java new file mode 100644 index 0000000..446849d --- /dev/null +++ b/src/main/java/com/oneidentity/safeguard/safeguardjava/authentication/ManagementServiceAuthenticator.java @@ -0,0 +1,102 @@ +package com.oneidentity.safeguard.safeguardjava.authentication; + +import com.oneidentity.safeguard.safeguardjava.exceptions.ObjectDisposedException; +import com.oneidentity.safeguard.safeguardjava.exceptions.SafeguardForJavaException; +import javax.net.ssl.HostnameVerifier; + +public class ManagementServiceAuthenticator implements IAuthenticationMechanism { + + private boolean disposed; + + private final String networkAddress; + private final int apiVersion; + private final boolean ignoreSsl; + private final HostnameVerifier validationCallback; + + + public ManagementServiceAuthenticator(IAuthenticationMechanism parentAuthenticationMechanism, String networkAddress) { + this.apiVersion = parentAuthenticationMechanism.getApiVersion(); + this.ignoreSsl = parentAuthenticationMechanism.isIgnoreSsl(); + this.validationCallback = parentAuthenticationMechanism.getValidationCallback(); + this.networkAddress = networkAddress; + } + + @Override + public String getId() { + return "Management"; + } + + @Override + public String getNetworkAddress() { + return networkAddress; + } + + @Override + public int getApiVersion() { + return apiVersion; + } + + @Override + public boolean isIgnoreSsl() { + return ignoreSsl; + } + + @Override + public HostnameVerifier getValidationCallback() { + return validationCallback; + } + + @Override + public boolean isAnonymous() { + return true; + } + + @Override + public boolean hasAccessToken() { + return false; + } + + @Override + public void clearAccessToken() { + // There is no access token for anonymous auth + } + + @Override + public char[] getAccessToken() throws ObjectDisposedException { + return null; + } + + @Override + public int getAccessTokenLifetimeRemaining() throws ObjectDisposedException, SafeguardForJavaException { + return 0; + } + + @Override + public void refreshAccessToken() throws ObjectDisposedException, SafeguardForJavaException { + throw new SafeguardForJavaException("Anonymous connection cannot be used to get an API access token, Error: Unsupported operation"); + } + + @Override + public String resolveProviderToScope(String provider) throws SafeguardForJavaException { + throw new SafeguardForJavaException("Anonymous connection does not require a provider, Error: Unsupported operation"); + } + + @Override + public Object cloneObject() throws SafeguardForJavaException { + throw new SafeguardForJavaException("Anonymous authenticators are not cloneable"); + } + + @Override + public void dispose() { + disposed = true; + } + + @Override + protected void finalize() throws Throwable { + try { + } finally { + disposed = true; + super.finalize(); + } + } +} diff --git a/src/main/java/com/oneidentity/safeguard/safeguardjava/data/Service.java b/src/main/java/com/oneidentity/safeguard/safeguardjava/data/Service.java index 81dae7b..54e5424 100644 --- a/src/main/java/com/oneidentity/safeguard/safeguardjava/data/Service.java +++ b/src/main/java/com/oneidentity/safeguard/safeguardjava/data/Service.java @@ -22,5 +22,12 @@ public enum Service { /** * The a2a service contains application integration Safeguard operations. It is called via the Safeguard.A2A class. */ - A2A + A2A, + + /** + * The Management service contains unauthenticated endpoints for disaster-recovery and support operations. On hardware + * it is bound to the MGMT network interface. For on-prem VM it is unavailable except through the Kiosk app. On cloud + * VM it is listening on port 9337 and should be firewalled appropriately to restrict access. + */ + Management } diff --git a/src/main/java/com/oneidentity/safeguard/safeguardjava/restclient/RestClient.java b/src/main/java/com/oneidentity/safeguard/safeguardjava/restclient/RestClient.java index 9e93f50..eec42c8 100644 --- a/src/main/java/com/oneidentity/safeguard/safeguardjava/restclient/RestClient.java +++ b/src/main/java/com/oneidentity/safeguard/safeguardjava/restclient/RestClient.java @@ -260,7 +260,8 @@ public CloseableHttpResponse execPUT(String path, Map queryParam RequestBuilder rb = prepareRequest(RequestBuilder.put(getBaseURI(path)), queryParams, headers, timeout); try { - rb.setEntity(new StringEntity(requestEntity.toJson())); + String body = requestEntity.toJson(); + rb.setEntity(new StringEntity(body == null ? "{}" : body)); CloseableHttpResponse r = client.execute(rb.build()); return r; } catch (Exception ex) { @@ -273,7 +274,8 @@ public CloseableHttpResponse execPOST(String path, Map queryPara RequestBuilder rb = prepareRequest(RequestBuilder.post(getBaseURI(path)), queryParams, headers, timeout); try { - rb.setEntity(new StringEntity(requestEntity.toJson())); + String body = requestEntity.toJson(); + rb.setEntity(new StringEntity(body == null ? "{}" : body)); CloseableHttpResponse r = client.execute(rb.build()); return r; } catch (Exception ex) { @@ -290,7 +292,8 @@ public CloseableHttpResponse execPOST(String path, Map queryPara RequestBuilder rb = prepareRequest(RequestBuilder.post(getBaseURI(path)), queryParams, headers, timeout); try { - rb.setEntity(new StringEntity(requestEntity.toJson())); + String body = requestEntity.toJson(); + rb.setEntity(new StringEntity(body == null ? "{}" : body)); CloseableHttpResponse r = certClient.execute(rb.build()); return r; } catch (IOException ex) { diff --git a/tests/safeguardjavaclient/pom.xml b/tests/safeguardjavaclient/pom.xml index 349c5c2..a015912 100644 --- a/tests/safeguardjavaclient/pom.xml +++ b/tests/safeguardjavaclient/pom.xml @@ -23,7 +23,7 @@ com.oneidentity.safeguard safeguardjava - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/tests/safeguardjavaclient/src/main/java/com/oneidentity/safeguard/safeguardclient/SafeguardJavaClient.java b/tests/safeguardjavaclient/src/main/java/com/oneidentity/safeguard/safeguardclient/SafeguardJavaClient.java index be15c42..99a3ebd 100644 --- a/tests/safeguardjavaclient/src/main/java/com/oneidentity/safeguard/safeguardclient/SafeguardJavaClient.java +++ b/tests/safeguardjavaclient/src/main/java/com/oneidentity/safeguard/safeguardclient/SafeguardJavaClient.java @@ -100,6 +100,12 @@ public static void main(String[] args) { case 23: tests.safeguardSessionsApi(sessionConnection); break; + case 24: + tests.safeguardTestManagementConnection(connection); + break; + case 25: + tests.safeguardTestAnonymousConnection(connection); + break; default: done = true; break; @@ -135,6 +141,8 @@ private static Integer displayMenu() { System.out.println ("\t21. Test Upload Backup File"); System.out.println ("\t22. Test SPS Connection"); System.out.println ("\t23. Test SPS API"); + System.out.println ("\t24. Test Management Interface API"); + System.out.println ("\t25. Test Anonymous Connection"); System.out.println ("\t99. Exit"); diff --git a/tests/safeguardjavaclient/src/main/java/com/oneidentity/safeguard/safeguardclient/SafeguardTests.java b/tests/safeguardjavaclient/src/main/java/com/oneidentity/safeguard/safeguardclient/SafeguardTests.java index 6f3573f..422bc63 100644 --- a/tests/safeguardjavaclient/src/main/java/com/oneidentity/safeguard/safeguardclient/SafeguardTests.java +++ b/tests/safeguardjavaclient/src/main/java/com/oneidentity/safeguard/safeguardclient/SafeguardTests.java @@ -22,12 +22,22 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.stream.Collectors; public class SafeguardTests { public SafeguardTests() { } + private void logResponseDetails(FullResponse fullResponse) + { + System.out.println(String.format("\t\tReponse status code: %d", fullResponse.getStatusCode())); + String msg = fullResponse.getHeaders() == null ? "None" : fullResponse.getHeaders().stream().map(header -> header.getName() + "=" + header.getValue()).collect(Collectors.joining(", ", "{", "}")); + System.out.println(String.format("\t\tResponse headers: %s", msg)); + msg = (fullResponse.getBody() == null) || (fullResponse.getBody().trim().length() == 0) ? "No-Content" : fullResponse.getBody(); + System.out.println(String.format("\t\tBody: %s", msg)); + } + public ISafeguardConnection safeguardConnectByUserPassword() { String address = readLine("SPP address: ", null); @@ -195,15 +205,21 @@ public void safeguardTestConnection(ISafeguardConnection connection) { FullResponse fullResponse = connection.invokeMethodFull(Service.Core, Method.Get, "Users", null, null, null, null); System.out.println(String.format("\t\\Users full response:")); - System.out.println(fullResponse.toString()); + logResponseDetails(fullResponse); + + fullResponse = connection.invokeMethodFull(Service.Core, Method.Post, "Events/FireTestEvent", null, null, null, null); + System.out.println(String.format("\t\\FireTestEvent response:")); + logResponseDetails(fullResponse); - response = connection.invokeMethod(Service.Notification, Method.Get, "Status", null, null, null, null); + fullResponse = connection.invokeMethodFull(Service.Notification, Method.Get, "Status", null, null, null, null); System.out.println(String.format("\t\\Appliance status:")); - System.out.println(response); + logResponseDetails(fullResponse); - } catch (ObjectDisposedException | SafeguardForJavaException ex) { - System.out.println("\t[ERROR]Test connection failed: " + ex.getMessage()); - } catch (Exception ex) { + fullResponse = connection.invokeMethodFull(Service.Appliance, Method.Get, "NetworkInterfaces", null, null, null, null); + System.out.println(String.format("\t\\NetworkInterfaces response:")); + logResponseDetails(fullResponse); + + } catch (ArgumentException | ObjectDisposedException | SafeguardForJavaException ex) { System.out.println("\t[ERROR]Test connection failed: " + ex.getMessage()); } } @@ -586,13 +602,50 @@ void safeguardSessionsApi(ISafeguardSessionsConnection connection) { try { FullResponse fullResponse = connection.InvokeMethodFull(Method.Get, "firmware", null); System.out.println(String.format("\t\\Users full response:")); - System.out.println(fullResponse.getBody()); + logResponseDetails(fullResponse); - } catch (ObjectDisposedException | SafeguardForJavaException ex) { - System.out.println("\t[ERROR]Test connection failed: " + ex.getMessage()); - } catch (Exception ex) { + } catch (ArgumentException | ObjectDisposedException | SafeguardForJavaException ex) { System.out.println("\t[ERROR]Test connection failed: " + ex.getMessage()); } } + + void safeguardTestManagementConnection(ISafeguardConnection connection) { + if (connection == null) { + System.out.println(String.format("Safeguard not connected. This test requires an annonymous connection.")); + return; + } + + String address = readLine("SPP address(management service): ", null); + + try { + ISafeguardConnection managementConnection = connection.GetManagementServiceConnection(address); + FullResponse response = managementConnection.invokeMethodFull(Service.Management, Method.Get, "ApplianceInformation", null, null, null, null); + System.out.println(String.format("\t\\ApplianceInformation response:")); + logResponseDetails(response); + } catch (ArgumentException | ObjectDisposedException | SafeguardForJavaException ex) { + System.out.println("\t[ERROR]Test management connection failed: " + ex.getMessage()); + } + + } + public void safeguardTestAnonymousConnection(ISafeguardConnection connection) { + + if (connection == null) { + System.out.println(String.format("Safeguard not connected")); + return; + } + + try { + int remaining = connection.getAccessTokenLifetimeRemaining(); + System.out.println(String.format("\tTime remaining: %d", remaining)); + + FullResponse fullResponse = connection.invokeMethodFull(Service.Notification, Method.Get, "Status", null, null, null, null); + System.out.println(String.format("\t\\Appliance status:")); + logResponseDetails(fullResponse); + + } catch (ArgumentException | ObjectDisposedException | SafeguardForJavaException ex) { + System.out.println("\t[ERROR]Test connection failed: " + ex.getMessage()); + } + } + }