/* * 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.authentication; import java.awt.Component; import java.awt.GridBagLayout; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.plaf.basic.BasicComboBoxRenderer; import net.sf.json.JSONObject; import org.apache.commons.codec.binary.Base64; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.httpclient.Cookie; import org.apache.log4j.Logger; import org.parosproxy.paros.Constant; import org.parosproxy.paros.control.Control; import org.parosproxy.paros.extension.ExtensionHook; import org.parosproxy.paros.model.Session; import org.zaproxy.zap.extension.api.ApiDynamicActionImplementor; import org.zaproxy.zap.extension.api.ApiException; import org.zaproxy.zap.extension.api.ApiException.Type; import org.zaproxy.zap.extension.api.ApiResponse; import org.zaproxy.zap.extension.api.ApiResponseElement; import org.zaproxy.zap.extension.api.ApiResponseSet; import org.zaproxy.zap.extension.authentication.AuthenticationAPI; import org.zaproxy.zap.extension.httpsessions.ExtensionHttpSessions; import org.zaproxy.zap.extension.httpsessions.HttpSession; import org.zaproxy.zap.extension.users.ExtensionUserManagement; import org.zaproxy.zap.extension.users.UsersAPI; import org.zaproxy.zap.model.Context; import org.zaproxy.zap.session.CookieBasedSessionManagementMethodType; import org.zaproxy.zap.session.SessionManagementMethod; import org.zaproxy.zap.session.WebSession; import org.zaproxy.zap.users.User; import org.zaproxy.zap.utils.ApiUtils; import org.zaproxy.zap.view.LayoutHelper; /** * The implementation for an {@link AuthenticationMethodType} where the user manually authenticates and then just * selects an already authenticated {@link WebSession}. */ public class ManualAuthenticationMethodType extends AuthenticationMethodType { private static final int METHOD_IDENTIFIER = 0; /** The Authentication method's name. */ private static final String METHOD_NAME = Constant.messages.getString("authentication.method.manual.name"); private static final String API_METHOD_NAME = "manualAuthentication"; /** * The implementation for an {@link AuthenticationMethod} where the user manually authenticates and then just * selects an already authenticated {@link WebSession}. */ public static class ManualAuthenticationMethod extends AuthenticationMethod { private int contextId; public ManualAuthenticationMethod(int contextId) { super(); this.contextId = contextId; } protected int getContextId() { return contextId; } @Override public boolean isConfigured() { // Nothing to configure return true; } @Override public AuthenticationCredentials createAuthenticationCredentials() { return new ManualAuthenticationCredentials(); } @Override public WebSession authenticate(SessionManagementMethod sessionManagementMethod, AuthenticationCredentials credentials, User user) { // Check proper type if (!(credentials instanceof ManualAuthenticationCredentials)) { Logger.getLogger(ManualAuthenticationMethod.class).error( "Manual authentication credentials should be used for Manual authentication."); throw new UnsupportedAuthenticationCredentialsException( "Manual authentication credentials should be used for Manual authentication."); } // Build a new WebSession based on the values from the HttpSession // TODO: Changes in either the WebSession or the HttpSession are not // visible in the other ManualAuthenticationCredentials mc = (ManualAuthenticationCredentials) credentials; WebSession session = new CookieBasedSessionManagementMethodType.CookieBasedSession(); for (Entry<String, Cookie> v : mc.getSelectedSession().getTokenValuesUnmodifiableMap().entrySet()) { session.getHttpState().addCookie(v.getValue()); } return session; } @Override public AuthenticationMethodType getType() { return new ManualAuthenticationMethodType(); } @Override public AuthenticationMethod duplicate() { return new ManualAuthenticationMethod(contextId); } @Override public void onMethodPersisted() { // Do nothing } @Override public void onMethodDiscarded() { // Do nothing } @Override public ApiResponse getApiResponseRepresentation() { return new ApiResponseElement("methodName", API_METHOD_NAME); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + contextId; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; ManualAuthenticationMethod other = (ManualAuthenticationMethod) obj; if (contextId != other.contextId) return false; return true; } } /** * A credentials implementation that allows users to manually select an existing {@link WebSession} that corresponds * to an already authenticated session. */ private static class ManualAuthenticationCredentials implements AuthenticationCredentials { /** * The Constant defining the name/type in api calls. Should not be localized. */ private static final String API_NAME = "ManualAuthenticationCredentials"; private HttpSession selectedSession; protected HttpSession getSelectedSession() { return selectedSession; } @Override public boolean isConfigured() { return selectedSession != null; } protected void setSelectedSession(HttpSession selectedSession) { this.selectedSession = selectedSession; } @Override public String encode(String parentStringSeparator) { return Base64.encodeBase64String(selectedSession.getName().getBytes()); } @Override public void decode(String encodedCredentials) { // TODO: Currently, cannot be decoded as HttpSessions are not // persisted. throw new IllegalStateException("Manual Authentication Credentials cannot be decoded."); } @Override public ApiResponse getApiResponseRepresentation() { Map<String, String> values = new HashMap<>(); values.put("type", API_NAME); values.put("sessionName", selectedSession != null ? selectedSession.getName() : ""); return new ApiResponseSet<String>("credentials", values); } } /** * The option panel for configuring {@link ManualAuthenticationCredentials} objects. */ private static class ManualAuthenticationCredentialsOptionsPanel extends AbstractCredentialsOptionsPanel<ManualAuthenticationCredentials> { /** The Constant serialVersionUID. */ private static final long serialVersionUID = -8081914793980311435L; private static final Logger log = Logger.getLogger(ManualAuthenticationCredentialsOptionsPanel.class); private JComboBox<HttpSession> sessionsComboBox; private Context uiSharedContext; public ManualAuthenticationCredentialsOptionsPanel(ManualAuthenticationCredentials credentials, Context uiSharedContext) { super(credentials); this.uiSharedContext = uiSharedContext; initialize(); } /** * Initialize the panel. */ @SuppressWarnings("unchecked") protected void initialize() { this.setLayout(new GridBagLayout()); JLabel sessionsLabel = new JLabel(Constant.messages.getString("authentication.method.manual.field.session")); this.add(sessionsLabel, LayoutHelper.getGBC(0, 0, 1, 0.5D)); this.add(getSessionsComboBox(), LayoutHelper.getGBC(1, 0, 1, 0.5D)); this.getSessionsComboBox().setRenderer(new HttpSessionRenderer()); this.add(new JLabel(Constant.messages.getString("authentication.method.manual.field.description")), LayoutHelper.getGBC(0, 1, 2, 0.0d, 0.0d)); } /** * A renderer for properly displaying the name of an HttpSession in a ComboBox. */ private static class HttpSessionRenderer extends BasicComboBoxRenderer { private static final long serialVersionUID = 3654541772447187317L; @Override @SuppressWarnings("rawtypes") public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value != null) { HttpSession item = (HttpSession) value; setText(item.getName()); } return this; } } private JComboBox<HttpSession> getSessionsComboBox() { if (sessionsComboBox == null) { ExtensionHttpSessions extensionHttpSessions = (ExtensionHttpSessions) Control.getSingleton().getExtensionLoader() .getExtension(ExtensionHttpSessions.NAME); List<HttpSession> sessions = extensionHttpSessions.getHttpSessionsForContext(uiSharedContext); if (log.isDebugEnabled()) log.debug("Found sessions for Manual Authentication Config: " + sessions); sessionsComboBox = new JComboBox<>(sessions.toArray(new HttpSession[sessions.size()])); sessionsComboBox.setSelectedItem(this.getCredentials().getSelectedSession()); } return sessionsComboBox; } @Override public boolean validateFields() { if (sessionsComboBox.getSelectedIndex() < 0) { JOptionPane.showMessageDialog(this, Constant.messages.getString("authentication.method.manual.dialog.error.nosession.text"), Constant.messages.getString("authentication.method.manual.dialog.error.title"), JOptionPane.WARNING_MESSAGE); sessionsComboBox.requestFocusInWindow(); return false; } return true; } @Override public void saveCredentials() { log.info("Saving Manual Authentication Method: " + getSessionsComboBox().getSelectedItem()); getCredentials().setSelectedSession((HttpSession) getSessionsComboBox().getSelectedItem()); } } @Override public String getName() { return METHOD_NAME; } @Override public boolean hasOptionsPanel() { // No options panel for the method return false; } @Override public boolean hasCredentialsOptionsPanel() { return true; } @Override public ManualAuthenticationMethod createAuthenticationMethod(int contextId) { return new ManualAuthenticationMethod(contextId); } @Override public AbstractAuthenticationMethodOptionsPanel buildOptionsPanel(Context uiSharedContext) { // Not needed return null; } @Override public AbstractCredentialsOptionsPanel<? extends AuthenticationCredentials> buildCredentialsOptionsPanel( AuthenticationCredentials credentials, Context uiSharedContext) { return new ManualAuthenticationCredentialsOptionsPanel((ManualAuthenticationCredentials) credentials, uiSharedContext); } @Override public boolean isTypeForMethod(AuthenticationMethod method) { return (method instanceof ManualAuthenticationMethod); } @Override public void hook(ExtensionHook extensionHook) { // Do nothing } @Override public AuthenticationMethod loadMethodFromSession(Session session, int contextId) { return new ManualAuthenticationMethod(contextId); } @Override public void persistMethodToSession(Session session, int contextId, AuthenticationMethod authMethod) { // Nothing to persist } @Override public int getUniqueIdentifier() { return METHOD_IDENTIFIER; } @Override public ManualAuthenticationCredentials createAuthenticationCredentials() { return new ManualAuthenticationCredentials(); } public static ManualAuthenticationCredentials createAuthenticationCredentials(HttpSession session) { ManualAuthenticationCredentials c = new ManualAuthenticationCredentials(); c.setSelectedSession(session); return c; } /* API related constants and methods */ private static final String ACTION_SET_CREDENTIALS = "manualAuthenticationCredentials"; private static final String PARAM_SESSION_NAME = "sessionName"; @Override public ApiDynamicActionImplementor getSetMethodForContextApiAction() { return new ApiDynamicActionImplementor(API_METHOD_NAME, null, null) { @Override public void handleAction(JSONObject params) throws ApiException { Context context = ApiUtils.getContextByParamId(params, AuthenticationAPI.PARAM_CONTEXT_ID); ManualAuthenticationMethod method = createAuthenticationMethod(context.getIndex()); if (!context.getAuthenticationMethod().isSameType(method)) { apiChangedAuthenticationMethodForContext(context.getIndex()); } context.setAuthenticationMethod(method); } }; } @Override public ApiDynamicActionImplementor getSetCredentialsForUserApiAction() { return new ApiDynamicActionImplementor(ACTION_SET_CREDENTIALS, new String[] { PARAM_SESSION_NAME }, null) { @Override public void handleAction(JSONObject params) throws ApiException { Context context = ApiUtils.getContextByParamId(params, UsersAPI.PARAM_CONTEXT_ID); int userId = ApiUtils.getIntParam(params, UsersAPI.PARAM_USER_ID); // Make sure the type of authentication method is compatible if (!isTypeForMethod(context.getAuthenticationMethod())) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, "User's credentials should match authentication method type of the context: " + context.getAuthenticationMethod().getType().getName()); } // NOTE: no need to check if extension is loaded as this method // is called only if // the Users // extension is loaded ExtensionUserManagement extensionUserManagement = (ExtensionUserManagement) Control.getSingleton() .getExtensionLoader().getExtension(ExtensionUserManagement.NAME); User user = extensionUserManagement.getContextUserAuthManager(context.getIndex()).getUserById(userId); if (user == null) { throw new ApiException(Type.USER_NOT_FOUND, UsersAPI.PARAM_USER_ID); } String sessionName = ApiUtils.getNonEmptyStringParam(params, PARAM_SESSION_NAME); // Get the matching session ExtensionHttpSessions extensionHttpSessions = (ExtensionHttpSessions) Control.getSingleton().getExtensionLoader() .getExtension(ExtensionHttpSessions.NAME); if (extensionHttpSessions == null) { throw new ApiException(Type.NO_IMPLEMENTOR, "HttpSessions extension is not loaded."); } List<HttpSession> sessions = extensionHttpSessions.getHttpSessionsForContext(context); HttpSession matchedSession = null; for (HttpSession session : sessions) { if (session.getName().equals(sessionName)) { matchedSession = session; break; } } if (matchedSession == null) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_SESSION_NAME); } // Set the credentials ManualAuthenticationCredentials credentials = createAuthenticationCredentials(); credentials.setSelectedSession(matchedSession); user.setAuthenticationCredentials(credentials); } }; } @Override public void exportData(Configuration config, AuthenticationMethod authMethod) { // Nothing to do } @Override public void importData(Configuration config, AuthenticationMethod authMethod) throws ConfigurationException { // Nothing to do } }