/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2013 The ZAP Development Team
*
* 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.zaproxy.zap.extension.authentication;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Vector;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.model.Session;
import org.zaproxy.zap.authentication.AbstractAuthenticationMethodOptionsPanel;
import org.zaproxy.zap.authentication.AuthenticationIndicatorsPanel;
import org.zaproxy.zap.authentication.AuthenticationMethod;
import org.zaproxy.zap.authentication.AuthenticationMethodType;
import org.zaproxy.zap.extension.users.ExtensionUserManagement;
import org.zaproxy.zap.model.Context;
import org.zaproxy.zap.utils.FontUtils;
import org.zaproxy.zap.utils.ZapTextField;
import org.zaproxy.zap.view.AbstractContextPropertiesPanel;
import org.zaproxy.zap.view.LayoutHelper;
/**
* The Context Panel shown for configuring a Context's authentication methods.
*/
public class ContextAuthenticationPanel extends AbstractContextPropertiesPanel {
private static final Logger log = Logger.getLogger(ContextAuthenticationPanel.class);
private static final long serialVersionUID = -898084998156067286L;
/** The Constant PANEL NAME. */
private static final String PANEL_NAME = Constant.messages.getString("authentication.panel.title");
private static final String FIELD_LABEL_LOGGED_IN_INDICATOR = Constant.messages
.getString("authentication.panel.label.loggedIn");
private static final String FIELD_LABEL_LOGGED_OUT_INDICATOR = Constant.messages
.getString("authentication.panel.label.loggedOut");
private static final String FIELD_LABEL_TYPE_SELECT = Constant.messages
.getString("authentication.panel.label.typeSelect");
private static final String LABEL_DESCRIPTION = Constant.messages
.getString("authentication.panel.label.description");
private static final String PANEL_TITLE_CONFIG = Constant.messages
.getString("authentication.panel.label.configTitle");
private static final String LABEL_CONFIG_NOT_NEEDED = Constant.messages
.getHtmlWrappedString("sessionmanagement.panel.label.noConfigPanel");
/** The extension. */
private ExtensionAuthentication extension;
/** The authentication method types combo box. */
private JComboBox<AuthenticationMethodType> authenticationMethodsComboBox;
/** The selected authentication method. */
private AuthenticationMethod selectedAuthenticationMethod;
/** The shown method type. */
private AuthenticationMethodType shownMethodType;
/** The shown config panel. */
private AbstractAuthenticationMethodOptionsPanel shownConfigPanel;
/** The container panel for the authentication method's configuration. */
private JPanel configContainerPanel;
private ZapTextField loggedInIndicaterRegexField = null;
private ZapTextField loggedOutIndicaterRegexField = null;
/** Hacked used to make sure a confirmation is not needed if changes where done during init. */
private boolean needsConfirm = true;
private AuthenticationIndicatorsPanel authenticationIndicatorsPanel;
/**
* Instantiates a new context authentication configuration panel.
*
* @param extension the extension
* @param context the context
*/
public ContextAuthenticationPanel(ExtensionAuthentication extension, Context context) {
super(context.getIndex());
this.extension = extension;
initialize();
}
public static String buildName(int contextId) {
return contextId + ": " + PANEL_NAME;
}
/**
* Initialize the panel.
*/
private void initialize() {
this.setLayout(new CardLayout());
this.setName(buildName(getContextIndex()));
this.setLayout(new GridBagLayout());
this.setBorder(new EmptyBorder(2, 2, 2, 2));
this.add(new JLabel(LABEL_DESCRIPTION), LayoutHelper.getGBC(0, 0, 1, 1.0D));
// Method type combo box
this.add(new JLabel(FIELD_LABEL_TYPE_SELECT),
LayoutHelper.getGBC(0, 1, 1, 1.0D, new Insets(20, 0, 5, 5)));
this.add(getAuthenticationMethodsComboBox(), LayoutHelper.getGBC(0, 2, 1, 1.0D));
// Method config panel container
this.add(getConfigContainerPanel(), LayoutHelper.getGBC(0, 3, 1, 1.0d, new Insets(10, 0, 10, 0)));
// Logged In/Out indicators
this.add(new JLabel(FIELD_LABEL_LOGGED_IN_INDICATOR), LayoutHelper.getGBC(0, 4, 1, 1.0D));
this.add(getLoggedInIndicaterRegexField(), LayoutHelper.getGBC(0, 5, 1, 1.0D));
this.add(new JLabel(FIELD_LABEL_LOGGED_OUT_INDICATOR), LayoutHelper.getGBC(0, 6, 1, 1.0D));
this.add(getLoggedOutIndicaterRegexField(), LayoutHelper.getGBC(0, 7, 1, 1.0D));
// Padding
this.add(new JLabel(), LayoutHelper.getGBC(0, 99, 1, 1.0D, 1.0D));
}
/**
* Changes the shown method's configuration panel (used to display brief info about the method
* and configure it) with a new one, based on a new method type. If {@code null} is provided as
* a parameter, nothing is shown. If the provided method type does not require configuration, a
* simple message is shown stating that no configuration is needed.
*
* @param newMethodType the new method type. If null, nothing is shown.
*/
private void changeMethodConfigPanel(AuthenticationMethodType newMethodType) {
// If there's no new method, don't display anything
if (newMethodType == null) {
getConfigContainerPanel().removeAll();
getConfigContainerPanel().setVisible(false);
this.shownMethodType = null;
return;
}
// If a panel of the correct type is already shown, do nothing
if (shownMethodType != null && newMethodType.getClass().equals(shownMethodType.getClass())) {
return;
}
log.info("Creating new panel for configuring: " + newMethodType.getName());
this.getConfigContainerPanel().removeAll();
// show the panel according to whether the authentication type needs configuration
if (newMethodType.hasOptionsPanel()) {
shownConfigPanel = newMethodType.buildOptionsPanel(getUISharedContext());
getConfigContainerPanel().add(shownConfigPanel, BorderLayout.CENTER);
} else {
shownConfigPanel = null;
getConfigContainerPanel().add(new JLabel(LABEL_CONFIG_NOT_NEEDED), BorderLayout.CENTER);
}
this.shownMethodType = newMethodType;
this.getConfigContainerPanel().setVisible(true);
this.getConfigContainerPanel().revalidate();
}
/**
* Gets the authentication method types combo box.
*
* @return the authentication methods combo box
*/
protected JComboBox<AuthenticationMethodType> getAuthenticationMethodsComboBox() {
if (authenticationMethodsComboBox == null) {
Vector<AuthenticationMethodType> methods = new Vector<>(extension.getAuthenticationMethodTypes());
authenticationMethodsComboBox = new JComboBox<>(methods);
authenticationMethodsComboBox.setSelectedItem(null);
// Prepare the listener for the change of selection
authenticationMethodsComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED && !e.getItem().equals(shownMethodType)) {
log.debug("Selected new Authentication type: " + e.getItem());
if (needsConfirm && !confirmAndExecuteUsersDeletion()) {
log.debug("Cancelled change of authentication type.");
authenticationMethodsComboBox.setSelectedItem(shownMethodType);
return;
}
resetLoggedInOutIndicators();
// Prepare the new authentication method
AuthenticationMethodType type = ((AuthenticationMethodType) e.getItem());
// If no authentication method was previously selected or it's a different
// class, create a new authentication method object
if (selectedAuthenticationMethod == null
|| !type.isTypeForMethod(selectedAuthenticationMethod)) {
selectedAuthenticationMethod = type.createAuthenticationMethod(getContextIndex());
}
// Show the configuration panel
changeMethodConfigPanel(type);
if (type.hasOptionsPanel()) {
shownConfigPanel.bindMethod(selectedAuthenticationMethod, getAuthenticationIndicatorsPanel());
}
}
}
});
}
return authenticationMethodsComboBox;
}
private AuthenticationIndicatorsPanel getAuthenticationIndicatorsPanel() {
if (authenticationIndicatorsPanel == null) {
authenticationIndicatorsPanel = new AuthenticationIndicatorsPanelImpl();
}
return authenticationIndicatorsPanel;
}
/**
* Make sure the user acknowledges the Users corresponding to this context will be deleted.
*
* @return true, if successful
*/
private boolean confirmAndExecuteUsersDeletion() {
ExtensionUserManagement usersExtension = (ExtensionUserManagement) Control.getSingleton()
.getExtensionLoader().getExtension(ExtensionUserManagement.NAME);
if (usersExtension != null) {
if (usersExtension.getSharedContextUsers(getUISharedContext()).size() > 0) {
authenticationMethodsComboBox.transferFocus();
int choice = JOptionPane.showConfirmDialog(this,
Constant.messages.getString("authentication.dialog.confirmChange.label"),
Constant.messages.getString("authentication.dialog.confirmChange.title"),
JOptionPane.OK_CANCEL_OPTION);
if (choice == JOptionPane.CANCEL_OPTION) {
return false;
}
// Removing the users from the 'shared context' (the UI) will cause their removal at
// save as well
usersExtension.removeSharedContextUsers(getUISharedContext());
}
}
return true;
}
private JPanel getConfigContainerPanel() {
if (configContainerPanel == null) {
configContainerPanel = new JPanel(new BorderLayout());
configContainerPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null,
PANEL_TITLE_CONFIG, javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
javax.swing.border.TitledBorder.DEFAULT_POSITION,
FontUtils.getFont(FontUtils.Size.standard), java.awt.Color.black));
}
return configContainerPanel;
}
private ZapTextField getLoggedInIndicaterRegexField() {
if (loggedInIndicaterRegexField == null)
loggedInIndicaterRegexField = new ZapTextField();
return loggedInIndicaterRegexField;
}
private ZapTextField getLoggedOutIndicaterRegexField() {
if (loggedOutIndicaterRegexField == null)
loggedOutIndicaterRegexField = new ZapTextField();
return loggedOutIndicaterRegexField;
}
@Override
public String getHelpIndex() {
return "ui.dialogs.context-auth";
}
@Override
public void initContextData(Session session, Context uiSharedContext) {
selectedAuthenticationMethod = uiSharedContext.getAuthenticationMethod();
if (log.isDebugEnabled())
log.debug("Initializing configuration panel for authentication method: "
+ selectedAuthenticationMethod + " for context " + uiSharedContext.getName());
resetLoggedInOutIndicators();
// If something was already configured, find the type and set the UI accordingly
if (selectedAuthenticationMethod != null) {
// Set logged in/out indicators
if (selectedAuthenticationMethod.getLoggedInIndicatorPattern() != null)
getLoggedInIndicaterRegexField().setText(
selectedAuthenticationMethod.getLoggedInIndicatorPattern().pattern());
else
getLoggedInIndicaterRegexField().setText("");
if (selectedAuthenticationMethod.getLoggedOutIndicatorPattern() != null)
getLoggedOutIndicaterRegexField().setText(
selectedAuthenticationMethod.getLoggedOutIndicatorPattern().pattern());
else
getLoggedOutIndicaterRegexField().setText("");
// If the proper type is already selected, just rebind the data
if (shownMethodType != null && shownMethodType.isTypeForMethod(selectedAuthenticationMethod)) {
if (shownMethodType.hasOptionsPanel()) {
log.debug("Binding authentication method to existing panel of proper type for context "
+ uiSharedContext.getName());
shownConfigPanel.bindMethod(selectedAuthenticationMethod, getAuthenticationIndicatorsPanel());
}
return;
}
// Select what needs to be selected
for (AuthenticationMethodType type : extension.getAuthenticationMethodTypes())
if (type.isTypeForMethod(selectedAuthenticationMethod)) {
// Selecting the type here will also force the selection listener to run and
// change the config panel accordingly
log.debug("Binding authentication method to new panel of proper type for context "
+ uiSharedContext.getName());
// Add hack to make sure no confirmation is needed if a change has been done
// somewhere else (e.g. API)
needsConfirm = false;
getAuthenticationMethodsComboBox().setSelectedItem(type);
needsConfirm = true;
break;
}
}
}
/**
* Resets the tool tip and enables the fields of the logged in/out indicators.
*
* @see #getLoggedInIndicaterRegexField()
* @see #getLoggedOutIndicaterRegexField()
*/
private void resetLoggedInOutIndicators() {
getLoggedInIndicaterRegexField().setToolTipText(null);
getLoggedInIndicaterRegexField().setEnabled(true);
getLoggedOutIndicaterRegexField().setToolTipText(null);
getLoggedOutIndicaterRegexField().setEnabled(true);
}
@Override
public void validateContextData(Session session) throws Exception {
if (shownConfigPanel != null)
shownConfigPanel.validateFields();
try {
Pattern.compile(getLoggedInIndicaterRegexField().getText());
Pattern.compile(getLoggedOutIndicaterRegexField().getText());
} catch (PatternSyntaxException e) {
throw new IllegalStateException(Constant.messages.getString(
"authentication.panel.error.illegalPattern", getUISharedContext().getName()), e);
}
}
private void saveMethod() {
if (shownConfigPanel != null)
shownConfigPanel.saveMethod();
selectedAuthenticationMethod.setLoggedInIndicatorPattern(getLoggedInIndicaterRegexField().getText());
selectedAuthenticationMethod
.setLoggedOutIndicatorPattern(getLoggedOutIndicaterRegexField().getText());
}
@Override
public void saveContextData(Session session) throws Exception {
saveMethod();
Context context = session.getContext(getContextIndex());
// Notify the previously saved method that it's being discarded so the changes can be
// reflected in the UI
if (context.getAuthenticationMethod() != null)
if (!shownMethodType.isTypeForMethod(context.getAuthenticationMethod()))
context.getAuthenticationMethod().onMethodDiscarded();
context.setAuthenticationMethod(selectedAuthenticationMethod);
// Notify the newly saved method that it's being persisted so the changes can be
// reflected in the UI
selectedAuthenticationMethod.onMethodPersisted();
}
@Override
public void saveTemporaryContextData(Context uiSharedContext) {
saveMethod();
uiSharedContext.setAuthenticationMethod(selectedAuthenticationMethod);
}
private class AuthenticationIndicatorsPanelImpl implements AuthenticationIndicatorsPanel {
@Override
public String getLoggedInIndicatorPattern() {
return getLoggedInIndicaterRegexField().getText();
}
@Override
public void setLoggedInIndicatorPattern(String loggedInIndicatorPattern) {
getLoggedInIndicaterRegexField().setText(loggedInIndicatorPattern);
}
@Override
public void setLoggedInIndicatorEnabled(boolean enabled) {
getLoggedInIndicaterRegexField().setEnabled(enabled);
}
@Override
public void setLoggedInIndicatorToolTip(String toolTip) {
getLoggedInIndicaterRegexField().setToolTipText(toolTip);
}
@Override
public String getLoggedOutIndicatorPattern() {
return getLoggedOutIndicaterRegexField().getText();
}
@Override
public void setLoggedOutIndicatorPattern(String loggedOutIndicatorPattern) {
getLoggedOutIndicaterRegexField().setText(loggedOutIndicatorPattern);
}
@Override
public void setLoggedOutIndicatorEnabled(boolean enabled) {
getLoggedOutIndicaterRegexField().setEnabled(enabled);
}
@Override
public void setLoggedOutIndicatorToolTip(String toolTip) {
getLoggedOutIndicaterRegexField().setToolTipText(toolTip);
}
}
}