14262: Move the context deadline to the top of the handler stack
[arvados.git] / lib / controller / fed_containers.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package controller
6
7 import (
8         "bytes"
9         "encoding/json"
10         "fmt"
11         "io/ioutil"
12         "net/http"
13
14         "git.curoverse.com/arvados.git/sdk/go/auth"
15         "git.curoverse.com/arvados.git/sdk/go/httpserver"
16 )
17
18 func remoteContainerRequestCreate(
19         h *genericFederatedRequestHandler,
20         effectiveMethod string,
21         clusterId *string,
22         uuid string,
23         remainder string,
24         w http.ResponseWriter,
25         req *http.Request) bool {
26
27         if effectiveMethod != "POST" || uuid != "" || remainder != "" ||
28                 *clusterId == "" || *clusterId == h.handler.Cluster.ClusterID {
29                 return false
30         }
31
32         if req.Header.Get("Content-Type") != "application/json" {
33                 httpserver.Error(w, "Expected Content-Type: application/json, got "+req.Header.Get("Content-Type"), http.StatusBadRequest)
34                 return true
35         }
36
37         originalBody := req.Body
38         defer originalBody.Close()
39         var request map[string]interface{}
40         err := json.NewDecoder(req.Body).Decode(&request)
41         if err != nil {
42                 httpserver.Error(w, err.Error(), http.StatusBadRequest)
43                 return true
44         }
45
46         crString, ok := request["container_request"].(string)
47         if ok {
48                 var crJson map[string]interface{}
49                 err := json.Unmarshal([]byte(crString), &crJson)
50                 if err != nil {
51                         httpserver.Error(w, err.Error(), http.StatusBadRequest)
52                         return true
53                 }
54
55                 request["container_request"] = crJson
56         }
57
58         containerRequest, ok := request["container_request"].(map[string]interface{})
59         if !ok {
60                 // Use toplevel object as the container_request object
61                 containerRequest = request
62         }
63
64         // If runtime_token is not set, create a new token
65         if _, ok := containerRequest["runtime_token"]; !ok {
66                 // First make sure supplied token is valid.
67                 creds := auth.NewCredentials()
68                 creds.LoadTokensFromHTTPRequest(req)
69
70                 currentUser, err := h.handler.validateAPItoken(req, creds.Tokens[0])
71                 if err != nil {
72                         httpserver.Error(w, err.Error(), http.StatusForbidden)
73                         return true
74                 }
75
76                 if len(currentUser.Authorization.Scopes) != 1 || currentUser.Authorization.Scopes[0] != "all" {
77                         httpserver.Error(w, "Token scope is not [all]", http.StatusForbidden)
78                         return true
79                 }
80
81                 // Must be home cluster for this authorization
82                 if currentUser.Authorization.UUID[0:5] == h.handler.Cluster.ClusterID {
83                         newtok, err := h.handler.createAPItoken(req, currentUser.UUID, nil)
84                         if err != nil {
85                                 httpserver.Error(w, err.Error(), http.StatusForbidden)
86                                 return true
87                         }
88                         containerRequest["runtime_token"] = newtok.TokenV2()
89                 }
90         }
91
92         newbody, err := json.Marshal(request)
93         buf := bytes.NewBuffer(newbody)
94         req.Body = ioutil.NopCloser(buf)
95         req.ContentLength = int64(buf.Len())
96         req.Header.Set("Content-Length", fmt.Sprintf("%v", buf.Len()))
97
98         resp, err := h.handler.remoteClusterRequest(*clusterId, req)
99         h.handler.proxy.ForwardResponse(w, resp, err)
100         return true
101 }