X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/5a76116145b95da344f9fd2c7d3a51be2c4abc68..cb73538f0851cea402a0544861966c2515a24b5b:/services/keep-web/handler.go diff --git a/services/keep-web/handler.go b/services/keep-web/handler.go index 4625602fc2..123c4fe34d 100644 --- a/services/keep-web/handler.go +++ b/services/keep-web/handler.go @@ -293,12 +293,18 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) { reqTokens = auth.CredentialsFromRequest(r).Tokens } - formToken := r.FormValue("api_token") + r.ParseForm() origin := r.Header.Get("Origin") cors := origin != "" && !strings.HasSuffix(origin, "://"+r.Host) safeAjax := cors && (r.Method == http.MethodGet || r.Method == http.MethodHead) - safeAttachment := attachment && r.URL.Query().Get("api_token") == "" - if formToken == "" { + // Important distinction: safeAttachment checks whether api_token exists + // as a query parameter. haveFormTokens checks whether api_token exists + // as request form data *or* a query parameter. Different checks are + // necessary because both the request disposition and the location of + // the API token affect whether or not the request needs to be + // redirected. The different branch comments below explain further. + safeAttachment := attachment && !r.URL.Query().Has("api_token") + if formTokens, haveFormTokens := r.Form["api_token"]; !haveFormTokens { // No token to use or redact. } else if safeAjax || safeAttachment { // If this is a cross-origin request, the URL won't @@ -313,7 +319,9 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) { // form?" problem, so provided the token isn't // embedded in the URL, there's no reason to do // redirect-with-cookie in this case either. - reqTokens = append(reqTokens, formToken) + for _, tok := range formTokens { + reqTokens = append(reqTokens, tok) + } } else if browserMethod[r.Method] { // If this is a page view, and the client provided a // token via query string or POST body, we must put @@ -411,16 +419,20 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) { // collection id is outside scope of supplied // token tokenScopeProblem = true + sess.Release() continue } else if os.IsNotExist(err) { // collection does not exist or is not // readable using this token + sess.Release() continue } else if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) + sess.Release() return } defer f.Close() + defer sess.Release() collectionDir, sessionFS, session, tokenUser = f, fs, sess, user break @@ -772,7 +784,7 @@ func applyContentDispositionHdr(w http.ResponseWriter, r *http.Request, filename } func (h *handler) seeOtherWithCookie(w http.ResponseWriter, r *http.Request, location string, credentialsOK bool) { - if formToken := r.FormValue("api_token"); formToken != "" { + if formTokens, haveFormTokens := r.Form["api_token"]; haveFormTokens { if !credentialsOK { // It is not safe to copy the provided token // into a cookie unless the current vhost @@ -793,13 +805,19 @@ func (h *handler) seeOtherWithCookie(w http.ResponseWriter, r *http.Request, loc // bar, and in the case of a POST request to avoid // raising warnings when the user refreshes the // resulting page. - http.SetCookie(w, &http.Cookie{ - Name: "arvados_api_token", - Value: auth.EncodeTokenCookie([]byte(formToken)), - Path: "/", - HttpOnly: true, - SameSite: http.SameSiteLaxMode, - }) + for _, tok := range formTokens { + if tok == "" { + continue + } + http.SetCookie(w, &http.Cookie{ + Name: "arvados_api_token", + Value: auth.EncodeTokenCookie([]byte(tok)), + Path: "/", + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + }) + break + } } // Propagate query parameters (except api_token) from