1 package ldntlm
2
3 import (
4 "crypto/x509"
5 "fmt"
6 "io"
7 "net/http"
8 "net/http/httptest"
9 "regexp"
10 "testing"
11
12 "github.com/launchdarkly/go-test-helpers/v3/httphelpers"
13
14 "github.com/stretchr/testify/assert"
15 "github.com/stretchr/testify/require"
16
17 "github.com/launchdarkly/go-server-sdk/v6/ldhttp"
18 )
19
20 const (
21 username = "username"
22 password = "password"
23 domain = "domain"
24 targetURL = "http://example.com/test"
25 targetServer = "example.com:80"
26 targetURLPath = "/test"
27 responseBody = "hello"
28
29
30
31 proxyAuthStep1Expected = "NTLM TlRMTVNTUAABAAAAAZKIoAYABgAoAAAAAAAAAC4AAAAGAbEdAAAAD0RPTUFJTg=="
32 proxyAuthStep2Challenge = "NTLM TlRMTVNTUAACAAAADAAMADAAAAA1gomgZ38cVXpe6WwAAAAAAAAAAEYARgA8AAAAVABFAFMAVABOAFQAAgAMAFQARQBTAFQATgBUAAEADABNAEUATQBCAEUAUgADAB4AbQBlAG0AYgBlAHIALgB0AGUAcwB0AC4AYwBvAG0AAAAAAA=="
33 proxyAuthStep3ExpectedRegex = "NTLM TlRMTVNTUAADAAAAAAAAAEAAAAB2AHYAQAAAAAwADAC2AAAAEAAQAMIAAAAUABQA0gAAAAAAAAAAAAAANYK.*AAAAAAgAMAFQARQBTAFQATgBUAAEADABNAEUATQBCAEUAUgADAB4AbQBlAG0AYgBlAHIALgB0AGUAcwB0AC4AYwBvAG0AAAAAAAAAAABUAEUAUwBUAE4AVAB1AHMAZQByAG4AYQBtAGUAZwBvAC0AbgB0AGwAbQBzAHMAcAA="
34 )
35
36 func TestCanConnectToNTLMProxyServer(t *testing.T) {
37 httphelpers.WithServer(makeFakeNTLMProxyHandler(), func(server *httptest.Server) {
38 factory, err := NewNTLMProxyHTTPClientFactory(server.URL, username, password, domain)
39 require.NoError(t, err)
40 client := factory()
41
42 resp, err := client.Get(targetURL)
43 require.NoError(t, err)
44 assert.Equal(t, 200, resp.StatusCode)
45 body, err := io.ReadAll(resp.Body)
46 require.NoError(t, err)
47 assert.Equal(t, responseBody, string(body))
48 })
49 }
50
51 func TestCanConnectSecurelyToNTLMProxyServerWithSelfSignedCert(t *testing.T) {
52 handler := makeFakeNTLMProxyHandler()
53 httphelpers.WithSelfSignedServer(handler, func(server *httptest.Server, certData []byte, certs *x509.CertPool) {
54 factory, err := NewNTLMProxyHTTPClientFactory(server.URL, username, password, domain,
55 ldhttp.CACertOption(certData))
56 require.NoError(t, err)
57 client := factory()
58
59 resp, err := client.Get(targetURL)
60 require.NoError(t, err)
61 assert.Equal(t, 200, resp.StatusCode)
62 body, err := io.ReadAll(resp.Body)
63 require.NoError(t, err)
64 assert.Equal(t, responseBody, string(body))
65 })
66 }
67
68 func TestInvalidParameters(t *testing.T) {
69 _, err := NewNTLMProxyHTTPClientFactory("", "user", "pass", domain)
70 assert.Error(t, err)
71
72 _, err = NewNTLMProxyHTTPClientFactory("http://proxy", "", "pass", domain)
73 assert.Error(t, err)
74
75 _, err = NewNTLMProxyHTTPClientFactory("http://proxy", "user", "", domain)
76 assert.Error(t, err)
77
78 _, err = NewNTLMProxyHTTPClientFactory("://bad", "user", "pass", domain)
79 assert.Error(t, err)
80
81 _, err = NewNTLMProxyHTTPClientFactory("http://proxy", "user", "pass", "domain",
82 ldhttp.CACertOption([]byte("not a valid cert")))
83 assert.Error(t, err)
84 }
85
86 func makeFakeNTLMProxyHandler() http.Handler {
87 step := 0
88
89
90
91
92
93
94 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
95 step = step + 1
96 expectedMethod := "CONNECT"
97 expectedURL := targetServer
98 if step == 3 {
99 expectedMethod = "GET"
100 expectedURL = targetURLPath
101 }
102 if step < 3 {
103 if req.Method != expectedMethod {
104 fmt.Printf("Expected %s, got %s for step %d\n", expectedMethod, req.Method, step)
105 w.WriteHeader(405)
106 return
107 }
108 if req.RequestURI != expectedURL {
109 fmt.Printf("Expected %s, got %s for step %d\n", expectedURL, req.RequestURI, step)
110 w.WriteHeader(404)
111 return
112 }
113 }
114 proxyAuth := req.Header.Get("Proxy-Authorization")
115 badAuth := func() {
116 fmt.Printf("Unexpected Proxy-Authorization value: %s\n", proxyAuth)
117 w.WriteHeader(401)
118 }
119 switch step {
120 case 1:
121 if proxyAuth == proxyAuthStep1Expected {
122 w.Header().Set("Proxy-Authenticate", proxyAuthStep2Challenge)
123 w.WriteHeader(407)
124 } else {
125 badAuth()
126 }
127 case 2:
128 if matched, _ := regexp.MatchString(proxyAuthStep3ExpectedRegex, proxyAuth); matched {
129 w.WriteHeader(200)
130 } else {
131 badAuth()
132 }
133 case 3:
134 w.WriteHeader(200)
135 w.Write([]byte(responseBody))
136 }
137 })
138 }
139
View as plain text