diff --git a/src/main/java/com/blackberry/jwteditor/model/keys/KeysModel.java b/src/main/java/com/blackberry/jwteditor/model/keys/KeysModel.java
index e71531b..7480002 100644
--- a/src/main/java/com/blackberry/jwteditor/model/keys/KeysModel.java
+++ b/src/main/java/com/blackberry/jwteditor/model/keys/KeysModel.java
@@ -131,8 +131,11 @@ public void addKey(Key key) {
oldKey = keys.put(key.getID(), key);
}
- for (KeysModelListener modelListener: this.modelListeners) {
- modelListener.notifyKeyDeleted(oldKey);
+ for (KeysModelListener modelListener : modelListeners) {
+ if (oldKey != null) {
+ modelListener.notifyKeyDeleted(oldKey);
+ }
+
modelListener.notifyKeyInserted(key);
}
}
@@ -165,7 +168,7 @@ public void deleteKey(String keyId) {
}
if (rowIndex >= 0) {
- for (KeysModelListener modelListener: this.modelListeners) {
+ for (KeysModelListener modelListener : this.modelListeners) {
modelListener.notifyKeyDeleted(rowIndex);
}
}
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/ConfigView.form b/src/main/java/com/blackberry/jwteditor/view/config/ConfigView.form
index 13c70cc..a36bee6 100644
--- a/src/main/java/com/blackberry/jwteditor/view/config/ConfigView.form
+++ b/src/main/java/com/blackberry/jwteditor/view/config/ConfigView.form
@@ -1,320 +1,51 @@
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/ConfigView.java b/src/main/java/com/blackberry/jwteditor/view/config/ConfigView.java
index fc01d61..8da71f1 100644
--- a/src/main/java/com/blackberry/jwteditor/view/config/ConfigView.java
+++ b/src/main/java/com/blackberry/jwteditor/view/config/ConfigView.java
@@ -20,185 +20,32 @@
import burp.api.montoya.ui.UserInterface;
import burp.config.BurpConfig;
-import burp.intruder.FuzzLocation;
-import burp.intruder.IntruderConfig;
-import burp.proxy.HighlightColor;
-import burp.proxy.ProxyConfig;
-import burp.scanner.ScannerConfig;
-import com.blackberry.jwteditor.model.keys.Key;
import com.blackberry.jwteditor.model.keys.KeysModel;
-import com.blackberry.jwteditor.model.keys.KeysModelListener.SimpleKeysModelListener;
-import com.blackberry.jwteditor.view.utils.DocumentAdapter;
-import com.nimbusds.jose.JWSAlgorithm;
import javax.swing.*;
-import java.awt.*;
-import java.util.List;
-import java.util.Optional;
-
-import static java.awt.Font.BOLD;
public class ConfigView {
- private final IntruderConfig intruderConfig;
+ private final BurpConfig burpConfig;
+ private final UserInterface userInterface;
+ private final boolean isProVersion;
private final KeysModel keysModel;
private JPanel mainPanel;
- private JCheckBox checkBoxHighlightJWT;
- private JLabel labelHighlightColor;
- private JComboBox comboBoxHighlightColor;
- private JLabel labelHighlightJWT;
- private JTextField intruderParameterName;
- private JComboBox comboBoxPayloadPosition;
- private JComboBox comboBoxIntruderSigningKeyId;
- private JCheckBox checkBoxHeaderInsertionPoint;
- private JTextField scannerParameterName;
- private JPanel proxyPanel;
- private JLabel proxyLabel;
- private JLabel intruderLabel;
- private JLabel scannerLabel;
- private JPanel intruderPanel;
- private JLabel spacerLabel;
- private JCheckBox resignIntruderJWS;
- private JComboBox comboBoxIntruderSigningAlg;
+ private ProxyConfigView proxyConfigView;
+ private ScannerConfigView scannerConfigView;
+ private IntruderConfigView intruderConfigView;
public ConfigView(BurpConfig burpConfig, UserInterface userInterface, boolean isProVersion, KeysModel keysModel) {
+ this.burpConfig = burpConfig;
+ this.userInterface = userInterface;
+ this.isProVersion = isProVersion;
this.keysModel = keysModel;
- this.intruderConfig = burpConfig.intruderConfig();
-
- ProxyConfig proxyConfig = burpConfig.proxyConfig();
-
- checkBoxHighlightJWT.setSelected(proxyConfig.highlightJWT());
- checkBoxHighlightJWT.addActionListener(e -> {
- comboBoxHighlightColor.setEnabled(checkBoxHighlightJWT.isSelected());
- proxyConfig.setHighlightJWT(checkBoxHighlightJWT.isSelected());
- });
-
- comboBoxHighlightColor.setModel(new DefaultComboBoxModel<>(HighlightColor.values()));
- comboBoxHighlightColor.setSelectedItem(proxyConfig.highlightColor());
- comboBoxHighlightColor.setEnabled(proxyConfig.highlightJWT());
- comboBoxHighlightColor.addActionListener(e -> proxyConfig.setHighlightColor((HighlightColor) comboBoxHighlightColor.getSelectedItem()));
-
- intruderParameterName.setText(intruderConfig.fuzzParameter());
- intruderParameterName.getDocument().addDocumentListener(
- new DocumentAdapter(e -> intruderConfig.setFuzzParameter(intruderParameterName.getText()))
- );
-
- comboBoxPayloadPosition.setModel(new DefaultComboBoxModel<>(FuzzLocation.values()));
- comboBoxPayloadPosition.setSelectedItem(intruderConfig.fuzzLocation());
- comboBoxPayloadPosition.addActionListener(e -> intruderConfig.setFuzzLocation((FuzzLocation) comboBoxPayloadPosition.getSelectedItem()));
-
- updateSigningKeyList();
- comboBoxIntruderSigningKeyId.addActionListener(e -> {
- String newSigningKeyId = (String) comboBoxIntruderSigningKeyId.getSelectedItem();
-
- if (!intruderConfig.signingKeyId().equals(newSigningKeyId)) {
- intruderConfig.setSigningKeyId(newSigningKeyId);
- updateSigningAlgorithmList();
- }
- });
- comboBoxIntruderSigningAlg.addActionListener(e -> intruderConfig.setSigningAlgorithm((JWSAlgorithm) comboBoxIntruderSigningAlg.getSelectedItem()));
- resignIntruderJWS.addActionListener(e -> intruderConfig.setResign(resignIntruderJWS.isSelected()));
- keysModel.addKeyModelListener(new SimpleKeysModelListener(this::updateSigningKeyList));
-
- ScannerConfig scannerConfig = burpConfig.scannerConfig();
-
- checkBoxHeaderInsertionPoint.setEnabled(isProVersion);
- checkBoxHeaderInsertionPoint.setSelected(scannerConfig.enableHeaderJWSInsertionPointLocation());
- checkBoxHeaderInsertionPoint.addActionListener(e -> {
- scannerConfig.setEnableHeaderJWSInsertionPointLocation(checkBoxHeaderInsertionPoint.isSelected());
- scannerParameterName.setEnabled(checkBoxHeaderInsertionPoint.isSelected());
- });
-
- scannerParameterName.setEnabled(scannerConfig.enableHeaderJWSInsertionPointLocation());
- scannerParameterName.setText(scannerConfig.insertionPointLocationParameterName());
- scannerParameterName.getDocument().addDocumentListener(
- new DocumentAdapter(e -> scannerConfig.setInsertionPointLocationParameterName(scannerParameterName.getText()))
- );
-
- proxyLabel.setFont(proxyLabel.getFont().deriveFont(BOLD));
- intruderLabel.setFont(intruderLabel.getFont().deriveFont(BOLD));
- scannerLabel.setFont(scannerLabel.getFont().deriveFont(BOLD));
- userInterface.applyThemeToComponent(mainPanel);
-
- comboBoxHighlightColor.setRenderer(new HighlightComboRenderer());
- }
-
- private void updateSigningKeyList() {
- List signingKeys = keysModel.getSigningKeys();
- String[] signingKeyIds = signingKeys.stream().map(Key::getID).toArray(String[]::new);
- String modelSelectedSigningId = intruderConfig.signingKeyId();
-
- String viewSelectedKeyId = (String) comboBoxIntruderSigningKeyId.getSelectedItem();
- comboBoxIntruderSigningKeyId.setModel(new DefaultComboBoxModel<>(signingKeyIds));
-
- if (signingKeys.isEmpty()) {
- resignIntruderJWS.setSelected(false);
- resignIntruderJWS.setEnabled(false);
- comboBoxIntruderSigningKeyId.setEnabled(false);
- comboBoxIntruderSigningAlg.setEnabled(false);
- intruderConfig.setResign(false);
- intruderConfig.setSigningKeyId(null);
- } else {
- resignIntruderJWS.setEnabled(true);
- comboBoxIntruderSigningKeyId.setEnabled(true);
- comboBoxIntruderSigningAlg.setEnabled(true);
-
- Optional selectedKey = signingKeys.stream()
- .filter(k -> k.getID().equals(modelSelectedSigningId))
- .findFirst();
-
-
- if (selectedKey.isPresent()) {
- Key key = selectedKey.get();
-
- resignIntruderJWS.setSelected(intruderConfig.resign());
- comboBoxIntruderSigningKeyId.setSelectedItem(key.getID());
-
- if (!modelSelectedSigningId.equals(viewSelectedKeyId)) {
- comboBoxIntruderSigningAlg.setModel(new DefaultComboBoxModel(key.getSigningAlgorithms()));
- comboBoxIntruderSigningAlg.setSelectedIndex(0);
- }
- } else {
- resignIntruderJWS.setSelected(false);
- comboBoxIntruderSigningKeyId.setSelectedIndex(0);
-
- Key key = signingKeys.get(0);
- comboBoxIntruderSigningAlg.setModel(new DefaultComboBoxModel(key.getSigningAlgorithms()));
- }
- }
}
- private void updateSigningAlgorithmList() {
- Key key = keysModel.getSigningKeys().stream()
- .filter(k -> k.getID().equals(intruderConfig.signingKeyId()))
- .findFirst()
- .orElseThrow();
-
- JWSAlgorithm[] signingAlgorithms = key.getSigningAlgorithms();
- comboBoxIntruderSigningAlg.setModel(new DefaultComboBoxModel(signingAlgorithms));
-
- if (signingAlgorithms.length > 0) {
- JWSAlgorithm algorithm = signingAlgorithms[0];
- comboBoxIntruderSigningAlg.setSelectedItem(algorithm);
- intruderConfig.setSigningAlgorithm(algorithm);
- }
- }
-
- /**
- * Custom list cell renderer to color rows of combo box drop down list.
- */
- private static class HighlightComboRenderer implements ListCellRenderer {
- private final ListCellRenderer renderer = new DefaultListCellRenderer();
-
- @Override
- public Component getListCellRendererComponent(JList extends HighlightColor> list, HighlightColor value, int index, boolean isSelected, boolean cellHasFocus) {
- JLabel label = (JLabel) renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
-
- Color background = isSelected ? list.getSelectionBackground() : value.color;
- label.setBackground(background);
-
- return label;
- }
+ private void createUIComponents() {
+ proxyConfigView = new ProxyConfigView(userInterface, burpConfig.proxyConfig());
+ intruderConfigView = new IntruderConfigView(userInterface, new IntruderConfigModel(keysModel, burpConfig.intruderConfig()));
+ scannerConfigView = new ScannerConfigView(userInterface, burpConfig.scannerConfig(), isProVersion);
}
}
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigModel.java b/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigModel.java
new file mode 100644
index 0000000..a3dd2e1
--- /dev/null
+++ b/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigModel.java
@@ -0,0 +1,168 @@
+package com.blackberry.jwteditor.view.config;
+
+import burp.intruder.FuzzLocation;
+import burp.intruder.IntruderConfig;
+import com.blackberry.jwteditor.model.keys.Key;
+import com.blackberry.jwteditor.model.keys.KeysModel;
+import com.blackberry.jwteditor.model.keys.KeysModelListener.SimpleKeysModelListener;
+import com.nimbusds.jose.JWSAlgorithm;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Arrays.stream;
+
+class IntruderConfigModel {
+ static final String SIGNING_KEYS_UPDATED = "signingKeysUpdated";
+ static final String SELECTED_KEY_UPDATED = "selectedKeyUpdated";
+ static final String SIGNING_ALGORITHMS_UPDATED = "signingAlgorithmsUpdated";
+ static final String SELECTED_ALGORITHM_UPDATED = "selectedAlgorithmUpdated";
+ static final String RESIGN_UPDATED = "resignUpdated";
+
+ private static final JWSAlgorithm[] NO_ALGORITHMS = new JWSAlgorithm[0];
+
+ private final PropertyChangeSupport propertyChangeSupport;
+ private final KeysModel keysModel;
+ private final IntruderConfig intruderConfig;
+ private final List listeners;
+
+ private String[] signingKeyIds;
+ private JWSAlgorithm[] selectedKeySigningAlgorithms;
+
+ IntruderConfigModel(KeysModel keysModel, IntruderConfig intruderConfig) {
+ this.keysModel = keysModel;
+ this.intruderConfig = intruderConfig;
+ this.signingKeyIds = signingKeyIds();
+ this.selectedKeySigningAlgorithms = signingAlgorithms();
+ this.listeners = new ArrayList<>();
+ this.propertyChangeSupport = new PropertyChangeSupport(this);
+
+ keysModel.addKeyModelListener(new SimpleKeysModelListener(this::updateSigningKeyList));
+ }
+
+ String fuzzParameter() {
+ return intruderConfig.fuzzParameter();
+ }
+
+ void setFuzzParameter(String fuzzParameter) {
+ intruderConfig.setFuzzParameter(fuzzParameter);
+ }
+
+ FuzzLocation fuzzLocation() {
+ return intruderConfig.fuzzLocation();
+ }
+
+ void setFuzzLocation(FuzzLocation fuzzLocation) {
+ intruderConfig.setFuzzLocation(fuzzLocation);
+ }
+
+ FuzzLocation[] fuzzLocations() {
+ return FuzzLocation.values();
+ }
+
+ boolean hasSigningKeys() {
+ return !keysModel.getSigningKeys().isEmpty();
+ }
+
+ String[] signingKeyIds() {
+ return keysModel.getSigningKeys().stream().map(Key::getID).toArray(String[]::new);
+ }
+
+ String signingKeyId() {
+ return intruderConfig.signingKeyId();
+ }
+
+ public void setSigningKeyId(String signingKeyId) {
+ String oldSigningKeyId = intruderConfig.signingKeyId();
+ JWSAlgorithm[] oldAlgorithms = selectedKeySigningAlgorithms;
+ intruderConfig.setSigningKeyId(signingKeyId);
+
+ boolean selectedKeyUnchanged = (oldSigningKeyId == null && signingKeyId==null) || (oldSigningKeyId != null && oldSigningKeyId.equals(signingKeyId));
+
+ if (!selectedKeyUnchanged) {
+ selectedKeySigningAlgorithms = signingAlgorithms();
+ propertyChangeSupport.firePropertyChange(SIGNING_ALGORITHMS_UPDATED, oldAlgorithms, selectedKeySigningAlgorithms);
+ }
+ }
+
+ JWSAlgorithm[] signingAlgorithms() {
+ if (intruderConfig.signingKeyId() == null) {
+ return NO_ALGORITHMS;
+ }
+
+ return keysModel.getSigningKeys().stream()
+ .filter(k -> k.getID().equals(intruderConfig.signingKeyId()))
+ .findFirst()
+ .orElseThrow()
+ .getSigningAlgorithms();
+ }
+
+ JWSAlgorithm signingAlgorithm() {
+ return intruderConfig.signingAlgorithm();
+ }
+
+ void setSigningAlgorithm(JWSAlgorithm signingAlgorithm) {
+ intruderConfig.setSigningAlgorithm(signingAlgorithm);
+ }
+
+ boolean resign() {
+ return intruderConfig.resign() && hasSigningKeys();
+ }
+
+ void setResign(boolean resign) {
+ intruderConfig.setResign(resign);
+ }
+
+ void addPropertyChangeListener(PropertyChangeListener listener) {
+ listeners.add(listener);
+ propertyChangeSupport.addPropertyChangeListener(listener);
+ }
+
+ void clearListeners() {
+ listeners.forEach(propertyChangeSupport::removePropertyChangeListener);
+ }
+
+ private void updateSigningKeyList() {
+ String[] oldSigningKeyIds = signingKeyIds;
+ JWSAlgorithm[] oldSigningAlgorithms = selectedKeySigningAlgorithms;
+ JWSAlgorithm oldSigningAlgorithm = intruderConfig.signingAlgorithm();
+ signingKeyIds = signingKeyIds();
+ propertyChangeSupport.firePropertyChange(SIGNING_KEYS_UPDATED, oldSigningKeyIds, signingKeyIds);
+
+ String selectedKeyId = intruderConfig.signingKeyId();
+ boolean modelIsEmpty = signingKeyIds.length == 0;
+ boolean modelWasEmpty = oldSigningKeyIds.length == 0 && signingKeyIds.length > 0;
+ boolean selectedKeyDeleted = selectedKeyId != null && (signingKeyIds.length == 0 || stream(signingKeyIds).noneMatch(selectedKeyId::equals));
+ boolean wasResigning = intruderConfig.resign();
+
+ if (modelIsEmpty) {
+ selectedKeySigningAlgorithms = NO_ALGORITHMS;
+ propertyChangeSupport.firePropertyChange(SIGNING_ALGORITHMS_UPDATED, oldSigningAlgorithms, NO_ALGORITHMS);
+
+ String oldSigningKeyId = intruderConfig.signingKeyId();
+ intruderConfig.setSigningKeyId(null);
+ propertyChangeSupport.firePropertyChange(SELECTED_KEY_UPDATED, oldSigningKeyId, null);
+
+ intruderConfig.setSigningAlgorithm(null);
+ propertyChangeSupport.firePropertyChange(SELECTED_ALGORITHM_UPDATED, oldSigningAlgorithm, null);
+
+ intruderConfig.setResign(false);
+ propertyChangeSupport.firePropertyChange(RESIGN_UPDATED, wasResigning, false);
+ }
+ else if (modelWasEmpty || selectedKeyDeleted) {
+ String firstKeyId = signingKeyIds[0];
+ intruderConfig.setSigningKeyId(firstKeyId);
+ selectedKeySigningAlgorithms = signingAlgorithms();
+ JWSAlgorithm firstSigningAlgorithm = selectedKeySigningAlgorithms[0];
+ intruderConfig.setSigningAlgorithm(firstSigningAlgorithm);
+ intruderConfig.setResign(false);
+
+ propertyChangeSupport.firePropertyChange(SELECTED_KEY_UPDATED, null, firstKeyId);
+ propertyChangeSupport.firePropertyChange(SIGNING_ALGORITHMS_UPDATED, oldSigningAlgorithms, selectedKeySigningAlgorithms);
+ propertyChangeSupport.firePropertyChange(SELECTED_ALGORITHM_UPDATED, oldSigningAlgorithm, firstSigningAlgorithm);
+ propertyChangeSupport.firePropertyChange(RESIGN_UPDATED, wasResigning, false);
+ }
+ }
+}
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigView.form b/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigView.form
new file mode 100644
index 0000000..265fb65
--- /dev/null
+++ b/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigView.form
@@ -0,0 +1,143 @@
+
+
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigView.java b/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigView.java
new file mode 100644
index 0000000..e5e48df
--- /dev/null
+++ b/src/main/java/com/blackberry/jwteditor/view/config/IntruderConfigView.java
@@ -0,0 +1,104 @@
+/*
+Author : Dolph Flynn
+
+Copyright 2024 Dolph Flynn
+
+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 com.blackberry.jwteditor.view.config;
+
+import burp.api.montoya.ui.UserInterface;
+import burp.intruder.FuzzLocation;
+import com.blackberry.jwteditor.view.utils.DocumentAdapter;
+import com.nimbusds.jose.JWSAlgorithm;
+
+import javax.swing.*;
+
+import static com.blackberry.jwteditor.view.config.IntruderConfigModel.*;
+import static java.awt.Font.BOLD;
+
+
+class IntruderConfigView {
+ private JPanel mainPanel;
+ private JTextField intruderParameterName;
+ private JComboBox comboBoxPayloadPosition;
+ private JComboBox comboBoxIntruderSigningKeyId;
+ private JLabel intruderLabel;
+ private JLabel spacerLabel;
+ private JCheckBox resignIntruderJWS;
+ private JComboBox comboBoxIntruderSigningAlg;
+
+ IntruderConfigView(UserInterface userInterface, IntruderConfigModel model) {
+ intruderParameterName.setText(model.fuzzParameter());
+ intruderParameterName.getDocument().addDocumentListener(
+ new DocumentAdapter(e -> model.setFuzzParameter(intruderParameterName.getText()))
+ );
+
+ comboBoxPayloadPosition.setModel(new DefaultComboBoxModel<>(model.fuzzLocations()));
+ comboBoxPayloadPosition.setSelectedItem(model.fuzzLocation());
+ comboBoxPayloadPosition.addActionListener(e -> model.setFuzzLocation((FuzzLocation) comboBoxPayloadPosition.getSelectedItem()));
+
+ comboBoxIntruderSigningKeyId.setModel(new DefaultComboBoxModel<>(model.signingKeyIds()));
+ comboBoxIntruderSigningKeyId.setSelectedItem(model.signingKeyId());
+ comboBoxIntruderSigningKeyId.setEnabled(model.hasSigningKeys());
+ comboBoxIntruderSigningKeyId.addActionListener(e -> model.setSigningKeyId((String) comboBoxIntruderSigningKeyId.getSelectedItem()));
+
+ comboBoxIntruderSigningAlg.setModel(new DefaultComboBoxModel<>(model.signingAlgorithms()));
+ comboBoxIntruderSigningAlg.setSelectedItem(model.signingAlgorithm());
+ comboBoxIntruderSigningAlg.setEnabled(model.hasSigningKeys());
+ comboBoxIntruderSigningAlg.addActionListener(e -> model.setSigningAlgorithm((JWSAlgorithm) comboBoxIntruderSigningAlg.getSelectedItem()));
+
+ resignIntruderJWS.setEnabled(model.hasSigningKeys());
+ resignIntruderJWS.setSelected(model.resign());
+ resignIntruderJWS.addActionListener(e -> model.setResign(resignIntruderJWS.isSelected()));
+
+ intruderLabel.setFont(intruderLabel.getFont().deriveFont(BOLD));
+ userInterface.applyThemeToComponent(mainPanel);
+
+ updateControls(model.hasSigningKeys());
+
+ model.addPropertyChangeListener(evt -> {
+ switch (evt.getPropertyName()) {
+ case SIGNING_KEYS_UPDATED:
+ comboBoxIntruderSigningKeyId.setModel(new DefaultComboBoxModel<>((String[]) evt.getNewValue()));
+ comboBoxIntruderSigningKeyId.setSelectedItem(model.signingKeyId());
+ updateControls(model.hasSigningKeys());
+ resignIntruderJWS.setSelected(model.resign());
+ break;
+
+ case SIGNING_ALGORITHMS_UPDATED:
+ comboBoxIntruderSigningAlg.setModel(new DefaultComboBoxModel((JWSAlgorithm[]) evt.getNewValue()));
+ break;
+
+ case SELECTED_KEY_UPDATED:
+ comboBoxIntruderSigningKeyId.setSelectedItem(evt.getNewValue());
+ break;
+
+ case SELECTED_ALGORITHM_UPDATED:
+ comboBoxIntruderSigningAlg.setSelectedItem(evt.getNewValue());
+ break;
+
+ case RESIGN_UPDATED:
+ resignIntruderJWS.setSelected((Boolean) evt.getNewValue());
+ break;
+ }
+ });
+ }
+
+ private void updateControls(boolean hasSigningKeys) {
+ resignIntruderJWS.setEnabled(hasSigningKeys);
+ comboBoxIntruderSigningKeyId.setEnabled(hasSigningKeys);
+ comboBoxIntruderSigningAlg.setEnabled(hasSigningKeys);
+ }
+}
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/ProxyConfigView.form b/src/main/java/com/blackberry/jwteditor/view/config/ProxyConfigView.form
new file mode 100644
index 0000000..84b5ca7
--- /dev/null
+++ b/src/main/java/com/blackberry/jwteditor/view/config/ProxyConfigView.form
@@ -0,0 +1,66 @@
+
+
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/ProxyConfigView.java b/src/main/java/com/blackberry/jwteditor/view/config/ProxyConfigView.java
new file mode 100644
index 0000000..e8a11c6
--- /dev/null
+++ b/src/main/java/com/blackberry/jwteditor/view/config/ProxyConfigView.java
@@ -0,0 +1,69 @@
+/*
+Author : Dolph Flynn
+
+Copyright 2024 Dolph Flynn
+
+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 com.blackberry.jwteditor.view.config;
+
+import burp.api.montoya.ui.UserInterface;
+import burp.proxy.HighlightColor;
+import burp.proxy.ProxyConfig;
+
+import javax.swing.*;
+import java.awt.*;
+
+import static java.awt.Font.BOLD;
+
+
+class ProxyConfigView {
+ private JPanel mainPanel;
+ private JCheckBox checkBoxHighlightJWT;
+ private JComboBox comboBoxHighlightColor;
+ private JLabel proxyLabel;
+
+ ProxyConfigView(UserInterface userInterface, ProxyConfig proxyConfig) {
+ checkBoxHighlightJWT.setSelected(proxyConfig.highlightJWT());
+
+ checkBoxHighlightJWT.addActionListener(e -> {
+ comboBoxHighlightColor.setEnabled(checkBoxHighlightJWT.isSelected());
+ proxyConfig.setHighlightJWT(checkBoxHighlightJWT.isSelected());
+ });
+
+ comboBoxHighlightColor.setModel(new DefaultComboBoxModel<>(HighlightColor.values()));
+ comboBoxHighlightColor.setSelectedItem(proxyConfig.highlightColor());
+ comboBoxHighlightColor.setEnabled(proxyConfig.highlightJWT());
+ comboBoxHighlightColor.addActionListener(e -> proxyConfig.setHighlightColor((HighlightColor) comboBoxHighlightColor.getSelectedItem()));
+
+ proxyLabel.setFont(proxyLabel.getFont().deriveFont(BOLD));
+ userInterface.applyThemeToComponent(mainPanel);
+
+ comboBoxHighlightColor.setRenderer(new HighlightComboRenderer());
+ }
+
+ private static class HighlightComboRenderer implements ListCellRenderer {
+ private final ListCellRenderer renderer = new DefaultListCellRenderer();
+
+ @Override
+ public Component getListCellRendererComponent(JList extends HighlightColor> list, HighlightColor value, int index, boolean isSelected, boolean cellHasFocus) {
+ JLabel label = (JLabel) renderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+
+ Color background = isSelected ? list.getSelectionBackground() : value.color;
+ label.setBackground(background);
+
+ return label;
+ }
+ }
+}
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/ScannerConfigView.form b/src/main/java/com/blackberry/jwteditor/view/config/ScannerConfigView.form
new file mode 100644
index 0000000..a8ef9aa
--- /dev/null
+++ b/src/main/java/com/blackberry/jwteditor/view/config/ScannerConfigView.form
@@ -0,0 +1,96 @@
+
+
diff --git a/src/main/java/com/blackberry/jwteditor/view/config/ScannerConfigView.java b/src/main/java/com/blackberry/jwteditor/view/config/ScannerConfigView.java
new file mode 100644
index 0000000..9e11d8d
--- /dev/null
+++ b/src/main/java/com/blackberry/jwteditor/view/config/ScannerConfigView.java
@@ -0,0 +1,54 @@
+/*
+Author : Dolph Flynn
+
+Copyright 2024 Dolph Flynn
+
+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 com.blackberry.jwteditor.view.config;
+
+import burp.api.montoya.ui.UserInterface;
+import burp.scanner.ScannerConfig;
+import com.blackberry.jwteditor.view.utils.DocumentAdapter;
+
+import javax.swing.*;
+
+import static java.awt.Font.BOLD;
+
+
+class ScannerConfigView {
+ private JPanel mainPanel;
+ private JCheckBox checkBoxHeaderInsertionPoint;
+ private JTextField scannerParameterName;
+ private JLabel scannerLabel;
+
+ ScannerConfigView(UserInterface userInterface, ScannerConfig scannerConfig, boolean isProVersion) {
+
+ checkBoxHeaderInsertionPoint.setEnabled(isProVersion);
+ checkBoxHeaderInsertionPoint.setSelected(scannerConfig.enableHeaderJWSInsertionPointLocation());
+ checkBoxHeaderInsertionPoint.addActionListener(e -> {
+ scannerConfig.setEnableHeaderJWSInsertionPointLocation(checkBoxHeaderInsertionPoint.isSelected());
+ scannerParameterName.setEnabled(checkBoxHeaderInsertionPoint.isSelected());
+ });
+
+ scannerParameterName.setEnabled(scannerConfig.enableHeaderJWSInsertionPointLocation());
+ scannerParameterName.setText(scannerConfig.insertionPointLocationParameterName());
+ scannerParameterName.getDocument().addDocumentListener(
+ new DocumentAdapter(e -> scannerConfig.setInsertionPointLocationParameterName(scannerParameterName.getText()))
+ );
+
+ scannerLabel.setFont(scannerLabel.getFont().deriveFont(BOLD));
+ userInterface.applyThemeToComponent(mainPanel);
+ }
+}
diff --git a/src/test/java/com/blackberry/jwteditor/KeyLoader.java b/src/test/java/com/blackberry/jwteditor/KeyLoader.java
new file mode 100644
index 0000000..320d7a1
--- /dev/null
+++ b/src/test/java/com/blackberry/jwteditor/KeyLoader.java
@@ -0,0 +1,56 @@
+/*
+Author : Dolph Flynn
+
+Copyright 2024 Dolph Flynn
+
+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 com.blackberry.jwteditor;
+
+import com.blackberry.jwteditor.exceptions.PemException;
+import com.blackberry.jwteditor.exceptions.UnsupportedKeyException;
+import com.blackberry.jwteditor.model.keys.JWKKeyFactory;
+import com.blackberry.jwteditor.model.keys.Key;
+import com.blackberry.jwteditor.utils.PEMUtils;
+import com.nimbusds.jose.jwk.JWK;
+
+public class KeyLoader {
+
+ public static Key loadECKey(String pem, String keyId) {
+ try {
+ JWK jwk = PEMUtils.pemToECKey(pem, keyId);
+ return JWKKeyFactory.from(jwk);
+ } catch (PemException | UnsupportedKeyException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public static Key loadRSAKey(String pem, String keyId) {
+ try {
+ JWK jwk = PEMUtils.pemToRSAKey(pem, keyId);
+ return JWKKeyFactory.from(jwk);
+ } catch (PemException | UnsupportedKeyException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public static Key loadOKPKey(String pem, String keyId) {
+ try {
+ JWK jwk = PEMUtils.pemToOctetKeyPair(pem, keyId);
+ return JWKKeyFactory.from(jwk);
+ } catch (PemException | UnsupportedKeyException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/src/test/java/com/blackberry/jwteditor/KeysModelBuilder.java b/src/test/java/com/blackberry/jwteditor/KeysModelBuilder.java
index 647ddfb..054e9d5 100644
--- a/src/test/java/com/blackberry/jwteditor/KeysModelBuilder.java
+++ b/src/test/java/com/blackberry/jwteditor/KeysModelBuilder.java
@@ -18,50 +18,29 @@
package com.blackberry.jwteditor;
-import com.blackberry.jwteditor.exceptions.PemException;
-import com.blackberry.jwteditor.exceptions.UnsupportedKeyException;
-import com.blackberry.jwteditor.model.keys.JWKKeyFactory;
import com.blackberry.jwteditor.model.keys.Key;
import com.blackberry.jwteditor.model.keys.KeysModel;
-import com.blackberry.jwteditor.utils.PEMUtils;
-import com.nimbusds.jose.jwk.JWK;
import java.util.concurrent.atomic.AtomicInteger;
+import static com.blackberry.jwteditor.KeyLoader.*;
+
class KeysModelBuilder {
private final AtomicInteger keyId = new AtomicInteger();
private final KeysModel model = new KeysModel();
KeysModelBuilder withECKey(String pem) {
- try {
- JWK jwk = PEMUtils.pemToECKey(pem, Integer.toString(keyId.incrementAndGet()));
- model.addKey(JWKKeyFactory.from(jwk));
- } catch (PemException | UnsupportedKeyException e) {
- throw new IllegalStateException(e);
- }
-
+ model.addKey(loadECKey(pem, nextKeyId()));
return this;
}
KeysModelBuilder withRSAKey(String pem) {
- try {
- JWK jwk = PEMUtils.pemToRSAKey(pem, Integer.toString(keyId.incrementAndGet()));
- model.addKey(JWKKeyFactory.from(jwk));
- } catch (PemException | UnsupportedKeyException e) {
- throw new IllegalStateException(e);
- }
-
+ model.addKey(loadRSAKey(pem, nextKeyId()));
return this;
}
KeysModelBuilder withOKPKey(String pem) {
- try {
- JWK jwk = PEMUtils.pemToOctetKeyPair(pem, Integer.toString(keyId.incrementAndGet()));
- model.addKey(JWKKeyFactory.from(jwk));
- } catch (PemException | UnsupportedKeyException e) {
- throw new IllegalStateException(e);
- }
-
+ model.addKey(loadOKPKey(pem, nextKeyId()));
return this;
}
@@ -74,6 +53,10 @@ KeysModel build() {
return model;
}
+ private String nextKeyId() {
+ return Integer.toString(keyId.incrementAndGet());
+ }
+
static KeysModelBuilder keysModel() {
return new KeysModelBuilder();
}
diff --git a/src/test/java/com/blackberry/jwteditor/KeysModelTest.java b/src/test/java/com/blackberry/jwteditor/KeysModelTest.java
index 01d00ed..a5d11e4 100644
--- a/src/test/java/com/blackberry/jwteditor/KeysModelTest.java
+++ b/src/test/java/com/blackberry/jwteditor/KeysModelTest.java
@@ -18,12 +18,16 @@
package com.blackberry.jwteditor;
+import com.blackberry.jwteditor.model.keys.Key;
import com.blackberry.jwteditor.model.keys.KeysModel;
+import com.blackberry.jwteditor.model.keys.KeysModelListener;
import com.blackberry.jwteditor.model.keys.KeysModelListener.InertKeysModelListener;
import com.blackberry.jwteditor.model.keys.PasswordKey;
import org.junit.jupiter.api.Test;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import static com.blackberry.jwteditor.KeysModelBuilder.keysModel;
import static data.PemData.*;
@@ -122,4 +126,52 @@ public void notifyKeyDeleted(int rowIndex) {
assertThat(noOfListenerInvocations.get()).isEqualTo(4);
}
+
+ @Test
+ void addKeyWithNewId_listenerOnlyNotifiedOfKeyInserted() {
+ AtomicBoolean keyDeleted = new AtomicBoolean();
+ AtomicReference insertedKey = new AtomicReference<>();
+ Key key = new PasswordKey("testKeyId", "secret", 8, 1337);
+ KeysModel model = new KeysModel();
+
+ model.addKeyModelListener(new KeysModelListener() {
+ @Override
+ public void notifyKeyInserted(Key key) {
+ insertedKey.compareAndSet(null, key);
+ }
+
+ @Override
+ public void notifyKeyDeleted(int rowIndex) {
+ keyDeleted.set(true);
+ }
+
+ @Override
+ public void notifyKeyDeleted(Key key) {
+ keyDeleted.set(true);
+ }
+ });
+
+ model.addKey(key);
+
+ assertThat(insertedKey.get()).isSameAs(key);
+ assertThat(keyDeleted).isFalse();
+ }
+
+ @Test
+ void addKeyWithExistingId_listenerNotifiedOfKeyDeletion() {
+ AtomicReference deletedKey = new AtomicReference<>();
+ Key oldKey = new PasswordKey("testKeyId", "secret", 8, 1337);
+ KeysModel model = keysModel().withKey(oldKey).build();
+
+ model.addKeyModelListener(new InertKeysModelListener() {
+ @Override
+ public void notifyKeyDeleted(Key key) {
+ deletedKey.set(key);
+ }
+ });
+
+ model.addKey(new PasswordKey("testKeyId", "sci", 8, 1337));
+
+ assertThat(deletedKey.get()).isSameAs(oldKey);
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/blackberry/jwteditor/view/config/IntruderConfigModelTest.java b/src/test/java/com/blackberry/jwteditor/view/config/IntruderConfigModelTest.java
new file mode 100644
index 0000000..a78954d
--- /dev/null
+++ b/src/test/java/com/blackberry/jwteditor/view/config/IntruderConfigModelTest.java
@@ -0,0 +1,480 @@
+package com.blackberry.jwteditor.view.config;
+
+import burp.intruder.FuzzLocation;
+import burp.intruder.IntruderConfig;
+import com.blackberry.jwteditor.model.keys.KeysModel;
+import com.nimbusds.jose.JWSAlgorithm;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static burp.intruder.FuzzLocation.HEADER;
+import static com.blackberry.jwteditor.KeyLoader.*;
+import static com.blackberry.jwteditor.view.config.IntruderConfigModel.*;
+import static com.nimbusds.jose.JWSAlgorithm.*;
+import static data.PemData.*;
+import static java.util.Arrays.asList;
+import static java.util.Arrays.stream;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class IntruderConfigModelTest {
+ private static final String KEY_ID = "id";
+
+ private final KeysModel keysModel = new KeysModel();
+ private final IntruderConfig wrappedConfig = new IntruderConfig();
+ private final IntruderConfigModel model = new IntruderConfigModel(keysModel, wrappedConfig);
+
+ @AfterEach
+ void cleanUp() {
+ model.clearListeners();
+ stream(model.signingKeyIds()).forEach(keysModel::deleteKey);
+ }
+
+ @Test
+ void whenSetFuzzParameter_thenValueSetOnModelAndWrappedConfig() {
+ String parameter = "kid";
+ model.setFuzzParameter(parameter);
+
+ assertThat(model.fuzzParameter()).isEqualTo(parameter);
+ assertThat(wrappedConfig.fuzzParameter()).isEqualTo(parameter);
+ }
+
+ @Test
+ void whenGetFuzzParameter_thenValueRetrievedFromWrappedConfig() {
+ String parameter = "kid";
+ wrappedConfig.setFuzzParameter(parameter);
+
+ assertThat(model.fuzzParameter()).isEqualTo(parameter);
+ }
+
+ @Test
+ void whenSetFuzzLocation_thenValueSetOnModelAndWrappedConfig() {
+ FuzzLocation location = HEADER;
+ model.setFuzzLocation(location);
+
+ assertThat(model.fuzzLocation()).isEqualTo(location);
+ assertThat(wrappedConfig.fuzzLocation()).isEqualTo(location);
+ }
+
+ @Test
+ void whenGetFuzzLocation_thenValueRetrievedFromWrappedConfig() {
+ FuzzLocation location = HEADER;
+ wrappedConfig.setFuzzLocation(location);
+
+ assertThat(model.fuzzLocation()).isEqualTo(location);
+ }
+
+ @Test
+ void whenGetFuzzLocations_thenAllFuzzLocationValuesReturned() {
+ assertThat(model.fuzzLocations()).containsExactly(FuzzLocation.values());
+ }
+
+ @Test
+ void whenEmptyKeysModel_thenHasSigningKeyIsFalse() {
+ assertThat(model.hasSigningKeys()).isFalse();
+ }
+
+ @Test
+ void whenEmptyKeysModel_thenNoSigningKeyIds() {
+ assertThat(model.signingKeyIds()).isEmpty();
+ }
+
+ @Test
+ void whenEmptyKeysModel_thenSelectedSigningKeyIdIsNull() {
+ assertThat(model.signingKeyId()).isNull();
+ }
+
+ @Test
+ void whenEmptyKeysModel_thenNoSigningAlgorithms() {
+ assertThat(model.signingAlgorithms()).isEmpty();
+ }
+
+ @Test
+ void whenEmptyKeysModel_thenSelectedSigningAlgorithmsIsNull() {
+ assertThat(model.signingAlgorithm()).isNull();
+ }
+
+ @Test
+ void whenEmptyKeysModel_thenResignIsFalse() {
+ wrappedConfig.setResign(true);
+
+ assertThat(model.resign()).isFalse();
+ }
+
+ @Test
+ void whenKeysModelNotEmptyButHasNoSigningKeys_thenHasSigningKeysIsFalse() {
+ keysModel.addKey(loadOKPKey(X25519Public, "kid"));
+
+ assertThat(model.hasSigningKeys()).isFalse();
+ }
+
+ @Test
+ void whenKeysModelHasSigningKeys_thenHasSigningKeysIsTrue() {
+ keysModel.addKey(loadRSAKey(RSA1024Private, "kid"));
+
+ assertThat(model.hasSigningKeys()).isTrue();
+ }
+
+ @Test
+ void whenKeysModelNotEmptyButHasNoSigningKeys_thenSigningKeyIdsEmpty() {
+ keysModel.addKey(loadOKPKey(X25519Public, "kid"));
+
+ assertThat(model.signingKeyIds()).isEmpty();
+ }
+
+ @Test
+ void whenKeysModelHasSigningKeys_thenSigningKeyIdsNotEmpty() {
+ keysModel.addKey(loadRSAKey(RSA1024Private, "kid1"));
+ keysModel.addKey(loadECKey(PRIME256v1PrivateSEC1, "kid2"));
+
+ assertThat(model.signingKeyIds()).containsExactly("kid1", "kid2");
+ }
+
+ @Test
+ void whenKeysModelHasSigningAndNonSigningKeys_thenSigningKeyIdsContainsOnlySigningKeyIds() {
+ keysModel.addKey(loadRSAKey(RSA1024Private, "kid1"));
+ keysModel.addKey(loadOKPKey(X25519Public, "kid2"));
+ keysModel.addKey(loadECKey(PRIME256v1PrivateSEC1, "kid3"));
+
+ assertThat(model.signingKeyIds()).containsExactly("kid1", "kid3");
+ }
+
+ @Test
+ void givenKeysModelHasSigningKey_whenSetSigningKeyId_thenSigningKeyAndSigningAlgorithmsUpdated() {
+ String keyId = "kid1";
+ keysModel.addKey(loadRSAKey(RSA1024Private, keyId));
+
+ model.setSigningKeyId(keyId);
+
+ assertThat(model.signingKeyId()).isEqualTo(keyId);
+ assertThat(wrappedConfig.signingKeyId()).isEqualTo(keyId);
+ assertThat(model.signingAlgorithms()).containsExactly(RS256, RS384, RS512, PS256, PS384);
+ }
+
+ @Test
+ void givenKeysModelHasSigningKey_whenSetInvalidSigningKeyId_thenExceptionThrown() {
+ keysModel.addKey(loadRSAKey(RSA1024Private, "kid1"));
+
+ assertThrows(NoSuchElementException.class, () -> model.setSigningKeyId("invalid"));
+ }
+
+ @Test
+ void givenKeysModelHasSigningKey_whenSetSigningKeyId_thenSigningAlgorithmsUpdatedEventPublished() {
+ List publishedAlgorithms = new ArrayList<>();
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SIGNING_ALGORITHMS_UPDATED) && evt.getNewValue() instanceof JWSAlgorithm[] algorithms) {
+ publishedAlgorithms.addAll(asList(algorithms));
+ }
+ });
+
+ String keyId = "kid1";
+ keysModel.addKey(loadRSAKey(RSA1024Private, keyId));
+
+ model.setSigningKeyId(keyId);
+
+ assertThat(publishedAlgorithms).containsExactly(RS256, RS384, RS512, PS256, PS384);
+ }
+
+ @Test
+ void givenKeysModelHasSigningKey_whenSetAlgorithm_thenAlgorithmStored() {
+ String keyId = "kid1";
+ keysModel.addKey(loadRSAKey(RSA1024Private, keyId));
+
+ model.setSigningAlgorithm(PS256);
+
+ assertThat(model.signingAlgorithm()).isEqualTo(PS256);
+ assertThat(wrappedConfig.signingAlgorithm()).isEqualTo(PS256);
+ }
+
+ @Test
+ void givenNoSelectedKeyIdOrAlgorithm_whenSetResign_thenResignFalse() {
+ model.setResign(true);
+
+ assertThat(model.resign()).isFalse();
+ }
+
+ @Test
+ void givenSelectedAlgorithmButNoKeyId_whenSetResign_thenResignFalse() {
+ model.setSigningAlgorithm(EdDSA);
+
+ model.setResign(true);
+
+ assertThat(model.resign()).isFalse();
+ }
+
+ @Test
+ void givenSelectedKeyIdAndAlgorithm_whenSetResign_thenResignFalse() {
+ String keyId = "kid";
+ keysModel.addKey(loadRSAKey(RSA1024Private, keyId));
+ model.setSigningKeyId(keyId);
+ model.setSigningAlgorithm(RS256);
+
+ model.setResign(true);
+
+ assertThat(model.resign()).isTrue();
+ }
+
+ @Test
+ void whenKeyAddedToModel_thenSigningKeysUpdatedEventFired() {
+ List oldIds = new ArrayList<>();
+ List newIds = new ArrayList<>();
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SIGNING_KEYS_UPDATED)) {
+ oldIds.addAll(asList((String[]) evt.getOldValue()));
+ newIds.addAll(asList((String[]) evt.getNewValue()));
+ }
+ });
+
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+
+ assertThat(oldIds).isEmpty();
+ assertThat(newIds).containsExactly(KEY_ID);
+ }
+
+ @Test
+ void whenKeyDeletedFromModel_thenSigningKeysUpdatedEventFired() {
+ List oldIds = new ArrayList<>();
+ List newIds = new ArrayList<>();
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SIGNING_KEYS_UPDATED)) {
+ oldIds.addAll(asList((String[]) evt.getOldValue()));
+ newIds.addAll(asList((String[]) evt.getNewValue()));
+ }
+ });
+
+ keysModel.deleteKey(KEY_ID);
+
+ assertThat(oldIds).containsExactly(KEY_ID);
+ assertThat(newIds).isEmpty();
+ }
+
+ @Test
+ void givenSelectedKey_whenSelectedKeyDeletedFromModel_thenSelectedKeyUpdatedFired() {
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+ model.setSigningKeyId(KEY_ID);
+ AtomicReference event = new AtomicReference<>();
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SELECTED_KEY_UPDATED)) {
+ event.set(evt);
+ }
+ });
+
+ keysModel.deleteKey(KEY_ID);
+
+ assertThat(event.get().getOldValue()).isEqualTo(KEY_ID);
+ assertThat(event.get().getNewValue()).isNull();
+ }
+
+ @Test
+ void givenSelectedKey_whenSelectedKeyDeletedFromModel_thenSigningAlgorithmsEventUpdatedFired() {
+ List oldAlgorithms = new ArrayList<>();
+ List newAlgorithms = new ArrayList<>();
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+ model.setSigningKeyId(KEY_ID);
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SIGNING_ALGORITHMS_UPDATED)) {
+ oldAlgorithms.addAll(asList((JWSAlgorithm[]) evt.getOldValue()));
+ newAlgorithms.addAll(asList((JWSAlgorithm[]) evt.getNewValue()));
+ }
+ });
+
+ keysModel.deleteKey(KEY_ID);
+
+ assertThat(oldAlgorithms).containsExactly(RS256, RS384, RS512, PS256, PS384);
+ assertThat(newAlgorithms).isEmpty();
+ }
+
+ @Test
+ void givenSelectedAlgorithm_whenSelectedKeyDeletedFromModel_thenSelectedAlgorithmUpdatedFired() {
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+ model.setSigningKeyId(KEY_ID);
+ model.setSigningAlgorithm(RS256);
+ AtomicReference event = new AtomicReference<>();
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SELECTED_ALGORITHM_UPDATED)) {
+ event.set(evt);
+ }
+ });
+
+ keysModel.deleteKey(KEY_ID);
+
+ assertThat(event.get().getOldValue()).isEqualTo(RS256);
+ assertThat(event.get().getNewValue()).isNull();
+ }
+
+ @Test
+ void givenSelectedAlgorithm_whenResigningAndSelectedKeyDeleted_thenResignDisabled() {
+ AtomicBoolean resign = new AtomicBoolean(true);
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(RESIGN_UPDATED) && evt.getNewValue() instanceof Boolean selected) {
+ resign.set(selected);
+ }
+ });
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+ model.setResign(true);
+
+ keysModel.deleteKey(KEY_ID);
+
+ assertThat(model.resign()).isFalse();
+ assertThat(resign).isFalse();
+ }
+
+ @Test
+ void givenEmptyModel_whenKeyLoaded_thenKeyAndFirstAlgorithmSelected() {
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+
+ assertThat(model.signingKeyId()).isEqualTo(KEY_ID);
+ assertThat(wrappedConfig.signingKeyId()).isEqualTo(KEY_ID);
+ assertThat(model.signingAlgorithm()).isEqualTo(RS256);
+ assertThat(wrappedConfig.signingAlgorithm()).isEqualTo(RS256);
+ assertThat(model.signingAlgorithms()).containsExactly(RS256, RS384, RS512, PS256, PS384);
+ }
+
+ @Test
+ void givenEmptyModel_whenKeyLoaded_thenSigningKeysUpdatedEventFired() {
+ List oldIds = new ArrayList<>();
+ List newIds = new ArrayList<>();
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SIGNING_KEYS_UPDATED)) {
+ oldIds.addAll(asList((String[]) evt.getOldValue()));
+ newIds.addAll(asList((String[]) evt.getNewValue()));
+ }
+ });
+
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+
+ assertThat(oldIds).isEmpty();
+ assertThat(newIds).containsExactly(KEY_ID);
+ }
+
+ @Test
+ void givenEmptyModel_whenKeyLoaded_thenSelectedKeyUpdatedEventFired() {
+ AtomicReference oldKeyId = new AtomicReference<>();
+ AtomicReference newKeyId = new AtomicReference<>();
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SELECTED_KEY_UPDATED)) {
+ oldKeyId.set((String) evt.getOldValue());
+ newKeyId.set((String) evt.getNewValue());
+ }
+ });
+
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+
+ assertThat(oldKeyId.get()).isNull();
+ assertThat(newKeyId.get()).isEqualTo(KEY_ID);
+ }
+
+ @Test
+ void givenEmptyModel_whenKeyLoaded_thenSigningAlgorithmsUpdatedEventFired() {
+ List oldAlgorithms = new ArrayList<>();
+ List newAlgorithms = new ArrayList<>();
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SIGNING_ALGORITHMS_UPDATED)) {
+ oldAlgorithms.addAll(asList((JWSAlgorithm[]) evt.getOldValue()));
+ newAlgorithms.addAll(asList((JWSAlgorithm[]) evt.getNewValue()));
+ }
+ });
+
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+
+ assertThat(oldAlgorithms).isEmpty();
+ assertThat(newAlgorithms).containsExactly(RS256, RS384, RS512, PS256, PS384);
+ }
+
+ @Test
+ void givenEmptyModel_whenKeyLoaded_thenSelectedAlgorithmUpdatedEventFired() {
+ AtomicReference oldSigningAlgorithm = new AtomicReference<>();
+ AtomicReference newSigningAlgorithm = new AtomicReference<>();
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(SELECTED_ALGORITHM_UPDATED)) {
+ oldSigningAlgorithm.set((JWSAlgorithm) evt.getOldValue());
+ newSigningAlgorithm.set((JWSAlgorithm) evt.getNewValue());
+ }
+ });
+
+ keysModel.addKey(loadRSAKey(RSA1024Private, KEY_ID));
+
+ assertThat(oldSigningAlgorithm.get()).isNull();
+ assertThat(newSigningAlgorithm.get()).isEqualTo(RS256);
+ }
+
+ @Test
+ void givenTwoKeysInModel_whenSelectedKeyDeleted_thenRemainingKeyAndFirstAlgorithmSelected() {
+ String kid = "kid2";
+ keysModel.addKey(loadRSAKey(RSA1024Private, kid));
+ keysModel.addKey(loadOKPKey(ED25519Private, KEY_ID));
+
+ keysModel.deleteKey(kid);
+
+ assertThat(model.signingKeyId()).isEqualTo(KEY_ID);
+ assertThat(wrappedConfig.signingKeyId()).isEqualTo(KEY_ID);
+ assertThat(model.signingAlgorithm()).isEqualTo(EdDSA);
+ assertThat(wrappedConfig.signingAlgorithm()).isEqualTo(EdDSA);
+ assertThat(model.signingAlgorithms()).containsExactly(EdDSA);
+ }
+
+ @Test
+ void givenTwoKeysInModel_whenResigningAndSelectedKeyDeleted_thenResignDisabled() {
+ AtomicBoolean resign = new AtomicBoolean(true);
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(RESIGN_UPDATED) && evt.getNewValue() instanceof Boolean selected) {
+ resign.set(selected);
+ }
+ });
+
+ String kid = "kid2";
+ keysModel.addKey(loadRSAKey(RSA1024Private, kid));
+ keysModel.addKey(loadOKPKey(ED25519Private, KEY_ID));
+ model.setResign(true);
+
+ keysModel.deleteKey(kid);
+
+ assertThat(model.resign()).isFalse();
+ assertThat(resign).isFalse();
+ }
+
+ @Test
+ void givenMultipleKeysInModel_whenSelectedKeyDeleted_thenFirstKeyAndAlgorithmSelected() {
+ String kid = "kid2";
+ keysModel.addKey(loadRSAKey(RSA1024Private, kid));
+ keysModel.addKey(loadOKPKey(ED25519Private, KEY_ID));
+ keysModel.addKey(loadOKPKey(X448Private, "kid3"));
+
+ keysModel.deleteKey(kid);
+
+ assertThat(model.signingKeyId()).isEqualTo(KEY_ID);
+ assertThat(wrappedConfig.signingKeyId()).isEqualTo(KEY_ID);
+ assertThat(model.signingAlgorithm()).isEqualTo(EdDSA);
+ assertThat(wrappedConfig.signingAlgorithm()).isEqualTo(EdDSA);
+ assertThat(model.signingAlgorithms()).containsExactly(EdDSA);
+ }
+
+ @Test
+ void givenMultipleKeysInModel_whenResigningAndSelectedKeyDeleted_thenResignDisabled() {
+ AtomicBoolean resign = new AtomicBoolean(true);
+ model.addPropertyChangeListener(evt -> {
+ if (evt.getPropertyName().equals(RESIGN_UPDATED) && evt.getNewValue() instanceof Boolean selected) {
+ resign.set(selected);
+ }
+ });
+
+ String kid = "kid2";
+ keysModel.addKey(loadRSAKey(RSA1024Private, kid));
+ keysModel.addKey(loadOKPKey(ED25519Private, KEY_ID));
+ keysModel.addKey(loadOKPKey(X448Private, "kid3"));
+ model.setResign(true);
+
+ keysModel.deleteKey(kid);
+
+ assertThat(model.resign()).isFalse();
+ assertThat(resign).isFalse();
+ }
+}
\ No newline at end of file