Merge pull request #7841 from gdicristofaro/AUT-2465-betterFeedback

AUT-2465 better feedback on unsuccessful license
This commit is contained in:
eugene7646 2023-08-25 11:05:16 -04:00 committed by GitHub
commit a4652e6da3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 94 additions and 23 deletions

View File

@ -158,16 +158,23 @@ class CTCloudHttpClient {
// Parse Response // Parse Response
if (classType != null) { if (classType != null) {
HttpEntity entity = response.getEntity(); HttpEntity entity = response.getEntity();
String entityStr = EntityUtils.toString(entity); if (entity != null) {
O respObj = mapper.readValue(entityStr, classType); String entityStr = EntityUtils.toString(entity);
return respObj; if (StringUtils.isNotBlank(entityStr)) {
} else { O respObj = mapper.readValue(entityStr, classType);
return null; return respObj;
}
}
} }
return null;
} else { } else {
LOGGER.log(Level.WARNING, "Response Received. - Status Error {}", response.getStatusLine()); LOGGER.log(Level.WARNING, "Response Received. - Status Error {}", response.getStatusLine());
handleNonOKResponse(response, ""); handleNonOKResponse(response, "");
} }
// transform all non-CTCloudException's into a CTCloudException
} catch (CTCloudException ex) {
throw ex;
} catch (Exception ex) { } catch (Exception ex) {
LOGGER.log(Level.WARNING, "Error when parsing response from CyberTriage Cloud", ex); LOGGER.log(Level.WARNING, "Error when parsing response from CyberTriage Cloud", ex);
throw new CTCloudException(CTCloudException.parseUnknownException(ex), ex); throw new CTCloudException(CTCloudException.parseUnknownException(ex), ex);
@ -191,7 +198,7 @@ class CTCloudHttpClient {
if (fileUploadRequest == null) { if (fileUploadRequest == null) {
throw new CTCloudException(ErrorCode.BAD_REQUEST, new IllegalArgumentException("fileUploadRequest cannot be null")); throw new CTCloudException(ErrorCode.BAD_REQUEST, new IllegalArgumentException("fileUploadRequest cannot be null"));
} }
String fullUrlPath = fileUploadRequest.getFullUrlPath(); String fullUrlPath = fileUploadRequest.getFullUrlPath();
String fileName = fileUploadRequest.getFileName(); String fileName = fileUploadRequest.getFileName();
InputStream fileInputStream = fileUploadRequest.getFileInputStream(); InputStream fileInputStream = fileUploadRequest.getFileInputStream();
@ -200,7 +207,7 @@ class CTCloudHttpClient {
if (StringUtils.isBlank(fullUrlPath) || fileInputStream == null || contentLength == null || contentLength <= 0) { if (StringUtils.isBlank(fullUrlPath) || fileInputStream == null || contentLength == null || contentLength <= 0) {
throw new CTCloudException(ErrorCode.BAD_REQUEST, new IllegalArgumentException("fullUrlPath, fileInputStream, contentLength must not be empty, null or less than 0")); throw new CTCloudException(ErrorCode.BAD_REQUEST, new IllegalArgumentException("fullUrlPath, fileInputStream, contentLength must not be empty, null or less than 0"));
} }
URI putUri; URI putUri;
try { try {
putUri = new URI(fullUrlPath); putUri = new URI(fullUrlPath);

View File

@ -32,18 +32,21 @@ public class LicenseResponse {
private final Boolean hostChanged; private final Boolean hostChanged;
private final Long hostChangesRemaining; private final Long hostChangesRemaining;
private final BoostLicenseResponse boostLicense; private final BoostLicenseResponse boostLicense;
private final String errorMsg;
@JsonCreator @JsonCreator
public LicenseResponse( public LicenseResponse(
@JsonProperty("success") Boolean success, @JsonProperty("success") Boolean success,
@JsonProperty("hostChanged") Boolean hostChanged, @JsonProperty("hostChanged") Boolean hostChanged,
@JsonProperty("hostChangesRemaining") Long hostChangesRemaining, @JsonProperty("hostChangesRemaining") Long hostChangesRemaining,
@JsonProperty("boostLicense") BoostLicenseResponse boostLicense @JsonProperty("boostLicense") BoostLicenseResponse boostLicense,
@JsonProperty("errorMsg") String errorMsg
) { ) {
this.success = success; this.success = success;
this.hostChanged = hostChanged; this.hostChanged = hostChanged;
this.hostChangesRemaining = hostChangesRemaining; this.hostChangesRemaining = hostChangesRemaining;
this.boostLicense = boostLicense; this.boostLicense = boostLicense;
this.errorMsg = errorMsg;
} }
public Boolean isSuccess() { public Boolean isSuccess() {
@ -61,4 +64,8 @@ public class LicenseResponse {
public BoostLicenseResponse getBoostLicense() { public BoostLicenseResponse getBoostLicense() {
return boostLicense; return boostLicense;
} }
public String getErrorMsg() {
return errorMsg;
}
} }

View File

@ -34,6 +34,7 @@ import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec; import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.text.MessageFormat;
import java.util.Base64; import java.util.Base64;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
@ -42,6 +43,7 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.ObjectUtils;
/** /**
* Decrypts the payload of boost license. * Decrypts the payload of boost license.
@ -58,12 +60,12 @@ public class LicenseDecryptorUtil {
private LicenseDecryptorUtil() { private LicenseDecryptorUtil() {
} }
public LicenseInfo createLicenseInfo(LicenseResponse licenseResponse) throws JsonProcessingException, InvalidLicenseException { public LicenseInfo createLicenseInfo(LicenseResponse licenseResponse) throws JsonProcessingException, InvalidLicenseException {
if (licenseResponse == null || licenseResponse.getBoostLicense() == null) { if (licenseResponse == null) {
throw new InvalidLicenseException("License or boost license are null"); throw new InvalidLicenseException("License is null");
} }
DecryptedLicenseResponse decrypted = parseLicenseJSON(licenseResponse.getBoostLicense()); DecryptedLicenseResponse decrypted = parseLicenseJSON(licenseResponse.getBoostLicense());
return new LicenseInfo(licenseResponse, decrypted); return new LicenseInfo(licenseResponse, decrypted);
} }
@ -78,6 +80,9 @@ public class LicenseDecryptorUtil {
* com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil.InvalidLicenseException * com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil.InvalidLicenseException
*/ */
public DecryptedLicenseResponse parseLicenseJSON(BoostLicenseResponse licenseResponse) throws JsonProcessingException, InvalidLicenseException { public DecryptedLicenseResponse parseLicenseJSON(BoostLicenseResponse licenseResponse) throws JsonProcessingException, InvalidLicenseException {
if (licenseResponse == null) {
throw new InvalidLicenseException("Boost license is null");
}
String decryptedJsonResponse; String decryptedJsonResponse;
try { try {
@ -101,6 +106,12 @@ public class LicenseDecryptorUtil {
} }
private String decryptLicenseString(String encryptedJson, String ivBase64, String encryptedKey, String version) throws IOException, GeneralSecurityException, InvalidLicenseException { private String decryptLicenseString(String encryptedJson, String ivBase64, String encryptedKey, String version) throws IOException, GeneralSecurityException, InvalidLicenseException {
if (ObjectUtils.anyNull(encryptedJson, ivBase64, encryptedKey, version)) {
throw new InvalidLicenseException(MessageFormat.format(
"encryptedJson: {0}, iv: {1}, encryptedKey: {2}, version: {3} must all be non-null",
encryptedJson, ivBase64, encryptedKey, version));
}
if (!"1.0".equals(version)) { if (!"1.0".equals(version)) {
throw new InvalidLicenseException("Unexpected file version: " + version); throw new InvalidLicenseException("Unexpected file version: " + version);
} }

View File

@ -4,7 +4,7 @@
CTLicenseDialog.title=Add a License... CTLicenseDialog.title=Add a License...
CTLicenseDialog.licenseNumberLabel.text=License Number: CTLicenseDialog.licenseNumberLabel.text=License Number:
CTLicenseDialog.licenseNumberTextField.text=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX CTLicenseDialog.licenseNumberTextField.text=
CTLicenseDialog.cancelButton.text=Cancel CTLicenseDialog.cancelButton.text=Cancel
CTLicenseDialog.okButton.text=Ok CTLicenseDialog.okButton.text=Ok
CTLicenseDialog.warningLabel.text= CTLicenseDialog.warningLabel.text=
@ -25,3 +25,4 @@ EULADialog.title=Cyber Triage End User License Agreement
CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text= CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text=
CTMalwareScannerOptionsPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a paid subscription to use.</html> CTMalwareScannerOptionsPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a paid subscription to use.</html>
CTMalwareScannerOptionsPanel.purchaseFromLabel.text=For licensing information, visit CTMalwareScannerOptionsPanel.purchaseFromLabel.text=For licensing information, visit
CTLicenseDialog.licenseNumberTextField.toolTipText=AUT-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

View File

@ -4,11 +4,11 @@
CTLicenseDialog.title=Add a License... CTLicenseDialog.title=Add a License...
CTLicenseDialog.licenseNumberLabel.text=License Number: CTLicenseDialog.licenseNumberLabel.text=License Number:
CTLicenseDialog.licenseNumberTextField.text=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX CTLicenseDialog.licenseNumberTextField.text=
CTLicenseDialog.cancelButton.text=Cancel CTLicenseDialog.cancelButton.text=Cancel
CTLicenseDialog.okButton.text=Ok CTLicenseDialog.okButton.text=Ok
CTLicenseDialog.warningLabel.text= CTLicenseDialog.warningLabel.text=
CTLicenseDialog_verifyInput_licenseNumberError=<html>Please verify license number format of 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'</html> CTLicenseDialog_verifyInput_licenseNumberError=<html>Please enter a license number</html>
CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text= CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text=
CTMalwareScannerOptionsPanel.countersResetLabel.text= CTMalwareScannerOptionsPanel.countersResetLabel.text=
CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text= CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text=
@ -31,6 +31,8 @@ CTMalwareScannerOptionsPanel_licenseAddDialogEnteredErr_title=License Number Alr
CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_desc=Please verify that license number is of format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_desc=Please verify that license number is of format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_title=Invalid License Number CTMalwareScannerOptionsPanel_licenseAddDialogPatternErr_title=Invalid License Number
CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error
# {0} - licenseCode
CTMalwareScannerOptionsPanel_LicenseFetcher_defaultErrMsg_desc=Error activating boost license {0}
CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information. Please try again later. CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information. Please try again later.
CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error
# {0} - expiresDate # {0} - expiresDate
@ -63,3 +65,4 @@ EULADialog.title=Cyber Triage End User License Agreement
CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text= CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text=
CTMalwareScannerOptionsPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a paid subscription to use.</html> CTMalwareScannerOptionsPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a paid subscription to use.</html>
CTMalwareScannerOptionsPanel.purchaseFromLabel.text=For licensing information, visit CTMalwareScannerOptionsPanel.purchaseFromLabel.text=For licensing information, visit
CTLicenseDialog.licenseNumberTextField.toolTipText=AUT-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

View File

@ -127,6 +127,9 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTLicenseDialog.licenseNumberTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTLicenseDialog.licenseNumberTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTLicenseDialog.licenseNumberTextField.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">

View File

@ -18,18 +18,20 @@
*/ */
package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud; package com.basistech.df.cybertriage.autopsy.ctoptions.ctcloud;
import java.awt.Color;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.corecomponents.TextPrompt;
/** /**
* License dialog * License dialog
*/ */
class CTLicenseDialog extends javax.swing.JDialog { class CTLicenseDialog extends javax.swing.JDialog {
private static final Pattern LICENSE_PATTERN = Pattern.compile("^\\s*[a-zA-Z0-9\\-]+?\\s*$"); private static final Pattern LICENSE_PATTERN = Pattern.compile("^\\s*[a-zA-Z0-9-_]+?\\s*$");
private String licenseString = null; private String licenseString = null;
/** /**
@ -38,6 +40,7 @@ class CTLicenseDialog extends javax.swing.JDialog {
public CTLicenseDialog(java.awt.Frame parent, boolean modal) { public CTLicenseDialog(java.awt.Frame parent, boolean modal) {
super(parent, modal); super(parent, modal);
initComponents(); initComponents();
configureHintText();
this.licenseNumberTextField.getDocument().putProperty("filterNewlines", Boolean.TRUE); this.licenseNumberTextField.getDocument().putProperty("filterNewlines", Boolean.TRUE);
this.licenseNumberTextField.getDocument().addDocumentListener(new DocumentListener() { this.licenseNumberTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override @Override
@ -56,13 +59,23 @@ class CTLicenseDialog extends javax.swing.JDialog {
} }
}); });
} }
private void configureHintText() {
TextPrompt textPrompt = new TextPrompt(
StringUtils.defaultString(this.licenseNumberTextField.getToolTipText()),
this.licenseNumberTextField);
textPrompt.setForeground(Color.LIGHT_GRAY);
float alpha = 0.9f; // Mostly opaque
textPrompt.changeAlpha(alpha);
}
String getValue() { String getValue() {
return licenseString; return licenseString;
} }
@Messages({ @Messages({
"CTLicenseDialog_verifyInput_licenseNumberError=<html>Please verify license number format of 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'</html>" "CTLicenseDialog_verifyInput_licenseNumberError=<html>Please enter a license number</html>"
}) })
private void verifyInput() { private void verifyInput() {
String licenseInput = StringUtils.defaultString(this.licenseNumberTextField.getText()); String licenseInput = StringUtils.defaultString(this.licenseNumberTextField.getText());
@ -165,6 +178,7 @@ class CTLicenseDialog extends javax.swing.JDialog {
getContentPane().add(cancelButton, gridBagConstraints); getContentPane().add(cancelButton, gridBagConstraints);
licenseNumberTextField.setText(org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.licenseNumberTextField.text")); // NOI18N licenseNumberTextField.setText(org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.licenseNumberTextField.text")); // NOI18N
licenseNumberTextField.setToolTipText(org.openide.util.NbBundle.getMessage(CTLicenseDialog.class, "CTLicenseDialog.licenseNumberTextField.toolTipText")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0; gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1; gridBagConstraints.gridy = 1;
@ -177,7 +191,8 @@ class CTLicenseDialog extends javax.swing.JDialog {
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
this.licenseString = this.licenseNumberTextField.getText(); String inputText = this.licenseNumberTextField.getText();
this.licenseString = inputText == null ? null : inputText.trim();
this.dispose(); this.dispose();
}//GEN-LAST:event_okButtonActionPerformed }//GEN-LAST:event_okButtonActionPerformed

View File

@ -608,6 +608,8 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
@NbBundle.Messages({ @NbBundle.Messages({
"CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error", "CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title=Server Error",
"CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error", "CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_title=General Error",
"# {0} - licenseCode",
"CTMalwareScannerOptionsPanel_LicenseFetcher_defaultErrMsg_desc=Error activating boost license {0}",
"CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information. Please try again later.",}) "CTMalwareScannerOptionsPanel_LicenseFetcher_localErr_desc=A general error occurred while fetching license information. Please try again later.",})
private class LicenseFetcher extends SwingWorker<LicenseResponse, Void> { private class LicenseFetcher extends SwingWorker<LicenseResponse, Void> {
@ -629,10 +631,9 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
protected void done() { protected void done() {
try { try {
LicenseResponse licenseResponse = get(); LicenseResponse licenseResponse = get();
if (licenseResponse != null && licenseResponse.isSuccess()) { // if no result, show unauthorized
SwingUtilities.invokeLater(() -> acceptEula(licenseResponse)); if (licenseResponse == null) {
} else { logger.log(Level.WARNING, "An API error occurred while fetching license information. License fetch returned no result.");
logger.log(Level.WARNING, "An API error occurred while fetching license information. License fetch was not successful");
JOptionPane.showMessageDialog( JOptionPane.showMessageDialog(
CTMalwareScannerOptionsPanel.this, CTMalwareScannerOptionsPanel.this,
CTCloudException.ErrorCode.UN_AUTHORIZED.getDescription(), CTCloudException.ErrorCode.UN_AUTHORIZED.getDescription(),
@ -640,7 +641,30 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
setLicenseDisplay(licenseInfo, null); setLicenseDisplay(licenseInfo, null);
loadMalwareScansInfo(licenseInfo); loadMalwareScansInfo(licenseInfo);
return;
} }
// if not successful response
if (!Boolean.TRUE.equals(licenseResponse.isSuccess())) {
logger.log(Level.WARNING, "An API error occurred while fetching license information. License fetch was not successful");
// use default message unless error message specified
String message = Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_defaultErrMsg_desc(licenseText);
if (!StringUtils.isBlank(licenseResponse.getErrorMsg())) {
message = licenseResponse.getErrorMsg();
}
JOptionPane.showMessageDialog(
CTMalwareScannerOptionsPanel.this,
message,
Bundle.CTMalwareScannerOptionsPanel_LicenseFetcher_apiErr_title(),
JOptionPane.ERROR_MESSAGE);
setLicenseDisplay(licenseInfo, null);
loadMalwareScansInfo(licenseInfo);
return;
}
// otherwise, load
SwingUtilities.invokeLater(() -> acceptEula(licenseResponse));
} catch (InterruptedException | CancellationException ex) { } catch (InterruptedException | CancellationException ex) {
// ignore cancellation; just load current license // ignore cancellation; just load current license
setLicenseDisplay(licenseInfo, null); setLicenseDisplay(licenseInfo, null);