diff --git a/zap/src/main/java/org/parosproxy/paros/db/RecordContext.java b/zap/src/main/java/org/parosproxy/paros/db/RecordContext.java
index 828b2a80bca..145d332e7ce 100644
--- a/zap/src/main/java/org/parosproxy/paros/db/RecordContext.java
+++ b/zap/src/main/java/org/parosproxy/paros/db/RecordContext.java
@@ -50,6 +50,7 @@ public class RecordContext {
public static final int TYPE_AUTH_POLL_FREQ = 211;
public static final int TYPE_AUTH_POLL_FREQ_UNITS = 212;
public static final int TYPE_AUTH_POLL_HEADERS = 213;
+ public static final int TYPE_AUTH_POLL_METHOD = 214;
public static final int TYPE_SESSION_MANAGEMENT_TYPE = 220;
public static final int TYPE_SESSION_MANAGEMENT_FIELD_1 = 221;
diff --git a/zap/src/main/java/org/zaproxy/zap/authentication/AuthenticationMethod.java b/zap/src/main/java/org/zaproxy/zap/authentication/AuthenticationMethod.java
index 2623ef0c4df..b4b166418a5 100644
--- a/zap/src/main/java/org/zaproxy/zap/authentication/AuthenticationMethod.java
+++ b/zap/src/main/java/org/zaproxy/zap/authentication/AuthenticationMethod.java
@@ -20,31 +20,20 @@
package org.zaproxy.zap.authentication;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.regex.Pattern;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
-import org.apache.commons.httpclient.URI;
-import org.apache.commons.httpclient.URIException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.parosproxy.paros.Constant;
import org.parosproxy.paros.network.HttpMessage;
-import org.parosproxy.paros.network.HttpRequestHeader;
-import org.parosproxy.paros.network.HttpSender;
-import org.parosproxy.paros.view.View;
import org.zaproxy.zap.extension.api.ApiResponse;
import org.zaproxy.zap.extension.api.ApiResponseSet;
import org.zaproxy.zap.model.Context;
-import org.zaproxy.zap.model.SessionStructure;
import org.zaproxy.zap.session.SessionManagementMethod;
import org.zaproxy.zap.session.WebSession;
import org.zaproxy.zap.users.AuthenticationState;
import org.zaproxy.zap.users.User;
-import org.zaproxy.zap.utils.Stats;
/**
* The {@code AuthenticationMethod} represents an authentication method that can be used to
@@ -57,6 +46,8 @@ public abstract class AuthenticationMethod {
public static final String CONTEXT_CONFIG_AUTH = Context.CONTEXT_CONFIG + ".authentication";
public static final String CONTEXT_CONFIG_AUTH_TYPE = CONTEXT_CONFIG_AUTH + ".type";
public static final String CONTEXT_CONFIG_AUTH_STRATEGY = CONTEXT_CONFIG_AUTH + ".strategy";
+ public static final String CONTEXT_CONFIG_AUTH_POLL_METHOD =
+ CONTEXT_CONFIG_AUTH + ".pollmethod";
public static final String CONTEXT_CONFIG_AUTH_POLL_URL = CONTEXT_CONFIG_AUTH + ".pollurl";
public static final String CONTEXT_CONFIG_AUTH_POLL_DATA = CONTEXT_CONFIG_AUTH + ".polldata";
public static final String CONTEXT_CONFIG_AUTH_POLL_HEADERS =
@@ -66,16 +57,49 @@ public abstract class AuthenticationMethod {
public static final String CONTEXT_CONFIG_AUTH_LOGGEDIN = CONTEXT_CONFIG_AUTH + ".loggedin";
public static final String CONTEXT_CONFIG_AUTH_LOGGEDOUT = CONTEXT_CONFIG_AUTH + ".loggedout";
- public static final String AUTH_STATE_ASSUMED_IN_STATS = "stats.auth.state.assumedin";
- public static final String AUTH_STATE_LOGGED_IN_STATS = "stats.auth.state.loggedin";
- public static final String AUTH_STATE_LOGGED_OUT_STATS = "stats.auth.state.loggedout";
- public static final String AUTH_STATE_NO_INDICATOR_STATS = "stats.auth.state.noindicator";
- public static final String AUTH_STATE_UNKNOWN_STATS = "stats.auth.state.unknown";
+ /**
+ * @deprecated Use {@link VerificationMethod#AUTH_STATE_ASSUMED_IN_STATS}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
+ public static final String AUTH_STATE_ASSUMED_IN_STATS =
+ VerificationMethod.AUTH_STATE_ASSUMED_IN_STATS;
+
+ /**
+ * @deprecated Use {@link VerificationMethod#AUTH_STATE_LOGGED_IN_STATS}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
+ public static final String AUTH_STATE_LOGGED_IN_STATS =
+ VerificationMethod.AUTH_STATE_LOGGED_IN_STATS;
+
+ /**
+ * @deprecated Use {@link VerificationMethod#AUTH_STATE_LOGGED_OUT_STATS}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
+ public static final String AUTH_STATE_LOGGED_OUT_STATS =
+ VerificationMethod.AUTH_STATE_LOGGED_OUT_STATS;
+
+ /**
+ * @deprecated Use {@link VerificationMethod#AUTH_STATE_NO_INDICATOR_STATS}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
+ public static final String AUTH_STATE_NO_INDICATOR_STATS =
+ VerificationMethod.AUTH_STATE_NO_INDICATOR_STATS;
+
+ /**
+ * @deprecated Use {@link VerificationMethod#AUTH_STATE_UNKNOWN_STATS}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
+ public static final String AUTH_STATE_UNKNOWN_STATS =
+ VerificationMethod.AUTH_STATE_UNKNOWN_STATS;
public static final String TOKEN_PREFIX = "{%";
public static final String TOKEN_POSTFIX = "%}";
- public static final int DEFAULT_POLL_FREQUENCY = 60;
+ /**
+ * @deprecated Use {@link VerificationMethod#DEFAULT_POLL_FREQUENCY}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
+ public static final int DEFAULT_POLL_FREQUENCY = VerificationMethod.DEFAULT_POLL_FREQUENCY;
public static enum AuthCheckingStrategy {
EACH_RESP,
@@ -90,17 +114,20 @@ public static enum AuthPollFrequencyUnits {
SECONDS
}
- private AuthCheckingStrategy authCheckingStrategy = AuthCheckingStrategy.EACH_RESP;
-
- private String pollUrl;
-
- private String pollData;
+ private VerificationMethod verificationMethod;
- private String pollHeaders;
+ // Kept for binary compatibility with subclasses. Use VerificationMethod for all access.
+ @Deprecated(since = "2.18.0", forRemoval = true)
+ protected Pattern loggedInIndicatorPattern;
- private int pollFrequency = DEFAULT_POLL_FREQUENCY;
+ // Kept for binary compatibility with subclasses. Use VerificationMethod for all access.
+ @Deprecated(since = "2.18.0", forRemoval = true)
+ protected Pattern loggedOutIndicatorPattern;
- private AuthPollFrequencyUnits pollFrequencyUnits = AuthPollFrequencyUnits.REQUESTS;
+ {
+ verificationMethod = new VerificationMethod();
+ verificationMethod.setUserDataReplacer(this::replaceUserDataInPollRequest);
+ }
/**
* Checks if the authentication method is fully configured.
@@ -117,14 +144,8 @@ public static enum AuthPollFrequencyUnits {
@Override
public AuthenticationMethod clone() {
AuthenticationMethod method = duplicate();
- method.authCheckingStrategy = this.authCheckingStrategy;
- method.pollUrl = this.pollUrl;
- method.pollData = this.pollData;
- method.pollHeaders = this.pollHeaders;
- method.pollFrequency = this.pollFrequency;
- method.pollFrequencyUnits = this.pollFrequencyUnits;
- method.loggedInIndicatorPattern = this.loggedInIndicatorPattern;
- method.loggedOutIndicatorPattern = this.loggedOutIndicatorPattern;
+ method.setVerificationMethod(
+ this.verificationMethod.copy(method::replaceUserDataInPollRequest));
return method;
}
@@ -210,341 +231,200 @@ public void onMethodPersisted() {}
*/
public void onMethodDiscarded() {}
- /** The logged in indicator pattern. */
- protected Pattern loggedInIndicatorPattern = null;
-
- /** The logged out indicator pattern. */
- protected Pattern loggedOutIndicatorPattern = null;
-
- private HttpSender httpSender;
+ /**
+ * Gets the {@link VerificationMethod} that handles authentication state checking for this
+ * authentication method.
+ *
+ * @return the verification method
+ * @since 2.18.0
+ */
+ public VerificationMethod getVerificationMethod() {
+ return verificationMethod;
+ }
- private HttpSender getHttpSender() {
- if (this.httpSender == null) {
- this.httpSender = new HttpSender(HttpSender.AUTHENTICATION_POLL_INITIATOR);
- }
- return httpSender;
+ /**
+ * Sets the {@link VerificationMethod} that handles authentication state checking for this
+ * authentication method.
+ *
+ * @param verificationMethod the verification method to use
+ * @since 2.18.0
+ */
+ public void setVerificationMethod(VerificationMethod verificationMethod) {
+ this.verificationMethod = verificationMethod;
+ verificationMethod.setUserDataReplacer(this::replaceUserDataInPollRequest);
+ this.loggedInIndicatorPattern = verificationMethod.getLoggedInIndicatorPattern();
+ this.loggedOutIndicatorPattern = verificationMethod.getLoggedOutIndicatorPattern();
}
- /** Deprecated 2.10.0. */
- @Deprecated
+ /**
+ * @deprecated Use {@link VerificationMethod#isAuthenticated(HttpMessage, User)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public boolean isAuthenticated(HttpMessage msg) {
- return this.isAuthenticated(msg, null, false);
+ return verificationMethod.isAuthenticated(msg, null, false);
}
/**
- * Checks if the response received by the Http Message corresponds to an authenticated Web
- * Session.
- *
- *
If none of the indicators are set up, the method defaults to returning true, so that no
- * authentications are tried when there is no way to check authentication. A message is also
- * shown on the output console in this case.
- *
- * @param msg the http message
- * @return true, if is authenticated or no indicators have been set, and false otherwise
+ * @deprecated Use {@link VerificationMethod#isAuthenticated(HttpMessage, User)}.
*/
+ @Deprecated(since = "2.18.0", forRemoval = true)
public boolean isAuthenticated(HttpMessage msg, User user) {
- return this.isAuthenticated(msg, user, false);
+ return verificationMethod.isAuthenticated(msg, user);
}
/**
- * Checks if the response received by the Http Message corresponds to an authenticated Web
- * Session.
- *
- *
If none of the indicators are set up, the method defaults to returning true, so that no
- * authentications are tried when there is no way to check authentication. A message is also
- * shown on the output console in this case.
- *
- * @param msg the http message
- * @param force always check even if the polling strategy is being used
- * @return true, if is authenticated or no indicators have been set, and false otherwise
+ * @deprecated Use {@link VerificationMethod#isAuthenticated(HttpMessage, User, boolean)}.
*/
+ @Deprecated(since = "2.18.0", forRemoval = true)
public boolean isAuthenticated(HttpMessage msg, User user, boolean force) {
-
- if (msg == null
- || user == null
- || AuthCheckingStrategy.AUTO_DETECT.equals(this.authCheckingStrategy)) {
- return false;
- }
- AuthenticationState authState = user.getAuthenticationState();
- // Assume logged in if nothing was set up
- if (loggedInIndicatorPattern == null && loggedOutIndicatorPattern == null) {
- try {
- Stats.incCounter(SessionStructure.getHostName(msg), AUTH_STATE_NO_INDICATOR_STATS);
- } catch (URIException e) {
- // Ignore
- }
- if (View.isInitialised()) {
- // Let the user know this
- View.getSingleton()
- .getOutputPanel()
- .append(
- Constant.messages.getString(
- "authentication.output.indicatorsNotSet",
- msg.getRequestHeader().getURI())
- + "\n");
- }
- return true;
- }
-
- HttpMessage msgToTest;
-
- switch (this.authCheckingStrategy) {
- case EACH_REQ:
- case EACH_REQ_RESP:
- case EACH_RESP:
- msgToTest = msg;
- break;
- case POLL_URL:
- if (!force
- && authState.getLastPollResult() != null
- && authState.getLastPollResult()) {
- // Check if we really need to poll the relevant URL again
- switch (pollFrequencyUnits) {
- case SECONDS:
- if ((System.currentTimeMillis() - authState.getLastPollTime()) / 1000
- < pollFrequency) {
- try {
- Stats.incCounter(
- SessionStructure.getHostName(msg),
- AUTH_STATE_ASSUMED_IN_STATS);
- } catch (URIException e) {
- // Ignore
- }
- return true;
- }
- break;
- case REQUESTS:
- default:
- if (authState.getRequestsSincePoll() < pollFrequency) {
- authState.incRequestsSincePoll();
- try {
- Stats.incCounter(
- SessionStructure.getHostName(msg),
- AUTH_STATE_ASSUMED_IN_STATS);
- } catch (URIException e) {
- // Ignore
- }
- return true;
- }
- break;
- }
- }
- // Make the poll request
- try {
- HttpMessage pollMsg = pollAsUser(user);
- msgToTest = pollMsg;
- } catch (Exception e1) {
- LOGGER.warn("Failed sending poll request to {}", this.getPollUrl(), e1);
- return false;
- }
- break;
- default:
- return false;
- }
-
- return evaluateAuthRequest(msgToTest, authState);
+ return verificationMethod.isAuthenticated(msg, user, force);
}
+ /**
+ * @deprecated Use {@link VerificationMethod#evaluateAuthRequest(HttpMessage,
+ * AuthenticationState)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public boolean evaluateAuthRequest(HttpMessage msg, AuthenticationState authState) {
- List contentToTest = new ArrayList<>();
- switch (authCheckingStrategy) {
- case EACH_REQ:
- contentToTest.add(msg.getRequestHeader().toString());
- contentToTest.add(msg.getRequestBody().toString());
- break;
- case EACH_REQ_RESP:
- contentToTest.add(msg.getRequestHeader().toString());
- contentToTest.add(msg.getRequestBody().toString());
- contentToTest.add(msg.getResponseHeader().toString());
- contentToTest.add(msg.getResponseBody().toString());
- break;
- case EACH_RESP:
- case POLL_URL:
- contentToTest.add(msg.getResponseHeader().toString());
- contentToTest.add(msg.getResponseBody().toString());
- break;
- case AUTO_DETECT:
- return false;
- }
- if (patternMatchesAny(loggedInIndicatorPattern, contentToTest)) {
- // Looks like we're authenticated
- try {
- Stats.incCounter(SessionStructure.getHostName(msg), AUTH_STATE_LOGGED_IN_STATS);
- } catch (URIException e) {
- // Ignore
- }
- if (authCheckingStrategy.equals(AuthCheckingStrategy.POLL_URL)) {
- authState.setLastPollResult(true);
- }
- return true;
- }
-
- if (loggedOutIndicatorPattern != null
- && !patternMatchesAny(loggedOutIndicatorPattern, contentToTest)) {
- // Cant find the unauthenticated indicator, assume we're authenticated but record as
- // unknown
- try {
- Stats.incCounter(SessionStructure.getHostName(msg), AUTH_STATE_UNKNOWN_STATS);
- } catch (URIException e) {
- // Ignore
- }
- if (this.authCheckingStrategy.equals(AuthCheckingStrategy.POLL_URL)) {
- authState.setLastPollResult(true);
- }
- return true;
- }
- // Not looking good...
- try {
- Stats.incCounter(SessionStructure.getHostName(msg), AUTH_STATE_LOGGED_OUT_STATS);
- } catch (URIException e) {
- // Ignore
- }
- if (this.authCheckingStrategy.equals(AuthCheckingStrategy.POLL_URL)) {
- authState.setLastPollResult(false);
- }
- return false;
+ return verificationMethod.evaluateAuthRequest(msg, authState);
}
+ /**
+ * @deprecated Use {@link VerificationMethod#pollAsUser(User)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public HttpMessage pollAsUser(User user) throws IOException {
- if (!this.authCheckingStrategy.equals(AuthCheckingStrategy.POLL_URL)) {
- throw new IllegalArgumentException("Authentication checking strategy is not POLL_URL");
- }
- HttpMessage pollMsg = new HttpMessage(new URI(this.getPollUrl(), true));
- if (this.getPollData() != null && this.getPollData().length() > 0) {
- pollMsg.getRequestHeader().setMethod(HttpRequestHeader.POST);
- pollMsg.getRequestBody().setBody(this.getPollData());
- pollMsg.getRequestHeader().setContentLength(pollMsg.getRequestBody().length());
- }
- if (this.getPollHeaders() != null && this.getPollHeaders().length() > 0) {
- for (String header : this.getPollHeaders().split("\n")) {
- String[] headerValue = header.split(":", 2);
- if (headerValue.length == 2) {
- pollMsg.getRequestHeader()
- .addHeader(headerValue[0].trim(), headerValue[1].trim());
- } else {
- LOGGER.error(
- "Invalid header '{}' for poll request to {}",
- header,
- this.getPollUrl());
- }
- }
- }
- pollMsg.setRequestingUser(user);
- replaceUserDataInPollRequest(pollMsg, user);
-
- getHttpSender().sendAndReceive(pollMsg);
- AuthenticationHelper.addAuthMessageToHistory(
- pollMsg, List.of(AuthenticationHelper.HISTORY_TAG_VERIFICATION));
-
- AuthenticationState authState = user.getAuthenticationState();
- authState.setLastPollTime(System.currentTimeMillis());
- authState.setRequestsSincePoll(0);
-
- return pollMsg;
- }
-
- private static boolean patternMatchesAny(Pattern pattern, List content) {
- if (pattern != null) {
- for (String str : content) {
- if (pattern.matcher(str).find()) {
- return true;
- }
- }
- }
- return false;
+ return verificationMethod.pollAsUser(user);
}
/**
- * Gets the logged in indicator pattern.
- *
- * @return the logged in indicator pattern
+ * @deprecated Use {@link VerificationMethod#getLoggedInIndicatorPattern()}.
*/
+ @Deprecated(since = "2.18.0", forRemoval = true)
public Pattern getLoggedInIndicatorPattern() {
- return loggedInIndicatorPattern;
+ return verificationMethod.getLoggedInIndicatorPattern();
}
/**
- * Sets the logged in indicator pattern.
- *
- * @param loggedInIndicatorPattern the new logged in indicator pattern
+ * @deprecated Use {@link VerificationMethod#setLoggedInIndicatorPattern(String)}.
*/
+ @Deprecated(since = "2.18.0", forRemoval = true)
public void setLoggedInIndicatorPattern(String loggedInIndicatorPattern) {
- if (loggedInIndicatorPattern == null || loggedInIndicatorPattern.trim().length() == 0) {
- this.loggedInIndicatorPattern = null;
- } else {
- this.loggedInIndicatorPattern = Pattern.compile(loggedInIndicatorPattern);
- }
+ verificationMethod.setLoggedInIndicatorPattern(loggedInIndicatorPattern);
+ this.loggedInIndicatorPattern = verificationMethod.getLoggedInIndicatorPattern();
}
/**
- * Gets the logged out indicator pattern.
- *
- * @return the logged out indicator pattern
+ * @deprecated Use {@link VerificationMethod#getLoggedOutIndicatorPattern()}.
*/
+ @Deprecated(since = "2.18.0", forRemoval = true)
public Pattern getLoggedOutIndicatorPattern() {
- return loggedOutIndicatorPattern;
+ return verificationMethod.getLoggedOutIndicatorPattern();
}
/**
- * Sets the logged out indicator pattern.
- *
- * @param loggedOutIndicatorPattern the new logged out indicator pattern
+ * @deprecated Use {@link VerificationMethod#setLoggedOutIndicatorPattern(String)}.
*/
+ @Deprecated(since = "2.18.0", forRemoval = true)
public void setLoggedOutIndicatorPattern(String loggedOutIndicatorPattern) {
- if (loggedOutIndicatorPattern == null || loggedOutIndicatorPattern.trim().length() == 0) {
- this.loggedOutIndicatorPattern = null;
- } else {
- this.loggedOutIndicatorPattern = Pattern.compile(loggedOutIndicatorPattern);
- }
+ verificationMethod.setLoggedOutIndicatorPattern(loggedOutIndicatorPattern);
+ this.loggedOutIndicatorPattern = verificationMethod.getLoggedOutIndicatorPattern();
}
+ /**
+ * @deprecated Use {@link VerificationMethod#getAuthCheckingStrategy()}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public AuthCheckingStrategy getAuthCheckingStrategy() {
- return authCheckingStrategy;
+ return verificationMethod.getAuthCheckingStrategy();
}
+ /**
+ * @deprecated Use {@link VerificationMethod#setAuthCheckingStrategy(AuthCheckingStrategy)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public void setAuthCheckingStrategy(AuthCheckingStrategy authCheckingStrategy) {
- Objects.requireNonNull(authCheckingStrategy);
- this.authCheckingStrategy = authCheckingStrategy;
+ verificationMethod.setAuthCheckingStrategy(authCheckingStrategy);
}
+ /**
+ * @deprecated Use {@link VerificationMethod#getPollUrl()}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public String getPollUrl() {
- return pollUrl;
+ return verificationMethod.getPollUrl();
}
+ /**
+ * @deprecated Use {@link VerificationMethod#setPollUrl(String)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public void setPollUrl(String pollUrl) {
- this.pollUrl = pollUrl;
+ verificationMethod.setPollUrl(pollUrl);
}
+ /**
+ * @deprecated Use {@link VerificationMethod#getPollData()}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public String getPollData() {
- return pollData;
+ return verificationMethod.getPollData();
}
+ /**
+ * @deprecated Use {@link VerificationMethod#setPollData(String)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public void setPollData(String pollData) {
- this.pollData = pollData;
+ verificationMethod.setPollData(pollData);
}
+ /**
+ * @deprecated Use {@link VerificationMethod#getPollHeaders()}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public String getPollHeaders() {
- return pollHeaders;
+ return verificationMethod.getPollHeaders();
}
+ /**
+ * @deprecated Use {@link VerificationMethod#setPollHeaders(String)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public void setPollHeaders(String pollHeaders) {
- this.pollHeaders = pollHeaders;
+ verificationMethod.setPollHeaders(pollHeaders);
}
+ /**
+ * @deprecated Use {@link VerificationMethod#getPollFrequency()}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public int getPollFrequency() {
- return pollFrequency;
+ return verificationMethod.getPollFrequency();
}
+ /**
+ * @deprecated Use {@link VerificationMethod#setPollFrequency(int)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public void setPollFrequency(int pollFrequency) {
- this.pollFrequency = pollFrequency;
+ verificationMethod.setPollFrequency(pollFrequency);
}
+ /**
+ * @deprecated Use {@link VerificationMethod#getPollFrequencyUnits()}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public AuthPollFrequencyUnits getPollFrequencyUnits() {
- return pollFrequencyUnits;
+ return verificationMethod.getPollFrequencyUnits();
}
+ /**
+ * @deprecated Use {@link VerificationMethod#setPollFrequencyUnits(AuthPollFrequencyUnits)}.
+ */
+ @Deprecated(since = "2.18.0", forRemoval = true)
public void setPollFrequencyUnits(AuthPollFrequencyUnits pollFrequencyUnits) {
- this.pollFrequencyUnits = pollFrequencyUnits;
+ verificationMethod.setPollFrequencyUnits(pollFrequencyUnits);
}
/**
@@ -558,63 +438,6 @@ public boolean isSameType(AuthenticationMethod other) {
return other.getClass().equals(this.getClass());
}
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result =
- prime * result
- + ((loggedInIndicatorPattern == null)
- ? 0
- : loggedInIndicatorPattern.pattern().hashCode());
- result =
- prime * result
- + ((loggedOutIndicatorPattern == null)
- ? 0
- : loggedOutIndicatorPattern.pattern().hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- AuthenticationMethod other = (AuthenticationMethod) obj;
- if (!isSamePattern(loggedInIndicatorPattern, other.loggedInIndicatorPattern)) {
- return false;
- }
- if (!isSamePattern(loggedOutIndicatorPattern, other.loggedOutIndicatorPattern)) {
- return false;
- }
- if (!this.authCheckingStrategy.equals(other.authCheckingStrategy)) {
- return false;
- }
- if (!Objects.equals(this.pollUrl, other.pollUrl)) {
- return false;
- }
- if (!Objects.equals(this.pollData, other.pollData)) {
- return false;
- }
- if (!Objects.equals(this.pollHeaders, other.pollHeaders)) {
- return false;
- }
- if (this.pollFrequency != other.pollFrequency) {
- return false;
- }
- if (!this.pollFrequencyUnits.equals(other.pollFrequencyUnits)) {
- return false;
- }
- return true;
- }
-
- private static boolean isSamePattern(Pattern pattern, Pattern other) {
- if (pattern == null) {
- return other == null;
- }
- return other != null && pattern.pattern().equals(other.pattern());
- }
-
/**
* Thrown when an unsupported type of credentials is used with a {@link AuthenticationMethod} .
*/
diff --git a/zap/src/main/java/org/zaproxy/zap/authentication/PostBasedAuthenticationMethodType.java b/zap/src/main/java/org/zaproxy/zap/authentication/PostBasedAuthenticationMethodType.java
index cf5e6bbb9ed..fb360619bf3 100644
--- a/zap/src/main/java/org/zaproxy/zap/authentication/PostBasedAuthenticationMethodType.java
+++ b/zap/src/main/java/org/zaproxy/zap/authentication/PostBasedAuthenticationMethodType.java
@@ -392,7 +392,7 @@ public WebSession authenticate(
WebSession session = sessionManagementMethod.extractWebSession(msg);
user.setAuthenticatedSession(session);
- if (this.isAuthenticated(msg, user, true)) {
+ if (this.getVerificationMethod().isAuthenticated(msg, user, true)) {
// Let the user know it worked
AuthenticationHelper.notifyOutputAuthSuccessful(msg);
user.getAuthenticationState().setLastAuthFailure("");
diff --git a/zap/src/main/java/org/zaproxy/zap/authentication/ScriptBasedAuthenticationMethodType.java b/zap/src/main/java/org/zaproxy/zap/authentication/ScriptBasedAuthenticationMethodType.java
index d4646893356..4e05577efc8 100644
--- a/zap/src/main/java/org/zaproxy/zap/authentication/ScriptBasedAuthenticationMethodType.java
+++ b/zap/src/main/java/org/zaproxy/zap/authentication/ScriptBasedAuthenticationMethodType.java
@@ -192,8 +192,10 @@ public void loadScript(ScriptWrapper scriptW) {
try {
if (script instanceof AuthenticationScriptV2) {
AuthenticationScriptV2 scriptV2 = (AuthenticationScriptV2) script;
- setLoggedInIndicatorPattern(scriptV2.getLoggedInIndicator());
- setLoggedOutIndicatorPattern(scriptV2.getLoggedOutIndicator());
+ getVerificationMethod()
+ .setLoggedInIndicatorPattern(scriptV2.getLoggedInIndicator());
+ getVerificationMethod()
+ .setLoggedOutIndicatorPattern(scriptV2.getLoggedOutIndicator());
}
String[] requiredParams = script.getRequiredParamsNames();
String[] optionalParams = script.getOptionalParamsNames();
@@ -322,8 +324,10 @@ public WebSession authenticate(
try {
if (script instanceof AuthenticationScriptV2) {
AuthenticationScriptV2 scriptV2 = (AuthenticationScriptV2) script;
- setLoggedInIndicatorPattern(scriptV2.getLoggedInIndicator());
- setLoggedOutIndicatorPattern(scriptV2.getLoggedOutIndicator());
+ getVerificationMethod()
+ .setLoggedInIndicatorPattern(scriptV2.getLoggedInIndicator());
+ getVerificationMethod()
+ .setLoggedOutIndicatorPattern(scriptV2.getLoggedOutIndicator());
}
msg =
script.authenticate(
@@ -362,7 +366,7 @@ public WebSession authenticate(
return null;
}
- if (this.isAuthenticated(msg, user, true)) {
+ if (this.getVerificationMethod().isAuthenticated(msg, user, true)) {
// Let the user know it worked
user.getAuthenticationState().setLastAuthFailure("");
AuthenticationHelper.notifyOutputAuthSuccessful(msg);
@@ -581,17 +585,25 @@ private void loadScript(ScriptWrapper scriptW, boolean adaptOldValues) {
Constant.messages.getString(
"authentication.method.script.dialog.loggedInOutIndicatorsInScript.toolTip");
String loggedInIndicator = scriptV2.getLoggedInIndicator();
- this.method.setLoggedInIndicatorPattern(loggedInIndicator);
- this.indicatorsPanel.setLoggedInIndicatorPattern(loggedInIndicator);
- this.indicatorsPanel.setLoggedInIndicatorEnabled(false);
- this.indicatorsPanel.setLoggedInIndicatorToolTip(toolTip);
+ this.method
+ .getVerificationMethod()
+ .setLoggedInIndicatorPattern(loggedInIndicator);
String loggedOutIndicator = scriptV2.getLoggedOutIndicator();
- this.method.setLoggedOutIndicatorPattern(loggedOutIndicator);
- this.indicatorsPanel.setLoggedOutIndicatorPattern(loggedOutIndicator);
- this.indicatorsPanel.setLoggedOutIndicatorEnabled(false);
- this.indicatorsPanel.setLoggedOutIndicatorToolTip(toolTip);
- } else {
+ this.method
+ .getVerificationMethod()
+ .setLoggedOutIndicatorPattern(loggedOutIndicator);
+
+ if (this.indicatorsPanel != null) {
+ this.indicatorsPanel.setLoggedInIndicatorPattern(loggedInIndicator);
+ this.indicatorsPanel.setLoggedInIndicatorEnabled(false);
+ this.indicatorsPanel.setLoggedInIndicatorToolTip(toolTip);
+
+ this.indicatorsPanel.setLoggedOutIndicatorPattern(loggedOutIndicator);
+ this.indicatorsPanel.setLoggedOutIndicatorEnabled(false);
+ this.indicatorsPanel.setLoggedOutIndicatorToolTip(toolTip);
+ }
+ } else if (this.indicatorsPanel != null) {
this.indicatorsPanel.setLoggedInIndicatorEnabled(true);
this.indicatorsPanel.setLoggedInIndicatorToolTip(null);
this.indicatorsPanel.setLoggedOutIndicatorEnabled(true);
@@ -764,8 +776,10 @@ public void loadMethod(
try {
if (s instanceof AuthenticationScriptV2) {
AuthenticationScriptV2 sV2 = (AuthenticationScriptV2) s;
- method.setLoggedInIndicatorPattern(sV2.getLoggedInIndicator());
- method.setLoggedOutIndicatorPattern(sV2.getLoggedOutIndicator());
+ method.getVerificationMethod()
+ .setLoggedInIndicatorPattern(sV2.getLoggedInIndicator());
+ method.getVerificationMethod()
+ .setLoggedOutIndicatorPattern(sV2.getLoggedOutIndicator());
}
method.credentialsParamNames = s.getCredentialsParamsNames();
} catch (Exception e) {
@@ -986,8 +1000,10 @@ public void handleAction(JSONObject params) throws ApiException {
try {
if (s instanceof AuthenticationScriptV2) {
AuthenticationScriptV2 sV2 = (AuthenticationScriptV2) s;
- method.setLoggedInIndicatorPattern(sV2.getLoggedInIndicator());
- method.setLoggedOutIndicatorPattern(sV2.getLoggedOutIndicator());
+ method.getVerificationMethod()
+ .setLoggedInIndicatorPattern(sV2.getLoggedInIndicator());
+ method.getVerificationMethod()
+ .setLoggedOutIndicatorPattern(sV2.getLoggedOutIndicator());
}
method.credentialsParamNames = s.getCredentialsParamsNames();
diff --git a/zap/src/main/java/org/zaproxy/zap/authentication/VerificationMethod.java b/zap/src/main/java/org/zaproxy/zap/authentication/VerificationMethod.java
new file mode 100644
index 00000000000..a9dc8edca9c
--- /dev/null
+++ b/zap/src/main/java/org/zaproxy/zap/authentication/VerificationMethod.java
@@ -0,0 +1,532 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2026 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.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.regex.Pattern;
+import org.apache.commons.httpclient.URI;
+import org.apache.commons.httpclient.URIException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.parosproxy.paros.Constant;
+import org.parosproxy.paros.network.HttpHeader;
+import org.parosproxy.paros.network.HttpMessage;
+import org.parosproxy.paros.network.HttpRequestHeader;
+import org.parosproxy.paros.network.HttpSender;
+import org.parosproxy.paros.view.View;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
+import org.zaproxy.zap.model.SessionStructure;
+import org.zaproxy.zap.users.AuthenticationState;
+import org.zaproxy.zap.users.User;
+import org.zaproxy.zap.utils.Stats;
+
+/**
+ * Handles verification of whether a user is authenticated.
+ *
+ * Verification can be done by checking each request/response for indicator patterns, or by
+ * polling a dedicated URL at a configurable frequency.
+ *
+ * @since 2.18.0
+ */
+public class VerificationMethod {
+
+ private static final Logger LOGGER = LogManager.getLogger(VerificationMethod.class);
+
+ public static final String AUTH_STATE_ASSUMED_IN_STATS = "stats.auth.state.assumedin";
+ public static final String AUTH_STATE_LOGGED_IN_STATS = "stats.auth.state.loggedin";
+ public static final String AUTH_STATE_LOGGED_OUT_STATS = "stats.auth.state.loggedout";
+ public static final String AUTH_STATE_NO_INDICATOR_STATS = "stats.auth.state.noindicator";
+ public static final String AUTH_STATE_UNKNOWN_STATS = "stats.auth.state.unknown";
+
+ public static final int DEFAULT_POLL_FREQUENCY = 60;
+
+ private AuthCheckingStrategy authCheckingStrategy = AuthCheckingStrategy.POLL_URL;
+
+ private String pollMethod;
+ private String pollUrl;
+ private String pollData;
+ private String pollHeaders;
+ private int pollFrequency = DEFAULT_POLL_FREQUENCY;
+ private AuthPollFrequencyUnits pollFrequencyUnits = AuthPollFrequencyUnits.SECONDS;
+
+ private Pattern loggedInIndicatorPattern = null;
+ private Pattern loggedOutIndicatorPattern = null;
+
+ private HttpSender httpSender;
+
+ private BiConsumer userDataReplacer;
+
+ private HttpSender getHttpSender() {
+ if (this.httpSender == null) {
+ this.httpSender = new HttpSender(HttpSender.AUTHENTICATION_POLL_INITIATOR);
+ }
+ return httpSender;
+ }
+
+ /**
+ * Checks if the response received by the HTTP message corresponds to an authenticated web
+ * session.
+ *
+ * If none of the indicators are set up, the method defaults to returning {@code true}, so
+ * that no authentications are attempted when there is no way to check authentication.
+ *
+ * @param msg the http message
+ * @param user the user being checked
+ * @return {@code true} if authenticated or no indicators have been set, {@code false} otherwise
+ */
+ public boolean isAuthenticated(HttpMessage msg, User user) {
+ return this.isAuthenticated(msg, user, false);
+ }
+
+ /**
+ * Checks if the response received by the HTTP message corresponds to an authenticated web
+ * session.
+ *
+ *
If none of the indicators are set up, the method defaults to returning {@code true}, so
+ * that no authentications are attempted when there is no way to check authentication.
+ *
+ * @param msg the http message
+ * @param user the user being checked
+ * @param force always check even when the polling strategy would otherwise skip this check
+ * @return {@code true} if authenticated or no indicators have been set, {@code false} otherwise
+ */
+ public boolean isAuthenticated(HttpMessage msg, User user, boolean force) {
+ if (msg == null
+ || user == null
+ || AuthCheckingStrategy.AUTO_DETECT.equals(this.authCheckingStrategy)) {
+ return false;
+ }
+ AuthenticationState authState = user.getAuthenticationState();
+ if (loggedInIndicatorPattern == null && loggedOutIndicatorPattern == null) {
+ try {
+ Stats.incCounter(SessionStructure.getHostName(msg), AUTH_STATE_NO_INDICATOR_STATS);
+ } catch (URIException e) {
+ // Ignore
+ }
+ if (View.isInitialised()) {
+ View.getSingleton()
+ .getOutputPanel()
+ .append(
+ Constant.messages.getString(
+ "authentication.output.indicatorsNotSet",
+ msg.getRequestHeader().getURI())
+ + "\n");
+ }
+ return true;
+ }
+
+ HttpMessage msgToTest;
+
+ switch (this.authCheckingStrategy) {
+ case EACH_REQ:
+ case EACH_REQ_RESP:
+ case EACH_RESP:
+ msgToTest = msg;
+ break;
+ case POLL_URL:
+ if (!force
+ && authState.getLastPollResult() != null
+ && authState.getLastPollResult()) {
+ switch (pollFrequencyUnits) {
+ case SECONDS:
+ if ((System.currentTimeMillis() - authState.getLastPollTime()) / 1000
+ < pollFrequency) {
+ try {
+ Stats.incCounter(
+ SessionStructure.getHostName(msg),
+ AUTH_STATE_ASSUMED_IN_STATS);
+ } catch (URIException e) {
+ // Ignore
+ }
+ return true;
+ }
+ break;
+ case REQUESTS:
+ default:
+ if (authState.getRequestsSincePoll() < pollFrequency) {
+ authState.incRequestsSincePoll();
+ try {
+ Stats.incCounter(
+ SessionStructure.getHostName(msg),
+ AUTH_STATE_ASSUMED_IN_STATS);
+ } catch (URIException e) {
+ // Ignore
+ }
+ return true;
+ }
+ break;
+ }
+ }
+ try {
+ msgToTest = pollAsUser(user);
+ } catch (Exception e) {
+ LOGGER.warn("Failed sending poll request to {}", this.getPollUrl(), e);
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return evaluateAuthRequest(msgToTest, authState);
+ }
+
+ /**
+ * Evaluates whether the given message indicates an authenticated state, updating the
+ * authentication state accordingly.
+ *
+ * @param msg the http message to evaluate
+ * @param authState the current authentication state to update
+ * @return {@code true} if the message indicates an authenticated state
+ */
+ public boolean evaluateAuthRequest(HttpMessage msg, AuthenticationState authState) {
+ List contentToTest = new ArrayList<>();
+ switch (authCheckingStrategy) {
+ case EACH_REQ:
+ contentToTest.add(msg.getRequestHeader().toString());
+ contentToTest.add(msg.getRequestBody().toString());
+ break;
+ case EACH_REQ_RESP:
+ contentToTest.add(msg.getRequestHeader().toString());
+ contentToTest.add(msg.getRequestBody().toString());
+ contentToTest.add(msg.getResponseHeader().toString());
+ contentToTest.add(msg.getResponseBody().toString());
+ break;
+ case EACH_RESP:
+ case POLL_URL:
+ contentToTest.add(msg.getResponseHeader().toString());
+ contentToTest.add(msg.getResponseBody().toString());
+ break;
+ case AUTO_DETECT:
+ return false;
+ }
+ if (patternMatchesAny(loggedInIndicatorPattern, contentToTest)) {
+ try {
+ Stats.incCounter(SessionStructure.getHostName(msg), AUTH_STATE_LOGGED_IN_STATS);
+ } catch (URIException e) {
+ // Ignore
+ }
+ if (authCheckingStrategy.equals(AuthCheckingStrategy.POLL_URL)) {
+ authState.setLastPollResult(true);
+ }
+ return true;
+ }
+
+ if (loggedOutIndicatorPattern != null
+ && !patternMatchesAny(loggedOutIndicatorPattern, contentToTest)) {
+ // Can't find the unauthenticated indicator — assume authenticated but record as unknown
+ try {
+ Stats.incCounter(SessionStructure.getHostName(msg), AUTH_STATE_UNKNOWN_STATS);
+ } catch (URIException e) {
+ // Ignore
+ }
+ if (this.authCheckingStrategy.equals(AuthCheckingStrategy.POLL_URL)) {
+ authState.setLastPollResult(true);
+ }
+ return true;
+ }
+ try {
+ Stats.incCounter(SessionStructure.getHostName(msg), AUTH_STATE_LOGGED_OUT_STATS);
+ } catch (URIException e) {
+ // Ignore
+ }
+ if (this.authCheckingStrategy.equals(AuthCheckingStrategy.POLL_URL)) {
+ authState.setLastPollResult(false);
+ }
+ return false;
+ }
+
+ /**
+ * Sends a poll request as the given user and returns the response message.
+ *
+ * @param user the user on whose behalf to poll
+ * @return the poll response message
+ * @throws IOException if the poll request could not be sent
+ * @throws IllegalArgumentException if the checking strategy is not {@link
+ * AuthCheckingStrategy#POLL_URL}, or if the poll URL is not set
+ */
+ public HttpMessage pollAsUser(User user) throws IOException {
+ if (!this.authCheckingStrategy.equals(AuthCheckingStrategy.POLL_URL)) {
+ throw new IllegalArgumentException("Authentication checking strategy is not POLL_URL");
+ }
+ if (pollUrl == null || pollUrl.isBlank()) {
+ throw new IllegalArgumentException("Poll URL is not set");
+ }
+ HttpMessage pollMsg = new HttpMessage(new URI(this.getPollUrl(), true));
+ if (!StringUtils.isBlank(pollMethod)) {
+ setRequestBody(pollMsg, pollMethod, getPollData());
+ } else if (this.getPollData() != null && this.getPollData().length() > 0) {
+ setRequestBody(pollMsg, HttpRequestHeader.POST, getPollData());
+ }
+ if (this.getPollHeaders() != null && this.getPollHeaders().length() > 0) {
+ for (String header : this.getPollHeaders().split("\n")) {
+ String[] headerValue = header.split(":", 2);
+ if (headerValue.length == 2) {
+ pollMsg.getRequestHeader()
+ .addHeader(headerValue[0].trim(), headerValue[1].trim());
+ } else {
+ LOGGER.error(
+ "Invalid header '{}' for poll request to {}",
+ header,
+ this.getPollUrl());
+ }
+ }
+ }
+ pollMsg.setRequestingUser(user);
+ if (userDataReplacer != null) {
+ userDataReplacer.accept(pollMsg, user);
+ }
+
+ getHttpSender().sendAndReceive(pollMsg);
+ AuthenticationHelper.addAuthMessageToHistory(
+ pollMsg, List.of(AuthenticationHelper.HISTORY_TAG_VERIFICATION));
+
+ AuthenticationState authState = user.getAuthenticationState();
+ authState.setLastPollTime(System.currentTimeMillis());
+ authState.setRequestsSincePoll(0);
+
+ return pollMsg;
+ }
+
+ private static void setRequestBody(HttpMessage msg, String method, String data) {
+ msg.getRequestHeader().setMethod(method);
+ msg.getRequestBody().setBody(data);
+
+ int bodyLength = msg.getRequestBody().length();
+ if (bodyLength == 0
+ && (HttpRequestHeader.GET.equalsIgnoreCase(method)
+ || HttpRequestHeader.HEAD.equalsIgnoreCase(method))) {
+ msg.getRequestHeader().setHeader(HttpHeader.CONTENT_LENGTH, null);
+ } else {
+ msg.getRequestHeader().setContentLength(bodyLength);
+ }
+ }
+
+ private static boolean patternMatchesAny(Pattern pattern, List content) {
+ if (pattern != null) {
+ for (String str : content) {
+ if (pattern.matcher(str).find()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the logged in indicator pattern.
+ *
+ * @return the logged in indicator pattern
+ */
+ public Pattern getLoggedInIndicatorPattern() {
+ return loggedInIndicatorPattern;
+ }
+
+ /**
+ * Sets the logged in indicator pattern.
+ *
+ * @param loggedInIndicatorPattern the new logged in indicator pattern
+ */
+ public void setLoggedInIndicatorPattern(String loggedInIndicatorPattern) {
+ if (loggedInIndicatorPattern == null || loggedInIndicatorPattern.trim().length() == 0) {
+ this.loggedInIndicatorPattern = null;
+ } else {
+ this.loggedInIndicatorPattern = Pattern.compile(loggedInIndicatorPattern);
+ }
+ }
+
+ /**
+ * Gets the logged out indicator pattern.
+ *
+ * @return the logged out indicator pattern
+ */
+ public Pattern getLoggedOutIndicatorPattern() {
+ return loggedOutIndicatorPattern;
+ }
+
+ /**
+ * Sets the logged out indicator pattern.
+ *
+ * @param loggedOutIndicatorPattern the new logged out indicator pattern
+ */
+ public void setLoggedOutIndicatorPattern(String loggedOutIndicatorPattern) {
+ if (loggedOutIndicatorPattern == null || loggedOutIndicatorPattern.trim().length() == 0) {
+ this.loggedOutIndicatorPattern = null;
+ } else {
+ this.loggedOutIndicatorPattern = Pattern.compile(loggedOutIndicatorPattern);
+ }
+ }
+
+ public AuthCheckingStrategy getAuthCheckingStrategy() {
+ return authCheckingStrategy;
+ }
+
+ public void setAuthCheckingStrategy(AuthCheckingStrategy authCheckingStrategy) {
+ Objects.requireNonNull(authCheckingStrategy);
+ this.authCheckingStrategy = authCheckingStrategy;
+ }
+
+ public String getPollMethod() {
+ return pollMethod;
+ }
+
+ public void setPollMethod(String pollMethod) {
+ this.pollMethod = StringUtils.isBlank(pollMethod) ? null : pollMethod;
+ }
+
+ public String getPollUrl() {
+ return pollUrl;
+ }
+
+ public void setPollUrl(String pollUrl) {
+ this.pollUrl = pollUrl;
+ }
+
+ public String getPollData() {
+ return pollData;
+ }
+
+ public void setPollData(String pollData) {
+ this.pollData = pollData;
+ }
+
+ public String getPollHeaders() {
+ return pollHeaders;
+ }
+
+ public void setPollHeaders(String pollHeaders) {
+ this.pollHeaders = pollHeaders;
+ }
+
+ public int getPollFrequency() {
+ return pollFrequency;
+ }
+
+ public void setPollFrequency(int pollFrequency) {
+ this.pollFrequency = pollFrequency;
+ }
+
+ public AuthPollFrequencyUnits getPollFrequencyUnits() {
+ return pollFrequencyUnits;
+ }
+
+ public void setPollFrequencyUnits(AuthPollFrequencyUnits pollFrequencyUnits) {
+ this.pollFrequencyUnits = pollFrequencyUnits;
+ }
+
+ /**
+ * Sets the callback used to replace user-specific data (e.g. username/password tokens) in poll
+ * requests. If {@code null}, no replacement is performed.
+ *
+ * @param userDataReplacer the callback, or {@code null}
+ */
+ public void setUserDataReplacer(BiConsumer userDataReplacer) {
+ this.userDataReplacer = userDataReplacer;
+ }
+
+ /**
+ * Gets the callback used to replace user-specific data in poll requests.
+ *
+ * @return the callback, or {@code null} if no replacement is configured
+ */
+ public BiConsumer getUserDataReplacer() {
+ return userDataReplacer;
+ }
+
+ /**
+ * Creates a deep copy of this verification method.
+ *
+ * @return a deep copy
+ */
+ public VerificationMethod copy(BiConsumer userDataReplacer) {
+ VerificationMethod clone = new VerificationMethod();
+ clone.authCheckingStrategy = this.authCheckingStrategy;
+ clone.pollUrl = this.pollMethod;
+ clone.pollUrl = this.pollUrl;
+ clone.pollData = this.pollData;
+ clone.pollHeaders = this.pollHeaders;
+ clone.pollFrequency = this.pollFrequency;
+ clone.pollFrequencyUnits = this.pollFrequencyUnits;
+ clone.loggedInIndicatorPattern = this.loggedInIndicatorPattern;
+ clone.loggedOutIndicatorPattern = this.loggedOutIndicatorPattern;
+ clone.userDataReplacer = userDataReplacer;
+ return clone;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ loggedInIndicatorPattern == null ? null : loggedInIndicatorPattern.pattern(),
+ loggedOutIndicatorPattern == null ? null : loggedOutIndicatorPattern.pattern(),
+ authCheckingStrategy,
+ pollMethod,
+ pollUrl,
+ pollData,
+ pollHeaders,
+ pollFrequency,
+ pollFrequencyUnits);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ VerificationMethod other = (VerificationMethod) obj;
+ if (!isSamePattern(loggedInIndicatorPattern, other.loggedInIndicatorPattern)) {
+ return false;
+ }
+ if (!isSamePattern(loggedOutIndicatorPattern, other.loggedOutIndicatorPattern)) {
+ return false;
+ }
+ if (!this.authCheckingStrategy.equals(other.authCheckingStrategy)) {
+ return false;
+ }
+ if (!Objects.equals(pollMethod, other.pollMethod)) {
+ return false;
+ }
+ if (!Objects.equals(this.pollUrl, other.pollUrl)) {
+ return false;
+ }
+ if (!Objects.equals(this.pollData, other.pollData)) {
+ return false;
+ }
+ if (!Objects.equals(this.pollHeaders, other.pollHeaders)) {
+ return false;
+ }
+ if (this.pollFrequency != other.pollFrequency) {
+ return false;
+ }
+ return this.pollFrequencyUnits.equals(other.pollFrequencyUnits);
+ }
+
+ private static boolean isSamePattern(Pattern pattern, Pattern other) {
+ if (pattern == null) {
+ return other == null;
+ }
+ return other != null && pattern.pattern().equals(other.pattern());
+ }
+}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/api/ApiGeneratorUtils.java b/zap/src/main/java/org/zaproxy/zap/extension/api/ApiGeneratorUtils.java
index 30ae98b5165..a9667abca88 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/api/ApiGeneratorUtils.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/api/ApiGeneratorUtils.java
@@ -27,6 +27,7 @@
import org.zaproxy.zap.extension.anticsrf.AntiCsrfParam;
import org.zaproxy.zap.extension.ascan.ActiveScanAPI;
import org.zaproxy.zap.extension.authentication.AuthenticationAPI;
+import org.zaproxy.zap.extension.authentication.VerificationAPI;
import org.zaproxy.zap.extension.authorization.AuthorizationAPI;
import org.zaproxy.zap.extension.autoupdate.AutoUpdateAPI;
import org.zaproxy.zap.extension.autoupdate.OptionsParamCheckForUpdates;
@@ -85,6 +86,8 @@ public static List getAllImplementors() {
imps.add(new AuthenticationAPI(null));
+ imps.add(new VerificationAPI());
+
imps.add(new AuthorizationAPI());
imps.add(new RuleConfigAPI(null));
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/api/ContextAPI.java b/zap/src/main/java/org/zaproxy/zap/extension/api/ContextAPI.java
index 24cd21002a6..def43773f20 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/api/ContextAPI.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/api/ContextAPI.java
@@ -46,6 +46,7 @@
import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
import org.zaproxy.zap.authentication.AuthenticationMethodType;
+import org.zaproxy.zap.authentication.VerificationMethod;
import org.zaproxy.zap.extension.api.ApiException.Type;
import org.zaproxy.zap.extension.authorization.AuthorizationDetectionMethod;
import org.zaproxy.zap.model.Context;
@@ -113,7 +114,7 @@ public ContextAPI() {
new ApiAction(
ACTION_SET_CONTEXT_REGEXS,
new String[] {CONTEXT_NAME, INC_REGEXS_PARAM, EXC_REGEXS_PARAM}));
- this.addApiAction(
+ ApiAction setCheckingStrategyAction =
new ApiAction(
ACTION_SET_CONTEXT_CHECKING_STRATEGY,
new String[] {CONTEXT_NAME, PARAM_CHECKING_STRATEGRY},
@@ -123,7 +124,11 @@ public ContextAPI() {
PARAM_POLL_HEADERS,
PARAM_POLL_FREQ,
PARAM_POLL_FREQ_UNITS
- }));
+ });
+ setCheckingStrategyAction.setDeprecated(true);
+ setCheckingStrategyAction.setDeprecatedDescription(
+ "Use verification/setVerificationMethod instead.");
+ this.addApiAction(setCheckingStrategyAction);
this.addApiAction(new ApiAction(ACTION_NEW_CONTEXT, contextNameOnlyParam));
this.addApiAction(new ApiAction(ACTION_REMOVE_CONTEXT, contextNameOnlyParam));
this.addApiAction(
@@ -244,13 +249,13 @@ public ApiResponse handleApiAction(String name, JSONObject params) throws ApiExc
throw new ApiException(
ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_FREQ);
}
- context.getAuthenticationMethod().setPollUrl(pollUrl);
- context.getAuthenticationMethod().setPollData(pollData);
- context.getAuthenticationMethod().setPollHeaders(pollHeaders);
- context.getAuthenticationMethod().setPollFrequency(freq);
- context.getAuthenticationMethod().setPollFrequencyUnits(units);
+ context.getVerificationMethod().setPollUrl(pollUrl);
+ context.getVerificationMethod().setPollData(pollData);
+ context.getVerificationMethod().setPollHeaders(pollHeaders);
+ context.getVerificationMethod().setPollFrequency(freq);
+ context.getVerificationMethod().setPollFrequencyUnits(units);
}
- context.getAuthenticationMethod().setAuthCheckingStrategy(checkingStrategy);
+ context.getVerificationMethod().setAuthCheckingStrategy(checkingStrategy);
Model.getSingleton().getSession().saveContext(context);
break;
case ACTION_NEW_CONTEXT:
@@ -487,22 +492,22 @@ private ApiResponse buildResponseFromContext(Context c) {
AuthenticationMethod authenticationMethod = c.getAuthenticationMethod();
if (authenticationMethod != null) {
- Pattern pattern = authenticationMethod.getLoggedInIndicatorPattern();
+ VerificationMethod vm = c.getVerificationMethod();
+ Pattern pattern = vm.getLoggedInIndicatorPattern();
fields.put("loggedInPattern", pattern == null ? "" : pattern.toString());
- pattern = authenticationMethod.getLoggedOutIndicatorPattern();
+ pattern = vm.getLoggedOutIndicatorPattern();
fields.put("loggedOutPattern", pattern == null ? "" : pattern.toString());
AuthenticationMethodType type = authenticationMethod.getType();
fields.put("authType", type == null ? "" : type.getName());
- AuthCheckingStrategy strategy = authenticationMethod.getAuthCheckingStrategy();
+ AuthCheckingStrategy strategy = vm.getAuthCheckingStrategy();
fields.put(PARAM_CHECKING_STRATEGRY, strategy == null ? "" : strategy.name());
if (AuthCheckingStrategy.POLL_URL.equals(strategy)) {
- fields.put(PARAM_POLL_URL, authenticationMethod.getPollUrl());
- fields.put(PARAM_POLL_DATA, authenticationMethod.getPollData());
- fields.put(PARAM_POLL_HEADERS, authenticationMethod.getPollHeaders());
- fields.put(
- PARAM_POLL_FREQ, Integer.toString(authenticationMethod.getPollFrequency()));
- AuthPollFrequencyUnits units = authenticationMethod.getPollFrequencyUnits();
+ fields.put(PARAM_POLL_URL, vm.getPollUrl());
+ fields.put(PARAM_POLL_DATA, vm.getPollData());
+ fields.put(PARAM_POLL_HEADERS, vm.getPollHeaders());
+ fields.put(PARAM_POLL_FREQ, Integer.toString(vm.getPollFrequency()));
+ AuthPollFrequencyUnits units = vm.getPollFrequencyUnits();
fields.put(PARAM_POLL_FREQ_UNITS, units == null ? "" : units.name());
}
}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/authentication/AuthenticationAPI.java b/zap/src/main/java/org/zaproxy/zap/extension/authentication/AuthenticationAPI.java
index 68a9ba712a0..b8521a04995 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/authentication/AuthenticationAPI.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/authentication/AuthenticationAPI.java
@@ -81,23 +81,44 @@ public AuthenticationAPI(ExtensionAuthentication extension) {
this.addApiView(
new ApiView(VIEW_GET_METHOD_CONFIG_PARAMETERS, new String[] {PARAM_METHOD_NAME}));
this.addApiView(new ApiView(VIEW_GET_AUTHENTICATION, new String[] {PARAM_CONTEXT_ID}));
- this.addApiView(new ApiView(VIEW_GET_LOGGED_IN_INDICATOR, new String[] {PARAM_CONTEXT_ID}));
- this.addApiView(
- new ApiView(VIEW_GET_LOGGED_OUT_INDICATOR, new String[] {PARAM_CONTEXT_ID}));
+
+ ApiView getLoggedInView =
+ new ApiView(VIEW_GET_LOGGED_IN_INDICATOR, new String[] {PARAM_CONTEXT_ID});
+ getLoggedInView.setDeprecated(true);
+ getLoggedInView.setDeprecatedDescription(
+ "Use verification/" + VIEW_GET_LOGGED_IN_INDICATOR + " instead.");
+ this.addApiView(getLoggedInView);
+
+ ApiView getLoggedOutView =
+ new ApiView(VIEW_GET_LOGGED_OUT_INDICATOR, new String[] {PARAM_CONTEXT_ID});
+ getLoggedOutView.setDeprecated(true);
+ getLoggedOutView.setDeprecatedDescription(
+ "Use verification/" + VIEW_GET_LOGGED_OUT_INDICATOR + " instead.");
+ this.addApiView(getLoggedOutView);
this.addApiAction(
new ApiAction(
ACTION_SET_METHOD,
new String[] {PARAM_CONTEXT_ID, PARAM_METHOD_NAME},
new String[] {PARAM_METHOD_CONFIG_PARAMS}));
- this.addApiAction(
+
+ ApiAction setLoggedInAction =
new ApiAction(
ACTION_SET_LOGGED_IN_INDICATOR,
- new String[] {PARAM_CONTEXT_ID, PARAM_LOGGED_IN_INDICATOR}));
- this.addApiAction(
+ new String[] {PARAM_CONTEXT_ID, PARAM_LOGGED_IN_INDICATOR});
+ setLoggedInAction.setDeprecated(true);
+ setLoggedInAction.setDeprecatedDescription(
+ "Use verification/" + ACTION_SET_LOGGED_IN_INDICATOR + " instead.");
+ this.addApiAction(setLoggedInAction);
+
+ ApiAction setLoggedOutAction =
new ApiAction(
ACTION_SET_LOGGED_OUT_INDICATOR,
- new String[] {PARAM_CONTEXT_ID, PARAM_LOGGED_OUT_INDICATOR}));
+ new String[] {PARAM_CONTEXT_ID, PARAM_LOGGED_OUT_INDICATOR});
+ setLoggedOutAction.setDeprecated(true);
+ setLoggedOutAction.setDeprecatedDescription(
+ "Use verification/" + ACTION_SET_LOGGED_OUT_INDICATOR + " instead.");
+ this.addApiAction(setLoggedOutAction);
}
@Override
@@ -114,13 +135,13 @@ public ApiResponse handleApiView(String name, JSONObject params) throws ApiExcep
return getContext(params).getAuthenticationMethod().getApiResponseRepresentation();
case VIEW_GET_LOGGED_IN_INDICATOR:
Pattern loggedInPattern =
- getContext(params).getAuthenticationMethod().getLoggedInIndicatorPattern();
+ getContext(params).getVerificationMethod().getLoggedInIndicatorPattern();
if (loggedInPattern != null)
return new ApiResponseElement("logged_in_regex", loggedInPattern.toString());
else return new ApiResponseElement("logged_in_regex", "");
case VIEW_GET_LOGGED_OUT_INDICATOR:
Pattern loggedOutPattern =
- getContext(params).getAuthenticationMethod().getLoggedOutIndicatorPattern();
+ getContext(params).getVerificationMethod().getLoggedOutIndicatorPattern();
if (loggedOutPattern != null)
return new ApiResponseElement("logged_out_regex", loggedOutPattern.toString());
else return new ApiResponseElement("logged_out_regex", "");
@@ -149,7 +170,7 @@ public ApiResponse handleApiAction(String name, JSONObject params) throws ApiExc
if (loggedInIndicator == null || loggedInIndicator.isEmpty())
throw new ApiException(Type.MISSING_PARAMETER, PARAM_LOGGED_IN_INDICATOR);
context = getContext(params);
- context.getAuthenticationMethod().setLoggedInIndicatorPattern(loggedInIndicator);
+ context.getVerificationMethod().setLoggedInIndicatorPattern(loggedInIndicator);
context.save();
return ApiResponseElement.OK;
@@ -158,7 +179,7 @@ public ApiResponse handleApiAction(String name, JSONObject params) throws ApiExc
if (loggedOutIndicator == null || loggedOutIndicator.isEmpty())
throw new ApiException(Type.MISSING_PARAMETER, PARAM_LOGGED_OUT_INDICATOR);
context = getContext(params);
- context.getAuthenticationMethod().setLoggedOutIndicatorPattern(loggedOutIndicator);
+ context.getVerificationMethod().setLoggedOutIndicatorPattern(loggedOutIndicator);
context.save();
return ApiResponseElement.OK;
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/authentication/ContextAuthenticationPanel.java b/zap/src/main/java/org/zaproxy/zap/extension/authentication/ContextAuthenticationPanel.java
index 417ec404678..ed8e86b5afd 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/authentication/ContextAuthenticationPanel.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/authentication/ContextAuthenticationPanel.java
@@ -21,54 +21,35 @@
import java.awt.BorderLayout;
import java.awt.CardLayout;
-import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
-import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
import javax.swing.JComboBox;
-import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
-import javax.swing.JSpinner;
import javax.swing.border.EmptyBorder;
-import org.apache.commons.httpclient.URI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
-import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.Session;
-import org.parosproxy.paros.model.SiteNode;
-import org.parosproxy.paros.network.HttpRequestHeader;
-import org.parosproxy.paros.view.View;
import org.zaproxy.zap.authentication.AbstractAuthenticationMethodOptionsPanel;
-import org.zaproxy.zap.authentication.AuthenticationIndicatorsPanel;
import org.zaproxy.zap.authentication.AuthenticationMethod;
-import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
-import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
import org.zaproxy.zap.authentication.AuthenticationMethodType;
+import org.zaproxy.zap.authentication.VerificationMethod;
import org.zaproxy.zap.extension.users.ExtensionUserManagement;
import org.zaproxy.zap.model.Context;
import org.zaproxy.zap.users.User;
import org.zaproxy.zap.utils.FontUtils;
import org.zaproxy.zap.utils.ZapHtmlLabel;
-import org.zaproxy.zap.utils.ZapNumberSpinner;
-import org.zaproxy.zap.utils.ZapTextArea;
-import org.zaproxy.zap.utils.ZapTextField;
import org.zaproxy.zap.view.AbstractContextPropertiesPanel;
import org.zaproxy.zap.view.LayoutHelper;
-import org.zaproxy.zap.view.NodeSelectDialog;
/** The Context Panel shown for configuring a Context's authentication methods. */
@SuppressWarnings("serial")
@@ -81,36 +62,21 @@ public class ContextAuthenticationPanel extends AbstractContextPropertiesPanel {
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 PANEL_TITLE_VERIF =
- Constant.messages.getString("authentication.panel.label.verifTitle");
- private static final String LABEL_POLL_URL =
- Constant.messages.getString("authentication.panel.label.pollurl");
- private static final String LABEL_POLL_DATA =
- Constant.messages.getString("authentication.panel.label.polldata");
- private static final String LABEL_POLL_HEADERS =
- Constant.messages.getString("authentication.panel.label.pollheaders");
- private static final String LABEL_POLL_FREQUENCY =
- Constant.messages.getString("authentication.panel.label.freq");
private static final String LABEL_CONFIG_NOT_NEEDED =
Constant.messages.getString("sessionmanagement.panel.label.noConfigPanel");
- private static final String LABEL_STRATEGY =
- Constant.messages.getString("authentication.panel.label.strategy");
- private static final String STRATEGY_PREFIX = "authentication.panel.label.strategy.";
- private static final String FREQUENCY_UNITS_PREFIX = "authentication.panel.label.units.";
/** The extension. */
private ExtensionAuthentication extension;
+ /** The verification panel — owns all verification UI fields. */
+ private ContextVerificationPanel verificationPanel;
+
/** The authentication method types combo box. */
private JComboBox authenticationMethodsComboBox;
@@ -126,38 +92,35 @@ public class ContextAuthenticationPanel extends AbstractContextPropertiesPanel {
/** The container panel for the authentication method's configuration. */
private JPanel configContainerPanel;
- /** The container panel for the authentication verification configuration. */
- private JPanel verifContainerPanel;
-
- private JComboBox authenticationVerifComboBox;
- private JComboBox authFrequencyUnitsComboBox;
-
- private JButton pollUrlSelectButton = null;
- private ZapTextField pollUrlField = null;
- private ZapTextField pollDataField = null;
- private ZapTextArea pollHeadersField = null;
- private ZapNumberSpinner pollFrequency = null;
-
- private ZapTextField loggedInIndicatorRegexField = null;
- private ZapTextField loggedOutIndicatorRegexField = 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
+ * @param verificationPanel the sibling verification panel that owns indicator fields
*/
- public ContextAuthenticationPanel(ExtensionAuthentication extension, Context context) {
+ public ContextAuthenticationPanel(
+ ExtensionAuthentication extension,
+ Context context,
+ ContextVerificationPanel verificationPanel) {
super(context.getId());
this.extension = extension;
+ this.verificationPanel = verificationPanel;
initialize();
}
+ /**
+ * @deprecated Use {@link #ContextAuthenticationPanel(ExtensionAuthentication, Context,
+ * ContextVerificationPanel)} instead.
+ */
+ @Deprecated
+ public ContextAuthenticationPanel(ExtensionAuthentication extension, Context context) {
+ this(extension, context, new ContextVerificationPanel(context));
+ }
+
public static String buildName(int contextId) {
return contextId + ": " + PANEL_NAME;
}
@@ -173,7 +136,7 @@ private void initialize() {
panel.setLayout(new GridBagLayout());
// Only known way to minimise the horizontal space taken up
// needs to be big enough to cope with Form based auth panel
- panel.setPreferredSize(new Dimension(400, 800));
+ panel.setPreferredSize(new Dimension(400, 600));
JScrollPane scrollPanel = new JScrollPane();
scrollPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
@@ -194,70 +157,14 @@ private void initialize() {
getConfigContainerPanel(),
LayoutHelper.getGBC(0, 3, 1, 1.0d, new Insets(10, 0, 10, 0)));
- // Verification panel container
- panel.add(
- getVerifContainerPanel(),
- LayoutHelper.getGBC(0, 4, 1, 1.0d, new Insets(10, 0, 10, 0)));
-
- int y = 0;
- int fullWidth = 3;
- getVerifContainerPanel()
- .add(new JLabel(LABEL_STRATEGY), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- getVerifContainerPanel()
- .add(
- getAuthenticationVerifComboBox(),
- LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
-
- getVerifContainerPanel()
- .add(
- new JLabel(FIELD_LABEL_LOGGED_IN_INDICATOR),
- LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- getVerifContainerPanel()
- .add(
- getLoggedInIndicatorRegexField(),
- LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- getVerifContainerPanel()
- .add(
- new JLabel(FIELD_LABEL_LOGGED_OUT_INDICATOR),
- LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- getVerifContainerPanel()
- .add(
- getLoggedOutIndicatorRegexField(),
- LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
-
- getVerifContainerPanel()
- .add(new JLabel(LABEL_POLL_FREQUENCY), LayoutHelper.getGBC(0, y, 1, 0.34D));
- getVerifContainerPanel()
- .add(getPollFrequencySpinner(), LayoutHelper.getGBC(1, y, 1, 0.33D));
- getVerifContainerPanel()
- .add(getAuthFrequencyUnitsComboBox(), LayoutHelper.getGBC(2, y++, 1, 0.33D));
-
- getVerifContainerPanel()
- .add(new JLabel(LABEL_POLL_URL), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- JPanel urlPanel = new JPanel(new GridBagLayout());
- urlPanel.add(this.getPollUrlField(), LayoutHelper.getGBC(0, 0, 1, 1.0D));
- urlPanel.add(getPollUrlSelectButton(), LayoutHelper.getGBC(1, 0, 1, 0.0D));
- getVerifContainerPanel().add(urlPanel, LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- getVerifContainerPanel()
- .add(new JLabel(LABEL_POLL_DATA), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- getVerifContainerPanel()
- .add(this.getPollDataField(), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- getVerifContainerPanel()
- .add(new JLabel(LABEL_POLL_HEADERS), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
- getVerifContainerPanel()
- .add(this.getPollHeadersField(), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
-
// Padding
panel.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.
+ * Changes the shown method's configuration panel with a new one based on a new method type.
*
- * @param newMethodType the new method type. If null, nothing is shown.
+ * @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
@@ -330,23 +237,23 @@ public void itemStateChanged(ItemEvent e) {
return;
}
}
- resetLoggedInOutIndicators();
-
- // 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(getContextId());
+ // Copy current verification state into the new auth method's VM
+ VerificationMethod vmCopy =
+ verificationPanel.buildVerificationMethodFromUI();
+ vmCopy.setUserDataReplacer(
+ selectedAuthenticationMethod
+ ::replaceUserDataInPollRequest);
+ selectedAuthenticationMethod.setVerificationMethod(vmCopy);
}
- // Show the configuration panel
changeMethodConfigPanel(type);
if (type.hasOptionsPanel()) {
shownConfigPanel.bindMethod(
- selectedAuthenticationMethod,
- getAuthenticationIndicatorsPanel());
+ selectedAuthenticationMethod, verificationPanel);
}
}
}
@@ -355,72 +262,6 @@ public void itemStateChanged(ItemEvent e) {
return authenticationMethodsComboBox;
}
- private JComboBox getAuthenticationVerifComboBox() {
- if (authenticationVerifComboBox == null) {
- authenticationVerifComboBox = new JComboBox<>();
- for (AuthCheckingStrategyType acst : AuthCheckingStrategyType.getAllValues()) {
- authenticationVerifComboBox.addItem(acst);
- }
- // Prepare the listener for the change of selection
- authenticationVerifComboBox.addItemListener(
- new ItemListener() {
-
- @Override
- public void itemStateChanged(ItemEvent e) {
- if (e.getStateChange() == ItemEvent.SELECTED
- && !e.getItem().equals(shownMethodType)) {
-
- setPollFieldStatuses(((AuthCheckingStrategyType) e.getItem()));
- }
- }
- });
- }
- return authenticationVerifComboBox;
- }
-
- private void setPollFieldStatuses(AuthCheckingStrategyType type) {
- boolean isPoll = type.getStrategy().equals(AuthCheckingStrategy.POLL_URL);
- getAuthFrequencyUnitsComboBox().setEnabled(isPoll);
- getPollFrequencySpinner().setEnabled(isPoll);
- getPollUrlSelectButton().setEnabled(isPoll);
- getPollUrlField().setEnabled(isPoll);
- getPollDataField().setEnabled(isPoll);
- getPollHeadersField().setEnabled(isPoll);
- boolean isAutoDetect = type.getStrategy().equals(AuthCheckingStrategy.AUTO_DETECT);
- this.getLoggedInIndicatorRegexField().setEnabled(!isAutoDetect);
- this.getLoggedOutIndicatorRegexField().setEnabled(!isAutoDetect);
- }
-
- private JComboBox getAuthFrequencyUnitsComboBox() {
- if (authFrequencyUnitsComboBox == null) {
- authFrequencyUnitsComboBox = new JComboBox<>();
- for (AuthPollFrequencyUnitsType acst : AuthPollFrequencyUnitsType.getAllValues()) {
- authFrequencyUnitsComboBox.addItem(acst);
- }
- }
- return authFrequencyUnitsComboBox;
- }
-
- private ZapNumberSpinner getPollFrequencySpinner() {
- if (pollFrequency == null) {
- pollFrequency =
- new ZapNumberSpinner(
- 1, AuthenticationMethod.DEFAULT_POLL_FREQUENCY, Integer.MAX_VALUE);
- // Reduce the field size otherwise it takes up too much space
- Component mySpinnerEditor = pollFrequency.getEditor();
- JFormattedTextField jftf = ((JSpinner.DefaultEditor) mySpinnerEditor).getTextField();
- jftf.setColumns(6);
- }
- return pollFrequency;
- }
-
- private AuthenticationIndicatorsPanel getAuthenticationIndicatorsPanel() {
- if (authenticationIndicatorsPanel == null) {
- authenticationIndicatorsPanel = new AuthenticationIndicatorsPanelImpl();
- }
- return authenticationIndicatorsPanel;
- }
-
/**
* Make sure the user acknowledges the Users corresponding to this context will have the
* credentials changed with the new type of authentication method.
@@ -479,120 +320,6 @@ private JPanel getConfigContainerPanel() {
return configContainerPanel;
}
- private JPanel getVerifContainerPanel() {
- if (verifContainerPanel == null) {
- verifContainerPanel = new JPanel(new GridBagLayout());
- verifContainerPanel.setBorder(
- javax.swing.BorderFactory.createTitledBorder(
- null,
- PANEL_TITLE_VERIF,
- javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
- javax.swing.border.TitledBorder.DEFAULT_POSITION,
- FontUtils.getFont(FontUtils.Size.standard)));
- }
- return verifContainerPanel;
- }
-
- private JButton getPollUrlSelectButton() {
- if (pollUrlSelectButton == null) {
- pollUrlSelectButton = new JButton(Constant.messages.getString("all.button.select"));
- pollUrlSelectButton.setIcon(
- new ImageIcon(
- View.class.getResource("/resource/icon/16/094.png"))); // Globe Icon
- // Add behaviour for Node Select dialog
- pollUrlSelectButton.addActionListener(
- new java.awt.event.ActionListener() {
- @Override
- public void actionPerformed(java.awt.event.ActionEvent e) {
- NodeSelectDialog nsd =
- new NodeSelectDialog(View.getSingleton().getMainFrame());
- // Try to pre-select the node according to what has been inserted in the
- // fields
- SiteNode node = null;
- if (getPollUrlField().getText().trim().length() > 0)
- try {
- // If it's a POST query
- if (getPollDataField().getText().trim().length() > 0)
- node =
- Model.getSingleton()
- .getSession()
- .getSiteTree()
- .findNode(
- new URI(
- getPollUrlField().getText(),
- false),
- HttpRequestHeader.POST,
- getPollDataField().getText());
- else
- node =
- Model.getSingleton()
- .getSession()
- .getSiteTree()
- .findNode(
- new URI(
- getPollUrlField().getText(),
- false));
- } catch (Exception e2) {
- // Ignore. It means we could not properly get a node for the
- // existing
- // value and does not have any harmful effects
- }
-
- // Show the dialog and wait for input
- node = nsd.showDialog(node);
- if (node != null && node.getHistoryReference() != null) {
- try {
-
- getPollUrlField()
- .setText(
- node.getHistoryReference().getURI().toString());
- getPollDataField()
- .setText(
- node.getHistoryReference()
- .getHttpMessage()
- .getRequestBody()
- .toString());
- } catch (Exception e1) {
- LOGGER.error(e1.getMessage(), e1);
- }
- }
- }
- });
- }
- return pollUrlSelectButton;
- }
-
- private ZapTextField getPollUrlField() {
- if (pollUrlField == null) {
- pollUrlField = new ZapTextField();
- }
- return pollUrlField;
- }
-
- private ZapTextField getPollDataField() {
- if (pollDataField == null) {
- pollDataField = new ZapTextField();
- }
- return pollDataField;
- }
-
- private ZapTextArea getPollHeadersField() {
- if (pollHeadersField == null) {
- pollHeadersField = new ZapTextArea(2, 0);
- }
- return pollHeadersField;
- }
-
- private ZapTextField getLoggedInIndicatorRegexField() {
- if (loggedInIndicatorRegexField == null) loggedInIndicatorRegexField = new ZapTextField();
- return loggedInIndicatorRegexField;
- }
-
- private ZapTextField getLoggedOutIndicatorRegexField() {
- if (loggedOutIndicatorRegexField == null) loggedOutIndicatorRegexField = new ZapTextField();
- return loggedOutIndicatorRegexField;
- }
-
@Override
public String getHelpIndex() {
return "ui.dialogs.context-auth";
@@ -606,45 +333,7 @@ public void initContextData(Session session, Context uiSharedContext) {
selectedAuthenticationMethod,
uiSharedContext.getName());
- resetLoggedInOutIndicators();
-
- // If something was already configured, find the type and set the UI accordingly
if (selectedAuthenticationMethod != null) {
- // Set verification
- if (selectedAuthenticationMethod.getAuthCheckingStrategy() != null) {
- getAuthenticationVerifComboBox()
- .getModel()
- .setSelectedItem(
- new AuthCheckingStrategyType(
- selectedAuthenticationMethod.getAuthCheckingStrategy()));
- }
- setPollFieldStatuses(
- (AuthCheckingStrategyType) getAuthenticationVerifComboBox().getSelectedItem());
-
- getPollUrlField().setText(selectedAuthenticationMethod.getPollUrl());
- getPollDataField().setText(selectedAuthenticationMethod.getPollData());
- getPollHeadersField().setText(selectedAuthenticationMethod.getPollHeaders());
- getPollFrequencySpinner().setValue(selectedAuthenticationMethod.getPollFrequency());
- getAuthFrequencyUnitsComboBox()
- .setSelectedItem(
- new AuthPollFrequencyUnitsType(
- selectedAuthenticationMethod.getPollFrequencyUnits()));
-
- if (selectedAuthenticationMethod.getLoggedInIndicatorPattern() != null)
- getLoggedInIndicatorRegexField()
- .setText(
- selectedAuthenticationMethod
- .getLoggedInIndicatorPattern()
- .pattern());
- else getLoggedInIndicatorRegexField().setText("");
- if (selectedAuthenticationMethod.getLoggedOutIndicatorPattern() != null)
- getLoggedOutIndicatorRegexField()
- .setText(
- selectedAuthenticationMethod
- .getLoggedOutIndicatorPattern()
- .pattern());
- else getLoggedOutIndicatorRegexField().setText("");
-
// If the proper type is already selected, just rebind the data
if (shownMethodType != null
&& shownMethodType.isTypeForMethod(selectedAuthenticationMethod)) {
@@ -652,8 +341,7 @@ public void initContextData(Session session, Context uiSharedContext) {
LOGGER.debug(
"Binding authentication method to existing panel of proper type for context {}",
uiSharedContext.getName());
- shownConfigPanel.bindMethod(
- selectedAuthenticationMethod, getAuthenticationIndicatorsPanel());
+ shownConfigPanel.bindMethod(selectedAuthenticationMethod, verificationPanel);
}
return;
}
@@ -661,13 +349,9 @@ public void initContextData(Session session, Context uiSharedContext) {
// 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
LOGGER.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;
@@ -676,84 +360,13 @@ public void initContextData(Session session, Context uiSharedContext) {
}
}
- /**
- * Resets the tool tip and enables the fields of the logged in/out indicators.
- *
- * @see #getLoggedInIndicatorRegexField()
- * @see #getLoggedOutIndicatorRegexField()
- */
- private void resetLoggedInOutIndicators() {
- getLoggedInIndicatorRegexField().setToolTipText(null);
- getLoggedInIndicatorRegexField().setEnabled(true);
- getLoggedOutIndicatorRegexField().setToolTipText(null);
- getLoggedOutIndicatorRegexField().setEnabled(true);
- }
-
@Override
public void validateContextData(Session session) throws Exception {
if (shownConfigPanel != null) shownConfigPanel.validateFields();
- try {
- Pattern.compile(getLoggedInIndicatorRegexField().getText());
- Pattern.compile(getLoggedOutIndicatorRegexField().getText());
- } catch (PatternSyntaxException e) {
- throw new IllegalStateException(
- Constant.messages.getString(
- "authentication.panel.error.illegalPattern",
- getUISharedContext().getName()),
- e);
- }
- if (((AuthCheckingStrategyType) getAuthenticationVerifComboBox().getSelectedItem())
- .getStrategy()
- .equals(AuthCheckingStrategy.POLL_URL)) {
- String url = this.getPollUrlField().getText();
- if (url.length() == 0) {
- throw new IllegalStateException(
- Constant.messages.getString(
- "authentication.panel.error.nopollurl",
- getUISharedContext().getName()));
- } else {
- try {
- new URI(url, true);
- } catch (Exception e) {
- throw new IllegalStateException(
- Constant.messages.getString(
- "authentication.panel.error.badpollurl",
- getUISharedContext().getName()),
- e);
- }
- }
- for (String header : this.getPollHeadersField().getText().split("\n")) {
- if (header.trim().length() > 0) {
- String[] headerValue = header.split(":", 2);
- if (headerValue.length != 2) {
- throw new IllegalStateException(
- Constant.messages.getString(
- "authentication.panel.error.badpollheaders",
- getUISharedContext().getName()));
- }
- }
- }
- }
}
private void saveMethod() {
if (shownConfigPanel != null) shownConfigPanel.saveMethod();
-
- selectedAuthenticationMethod.setAuthCheckingStrategy(
- ((AuthCheckingStrategyType) getAuthenticationVerifComboBox().getSelectedItem())
- .getStrategy());
- selectedAuthenticationMethod.setPollUrl(this.getPollUrlField().getText());
- selectedAuthenticationMethod.setPollData(this.getPollDataField().getText());
- selectedAuthenticationMethod.setPollHeaders(this.getPollHeadersField().getText());
- selectedAuthenticationMethod.setPollFrequency(this.getPollFrequencySpinner().getValue());
- selectedAuthenticationMethod.setPollFrequencyUnits(
- ((AuthPollFrequencyUnitsType)
- this.getAuthFrequencyUnitsComboBox().getSelectedItem())
- .getUnits());
- selectedAuthenticationMethod.setLoggedInIndicatorPattern(
- getLoggedInIndicatorRegexField().getText());
- selectedAuthenticationMethod.setLoggedOutIndicatorPattern(
- getLoggedOutIndicatorRegexField().getText());
}
@Override
@@ -761,145 +374,25 @@ public void saveContextData(Session session) throws Exception {
saveMethod();
Context context = session.getContext(getContextId());
- // 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 getLoggedInIndicatorRegexField().getText();
- }
-
- @Override
- public void setLoggedInIndicatorPattern(String loggedInIndicatorPattern) {
- getLoggedInIndicatorRegexField().setText(loggedInIndicatorPattern);
- }
-
- @Override
- public void setLoggedInIndicatorEnabled(boolean enabled) {
- getLoggedInIndicatorRegexField().setEnabled(enabled);
- }
-
- @Override
- public void setLoggedInIndicatorToolTip(String toolTip) {
- getLoggedInIndicatorRegexField().setToolTipText(toolTip);
- }
-
- @Override
- public String getLoggedOutIndicatorPattern() {
- return getLoggedOutIndicatorRegexField().getText();
- }
-
- @Override
- public void setLoggedOutIndicatorPattern(String loggedOutIndicatorPattern) {
- getLoggedOutIndicatorRegexField().setText(loggedOutIndicatorPattern);
- }
-
- @Override
- public void setLoggedOutIndicatorEnabled(boolean enabled) {
- getLoggedOutIndicatorRegexField().setEnabled(enabled);
- }
- @Override
- public void setLoggedOutIndicatorToolTip(String toolTip) {
- getLoggedOutIndicatorRegexField().setToolTipText(toolTip);
- }
- }
-
- private static class AuthCheckingStrategyType {
- private AuthCheckingStrategy strategy;
-
- public AuthCheckingStrategyType(AuthCheckingStrategy strategy) {
- super();
- this.strategy = strategy;
- }
-
- public AuthCheckingStrategy getStrategy() {
- return strategy;
- }
-
- @Override
- public String toString() {
- return Constant.messages.getString(STRATEGY_PREFIX + strategy.name().toLowerCase());
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof AuthCheckingStrategyType)) {
- return false;
- }
- return this.strategy.equals(((AuthCheckingStrategyType) o).getStrategy());
- }
-
- @Override
- public int hashCode() {
- return this.strategy.hashCode();
- }
-
- public static List getAllValues() {
- List list = new ArrayList<>();
- for (AuthenticationMethod.AuthCheckingStrategy strategy :
- AuthenticationMethod.AuthCheckingStrategy.values()) {
- list.add(new AuthCheckingStrategyType(strategy));
- }
- return list;
- }
- }
-
- private static class AuthPollFrequencyUnitsType {
- private AuthPollFrequencyUnits units;
-
- public AuthPollFrequencyUnitsType(AuthPollFrequencyUnits units) {
- super();
- this.units = units;
- }
-
- public AuthPollFrequencyUnits getUnits() {
- return units;
- }
-
- @Override
- public String toString() {
- return Constant.messages.getString(FREQUENCY_UNITS_PREFIX + units.name().toLowerCase());
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof AuthPollFrequencyUnitsType)) {
- return false;
- }
- return this.units.equals(((AuthPollFrequencyUnitsType) o).getUnits());
- }
-
- @Override
- public int hashCode() {
- return this.units.hashCode();
- }
-
- public static List getAllValues() {
- List list = new ArrayList<>();
- for (AuthenticationMethod.AuthPollFrequencyUnits strategy :
- AuthenticationMethod.AuthPollFrequencyUnits.values()) {
- list.add(new AuthPollFrequencyUnitsType(strategy));
- }
- return list;
+ // Preserve the current verification method — it is owned by the verification panel.
+ // setAuthenticationMethod would replace the VM reference, so we restore it afterwards.
+ VerificationMethod currentVm = uiSharedContext.getVerificationMethod();
+ uiSharedContext.setAuthenticationMethod(selectedAuthenticationMethod);
+ if (currentVm != null) {
+ uiSharedContext.setVerificationMethod(currentVm);
}
}
}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/authentication/ContextVerificationPanel.java b/zap/src/main/java/org/zaproxy/zap/extension/authentication/ContextVerificationPanel.java
new file mode 100644
index 00000000000..e873816e16d
--- /dev/null
+++ b/zap/src/main/java/org/zaproxy/zap/extension/authentication/ContextVerificationPanel.java
@@ -0,0 +1,565 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2016 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.CardLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import java.util.stream.Stream;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFormattedTextField;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.border.EmptyBorder;
+import org.apache.commons.httpclient.URI;
+import org.parosproxy.paros.Constant;
+import org.parosproxy.paros.model.Model;
+import org.parosproxy.paros.model.Session;
+import org.parosproxy.paros.model.SiteNode;
+import org.parosproxy.paros.network.HttpRequestHeader;
+import org.parosproxy.paros.view.View;
+import org.zaproxy.zap.authentication.AuthenticationIndicatorsPanel;
+import org.zaproxy.zap.authentication.AuthenticationMethod;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
+import org.zaproxy.zap.authentication.VerificationMethod;
+import org.zaproxy.zap.model.Context;
+import org.zaproxy.zap.utils.ZapHtmlLabel;
+import org.zaproxy.zap.utils.ZapNumberSpinner;
+import org.zaproxy.zap.utils.ZapTextArea;
+import org.zaproxy.zap.utils.ZapTextField;
+import org.zaproxy.zap.view.AbstractContextPropertiesPanel;
+import org.zaproxy.zap.view.LayoutHelper;
+import org.zaproxy.zap.view.NodeSelectDialog;
+
+/**
+ * The Context Panel shown for configuring a Context's verification method.
+ *
+ * @since 2.18.0
+ */
+@SuppressWarnings("serial")
+public class ContextVerificationPanel extends AbstractContextPropertiesPanel
+ implements AuthenticationIndicatorsPanel {
+
+ private static final long serialVersionUID = 1L;
+
+ static final String PANEL_NAME = Constant.messages.getString("verification.panel.title");
+
+ private static final String LABEL_DESCRIPTION =
+ Constant.messages.getString("verification.panel.label.description");
+
+ 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 LABEL_POLL_METHOD =
+ Constant.messages.getString("authentication.panel.label.pollmethod");
+ private static final String LABEL_POLL_URL =
+ Constant.messages.getString("authentication.panel.label.pollurl");
+ private static final String LABEL_POLL_DATA =
+ Constant.messages.getString("authentication.panel.label.polldata");
+ private static final String LABEL_POLL_HEADERS =
+ Constant.messages.getString("authentication.panel.label.pollheaders");
+ private static final String LABEL_POLL_FREQUENCY =
+ Constant.messages.getString("authentication.panel.label.freq");
+ private static final String LABEL_STRATEGY =
+ Constant.messages.getString("authentication.panel.label.strategy");
+ private static final String STRATEGY_PREFIX = "authentication.panel.label.strategy.";
+ private static final String FREQUENCY_UNITS_PREFIX = "authentication.panel.label.units.";
+
+ private JComboBox authenticationVerifComboBox;
+ private JComboBox authFrequencyUnitsComboBox;
+
+ private JButton pollUrlSelectButton = null;
+ private JComboBox pollMethodComboBox;
+ private ZapTextField pollUrlField = null;
+ private ZapTextField pollDataField = null;
+ private ZapTextArea pollHeadersField = null;
+ private ZapNumberSpinner pollFrequency = null;
+
+ private ZapTextField loggedInIndicatorRegexField = null;
+ private ZapTextField loggedOutIndicatorRegexField = null;
+
+ public ContextVerificationPanel(Context context) {
+ super(context.getId());
+ initialize();
+ }
+
+ public static String buildName(int contextId) {
+ return contextId + ": " + PANEL_NAME;
+ }
+
+ private void initialize() {
+ this.setLayout(new CardLayout());
+ this.setName(buildName(getContextId()));
+ this.setBorder(new EmptyBorder(2, 2, 2, 2));
+
+ JPanel panel = new JPanel();
+ panel.setLayout(new GridBagLayout());
+ panel.setPreferredSize(new Dimension(400, 500));
+
+ JScrollPane scrollPanel = new JScrollPane();
+ scrollPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ scrollPanel.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ scrollPanel.setViewportView(panel);
+ this.add(scrollPanel);
+
+ int y = 0;
+ int fullWidth = 3;
+ panel.add(
+ new ZapHtmlLabel(LABEL_DESCRIPTION), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ panel.add(
+ new JLabel(LABEL_STRATEGY),
+ LayoutHelper.getGBC(0, y++, fullWidth, 1.0D, new Insets(20, 0, 5, 5)));
+ panel.add(getAuthenticationVerifComboBox(), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+
+ panel.add(
+ new JLabel(FIELD_LABEL_LOGGED_IN_INDICATOR),
+ LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ panel.add(getLoggedInIndicatorRegexField(), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ panel.add(
+ new JLabel(FIELD_LABEL_LOGGED_OUT_INDICATOR),
+ LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ panel.add(getLoggedOutIndicatorRegexField(), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+
+ panel.add(new JLabel(LABEL_POLL_FREQUENCY), LayoutHelper.getGBC(0, y, 1, 0.34D));
+ panel.add(getPollFrequencySpinner(), LayoutHelper.getGBC(1, y, 1, 0.33D));
+ panel.add(getAuthFrequencyUnitsComboBox(), LayoutHelper.getGBC(2, y++, 1, 0.33D));
+
+ panel.add(new JLabel(LABEL_POLL_METHOD), LayoutHelper.getGBC(0, y, 1, 0.25D));
+ panel.add(getPollMethodComboBox(), LayoutHelper.getGBC(1, y++, 2, 0.75D));
+ panel.add(new JLabel(LABEL_POLL_URL), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ JPanel urlPanel = new JPanel(new GridBagLayout());
+ urlPanel.add(this.getPollUrlField(), LayoutHelper.getGBC(0, 0, 1, 1.0D));
+ urlPanel.add(getPollUrlSelectButton(), LayoutHelper.getGBC(1, 0, 1, 0.0D));
+ panel.add(urlPanel, LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ panel.add(new JLabel(LABEL_POLL_DATA), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ panel.add(this.getPollDataField(), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ panel.add(new JLabel(LABEL_POLL_HEADERS), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+ panel.add(this.getPollHeadersField(), LayoutHelper.getGBC(0, y++, fullWidth, 1.0D));
+
+ // Padding
+ panel.add(new JLabel(), LayoutHelper.getGBC(0, 99, 1, 1.0D, 1.0D));
+ }
+
+ private JComboBox getAuthenticationVerifComboBox() {
+ if (authenticationVerifComboBox == null) {
+ authenticationVerifComboBox = new JComboBox<>();
+ for (AuthCheckingStrategyType acst : AuthCheckingStrategyType.getAllValues()) {
+ authenticationVerifComboBox.addItem(acst);
+ }
+ authenticationVerifComboBox.addItemListener(
+ new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ setPollFieldStatuses((AuthCheckingStrategyType) e.getItem());
+ }
+ }
+ });
+ }
+ return authenticationVerifComboBox;
+ }
+
+ void setPollFieldStatuses(AuthCheckingStrategyType type) {
+ boolean isPoll = type.getStrategy().equals(AuthCheckingStrategy.POLL_URL);
+ getAuthFrequencyUnitsComboBox().setEnabled(isPoll);
+ getPollFrequencySpinner().setEnabled(isPoll);
+ getPollUrlSelectButton().setEnabled(isPoll);
+ getPollMethodComboBox().setEnabled(isPoll);
+ getPollUrlField().setEnabled(isPoll);
+ getPollDataField().setEnabled(isPoll);
+ getPollHeadersField().setEnabled(isPoll);
+ boolean isAutoDetect = type.getStrategy().equals(AuthCheckingStrategy.AUTO_DETECT);
+ getLoggedInIndicatorRegexField().setEnabled(!isAutoDetect);
+ getLoggedOutIndicatorRegexField().setEnabled(!isAutoDetect);
+ }
+
+ private JComboBox getAuthFrequencyUnitsComboBox() {
+ if (authFrequencyUnitsComboBox == null) {
+ authFrequencyUnitsComboBox = new JComboBox<>();
+ for (AuthPollFrequencyUnitsType acst : AuthPollFrequencyUnitsType.getAllValues()) {
+ authFrequencyUnitsComboBox.addItem(acst);
+ }
+ }
+ return authFrequencyUnitsComboBox;
+ }
+
+ private ZapNumberSpinner getPollFrequencySpinner() {
+ if (pollFrequency == null) {
+ pollFrequency =
+ new ZapNumberSpinner(
+ 1, VerificationMethod.DEFAULT_POLL_FREQUENCY, Integer.MAX_VALUE);
+ Component mySpinnerEditor = pollFrequency.getEditor();
+ JFormattedTextField jftf = ((JSpinner.DefaultEditor) mySpinnerEditor).getTextField();
+ jftf.setColumns(6);
+ }
+ return pollFrequency;
+ }
+
+ private JButton getPollUrlSelectButton() {
+ if (pollUrlSelectButton == null) {
+ pollUrlSelectButton = new JButton(Constant.messages.getString("all.button.select"));
+ pollUrlSelectButton.setIcon(
+ new ImageIcon(
+ View.class.getResource("/resource/icon/16/094.png"))); // Globe Icon
+ pollUrlSelectButton.addActionListener(
+ new java.awt.event.ActionListener() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ NodeSelectDialog nsd =
+ new NodeSelectDialog(View.getSingleton().getMainFrame());
+ SiteNode node = null;
+ if (getPollUrlField().getText().trim().length() > 0)
+ try {
+ if (getPollDataField().getText().trim().length() > 0)
+ node =
+ Model.getSingleton()
+ .getSession()
+ .getSiteTree()
+ .findNode(
+ new URI(
+ getPollUrlField().getText(),
+ false),
+ HttpRequestHeader.POST,
+ getPollDataField().getText());
+ else
+ node =
+ Model.getSingleton()
+ .getSession()
+ .getSiteTree()
+ .findNode(
+ new URI(
+ getPollUrlField().getText(),
+ false));
+ } catch (Exception e2) {
+ // Could not find node for existing value, no harm done
+ }
+
+ node = nsd.showDialog(node);
+ if (node != null && node.getHistoryReference() != null) {
+ try {
+ getPollUrlField()
+ .setText(
+ node.getHistoryReference().getURI().toString());
+ getPollDataField()
+ .setText(
+ node.getHistoryReference()
+ .getHttpMessage()
+ .getRequestBody()
+ .toString());
+ } catch (Exception e1) {
+ // Ignore
+ }
+ }
+ }
+ });
+ }
+ return pollUrlSelectButton;
+ }
+
+ private JComboBox getPollMethodComboBox() {
+ if (pollMethodComboBox == null) {
+ pollMethodComboBox = new JComboBox<>();
+ pollMethodComboBox.setEditable(true);
+ Stream.of(HttpRequestHeader.GET, HttpRequestHeader.POST)
+ .sorted()
+ .forEach(pollMethodComboBox::addItem);
+ }
+ return pollMethodComboBox;
+ }
+
+ private ZapTextField getPollUrlField() {
+ if (pollUrlField == null) {
+ pollUrlField = new ZapTextField();
+ }
+ return pollUrlField;
+ }
+
+ private ZapTextField getPollDataField() {
+ if (pollDataField == null) {
+ pollDataField = new ZapTextField();
+ }
+ return pollDataField;
+ }
+
+ private ZapTextArea getPollHeadersField() {
+ if (pollHeadersField == null) {
+ pollHeadersField = new ZapTextArea(2, 0);
+ }
+ return pollHeadersField;
+ }
+
+ private ZapTextField getLoggedInIndicatorRegexField() {
+ if (loggedInIndicatorRegexField == null) loggedInIndicatorRegexField = new ZapTextField();
+ return loggedInIndicatorRegexField;
+ }
+
+ private ZapTextField getLoggedOutIndicatorRegexField() {
+ if (loggedOutIndicatorRegexField == null) loggedOutIndicatorRegexField = new ZapTextField();
+ return loggedOutIndicatorRegexField;
+ }
+
+ @Override
+ public String getLoggedInIndicatorPattern() {
+ return getLoggedInIndicatorRegexField().getText();
+ }
+
+ @Override
+ public void setLoggedInIndicatorPattern(String loggedInIndicatorPattern) {
+ getLoggedInIndicatorRegexField().setText(loggedInIndicatorPattern);
+ }
+
+ @Override
+ public void setLoggedInIndicatorEnabled(boolean enabled) {
+ getLoggedInIndicatorRegexField().setEnabled(enabled);
+ }
+
+ @Override
+ public void setLoggedInIndicatorToolTip(String toolTip) {
+ getLoggedInIndicatorRegexField().setToolTipText(toolTip);
+ }
+
+ @Override
+ public String getLoggedOutIndicatorPattern() {
+ return getLoggedOutIndicatorRegexField().getText();
+ }
+
+ @Override
+ public void setLoggedOutIndicatorPattern(String loggedOutIndicatorPattern) {
+ getLoggedOutIndicatorRegexField().setText(loggedOutIndicatorPattern);
+ }
+
+ @Override
+ public void setLoggedOutIndicatorEnabled(boolean enabled) {
+ getLoggedOutIndicatorRegexField().setEnabled(enabled);
+ }
+
+ @Override
+ public void setLoggedOutIndicatorToolTip(String toolTip) {
+ getLoggedOutIndicatorRegexField().setToolTipText(toolTip);
+ }
+
+ /** Builds a VerificationMethod from the current UI state. */
+ VerificationMethod buildVerificationMethodFromUI() {
+ VerificationMethod vm = new VerificationMethod();
+ vm.setAuthCheckingStrategy(
+ ((AuthCheckingStrategyType) getAuthenticationVerifComboBox().getSelectedItem())
+ .getStrategy());
+ vm.setPollMethod((String) getPollMethodComboBox().getSelectedItem());
+ vm.setPollUrl(getPollUrlField().getText());
+ vm.setPollData(getPollDataField().getText());
+ vm.setPollHeaders(getPollHeadersField().getText());
+ vm.setPollFrequency(getPollFrequencySpinner().getValue());
+ vm.setPollFrequencyUnits(
+ ((AuthPollFrequencyUnitsType) getAuthFrequencyUnitsComboBox().getSelectedItem())
+ .getUnits());
+ vm.setLoggedInIndicatorPattern(getLoggedInIndicatorRegexField().getText());
+ vm.setLoggedOutIndicatorPattern(getLoggedOutIndicatorRegexField().getText());
+ return vm;
+ }
+
+ private void populateUIFromVm(VerificationMethod vm) {
+ if (vm.getAuthCheckingStrategy() != null) {
+ getAuthenticationVerifComboBox()
+ .getModel()
+ .setSelectedItem(new AuthCheckingStrategyType(vm.getAuthCheckingStrategy()));
+ }
+ setPollFieldStatuses(
+ (AuthCheckingStrategyType) getAuthenticationVerifComboBox().getSelectedItem());
+
+ String pollMethod = vm.getPollMethod();
+ getPollMethodComboBox().setSelectedItem(pollMethod != null ? pollMethod : "");
+ getPollUrlField().setText(vm.getPollUrl());
+ getPollDataField().setText(vm.getPollData());
+ getPollHeadersField().setText(vm.getPollHeaders());
+ getPollFrequencySpinner().setValue(vm.getPollFrequency());
+ getAuthFrequencyUnitsComboBox()
+ .setSelectedItem(new AuthPollFrequencyUnitsType(vm.getPollFrequencyUnits()));
+
+ if (vm.getLoggedInIndicatorPattern() != null)
+ getLoggedInIndicatorRegexField().setText(vm.getLoggedInIndicatorPattern().pattern());
+ else getLoggedInIndicatorRegexField().setText("");
+ if (vm.getLoggedOutIndicatorPattern() != null)
+ getLoggedOutIndicatorRegexField().setText(vm.getLoggedOutIndicatorPattern().pattern());
+ else getLoggedOutIndicatorRegexField().setText("");
+ }
+
+ @Override
+ public String getHelpIndex() {
+ return "ui.dialogs.context-auth";
+ }
+
+ @Override
+ public void initContextData(Session session, Context uiSharedContext) {
+ VerificationMethod vm = uiSharedContext.getVerificationMethod();
+ if (vm != null) {
+ populateUIFromVm(vm);
+ }
+ }
+
+ @Override
+ public void validateContextData(Session session) throws Exception {
+ try {
+ Pattern.compile(getLoggedInIndicatorRegexField().getText());
+ Pattern.compile(getLoggedOutIndicatorRegexField().getText());
+ } catch (PatternSyntaxException e) {
+ throw new IllegalStateException(
+ Constant.messages.getString(
+ "authentication.panel.error.illegalPattern",
+ getUISharedContext().getName()),
+ e);
+ }
+ if (((AuthCheckingStrategyType) getAuthenticationVerifComboBox().getSelectedItem())
+ .getStrategy()
+ .equals(AuthCheckingStrategy.POLL_URL)) {
+ String url = getPollUrlField().getText();
+ if (url.length() == 0) {
+ throw new IllegalStateException(
+ Constant.messages.getString(
+ "authentication.panel.error.nopollurl",
+ getUISharedContext().getName()));
+ } else {
+ try {
+ new URI(url, true);
+ } catch (Exception e) {
+ throw new IllegalStateException(
+ Constant.messages.getString(
+ "authentication.panel.error.badpollurl",
+ getUISharedContext().getName()),
+ e);
+ }
+ }
+ for (String header : getPollHeadersField().getText().split("\n")) {
+ if (header.trim().length() > 0) {
+ String[] headerValue = header.split(":", 2);
+ if (headerValue.length != 2) {
+ throw new IllegalStateException(
+ Constant.messages.getString(
+ "authentication.panel.error.badpollheaders",
+ getUISharedContext().getName()));
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void saveTemporaryContextData(Context uiSharedContext) {
+ VerificationMethod vm = buildVerificationMethodFromUI();
+ uiSharedContext.setVerificationMethod(vm);
+ }
+
+ @Override
+ public void saveContextData(Session session) throws Exception {
+ VerificationMethod vm = buildVerificationMethodFromUI();
+ session.getContext(getContextId()).setVerificationMethod(vm);
+ }
+
+ static class AuthCheckingStrategyType {
+ private AuthCheckingStrategy strategy;
+
+ public AuthCheckingStrategyType(AuthCheckingStrategy strategy) {
+ this.strategy = strategy;
+ }
+
+ public AuthCheckingStrategy getStrategy() {
+ return strategy;
+ }
+
+ @Override
+ public String toString() {
+ return Constant.messages.getString(STRATEGY_PREFIX + strategy.name().toLowerCase());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof AuthCheckingStrategyType)) {
+ return false;
+ }
+ return this.strategy.equals(((AuthCheckingStrategyType) o).getStrategy());
+ }
+
+ @Override
+ public int hashCode() {
+ return this.strategy.hashCode();
+ }
+
+ public static List getAllValues() {
+ List list = new ArrayList<>();
+ for (AuthenticationMethod.AuthCheckingStrategy strategy :
+ AuthenticationMethod.AuthCheckingStrategy.values()) {
+ list.add(new AuthCheckingStrategyType(strategy));
+ }
+ return list;
+ }
+ }
+
+ static class AuthPollFrequencyUnitsType {
+ private AuthPollFrequencyUnits units;
+
+ public AuthPollFrequencyUnitsType(AuthPollFrequencyUnits units) {
+ this.units = units;
+ }
+
+ public AuthPollFrequencyUnits getUnits() {
+ return units;
+ }
+
+ @Override
+ public String toString() {
+ return Constant.messages.getString(FREQUENCY_UNITS_PREFIX + units.name().toLowerCase());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof AuthPollFrequencyUnitsType)) {
+ return false;
+ }
+ return this.units.equals(((AuthPollFrequencyUnitsType) o).getUnits());
+ }
+
+ @Override
+ public int hashCode() {
+ return this.units.hashCode();
+ }
+
+ public static List getAllValues() {
+ List list = new ArrayList<>();
+ for (AuthenticationMethod.AuthPollFrequencyUnits strategy :
+ AuthenticationMethod.AuthPollFrequencyUnits.values()) {
+ list.add(new AuthPollFrequencyUnitsType(strategy));
+ }
+ return list;
+ }
+ }
+}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/authentication/ExtensionAuthentication.java b/zap/src/main/java/org/zaproxy/zap/extension/authentication/ExtensionAuthentication.java
index c6d11d458f9..5fe4c61b0d4 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/authentication/ExtensionAuthentication.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/authentication/ExtensionAuthentication.java
@@ -47,6 +47,7 @@
import org.zaproxy.zap.authentication.JsonBasedAuthenticationMethodType;
import org.zaproxy.zap.authentication.ManualAuthenticationMethodType;
import org.zaproxy.zap.authentication.ScriptBasedAuthenticationMethodType;
+import org.zaproxy.zap.authentication.VerificationMethod;
import org.zaproxy.zap.extension.stdmenus.PopupContextMenuItemFactory;
import org.zaproxy.zap.model.Context;
import org.zaproxy.zap.model.ContextDataFactory;
@@ -74,6 +75,8 @@ public class ExtensionAuthentication extends ExtensionAdaptor
/** The context panels map. */
private Map contextPanelsMap = new HashMap<>();
+ private Map verificationPanelsMap = new HashMap<>();
+
private PopupContextMenuItemFactory popupFlagLoggedInIndicatorMenuFactory;
private PopupContextMenuItemFactory popupFlagLoggedOutIndicatorMenuFactory;
@@ -113,8 +116,29 @@ public void hook(ExtensionHook extensionHook) {
extensionHook.getHookMenu().addPopupMenuItem(getPopupFlagLoggedInIndicatorMenu());
extensionHook.getHookMenu().addPopupMenuItem(getPopupFlagLoggedOutIndicatorMenu());
- // Factory for generating Session Context UserAuth panels
+ // Factory for generating Session Context Authentication panels
extensionHook.getHookView().addContextPanelFactory(this);
+ // Factory for generating Session Context Verification panels
+ extensionHook
+ .getHookView()
+ .addContextPanelFactory(
+ new ContextPanelFactory() {
+ @Override
+ public AbstractContextPropertiesPanel getContextPanel(
+ Context context) {
+ return getVerificationPanel(context);
+ }
+
+ @Override
+ public void discardContext(Context ctx) {
+ verificationPanelsMap.remove(ctx.getId());
+ }
+
+ @Override
+ public void discardContexts() {
+ verificationPanelsMap.clear();
+ }
+ });
}
// Load the Authentication and Session Management methods
@@ -123,6 +147,7 @@ public void hook(ExtensionHook extensionHook) {
// Register the api
this.api = new AuthenticationAPI(this);
extensionHook.addApiImplementor(api);
+ extensionHook.addApiImplementor(new VerificationAPI());
extensionHook.addHttpSenderListener(getHttpSenderAuthHeaderListener());
}
@@ -131,12 +156,22 @@ public void hook(ExtensionHook extensionHook) {
public AbstractContextPropertiesPanel getContextPanel(Context context) {
ContextAuthenticationPanel panel = this.contextPanelsMap.get(context.getId());
if (panel == null) {
- panel = new ContextAuthenticationPanel(this, context);
+ ContextVerificationPanel verifPanel = getVerificationPanel(context);
+ panel = new ContextAuthenticationPanel(this, context, verifPanel);
this.contextPanelsMap.put(context.getId(), panel);
}
return panel;
}
+ private ContextVerificationPanel getVerificationPanel(Context context) {
+ ContextVerificationPanel panel = this.verificationPanelsMap.get(context.getId());
+ if (panel == null) {
+ panel = new ContextVerificationPanel(context);
+ this.verificationPanelsMap.put(context.getId(), panel);
+ }
+ return panel;
+ }
+
@Override
public String getAuthor() {
return Constant.ZAP_TEAM;
@@ -260,71 +295,61 @@ public void loadContextData(Session session, Context context) {
context.setAuthenticationMethod(
t.loadMethodFromSession(session, context.getId()));
+ VerificationMethod vm = context.getVerificationMethod();
+
String strategy =
session.getContextDataString(
context.getId(), RecordContext.TYPE_AUTH_VERIF_STRATEGY, null);
if (strategy != null) {
try {
- context.getAuthenticationMethod()
- .setAuthCheckingStrategy(
- AuthCheckingStrategy.valueOf(strategy));
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.valueOf(strategy));
} catch (Exception e) {
LOGGER.error("Failed to parse auth checking strategy {}", strategy, e);
}
}
- context.getAuthenticationMethod()
- .setPollUrl(
- session.getContextDataString(
- context.getId(),
- RecordContext.TYPE_AUTH_POLL_URL,
- null));
-
- context.getAuthenticationMethod()
- .setPollData(
- session.getContextDataString(
- context.getId(),
- RecordContext.TYPE_AUTH_POLL_DATA,
- null));
-
- context.getAuthenticationMethod()
- .setPollHeaders(
- session.getContextDataString(
- context.getId(),
- RecordContext.TYPE_AUTH_POLL_HEADERS,
- null));
-
- context.getAuthenticationMethod()
- .setPollFrequency(
- session.getContextDataInteger(
- context.getId(), RecordContext.TYPE_AUTH_POLL_FREQ, 0));
+ vm.setPollUrl(
+ session.getContextDataString(
+ context.getId(), RecordContext.TYPE_AUTH_POLL_URL, null));
+
+ vm.setPollData(
+ session.getContextDataString(
+ context.getId(), RecordContext.TYPE_AUTH_POLL_DATA, null));
+
+ vm.setPollHeaders(
+ session.getContextDataString(
+ context.getId(), RecordContext.TYPE_AUTH_POLL_HEADERS, null));
+
+ vm.setPollFrequency(
+ session.getContextDataInteger(
+ context.getId(), RecordContext.TYPE_AUTH_POLL_FREQ, 0));
String freqUnits =
session.getContextDataString(
context.getId(), RecordContext.TYPE_AUTH_POLL_FREQ_UNITS, null);
if (freqUnits != null) {
try {
- context.getAuthenticationMethod()
- .setPollFrequencyUnits(
- AuthPollFrequencyUnits.valueOf(freqUnits));
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.valueOf(freqUnits));
} catch (Exception e) {
LOGGER.error("Failed to parse auth frequency units {}", freqUnits, e);
}
}
- context.getAuthenticationMethod()
- .setLoggedInIndicatorPattern(
- session.getContextDataString(
- context.getId(),
- RecordContext.TYPE_AUTH_METHOD_LOGGEDIN_INDICATOR,
- null));
-
- context.getAuthenticationMethod()
- .setLoggedOutIndicatorPattern(
- session.getContextDataString(
- context.getId(),
- RecordContext.TYPE_AUTH_METHOD_LOGGEDOUT_INDICATOR,
- null));
+ vm.setPollMethod(
+ session.getContextDataString(
+ context.getId(), RecordContext.TYPE_AUTH_POLL_METHOD, null));
+
+ vm.setLoggedInIndicatorPattern(
+ session.getContextDataString(
+ context.getId(),
+ RecordContext.TYPE_AUTH_METHOD_LOGGEDIN_INDICATOR,
+ null));
+
+ vm.setLoggedOutIndicatorPattern(
+ session.getContextDataString(
+ context.getId(),
+ RecordContext.TYPE_AUTH_METHOD_LOGGEDOUT_INDICATOR,
+ null));
}
}
@@ -343,64 +368,67 @@ public void persistContextData(Session session, Context context) {
RecordContext.TYPE_AUTH_METHOD_TYPE,
Integer.toString(t.getUniqueIdentifier()));
- if (context.getAuthenticationMethod().getAuthCheckingStrategy() != null) {
+ VerificationMethod vm = context.getVerificationMethod();
+
+ if (vm.getAuthCheckingStrategy() != null) {
session.setContextData(
contextIdx,
RecordContext.TYPE_AUTH_VERIF_STRATEGY,
- context.getAuthenticationMethod().getAuthCheckingStrategy().name());
+ vm.getAuthCheckingStrategy().name());
} else {
session.clearContextDataForType(contextIdx, RecordContext.TYPE_AUTH_VERIF_STRATEGY);
}
- if (context.getAuthenticationMethod().getPollUrl() != null) {
+ if (vm.getPollUrl() != null) {
session.setContextData(
- contextIdx,
- RecordContext.TYPE_AUTH_POLL_URL,
- context.getAuthenticationMethod().getPollUrl());
+ contextIdx, RecordContext.TYPE_AUTH_POLL_URL, vm.getPollUrl());
} else {
session.clearContextDataForType(contextIdx, RecordContext.TYPE_AUTH_POLL_URL);
}
- if (context.getAuthenticationMethod().getPollData() != null) {
+ if (vm.getPollData() != null) {
session.setContextData(
- contextIdx,
- RecordContext.TYPE_AUTH_POLL_DATA,
- context.getAuthenticationMethod().getPollData());
+ contextIdx, RecordContext.TYPE_AUTH_POLL_DATA, vm.getPollData());
} else {
session.clearContextDataForType(contextIdx, RecordContext.TYPE_AUTH_POLL_DATA);
}
- if (context.getAuthenticationMethod().getPollHeaders() != null) {
+ if (vm.getPollHeaders() != null) {
session.setContextData(
- contextIdx,
- RecordContext.TYPE_AUTH_POLL_HEADERS,
- context.getAuthenticationMethod().getPollHeaders());
+ contextIdx, RecordContext.TYPE_AUTH_POLL_HEADERS, vm.getPollHeaders());
} else {
session.clearContextDataForType(contextIdx, RecordContext.TYPE_AUTH_POLL_HEADERS);
}
session.setContextData(
contextIdx,
RecordContext.TYPE_AUTH_POLL_FREQ,
- Integer.toString(context.getAuthenticationMethod().getPollFrequency()));
+ Integer.toString(vm.getPollFrequency()));
- if (context.getAuthenticationMethod().getPollFrequencyUnits() != null) {
+ if (vm.getPollFrequencyUnits() != null) {
session.setContextData(
contextIdx,
RecordContext.TYPE_AUTH_POLL_FREQ_UNITS,
- context.getAuthenticationMethod().getPollFrequencyUnits().name());
+ vm.getPollFrequencyUnits().name());
} else {
- session.clearContextDataForType(contextIdx, RecordContext.TYPE_AUTH_VERIF_STRATEGY);
+ session.clearContextDataForType(
+ contextIdx, RecordContext.TYPE_AUTH_POLL_FREQ_UNITS);
+ }
+ if (vm.getPollMethod() != null) {
+ session.setContextData(
+ contextIdx, RecordContext.TYPE_AUTH_POLL_METHOD, vm.getPollMethod());
+ } else {
+ session.clearContextDataForType(contextIdx, RecordContext.TYPE_AUTH_POLL_METHOD);
}
persistLoggedIndicator(
session,
contextIdx,
RecordContext.TYPE_AUTH_METHOD_LOGGEDIN_INDICATOR,
- context.getAuthenticationMethod().getLoggedInIndicatorPattern());
+ vm.getLoggedInIndicatorPattern());
persistLoggedIndicator(
session,
contextIdx,
RecordContext.TYPE_AUTH_METHOD_LOGGEDOUT_INDICATOR,
- context.getAuthenticationMethod().getLoggedOutIndicatorPattern());
+ vm.getLoggedOutIndicatorPattern());
t.persistMethodToSession(session, contextIdx, context.getAuthenticationMethod());
} catch (DatabaseException e) {
@@ -421,11 +449,13 @@ private static void persistLoggedIndicator(
@Override
public void discardContexts() {
contextPanelsMap.clear();
+ verificationPanelsMap.clear();
}
@Override
public void discardContext(Context ctx) {
contextPanelsMap.remove(ctx.getId());
+ verificationPanelsMap.remove(ctx.getId());
}
@Override
@@ -433,37 +463,35 @@ public void exportContextData(Context ctx, Configuration config) {
config.setProperty(
AuthenticationMethod.CONTEXT_CONFIG_AUTH_TYPE,
ctx.getAuthenticationMethod().getType().getUniqueIdentifier());
- if (ctx.getAuthenticationMethod().getAuthCheckingStrategy() != null) {
+
+ VerificationMethod vm = ctx.getVerificationMethod();
+ if (vm.getAuthCheckingStrategy() != null) {
config.setProperty(
AuthenticationMethod.CONTEXT_CONFIG_AUTH_STRATEGY,
- ctx.getAuthenticationMethod().getAuthCheckingStrategy().name());
+ vm.getAuthCheckingStrategy().name());
}
+ config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_URL, vm.getPollUrl());
+ config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_DATA, vm.getPollData());
config.setProperty(
- AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_URL,
- ctx.getAuthenticationMethod().getPollUrl());
+ AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_HEADERS, vm.getPollHeaders());
config.setProperty(
- AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_DATA,
- ctx.getAuthenticationMethod().getPollData());
- config.setProperty(
- AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_HEADERS,
- ctx.getAuthenticationMethod().getPollHeaders());
- config.setProperty(
- AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_FREQ,
- ctx.getAuthenticationMethod().getPollFrequency());
- if (ctx.getAuthenticationMethod().getPollFrequencyUnits() != null) {
+ AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_FREQ, vm.getPollFrequency());
+ if (vm.getPollFrequencyUnits() != null) {
config.setProperty(
AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_UNITS,
- ctx.getAuthenticationMethod().getPollFrequencyUnits().name());
+ vm.getPollFrequencyUnits().name());
}
- if (ctx.getAuthenticationMethod().getLoggedInIndicatorPattern() != null) {
+ config.setProperty(
+ AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_METHOD, vm.getPollMethod());
+ if (vm.getLoggedInIndicatorPattern() != null) {
config.setProperty(
AuthenticationMethod.CONTEXT_CONFIG_AUTH_LOGGEDIN,
- ctx.getAuthenticationMethod().getLoggedInIndicatorPattern().toString());
+ vm.getLoggedInIndicatorPattern().toString());
}
- if (ctx.getAuthenticationMethod().getLoggedOutIndicatorPattern() != null) {
+ if (vm.getLoggedOutIndicatorPattern() != null) {
config.setProperty(
AuthenticationMethod.CONTEXT_CONFIG_AUTH_LOGGEDOUT,
- ctx.getAuthenticationMethod().getLoggedOutIndicatorPattern().toString());
+ vm.getLoggedOutIndicatorPattern().toString());
}
ctx.getAuthenticationMethod().getType().exportData(config, ctx.getAuthenticationMethod());
}
@@ -481,37 +509,48 @@ public void importContextData(Context ctx, Configuration config) throws Configur
return;
}
ctx.setAuthenticationMethod(authMethodType.createAuthenticationMethod(ctx.getId()));
- AuthenticationMethod method = ctx.getAuthenticationMethod();
- AuthCheckingStrategy strategy =
- AuthCheckingStrategy.valueOf(
- config.getString(
- AuthenticationMethod.CONTEXT_CONFIG_AUTH_STRATEGY,
- AuthCheckingStrategy.EACH_RESP.name()));
- method.setAuthCheckingStrategy(strategy);
-
- method.setPollUrl(config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_URL, ""));
- method.setPollData(
- config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_DATA, ""));
- method.setPollHeaders(
+ VerificationMethod vm = ctx.getVerificationMethod();
+
+ String pollUrl = config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_URL, "");
+ String strategyName =
+ config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_STRATEGY, null);
+ AuthCheckingStrategy strategy;
+ if (strategyName != null && !strategyName.isBlank()) {
+ strategy = AuthCheckingStrategy.valueOf(strategyName);
+ } else {
+ // Older context files had no strategy field; indicator-only configs checked each
+ // response. Prefer POLL_URL only when a poll URL is present.
+ strategy =
+ pollUrl != null && !pollUrl.isBlank()
+ ? AuthCheckingStrategy.POLL_URL
+ : AuthCheckingStrategy.EACH_RESP;
+ }
+ vm.setAuthCheckingStrategy(strategy);
+
+ vm.setPollMethod(
+ config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_METHOD, null));
+ vm.setPollUrl(pollUrl);
+ vm.setPollData(config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_DATA, ""));
+ vm.setPollHeaders(
config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_HEADERS, ""));
- method.setPollFrequency(
+ vm.setPollFrequency(
config.getInt(
AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_FREQ,
- AuthenticationMethod.DEFAULT_POLL_FREQUENCY));
+ VerificationMethod.DEFAULT_POLL_FREQUENCY));
AuthPollFrequencyUnits units =
AuthPollFrequencyUnits.valueOf(
config.getString(
AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_UNITS,
- AuthPollFrequencyUnits.REQUESTS.name()));
- method.setPollFrequencyUnits(units);
+ AuthPollFrequencyUnits.SECONDS.name()));
+ vm.setPollFrequencyUnits(units);
- method.setLoggedInIndicatorPattern(
+ vm.setLoggedInIndicatorPattern(
config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_LOGGEDIN, ""));
- method.setLoggedOutIndicatorPattern(
+ vm.setLoggedOutIndicatorPattern(
config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_LOGGEDOUT, ""));
- method.getType().importData(config, method);
+ ctx.getAuthenticationMethod().getType().importData(config, ctx.getAuthenticationMethod());
}
private HttpSenderAuthHeaderListener getHttpSenderAuthHeaderListener() {
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/authentication/PopupFlagLoggedInIndicatorMenu.java b/zap/src/main/java/org/zaproxy/zap/extension/authentication/PopupFlagLoggedInIndicatorMenu.java
index a1daf30235d..6d905b2e569 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/authentication/PopupFlagLoggedInIndicatorMenu.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/authentication/PopupFlagLoggedInIndicatorMenu.java
@@ -63,7 +63,7 @@ public void performAction() {
Context uiSharedContext = sessionDialog.getUISharedContext(this.contextId);
uiSharedContext
- .getAuthenticationMethod()
+ .getVerificationMethod()
.setLoggedInIndicatorPattern(Pattern.quote(getSelectedText()));
// Show the session dialog without recreating UI Shared contexts
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/authentication/PopupFlagLoggedOutIndicatorMenu.java b/zap/src/main/java/org/zaproxy/zap/extension/authentication/PopupFlagLoggedOutIndicatorMenu.java
index 0f24713e0d3..99564d6e745 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/authentication/PopupFlagLoggedOutIndicatorMenu.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/authentication/PopupFlagLoggedOutIndicatorMenu.java
@@ -63,7 +63,7 @@ public void performAction() {
Context uiSharedContext = sessionDialog.getUISharedContext(this.contextId);
uiSharedContext
- .getAuthenticationMethod()
+ .getVerificationMethod()
.setLoggedOutIndicatorPattern(Pattern.quote(getSelectedText()));
// Show the session dialog without recreating UI Shared contexts
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/authentication/VerificationAPI.java b/zap/src/main/java/org/zaproxy/zap/extension/authentication/VerificationAPI.java
new file mode 100644
index 00000000000..75f4e40ad27
--- /dev/null
+++ b/zap/src/main/java/org/zaproxy/zap/extension/authentication/VerificationAPI.java
@@ -0,0 +1,265 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2026 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.io.IOException;
+import java.util.regex.Pattern;
+import net.sf.json.JSONObject;
+import org.apache.commons.httpclient.URI;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.parosproxy.paros.control.Control;
+import org.parosproxy.paros.model.Model;
+import org.parosproxy.paros.network.HttpMessage;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
+import org.zaproxy.zap.authentication.VerificationMethod;
+import org.zaproxy.zap.extension.api.ApiAction;
+import org.zaproxy.zap.extension.api.ApiException;
+import org.zaproxy.zap.extension.api.ApiException.Type;
+import org.zaproxy.zap.extension.api.ApiImplementor;
+import org.zaproxy.zap.extension.api.ApiResponse;
+import org.zaproxy.zap.extension.api.ApiResponseConversionUtils;
+import org.zaproxy.zap.extension.api.ApiResponseElement;
+import org.zaproxy.zap.extension.api.ApiResponseSet;
+import org.zaproxy.zap.extension.api.ApiView;
+import org.zaproxy.zap.extension.users.ExtensionUserManagement;
+import org.zaproxy.zap.model.Context;
+import org.zaproxy.zap.users.User;
+import org.zaproxy.zap.utils.ApiUtils;
+
+/**
+ * The API for manipulating the {@link org.zaproxy.zap.authentication.VerificationMethod
+ * VerificationMethod} for {@link Context Contexts}.
+ */
+public class VerificationAPI extends ApiImplementor {
+
+ private static final Logger LOGGER = LogManager.getLogger(VerificationAPI.class);
+
+ private static final String PREFIX = "verification";
+
+ private static final String VIEW_GET_LOGGED_IN_INDICATOR = "getLoggedInIndicator";
+ private static final String VIEW_GET_LOGGED_OUT_INDICATOR = "getLoggedOutIndicator";
+
+ private static final String ACTION_SET_LOGGED_IN_INDICATOR = "setLoggedInIndicator";
+ private static final String ACTION_SET_LOGGED_OUT_INDICATOR = "setLoggedOutIndicator";
+ private static final String ACTION_SET_VERIFICATION_METHOD = "setVerificationMethod";
+ private static final String ACTION_POLL_AS_USER = "pollAsUser";
+
+ public static final String PARAM_CONTEXT_ID = "contextId";
+ public static final String PARAM_USER_ID = "userId";
+ private static final String PARAM_LOGGED_IN_INDICATOR = "loggedInIndicatorRegex";
+ private static final String PARAM_LOGGED_OUT_INDICATOR = "loggedOutIndicatorRegex";
+ private static final String PARAM_CHECKING_STRATEGY = "checkingStrategy";
+ private static final String PARAM_POLL_URL = "pollUrl";
+ private static final String PARAM_POLL_DATA = "pollData";
+ private static final String PARAM_POLL_HEADERS = "pollHeaders";
+ private static final String PARAM_POLL_FREQ = "pollFrequency";
+ private static final String PARAM_POLL_FREQ_UNITS = "pollFrequencyUnits";
+
+ public VerificationAPI() {
+ super();
+
+ this.addApiView(new ApiView(VIEW_GET_LOGGED_IN_INDICATOR, new String[] {PARAM_CONTEXT_ID}));
+ this.addApiView(
+ new ApiView(VIEW_GET_LOGGED_OUT_INDICATOR, new String[] {PARAM_CONTEXT_ID}));
+
+ this.addApiAction(
+ new ApiAction(
+ ACTION_SET_LOGGED_IN_INDICATOR,
+ new String[] {PARAM_CONTEXT_ID, PARAM_LOGGED_IN_INDICATOR}));
+ this.addApiAction(
+ new ApiAction(
+ ACTION_SET_LOGGED_OUT_INDICATOR,
+ new String[] {PARAM_CONTEXT_ID, PARAM_LOGGED_OUT_INDICATOR}));
+ this.addApiAction(
+ new ApiAction(
+ ACTION_SET_VERIFICATION_METHOD,
+ new String[] {PARAM_CONTEXT_ID, PARAM_CHECKING_STRATEGY},
+ new String[] {
+ PARAM_POLL_URL,
+ PARAM_POLL_DATA,
+ PARAM_POLL_HEADERS,
+ PARAM_POLL_FREQ,
+ PARAM_POLL_FREQ_UNITS
+ }));
+ this.addApiAction(
+ new ApiAction(ACTION_POLL_AS_USER, new String[] {PARAM_CONTEXT_ID, PARAM_USER_ID}));
+ }
+
+ @Override
+ public String getPrefix() {
+ return PREFIX;
+ }
+
+ @Override
+ public ApiResponse handleApiView(String name, JSONObject params) throws ApiException {
+ LOGGER.debug("handleApiView {} {}", name, params);
+
+ switch (name) {
+ case VIEW_GET_LOGGED_IN_INDICATOR:
+ Pattern loggedInPattern =
+ getContext(params).getVerificationMethod().getLoggedInIndicatorPattern();
+ return new ApiResponseElement(
+ "logged_in_regex",
+ loggedInPattern != null ? loggedInPattern.toString() : "");
+ case VIEW_GET_LOGGED_OUT_INDICATOR:
+ Pattern loggedOutPattern =
+ getContext(params).getVerificationMethod().getLoggedOutIndicatorPattern();
+ return new ApiResponseElement(
+ "logged_out_regex",
+ loggedOutPattern != null ? loggedOutPattern.toString() : "");
+ default:
+ throw new ApiException(ApiException.Type.BAD_VIEW);
+ }
+ }
+
+ @Override
+ public ApiResponse handleApiAction(String name, JSONObject params) throws ApiException {
+ LOGGER.debug("handleApiAction {} {}", name, params);
+
+ Context context;
+ switch (name) {
+ case ACTION_SET_LOGGED_IN_INDICATOR:
+ String loggedInIndicator = params.getString(PARAM_LOGGED_IN_INDICATOR);
+ if (loggedInIndicator == null || loggedInIndicator.isEmpty())
+ throw new ApiException(Type.MISSING_PARAMETER, PARAM_LOGGED_IN_INDICATOR);
+ context = getContext(params);
+ context.getVerificationMethod().setLoggedInIndicatorPattern(loggedInIndicator);
+ context.save();
+ return ApiResponseElement.OK;
+
+ case ACTION_SET_LOGGED_OUT_INDICATOR:
+ String loggedOutIndicator = params.getString(PARAM_LOGGED_OUT_INDICATOR);
+ if (loggedOutIndicator == null || loggedOutIndicator.isEmpty())
+ throw new ApiException(Type.MISSING_PARAMETER, PARAM_LOGGED_OUT_INDICATOR);
+ context = getContext(params);
+ context.getVerificationMethod().setLoggedOutIndicatorPattern(loggedOutIndicator);
+ context.save();
+ return ApiResponseElement.OK;
+
+ case ACTION_SET_VERIFICATION_METHOD:
+ context = getContext(params);
+ AuthCheckingStrategy strategy;
+ try {
+ strategy =
+ AuthCheckingStrategy.valueOf(params.getString(PARAM_CHECKING_STRATEGY));
+ } catch (Exception e) {
+ throw new ApiException(
+ ApiException.Type.ILLEGAL_PARAMETER, PARAM_CHECKING_STRATEGY);
+ }
+ if (AuthCheckingStrategy.POLL_URL.equals(strategy)) {
+ AuthPollFrequencyUnits units;
+ try {
+ units =
+ AuthPollFrequencyUnits.valueOf(
+ params.getString(PARAM_POLL_FREQ_UNITS));
+ } catch (Exception e) {
+ throw new ApiException(
+ ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_FREQ_UNITS);
+ }
+ String pollUrl = params.getString(PARAM_POLL_URL);
+ if (pollUrl == null || pollUrl.isEmpty()) {
+ throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_URL);
+ }
+ try {
+ new URI(pollUrl, true);
+ } catch (Exception e) {
+ throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_URL);
+ }
+ int freq;
+ try {
+ freq = params.getInt(PARAM_POLL_FREQ);
+ } catch (Exception e) {
+ throw new ApiException(
+ ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_FREQ);
+ }
+ if (freq <= 0) {
+ throw new ApiException(
+ ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_FREQ);
+ }
+ String pollData = params.getString(PARAM_POLL_DATA);
+ String pollHeaders = params.getString(PARAM_POLL_HEADERS);
+ context.getVerificationMethod().setPollUrl(pollUrl);
+ context.getVerificationMethod().setPollData(pollData);
+ context.getVerificationMethod().setPollHeaders(pollHeaders);
+ context.getVerificationMethod().setPollFrequency(freq);
+ context.getVerificationMethod().setPollFrequencyUnits(units);
+ }
+ context.getVerificationMethod().setAuthCheckingStrategy(strategy);
+ Model.getSingleton().getSession().saveContext(context);
+ return ApiResponseElement.OK;
+
+ case ACTION_POLL_AS_USER:
+ User user = getUser(params);
+ try {
+ VerificationMethod verifMethod = user.getContext().getVerificationMethod();
+ if (!verifMethod
+ .getAuthCheckingStrategy()
+ .equals(AuthCheckingStrategy.POLL_URL)) {
+ throw new ApiException(Type.ILLEGAL_PARAMETER, PARAM_CHECKING_STRATEGY);
+ }
+ if (StringUtils.isBlank(verifMethod.getPollUrl())) {
+ throw new ApiException(Type.ILLEGAL_PARAMETER, PARAM_POLL_URL);
+ }
+ HttpMessage msg = verifMethod.pollAsUser(user);
+ int href = -1;
+ if (msg.getHistoryRef() != null) {
+ href = msg.getHistoryRef().getHistoryId();
+ }
+ ApiResponseSet responseSet =
+ ApiResponseConversionUtils.httpMessageToSet(href, msg);
+ responseSet.put(
+ "pollSuccessful",
+ Boolean.toString(
+ verifMethod.evaluateAuthRequest(
+ msg, user.getAuthenticationState())));
+ return responseSet;
+ } catch (IllegalArgumentException e) {
+ throw new ApiException(Type.ILLEGAL_PARAMETER, PARAM_CONTEXT_ID);
+ } catch (IOException e) {
+ throw new ApiException(Type.INTERNAL_ERROR, e);
+ }
+
+ default:
+ throw new ApiException(Type.BAD_ACTION);
+ }
+ }
+
+ private Context getContext(JSONObject params) throws ApiException {
+ return ApiUtils.getContextByParamId(params, PARAM_CONTEXT_ID);
+ }
+
+ private User getUser(JSONObject params) throws ApiException {
+ int contextId = ApiUtils.getIntParam(params, PARAM_CONTEXT_ID);
+ int userId = ApiUtils.getIntParam(params, PARAM_USER_ID);
+ ExtensionUserManagement usersExtension =
+ Control.getSingleton()
+ .getExtensionLoader()
+ .getExtension(ExtensionUserManagement.class);
+ if (usersExtension == null) {
+ throw new ApiException(Type.DOES_NOT_EXIST, PARAM_USER_ID);
+ }
+ User user = usersExtension.getContextUserAuthManager(contextId).getUserById(userId);
+ if (user == null) throw new ApiException(Type.USER_NOT_FOUND, PARAM_USER_ID);
+ return user;
+ }
+}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/users/UsersAPI.java b/zap/src/main/java/org/zaproxy/zap/extension/users/UsersAPI.java
index abd2f1ac079..8b2f4b2580c 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/users/UsersAPI.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/users/UsersAPI.java
@@ -141,8 +141,11 @@ public UsersAPI(ExtensionUserManagement extension) {
new ApiAction(
ACTION_AUTHENTICATE_AS_USER,
new String[] {PARAM_CONTEXT_ID, PARAM_USER_ID}));
- this.addApiAction(
- new ApiAction(ACTION_POLL_AS_USER, new String[] {PARAM_CONTEXT_ID, PARAM_USER_ID}));
+ ApiAction pollAsUserAction =
+ new ApiAction(ACTION_POLL_AS_USER, new String[] {PARAM_CONTEXT_ID, PARAM_USER_ID});
+ pollAsUserAction.setDeprecated(true);
+ pollAsUserAction.setDeprecatedDescription("Use verification/pollAsUser instead.");
+ this.addApiAction(pollAsUserAction);
this.addApiAction(
new ApiAction(
ACTION_SET_AUTH_STATE,
@@ -326,7 +329,7 @@ public ApiResponse handleApiAction(String name, JSONObject params) throws ApiExc
"authSuccessful",
Boolean.toString(
user.getContext()
- .getAuthenticationMethod()
+ .getVerificationMethod()
.evaluateAuthRequest(
authMsg,
user.getAuthenticationState())));
@@ -343,7 +346,7 @@ public ApiResponse handleApiAction(String name, JSONObject params) throws ApiExc
case ACTION_POLL_AS_USER:
user = getUser(params);
try {
- HttpMessage msg = user.getContext().getAuthenticationMethod().pollAsUser(user);
+ HttpMessage msg = user.getContext().getVerificationMethod().pollAsUser(user);
int href = -1;
if (msg.getHistoryRef() != null) {
href = msg.getHistoryRef().getHistoryId();
@@ -354,7 +357,7 @@ public ApiResponse handleApiAction(String name, JSONObject params) throws ApiExc
"pollSuccessful",
Boolean.toString(
user.getContext()
- .getAuthenticationMethod()
+ .getVerificationMethod()
.evaluateAuthRequest(
msg, user.getAuthenticationState())));
diff --git a/zap/src/main/java/org/zaproxy/zap/model/Context.java b/zap/src/main/java/org/zaproxy/zap/model/Context.java
index 337d1bc6f06..a9956a32990 100644
--- a/zap/src/main/java/org/zaproxy/zap/model/Context.java
+++ b/zap/src/main/java/org/zaproxy/zap/model/Context.java
@@ -41,6 +41,7 @@
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.authentication.AuthenticationMethod;
import org.zaproxy.zap.authentication.ManualAuthenticationMethodType.ManualAuthenticationMethod;
+import org.zaproxy.zap.authentication.VerificationMethod;
import org.zaproxy.zap.extension.authorization.AuthorizationDetectionMethod;
import org.zaproxy.zap.extension.authorization.BasicAuthorizationDetectionMethod;
import org.zaproxy.zap.extension.authorization.BasicAuthorizationDetectionMethod.LogicalOperator;
@@ -87,6 +88,9 @@ public class Context {
/** The authentication method. */
private AuthenticationMethod authenticationMethod = null;
+ /** The verification method. */
+ private VerificationMethod verificationMethod = null;
+
/** The session management method. */
private SessionManagementMethod sessionManagementMethod;
@@ -106,6 +110,7 @@ public Context(Session session, int id) {
this.name = String.valueOf(id);
this.sessionManagementMethod = new CookieBasedSessionManagementMethod(id);
this.authenticationMethod = new ManualAuthenticationMethod(id);
+ this.verificationMethod = this.authenticationMethod.getVerificationMethod();
this.authorizationDetectionMethod =
new BasicAuthorizationDetectionMethod(null, null, null, LogicalOperator.AND);
this.urlParamParser.setContext(this);
@@ -526,10 +531,39 @@ public AuthenticationMethod getAuthenticationMethod() {
/**
* Sets the authentication method corresponding to this context.
*
+ * Setting a new authentication method also links this context's verification method to the
+ * one owned by the new authentication method. To use an independent verification method, call
+ * {@link #setVerificationMethod(VerificationMethod)} afterwards.
+ *
* @param authenticationMethod the new authentication method
*/
public void setAuthenticationMethod(AuthenticationMethod authenticationMethod) {
this.authenticationMethod = authenticationMethod;
+ this.verificationMethod = authenticationMethod.getVerificationMethod();
+ }
+
+ /**
+ * Gets the verification method corresponding to this context.
+ *
+ * @return the verification method
+ * @since 2.18.0
+ */
+ public VerificationMethod getVerificationMethod() {
+ return verificationMethod;
+ }
+
+ /**
+ * Sets the verification method corresponding to this context, independently of the
+ * authentication method.
+ *
+ * @param verificationMethod the new verification method
+ * @since 2.18.0
+ */
+ public void setVerificationMethod(VerificationMethod verificationMethod) {
+ this.verificationMethod = verificationMethod;
+ if (this.authenticationMethod != null) {
+ this.authenticationMethod.setVerificationMethod(verificationMethod);
+ }
}
/**
@@ -888,7 +922,10 @@ public Context duplicate() {
newContext.excludeFromPatterns = new ArrayList<>(this.excludeFromPatterns);
newContext.inScope = this.inScope;
newContext.techSet = new TechSet(this.techSet);
- newContext.authenticationMethod = this.authenticationMethod.clone();
+ AuthenticationMethod am = this.authenticationMethod.clone();
+ newContext.setAuthenticationMethod(am);
+ newContext.setVerificationMethod(
+ this.verificationMethod.copy(am::replaceUserDataInPollRequest));
newContext.sessionManagementMethod = this.sessionManagementMethod.clone();
newContext.urlParamParser = this.urlParamParser.clone();
newContext.postParamParser = this.postParamParser.clone();
diff --git a/zap/src/main/java/org/zaproxy/zap/users/User.java b/zap/src/main/java/org/zaproxy/zap/users/User.java
index 825f9322d5b..b7516e78a3a 100644
--- a/zap/src/main/java/org/zaproxy/zap/users/User.java
+++ b/zap/src/main/java/org/zaproxy/zap/users/User.java
@@ -250,7 +250,7 @@ protected long getLastSuccessfulAuthTime() {
* @return true, if is authenticated
*/
public boolean isAuthenticated(HttpMessage msg) {
- return getContext().getAuthenticationMethod().isAuthenticated(msg, this);
+ return getContext().getVerificationMethod().isAuthenticated(msg, this);
}
/**
diff --git a/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties b/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties
index b83d3cb7981..ed5a3be1842 100644
--- a/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties
+++ b/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties
@@ -710,6 +710,7 @@ authentication.panel.label.loggedOut = Regex pattern used to identify Logged Out
authentication.panel.label.noConfigPanel = This method is fully configured and does not require any configuration.
authentication.panel.label.polldata = Poll Request POST Data (if any):
authentication.panel.label.pollheaders = Additional Poll Request Headers:
+authentication.panel.label.pollmethod = Poll Request Method:
authentication.panel.label.pollurl = URL to Poll for Verification:
authentication.panel.label.strategy = Verification Strategy:
authentication.panel.label.strategy.auto_detect = Auto-Detect
@@ -2971,6 +2972,31 @@ variant.shortname.urlpath = URL Path
variant.shortname.userdefined = User Defined
variant.shortname.xml = XML Tag/Attribute
+verification.api.action.pollAsUser = Tries to poll as the identified user, returning the poll request and whether it appears to have succeeded. This will only work if the polling verification strategy has been configured.
+verification.api.action.pollAsUser.param.contextId = The Context ID
+verification.api.action.pollAsUser.param.userId = The User ID
+verification.api.action.setLoggedInIndicator = Sets the logged in indicator for the context with the given ID.
+verification.api.action.setLoggedInIndicator.param.contextId = The Context ID
+verification.api.action.setLoggedInIndicator.param.loggedInIndicatorRegex = The regular expression to match against when determining if the user is logged in.
+verification.api.action.setLoggedOutIndicator = Sets the logged out indicator for the context with the given ID.
+verification.api.action.setLoggedOutIndicator.param.contextId = The Context ID
+verification.api.action.setLoggedOutIndicator.param.loggedOutIndicatorRegex = The regular expression to match against when determining if the user is logged out.
+verification.api.action.setVerificationMethod = Sets the verification method and related configuration for the context with the given ID.
+verification.api.action.setVerificationMethod.param.checkingStrategy = One of EACH_RESP, EACH_REQ, EACH_REQ_RESP, POLL_URL
+verification.api.action.setVerificationMethod.param.contextId = The Context ID
+verification.api.action.setVerificationMethod.param.pollData = The POST data to supply to the pollUrl, optional and only takes effect if checkingStrategy = POLL_URL
+verification.api.action.setVerificationMethod.param.pollFrequency = An integer greater than zero, must be supplied if checkingStrategy = POLL_URL, otherwise ignored
+verification.api.action.setVerificationMethod.param.pollFrequencyUnits = One of REQUESTS, SECONDS, must be supplied if checkingStrategy = POLL_URL, otherwise ignored
+verification.api.action.setVerificationMethod.param.pollHeaders = Any additional headers to include in the poll request, separated by '\\n' characters, optional and only takes effect if checkingStrategy = POLL_URL
+verification.api.action.setVerificationMethod.param.pollUrl = The URL for ZAP to poll, must be supplied if checkingStrategy = POLL_URL, otherwise ignored
+verification.api.desc = API for managing the verification method used to determine if a user is authenticated.
+verification.api.view.getLoggedInIndicator = Gets the logged in indicator for the context with the given ID.
+verification.api.view.getLoggedInIndicator.param.contextId = The Context ID
+verification.api.view.getLoggedOutIndicator = Gets the logged out indicator for the context with the given ID.
+verification.api.view.getLoggedOutIndicator.param.contextId = The Context ID
+verification.panel.label.description =
This panel allows you to change the verification strategy for this Context.
+verification.panel.title = Verification
+
view.dialog.dontPrompt = Do not show this message again.
view.dialog.remember = Remember my choice and do not show this message again.
view.href.table.cell.alert.risk.label.high = High
diff --git a/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodIndicatorsUnitTest.java b/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodIndicatorsUnitTest.java
deleted file mode 100644
index 6925e137d7a..00000000000
--- a/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodIndicatorsUnitTest.java
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * 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 static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.mock;
-
-import org.apache.commons.httpclient.URI;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.parosproxy.paros.network.HttpMessage;
-import org.parosproxy.paros.network.HttpRequestHeader;
-import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
-import org.zaproxy.zap.users.AuthenticationState;
-import org.zaproxy.zap.users.User;
-
-@ExtendWith(MockitoExtension.class)
-class AuthenticationMethodIndicatorsUnitTest {
-
- private static final String LOGGED_OUT_COMPLEX_INDICATOR = "User [^\\s]* logged out";
- private static final String LOGGED_OUT_COMPLEX_BODY =
- "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- + "Pellentesque auctor nulla id turpis placerat vulputate. User Test logged out. "
- + " Proin tempor bibendum eros rutrum. ";
- private static final String LOGGED_IN_INDICATOR = "logged in";
- private static final String LOGGED_OUT_INDICATOR = "logged out";
- private static final String LOGGED_IN_BODY =
- "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- + "Pellentesque auctor nulla id turpis placerat vulputate."
- + LOGGED_IN_INDICATOR
- + " Proin tempor bibendum eros rutrum. ";
- private static final String LOGGED_OUT_BODY =
- "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- + "Pellentesque auctor nulla id turpis placerat vulputate."
- + LOGGED_OUT_INDICATOR
- + " Proin tempor bibendum eros rutrum. ";
-
- private HttpMessage loginMessage;
- private AuthenticationMethod method;
-
- @BeforeEach
- void setUp() throws Exception {
- loginMessage = new HttpMessage();
- HttpRequestHeader header = new HttpRequestHeader();
- header.setURI(new URI("http://www.example.com", true));
- loginMessage.setRequestHeader(header);
- method = Mockito.mock(AuthenticationMethod.class, Mockito.CALLS_REAL_METHODS);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
- }
-
- @Test
- void shouldStoreSetLoggedInIndicator() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
-
- // When/Then
- assertEquals(LOGGED_IN_INDICATOR, method.getLoggedInIndicatorPattern().pattern());
- }
-
- @Test
- void shouldStoreSetLoggedOutIndicator() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
-
- // When/Then
- assertEquals(LOGGED_OUT_INDICATOR, method.getLoggedOutIndicatorPattern().pattern());
- }
-
- @Test
- void shouldNotStoreNullOrEmptyLoggedInIndicator() {
- // Given
- method.setLoggedInIndicatorPattern(null);
-
- // When/Then
- assertNull(method.getLoggedInIndicatorPattern());
-
- // Given
- method.setLoggedInIndicatorPattern(" ");
-
- // When/Then
- assertNull(method.getLoggedInIndicatorPattern());
- }
-
- @Test
- void shouldNotStoreNullOrEmptyLoggedOutIndicator() {
- // Given
- method.setLoggedOutIndicatorPattern(null);
-
- // When/Then
- assertNull(method.getLoggedOutIndicatorPattern());
-
- // Given
- method.setLoggedOutIndicatorPattern(" ");
-
- // When/Then
- assertNull(method.getLoggedOutIndicatorPattern());
- }
-
- @Test
- void shouldIdentifyLoggedInResponseBodyWhenLoggedInIndicatorIsSet() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- loginMessage.setResponseBody(LOGGED_IN_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedOutResponseBodyWhenLoggedInIndicatorIsSet() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- loginMessage.setResponseBody(LOGGED_OUT_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedInResponseHeaderWhenLoggedInIndicatorIsSet() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- loginMessage.getResponseHeader().addHeader("test", LOGGED_IN_INDICATOR);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedOutResponseHeaderWhenLoggedInIndicatorIsSet() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- loginMessage.getResponseHeader().addHeader("test", LOGGED_OUT_INDICATOR);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedOutResponseBodyWhenLoggedOutIndicatorIsSet() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
- loginMessage.setResponseBody(LOGGED_OUT_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedInResponseBodyWhenLoggedOutIndicatorIsSet() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
- loginMessage.setResponseBody(LOGGED_IN_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedOutResponseHeaderWhenLoggedOutIndicatorIsSet() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
- loginMessage.getResponseHeader().addHeader("test", LOGGED_OUT_INDICATOR);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedInResponseHeaderWhenLoggedOutIndicatorIsSet() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
- loginMessage.getResponseHeader().addHeader("test", LOGGED_IN_INDICATOR);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedOutResponseWithComplexRegex() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_COMPLEX_INDICATOR);
- loginMessage.setResponseBody(LOGGED_OUT_COMPLEX_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedInResponseWithComplexRegex() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_COMPLEX_INDICATOR);
- loginMessage.setResponseBody(LOGGED_OUT_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyResponseAsLoggedInWhenNoIndicatorIsSet() {
- // Given
- loginMessage.setResponseBody(LOGGED_OUT_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedInRequestBodyWhenLoggedInIndicatorIsSet() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.setRequestBody(LOGGED_IN_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedOutRequestBodyWhenLoggedInIndicatorIsSet() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.setRequestBody(LOGGED_OUT_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedInRequestHeaderWhenLoggedInIndicatorIsSet() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.getRequestHeader().addHeader("test", LOGGED_IN_INDICATOR);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedOutRequestHeaderWhenLoggedInIndicatorIsSet() {
- // Given
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.getRequestHeader().addHeader("test", LOGGED_OUT_INDICATOR);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedOutRequestBodyWhenLoggedOutIndicatorIsSet() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.setRequestBody(LOGGED_OUT_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedInRequestBodyWhenLoggedOutIndicatorIsSet() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.setRequestBody(LOGGED_IN_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedOutRequestHeaderWhenLoggedOutIndicatorIsSet() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.getRequestHeader().addHeader("test", LOGGED_OUT_INDICATOR);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedInRequestHeaderWhenLoggedOutIndicatorIsSet() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.getRequestHeader().addHeader("test", LOGGED_IN_INDICATOR);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyLoggedOutRequestWithComplexRegex() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_COMPLEX_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.setRequestBody(LOGGED_OUT_COMPLEX_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(false));
- }
-
- @Test
- void shouldIdentifyLoggedInRequestWithComplexRegex() {
- // Given
- method.setLoggedOutIndicatorPattern(LOGGED_OUT_COMPLEX_INDICATOR);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
- loginMessage.setRequestBody(LOGGED_OUT_BODY);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-
- @Test
- void shouldIdentifyRequestAsLoggedInWhenNoIndicatorIsSet() {
- // Given
- loginMessage.setRequestBody(LOGGED_OUT_BODY);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
-
- // When/Then
- assertThat(method.isAuthenticated(loginMessage, user), is(true));
- }
-}
diff --git a/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodUnitTest.java b/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodUnitTest.java
index bcf6b75dd5b..90d340e97d3 100644
--- a/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodUnitTest.java
+++ b/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodUnitTest.java
@@ -22,12 +22,11 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
import static org.mockito.Mockito.mock;
-import org.apache.commons.httpclient.URI;
import org.junit.jupiter.api.Test;
import org.parosproxy.paros.network.HttpMessage;
-import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
import org.zaproxy.zap.extension.api.ApiResponse;
import org.zaproxy.zap.session.SessionManagementMethod;
import org.zaproxy.zap.session.WebSession;
@@ -40,68 +39,8 @@ class AuthenticationMethodUnitTest {
void shouldBeEqualToItself() {
// Given
AuthenticationMethod authMethod = new AuthenticationMethodTest();
- // when
- boolean equals = authMethod.equals(authMethod);
- // Then
- assertThat(equals, is(equalTo(true)));
- }
-
- @Test
- void shouldBeEqualToDifferentAuthenticationMethodWithSameContents() {
- // Given
- String loggedInIndicator = "loggedInIndicator";
- String loggedOutIndicator = "loggedOutIndicator";
- AuthenticationMethod authMethod =
- createAuthenticationMethod(loggedInIndicator, loggedOutIndicator);
- AuthenticationMethod otherAuthMethod =
- createAuthenticationMethod(loggedInIndicator, loggedOutIndicator);
- // When
- boolean equals = authMethod.equals(otherAuthMethod) & otherAuthMethod.equals(authMethod);
- // Then
- assertThat(equals, is(equalTo(true)));
- }
-
- @Test
- void shouldBeEqualToDifferentAuthenticationMethodWithNullLoggedInIndicator() {
- // Given
- String loggedInIndicator = null;
- String loggedOutIndicator = "loggedOutIndicator";
- AuthenticationMethod authMethod =
- createAuthenticationMethod(loggedInIndicator, loggedOutIndicator);
- AuthenticationMethod otherAuthMethod =
- createAuthenticationMethod(loggedInIndicator, loggedOutIndicator);
// When
- boolean equals = authMethod.equals(otherAuthMethod) & otherAuthMethod.equals(authMethod);
- // Then
- assertThat(equals, is(equalTo(true)));
- }
-
- @Test
- void shouldBeEqualToDifferentAuthenticationMethodWithNullLoggedOutIndicator() {
- // Given
- String loggedInIndicator = "loggedInIndicator";
- String loggedOutIndicator = null;
- AuthenticationMethod authMethod =
- createAuthenticationMethod(loggedInIndicator, loggedOutIndicator);
- AuthenticationMethod otherAuthMethod =
- createAuthenticationMethod(loggedInIndicator, loggedOutIndicator);
- // When
- boolean equals = authMethod.equals(otherAuthMethod) & otherAuthMethod.equals(authMethod);
- // Then
- assertThat(equals, is(equalTo(true)));
- }
-
- @Test
- void shouldBeEqualToDifferentAuthenticationMethodWithNullIndicators() {
- // Given
- String loggedInIndicator = null;
- String loggedOutIndicator = null;
- AuthenticationMethod authMethod =
- createAuthenticationMethod(loggedInIndicator, loggedOutIndicator);
- AuthenticationMethod otherAuthMethod =
- createAuthenticationMethod(loggedInIndicator, loggedOutIndicator);
- // When
- boolean equals = authMethod.equals(otherAuthMethod) & otherAuthMethod.equals(authMethod);
+ boolean equals = authMethod.equals(authMethod);
// Then
assertThat(equals, is(equalTo(true)));
}
@@ -116,60 +55,6 @@ void shouldNotBeEqualToNull() {
assertThat(equals, is(false));
}
- @Test
- void shouldNotBeEqualToAuthenticationMethodWithJustDifferentLoggedInIndicator() {
- // Given
- String loggedOutIndicator = "loggedOutIndicator";
- AuthenticationMethod authMethod =
- createAuthenticationMethod("loggedInIndicator", loggedOutIndicator);
- AuthenticationMethod otherAuthMethod =
- createAuthenticationMethod("otherLoggedInIndicator", loggedOutIndicator);
- // When
- boolean equals = authMethod.equals(otherAuthMethod) | otherAuthMethod.equals(authMethod);
- // Then
- assertThat(equals, is(false));
- }
-
- @Test
- void shouldNotBeEqualToAuthenticationMethodWithJustDifferentNullLoggedInIndicator() {
- // Given
- String loggedOutIndicator = "loggedOutIndicator";
- AuthenticationMethod authMethod =
- createAuthenticationMethod("loggedInIndicator", loggedOutIndicator);
- AuthenticationMethod otherAuthMethod = createAuthenticationMethod(null, loggedOutIndicator);
- // When
- boolean equals = authMethod.equals(otherAuthMethod) | otherAuthMethod.equals(authMethod);
- // Then
- assertThat(equals, is(false));
- }
-
- @Test
- void shouldNotBeEqualToAuthenticationMethodWithJustDifferentLoggedOutIndicator() {
- // Given
- String loggedInIndicator = "loggedInIndicator";
- AuthenticationMethod authMethod =
- createAuthenticationMethod(loggedInIndicator, "loggedOutIndicator");
- AuthenticationMethod otherAuthMethod =
- createAuthenticationMethod(loggedInIndicator, "otherLoggedOutIndicator");
- // When
- boolean equals = authMethod.equals(otherAuthMethod) | otherAuthMethod.equals(authMethod);
- // Then
- assertThat(equals, is(false));
- }
-
- @Test
- void shouldNotBeEqualToAuthenticationMethodWithJustDifferentNullLoggedOutIndicator() {
- // Given
- String loggedInIndicator = "loggedInIndicator";
- AuthenticationMethod authMethod =
- createAuthenticationMethod(loggedInIndicator, "loggedOutIndicator");
- AuthenticationMethod otherAuthMethod = createAuthenticationMethod(loggedInIndicator, null);
- // When
- boolean equals = authMethod.equals(otherAuthMethod) | otherAuthMethod.equals(authMethod);
- // Then
- assertThat(equals, is(false));
- }
-
@Test
void shouldNotBeEqualToExtendedAuthenticationMethod() {
// Given
@@ -184,76 +69,16 @@ void shouldNotBeEqualToExtendedAuthenticationMethod() {
}
@Test
- void shouldFailAuthIfMessageNull() {
- // Given
- AuthenticationMethod authMethod = new AuthenticationMethodTest();
- User user = mock(User.class);
- // When
- boolean auth = authMethod.isAuthenticated(null, user);
- // Then
- assertThat(auth, is(false));
- }
-
- @Test
- void shouldFailAuthIfUserNull() {
+ void shouldWireVerificationMethodUserDataReplacer() {
// Given
- AuthenticationMethod authMethod = new AuthenticationMethodTest();
- HttpMessage msg = mock(HttpMessage.class);
- // When
- boolean auth = authMethod.isAuthenticated(msg, null);
- // Then
- assertThat(auth, is(false));
- }
-
- @Test
- void shouldFailAuthIfVerifIsAutoDetect() {
- // Given
- AuthenticationMethod authMethod = new AuthenticationMethodTest();
- authMethod.setAuthCheckingStrategy(AuthCheckingStrategy.AUTO_DETECT);
- HttpMessage msg = mock(HttpMessage.class);
+ RecordingAuthenticationMethod method = new RecordingAuthenticationMethod();
+ HttpMessage msg = new HttpMessage();
User user = mock(User.class);
// When
- boolean auth = authMethod.isAuthenticated(msg, user);
+ method.getVerificationMethod().getUserDataReplacer().accept(msg, user);
// Then
- assertThat(auth, is(false));
- }
-
- @Test
- void shouldPassAuthIfMsgContainsLoggedInIndicator() throws Exception {
- // Given
- AuthenticationMethod authMethod = new AuthenticationMethodTest();
- authMethod.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
- authMethod.setLoggedInIndicatorPattern("loggedin");
- HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
- msg.setResponseBody("The user is loggedin again.");
- User user = mock(User.class);
- // When
- boolean auth = authMethod.isAuthenticated(msg, user);
- // Then
- assertThat(auth, is(true));
- }
-
- @Test
- void shouldFailAuthIfMsgContainsLoggedOutIndicator() throws Exception {
- // Given
- AuthenticationMethod authMethod = new AuthenticationMethodTest();
- authMethod.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
- authMethod.setLoggedOutIndicatorPattern("loggedout");
- HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
- msg.setResponseBody("The user is loggedout this time.");
- User user = mock(User.class);
- // When
- boolean auth = authMethod.isAuthenticated(msg, user);
- // Then
- assertThat(auth, is(false));
- }
-
- private static AuthenticationMethod createAuthenticationMethod(
- String loggedInIndicator, String loggedOutIndicator) {
- AuthenticationMethod authMethod = new AuthenticationMethodTest();
- authMethod.setLoggedInIndicatorPattern(loggedInIndicator);
- authMethod.setLoggedOutIndicatorPattern(loggedOutIndicator);
- return authMethod;
+ assertThat(method.replacedMsg, is(sameInstance(msg)));
+ assertThat(method.replacedUser, is(sameInstance(user)));
}
private static class AuthenticationMethodTest extends AuthenticationMethod {
@@ -295,4 +120,16 @@ public ApiResponse getApiResponseRepresentation() {
@Override
public void replaceUserDataInPollRequest(HttpMessage msg, User user) {}
}
+
+ private static class RecordingAuthenticationMethod extends AuthenticationMethodTest {
+
+ private HttpMessage replacedMsg;
+ private User replacedUser;
+
+ @Override
+ public void replaceUserDataInPollRequest(HttpMessage msg, User user) {
+ this.replacedMsg = msg;
+ this.replacedUser = user;
+ }
+ }
}
diff --git a/zap/src/test/java/org/zaproxy/zap/authentication/FormBasedAuthenticationMethodTypeUnitTest.java b/zap/src/test/java/org/zaproxy/zap/authentication/FormBasedAuthenticationMethodTypeUnitTest.java
index b900e05c1eb..849bf3ee57a 100644
--- a/zap/src/test/java/org/zaproxy/zap/authentication/FormBasedAuthenticationMethodTypeUnitTest.java
+++ b/zap/src/test/java/org/zaproxy/zap/authentication/FormBasedAuthenticationMethodTypeUnitTest.java
@@ -24,159 +24,43 @@
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
-import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
import org.apache.commons.httpclient.URI;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.WithConfigsTest;
-import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
-import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
import org.zaproxy.zap.authentication.PostBasedAuthenticationMethodType.PostBasedAuthenticationMethod;
-import org.zaproxy.zap.users.AuthenticationState;
import org.zaproxy.zap.users.User;
class FormBasedAuthenticationMethodTypeUnitTest extends WithConfigsTest {
- private static final String LOGGED_IN_INDICATOR = "logged in";
- private static final String LOGGED_IN_BODY =
- "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- + "Pellentesque auctor nulla id turpis placerat vulputate."
- + LOGGED_IN_INDICATOR
- + " Proin tempor bibendum eros rutrum. ";
-
private AuthenticationMethod method;
- private FormBasedAuthenticationMethodType type;
@BeforeEach
void setUp() throws Exception {
-
- type = new FormBasedAuthenticationMethodType();
+ FormBasedAuthenticationMethodType type = new FormBasedAuthenticationMethodType();
method = type.createAuthenticationMethod(1);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
- method.setPollFrequency(5);
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- }
-
- @Test
- void shouldReplaceUsernameInPollRequest() throws NullPointerException, IOException {
- // Given
- String test = "/shouldReplaceUsernameInPollRequest/test";
- String encodedPattern =
- URLEncoder.encode(
- PostBasedAuthenticationMethod.MSG_USER_PATTERN,
- StandardCharsets.UTF_8.name());
- String pollUrl = "/shouldReplaceUsernameInPollRequest/pollUrl";
- String pollData = "user=" + PostBasedAuthenticationMethod.MSG_USER_PATTERN;
- String username = "user";
- final List orderedReqUrls = new ArrayList<>();
- final List orderedReqData = new ArrayList<>();
-
- setMessageHandler(
- msg -> {
- URI uri = msg.getRequestHeader().getURI();
- if (pollUrl.equals(uri.getPath())) {
- orderedReqUrls.add(uri.getEscapedPathQuery());
- orderedReqData.add(msg.getRequestBody().toString());
- msg.setResponseBody(LOGGED_IN_BODY);
- }
- });
- HttpMessage testMsg = this.getHttpMessage(test);
- HttpMessage pollMsg = this.getHttpMessage(pollUrl + "?" + encodedPattern);
-
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollData(pollData);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
- given(user.getAuthenticationCredentials())
- .willReturn(new UsernamePasswordAuthenticationCredentials(username, ""));
-
- // When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(orderedReqUrls.size(), is(1));
- assertThat(orderedReqUrls.get(0), is(pollUrl + "?" + username));
- assertThat(orderedReqData.size(), is(1));
- assertThat(
- orderedReqData.get(0),
- is(pollData.replace(PostBasedAuthenticationMethod.MSG_USER_PATTERN, username)));
}
@Test
- void shouldNotReplacePasswordInPollRequest() throws NullPointerException, IOException {
+ void shouldUrlEncodeUsernameInPollRequestBody() throws Exception {
// Given
- String test = "/shouldNotReplacePasswordInPollRequest/test";
- String pollUrl = "/shouldNotReplacePasswordInPollRequest/pollUrl";
- String pollData = "pwd=" + PostBasedAuthenticationMethod.MSG_PASS_PATTERN;
- String password = "password123!";
- final List orderedReqData = new ArrayList<>();
-
- setMessageHandler(
- msg -> {
- URI uri = msg.getRequestHeader().getURI();
- if (pollUrl.equals(uri.getPath())) {
- orderedReqData.add(msg.getRequestBody().toString());
- msg.setResponseBody(LOGGED_IN_BODY);
- }
- });
- HttpMessage testMsg = this.getHttpMessage(test);
- HttpMessage pollMsg = this.getHttpMessage(pollUrl);
-
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollData(pollData);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
- given(user.getAuthenticationCredentials())
- .willReturn(new UsernamePasswordAuthenticationCredentials("", password));
-
- // When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(orderedReqData.size(), is(1));
- assertThat(orderedReqData.get(0), is(pollData));
- }
-
- @Test
- void shouldUrlEncodeUsernameInPollRequestBody() throws NullPointerException, IOException {
- // Given
- String test = "/shouldEncodeSpacesInBody/test";
- String pollUrl = "/shouldEncodeSpacesInBody/pollUrl";
- String pollData = "user=" + PostBasedAuthenticationMethod.MSG_USER_PATTERN;
String username = "user name";
- final List orderedReqData = new ArrayList<>();
-
- setMessageHandler(
- msg -> {
- URI uri = msg.getRequestHeader().getURI();
- if (pollUrl.equals(uri.getPath())) {
- orderedReqData.add(msg.getRequestBody().toString());
- msg.setResponseBody(LOGGED_IN_BODY);
- }
- });
- HttpMessage testMsg = this.getHttpMessage(test);
- HttpMessage pollMsg = this.getHttpMessage(pollUrl);
-
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollData(pollData);
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/pollUrl", true));
+ msg.setRequestBody("user=" + PostBasedAuthenticationMethod.MSG_USER_PATTERN);
User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
given(user.getAuthenticationCredentials())
.willReturn(new UsernamePasswordAuthenticationCredentials(username, ""));
- // When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(orderedReqData.size(), is(1));
+ // When
+ method.replaceUserDataInPollRequest(msg, user);
+
+ // Then
assertThat(
- orderedReqData.get(0),
- is(
- pollData.replace(
- PostBasedAuthenticationMethod.MSG_USER_PATTERN,
- URLEncoder.encode(username, StandardCharsets.UTF_8.name()))));
+ msg.getRequestBody().toString(),
+ is("user=" + URLEncoder.encode(username, StandardCharsets.UTF_8.name())));
}
}
diff --git a/zap/src/test/java/org/zaproxy/zap/authentication/JsonBasedAuthenticationMethodTypeUnitTest.java b/zap/src/test/java/org/zaproxy/zap/authentication/JsonBasedAuthenticationMethodTypeUnitTest.java
index c84cda1a709..c9b13679390 100644
--- a/zap/src/test/java/org/zaproxy/zap/authentication/JsonBasedAuthenticationMethodTypeUnitTest.java
+++ b/zap/src/test/java/org/zaproxy/zap/authentication/JsonBasedAuthenticationMethodTypeUnitTest.java
@@ -24,157 +24,40 @@
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
-import java.io.IOException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
import org.apache.commons.httpclient.URI;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.WithConfigsTest;
-import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
-import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
import org.zaproxy.zap.authentication.PostBasedAuthenticationMethodType.PostBasedAuthenticationMethod;
-import org.zaproxy.zap.users.AuthenticationState;
import org.zaproxy.zap.users.User;
class JsonBasedAuthenticationMethodTypeUnitTest extends WithConfigsTest {
- private static final String LOGGED_IN_INDICATOR = "logged in";
- private static final String LOGGED_IN_BODY =
- "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
- + "Pellentesque auctor nulla id turpis placerat vulputate."
- + LOGGED_IN_INDICATOR
- + " Proin tempor bibendum eros rutrum. ";
-
private AuthenticationMethod method;
- private JsonBasedAuthenticationMethodType type;
@BeforeEach
void setUp() throws Exception {
-
- type = new JsonBasedAuthenticationMethodType();
+ JsonBasedAuthenticationMethodType type = new JsonBasedAuthenticationMethodType();
method = type.createAuthenticationMethod(1);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
- method.setPollFrequency(5);
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- }
-
- @Test
- void shouldReplaceUsernameInPollRequest() throws NullPointerException, IOException {
- // Given
- String test = "/shouldReplaceUsernameInPollRequest/test";
- String encodedPattern =
- URLEncoder.encode(
- PostBasedAuthenticationMethod.MSG_USER_PATTERN,
- StandardCharsets.UTF_8.name());
- String pollUrl = "/shouldReplaceUsernameInPollRequest/pollUrl";
- String pollData = "user=" + PostBasedAuthenticationMethod.MSG_USER_PATTERN;
- String username = "user";
- final List orderedReqUrls = new ArrayList<>();
- final List orderedReqData = new ArrayList<>();
-
- setMessageHandler(
- msg -> {
- URI uri = msg.getRequestHeader().getURI();
- if (pollUrl.equals(uri.getPath())) {
- orderedReqUrls.add(uri.getEscapedPathQuery());
- orderedReqData.add(msg.getRequestBody().toString());
- msg.setResponseBody(LOGGED_IN_BODY);
- }
- });
- HttpMessage testMsg = this.getHttpMessage(test);
- HttpMessage pollMsg = this.getHttpMessage(pollUrl + "?" + encodedPattern);
-
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollData(pollData);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
- given(user.getAuthenticationCredentials())
- .willReturn(new UsernamePasswordAuthenticationCredentials(username, ""));
-
- // When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(orderedReqUrls.size(), is(1));
- assertThat(orderedReqUrls.get(0), is(pollUrl + "?" + username));
- assertThat(orderedReqData.size(), is(1));
- assertThat(
- orderedReqData.get(0),
- is(pollData.replace(PostBasedAuthenticationMethod.MSG_USER_PATTERN, username)));
}
@Test
- void shouldNotReplacePasswordInPollRequest() throws NullPointerException, IOException {
+ void shouldNotUrlEncodeUsernameInPollRequestBody() throws Exception {
// Given
- String test = "/shouldNotReplacePasswordInPollRequest/test";
- String pollUrl = "/shouldNotReplacePasswordInPollRequest/pollUrl";
- String pollData = "pwd=" + PostBasedAuthenticationMethod.MSG_PASS_PATTERN;
- String password = "password123!";
- final List orderedReqData = new ArrayList<>();
-
- setMessageHandler(
- msg -> {
- URI uri = msg.getRequestHeader().getURI();
- if (pollUrl.equals(uri.getPath())) {
- orderedReqData.add(msg.getRequestBody().toString());
- msg.setResponseBody(LOGGED_IN_BODY);
- }
- });
- HttpMessage testMsg = this.getHttpMessage(test);
- HttpMessage pollMsg = this.getHttpMessage(pollUrl);
-
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollData(pollData);
-
- User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
- given(user.getAuthenticationCredentials())
- .willReturn(new UsernamePasswordAuthenticationCredentials("", password));
-
- // When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(orderedReqData.size(), is(1));
- assertThat(orderedReqData.get(0), is(pollData));
- }
-
- @Test
- void shouldNotUrlEncodeUsernameInPollRequestBody() throws NullPointerException, IOException {
- // Given
- String test = "/shouldEncodeSpacesInBody/test";
- String pollUrl = "/shouldEncodeSpacesInBody/pollUrl";
- String pollData =
- "{ \"user\": \"" + PostBasedAuthenticationMethod.MSG_USER_PATTERN + "\" }";
String username = "user name";
- final List orderedReqData = new ArrayList<>();
-
- setMessageHandler(
- msg -> {
- URI uri = msg.getRequestHeader().getURI();
- if (pollUrl.equals(uri.getPath())) {
- orderedReqData.add(msg.getRequestBody().toString());
- msg.setResponseBody(LOGGED_IN_BODY);
- }
- });
- HttpMessage testMsg = this.getHttpMessage(test);
- HttpMessage pollMsg = this.getHttpMessage(pollUrl);
-
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollData(pollData);
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/pollUrl", true));
+ msg.setRequestBody(
+ "{ \"user\": \"" + PostBasedAuthenticationMethod.MSG_USER_PATTERN + "\" }");
User user = mock(User.class);
- given(user.getAuthenticationState()).willReturn(new AuthenticationState());
given(user.getAuthenticationCredentials())
.willReturn(new UsernamePasswordAuthenticationCredentials(username, ""));
- // When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(orderedReqData.size(), is(1));
- assertThat(
- orderedReqData.get(0),
- is(pollData.replace(PostBasedAuthenticationMethod.MSG_USER_PATTERN, username)));
+ // When
+ method.replaceUserDataInPollRequest(msg, user);
+
+ // Then
+ assertThat(msg.getRequestBody().toString(), is("{ \"user\": \"" + username + "\" }"));
}
}
diff --git a/zap/src/test/java/org/zaproxy/zap/authentication/PostBasedAuthenticationMethodTypeUnitTest.java b/zap/src/test/java/org/zaproxy/zap/authentication/PostBasedAuthenticationMethodTypeUnitTest.java
index bd3053146de..9028dabb77b 100644
--- a/zap/src/test/java/org/zaproxy/zap/authentication/PostBasedAuthenticationMethodTypeUnitTest.java
+++ b/zap/src/test/java/org/zaproxy/zap/authentication/PostBasedAuthenticationMethodTypeUnitTest.java
@@ -33,6 +33,8 @@
import static org.mockito.Mockito.withSettings;
import static org.zaproxy.zap.authentication.PostBasedAuthenticationMethodTypeUnitTest.ReplaceAntiCsrfTokenValueIfRequired.token;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -47,7 +49,6 @@
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.WithConfigsTest;
-import org.zaproxy.zap.authentication.FormBasedAuthenticationMethodType.FormBasedAuthenticationMethod;
import org.zaproxy.zap.authentication.PostBasedAuthenticationMethodType.PostBasedAuthenticationMethod;
import org.zaproxy.zap.extension.anticsrf.AntiCsrfToken;
import org.zaproxy.zap.extension.anticsrf.ExtensionAntiCSRF;
@@ -292,10 +293,107 @@ public String apply(String value) {
}
}
- static class FormBasedAuthenticationMethodTest extends WithConfigsTest {
+ /**
+ * Test {@link
+ * PostBasedAuthenticationMethodType#replaceUserCredentialsDataInPollRequest(HttpMessage, User,
+ * UnaryOperator)}.
+ */
+ static class ReplaceUserCredentialsDataInPollRequest {
+
+ private static final String USER_PATTERN = PostBasedAuthenticationMethod.MSG_USER_PATTERN;
+ private static final String PASS_PATTERN = PostBasedAuthenticationMethod.MSG_PASS_PATTERN;
+
+ private User user;
+
+ @BeforeEach
+ void setup() {
+ user = mock(User.class);
+ }
+
+ @Test
+ void shouldReplaceUsernameInUrlAndBody() throws Exception {
+ // Given
+ given(user.getAuthenticationCredentials())
+ .willReturn(new UsernamePasswordAuthenticationCredentials("alice", "secret"));
+ String encodedPattern = URLEncoder.encode(USER_PATTERN, StandardCharsets.UTF_8.name());
+ HttpMessage msg =
+ new HttpMessage(
+ new URI("http://example.com/poll?user=" + encodedPattern, true));
+ msg.setRequestBody("user=" + USER_PATTERN);
+ // When
+ PostBasedAuthenticationMethodType.replaceUserCredentialsDataInPollRequest(
+ msg, user, UnaryOperator.identity());
+ // Then
+ assertThat(msg.getRequestHeader().getURI().getEscapedQuery(), is("user=alice"));
+ assertThat(msg.getRequestBody().toString(), is("user=alice"));
+ }
+
+ @Test
+ void shouldNotReplacePasswordInBody() throws Exception {
+ // Given
+ given(user.getAuthenticationCredentials())
+ .willReturn(new UsernamePasswordAuthenticationCredentials("alice", "secret"));
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/poll", true));
+ msg.setRequestBody("pwd=" + PASS_PATTERN);
+ // When
+ PostBasedAuthenticationMethodType.replaceUserCredentialsDataInPollRequest(
+ msg, user, UnaryOperator.identity());
+ // Then
+ assertThat(msg.getRequestBody().toString(), is("pwd=" + PASS_PATTERN));
+ }
+
+ @Test
+ void shouldEncodeUsernameInBodyUsingProvidedEncoder() throws Exception {
+ // Given
+ given(user.getAuthenticationCredentials())
+ .willReturn(new UsernamePasswordAuthenticationCredentials("user name", ""));
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/poll", true));
+ msg.setRequestBody("user=" + USER_PATTERN);
+ // When
+ PostBasedAuthenticationMethodType.replaceUserCredentialsDataInPollRequest(
+ msg, user, value -> value.replace(" ", "+"));
+ // Then
+ assertThat(msg.getRequestBody().toString(), is("user=user+name"));
+ }
+
+ @Test
+ void shouldNotReplaceAnythingWhenUserIsNull() throws Exception {
+ // Given
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/poll", true));
+ msg.setRequestBody("user=" + USER_PATTERN);
+ String originalBody = msg.getRequestBody().toString();
+ // When
+ PostBasedAuthenticationMethodType.replaceUserCredentialsDataInPollRequest(
+ msg, null, UnaryOperator.identity());
+ // Then
+ assertThat(msg.getRequestBody().toString(), is(originalBody));
+ }
- private AuthenticationMethod method;
- private FormBasedAuthenticationMethodType type;
+ @Test
+ void shouldNotReplaceAnythingWhenCredentialsAreNotUsernamePassword() throws Exception {
+ // Given
+ given(user.getAuthenticationCredentials())
+ .willReturn(mock(AuthenticationCredentials.class));
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/poll", true));
+ msg.setRequestBody("user=" + USER_PATTERN);
+ String originalBody = msg.getRequestBody().toString();
+ // When
+ PostBasedAuthenticationMethodType.replaceUserCredentialsDataInPollRequest(
+ msg, user, UnaryOperator.identity());
+ // Then
+ assertThat(msg.getRequestBody().toString(), is(originalBody));
+ }
+ }
+
+ /**
+ * Test the shared {@code authenticate} behaviour of {@link PostBasedAuthenticationMethod},
+ * using a minimal concrete implementation since the behaviour under test does not depend on the
+ * POST content format (form vs JSON).
+ */
+ static class Authenticate extends WithConfigsTest {
+
+ private PostBasedAuthenticationMethod method;
+ private TestPostBasedAuthenticationMethodType type;
private Context context;
private ExtensionAntiCSRF extAntiCsrf;
@@ -307,10 +405,9 @@ void setUp() throws Exception {
Control.initSingletonForTesting();
extAntiCsrf = mock(ExtensionAntiCSRF.class);
- type = spy(new FormBasedAuthenticationMethodType());
+ type = spy(new TestPostBasedAuthenticationMethodType());
method = type.createAuthenticationMethod(1);
- given(type.createAuthenticationMethod(anyInt()))
- .willReturn((FormBasedAuthenticationMethod) method);
+ given(type.createAuthenticationMethod(anyInt())).willReturn(method);
context = Model.getSingleton().getSession().getNewContext("test");
}
@@ -378,4 +475,52 @@ void shouldSetCorrectContentLengthWithAntiCsrfTokens()
assertThat(expectedRequestBody, is(orderedReqData.get(1)));
}
}
+
+ /**
+ * Minimal concrete {@link PostBasedAuthenticationMethodType}, used to exercise the behaviour
+ * shared by all post-based authentication methods without depending on a specific POST content
+ * format (form vs JSON).
+ */
+ private static class TestPostBasedAuthenticationMethodType
+ extends PostBasedAuthenticationMethodType {
+
+ TestPostBasedAuthenticationMethodType() {
+ super("Test Based Authentication", 100, "testBasedAuthentication", "test.popup", false);
+ }
+
+ @Override
+ public PostBasedAuthenticationMethod createAuthenticationMethod(int contextId) {
+ return new TestPostBasedAuthenticationMethod(null);
+ }
+
+ @Override
+ public AbstractAuthenticationMethodOptionsPanel buildOptionsPanel(Context uiSharedContext) {
+ return null;
+ }
+
+ @Override
+ public boolean isTypeForMethod(AuthenticationMethod method) {
+ return method instanceof TestPostBasedAuthenticationMethod;
+ }
+
+ class TestPostBasedAuthenticationMethod extends PostBasedAuthenticationMethod {
+
+ TestPostBasedAuthenticationMethod(TestPostBasedAuthenticationMethod copy) {
+ super("test/content-type", UnaryOperator.identity(), copy);
+ }
+
+ @Override
+ public AuthenticationMethodType getType() {
+ return TestPostBasedAuthenticationMethodType.this;
+ }
+
+ @Override
+ public AuthenticationMethod duplicate() {
+ return new TestPostBasedAuthenticationMethod(this);
+ }
+
+ @Override
+ public void replaceUserDataInPollRequest(HttpMessage msg, User user) {}
+ }
+ }
}
diff --git a/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodPollUrlUnitTest.java b/zap/src/test/java/org/zaproxy/zap/authentication/VerificationMethodPollUrlUnitTest.java
similarity index 50%
rename from zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodPollUrlUnitTest.java
rename to zap/src/test/java/org/zaproxy/zap/authentication/VerificationMethodPollUrlUnitTest.java
index 0542ec55f9e..c1f0758a0ee 100644
--- a/zap/src/test/java/org/zaproxy/zap/authentication/AuthenticationMethodPollUrlUnitTest.java
+++ b/zap/src/test/java/org/zaproxy/zap/authentication/VerificationMethodPollUrlUnitTest.java
@@ -28,10 +28,8 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.apache.commons.httpclient.URI;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpRequestHeader;
import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
@@ -40,7 +38,8 @@
import org.zaproxy.zap.users.AuthenticationState;
import org.zaproxy.zap.users.User;
-class AuthenticationMethodPollUrlUnitTest extends TestUtils {
+/** Unit test for {@link VerificationMethod} poll URL behaviour. */
+class VerificationMethodPollUrlUnitTest extends TestUtils {
private static final String LOGGED_IN_INDICATOR = "logged in";
private static final String LOGGED_IN_BODY =
@@ -49,17 +48,12 @@ class AuthenticationMethodPollUrlUnitTest extends TestUtils {
+ LOGGED_IN_INDICATOR
+ " Proin tempor bibendum eros rutrum. ";
- private HttpMessage loginMessage;
- private AuthenticationMethod method;
+ private VerificationMethod vm;
@BeforeEach
void setUp() throws Exception {
- loginMessage = new HttpMessage();
- HttpRequestHeader header = new HttpRequestHeader();
- header.setURI(new URI("http://www.example.com", true));
- loginMessage.setRequestHeader(header);
- method = Mockito.mock(AuthenticationMethod.class, Mockito.CALLS_REAL_METHODS);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm = new VerificationMethod();
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
}
@Test
@@ -80,17 +74,17 @@ void shouldPollOnFirstRequest() throws NullPointerException, IOException {
HttpMessage testMsg = this.getHttpMessage(test);
HttpMessage pollMsg = this.getHttpMessage(pollUrl);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
- method.setPollFrequency(5);
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+ vm.setPollFrequency(5);
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
User user = mock(User.class);
given(user.getAuthenticationState()).willReturn(new AuthenticationState());
// When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
assertThat(orderedReqs.size(), is(1));
assertThat(orderedReqs.get(0), is(pollUrl));
}
@@ -113,25 +107,25 @@ void shouldPollOnSpecifiedNumberOfRequests() throws NullPointerException, IOExce
HttpMessage testMsg = this.getHttpMessage(test);
HttpMessage pollMsg = this.getHttpMessage(pollUrl);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
- method.setPollFrequency(5);
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+ vm.setPollFrequency(5);
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
User user = mock(User.class);
given(user.getAuthenticationState()).willReturn(new AuthenticationState());
// When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
assertThat(orderedReqs.size(), is(1));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
assertThat(orderedReqs.size(), is(1));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
assertThat(orderedReqs.size(), is(2));
assertThat(orderedReqs.get(0), is(pollUrl));
assertThat(orderedReqs.get(1), is(pollUrl));
@@ -154,23 +148,23 @@ void shouldPollEveryFailingRequest() throws NullPointerException, IOException {
HttpMessage testMsg = this.getHttpMessage(test);
HttpMessage pollMsg = this.getHttpMessage(pollUrl);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
- method.setPollFrequency(5);
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+ vm.setPollFrequency(5);
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
User user = mock(User.class);
given(user.getAuthenticationState()).willReturn(new AuthenticationState());
// When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(false));
+ assertThat(vm.isAuthenticated(testMsg, user), is(false));
assertThat(orderedReqs.size(), is(1));
- assertThat(method.isAuthenticated(testMsg, user), is(false));
+ assertThat(vm.isAuthenticated(testMsg, user), is(false));
assertThat(orderedReqs.size(), is(2));
- assertThat(method.isAuthenticated(testMsg, user), is(false));
+ assertThat(vm.isAuthenticated(testMsg, user), is(false));
assertThat(orderedReqs.size(), is(3));
- assertThat(method.isAuthenticated(testMsg, user), is(false));
+ assertThat(vm.isAuthenticated(testMsg, user), is(false));
assertThat(orderedReqs.size(), is(4));
}
@@ -195,24 +189,24 @@ void shouldPollWhenForced() throws NullPointerException, IOException {
HttpMessage testMsg = this.getHttpMessage(test);
HttpMessage pollMsg = this.getHttpMessage(pollUrl);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
- method.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
- method.setPollFrequency(500);
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(pollMsg.getRequestHeader().getURI().toString());
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+ vm.setPollFrequency(500);
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
// When/Then
- assertThat(method.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
assertThat(orderedReqs.size(), is(1));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
- assertThat(method.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
assertThat(orderedReqs.size(), is(1));
user.getAuthenticationState().setLastPollResult(false);
- assertThat(method.isAuthenticated(testMsg, user), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user), is(true));
assertThat(orderedReqs.size(), is(2));
assertThat(orderedReqs.get(0), is(pollUrl));
assertThat(orderedReqs.get(1), is(pollUrl));
@@ -236,11 +230,11 @@ void shouldPollOnSpecifiedNumberOfRequestsPerUser() throws NullPointerException,
HttpMessage testMsg = this.getHttpMessage(test);
HttpMessage pollMsg = this.getHttpMessage(pollUrl);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollUrl(pollMsg.getRequestHeader().getURI().toString() + "?");
- method.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
- method.setPollFrequency(5);
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(pollMsg.getRequestHeader().getURI().toString() + "?");
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+ vm.setPollFrequency(5);
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
User user1 = mock(User.class);
given(user1.getAuthenticationState()).willReturn(new AuthenticationState());
@@ -248,39 +242,131 @@ void shouldPollOnSpecifiedNumberOfRequestsPerUser() throws NullPointerException,
given(user2.getAuthenticationState()).willReturn(new AuthenticationState());
// When/Then
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
// First poll for user1
assertThat(orderedReqs.size(), is(1));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
- assertThat(method.isAuthenticated(testMsg, user2), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user2), is(true));
// First poll for user2
assertThat(orderedReqs.size(), is(2));
- assertThat(method.isAuthenticated(testMsg, user2), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user2), is(true));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
// Should not have changed yet
assertThat(orderedReqs.size(), is(2));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
// Second poll for user1
assertThat(orderedReqs.size(), is(3));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
- assertThat(method.isAuthenticated(testMsg, user1), is(true));
- assertThat(method.isAuthenticated(testMsg, user2), is(true));
- assertThat(method.isAuthenticated(testMsg, user2), is(true));
- assertThat(method.isAuthenticated(testMsg, user2), is(true));
- assertThat(method.isAuthenticated(testMsg, user2), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user1), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user2), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user2), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user2), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user2), is(true));
// Should not have changed yet
assertThat(orderedReqs.size(), is(3));
- assertThat(method.isAuthenticated(testMsg, user2), is(true));
+ assertThat(vm.isAuthenticated(testMsg, user2), is(true));
// Second poll for user2
assertThat(orderedReqs.size(), is(4));
}
+ @Test
+ void shouldUseGetWhenPollMethodNullAndDataEmpty() throws Exception {
+ // Given
+ String pollUrl = "/pollUrl";
+ List pollMessages = new ArrayList<>();
+ setMessageHandler(pollMessages::add);
+
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(getHttpMessage(pollUrl).getRequestHeader().getURI().toString());
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+
+ // When
+ vm.pollAsUser(user);
+
+ // Then
+ assertThat(pollMessages, hasSize(1));
+ assertThat(pollMessages.get(0).getRequestHeader().getMethod(), is(HttpRequestHeader.GET));
+ }
+
+ @Test
+ void shouldUsePostWhenPollMethodNullAndDataNonEmpty() throws Exception {
+ // Given
+ String pollUrl = "/pollUrl";
+ List pollMessages = new ArrayList<>();
+ setMessageHandler(pollMessages::add);
+
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(getHttpMessage(pollUrl).getRequestHeader().getURI().toString());
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setPollData("param=value");
+
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+
+ // When
+ vm.pollAsUser(user);
+
+ // Then
+ assertThat(pollMessages, hasSize(1));
+ assertThat(pollMessages.get(0).getRequestHeader().getMethod(), is(HttpRequestHeader.POST));
+ }
+
+ @Test
+ void shouldUseConfiguredMethodWhenPollMethodSet() throws Exception {
+ // Given
+ String pollUrl = "/pollUrl";
+ List pollMessages = new ArrayList<>();
+ setMessageHandler(pollMessages::add);
+
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(getHttpMessage(pollUrl).getRequestHeader().getURI().toString());
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setPollData("param=value");
+ vm.setPollMethod(HttpRequestHeader.GET);
+
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+
+ // When
+ vm.pollAsUser(user);
+
+ // Then
+ assertThat(pollMessages, hasSize(1));
+ assertThat(pollMessages.get(0).getRequestHeader().getMethod(), is(HttpRequestHeader.GET));
+ }
+
+ @Test
+ void shouldUseConfiguredPostMethodWhenPollMethodSetAndDataEmpty() throws Exception {
+ // Given
+ String pollUrl = "/pollUrl";
+ List pollMessages = new ArrayList<>();
+ setMessageHandler(pollMessages::add);
+
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(getHttpMessage(pollUrl).getRequestHeader().getURI().toString());
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setPollMethod(HttpRequestHeader.POST);
+
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+
+ // When
+ vm.pollAsUser(user);
+
+ // Then
+ assertThat(pollMessages, hasSize(1));
+ assertThat(pollMessages.get(0).getRequestHeader().getMethod(), is(HttpRequestHeader.POST));
+ }
+
@Test
void shouldHandlePollHeadersWithColonsInValues() throws Exception {
// Given
@@ -292,11 +378,11 @@ void shouldHandlePollHeadersWithColonsInValues() throws Exception {
HttpMessage testMsg = this.getHttpMessage(test);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollUrl(getHttpMessage(pollUrl).getRequestHeader().getURI().toString());
- method.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(getHttpMessage(pollUrl).getRequestHeader().getURI().toString());
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
- method.setPollHeaders(
+ vm.setPollHeaders(
"""
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIn0:signature
X-Custom-Time: 2025-07-19T10:30:45.123Z
@@ -307,7 +393,7 @@ void shouldHandlePollHeadersWithColonsInValues() throws Exception {
given(user.getAuthenticationState()).willReturn(new AuthenticationState());
// When
- method.isAuthenticated(testMsg, user);
+ vm.isAuthenticated(testMsg, user);
// Then
assertThat(pollMessages, hasSize(1));
@@ -318,4 +404,34 @@ void shouldHandlePollHeadersWithColonsInValues() throws Exception {
assertThat(requestHeader.getHeader("X-Custom-Time"), is("2025-07-19T10:30:45.123Z"));
assertThat(requestHeader.getHeader("Content-Type"), is("application/json"));
}
+
+ @Test
+ void shouldInvokeUserDataReplacerBeforeSendingPollRequest() throws Exception {
+ // Given
+ String pollUrl = "/shouldInvokeUserDataReplacer/pollUrl";
+ List sentBodies = new ArrayList<>();
+
+ setMessageHandler(
+ msg -> {
+ if (pollUrl.equals(msg.getRequestHeader().getURI().getPath())) {
+ sentBodies.add(msg.getRequestBody().toString());
+ msg.setResponseBody(LOGGED_IN_BODY);
+ }
+ });
+
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl(getHttpMessage(pollUrl).getRequestHeader().getURI().toString());
+ vm.setLoggedInIndicatorPattern(LOGGED_IN_INDICATOR);
+ vm.setUserDataReplacer((msg, user) -> msg.setRequestBody("replaced"));
+
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+
+ // When
+ vm.isAuthenticated(this.getHttpMessage("/test"), user);
+
+ // Then
+ assertThat(sentBodies, hasSize(1));
+ assertThat(sentBodies.get(0), is("replaced"));
+ }
}
diff --git a/zap/src/test/java/org/zaproxy/zap/authentication/VerificationMethodUnitTest.java b/zap/src/test/java/org/zaproxy/zap/authentication/VerificationMethodUnitTest.java
new file mode 100644
index 00000000000..2907da78ca0
--- /dev/null
+++ b/zap/src/test/java/org/zaproxy/zap/authentication/VerificationMethodUnitTest.java
@@ -0,0 +1,506 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2026 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 static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.httpclient.URI;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.parosproxy.paros.network.HttpMessage;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
+import org.zaproxy.zap.users.AuthenticationState;
+import org.zaproxy.zap.users.User;
+
+/** Unit test for {@link VerificationMethod}. */
+class VerificationMethodUnitTest {
+
+ private VerificationMethod vm;
+
+ @BeforeEach
+ void setUp() {
+ vm = new VerificationMethod();
+ }
+
+ @Test
+ void shouldHaveExpectedDefaults() {
+ assertThat(vm.getAuthCheckingStrategy(), is(AuthCheckingStrategy.POLL_URL));
+ assertThat(vm.getPollFrequencyUnits(), is(AuthPollFrequencyUnits.SECONDS));
+ assertThat(vm.getPollFrequency(), is(VerificationMethod.DEFAULT_POLL_FREQUENCY));
+ assertThat(vm.getLoggedInIndicatorPattern(), is(nullValue()));
+ assertThat(vm.getLoggedOutIndicatorPattern(), is(nullValue()));
+ }
+
+ @Nested
+ class SetAuthCheckingStrategy {
+
+ @Test
+ void shouldThrowOnNull() {
+ assertThrows(NullPointerException.class, () -> vm.setAuthCheckingStrategy(null));
+ }
+ }
+
+ @Nested
+ class IndicatorPatterns {
+
+ @Test
+ void shouldStoreNonEmptyLoggedInIndicator() {
+ vm.setLoggedInIndicatorPattern("loggedin");
+ assertThat(vm.getLoggedInIndicatorPattern().pattern(), is("loggedin"));
+ }
+
+ @Test
+ void shouldStoreNonEmptyLoggedOutIndicator() {
+ vm.setLoggedOutIndicatorPattern("loggedout");
+ assertThat(vm.getLoggedOutIndicatorPattern().pattern(), is("loggedout"));
+ }
+
+ @Test
+ void shouldClearLoggedInIndicatorWhenSetToNull() {
+ vm.setLoggedInIndicatorPattern("loggedin");
+ vm.setLoggedInIndicatorPattern(null);
+ assertThat(vm.getLoggedInIndicatorPattern(), is(nullValue()));
+ }
+
+ @Test
+ void shouldClearLoggedOutIndicatorWhenSetToNull() {
+ vm.setLoggedOutIndicatorPattern("loggedout");
+ vm.setLoggedOutIndicatorPattern(null);
+ assertThat(vm.getLoggedOutIndicatorPattern(), is(nullValue()));
+ }
+
+ @Test
+ void shouldClearLoggedInIndicatorWhenSetToBlank() {
+ vm.setLoggedInIndicatorPattern("loggedin");
+ vm.setLoggedInIndicatorPattern(" ");
+ assertThat(vm.getLoggedInIndicatorPattern(), is(nullValue()));
+ }
+
+ @Test
+ void shouldClearLoggedOutIndicatorWhenSetToBlank() {
+ vm.setLoggedOutIndicatorPattern("loggedout");
+ vm.setLoggedOutIndicatorPattern(" ");
+ assertThat(vm.getLoggedOutIndicatorPattern(), is(nullValue()));
+ }
+ }
+
+ @Nested
+ class IsAuthenticated {
+
+ @Test
+ void shouldReturnFalseWhenMessageNull() {
+ User user = mock(User.class);
+ assertThat(vm.isAuthenticated(null, user), is(false));
+ }
+
+ @Test
+ void shouldReturnFalseWhenUserNull() throws Exception {
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ assertThat(vm.isAuthenticated(msg, null), is(false));
+ }
+
+ @Test
+ void shouldReturnFalseWhenStrategyIsAutoDetect() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.AUTO_DETECT);
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ User user = mock(User.class);
+ assertThat(vm.isAuthenticated(msg, user), is(false));
+ }
+
+ @Test
+ void shouldReturnTrueWhenNoIndicatorsSet() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setResponseBody("some body");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(true));
+ }
+
+ @Test
+ void shouldReturnTrueWhenResponseContainsLoggedInIndicator() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm.setLoggedInIndicatorPattern("loggedin");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setResponseBody("The user is loggedin again.");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(true));
+ }
+
+ @Test
+ void shouldReturnFalseWhenResponseContainsLoggedOutIndicator() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm.setLoggedOutIndicatorPattern("loggedout");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setResponseBody("The user is loggedout this time.");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(false));
+ }
+
+ @Test
+ void shouldReturnTrueWhenResponseLacksLoggedOutIndicator() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm.setLoggedOutIndicatorPattern("loggedout");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setResponseBody("Welcome back, user.");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(true));
+ }
+
+ @Test
+ void shouldReturnFalseWhenResponseLacksLoggedInIndicatorAndNoLoggedOutIndicatorSet()
+ throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm.setLoggedInIndicatorPattern("loggedin");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setResponseBody("Welcome, you are logged out.");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(false));
+ }
+
+ @Test
+ void shouldReturnTrueWhenResponseHeaderContainsLoggedInIndicator() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm.setLoggedInIndicatorPattern("loggedin");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.getResponseHeader().addHeader("X-Test", "loggedin");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(true));
+ }
+
+ @Test
+ void shouldReturnFalseWhenResponseHeaderContainsLoggedOutIndicator() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm.setLoggedOutIndicatorPattern("loggedout");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.getResponseHeader().addHeader("X-Test", "loggedout");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(false));
+ }
+
+ @Test
+ void shouldReturnFalseWhenResponseMatchesComplexLoggedOutRegex() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm.setLoggedOutIndicatorPattern("User [^\\s]* logged out");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setResponseBody("Some text. User Test logged out. More text.");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(false));
+ }
+
+ @Test
+ void shouldReturnTrueWhenResponseDoesNotMatchComplexLoggedOutRegex() throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ vm.setLoggedOutIndicatorPattern("User [^\\s]* logged out");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setResponseBody("Welcome, you are logged out.");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(true));
+ }
+
+ @Test
+ void shouldReturnTrueWhenRequestBodyContainsLoggedInIndicatorAndStrategyIsEachReq()
+ throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
+ vm.setLoggedInIndicatorPattern("loggedin");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setRequestBody("The user is loggedin again.");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(true));
+ }
+
+ @Test
+ void shouldReturnTrueWhenRequestHeaderContainsLoggedInIndicatorAndStrategyIsEachReq()
+ throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
+ vm.setLoggedInIndicatorPattern("loggedin");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.getRequestHeader().addHeader("X-Test", "loggedin");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(true));
+ }
+
+ @Test
+ void shouldReturnFalseWhenRequestBodyContainsLoggedOutIndicatorAndStrategyIsEachReq()
+ throws Exception {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
+ vm.setLoggedOutIndicatorPattern("loggedout");
+ HttpMessage msg = new HttpMessage(new URI("http://example.com/", true));
+ msg.setRequestBody("The user is loggedout this time.");
+ User user = mock(User.class);
+ given(user.getAuthenticationState()).willReturn(new AuthenticationState());
+ assertThat(vm.isAuthenticated(msg, user), is(false));
+ }
+ }
+
+ @Nested
+ class PollAsUser {
+
+ @Test
+ void shouldThrowWhenStrategyIsNotPollUrl() {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ User user = mock(User.class);
+ assertThrows(IllegalArgumentException.class, () -> vm.pollAsUser(user));
+ }
+
+ @Test
+ void shouldThrowWhenPollUrlIsNull() {
+ User user = mock(User.class);
+ IllegalArgumentException ex =
+ assertThrows(IllegalArgumentException.class, () -> vm.pollAsUser(user));
+ assertThat(ex.getMessage(), is("Poll URL is not set"));
+ }
+
+ @Test
+ void shouldThrowWhenPollUrlIsBlank() {
+ vm.setPollUrl(" ");
+ User user = mock(User.class);
+ IllegalArgumentException ex =
+ assertThrows(IllegalArgumentException.class, () -> vm.pollAsUser(user));
+ assertThat(ex.getMessage(), is("Poll URL is not set"));
+ }
+
+ @Test
+ void shouldInvokeUserDataReplacerBeforeSending() {
+ // Given
+ List replacedMessages = new ArrayList<>();
+ vm.setPollUrl("http://127.0.0.1:1/");
+ vm.setUserDataReplacer((msg, user) -> replacedMessages.add(msg));
+ User user = mock(User.class);
+ // When
+ assertThrows(IOException.class, () -> vm.pollAsUser(user));
+ // Then
+ assertThat(replacedMessages, hasSize(1));
+ }
+ }
+
+ @Nested
+ class Copy {
+
+ @Test
+ void shouldCopyAllFields() {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
+ vm.setPollUrl("http://example.com/poll");
+ vm.setPollData("data=value");
+ vm.setPollHeaders("X-Header: value");
+ vm.setPollFrequency(30);
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+ vm.setLoggedInIndicatorPattern("loggedin");
+ vm.setLoggedOutIndicatorPattern("loggedout");
+
+ VerificationMethod copy = vm.copy(null);
+
+ assertThat(copy, is(not(sameInstance(vm))));
+ assertThat(copy.getAuthCheckingStrategy(), is(AuthCheckingStrategy.EACH_REQ));
+ assertThat(copy.getPollUrl(), is("http://example.com/poll"));
+ assertThat(copy.getPollData(), is("data=value"));
+ assertThat(copy.getPollHeaders(), is("X-Header: value"));
+ assertThat(copy.getPollFrequency(), is(30));
+ assertThat(copy.getPollFrequencyUnits(), is(AuthPollFrequencyUnits.REQUESTS));
+ assertThat(copy.getLoggedInIndicatorPattern().pattern(), is("loggedin"));
+ assertThat(copy.getLoggedOutIndicatorPattern().pattern(), is("loggedout"));
+ }
+
+ @Test
+ void shouldBeIndependentOfOriginal() {
+ vm.setLoggedInIndicatorPattern("original");
+ VerificationMethod copy = vm.copy(null);
+
+ vm.setLoggedInIndicatorPattern("modified");
+
+ assertThat(copy.getLoggedInIndicatorPattern().pattern(), is("original"));
+ }
+
+ @Test
+ void shouldSetProvidedUserDataReplacer() {
+ java.util.function.BiConsumer replacer = (msg, user) -> {};
+
+ VerificationMethod copy = vm.copy(replacer);
+
+ assertThat(copy.getUserDataReplacer(), is(sameInstance(replacer)));
+ }
+ }
+
+ @Nested
+ class Equals {
+
+ @Test
+ void shouldBeEqualToItself() {
+ assertThat(vm.equals(vm), is(equalTo(true)));
+ }
+
+ @Test
+ void shouldBeEqualToMethodWithSameContents() {
+ VerificationMethod other = createMethod("loggedin", "loggedout");
+ vm = createMethod("loggedin", "loggedout");
+ assertThat(vm.equals(other) & other.equals(vm), is(true));
+ }
+
+ @Test
+ void shouldBeEqualWhenBothIndicatorsNull() {
+ VerificationMethod other = new VerificationMethod();
+ assertThat(vm.equals(other) & other.equals(vm), is(true));
+ }
+
+ @Test
+ void shouldBeEqualWhenLoggedInIndicatorNull() {
+ vm = createMethod(null, "loggedout");
+ VerificationMethod other = createMethod(null, "loggedout");
+ assertThat(vm.equals(other) & other.equals(vm), is(true));
+ }
+
+ @Test
+ void shouldBeEqualWhenLoggedOutIndicatorNull() {
+ vm = createMethod("loggedin", null);
+ VerificationMethod other = createMethod("loggedin", null);
+ assertThat(vm.equals(other) & other.equals(vm), is(true));
+ }
+
+ @Test
+ void shouldNotBeEqualToNull() {
+ assertThat(vm.equals(null), is(false));
+ }
+
+ @Test
+ void shouldNotBeEqualWhenLoggedInIndicatorDiffers() {
+ vm = createMethod("loggedinA", "loggedout");
+ VerificationMethod other = createMethod("loggedinB", "loggedout");
+ assertThat(vm.equals(other) | other.equals(vm), is(false));
+ }
+
+ @Test
+ void shouldNotBeEqualWhenOneLoggedInIndicatorIsNull() {
+ vm = createMethod("loggedin", "loggedout");
+ VerificationMethod other = createMethod(null, "loggedout");
+ assertThat(vm.equals(other) | other.equals(vm), is(false));
+ }
+
+ @Test
+ void shouldNotBeEqualWhenLoggedOutIndicatorDiffers() {
+ vm = createMethod("loggedin", "loggedoutA");
+ VerificationMethod other = createMethod("loggedin", "loggedoutB");
+ assertThat(vm.equals(other) | other.equals(vm), is(false));
+ }
+
+ @Test
+ void shouldNotBeEqualWhenOneLoggedOutIndicatorIsNull() {
+ vm = createMethod("loggedin", "loggedout");
+ VerificationMethod other = createMethod("loggedin", null);
+ assertThat(vm.equals(other) | other.equals(vm), is(false));
+ }
+
+ @Test
+ void shouldNotBeEqualWhenStrategyDiffers() {
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ VerificationMethod other = new VerificationMethod();
+ other.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_REQ);
+ assertThat(vm.equals(other) | other.equals(vm), is(false));
+ }
+
+ @Test
+ void shouldNotBeEqualWhenPollUrlDiffers() {
+ vm.setPollUrl("http://example.com/a");
+ VerificationMethod other = new VerificationMethod();
+ other.setPollUrl("http://example.com/b");
+ assertThat(vm.equals(other) | other.equals(vm), is(false));
+ }
+
+ @Test
+ void shouldNotBeEqualWhenPollFrequencyDiffers() {
+ vm.setPollFrequency(10);
+ VerificationMethod other = new VerificationMethod();
+ other.setPollFrequency(20);
+ assertThat(vm.equals(other) | other.equals(vm), is(false));
+ }
+
+ @Test
+ void shouldNotBeEqualWhenPollFrequencyUnitsDiffer() {
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+ VerificationMethod other = new VerificationMethod();
+ other.setPollFrequencyUnits(AuthPollFrequencyUnits.SECONDS);
+ assertThat(vm.equals(other) | other.equals(vm), is(false));
+ }
+ }
+
+ @Nested
+ class HashCode {
+
+ @Test
+ void shouldBeEqualForEqualMethods() {
+ vm = createMethod("loggedin", "loggedout");
+ VerificationMethod other = createMethod("loggedin", "loggedout");
+ assertThat(vm.hashCode(), is(equalTo(other.hashCode())));
+ }
+
+ @Test
+ void shouldBeEqualForEqualMethodsWithSamePollSettings() {
+ vm = createMethod("loggedin", "loggedout");
+ vm.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ vm.setPollUrl("http://example.com/poll");
+ vm.setPollData("a=b");
+ vm.setPollHeaders("X-Test: 1");
+ vm.setPollFrequency(10);
+ vm.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+
+ VerificationMethod other = createMethod("loggedin", "loggedout");
+ other.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ other.setPollUrl("http://example.com/poll");
+ other.setPollData("a=b");
+ other.setPollHeaders("X-Test: 1");
+ other.setPollFrequency(10);
+ other.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+
+ assertThat(vm.equals(other), is(true));
+ assertThat(vm.hashCode(), is(equalTo(other.hashCode())));
+ }
+ }
+
+ private static VerificationMethod createMethod(String loggedIn, String loggedOut) {
+ VerificationMethod m = new VerificationMethod();
+ m.setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ m.setLoggedInIndicatorPattern(loggedIn);
+ m.setLoggedOutIndicatorPattern(loggedOut);
+ return m;
+ }
+}
diff --git a/zap/src/test/java/org/zaproxy/zap/extension/authentication/ExtensionAuthenticationUnitTest.java b/zap/src/test/java/org/zaproxy/zap/extension/authentication/ExtensionAuthenticationUnitTest.java
index b6417890e0e..315c702e2a2 100644
--- a/zap/src/test/java/org/zaproxy/zap/extension/authentication/ExtensionAuthenticationUnitTest.java
+++ b/zap/src/test/java/org/zaproxy/zap/extension/authentication/ExtensionAuthenticationUnitTest.java
@@ -80,6 +80,7 @@ void shouldExportAllAuthContextData() {
Context context = new Context(null, 0);
String loggedInIndicator = "logged in";
String loggedOutIndicator = "logged out";
+ String pollMethod = "Method";
String pollUrl = "https://www.example.com/poll";
String pollData = "example-poll-data";
String pollHeaders = "aaa : bbb\\Nccc : ddd";
@@ -87,15 +88,16 @@ void shouldExportAllAuthContextData() {
FormBasedAuthenticationMethodType type = new FormBasedAuthenticationMethodType();
FormBasedAuthenticationMethod method = type.createAuthenticationMethod(0);
- method.setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
- method.setPollUrl(pollUrl);
- method.setPollData(pollData);
- method.setPollHeaders(pollHeaders);
- method.setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
- method.setPollFrequency(pollFreq);
- method.setLoggedInIndicatorPattern(loggedInIndicator);
- method.setLoggedOutIndicatorPattern(loggedOutIndicator);
context.setAuthenticationMethod(method);
+ context.getVerificationMethod().setAuthCheckingStrategy(AuthCheckingStrategy.POLL_URL);
+ context.getVerificationMethod().setPollMethod(pollMethod);
+ context.getVerificationMethod().setPollUrl(pollUrl);
+ context.getVerificationMethod().setPollData(pollData);
+ context.getVerificationMethod().setPollHeaders(pollHeaders);
+ context.getVerificationMethod().setPollFrequencyUnits(AuthPollFrequencyUnits.REQUESTS);
+ context.getVerificationMethod().setPollFrequency(pollFreq);
+ context.getVerificationMethod().setLoggedInIndicatorPattern(loggedInIndicator);
+ context.getVerificationMethod().setLoggedOutIndicatorPattern(loggedOutIndicator);
Configuration config = new ZapXmlConfiguration();
// When
@@ -106,6 +108,9 @@ void shouldExportAllAuthContextData() {
assertThat(
config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_STRATEGY),
is(AuthCheckingStrategy.POLL_URL.name()));
+ assertThat(
+ config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_METHOD),
+ is(pollMethod));
assertThat(
config.getString(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_URL), is(pollUrl));
assertThat(
@@ -131,6 +136,7 @@ void shouldImportAllAuthContextData() throws ConfigurationException {
Context context = new Context(null, 0);
String loggedInIndicator = "logged in";
String loggedOutIndicator = "logged out";
+ String pollMethod = "Method";
String pollUrl = "https://www.example.com/poll";
String pollData = "example-poll-data";
String pollHeaders = "aaa : bbb\\Nccc : ddd";
@@ -141,6 +147,7 @@ void shouldImportAllAuthContextData() throws ConfigurationException {
config.setProperty(
AuthenticationMethod.CONTEXT_CONFIG_AUTH_STRATEGY,
AuthCheckingStrategy.POLL_URL.name());
+ config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_METHOD, pollMethod);
config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_URL, pollUrl);
config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_DATA, pollData);
config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_HEADERS, pollHeaders);
@@ -156,20 +163,28 @@ void shouldImportAllAuthContextData() throws ConfigurationException {
// When
extensionAuthentication.importContextData(context, config);
- AuthenticationMethod method = context.getAuthenticationMethod();
// Then
assertThat(
- method.getClass().getCanonicalName(),
+ context.getAuthenticationMethod().getClass().getCanonicalName(),
is(FormBasedAuthenticationMethod.class.getCanonicalName()));
- assertThat(method.getAuthCheckingStrategy(), is(AuthCheckingStrategy.POLL_URL));
- assertThat(method.getPollUrl(), is(pollUrl));
- assertThat(method.getPollData(), is(pollData));
- assertThat(method.getPollHeaders(), is(pollHeaders));
- assertThat(method.getPollFrequencyUnits(), is(AuthPollFrequencyUnits.REQUESTS));
- assertThat(method.getPollFrequency(), is(pollFreq));
- assertThat(method.getLoggedInIndicatorPattern().toString(), is(loggedInIndicator));
- assertThat(method.getLoggedOutIndicatorPattern().toString(), is(loggedOutIndicator));
+ assertThat(
+ context.getVerificationMethod().getAuthCheckingStrategy(),
+ is(AuthCheckingStrategy.POLL_URL));
+ assertThat(context.getVerificationMethod().getPollMethod(), is(pollMethod));
+ assertThat(context.getVerificationMethod().getPollUrl(), is(pollUrl));
+ assertThat(context.getVerificationMethod().getPollData(), is(pollData));
+ assertThat(context.getVerificationMethod().getPollHeaders(), is(pollHeaders));
+ assertThat(
+ context.getVerificationMethod().getPollFrequencyUnits(),
+ is(AuthPollFrequencyUnits.REQUESTS));
+ assertThat(context.getVerificationMethod().getPollFrequency(), is(pollFreq));
+ assertThat(
+ context.getVerificationMethod().getLoggedInIndicatorPattern().toString(),
+ is(loggedInIndicator));
+ assertThat(
+ context.getVerificationMethod().getLoggedOutIndicatorPattern().toString(),
+ is(loggedOutIndicator));
}
@Test
@@ -189,14 +204,44 @@ void shouldImportContextWithNoPollData() throws ConfigurationException {
// When
extensionAuthentication.importContextData(context, config);
- AuthenticationMethod method = context.getAuthenticationMethod();
// Then
assertThat(
- method.getClass().getCanonicalName(),
+ context.getAuthenticationMethod().getClass().getCanonicalName(),
is(FormBasedAuthenticationMethod.class.getCanonicalName()));
- assertThat(method.getAuthCheckingStrategy(), is(AuthCheckingStrategy.EACH_RESP));
- assertThat(method.getLoggedInIndicatorPattern().toString(), is(loggedInIndicator));
- assertThat(method.getLoggedOutIndicatorPattern().toString(), is(loggedOutIndicator));
+ assertThat(
+ context.getVerificationMethod().getAuthCheckingStrategy(),
+ is(AuthCheckingStrategy.EACH_RESP));
+ assertThat(
+ context.getVerificationMethod().getLoggedInIndicatorPattern().toString(),
+ is(loggedInIndicator));
+ assertThat(
+ context.getVerificationMethod().getLoggedOutIndicatorPattern().toString(),
+ is(loggedOutIndicator));
+ }
+
+ @Test
+ void shouldImportContextWithPollUrlButNoStrategyAsPollUrl() throws ConfigurationException {
+ // Given
+ Context context = new Context(null, 0);
+ String pollUrl = "https://www.example.com/poll";
+ String loggedInIndicator = "logged in";
+
+ Configuration config = new ZapXmlConfiguration();
+ config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_TYPE, 2);
+ config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_POLL_URL, pollUrl);
+ config.setProperty(AuthenticationMethod.CONTEXT_CONFIG_AUTH_LOGGEDIN, loggedInIndicator);
+
+ ExtensionHook hook = new ExtensionHook(Model.getSingleton(), null);
+ extensionAuthentication.hook(hook);
+
+ // When
+ extensionAuthentication.importContextData(context, config);
+
+ // Then
+ assertThat(
+ context.getVerificationMethod().getAuthCheckingStrategy(),
+ is(AuthCheckingStrategy.POLL_URL));
+ assertThat(context.getVerificationMethod().getPollUrl(), is(pollUrl));
}
}
diff --git a/zap/src/test/java/org/zaproxy/zap/model/ContextUnitTest.java b/zap/src/test/java/org/zaproxy/zap/model/ContextUnitTest.java
index 55487289b43..b3282d0ef73 100644
--- a/zap/src/test/java/org/zaproxy/zap/model/ContextUnitTest.java
+++ b/zap/src/test/java/org/zaproxy/zap/model/ContextUnitTest.java
@@ -22,6 +22,8 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
@@ -38,6 +40,7 @@
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.model.SiteMap;
import org.parosproxy.paros.model.SiteNode;
+import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
import org.zaproxy.zap.utils.I18N;
/** Unit test for {@link Context}. */
@@ -178,4 +181,30 @@ void shouldExcludeNodesWithParamsFromContext() {
assertThat(context.isExcluded(endNode), is(true));
assertThat(context.isInContext(endNode), is(false));
}
+
+ @Test
+ void shouldKeepAuthenticationAndVerificationMethodsLinkedWhenDuplicated() {
+ // Given
+ context.getVerificationMethod().setAuthCheckingStrategy(AuthCheckingStrategy.EACH_RESP);
+ context.getVerificationMethod().setLoggedInIndicatorPattern("loggedin");
+ context.getVerificationMethod().setPollUrl("http://example.com/poll");
+
+ // When
+ Context duplicate = context.duplicate();
+
+ // Then
+ assertThat(
+ duplicate.getVerificationMethod(),
+ is(sameInstance(duplicate.getAuthenticationMethod().getVerificationMethod())));
+ assertThat(
+ duplicate.getVerificationMethod(),
+ is(not(sameInstance(context.getVerificationMethod()))));
+ assertThat(
+ duplicate.getVerificationMethod().getAuthCheckingStrategy(),
+ is(AuthCheckingStrategy.EACH_RESP));
+ assertThat(
+ duplicate.getVerificationMethod().getLoggedInIndicatorPattern().pattern(),
+ is("loggedin"));
+ assertThat(duplicate.getVerificationMethod().getPollUrl(), is("http://example.com/poll"));
+ }
}