golang.org/x/build@v0.0.0-20240506185731-218518f32b70/gerrit/auth_test.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gerrit
     6  
     7  import (
     8  	"context"
     9  	"crypto/md5"
    10  	"encoding/hex"
    11  	"encoding/json"
    12  	"fmt"
    13  	"net/http"
    14  	"net/http/httptest"
    15  	"strings"
    16  	"testing"
    17  )
    18  
    19  func md5str(text string) string {
    20  	h := md5.Sum([]byte(text))
    21  	return hex.EncodeToString(h[:])
    22  }
    23  
    24  func TestBasicAuth(t *testing.T) {
    25  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    26  		expected := "User Password true"
    27  		u, p, ok := r.BasicAuth()
    28  		if expected != fmt.Sprintf("%s %s %t", u, p, ok) {
    29  			t.Errorf("Expected %s, got %s %s %t", expected, u, p, ok)
    30  			w.WriteHeader(http.StatusUnauthorized)
    31  		} else {
    32  			w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    33  			// The JSON response begins with an XSRF-defeating header ")]}\n"
    34  			fmt.Fprintln(w, ")]}")
    35  			json.NewEncoder(w).Encode(AccountInfo{})
    36  		}
    37  	}))
    38  	defer ts.Close()
    39  
    40  	_, err := NewClient(
    41  		ts.URL,
    42  		BasicAuth("User", "Password"),
    43  	).GetAccountInfo(context.Background(), "self")
    44  	if err != nil {
    45  		t.Error(err)
    46  	}
    47  }
    48  
    49  func TestDigestAuth(t *testing.T) {
    50  	const (
    51  		user   = "User"
    52  		pass   = "Password"
    53  		nonce  = "dcd98b7102dd2f0e8b11d0f600bfb0c093"
    54  		opaque = "5ccc069c403ebaf9f0171e9517f40e41"
    55  		realm  = "Gerrit Code Review"
    56  		qop    = "auth"
    57  	)
    58  
    59  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    60  		header := r.Header.Get("Authorization")
    61  		if header == "" {
    62  			w.Header().Set("WWW-Authenticate", fmt.Sprintf(
    63  				`Digest realm="%s", qop="%s", nonce="%s", opaque="%s"`,
    64  				realm, qop, nonce, opaque,
    65  			))
    66  			w.WriteHeader(http.StatusUnauthorized)
    67  		} else {
    68  			parts := strings.SplitN(header, " ", 2)
    69  			parts = strings.Split(parts[1], ", ")
    70  			opts := make(map[string]string)
    71  
    72  			for _, part := range parts {
    73  				vals := strings.SplitN(part, "=", 2)
    74  				key := vals[0]
    75  				val := strings.Trim(vals[1], "\",")
    76  				opts[key] = val
    77  			}
    78  
    79  			// https://en.wikipedia.org/wiki/Digest_access_authentication#Example_with_explanation
    80  			// The "response" value is calculated in three steps, as follows.
    81  			//   Where values are combined, they are delimited by colons.
    82  			// 1. The MD5 hash of the combined username, authentication realm and password is calculated.
    83  			//    The result is referred to as HA1.
    84  			// 2. The MD5 hash of the combined method and digest URI is calculated, e.g. of "GET" and "/index.html".
    85  			//    The result is referred to as HA2.
    86  			// 3. The MD5 hash of the combined HA1 result, server nonce (nonce), request counter (nc),
    87  			//    client nonce (cnonce), quality of protection code (qop) and HA2 result is calculated.
    88  			//    The result is the "response" value provided by the client.
    89  			ha1 := md5str(fmt.Sprintf("%s:%s:%s", user, realm, pass))
    90  			ha2 := md5str("GET:/a/accounts/self")
    91  			expected := md5str(fmt.Sprintf("%s:%s:%s:%s:%s:%s", ha1, nonce, opts["nc"], opts["cnonce"], qop, ha2))
    92  
    93  			if expected != opts["response"] {
    94  				t.Errorf("Expected %s, got %s", expected, opts["response"])
    95  				w.WriteHeader(http.StatusUnauthorized)
    96  			} else {
    97  				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    98  				// The JSON response begins with an XSRF-defeating header ")]}\n"
    99  				fmt.Fprintln(w, ")]}")
   100  				json.NewEncoder(w).Encode(AccountInfo{})
   101  			}
   102  		}
   103  	}))
   104  	defer ts.Close()
   105  
   106  	_, err := NewClient(
   107  		ts.URL,
   108  		DigestAuth(user, pass),
   109  	).GetAccountInfo(context.Background(), "self")
   110  	if err != nil {
   111  		t.Error(err)
   112  	}
   113  }