Skip to content

Commit

Permalink
Merge pull request #126 from bnicholesdell/adda2asetcredendpoints
Browse files Browse the repository at this point in the history
Add the APIs to set credentials using the A2A context.  Update the tests to verify the new APIs.
  • Loading branch information
bnicholesdell authored Jul 11, 2023
2 parents 32db966 + de5a336 commit 5724cfd
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.oneidentity.safeguard.safeguardjava;

import com.oneidentity.safeguard.safeguardjava.data.A2ARetrievableAccount;
import com.oneidentity.safeguard.safeguardjava.data.ApiKeySecret;
import com.oneidentity.safeguard.safeguardjava.data.BrokeredAccessRequest;
import com.oneidentity.safeguard.safeguardjava.data.KeyFormat;
import com.oneidentity.safeguard.safeguardjava.event.ISafeguardEventListener;
import com.oneidentity.safeguard.safeguardjava.exceptions.ArgumentException;
Expand Down Expand Up @@ -38,6 +35,15 @@ public interface ISafeguardA2AContext
*/
char[] retrievePassword(char[] apiKey) throws ObjectDisposedException, SafeguardForJavaException, ArgumentException;

/**
* Sets a password using Safeguard A2A.
*
* @param apiKey API key corresponding to the configured account.
* @param password Password to set.
* @return
*/
void SetPassword(char[] apiKey, char[] password) throws ObjectDisposedException, SafeguardForJavaException, ArgumentException;

/**
* Retrieves an SSH private key using Safeguard A2A.
*
Expand All @@ -61,6 +67,17 @@ public interface ISafeguardA2AContext
*/
List<IApiKeySecret> retrieveApiKeySecret(char[] apiKey) throws ObjectDisposedException, ArgumentException, SafeguardForJavaException;

/**
* Sets an SSH private key using Safeguard A2A.
*
* @param apiKey API key corresponding to the configured account.
* @param privateKey Private key to set.
* @param password Password associated with the private key.
* @param keyFormat Format to use when returning private key.
* @return
*/
void SetPrivateKey(char[] apiKey, char[] privateKey, char[] password, KeyFormat keyFormat) throws ObjectDisposedException, ArgumentException, SafeguardForJavaException;

/**
* Gets an A2A event listener. The handler passed in will be registered for the AssetAccountPasswordUpdated
* event, which is the only one supported in A2A. You just have to call Start(). The event listener returned
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import com.oneidentity.safeguard.safeguardjava.data.ApiKeySecretInternal;
import com.oneidentity.safeguard.safeguardjava.data.BrokeredAccessRequest;
import com.oneidentity.safeguard.safeguardjava.data.CertificateContext;
import com.oneidentity.safeguard.safeguardjava.data.JsonBody;
import com.oneidentity.safeguard.safeguardjava.data.KeyFormat;
import com.oneidentity.safeguard.safeguardjava.data.SshKey;
import com.oneidentity.safeguard.safeguardjava.event.ISafeguardEventListener;
import com.oneidentity.safeguard.safeguardjava.event.SafeguardEventListener;
import com.oneidentity.safeguard.safeguardjava.exceptions.ArgumentException;
Expand Down Expand Up @@ -182,7 +184,42 @@ public char[] retrievePassword(char[] apiKey) throws ObjectDisposedException, Sa
Logger.getLogger(SafeguardA2AContext.class.getName()).log(Level.INFO, "Successfully retrieved A2A password.");
return password;
}


@Override
public void SetPassword(char[] apiKey, char[] password) throws ObjectDisposedException, SafeguardForJavaException, ArgumentException {
if (disposed) {
throw new ObjectDisposedException("SafeguardA2AContext");
}

if (apiKey == null) {
throw new ArgumentException("The apiKey parameter may not be null");
}

if (password == null) {
throw new ArgumentException("The password parameter may not be null");
}

Map<String, String> headers = new HashMap<>();
headers.put(HttpHeaders.AUTHORIZATION, String.format("A2A %s", new String(apiKey)));

Map<String, String> parameters = new HashMap<>();

CloseableHttpResponse response = a2AClient.execPUT("Credentials/Password", parameters, headers, null,
new JsonBody("\""+new String(password)+"\""), clientCertificate);

if (response == null) {
throw new SafeguardForJavaException(String.format("Unable to connect to web service %s", a2AClient.getBaseURL()));
}

String reply = Utils.getResponse(response);
if (!Utils.isSuccessful(response.getStatusLine().getStatusCode())) {
throw new SafeguardForJavaException("Error returned from Safeguard API, Error: "
+ String.format("%s %s", response.getStatusLine().getStatusCode(), reply));
}

Logger.getLogger(SafeguardA2AContext.class.getName()).log(Level.INFO, "Successfully set A2A password.");
}

@Override
public char[] retrievePrivateKey(char[] apiKey, KeyFormat keyFormat) throws ObjectDisposedException, ArgumentException, SafeguardForJavaException {
if (disposed) {
Expand Down Expand Up @@ -220,6 +257,54 @@ public char[] retrievePrivateKey(char[] apiKey, KeyFormat keyFormat) throws Obje
return privateKey;
}

@Override
public void SetPrivateKey(char[] apiKey, char[] privateKey, char[] password, KeyFormat keyFormat)
throws ObjectDisposedException, ArgumentException, SafeguardForJavaException {

if (disposed) {
throw new ObjectDisposedException("SafeguardA2AContext");
}

if (keyFormat == null)
keyFormat = KeyFormat.OpenSsh;

if (apiKey == null)
throw new ArgumentException("The apiKey parameter may not be null.");

if (privateKey == null)
throw new ArgumentException("The privateKey parameter may not be null");

if (password == null)
throw new ArgumentException("The password parameter may not be null");

SshKey sshKey = new SshKey();
sshKey.setPassphrase(new String(password));
sshKey.setPrivateKey(new String(privateKey));

String body = new Gson().toJson(sshKey);

Map<String, String> headers = new HashMap<>();
headers.put(HttpHeaders.AUTHORIZATION, String.format("A2A %s", new String(apiKey)));

Map<String, String> parameters = new HashMap<>();
parameters.put("keyFormat", keyFormat.name());

CloseableHttpResponse response = a2AClient.execPUT("Credentials/SshKey", parameters, headers, null, new JsonBody(body), clientCertificate);

if (response == null) {
throw new SafeguardForJavaException(String.format("Unable to connect to web service %s", a2AClient.getBaseURL()));
}

String reply = Utils.getResponse(response);
if (!Utils.isSuccessful(response.getStatusLine().getStatusCode())) {
throw new SafeguardForJavaException("Error returned from Safeguard API, Error: "
+ String.format("%s %s", response.getStatusLine().getStatusCode(), reply));
}

Logger.getLogger(SafeguardA2AContext.class.getName()).log(Level.INFO, "Successfully set A2A private key.");
return;
}

@Override
public List<IApiKeySecret> retrieveApiKeySecret(char[] apiKey) throws ObjectDisposedException, ArgumentException, SafeguardForJavaException {
if (disposed) {
Expand Down Expand Up @@ -439,5 +524,4 @@ private List<ApiKeySecretInternal> parseApiKeySecretResponse(String response) {

return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.oneidentity.safeguard.safeguardjava.data;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* This class is used to set the SshKey.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SshKey {

@JsonProperty("Passphrase")
private String passphrase;
@JsonProperty("PrivateKey")
private String privateKey;

public SshKey() {
}

public String getPassphrase() {
return passphrase;
}

public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}

public String getPrivateKey() {
return privateKey;
}

public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,25 @@ public CloseableHttpResponse execPUT(String path, Map<String, String> queryParam
}
}

public CloseableHttpResponse execPUT(String path, Map<String, String> queryParams, Map<String, String> headers, Integer timeout,
JsonObject requestEntity, CertificateContext certificateContext) {
CloseableHttpClient certClient = getClientWithCertificate(certificateContext);

if (certClient != null) {
RequestBuilder rb = prepareRequest(RequestBuilder.put(getBaseURI(path)), queryParams, headers, timeout);

try {
String body = requestEntity.toJson();
rb.setEntity(new StringEntity(body == null ? "{}" : body));
CloseableHttpResponse r = certClient.execute(rb.build());
return r;
} catch (Exception ex) {
return null;
}
}
return null;
}

public CloseableHttpResponse execPOST(String path, Map<String, String> queryParams, Map<String, String> headers, Integer timeout, JsonObject requestEntity) {

RequestBuilder rb = prepareRequest(RequestBuilder.post(getBaseURI(path)), queryParams, headers, timeout);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,18 @@
import com.oneidentity.safeguard.safeguardjava.exceptions.ArgumentException;
import com.oneidentity.safeguard.safeguardjava.exceptions.ObjectDisposedException;
import com.oneidentity.safeguard.safeguardjava.exceptions.SafeguardForJavaException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class SafeguardTests {
Expand Down Expand Up @@ -315,6 +323,21 @@ ISafeguardA2AContext safeguardGetA2AContextByThumbprint() {
return a2aContext;
}

private byte[] readAllBytes(InputStream in) throws IOException {
ByteArrayOutputStream baos= new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for (int read=0; read != -1; read = in.read(buf)) { baos.write(buf, 0, read); }
return baos.toByteArray();
}

private String formatPEM(String resource) throws IOException {
InputStream in = new ByteArrayInputStream(resource.getBytes());
String pem = new String(readAllBytes(in), StandardCharsets.ISO_8859_1);
Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*");
String encoded = parse.matcher(pem).replaceFirst("$1");
return encoded.replace("\r", "").replace("\n", "");
}

public void safeguardTestA2AContext(ISafeguardA2AContext a2aContext) {

if (a2aContext == null) {
Expand Down Expand Up @@ -351,19 +374,65 @@ else if (typeOfRelease.equalsIgnoreCase("a")) {
System.out.println(String.format("Invalid credential release type."));
return;
}
} catch (ArgumentException | ObjectDisposedException | SafeguardForJavaException ex) {
System.out.println("\t[ERROR]Test connection failed: " + ex.getMessage());
}
}

if (readLine("Test Setting Credential(y/n): ", "y").equalsIgnoreCase("y")) {
String typeOfRelease = readLine("Password, Private Key (p/k): ", "p");
String apiKey = readLine("API Key: ", null);

try {
if (typeOfRelease.equalsIgnoreCase("p")) {
String newPassword = readLine("New Password: ", "");
a2aContext.SetPassword(apiKey.toCharArray(), newPassword.toCharArray());

String password = new String(a2aContext.retrievePassword(apiKey.toCharArray()));
if (password.compareTo(newPassword) == 0)
System.out.println(String.format("\tSuccessfully set password"));
else
System.out.println(String.format("\tFailed to set password"));
}
else if (typeOfRelease.equalsIgnoreCase("k")) {
String privateKeyPath = readLine("Private Key File Path: ", "");
String privateKeyPassword = readLine("Private Key Password: ", "");
Path filePath = Paths.get(privateKeyPath).toAbsolutePath();
String privateKey = new String(Files.readAllBytes(filePath));

a2aContext.SetPrivateKey(apiKey.toCharArray(), privateKey.toCharArray(), privateKeyPassword.toCharArray(), KeyFormat.OpenSsh);

String key = new String(a2aContext.retrievePrivateKey(apiKey.toCharArray(), KeyFormat.OpenSsh));

String privkey1 = formatPEM(privateKey);
String privkey2 = formatPEM(key);

if (privkey1.compareTo(privkey2) == 0)
System.out.println(String.format("\tSuccessful private key release"));
else
System.out.println(String.format("\tFailed to set private key"));
}
else {
System.out.println(String.format("Invalid credential release type."));
return;
}
} catch (ArgumentException | ObjectDisposedException | SafeguardForJavaException | IOException ex) {
System.out.println("\t[ERROR]Test connection failed: " + ex.getMessage());
}
}

if (readLine("Test Access Request Broker(y/n): ", "y").equalsIgnoreCase("y")) {
try {
List<IA2ARetrievableAccount> registrations = a2aContext.getRetrievableAccounts();
System.out.println(String.format("\tRetrievable accounts:"));
for (IA2ARetrievableAccount reg : registrations) {
System.out.println(String.format("\t\tAssetId: %d AssetName: %s AccountId: %d AccountName: %s AccountDescription: %s",
reg.getAssetId(), reg.getAssetName(), reg.getAccountId(), reg.getAccountName(), reg.getAccountDescription()));
}
} catch (ArgumentException | ObjectDisposedException | SafeguardForJavaException ex) {
System.out.println("\t[ERROR]Test connection failed: " + ex.getMessage());
} catch (ObjectDisposedException | SafeguardForJavaException ex) {
System.out.println("\t[ERROR]Failed to get the retrievable accounts: " + ex.getMessage());
}
}

if (readLine("Test Access Request Broker(y/n): ", "y").equalsIgnoreCase("y")) {
String accountId = readLine("Account Id: ", null);
String assetId = readLine("Asset Id:", null);
String forUserId = readLine("For User Id:", null);
Expand Down

0 comments on commit 5724cfd

Please sign in to comment.