From 1408fd289bd3aa7d61f93e7fa50b188b91f34e42 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Tue, 28 May 2019 18:08:24 -0400 Subject: [PATCH] 5061 allow Bing Translator credentials to be set by user --- .../translators/BingTranslator.java | 44 +++-- .../translators/BingTranslatorSettings.java | 80 ++++++++ .../BingTranslatorSettingsPanel.form | 85 +++++++++ .../BingTranslatorSettingsPanel.java | 171 ++++++++++++++++++ .../translators/Bundle.properties | 1 + .../translators/Bundle.properties-MERGED | 2 + .../translators/GoogleTranslator.java | 2 +- 7 files changed, 368 insertions(+), 17 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java create mode 100644 Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java index 892ecb5ac6..1f790d14c3 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslator.java @@ -19,12 +19,20 @@ package org.sleuthkit.autopsy.texttranslation.translators; -import java.io.*; -import com.google.gson.*; -import com.squareup.okhttp.*; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; import java.awt.Component; -import javax.swing.JLabel; +import java.io.IOException; import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.texttranslation.TextTranslator; +import org.sleuthkit.autopsy.texttranslation.TranslationException; /** * Translates text by making HTTP requests to Bing Translator. @@ -32,13 +40,13 @@ import org.openide.util.NbBundle.Messages; */ @ServiceProvider(service = TextTranslator.class) public class BingTranslator implements TextTranslator{ - // Insert the subscription key here. - private String subscriptionKey = ""; - //In the String below, "en" is the target language. You can include multiple target //languages separated by commas. A full list of supported languages is here: //https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support private static final String URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=en"; + private final BingTranslatorSettingsPanel settingsPanel; + private final BingTranslatorSettings settings = new BingTranslatorSettings(); + // This sends messages to Microsoft. private final OkHttpClient CLIENT = new OkHttpClient(); @@ -47,6 +55,15 @@ public class BingTranslator implements TextTranslator{ //paid account that's willing to pay for long documents. private final int MAX_STRING_LENGTH = 5000; + + public BingTranslator(){ + settingsPanel = new BingTranslatorSettingsPanel(settings.getCredentials()); + } + + static String getMicrosftTranlatorUrl(){ + return URL; + } + /** * Converts an input test to the JSON format required by Bing Translator, * posts it to Microsoft, and returns the JSON text response. @@ -68,7 +85,7 @@ public class BingTranslator implements TextTranslator{ bodyString); Request request = new Request.Builder() .url(URL).post(body) - .addHeader("Ocp-Apim-Subscription-Key", subscriptionKey) + .addHeader("Ocp-Apim-Subscription-Key", settings.getCredentials()) .addHeader("Content-type", "application/json").build(); Response response = CLIENT.newCall(request).execute(); return response.body().string(); @@ -76,7 +93,7 @@ public class BingTranslator implements TextTranslator{ @Override public String translate(String string) throws TranslationException { - if (subscriptionKey == null || subscriptionKey.isEmpty()) { + if (settings.getCredentials() == null || settings.getCredentials().isEmpty()) { throw new TranslationException("Bing Translator has not been configured, credentials need to be specified"); } @@ -111,17 +128,12 @@ public class BingTranslator implements TextTranslator{ @Override public Component getComponent() { - return new JLabel("There are no settings to configure for Bing Translator"); + return settingsPanel; } @Override public void saveSettings() { - //There are no settings to configure for Bing Translator - //Possible settings for the future: - //source language, target language, API key, path to JSON file of API key. - //We'll need test code to make sure that exceptions are thrown in all of - //those scenarios. - return; + settings.setCredentials(settingsPanel.getCredentials()); } private String parseJSONResponse(String json_text) throws TranslationException { diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java new file mode 100644 index 0000000000..6d6ef47b39 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettings.java @@ -0,0 +1,80 @@ +/* + * Autopsy + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.texttranslation.translators; + +import org.apache.commons.lang3.StringUtils; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; + +/** + * Class to handle the settings associated with the GoogleTranslator + */ +public final class BingTranslatorSettings { + + private static final String CREDENTIALS_KEY = "Credentials"; + private static final String BING_TRANSLATE_NAME = "BingTranslate"; + private static final String DEFAULT_CREDENTIALS = ""; + private String credentials; + + /** + * Construct a new GoogleTranslatorSettingsObject + */ + BingTranslatorSettings() { + loadSettings(); + } + + /** + * Get the path to the JSON credentials file + * + * @return the path to the credentials file + */ + String getCredentials() { + return credentials; + } + + /** + * Set the path to the JSON credentials file + * + * @param path the path to the credentials file + */ + void setCredentials(String creds) { + credentials = creds; + } + + + /** + * Load the settings into memory from their on disk storage + */ + void loadSettings() { + if (!ModuleSettings.configExists(BING_TRANSLATE_NAME)) { + ModuleSettings.makeConfigFile(BING_TRANSLATE_NAME); + } + if (ModuleSettings.settingExists(BING_TRANSLATE_NAME, CREDENTIALS_KEY)) { + credentials = ModuleSettings.getConfigSetting(BING_TRANSLATE_NAME, CREDENTIALS_KEY); + } else { + credentials = DEFAULT_CREDENTIALS; + } + } + + /** + * Save the setting from memory to their location on disk + */ + void saveSettings() { + ModuleSettings.setConfigSetting(BING_TRANSLATE_NAME, CREDENTIALS_KEY, credentials); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form new file mode 100644 index 0000000000..ae87b5a187 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.form @@ -0,0 +1,85 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java new file mode 100644 index 0000000000..ae8da0adcf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/BingTranslatorSettingsPanel.java @@ -0,0 +1,171 @@ +/* + * Autopsy + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.texttranslation.translators; + +import com.google.gson.JsonArray; +import com.google.gson.JsonParser; +import com.google.gson.JsonObject; +import com.squareup.okhttp.MediaType; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.RequestBody; +import com.squareup.okhttp.Response; +import java.io.IOException; +import java.util.logging.Logger; + +/** + * Settings panel for the GoogleTranslator + */ +public class BingTranslatorSettingsPanel extends javax.swing.JPanel { + + private static final Logger logger = Logger.getLogger(BingTranslatorSettingsPanel.class.getName()); + private static final long serialVersionUID = 1L; + + /** + * Creates new form GoogleTranslatorSettingsPanel + */ + public BingTranslatorSettingsPanel(String credentials) { + initComponents(); + credentialsField.setText(credentials); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + credentialsLabel = new javax.swing.JLabel(); + credentialsField = new javax.swing.JTextField(); + warningLabel = new javax.swing.JLabel(); + testButton = new javax.swing.JButton(); + + org.openide.awt.Mnemonics.setLocalizedText(credentialsLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.credentialsLabel.text")); // NOI18N + + warningLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(warningLabel, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "GoogleTranslatorSettingsPanel.warningLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(testButton, org.openide.util.NbBundle.getMessage(BingTranslatorSettingsPanel.class, "BingTranslatorSettingsPanel.testButton.text")); // NOI18N + testButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + testButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 551, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(credentialsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 86, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(credentialsField, javax.swing.GroupLayout.DEFAULT_SIZE, 463, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(testButton) + .addGap(8, 8, 8)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(credentialsLabel) + .addComponent(credentialsField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(testButton)) + .addGap(31, 31, 31) + .addComponent(warningLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(29, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_testButtonActionPerformed + if (testTranslationSetup()) { + warningLabel.setText(""); + } else { + warningLabel.setText("Invalid translation credentials"); + } + }//GEN-LAST:event_testButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField credentialsField; + private javax.swing.JLabel credentialsLabel; + private javax.swing.JButton testButton; + private javax.swing.JLabel warningLabel; + // End of variables declaration//GEN-END:variables + /** + * Converts an input test to the JSON format required by Bing Translator, + * posts it to Microsoft, and returns the JSON text response. + * + * @param string The input text to be translated. + * + * @return The translation response as a JSON string + * + * @throws IOException if the request could not be executed due to + * cancellation, a connectivity problem or timeout. + */ + private boolean testTranslationSetup() { + String testString = "forense"; + MediaType mediaType = MediaType.parse("application/json"); + + JsonArray jsonArray = new JsonArray(); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("Text", testString); + jsonArray.add(jsonObject); + String bodyString = jsonArray.toString(); + + RequestBody body = RequestBody.create(mediaType, + bodyString); + Request request = new Request.Builder() + .url(BingTranslator.getMicrosftTranlatorUrl()).post(body) + .addHeader("Ocp-Apim-Subscription-Key", credentialsField.getText()) + .addHeader("Content-type", "application/json").build(); + try { + Response response = new OkHttpClient().newCall(request).execute(); + JsonParser parser = new JsonParser(); + JsonArray responses = parser.parse(response.body().string()).getAsJsonArray(); + //As far as I know, there's always exactly one item in the array. + JsonObject response0 = responses.get(0).getAsJsonObject(); + JsonArray translations = response0.getAsJsonArray("translations"); + JsonObject translation0 = translations.get(0).getAsJsonObject(); + translation0.get("text").getAsString(); + return true; + } catch (IOException | IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) { + return false; + } + + } + + /** + * Get the currently set path to the JSON credentials file + * + * @return the path to the credentials file specified in the textarea + */ + String getCredentials() { + return credentialsField.getText(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties index 56782cdf33..024ddfadcd 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties @@ -2,3 +2,4 @@ GoogleTranslatorSettingsPanel.browseButton.text=Browse GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials: GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: +BingTranslatorSettingsPanel.testButton.text=Test diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED index b0e30f632c..bfe84cb856 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/Bundle.properties-MERGED @@ -1,3 +1,4 @@ +BingTranslator.name.text=Bing Translator GoogleTranslator.name.text=Google Translate GoogleTranslatorSettingsPanel.browseButton.text=Browse GoogleTranslatorSettingsPanel.credentialsLabel.text=Credentials: @@ -11,3 +12,4 @@ GoogleTranslatorSettingsPanel.fileChooser.confirmButton=Select GoogleTranslatorSettingsPanel.json.description=JSON Files GoogleTranslatorSettingsPanel.warningLabel.text= GoogleTranslatorSettingsPanel.targetLanguageLabel.text=Target Language: +BingTranslatorSettingsPanel.jButton1.text=Test diff --git a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java index 38506ef936..f40b2129e2 100644 --- a/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java +++ b/Core/src/org/sleuthkit/autopsy/texttranslation/translators/GoogleTranslator.java @@ -106,7 +106,7 @@ public final class GoogleTranslator implements TextTranslator { } /** - * Load the Google Cloud Translation service give the currently saved + * Load the Google Cloud Translation service given the currently saved * settings */ private void loadTranslator() {