/* * Copyright 2016 the original author or authors. * * 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 io.restassured.authentication; import io.restassured.config.LogConfig; import io.restassured.filter.log.LogDetail; import org.apache.commons.lang3.StringUtils; import static io.restassured.internal.assertion.AssertParameter.notNull; import static java.lang.String.format; /** * Configuration of form authentication to correctly identify which form that contains the username and password * and the action of the form. */ public class FormAuthConfig { private final String formAction; private final String userInputTagName; private final String passwordInputTagName; private final LogConfig logConfig; private final LogDetail logDetail; private final String csrfFieldName; private final boolean autoDetectCsrfFieldName; private final boolean sendCsrfTokenAsFormParam; /** * Create a form auth config with a pre-defined form action, username input tag, password input tag. * E.g. let's say that the login form on your login page looks like this: * <pre> * <form action="/j_spring_security_check"> * <label for="j_username">Username</label> * <input type="text" name="j_username" id="j_username"/> * <br/> * <label for="j_password">Password</label> * <input type="password" name="j_password" id="j_password"/> * <br/> * <input type='checkbox' name='_spring_security_remember_me'/> Remember me on this computer. * <br/> * <input type="submit" value="Login"/> * </form> * </pre> * <p/> * This means that <code>formAction</code> should be set to <code>/j_spring_security_check</code>, <code>userNameInputTagName</code> * should be set to <code>j_username</code> and <code>passwordInputTagName</code> should be set to <code>j_password</code>. * * @param formAction The action of the form * @param userNameInputTagName The name of the username input tag in the login form * @param passwordInputTagName The name of the password input tag in the login form */ public FormAuthConfig(String formAction, String userNameInputTagName, String passwordInputTagName) { this(formAction, userNameInputTagName, passwordInputTagName, null, null, null, false, true); } /** * Creates a new empty {@link FormAuthConfig}. */ public FormAuthConfig() { this(null, null, null); } private FormAuthConfig(String formAction, String userNameInputTagName, String passwordInputTagName, LogDetail logDetail, LogConfig logConfig, String csrfFieldName, boolean autoDetectCsrfFieldName, boolean sendCsrfTokenAsFormParam) { this.formAction = formAction; this.userInputTagName = userNameInputTagName; this.passwordInputTagName = passwordInputTagName; this.logDetail = logDetail; this.logConfig = logConfig; this.csrfFieldName = csrfFieldName; this.autoDetectCsrfFieldName = autoDetectCsrfFieldName; this.sendCsrfTokenAsFormParam = sendCsrfTokenAsFormParam; } /** * @return A predefined form authentication config for default Spring Security configuration (tested in version 3.0.5) (no CSRF detection). */ public static FormAuthConfig springSecurity() { return new FormAuthConfig("/j_spring_security_check", "j_username", "j_password"); } /** * Enable Cross-site request forgery (csrf) support when using form authentication by including the csrf value of the input field with the specified name. * For example if the login page looks like this: * <pre> * <html> * <head> * <title>Login</title> * </head> * <body> * <form action="j_spring_security_check_with_csrf" method="POST"> * <table> * <tr> * <td>User:&nbsp;</td> * <td><input type="text" name="j_username"></td> * </tr> * <tr> * <td>Password:</td> * <td><input type="password" name="j_password"></td> * </tr> * <tr> * <td colspan="2"><input name="submit" type="submit"/></td> * </tr> * </table> * <input type="hidden" name="_csrf" value="8adf2ea1-b246-40aa-8e13-a85fb7914341"/> * </form> * </body> * </html> * </pre> * The csrf field name is called <code>_csrf</code>. * <p/> * <b>Important:</b> When enabling csrf support then REST Assured <b>must always</b> make an additional request to the server in order to * be able to include in the csrf value which will slow down the tests. * * @param fieldName The csrf field name as specified in the login page. * @return A new FormAuthConfig instance. * @see #withAutoDetectionOfCsrf() */ public FormAuthConfig withCsrfFieldName(String fieldName) { notNull(fieldName, "CSRF field name"); if (autoDetectCsrfFieldName) { throw new IllegalStateException("Cannot defined a CSRF field name since the CSRF field name has been marked as auto-detected."); } return new FormAuthConfig(formAction, userInputTagName, passwordInputTagName, logDetail, logConfig, fieldName, false, sendCsrfTokenAsFormParam); } /** * @return Configure form authentication to send the csrf token in a header. */ public FormAuthConfig sendCsrfTokenAsHeader() { return new FormAuthConfig(formAction, userInputTagName, passwordInputTagName, logDetail, logConfig, csrfFieldName, autoDetectCsrfFieldName, false); } /** * @return Configure form authentication to send the csrf token as a form parameter (default setting). */ public FormAuthConfig sendCsrfTokenAsFormParam() { return new FormAuthConfig(formAction, userInputTagName, passwordInputTagName, logDetail, logConfig, csrfFieldName, autoDetectCsrfFieldName, true); } /** * Enable Cross-site request forgery (csrf) support when using form authentication by automatically trying to find the name and value of the csrf input field. * For example if the login page looks like this: * <pre> * <html> * <head> * <title>Login</title> * </head> * <body> * <form action="j_spring_security_check_with_csrf" method="POST"> * <table> * <tr> * <td>User:&nbsp;</td> * <td><input type="text" name="j_username"></td> * </tr> * <tr> * <td>Password:</td> * <td><input type="password" name="j_password"></td> * </tr> * <tr> * <td colspan="2"><input name="submit" type="submit"/></td> * </tr> * </table> * <input type="hidden" name="_csrf" value="8adf2ea1-b246-40aa-8e13-a85fb7914341"/> * </form> * </body> * </html> * </pre> * The csrf field name is called <code>_csrf</code> and REST Assured will autodetect its name since the field name is the only <code>hidden</code> field on this page. * If auto-detection fails you can consider using {@link #withCsrfFieldName(String)}. * <p/> * <b>Important:</b> When enabling csrf support then REST Assured <b>must always</b> make an additional request to the server in order to * be able to include in the csrf value which will slow down the tests. * * @return A new FormAuthConfig instance. * @see #withCsrfFieldName(String) */ public FormAuthConfig withAutoDetectionOfCsrf() { if (hasCsrfFieldName()) { throw new IllegalStateException(format("Cannot use auto-detection of CSRF field name since a CSRF field name was already defined as '%s'", csrfFieldName)); } return new FormAuthConfig(formAction, userInputTagName, passwordInputTagName, logDetail, logConfig, csrfFieldName, true, sendCsrfTokenAsFormParam); } /** * Enables logging with log level {@link LogDetail#ALL} of the request made to authenticate using * form authentication. Both the request and the response is logged. * * @return A new FormAuthConfig instance. */ public FormAuthConfig withLoggingEnabled() { return withLoggingEnabled(LogDetail.ALL); } /** * Enables logging with the supplied logDetail of the request made to authenticate using form authentication. * Both the request and the response is logged. * * @return A new FormAuthConfig instance. */ public FormAuthConfig withLoggingEnabled(LogDetail logDetail) { return withLoggingEnabled(logDetail, new LogConfig()); } /** * Enables logging with log level {@link LogDetail#ALL} of the request made to authenticate using * form authentication using the specified {@link LogConfig}. Both the request and the response is logged. * * @return A new FormAuthConfig instance. */ public FormAuthConfig withLoggingEnabled(LogConfig logConfig) { return withLoggingEnabled(LogDetail.ALL, logConfig); } /** * Enables logging with the supplied log detail of the request made to authenticate using form authentication using the * specified {@link LogConfig}. Both the request and the response is logged. * * @return A new FormAuthConfig instance. */ public FormAuthConfig withLoggingEnabled(LogDetail logDetail, LogConfig logConfig) { notNull(logDetail, LogDetail.class); notNull(logConfig, LogConfig.class); return new FormAuthConfig(formAction, userInputTagName, passwordInputTagName, logDetail, logConfig, csrfFieldName, autoDetectCsrfFieldName, sendCsrfTokenAsFormParam); } /** * Creates a new empty {@link FormAuthConfig}. * * @return A new FormAuthConfig instance. */ public static FormAuthConfig formAuthConfig() { return new FormAuthConfig(null, null, null); } /** * Syntactic sugar * * @return The same FormAuthConfig instance */ public FormAuthConfig and() { return this; } public String getFormAction() { return formAction; } public String getUserInputTagName() { return userInputTagName; } /** * @return The password input tag name or <code>null</code> if undefined */ public String getPasswordInputTagName() { return passwordInputTagName; } /** * @return The logging configuration */ public LogConfig getLogConfig() { return logConfig; } /** * @return <code>true</code> if logging is enabled or <code>false</code> otherwise. */ public boolean isLoggingEnabled() { return logConfig != null && logDetail != null; } /** * @return The specified log detail or <code>null</code> if undefined */ public LogDetail getLogDetail() { return logDetail; } /** * @return The specified csrf field name or <code>null</code> if undefined */ public String getCsrfFieldName() { return csrfFieldName; } /** * @return <code>true</code> if csrf field name is defined or <code>false</code> otherwise. */ public boolean hasCsrfFieldName() { return StringUtils.isNotBlank(csrfFieldName); } /** * @return <code>true</code> if auto detection of csrf field name is enabled, <code>false</code> otherwise. */ public boolean isAutoDetectCsrfFieldName() { return autoDetectCsrfFieldName; } /** * @return <code>true</code> if the user input tag name is defined or <code>false</code> otherwise. */ public boolean hasUserInputTagName() { return StringUtils.isNotBlank(userInputTagName); } /** * @return <code>true</code> if the password input tag name is defined or <code>false</code> otherwise. */ public boolean hasPasswordInputTagName() { return StringUtils.isNotBlank(passwordInputTagName); } /** * @return <code>true</code> if the form action is defined or <code>false</code> otherwise. */ public boolean hasFormAction() { return StringUtils.isNotBlank(formAction); } /** * @return <code>true</code> if the {@link FormAuthConfig} instance contains settings that require REST Assured to make a request to the server before applying form authentication, <code>false</code> otherwise. */ public boolean requiresParsingOfLoginPage() { return !hasFormAction() || !hasUserInputTagName() || !hasPasswordInputTagName() || isAutoDetectCsrfFieldName() || hasCsrfFieldName(); } /** * @return <code>true</code> if the csrf token should be sent as a form param or <code>false</code> if it's sent as a header. */ public boolean shouldSendCsrfTokenAsFormParam() { return sendCsrfTokenAsFormParam; } }