mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-19 06:31:47 +00:00
Login page styling/structure for users, screen readers, passwd managers
Also: - Add CSS to login page - Refactor login page HTML and CSS into a new package - New custom CSP headers for the login page, because the requirements are different from the form_post page
This commit is contained in:
@@ -54,7 +54,7 @@ func RequireNumberOfSecretsMatchingLabelSelector(t *testing.T, secrets v1.Secret
|
||||
require.Len(t, storedAuthcodeSecrets.Items, expectedNumberOfSecrets)
|
||||
}
|
||||
|
||||
func RequireSecurityHeadersWithFormPostCSPs(t *testing.T, response *httptest.ResponseRecorder) {
|
||||
func RequireSecurityHeadersWithFormPostPageCSPs(t *testing.T, response *httptest.ResponseRecorder) {
|
||||
// Loosely confirm that the unique CSPs needed for the form_post page were used.
|
||||
cspHeader := response.Header().Get("Content-Security-Policy")
|
||||
require.Contains(t, cspHeader, "script-src '") // loose assertion
|
||||
@@ -66,8 +66,20 @@ func RequireSecurityHeadersWithFormPostCSPs(t *testing.T, response *httptest.Res
|
||||
requireSecurityHeaders(t, response)
|
||||
}
|
||||
|
||||
func RequireSecurityHeadersWithoutFormPostCSPs(t *testing.T, response *httptest.ResponseRecorder) {
|
||||
// Confirm that the unique CSPs needed for the form_post page were NOT used.
|
||||
func RequireSecurityHeadersWithLoginPageCSPs(t *testing.T, response *httptest.ResponseRecorder) {
|
||||
// Loosely confirm that the unique CSPs needed for the login page were used.
|
||||
cspHeader := response.Header().Get("Content-Security-Policy")
|
||||
require.Contains(t, cspHeader, "style-src '") // loose assertion
|
||||
require.NotContains(t, cspHeader, "script-src") // only needed by form_post page
|
||||
require.NotContains(t, cspHeader, "img-src data:") // only needed by form_post page
|
||||
require.NotContains(t, cspHeader, "connect-src *") // only needed by form_post page
|
||||
|
||||
// Also require all the usual security headers.
|
||||
requireSecurityHeaders(t, response)
|
||||
}
|
||||
|
||||
func RequireSecurityHeadersWithoutCustomCSPs(t *testing.T, response *httptest.ResponseRecorder) {
|
||||
// Confirm that the unique CSPs needed for the form_post or login page were NOT used.
|
||||
cspHeader := response.Header().Get("Content-Security-Policy")
|
||||
require.NotContains(t, cspHeader, "script-src")
|
||||
require.NotContains(t, cspHeader, "style-src")
|
||||
@@ -79,7 +91,7 @@ func RequireSecurityHeadersWithoutFormPostCSPs(t *testing.T, response *httptest.
|
||||
}
|
||||
|
||||
func requireSecurityHeaders(t *testing.T, response *httptest.ResponseRecorder) {
|
||||
// Loosely confirm that the generic CSPs were used.
|
||||
// Loosely confirm that the generic default CSPs were used.
|
||||
cspHeader := response.Header().Get("Content-Security-Policy")
|
||||
require.Contains(t, cspHeader, "default-src 'none'")
|
||||
require.Contains(t, cspHeader, "frame-ancestors 'none'")
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.pinniped.dev/internal/here"
|
||||
)
|
||||
|
||||
func ExpectedLoginPageHTML(wantCSS, wantIDPName, wantPostPath, wantEncodedState, wantAlert string) string {
|
||||
alertHTML := ""
|
||||
if wantAlert != "" {
|
||||
alertHTML = fmt.Sprintf("\n"+
|
||||
" <div class=\"form-field\">\n"+
|
||||
" <span class=\"alert\" role=\"alert\" aria-label=\"login error message\">%s</span>\n"+
|
||||
" </div>\n ",
|
||||
wantAlert,
|
||||
)
|
||||
}
|
||||
|
||||
// Note that "role", "aria-*", and "alert" attributes are hints to screen readers.
|
||||
// Also note that some structure and attributes used here are hints to password managers,
|
||||
// see https://support.1password.com/compatible-website-design/.
|
||||
// Please take care when changing the HTML of this form,
|
||||
// and test with a screen reader and password manager after changes.
|
||||
return here.Docf(`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Pinniped</title>
|
||||
<meta charset="UTF-8">
|
||||
<style>%s</style>
|
||||
<link id="favicon" rel="icon"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="box" aria-label="login form" role="main">
|
||||
<div class="form-field">
|
||||
<h1>Log in to %s</h1>
|
||||
</div>
|
||||
%s
|
||||
<form action="%s" method="post">
|
||||
<input type="hidden" name="state" id="state" value="%s">
|
||||
<div class="form-field">
|
||||
<label for="username"><span class="hidden" aria-hidden="true">Username</span></label>
|
||||
<input type="text" name="username" id="username"
|
||||
autocomplete="username" placeholder="Username" required>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label for="password"><span class="hidden" aria-hidden="true">Password</span></label>
|
||||
<input type="password" name="password" id="password"
|
||||
autocomplete="current-password" placeholder="Password" required>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<input type="submit" name="submit" id="submit" value="Log in"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
wantCSS,
|
||||
wantIDPName,
|
||||
alertHTML,
|
||||
wantPostPath,
|
||||
wantEncodedState,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user