Skip to content
Snippets Groups Projects
Commit 2a19d327 authored by ale's avatar ale Committed by godog
Browse files

Return 404 for unauthenticated requests to URLs that do not exist

This avoids browsers messing up the session state (given that /login
calls session.Reset) with requests to various kinds of well-known URLs
that might not exist.

Also add an integration test for a server with non-nil URL prefix.
parent 543690f4
No related branches found
No related tags found
No related merge requests found
......@@ -142,6 +142,12 @@ func checkStatusOk(t testing.TB, resp *http.Response) {
}
}
func checkStatusNotFound(t testing.TB, resp *http.Response) {
if resp.StatusCode != 404 {
t.Fatalf("expected status 404, got %s", resp.Status)
}
}
func checkRedirectToTargetService(t testing.TB, resp *http.Response) {
if resp.StatusCode != 302 {
t.Fatalf("expected status 302, got %s", resp.Status)
......@@ -180,10 +186,13 @@ func checkTargetSSOTicket(config *Config) func(testing.TB, *http.Response) {
var usernameFieldRx = regexp.MustCompile(`<input[^>]*name="username"`)
func checkLoginPasswordPage(t testing.TB, resp *http.Response) {
func checkLoginPageURL(t testing.TB, resp *http.Response) {
if resp.Request.URL.Path != "/login" {
t.Errorf("request path is not /login (%s)", resp.Request.URL.String())
}
}
func checkLoginPasswordPage(t testing.TB, resp *http.Response) {
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("reading body: %v", err)
......@@ -314,6 +323,10 @@ func TestHTTP_LoginOTP(t *testing.T) {
v.Set("password", "password")
doPostForm(t, httpSrv, c, "/login", v, checkStatusOk, checkLoginOTPPage)
// Make a request for a URL that does not exist, browsers might do this
// for a number of reasons.
doGet(t, c, httpSrv.URL+"/apple-iphone-special-icon.ico", checkStatusNotFound)
// Submit the correct OTP token. We expect the result to be a
// 302 redirect to the target service.
v = make(url.Values)
......
package server
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"git.autistici.org/id/go-sso/httpsso"
"github.com/gorilla/securecookie"
)
// Create a SSO-wrapped service.
func createTestProtectedService(t testing.TB, serverURL, tmpdir string) *httptest.Server {
ssoPubKey, err := ioutil.ReadFile(filepath.Join(tmpdir, "public"))
if err != nil {
t.Fatalf("oops, can't read sso public key: %v", err)
}
w, err := httpsso.NewSSOWrapper(
serverURL,
ssoPubKey,
"example.com",
securecookie.GenerateRandomKey(64),
securecookie.GenerateRandomKey(32),
0,
)
if err != nil {
t.Fatalf("NewSSOWrapper(): %v", err)
}
h := w.Wrap(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write([]byte("OK")) // nolint
}), "service.example.com/", nil)
return httptest.NewTLSServer(h)
}
func startTestHTTPServerAndApp(t testing.TB) (string, *httptest.Server, *httptest.Server) {
tmpdir, _ := ioutil.TempDir("", "")
config := testConfig(t, tmpdir, "")
srv := createTestHTTPServer(t, config)
app := createTestProtectedService(t, "https://login.example.com/", tmpdir)
return tmpdir, srv, app
}
func startTestHTTPServerWithPrefixAndApp(t testing.TB) (string, *httptest.Server, *httptest.Server) {
tmpdir, _ := ioutil.TempDir("", "")
config := testConfig(t, tmpdir, "")
config.URLPrefix = "/sso"
srv := createTestHTTPServer(t, config)
app := createTestProtectedService(t, "https://login.example.com/sso", tmpdir)
return tmpdir, srv, app
}
func checkLoginPageURLWithPrefix(t testing.TB, resp *http.Response) {
if resp.Request.URL.Path != "/sso/login" {
t.Errorf("request path is not /sso/login (%s)", resp.Request.URL.String())
}
}
func checkIsProtectedService(t testing.TB, resp *http.Response) {
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("reading body: %v", err)
}
if s := string(data); s != "OK" {
t.Fatalf("not the target application, response body='%s'", s)
}
}
func checkLogoutPageHasLinks(t testing.TB, resp *http.Response) {
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("reading body: %v", err)
}
logoutURL := "https://service.example.com/sso_logout"
if sdata := string(data); !strings.Contains(sdata, logoutURL) {
t.Fatalf("service logout URL not found in logout page:\n%s", sdata)
}
}
func addrFromURL(s string) string {
u, _ := url.Parse(s)
if !strings.Contains(u.Host, ":") {
return u.Host + ":443"
}
return u.Host
}
// The integration test spins up an actual service and verifies the
// interaction between it and the login application, using DNS-level
// overrides to ensure proper name validation.
func TestIntegration(t *testing.T) {
tmpdir, srv, app := startTestHTTPServerAndApp(t)
defer os.RemoveAll(tmpdir)
defer srv.Close()
defer app.Close()
c := makeHTTPClient(map[string]string{
"login.example.com:443": addrFromURL(srv.URL),
"service.example.com:443": addrFromURL(app.URL),
}, true)
doGet(t, c, "https://service.example.com/", checkStatusOk, checkLoginPageURL, checkLoginPasswordPage)
v := make(url.Values)
v.Set("username", "testuser")
v.Set("password", "password")
doPostForm(t, c, "https://login.example.com/login", v, checkStatusOk, checkIsProtectedService)
// Now attempt to logout, and verify that we can't access the service anymore.
doGet(t, c, "https://login.example.com/logout", checkStatusOk, checkLogoutPageHasLinks)
doGet(t, c, "https://service.example.com/sso_logout", checkStatusOk)
doGet(t, c, "https://service.example.com/", checkStatusOk, checkLoginPageURL, checkLoginPasswordPage)
}
// Same test as above, but the server application has a URL prefix.
func TestIntegration_WithURLPrefix(t *testing.T) {
tmpdir, srv, app := startTestHTTPServerWithPrefixAndApp(t)
defer os.RemoveAll(tmpdir)
defer srv.Close()
defer app.Close()
c := makeHTTPClient(map[string]string{
"login.example.com:443": addrFromURL(srv.URL),
"service.example.com:443": addrFromURL(app.URL),
}, true)
doGet(t, c, "https://service.example.com/", checkStatusOk, checkLoginPageURLWithPrefix, checkLoginPasswordPage)
v := make(url.Values)
v.Set("username", "testuser")
v.Set("password", "password")
doPostForm(t, c, "https://login.example.com/sso/login", v, checkStatusOk, checkIsProtectedService)
// Now attempt to logout, and verify that we can't access the service anymore.
doGet(t, c, "https://login.example.com/sso/logout", checkStatusOk, checkLogoutPageHasLinks)
doGet(t, c, "https://service.example.com/sso_logout", checkStatusOk)
doGet(t, c, "https://service.example.com/", checkStatusOk, checkLoginPageURLWithPrefix, checkLoginPasswordPage)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment