| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package auth | |
| 6 | |
| 7 import ( | |
| 8 "fmt" | |
| 9 "net/http" | |
| 10 "net/http/httptest" | |
| 11 "net/url" | |
| 12 "testing" | |
| 13 | |
| 14 "golang.org/x/net/context" | |
| 15 | |
| 16 "github.com/luci/luci-go/common/errors" | |
| 17 "github.com/luci/luci-go/server/auth/identity" | |
| 18 "github.com/luci/luci-go/server/router" | |
| 19 . "github.com/smartystreets/goconvey/convey" | |
| 20 ) | |
| 21 | |
| 22 func TestContext(t *testing.T) { | |
| 23 t.Parallel() | |
| 24 | |
| 25 Convey("Works", t, func() { | |
| 26 c := context.Background() | |
| 27 | |
| 28 So(getAuthenticator(c), ShouldBeNil) | |
| 29 _, err := LoginURL(c, "dest") | |
| 30 So(err, ShouldEqual, ErrNoUsersAPI) | |
| 31 _, err = LogoutURL(c, "dest") | |
| 32 So(err, ShouldEqual, ErrNoUsersAPI) | |
| 33 | |
| 34 // Authenticator without UsersAPI. | |
| 35 c = SetAuthenticator(c, Authenticator{noUserAPI{}}) | |
| 36 | |
| 37 So(getAuthenticator(c), ShouldNotBeNil) | |
| 38 _, err = LoginURL(c, "dest") | |
| 39 So(err, ShouldEqual, ErrNoUsersAPI) | |
| 40 _, err = LogoutURL(c, "dest") | |
| 41 So(err, ShouldEqual, ErrNoUsersAPI) | |
| 42 | |
| 43 // Authenticator with UsersAPI. | |
| 44 c = SetAuthenticator(c, Authenticator{fakeMethod{}}) | |
| 45 | |
| 46 So(getAuthenticator(c), ShouldNotBeNil) | |
| 47 dest, err := LoginURL(c, "dest") | |
| 48 So(err, ShouldBeNil) | |
| 49 So(dest, ShouldEqual, "http://login_url?r=dest") | |
| 50 dest, err = LogoutURL(c, "dest") | |
| 51 So(err, ShouldBeNil) | |
| 52 So(dest, ShouldEqual, "http://logout_url?r=dest") | |
| 53 }) | |
| 54 | |
| 55 } | |
| 56 | |
| 57 func TestContextAuthenticate(t *testing.T) { | |
| 58 t.Parallel() | |
| 59 | |
| 60 call := func(c context.Context, m router.MiddlewareChain, h router.Handl
er) *httptest.ResponseRecorder { | |
| 61 req, err := http.NewRequest("GET", "http://example.com/foo", nil
) | |
| 62 So(err, ShouldBeNil) | |
| 63 w := httptest.NewRecorder() | |
| 64 router.RunMiddleware(&router.Context{ | |
| 65 Context: c, | |
| 66 Writer: w, | |
| 67 Request: req, | |
| 68 }, m, h) | |
| 69 return w | |
| 70 } | |
| 71 | |
| 72 handler := func(c *router.Context) { | |
| 73 fmt.Fprintf(c.Writer, "%s", CurrentIdentity(c.Context)) | |
| 74 } | |
| 75 | |
| 76 Convey("Not configured", t, func() { | |
| 77 rr := call(context.Background(), router.NewMiddlewareChain(Authe
nticate), handler) | |
| 78 So(rr.Code, ShouldEqual, 500) | |
| 79 So(rr.Body.String(), ShouldEqual, "Authentication middleware is
not configured\n") | |
| 80 }) | |
| 81 | |
| 82 Convey("Transient error", t, func() { | |
| 83 c := prepareCtx(fakeMethod{authError: errors.WrapTransient(error
s.New("boo"))}) | |
| 84 rr := call(c, router.NewMiddlewareChain(Authenticate), handler) | |
| 85 So(rr.Code, ShouldEqual, 500) | |
| 86 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") | |
| 87 }) | |
| 88 | |
| 89 Convey("Fatal error", t, func() { | |
| 90 c := prepareCtx(fakeMethod{authError: errors.New("boo")}) | |
| 91 rr := call(c, router.NewMiddlewareChain(Authenticate), handler) | |
| 92 So(rr.Code, ShouldEqual, 401) | |
| 93 So(rr.Body.String(), ShouldEqual, "Authentication error - boo\n"
) | |
| 94 }) | |
| 95 | |
| 96 Convey("Works", t, func() { | |
| 97 c := prepareCtx(fakeMethod{userID: "user:abc@example.com"}) | |
| 98 rr := call(c, router.NewMiddlewareChain(Authenticate), handler) | |
| 99 So(rr.Code, ShouldEqual, 200) | |
| 100 So(rr.Body.String(), ShouldEqual, "user:abc@example.com") | |
| 101 }) | |
| 102 | |
| 103 Convey("Anonymous works", t, func() { | |
| 104 c := prepareCtx(fakeMethod{anon: true}) | |
| 105 rr := call(c, router.NewMiddlewareChain(Authenticate), handler) | |
| 106 So(rr.Code, ShouldEqual, 200) | |
| 107 So(rr.Body.String(), ShouldEqual, "anonymous:anonymous") | |
| 108 }) | |
| 109 | |
| 110 Convey("Broken ID is rejected", t, func() { | |
| 111 c := prepareCtx(fakeMethod{userID: "???"}) | |
| 112 rr := call(c, router.NewMiddlewareChain(Authenticate), handler) | |
| 113 So(rr.Code, ShouldEqual, 401) | |
| 114 So(rr.Body.String(), ShouldEqual, "Authentication error - auth:
bad identity string \"???\"\n") | |
| 115 }) | |
| 116 } | |
| 117 | |
| 118 func TestAutologin(t *testing.T) { | |
| 119 t.Parallel() | |
| 120 | |
| 121 call := func(c context.Context, m router.MiddlewareChain, h router.Handl
er) *httptest.ResponseRecorder { | |
| 122 req, err := http.NewRequest("GET", "http://example.com/foo", nil
) | |
| 123 So(err, ShouldBeNil) | |
| 124 w := httptest.NewRecorder() | |
| 125 router.RunMiddleware(&router.Context{ | |
| 126 Context: c, | |
| 127 Writer: w, | |
| 128 Request: req, | |
| 129 }, m, h) | |
| 130 return w | |
| 131 } | |
| 132 | |
| 133 handler := func(c *router.Context) { | |
| 134 fmt.Fprintf(c.Writer, "%s", CurrentIdentity(c.Context)) | |
| 135 } | |
| 136 | |
| 137 Convey("Not configured", t, func() { | |
| 138 rr := call(context.Background(), router.NewMiddlewareChain(Autol
ogin), handler) | |
| 139 So(rr.Code, ShouldEqual, 500) | |
| 140 So(rr.Body.String(), ShouldEqual, "Authentication middleware is
not configured\n") | |
| 141 }) | |
| 142 | |
| 143 Convey("Transient error", t, func() { | |
| 144 c := prepareCtx(fakeMethod{authError: errors.WrapTransient(error
s.New("boo"))}) | |
| 145 rr := call(c, router.NewMiddlewareChain(Autologin), handler) | |
| 146 So(rr.Code, ShouldEqual, 500) | |
| 147 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") | |
| 148 }) | |
| 149 | |
| 150 Convey("Fatal error", t, func() { | |
| 151 c := prepareCtx(fakeMethod{authError: errors.New("boo")}) | |
| 152 rr := call(c, router.NewMiddlewareChain(Autologin), handler) | |
| 153 So(rr.Code, ShouldEqual, 401) | |
| 154 }) | |
| 155 | |
| 156 Convey("Anonymous is redirected to login if has UsersAPI", t, func() { | |
| 157 c := prepareCtx(fakeMethod{anon: true}) | |
| 158 rr := call(c, router.NewMiddlewareChain(Autologin), handler) | |
| 159 So(rr.Code, ShouldEqual, 302) | |
| 160 So(rr.Header().Get("Location"), ShouldEqual, "http://login_url?r
=%2Ffoo") | |
| 161 }) | |
| 162 | |
| 163 Convey("Anonymous is rejected if no UsersAPI", t, func() { | |
| 164 c := prepareCtx(noUserAPI{}) | |
| 165 rr := call(c, router.NewMiddlewareChain(Autologin), handler) | |
| 166 So(rr.Code, ShouldEqual, 401) | |
| 167 So(rr.Body.String(), ShouldEqual, "Authentication error - auth:
methods do not support login or logout URL\n") | |
| 168 }) | |
| 169 | |
| 170 Convey("Handles transient error in LoginURL", t, func() { | |
| 171 c := prepareCtx(fakeMethod{anon: true, loginURLError: errors.Wra
pTransient(errors.New("boo"))}) | |
| 172 rr := call(c, router.NewMiddlewareChain(Autologin), handler) | |
| 173 So(rr.Code, ShouldEqual, 500) | |
| 174 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") | |
| 175 }) | |
| 176 | |
| 177 Convey("Passes authenticated user through", t, func() { | |
| 178 c := prepareCtx(fakeMethod{userID: "user:abc@example.com"}) | |
| 179 rr := call(c, router.NewMiddlewareChain(Autologin), handler) | |
| 180 So(rr.Code, ShouldEqual, 200) | |
| 181 So(rr.Body.String(), ShouldEqual, "user:abc@example.com") | |
| 182 }) | |
| 183 } | |
| 184 | |
| 185 func prepareCtx(m ...Method) context.Context { | |
| 186 c := injectTestDB(context.Background(), &fakeDB{}) | |
| 187 return SetAuthenticator(c, Authenticator(m)) | |
| 188 } | |
| 189 | |
| 190 type noUserAPI struct{} | |
| 191 | |
| 192 func (noUserAPI) Authenticate(context.Context, *http.Request) (*User, error) { | |
| 193 return nil, nil | |
| 194 } | |
| 195 | |
| 196 type fakeMethod struct { | |
| 197 authError error | |
| 198 loginURLError error | |
| 199 userID identity.Identity | |
| 200 anon bool | |
| 201 } | |
| 202 | |
| 203 func (m fakeMethod) Authenticate(context.Context, *http.Request) (*User, error)
{ | |
| 204 if m.anon { | |
| 205 return nil, nil | |
| 206 } | |
| 207 if m.authError != nil { | |
| 208 return nil, m.authError | |
| 209 } | |
| 210 return &User{Identity: m.userID}, nil | |
| 211 } | |
| 212 | |
| 213 func (m fakeMethod) LoginURL(c context.Context, dest string) (string, error) { | |
| 214 if m.loginURLError != nil { | |
| 215 return "", m.loginURLError | |
| 216 } | |
| 217 v := url.Values{} | |
| 218 v.Set("r", dest) | |
| 219 return "http://login_url?" + v.Encode(), nil | |
| 220 } | |
| 221 | |
| 222 func (m fakeMethod) LogoutURL(c context.Context, dest string) (string, error) { | |
| 223 v := url.Values{} | |
| 224 v.Set("r", dest) | |
| 225 return "http://logout_url?" + v.Encode(), nil | |
| 226 } | |
| OLD | NEW |