Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline CSS for default login and logout page #15303

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -45,6 +45,141 @@ public class FormLoginBeanDefinitionParserTests {

private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/http/FormLoginBeanDefinitionParserTests";

//@formatter:off
public static final String EXPECTED_HTML_HEAD = " <head>\n"
+ " <meta charset=\"utf-8\">\n"
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
+ " <meta name=\"description\" content=\"\">\n"
+ " <meta name=\"author\" content=\"\">\n"
+ " <title>Please sign in</title>\n"
+ " <style>\n"
+ " /* General layout */\n"
+ " body {\n"
+ " font-family: system-ui, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n"
+ " background-color: #eee;\n"
+ " padding: 40px 0;\n"
+ " margin: 0;\n"
+ " line-height: 1.5;\n"
+ " }\n"
+ " \n"
+ " h2 {\n"
+ " margin-top: 0;\n"
+ " margin-bottom: 0.5rem;\n"
+ " font-size: 2rem;\n"
+ " font-weight: 500;\n"
+ " line-height: 2rem;\n"
+ " }\n"
+ " \n"
+ " .content {\n"
+ " margin-right: auto;\n"
+ " margin-left: auto;\n"
+ " padding-right: 15px;\n"
+ " padding-left: 15px;\n"
+ " width: 100%;\n"
+ " box-sizing: border-box;\n"
+ " }\n"
+ " \n"
+ " @media (min-width: 800px) {\n"
+ " .content {\n"
+ " max-width: 760px;\n"
+ " }\n"
+ " }\n"
+ " \n"
+ " /* Components */\n"
+ " a,\n"
+ " a:visited {\n"
+ " text-decoration: none;\n"
+ " color: #06f;\n"
+ " }\n"
+ " \n"
+ " a:hover {\n"
+ " text-decoration: underline;\n"
+ " color: #003c97;\n"
+ " }\n"
+ " \n"
+ " input[type=\"text\"],\n"
+ " input[type=\"password\"] {\n"
+ " height: auto;\n"
+ " width: 100%;\n"
+ " font-size: 1rem;\n"
+ " padding: 0.5rem;\n"
+ " box-sizing: border-box;\n"
+ " }\n"
+ " \n"
+ " button {\n"
+ " padding: 0.5rem 1rem;\n"
+ " font-size: 1.25rem;\n"
+ " line-height: 1.5;\n"
+ " border: none;\n"
+ " border-radius: 0.1rem;\n"
+ " width: 100%;\n"
+ " }\n"
+ " \n"
+ " button.primary {\n"
+ " color: #fff;\n"
+ " background-color: #06f;\n"
+ " }\n"
+ " \n"
+ " .alert {\n"
+ " padding: 0.75rem 1rem;\n"
+ " margin-bottom: 1rem;\n"
+ " line-height: 1.5;\n"
+ " border-radius: 0.1rem;\n"
+ " width: 100%;\n"
+ " box-sizing: border-box;\n"
+ " border-width: 1px;\n"
+ " border-style: solid;\n"
+ " }\n"
+ " \n"
+ " .alert.alert-danger {\n"
+ " color: #6b1922;\n"
+ " background-color: #f7d5d7;\n"
+ " border-color: #eab6bb;\n"
+ " }\n"
+ " \n"
+ " .alert.alert-success {\n"
+ " color: #145222;\n"
+ " background-color: #d1f0d9;\n"
+ " border-color: #c2ebcb;\n"
+ " }\n"
+ " \n"
+ " .screenreader {\n"
+ " position: absolute;\n"
+ " clip: rect(0 0 0 0);\n"
+ " height: 1px;\n"
+ " width: 1px;\n"
+ " padding: 0;\n"
+ " border: 0;\n"
+ " overflow: hidden;\n"
+ " }\n"
+ " \n"
+ " table {\n"
+ " width: 100%;\n"
+ " max-width: 100%;\n"
+ " margin-bottom: 2rem;\n"
+ " }\n"
+ " \n"
+ " .table-striped tr:nth-of-type(2n + 1) {\n"
+ " background-color: #e1e1e1;\n"
+ " }\n"
+ " \n"
+ " td {\n"
+ " padding: 0.75rem;\n"
+ " vertical-align: top;\n"
+ " }\n"
+ " \n"
+ " /* Login / logout layouts */\n"
+ " .login-form,\n"
+ " .logout-form {\n"
+ " max-width: 340px;\n"
+ " padding: 0 15px 15px 15px;\n"
+ " margin: 0 auto 2rem auto;\n"
+ " box-sizing: border-box;\n"
+ " }\n"
+ " </style>\n"
+ " </head>\n";
//@formatter:on

public final SpringTestContext spring = new SpringTestContext(this);

@Autowired
Expand All @@ -56,28 +191,20 @@ public void getLoginWhenAutoConfigThenShowsDefaultLoginPage() throws Exception {
// @formatter:off
String expectedContent = "<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ " <head>\n"
+ " <meta charset=\"utf-8\">\n"
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
+ " <meta name=\"description\" content=\"\">\n"
+ " <meta name=\"author\" content=\"\">\n"
+ " <title>Please sign in</title>\n"
+ " <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"
+ " <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" integrity=\"sha384-oOE/3m0LUMPub4kaC09mrdEhIc+e3exm4xOGxAmuFXhBNF4hcg/6MiAXAf5p0P56\" crossorigin=\"anonymous\"/>\n"
+ " </head>\n"
+ EXPECTED_HTML_HEAD
+ " <body>\n"
+ " <div class=\"container\">\n"
+ " <form class=\"form-signin\" method=\"post\" action=\"/login\">\n"
+ " <h2 class=\"form-signin-heading\">Please sign in</h2>\n"
+ " <div class=\"content\">\n"
+ " <form class=\"login-form\" method=\"post\" action=\"/login\">\n"
+ " <h2>Please sign in</h2>\n"
+ " <p>\n"
+ " <label for=\"username\" class=\"sr-only\">Username</label>\n"
+ " <input type=\"text\" id=\"username\" name=\"username\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n"
+ " <label for=\"username\" class=\"screenreader\">Username</label>\n"
+ " <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"Username\" required autofocus>\n"
+ " </p>\n"
+ " <p>\n"
+ " <label for=\"password\" class=\"sr-only\">Password</label>\n"
+ " <input type=\"password\" id=\"password\" name=\"password\" class=\"form-control\" placeholder=\"Password\" required>\n"
+ " <label for=\"password\" class=\"screenreader\">Password</label>\n"
+ " <input type=\"password\" id=\"password\" name=\"password\" placeholder=\"Password\" required>\n"
+ " </p>\n"
+ " <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n"
+ " <button type=\"submit\" class=\"primary\">Sign in</button>\n"
+ " </form>\n"
+ "</div>\n"
+ "</body></html>";
Expand All @@ -97,28 +224,20 @@ public void getLoginWhenConfiguredWithCustomAttributesThenLoginPageReflects() th
// @formatter:off
String expectedContent = "<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ " <head>\n"
+ " <meta charset=\"utf-8\">\n"
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n"
+ " <meta name=\"description\" content=\"\">\n"
+ " <meta name=\"author\" content=\"\">\n"
+ " <title>Please sign in</title>\n"
+ " <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n"
+ " <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" rel=\"stylesheet\" integrity=\"sha384-oOE/3m0LUMPub4kaC09mrdEhIc+e3exm4xOGxAmuFXhBNF4hcg/6MiAXAf5p0P56\" crossorigin=\"anonymous\"/>\n"
+ " </head>\n"
+ EXPECTED_HTML_HEAD
+ " <body>\n"
+ " <div class=\"container\">\n"
+ " <form class=\"form-signin\" method=\"post\" action=\"/signin\">\n"
+ " <h2 class=\"form-signin-heading\">Please sign in</h2>\n"
+ " <div class=\"content\">\n"
+ " <form class=\"login-form\" method=\"post\" action=\"/signin\">\n"
+ " <h2>Please sign in</h2>\n"
+ " <p>\n"
+ " <label for=\"username\" class=\"sr-only\">Username</label>\n"
+ " <input type=\"text\" id=\"username\" name=\"custom_user\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n"
+ " <label for=\"username\" class=\"screenreader\">Username</label>\n"
+ " <input type=\"text\" id=\"username\" name=\"custom_user\" placeholder=\"Username\" required autofocus>\n"
+ " </p>\n"
+ " <p>\n"
+ " <label for=\"password\" class=\"sr-only\">Password</label>\n"
+ " <input type=\"password\" id=\"password\" name=\"custom_pass\" class=\"form-control\" placeholder=\"Password\" required>\n"
+ " <label for=\"password\" class=\"screenreader\">Password</label>\n"
+ " <input type=\"password\" id=\"password\" name=\"custom_pass\" placeholder=\"Password\" required>\n"
+ " </p>\n"
+ " <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n"
+ " <button type=\"submit\" class=\"primary\">Sign in</button>\n"
+ " </form>\n"
+ "</div>\n"
+ "</body></html>";
Expand Down
3 changes: 3 additions & 0 deletions etc/checkstyle/checkstyle-suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,7 @@

<!-- Lambdas that we can't replace with a method reference because a closure is required -->
<suppress files="BearerTokenAuthenticationFilter\.java" checks="SpringLambda"/>

<!-- CSS content -->
<suppress files="CssUtils\.java" checks="SpringLeadingWhitespace"/>
</suppressions>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -34,6 +34,7 @@
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.security.web.util.CssUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
Expand Down Expand Up @@ -201,33 +202,30 @@ private String generateLoginPageHtml(HttpServletRequest request, boolean loginEr
sb.append(" <meta name=\"description\" content=\"\">\n");
sb.append(" <meta name=\"author\" content=\"\">\n");
sb.append(" <title>Please sign in</title>\n");
sb.append(" <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" "
+ "rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" crossorigin=\"anonymous\">\n");
sb.append(" <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" "
+ "rel=\"stylesheet\" integrity=\"sha384-oOE/3m0LUMPub4kaC09mrdEhIc+e3exm4xOGxAmuFXhBNF4hcg/6MiAXAf5p0P56\" crossorigin=\"anonymous\"/>\n");
sb.append(CssUtils.getCssStyleBlock().indent(4));
sb.append(" </head>\n");
sb.append(" <body>\n");
sb.append(" <div class=\"container\">\n");
sb.append(" <div class=\"content\">\n");
if (this.formLoginEnabled) {
sb.append(" <form class=\"form-signin\" method=\"post\" action=\"" + contextPath
sb.append(" <form class=\"login-form\" method=\"post\" action=\"" + contextPath
+ this.authenticationUrl + "\">\n");
sb.append(" <h2 class=\"form-signin-heading\">Please sign in</h2>\n");
sb.append(" <h2>Please sign in</h2>\n");
sb.append(createError(loginError, errorMsg) + createLogoutSuccess(logoutSuccess) + " <p>\n");
sb.append(" <label for=\"username\" class=\"sr-only\">Username</label>\n");
sb.append(" <label for=\"username\" class=\"screenreader\">Username</label>\n");
sb.append(" <input type=\"text\" id=\"username\" name=\"" + this.usernameParameter
+ "\" class=\"form-control\" placeholder=\"Username\" required autofocus>\n");
+ "\" placeholder=\"Username\" required autofocus>\n");
sb.append(" </p>\n");
sb.append(" <p>\n");
sb.append(" <label for=\"password\" class=\"sr-only\">Password</label>\n");
sb.append(" <label for=\"password\" class=\"screenreader\">Password</label>\n");
sb.append(" <input type=\"password\" id=\"password\" name=\"" + this.passwordParameter
+ "\" class=\"form-control\" placeholder=\"Password\" required>\n");
+ "\" placeholder=\"Password\" required>\n");
sb.append(" </p>\n");
sb.append(createRememberMe(this.rememberMeParameter) + renderHiddenInputs(request));
sb.append(" <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Sign in</button>\n");
sb.append(" <button type=\"submit\" class=\"primary\">Sign in</button>\n");
sb.append(" </form>\n");
}
if (this.oauth2LoginEnabled) {
sb.append("<h2 class=\"form-signin-heading\">Login with OAuth 2.0</h2>");
sb.append("<h2>Login with OAuth 2.0</h2>");
sb.append(createError(loginError, errorMsg));
sb.append(createLogoutSuccess(logoutSuccess));
sb.append("<table class=\"table table-striped\">\n");
Expand All @@ -244,7 +242,7 @@ private String generateLoginPageHtml(HttpServletRequest request, boolean loginEr
sb.append("</table>\n");
}
if (this.saml2LoginEnabled) {
sb.append("<h2 class=\"form-signin-heading\">Login with SAML 2.0</h2>");
sb.append("<h2>Login with SAML 2.0</h2>");
sb.append(createError(loginError, errorMsg));
sb.append(createLogoutSuccess(logoutSuccess));
sb.append("<table class=\"table table-striped\">\n");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -27,6 +27,7 @@
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.core.log.LogMessage;
import org.springframework.security.web.util.CssUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -69,19 +70,15 @@ private void renderLogout(HttpServletRequest request, HttpServletResponse respon
sb.append(" <meta name=\"description\" content=\"\">\n");
sb.append(" <meta name=\"author\" content=\"\">\n");
sb.append(" <title>Confirm Log Out?</title>\n");
sb.append(" <link href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css\" "
+ "rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" "
+ "crossorigin=\"anonymous\">\n");
sb.append(" <link href=\"https://getbootstrap.com/docs/4.0/examples/signin/signin.css\" "
+ "rel=\"stylesheet\" integrity=\"sha384-oOE/3m0LUMPub4kaC09mrdEhIc+e3exm4xOGxAmuFXhBNF4hcg/6MiAXAf5p0P56\" crossorigin=\"anonymous\"/>\n");
sb.append(CssUtils.getCssStyleBlock().indent(4));
sb.append(" </head>\n");
sb.append(" <body>\n");
sb.append(" <div class=\"container\">\n");
sb.append(" <form class=\"form-signin\" method=\"post\" action=\"" + request.getContextPath()
sb.append(" <div class=\"content\">\n");
sb.append(" <form class=\"logout-form\" method=\"post\" action=\"" + request.getContextPath()
+ "/logout\">\n");
sb.append(" <h2 class=\"form-signin-heading\">Are you sure you want to log out?</h2>\n");
sb.append(renderHiddenInputs(request)
+ " <button class=\"btn btn-lg btn-primary btn-block\" type=\"submit\">Log Out</button>\n");
sb.append(" <h2>Are you sure you want to log out?</h2>\n");
sb.append(renderHiddenInputs(request));
sb.append(" <button class=\"primary\" type=\"submit\">Log Out</button>\n");
sb.append(" </form>\n");
sb.append(" </div>\n");
sb.append(" </body>\n");
Expand Down
Loading