8311: Add auth handler tests.
[arvados.git] / services / arv-git-httpd / auth_handler_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "io"
9         "log"
10         "net/http"
11         "net/http/httptest"
12         "net/url"
13         "path/filepath"
14         "strings"
15
16         "git.curoverse.com/arvados.git/sdk/go/arvados"
17         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
18         check "gopkg.in/check.v1"
19 )
20
21 var _ = check.Suite(&AuthHandlerSuite{})
22
23 type AuthHandlerSuite struct{}
24
25 func (s *AuthHandlerSuite) SetUpSuite(c *check.C) {
26         arvadostest.StartAPI()
27 }
28
29 func (s *AuthHandlerSuite) TearDownSuite(c *check.C) {
30         arvadostest.StopAPI()
31 }
32
33 func (s *AuthHandlerSuite) SetUpTest(c *check.C) {
34         arvadostest.ResetEnv()
35         repoRoot, err := filepath.Abs("../api/tmp/git/test")
36         c.Assert(err, check.IsNil)
37         theConfig = &Config{
38                 Client: arvados.Client{
39                         APIHost:  arvadostest.APIHost(),
40                         Insecure: true,
41                 },
42                 Listen:          ":0",
43                 GitCommand:      "/usr/bin/git",
44                 RepoRoot:        repoRoot,
45                 ManagementToken: arvadostest.ManagementToken,
46         }
47 }
48
49 func (s *AuthHandlerSuite) TestPermission(c *check.C) {
50         h := &authHandler{handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
51                 log.Printf("%v", r.URL)
52                 io.WriteString(w, r.URL.Path)
53         })}
54         baseURL, err := url.Parse("http://git.example/")
55         c.Assert(err, check.IsNil)
56         for _, trial := range []struct {
57                 label   string
58                 token   string
59                 pathIn  string
60                 pathOut string
61                 status  int
62         }{
63                 {
64                         label:   "read repo by name",
65                         token:   arvadostest.ActiveToken,
66                         pathIn:  arvadostest.Repository2Name + ".git/git-upload-pack",
67                         pathOut: arvadostest.Repository2UUID + ".git/git-upload-pack",
68                 },
69                 {
70                         label:   "read repo by uuid",
71                         token:   arvadostest.ActiveToken,
72                         pathIn:  arvadostest.Repository2UUID + ".git/git-upload-pack",
73                         pathOut: arvadostest.Repository2UUID + ".git/git-upload-pack",
74                 },
75                 {
76                         label:   "write repo by name",
77                         token:   arvadostest.ActiveToken,
78                         pathIn:  arvadostest.Repository2Name + ".git/git-receive-pack",
79                         pathOut: arvadostest.Repository2UUID + ".git/git-receive-pack",
80                 },
81                 {
82                         label:   "write repo by uuid",
83                         token:   arvadostest.ActiveToken,
84                         pathIn:  arvadostest.Repository2UUID + ".git/git-receive-pack",
85                         pathOut: arvadostest.Repository2UUID + ".git/git-receive-pack",
86                 },
87                 {
88                         label:  "uuid not found",
89                         token:  arvadostest.ActiveToken,
90                         pathIn: strings.Replace(arvadostest.Repository2UUID, "6", "z", -1) + ".git/git-upload-pack",
91                         status: http.StatusNotFound,
92                 },
93                 {
94                         label:  "name not found",
95                         token:  arvadostest.ActiveToken,
96                         pathIn: "nonexistent-bogus.git/git-upload-pack",
97                         status: http.StatusNotFound,
98                 },
99                 {
100                         label:   "read read-only repo",
101                         token:   arvadostest.SpectatorToken,
102                         pathIn:  arvadostest.FooRepoName + ".git/git-upload-pack",
103                         pathOut: arvadostest.FooRepoUUID + "/.git/git-upload-pack",
104                 },
105                 {
106                         label:  "write read-only repo",
107                         token:  arvadostest.SpectatorToken,
108                         pathIn: arvadostest.FooRepoName + ".git/git-receive-pack",
109                         status: http.StatusForbidden,
110                 },
111         } {
112                 c.Logf("trial label: %q", trial.label)
113                 u, err := baseURL.Parse(trial.pathIn)
114                 c.Assert(err, check.IsNil)
115                 resp := httptest.NewRecorder()
116                 req := &http.Request{
117                         Method: "POST",
118                         URL:    u,
119                         Header: http.Header{
120                                 "Authorization": {"Bearer " + trial.token}}}
121                 h.ServeHTTP(resp, req)
122                 if trial.status == 0 {
123                         trial.status = http.StatusOK
124                 }
125                 c.Check(resp.Code, check.Equals, trial.status)
126                 if trial.status < 400 {
127                         if trial.pathOut != "" && !strings.HasPrefix(trial.pathOut, "/") {
128                                 trial.pathOut = "/" + trial.pathOut
129                         }
130                         c.Check(resp.Body.String(), check.Equals, trial.pathOut)
131                 }
132         }
133 }
134
135 func (s *AuthHandlerSuite) TestCORS(c *check.C) {
136         h := &authHandler{}
137
138         // CORS preflight
139         resp := httptest.NewRecorder()
140         req := &http.Request{
141                 Method: "OPTIONS",
142                 Header: http.Header{
143                         "Origin":                        {"*"},
144                         "Access-Control-Request-Method": {"GET"},
145                 },
146         }
147         h.ServeHTTP(resp, req)
148         c.Check(resp.Code, check.Equals, http.StatusOK)
149         c.Check(resp.Header().Get("Access-Control-Allow-Methods"), check.Equals, "GET, POST")
150         c.Check(resp.Header().Get("Access-Control-Allow-Headers"), check.Equals, "Authorization, Content-Type")
151         c.Check(resp.Header().Get("Access-Control-Allow-Origin"), check.Equals, "*")
152         c.Check(resp.Body.String(), check.Equals, "")
153
154         // CORS actual request. Bogus token and path ensure
155         // authHandler responds 4xx without calling our wrapped (nil)
156         // handler.
157         u, err := url.Parse("git.zzzzz.arvadosapi.com/test")
158         c.Assert(err, check.Equals, nil)
159         resp = httptest.NewRecorder()
160         req = &http.Request{
161                 Method: "GET",
162                 URL:    u,
163                 Header: http.Header{
164                         "Origin":        {"*"},
165                         "Authorization": {"OAuth2 foobar"},
166                 },
167         }
168         h.ServeHTTP(resp, req)
169         c.Check(resp.Header().Get("Access-Control-Allow-Origin"), check.Equals, "*")
170 }