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