1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
14 "git.curoverse.com/arvados.git/sdk/go/auth"
15 "git.curoverse.com/arvados.git/sdk/go/httpserver"
18 var wfRe = regexp.MustCompile(`^/arvados/v1/workflows/([0-9a-z]{5})-[^/]+$`)
20 func (h *Handler) proxyRemoteCluster(w http.ResponseWriter, req *http.Request, next http.Handler) {
21 m := wfRe.FindStringSubmatch(req.URL.Path)
22 if len(m) < 2 || m[1] == h.Cluster.ClusterID {
23 next.ServeHTTP(w, req)
27 remote, ok := h.Cluster.RemoteClusters[remoteID]
29 httpserver.Error(w, "no proxy available for cluster "+remoteID, http.StatusNotFound)
32 scheme := remote.Scheme
36 err := h.saltAuthToken(req, remoteID)
38 httpserver.Error(w, err.Error(), http.StatusBadRequest)
45 RawPath: req.URL.RawPath,
46 RawQuery: req.URL.RawQuery,
48 client := h.secureClient
50 client = h.insecureClient
52 h.proxy.Do(w, req, urlOut, client)
55 // Extract the auth token supplied in req, and replace it with a
56 // salted token for the remote cluster.
57 func (h *Handler) saltAuthToken(req *http.Request, remote string) error {
58 creds := auth.NewCredentials()
59 creds.LoadTokensFromHTTPRequest(req)
60 if len(creds.Tokens) == 0 && req.Header.Get("Content-Type") == "application/x-www-form-encoded" {
61 // Override ParseForm's 10MiB limit by ensuring
62 // req.Body is a *http.maxBytesReader.
63 req.Body = http.MaxBytesReader(nil, req.Body, 1<<28) // 256MiB. TODO: use MaxRequestSize from discovery doc or config.
64 if err := creds.LoadTokensFromHTTPRequestBody(req); err != nil {
67 // Replace req.Body with a buffer that re-encodes the
68 // form without api_token, in case we end up
69 // forwarding the request.
70 if req.PostForm != nil {
71 req.PostForm.Del("api_token")
73 req.Body = ioutil.NopCloser(bytes.NewBufferString(req.PostForm.Encode()))
75 if len(creds.Tokens) == 0 {
78 token, err := auth.SaltToken(creds.Tokens[0], remote)
79 if err == auth.ErrObsoleteToken {
80 // FIXME: If the token exists in our own database,
81 // salt it for the remote. Otherwise, assume it was
82 // issued by the remote, and pass it through
84 token = creds.Tokens[0]
85 } else if err != nil {
88 req.Header.Set("Authorization", "Bearer "+token)
90 // Remove api_token=... from the the query string, in case we
91 // end up forwarding the request.
92 if values, err := url.ParseQuery(req.URL.RawQuery); err != nil {
94 } else if _, ok := values["api_token"]; ok {
95 delete(values, "api_token")
96 req.URL.RawQuery = values.Encode()