| 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, m router.MiddlewareChain, h router.Handl
er) *httptest.ResponseRecorder { |
| 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 » » router.RunMiddleware(&router.Context{ |
| 61 » » » Context: c, |
| 62 » » » Writer: w, |
| 63 » » » Request: req, |
| 64 » » }, m, h) |
| 62 return w | 65 return w |
| 63 } | 66 } |
| 64 | 67 |
| 65 » handler := func(c context.Context, rw http.ResponseWriter, r *http.Reque
st, p httprouter.Params) { | 68 » handler := func(c *router.Context) { |
| 66 » » fmt.Fprintf(rw, "%s", CurrentIdentity(c)) | 69 » » fmt.Fprintf(c.Writer, "%s", CurrentIdentity(c.Context)) |
| 67 } | 70 } |
| 68 | 71 |
| 69 Convey("Not configured", t, func() { | 72 Convey("Not configured", t, func() { |
| 70 » » rr := call(context.Background(), Authenticate(handler)) | 73 » » rr := call(context.Background(), router.MiddlewareChain{Authenti
cate}, handler) |
| 71 So(rr.Code, ShouldEqual, 500) | 74 So(rr.Code, ShouldEqual, 500) |
| 72 So(rr.Body.String(), ShouldEqual, "Authentication middleware is
not configured\n") | 75 So(rr.Body.String(), ShouldEqual, "Authentication middleware is
not configured\n") |
| 73 }) | 76 }) |
| 74 | 77 |
| 75 Convey("Transient error", t, func() { | 78 Convey("Transient error", t, func() { |
| 76 c := prepareCtx(fakeMethod{authError: errors.WrapTransient(error
s.New("boo"))}) | 79 c := prepareCtx(fakeMethod{authError: errors.WrapTransient(error
s.New("boo"))}) |
| 77 » » rr := call(c, Authenticate(handler)) | 80 » » rr := call(c, router.MiddlewareChain{Authenticate}, handler) |
| 78 So(rr.Code, ShouldEqual, 500) | 81 So(rr.Code, ShouldEqual, 500) |
| 79 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") | 82 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") |
| 80 }) | 83 }) |
| 81 | 84 |
| 82 Convey("Fatal error", t, func() { | 85 Convey("Fatal error", t, func() { |
| 83 c := prepareCtx(fakeMethod{authError: errors.New("boo")}) | 86 c := prepareCtx(fakeMethod{authError: errors.New("boo")}) |
| 84 » » rr := call(c, Authenticate(handler)) | 87 » » rr := call(c, router.MiddlewareChain{Authenticate}, handler) |
| 85 So(rr.Code, ShouldEqual, 401) | 88 So(rr.Code, ShouldEqual, 401) |
| 86 So(rr.Body.String(), ShouldEqual, "Authentication error - boo\n"
) | 89 So(rr.Body.String(), ShouldEqual, "Authentication error - boo\n"
) |
| 87 }) | 90 }) |
| 88 | 91 |
| 89 Convey("Works", t, func() { | 92 Convey("Works", t, func() { |
| 90 c := prepareCtx(fakeMethod{userID: "user:abc@example.com"}) | 93 c := prepareCtx(fakeMethod{userID: "user:abc@example.com"}) |
| 91 » » rr := call(c, Authenticate(handler)) | 94 » » rr := call(c, router.MiddlewareChain{Authenticate}, handler) |
| 92 So(rr.Code, ShouldEqual, 200) | 95 So(rr.Code, ShouldEqual, 200) |
| 93 So(rr.Body.String(), ShouldEqual, "user:abc@example.com") | 96 So(rr.Body.String(), ShouldEqual, "user:abc@example.com") |
| 94 }) | 97 }) |
| 95 | 98 |
| 96 Convey("Anonymous works", t, func() { | 99 Convey("Anonymous works", t, func() { |
| 97 c := prepareCtx(fakeMethod{anon: true}) | 100 c := prepareCtx(fakeMethod{anon: true}) |
| 98 » » rr := call(c, Authenticate(handler)) | 101 » » rr := call(c, router.MiddlewareChain{Authenticate}, handler) |
| 99 So(rr.Code, ShouldEqual, 200) | 102 So(rr.Code, ShouldEqual, 200) |
| 100 So(rr.Body.String(), ShouldEqual, "anonymous:anonymous") | 103 So(rr.Body.String(), ShouldEqual, "anonymous:anonymous") |
| 101 }) | 104 }) |
| 102 | 105 |
| 103 Convey("Broken ID is rejected", t, func() { | 106 Convey("Broken ID is rejected", t, func() { |
| 104 c := prepareCtx(fakeMethod{userID: "???"}) | 107 c := prepareCtx(fakeMethod{userID: "???"}) |
| 105 » » rr := call(c, Authenticate(handler)) | 108 » » rr := call(c, router.MiddlewareChain{Authenticate}, handler) |
| 106 So(rr.Code, ShouldEqual, 401) | 109 So(rr.Code, ShouldEqual, 401) |
| 107 So(rr.Body.String(), ShouldEqual, "Authentication error - auth:
bad identity string \"???\"\n") | 110 So(rr.Body.String(), ShouldEqual, "Authentication error - auth:
bad identity string \"???\"\n") |
| 108 }) | 111 }) |
| 109 } | 112 } |
| 110 | 113 |
| 111 func TestAutologin(t *testing.T) { | 114 func TestAutologin(t *testing.T) { |
| 112 » call := func(c context.Context, h middleware.Handler) *httptest.Response
Recorder { | 115 » call := func(c context.Context, m router.MiddlewareChain, h router.Handl
er) *httptest.ResponseRecorder { |
| 113 req, err := http.NewRequest("GET", "http://example.com/foo", nil
) | 116 req, err := http.NewRequest("GET", "http://example.com/foo", nil
) |
| 114 So(err, ShouldBeNil) | 117 So(err, ShouldBeNil) |
| 115 w := httptest.NewRecorder() | 118 w := httptest.NewRecorder() |
| 116 » » h(c, w, req, nil) | 119 » » router.RunMiddleware(&router.Context{ |
| 120 » » » Context: c, |
| 121 » » » Writer: w, |
| 122 » » » Request: req, |
| 123 » » }, m, h) |
| 117 return w | 124 return w |
| 118 } | 125 } |
| 119 | 126 |
| 120 » handler := func(c context.Context, rw http.ResponseWriter, r *http.Reque
st, p httprouter.Params) { | 127 » handler := func(c *router.Context) { |
| 121 » » fmt.Fprintf(rw, "%s", CurrentIdentity(c)) | 128 » » fmt.Fprintf(c.Writer, "%s", CurrentIdentity(c.Context)) |
| 122 } | 129 } |
| 123 | 130 |
| 124 Convey("Not configured", t, func() { | 131 Convey("Not configured", t, func() { |
| 125 » » rr := call(context.Background(), Autologin(handler)) | 132 » » rr := call(context.Background(), router.MiddlewareChain{Autologi
n}, handler) |
| 126 So(rr.Code, ShouldEqual, 500) | 133 So(rr.Code, ShouldEqual, 500) |
| 127 So(rr.Body.String(), ShouldEqual, "Authentication middleware is
not configured\n") | 134 So(rr.Body.String(), ShouldEqual, "Authentication middleware is
not configured\n") |
| 128 }) | 135 }) |
| 129 | 136 |
| 130 Convey("Transient error", t, func() { | 137 Convey("Transient error", t, func() { |
| 131 c := prepareCtx(fakeMethod{authError: errors.WrapTransient(error
s.New("boo"))}) | 138 c := prepareCtx(fakeMethod{authError: errors.WrapTransient(error
s.New("boo"))}) |
| 132 » » rr := call(c, Autologin(handler)) | 139 » » rr := call(c, router.MiddlewareChain{Autologin}, handler) |
| 133 So(rr.Code, ShouldEqual, 500) | 140 So(rr.Code, ShouldEqual, 500) |
| 134 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") | 141 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") |
| 135 }) | 142 }) |
| 136 | 143 |
| 137 Convey("Fatal error", t, func() { | 144 Convey("Fatal error", t, func() { |
| 138 c := prepareCtx(fakeMethod{authError: errors.New("boo")}) | 145 c := prepareCtx(fakeMethod{authError: errors.New("boo")}) |
| 139 » » rr := call(c, Autologin(handler)) | 146 » » rr := call(c, router.MiddlewareChain{Autologin}, handler) |
| 140 So(rr.Code, ShouldEqual, 401) | 147 So(rr.Code, ShouldEqual, 401) |
| 141 }) | 148 }) |
| 142 | 149 |
| 143 Convey("Anonymous is redirected to login if has UsersAPI", t, func() { | 150 Convey("Anonymous is redirected to login if has UsersAPI", t, func() { |
| 144 c := prepareCtx(fakeMethod{anon: true}) | 151 c := prepareCtx(fakeMethod{anon: true}) |
| 145 » » rr := call(c, Autologin(handler)) | 152 » » rr := call(c, router.MiddlewareChain{Autologin}, handler) |
| 146 So(rr.Code, ShouldEqual, 302) | 153 So(rr.Code, ShouldEqual, 302) |
| 147 So(rr.Header().Get("Location"), ShouldEqual, "http://login_url?r
=%2Ffoo") | 154 So(rr.Header().Get("Location"), ShouldEqual, "http://login_url?r
=%2Ffoo") |
| 148 }) | 155 }) |
| 149 | 156 |
| 150 Convey("Anonymous is rejected if no UsersAPI", t, func() { | 157 Convey("Anonymous is rejected if no UsersAPI", t, func() { |
| 151 c := prepareCtx(noUserAPI{}) | 158 c := prepareCtx(noUserAPI{}) |
| 152 » » rr := call(c, Autologin(handler)) | 159 » » rr := call(c, router.MiddlewareChain{Autologin}, handler) |
| 153 So(rr.Code, ShouldEqual, 401) | 160 So(rr.Code, ShouldEqual, 401) |
| 154 So(rr.Body.String(), ShouldEqual, "Authentication error - auth:
methods do not support login or logout URL\n") | 161 So(rr.Body.String(), ShouldEqual, "Authentication error - auth:
methods do not support login or logout URL\n") |
| 155 }) | 162 }) |
| 156 | 163 |
| 157 Convey("Handles transient error in LoginURL", t, func() { | 164 Convey("Handles transient error in LoginURL", t, func() { |
| 158 c := prepareCtx(fakeMethod{anon: true, loginURLError: errors.Wra
pTransient(errors.New("boo"))}) | 165 c := prepareCtx(fakeMethod{anon: true, loginURLError: errors.Wra
pTransient(errors.New("boo"))}) |
| 159 » » rr := call(c, Autologin(handler)) | 166 » » rr := call(c, router.MiddlewareChain{Autologin}, handler) |
| 160 So(rr.Code, ShouldEqual, 500) | 167 So(rr.Code, ShouldEqual, 500) |
| 161 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") | 168 So(rr.Body.String(), ShouldEqual, "Transient error during authen
tication - boo\n") |
| 162 }) | 169 }) |
| 163 | 170 |
| 164 Convey("Passes authenticated user through", t, func() { | 171 Convey("Passes authenticated user through", t, func() { |
| 165 c := prepareCtx(fakeMethod{userID: "user:abc@example.com"}) | 172 c := prepareCtx(fakeMethod{userID: "user:abc@example.com"}) |
| 166 » » rr := call(c, Autologin(handler)) | 173 » » rr := call(c, router.MiddlewareChain{Autologin}, handler) |
| 167 So(rr.Code, ShouldEqual, 200) | 174 So(rr.Code, ShouldEqual, 200) |
| 168 So(rr.Body.String(), ShouldEqual, "user:abc@example.com") | 175 So(rr.Body.String(), ShouldEqual, "user:abc@example.com") |
| 169 }) | 176 }) |
| 170 } | 177 } |
| 171 | 178 |
| 172 func prepareCtx(m ...Method) context.Context { | 179 func prepareCtx(m ...Method) context.Context { |
| 173 c := SetAuthenticator(context.Background(), Authenticator(m)) | 180 c := SetAuthenticator(context.Background(), Authenticator(m)) |
| 174 c = UseDB(c, func(context.Context) (DB, error) { | 181 c = UseDB(c, func(context.Context) (DB, error) { |
| 175 return &fakeDB{}, nil | 182 return &fakeDB{}, nil |
| 176 }) | 183 }) |
| (...skipping 30 matching lines...) Expand all Loading... |
| 207 v := url.Values{} | 214 v := url.Values{} |
| 208 v.Set("r", dest) | 215 v.Set("r", dest) |
| 209 return "http://login_url?" + v.Encode(), nil | 216 return "http://login_url?" + v.Encode(), nil |
| 210 } | 217 } |
| 211 | 218 |
| 212 func (m fakeMethod) LogoutURL(c context.Context, dest string) (string, error) { | 219 func (m fakeMethod) LogoutURL(c context.Context, dest string) (string, error) { |
| 213 v := url.Values{} | 220 v := url.Values{} |
| 214 v.Set("r", dest) | 221 v.Set("r", dest) |
| 215 return "http://logout_url?" + v.Encode(), nil | 222 return "http://logout_url?" + v.Encode(), nil |
| 216 } | 223 } |
| OLD | NEW |