github.com/go-kivik/kivik/v4@v4.3.2/couchdb/chttp/auth_test.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package chttp 14 15 import ( 16 "context" 17 "encoding/json" 18 "net/http" 19 "net/http/cookiejar" 20 "net/url" 21 "testing" 22 23 "gitlab.com/flimzy/testy" 24 "golang.org/x/net/publicsuffix" 25 26 kivik "github.com/go-kivik/kivik/v4" 27 internal "github.com/go-kivik/kivik/v4/int/errors" 28 "github.com/go-kivik/kivik/v4/int/mock" 29 "github.com/go-kivik/kivik/v4/internal/nettest" 30 ) 31 32 type mockRT struct { 33 resp *http.Response 34 err error 35 } 36 37 var _ http.RoundTripper = &mockRT{} 38 39 func (rt *mockRT) RoundTrip(_ *http.Request) (*http.Response, error) { 40 return rt.resp, rt.err 41 } 42 43 func TestAuthenticate(t *testing.T) { 44 s := nettest.NewHTTPTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 45 defer r.Body.Close() // nolint: errcheck 46 var authed bool 47 switch r.Header.Get("Authorization") { 48 case "Basic YWRtaW46YWJjMTIz", "Bearer tokennekot": 49 authed = true 50 } 51 if r.Method == http.MethodPost { 52 var result struct { 53 Name string 54 Password string 55 } 56 _ = json.NewDecoder(r.Body).Decode(&result) 57 if result.Name == "admin" && result.Password == "abc123" { 58 authed = true 59 http.SetCookie(w, &http.Cookie{ 60 Name: kivik.SessionCookieName, 61 Value: "auth-token", 62 Path: "/", 63 HttpOnly: true, 64 }) 65 } 66 } 67 if ses := r.Header.Get("Cookie"); ses == "AuthSession=auth-token" { 68 authed = true 69 } 70 if !authed { 71 w.WriteHeader(http.StatusUnauthorized) 72 return 73 } 74 w.WriteHeader(http.StatusOK) 75 if r.URL.Path == "/_session" { // nolint: goconst 76 _, _ = w.Write([]byte(`{"userCtx":{"name":"admin"}}`)) 77 return 78 } 79 _, _ = w.Write([]byte(`{"foo":123}`)) 80 })) 81 82 type authTest struct { 83 addr string 84 jar http.CookieJar 85 options kivik.Option 86 err string 87 status int 88 } 89 tests := testy.NewTable() 90 tests.Cleanup(s.Close) 91 tests.Add("unauthorized", authTest{ 92 addr: s.URL, 93 err: "Unauthorized", 94 status: http.StatusUnauthorized, 95 }) 96 tests.Add("basic auth", authTest{ 97 addr: s.URL, 98 options: BasicAuth("admin", "abc123"), 99 }) 100 tests.Add("cookie auth success", func(t *testing.T) interface{} { 101 sv := nettest.NewHTTPTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 102 h := w.Header() 103 h.Set("Content-Type", "application/json") 104 h.Set("Date", "Sat, 08 Sep 2018 15:49:29 GMT") 105 h.Set("Server", "CouchDB/2.2.0 (Erlang OTP/19)") 106 if r.URL.Path == "/_session" { 107 h.Set("Set-Cookie", "AuthSession=YWRtaW46NUI5M0VGODk6eLUGqXf0HRSEV9PPLaZX86sBYes; Version=1; Path=/; HttpOnly") 108 w.WriteHeader(200) 109 _, _ = w.Write([]byte(`{"ok":true,"name":"admin","roles":["_admin"]}`)) 110 } else { 111 w.WriteHeader(200) 112 _, _ = w.Write([]byte(`{"ok":true}`)) 113 } 114 })) 115 return authTest{ 116 addr: sv.URL, 117 options: CookieAuth("foo", "bar"), 118 } 119 }) 120 tests.Add("failed basic auth", authTest{ 121 addr: s.URL, 122 options: BasicAuth("foo", ""), 123 err: "Unauthorized", 124 status: http.StatusUnauthorized, 125 }) 126 tests.Add("failed cookie auth", authTest{ 127 addr: s.URL, 128 options: CookieAuth("foo", ""), 129 err: `Get "?` + s.URL + `/foo"?: Unauthorized`, 130 status: http.StatusUnauthorized, 131 }) 132 tests.Add("already authenticated with cookie", func() interface{} { 133 jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) 134 if err != nil { 135 t.Fatal(err) 136 } 137 u, _ := url.Parse(s.URL) 138 jar.SetCookies(u, []*http.Cookie{{ 139 Name: kivik.SessionCookieName, 140 Value: "auth-token", 141 Path: "/", 142 HttpOnly: true, 143 }}) 144 return authTest{ 145 addr: s.URL, 146 jar: jar, 147 } 148 }) 149 tests.Add("JWT auth", authTest{ 150 addr: s.URL, 151 options: JWTAuth("tokennekot"), 152 }) 153 tests.Add("failed JWT auth", authTest{ 154 addr: s.URL, 155 options: JWTAuth("nekot"), 156 err: "Unauthorized", 157 status: http.StatusUnauthorized, 158 }) 159 160 tests.Run(t, func(t *testing.T, test authTest) { 161 ctx := context.Background() 162 opts := test.options 163 if opts == nil { 164 opts = mock.NilOption 165 } 166 c, err := New(&http.Client{}, test.addr, opts) 167 if err != nil { 168 t.Fatal(err) 169 } 170 if test.jar != nil { 171 c.Client.Jar = test.jar 172 } 173 _, err = c.DoError(ctx, "GET", "/foo", nil) 174 if d := internal.StatusErrorDiffRE(test.err, test.status, err); d != "" { 175 t.Error(d) 176 } 177 }) 178 }