diff --git a/server/bindata.go b/server/bindata.go index d7a1cd7a401b6e69ade1a7ce2f22c69dc9c9ab54..2ca49775e0bdf1364d49db148c642e62aa85af1f 100644 --- a/server/bindata.go +++ b/server/bindata.go @@ -221,7 +221,7 @@ idlogout.get_services = function() { return JSON.parse($('#services').attr('data-services')); }; -idlogout.logout_service = function(idx, service) { +idlogout.logout_service = function(idx, service, successCallback, errorCallback) { console.log('logging out of ' + service.name); $.ajax({ type: 'GET', @@ -233,24 +233,47 @@ idlogout.logout_service = function(idx, service) { success: function() { $('#status_'+idx).addClass('logout-status-ok').text('OK'); console.log('successful logout for ' + service.name); + successCallback(service); }, error: function() { $('#status_'+idx).addClass('logout-status-error').text('ERROR'); console.log('error logging out of ' + service.name); + errorCallback(service); } }); }; -idlogout.logout = function() { +idlogout.logout = function(doneCallback) { var services = idlogout.get_services(); + var remaining = services.length; + var ok = 0, errors = 0; + var maybeDone = function() { + remaining--; + if (remaining <= 0) { + doneCallback(ok, errors); + } + }; $.each(services, function(index, arg) { - idlogout.logout_service(index, arg); + idlogout.logout_service(index, arg, function(svc) { + ok++; + maybeDone(); + }, function(svc) { + errors++; + maybeDone(); + }); }); }; $(function() { $('.logout-status').show(); - idlogout.logout(); + idlogout.logout(function(ok, errors) { + if (errors > 0) { + console.log('there were errors in the logout process, we have reached an unsafe state'); + $('#logout_err').show(); + } else { + $('#logout_ok').show(); + } + }); }); `) @@ -264,7 +287,7 @@ func staticJsLogoutJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "static/js/logout.js", size: 1005, mode: os.FileMode(420), modTime: time.Unix(1535013418, 0)} + info := bindataFileInfo{name: "static/js/logout.js", size: 1726, mode: os.FileMode(420), modTime: time.Unix(1581161609, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -1380,7 +1403,17 @@ var _templatesLogoutHtml = []byte(`{{define "title"}}Sign Out{{end}} {{end}} </tbody></table> - <div id="services" data-services="{{.ServicesJSON}}"></div> + <div id="services" data-services="{{json .Services}}"></div> + + <p id="logout_ok" class="hidden"> + You have been successfully logged out from all services. + </p> + + <p id="logout_err" class="hidden"> + There were some errors in the logout process. + <b>You must quit the browser</b> to avoid leaving open + sessions around! + </p> </div> @@ -1403,7 +1436,7 @@ func templatesLogoutHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "templates/logout.html", size: 1192, mode: os.FileMode(420), modTime: time.Unix(1581160289, 0)} + info := bindataFileInfo{name: "templates/logout.html", size: 1505, mode: os.FileMode(420), modTime: time.Unix(1581161411, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/server/http.go b/server/http.go index 53334e72b671fe6ecd2a5e21ba11120ee338af99..1e156c21c9f8cc9e0abea82deed13379c1c70e96 100644 --- a/server/http.go +++ b/server/http.go @@ -357,12 +357,8 @@ func (h *Server) handleLogout(w http.ResponseWriter, req *http.Request) { URL: serviceLogoutCallback(svc), }) } - - svcJSON, _ := json.Marshal(svcs) // nolint data := map[string]interface{}{ - "Services": svcs, - "ServicesJSON": string(svcJSON), - "IncludeLogoutScripts": true, + "Services": svcs, } // Close the keystore. diff --git a/server/http_test.go b/server/http_test.go index 64e266e1c535ae40ba1c666edacbcf9c57428b72..3c932a5e0bd0c3a4aa3fced5a1823e5d60567023 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "html" "io" "io/ioutil" "net" @@ -271,6 +272,8 @@ func checkAuthFailure(t testing.TB, resp *http.Response) { } } +var logoutServicesRx = regexp.MustCompile(`<div id="services" data-services="([^"]*)">`) + 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()) @@ -281,9 +284,30 @@ func checkLogoutPage(t testing.TB, resp *http.Response) { t.Fatalf("reading body: %v", err) } - if s := string(data); !strings.Contains(s, "Signing you out from all services") { + s := string(data) + // Check signature string of logout page. + if !strings.Contains(s, "Signing you out from all services") { t.Fatalf("not the logout page:\n%s", s) } + // Check presence of fallback service logout URL. + if !strings.Contains(s, "<img src=\"https://service.example.com/sso_logout\"") { + t.Fatalf("logout page does not contain fallback service logout URL:\n%s", s) + } + // Parse the JSON in the services div. + m := logoutServicesRx.FindStringSubmatch(s) + if len(m) == 0 { + t.Fatalf("logout page does not contain JSON-encoded services:\n%s", s) + } + var svcs []logoutServiceInfo + if err := json.Unmarshal([]byte(html.UnescapeString(m[1])), &svcs); err != nil { + t.Fatalf("error decoding JSON services: %v", err) + } + if len(svcs) != 1 { + t.Fatalf("expected 1 service, got %d: %v", len(svcs), svcs) + } + if svcs[0].URL != "https://service.example.com/sso_logout" { + t.Fatalf("bad service logout URL: %s", svcs[0].URL) + } } func extractSSOTicket(dest *string) func(testing.TB, *http.Response) { diff --git a/server/sri_map.go b/server/sri_map.go index 5e43bc574dbc7d0ecf07f025117e115d5efa8bd7..4149e6974b89a16461ac498a3de830ed98a46650 100644 --- a/server/sri_map.go +++ b/server/sri_map.go @@ -5,7 +5,7 @@ var sriMap = map[string]string{ "/static/css/signin.css": "sha384-Eg6R7EvngjwfifE75qnyhwjgbAuSpVLDEWoYuNWpF2Yn7QsqKCvxpFsR5YCrzV5d", "/static/js/bootstrap-4.1.3.min.js": "sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy", "/static/js/jquery-3.3.1.min.js": "sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT", - "/static/js/logout.js": "sha384-lChVngGLNFXetIJTSxc+scDpi1vsBL+7Xa4r2uZpQFP/6Y2z9eCDXe/Y4IUdklRD", + "/static/js/logout.js": "sha384-XfIFkQSluN5TZzrogxlZGauRX7e9Do42s6Y/Cx6RX9qcKtgw0FGSbEpPbCqQbFnb", "/static/js/popper-1.14.3.min.js": "sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49", "/static/js/u2f-api.js": "sha384-9ChevE6pp8ArGK03HgolnFjZbF3webZQtYkwcabzbcI28Lx1/2x2j2fbaAWD4cgR", "/static/js/u2f.js": "sha384-7zZy25ajTABErGlCQgcyRDpQDS9QVZv9o+95IfvCjWftQe20f411F1a39Ge5xmCe", diff --git a/server/static/js/logout.js b/server/static/js/logout.js index b3b8a4a795332c4ef4ac6c6bb662585889c226eb..e4654871be4e615ba25ab8a300db992ab0dbc3ee 100644 --- a/server/static/js/logout.js +++ b/server/static/js/logout.js @@ -4,7 +4,7 @@ idlogout.get_services = function() { return JSON.parse($('#services').attr('data-services')); }; -idlogout.logout_service = function(idx, service) { +idlogout.logout_service = function(idx, service, successCallback, errorCallback) { console.log('logging out of ' + service.name); $.ajax({ type: 'GET', @@ -16,22 +16,45 @@ idlogout.logout_service = function(idx, service) { success: function() { $('#status_'+idx).addClass('logout-status-ok').text('OK'); console.log('successful logout for ' + service.name); + successCallback(service); }, error: function() { $('#status_'+idx).addClass('logout-status-error').text('ERROR'); console.log('error logging out of ' + service.name); + errorCallback(service); } }); }; -idlogout.logout = function() { +idlogout.logout = function(doneCallback) { var services = idlogout.get_services(); + var remaining = services.length; + var ok = 0, errors = 0; + var maybeDone = function() { + remaining--; + if (remaining <= 0) { + doneCallback(ok, errors); + } + }; $.each(services, function(index, arg) { - idlogout.logout_service(index, arg); + idlogout.logout_service(index, arg, function(svc) { + ok++; + maybeDone(); + }, function(svc) { + errors++; + maybeDone(); + }); }); }; $(function() { $('.logout-status').show(); - idlogout.logout(); + idlogout.logout(function(ok, errors) { + if (errors > 0) { + console.log('there were errors in the logout process, we have reached an unsafe state'); + $('#logout_err').show(); + } else { + $('#logout_ok').show(); + } + }); }); diff --git a/server/templates/logout.html b/server/templates/logout.html index 066e40e14b0901b9129373c261fea9f04a7000a3..3af52f2d2ae7c3bc9cc8c2896a7824f2b59d687a 100644 --- a/server/templates/logout.html +++ b/server/templates/logout.html @@ -38,7 +38,17 @@ {{end}} </tbody></table> - <div id="services" data-services="{{.ServicesJSON}}"></div> + <div id="services" data-services="{{json .Services}}"></div> + + <p id="logout_ok" class="hidden"> + You have been successfully logged out from all services. + </p> + + <p id="logout_err" class="hidden"> + There were some errors in the logout process. + <b>You must quit the browser</b> to avoid leaving open + sessions around! + </p> </div>