| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package gerrit |
| |
| import ( |
| "context" |
| "crypto/md5" |
| "encoding/hex" |
| "encoding/json" |
| "fmt" |
| "net/http" |
| "net/http/httptest" |
| "strings" |
| "testing" |
| ) |
| |
| func md5str(text string) string { |
| h := md5.Sum([]byte(text)) |
| return hex.EncodeToString(h[:]) |
| } |
| |
| func TestBasicAuth(t *testing.T) { |
| ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| expected := "User Password true" |
| u, p, ok := r.BasicAuth() |
| if expected != fmt.Sprintf("%s %s %t", u, p, ok) { |
| t.Errorf("Expected %s, got %s %s %t", expected, u, p, ok) |
| w.WriteHeader(http.StatusUnauthorized) |
| } else { |
| w.Header().Set("Content-Type", "application/json; charset=UTF-8") |
| // The JSON response begins with an XSRF-defeating header ")]}\n" |
| fmt.Fprintln(w, ")]}") |
| json.NewEncoder(w).Encode(AccountInfo{}) |
| } |
| })) |
| defer ts.Close() |
| |
| _, err := NewClient( |
| ts.URL, |
| BasicAuth("User", "Password"), |
| ).GetAccountInfo(context.Background(), "self") |
| if err != nil { |
| t.Error(err) |
| } |
| } |
| |
| func TestDigestAuth(t *testing.T) { |
| const ( |
| user = "User" |
| pass = "Password" |
| nonce = "dcd98b7102dd2f0e8b11d0f600bfb0c093" |
| opaque = "5ccc069c403ebaf9f0171e9517f40e41" |
| realm = "Gerrit Code Review" |
| qop = "auth" |
| ) |
| |
| ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| header := r.Header.Get("Authorization") |
| if header == "" { |
| w.Header().Set("WWW-Authenticate", fmt.Sprintf( |
| `Digest realm="%s", qop="%s", nonce="%s", opaque="%s"`, |
| realm, qop, nonce, opaque, |
| )) |
| w.WriteHeader(http.StatusUnauthorized) |
| } else { |
| parts := strings.SplitN(header, " ", 2) |
| parts = strings.Split(parts[1], ", ") |
| opts := make(map[string]string) |
| |
| for _, part := range parts { |
| vals := strings.SplitN(part, "=", 2) |
| key := vals[0] |
| val := strings.Trim(vals[1], "\",") |
| opts[key] = val |
| } |
| |
| // https://en.wikipedia.org/wiki/Digest_access_authentication#Example_with_explanation |
| // The "response" value is calculated in three steps, as follows. |
| // Where values are combined, they are delimited by colons. |
| // 1. The MD5 hash of the combined username, authentication realm and password is calculated. |
| // The result is referred to as HA1. |
| // 2. The MD5 hash of the combined method and digest URI is calculated, e.g. of "GET" and "/index.html". |
| // The result is referred to as HA2. |
| // 3. The MD5 hash of the combined HA1 result, server nonce (nonce), request counter (nc), |
| // client nonce (cnonce), quality of protection code (qop) and HA2 result is calculated. |
| // The result is the "response" value provided by the client. |
| ha1 := md5str(fmt.Sprintf("%s:%s:%s", user, realm, pass)) |
| ha2 := md5str("GET:/a/accounts/self") |
| expected := md5str(fmt.Sprintf("%s:%s:%s:%s:%s:%s", ha1, nonce, opts["nc"], opts["cnonce"], qop, ha2)) |
| |
| if expected != opts["response"] { |
| t.Errorf("Expected %s, got %s", expected, opts["response"]) |
| w.WriteHeader(http.StatusUnauthorized) |
| } else { |
| w.Header().Set("Content-Type", "application/json; charset=UTF-8") |
| // The JSON response begins with an XSRF-defeating header ")]}\n" |
| fmt.Fprintln(w, ")]}") |
| json.NewEncoder(w).Encode(AccountInfo{}) |
| } |
| } |
| })) |
| defer ts.Close() |
| |
| _, err := NewClient( |
| ts.URL, |
| DigestAuth(user, pass), |
| ).GetAccountInfo(context.Background(), "self") |
| if err != nil { |
| t.Error(err) |
| } |
| } |