go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/auth/handlers_test.go (about) 1 // Copyright 2016 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package auth 16 17 import ( 18 "context" 19 "errors" 20 "net/http" 21 "net/http/httptest" 22 "testing" 23 24 "go.chromium.org/luci/server/auth/signing" 25 "go.chromium.org/luci/server/caching" 26 "go.chromium.org/luci/server/router" 27 28 . "github.com/smartystreets/goconvey/convey" 29 . "go.chromium.org/luci/common/testing/assertions" 30 ) 31 32 func withSigner(s signing.Signer) router.MiddlewareChain { 33 return router.NewMiddlewareChain( 34 func(c *router.Context, next router.Handler) { 35 c.Request = c.Request.WithContext(ModifyConfig(c.Request.Context(), func(cfg Config) Config { 36 cfg.Signer = s 37 return cfg 38 })) 39 next(c) 40 }, 41 ) 42 } 43 44 func TestCertificatesHandler(t *testing.T) { 45 t.Parallel() 46 47 call := func(s signing.Signer) (*signing.PublicCertificates, error) { 48 r := router.New() 49 InstallHandlers(r, withSigner(s)) 50 ts := httptest.NewServer(r) 51 defer ts.Close() 52 // Note: there are two contexts. One for outer /certificates call 53 // (this one), and another for /certificates request handler (it is setup 54 // in the middleware chain above). 55 ctx := caching.WithEmptyProcessCache(context.Background()) 56 ctx = ModifyConfig(ctx, func(cfg Config) Config { 57 cfg.AnonymousTransport = func(context.Context) http.RoundTripper { 58 return http.DefaultTransport 59 } 60 return cfg 61 }) 62 return signing.FetchCertificates(ctx, ts.URL+"/auth/api/v1/server/certificates") 63 } 64 65 Convey("Works", t, func() { 66 certs, err := call(&phonySigner{}) 67 So(err, ShouldBeNil) 68 So(len(certs.Certificates), ShouldEqual, 1) 69 }) 70 71 Convey("No signer", t, func() { 72 _, err := call(nil) 73 So(err, ShouldErrLike, "HTTP code (404)") 74 }) 75 76 Convey("Error getting certs", t, func() { 77 _, err := call(&phonySigner{errors.New("fail")}) 78 So(err, ShouldErrLike, "HTTP code (500)") 79 }) 80 } 81 82 func TestServiceInfoHandler(t *testing.T) { 83 t.Parallel() 84 85 Convey("Works", t, func() { 86 r := router.New() 87 signer := &phonySigner{} 88 InstallHandlers(r, withSigner(signer)) 89 90 w := httptest.NewRecorder() 91 req, _ := http.NewRequest("GET", "/auth/api/v1/server/info", nil) 92 r.ServeHTTP(w, req) 93 So(w.Code, ShouldEqual, 200) 94 So(w.Body.String(), ShouldResemble, 95 `{"app_id":"phony-app","app_runtime":"go",`+ 96 `"app_runtime_version":"go1.5.1",`+ 97 `"app_version":"1234-abcdef","service_account_name":`+ 98 `"phony-app-account@example.com"}`+"\n") 99 100 signer.err = errors.New("fail") 101 102 w = httptest.NewRecorder() 103 req, _ = http.NewRequest("GET", "/auth/api/v1/server/info", nil) 104 r.ServeHTTP(w, req) 105 So(w.Code, ShouldEqual, 500) 106 So(w.Body.String(), ShouldResemble, "{\"error\":\"Can't grab service info - fail\"}\n") 107 }) 108 } 109 110 func TestClientIDHandler(t *testing.T) { 111 t.Parallel() 112 113 Convey("Works", t, func() { 114 var clientIDErr error 115 modConfig := router.NewMiddlewareChain( 116 func(c *router.Context, next router.Handler) { 117 c.Request = c.Request.WithContext(ModifyConfig(c.Request.Context(), func(cfg Config) Config { 118 cfg.FrontendClientID = func(context.Context) (string, error) { 119 return "fake-client-id", clientIDErr 120 } 121 return cfg 122 })) 123 next(c) 124 }, 125 ) 126 127 r := router.New() 128 InstallHandlers(r, modConfig) 129 130 w := httptest.NewRecorder() 131 req, _ := http.NewRequest("GET", "/auth/api/v1/server/client_id", nil) 132 r.ServeHTTP(w, req) 133 So(w.Code, ShouldEqual, 200) 134 So(w.Body.String(), ShouldResemble, `{"client_id":"fake-client-id"}`+"\n") 135 136 clientIDErr = errors.New("fail") 137 138 w = httptest.NewRecorder() 139 req, _ = http.NewRequest("GET", "/auth/api/v1/server/client_id", nil) 140 r.ServeHTTP(w, req) 141 So(w.Code, ShouldEqual, 500) 142 So(w.Body.String(), ShouldResemble, "{\"error\":\"Can't grab the client ID - fail\"}\n") 143 }) 144 } 145 146 /// 147 148 type phonySigner struct { 149 err error 150 } 151 152 func (s *phonySigner) SignBytes(ctx context.Context, blob []byte) (string, []byte, error) { 153 if s.err != nil { 154 return "", nil, s.err 155 } 156 return "phonyKey", []byte("signature"), nil 157 } 158 159 func (s *phonySigner) Certificates(ctx context.Context) (*signing.PublicCertificates, error) { 160 if s.err != nil { 161 return nil, s.err 162 } 163 return &signing.PublicCertificates{ 164 Certificates: []signing.Certificate{ 165 { 166 KeyName: "phonyKey", 167 X509CertificatePEM: "phonyPEM", 168 }, 169 }, 170 }, nil 171 } 172 173 func (s *phonySigner) ServiceInfo(ctx context.Context) (*signing.ServiceInfo, error) { 174 if s.err != nil { 175 return nil, s.err 176 } 177 return &signing.ServiceInfo{ 178 AppID: "phony-app", 179 AppRuntime: "go", 180 AppRuntimeVersion: "go1.5.1", 181 AppVersion: "1234-abcdef", 182 ServiceAccountName: "phony-app-account@example.com", 183 }, nil 184 }