diff --git a/server/bindata.go b/server/bindata.go index 2ca4c909864581893a97908855c6deb84cec865e..ab3b88704960ea28a73f07f8605a93e4b433cd58 100644 --- a/server/bindata.go +++ b/server/bindata.go @@ -150,7 +150,7 @@ func staticCssSigninCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/css/signin.css", size: 1071, mode: os.FileMode(420), modTime: time.Unix(1576745577, 0)} + info := bindataFileInfo{name: "static/css/signin.css", size: 1071, mode: os.FileMode(420), modTime: time.Unix(1576748467, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1157,7 +1157,7 @@ func templatesLogin_otpHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "templates/login_otp.html", size: 973, mode: os.FileMode(420), modTime: time.Unix(1576746408, 0)} + info := bindataFileInfo{name: "templates/login_otp.html", size: 973, mode: os.FileMode(420), modTime: time.Unix(1576748467, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1285,7 +1285,7 @@ func templatesLogin_u2fHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "templates/login_u2f.html", size: 925, mode: os.FileMode(420), modTime: time.Unix(1576746452, 0)} + info := bindataFileInfo{name: "templates/login_u2f.html", size: 925, mode: os.FileMode(420), modTime: time.Unix(1576748467, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1394,7 +1394,7 @@ func templatesPageHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "templates/page.html", size: 1476, mode: os.FileMode(420), modTime: time.Unix(1576745577, 0)} + info := bindataFileInfo{name: "templates/page.html", size: 1476, mode: os.FileMode(420), modTime: time.Unix(1576748467, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/server/http_test.go b/server/http_test.go index 8d48948881ef5de7123a3d6b0df3a532f69715b4..b3a5e267f8ea748282089febbce4ebde411477a7 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -233,6 +233,18 @@ func checkLoginOTPPage(t testing.TB, resp *http.Response) { } } +var authFailureRx = regexp.MustCompile(`<p\s*class="error">\s*Authentication failed`) + +func checkAuthFailure(t testing.TB, resp *http.Response) { + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("reading body: %v", err) + } + if !authFailureRx.Match(data) { + t.Fatalf("expected authentication failure, but no errors found:\n%s", string(data)) + } +} + func checkLogoutPage(t testing.TB, resp *http.Response) { if resp.Request.URL.Path != "/logout" { t.Errorf("request path is not /logout (%s)", resp.Request.URL.String()) @@ -362,6 +374,33 @@ func TestHTTP_LoginOTP(t *testing.T) { doPostForm(t, c, httpSrv.URL+"/login/otp", v, checkRedirectToTargetService) } +func TestHTTP_LoginOTP_Fail(t *testing.T) { + tmpdir, httpSrv := startTestHTTPServer(t) + defer os.RemoveAll(tmpdir) + defer httpSrv.Close() + + c := newTestHTTPClient() + + // Simulate an authorization request from a service, expect to + // see the login page. + v := make(url.Values) + v.Set("s", "service.example.com/") + v.Set("d", "https://service.example.com/admin/") + v.Set("n", "averysecretnonce") + doGet(t, c, httpSrv.URL+"/?"+v.Encode(), checkStatusOk, checkLoginPageURL, checkLoginPasswordPage) + + // Attempt to login by submitting the form. We should see the OTP page. + v = make(url.Values) + v.Set("username", "test2fa") + v.Set("password", "password") + doPostForm(t, c, httpSrv.URL+"/login", v, checkStatusOk, checkLoginOTPPage) + + // Submit a bad OTP token, test for failure. + v = make(url.Values) + v.Set("otp", "000000") + doPostForm(t, c, httpSrv.URL+"/login/otp", v, checkAuthFailure) +} + func TestHTTP_LoginOTP_Intermediate404(t *testing.T) { // This test verifies that the session is not disrupted by a // request for a URL that does not exist during a 2FA login