Skip to content

Commit 24b3bc7

Browse files
authored
chore: add more plugin tests (#678)
1 parent f863c08 commit 24b3bc7

File tree

4 files changed

+290
-8
lines changed

4 files changed

+290
-8
lines changed

.golangci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ linters:
181181
text: "^underef: could simplify.*cgo.Handle"
182182
- path: lib/
183183
linters:
184+
- goconst
184185
- unused
185186
- path: lib/
186187
text: "fieldalignment:"

lib/openvpn-auth-oauth2/management/management.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ func (s *Server) handleManagementClient(ctx context.Context, conn net.Conn) erro
247247

248248
scan:
249249
for scanner.Scan() {
250+
var cmd string
251+
250252
line := strings.TrimSpace(scanner.Text())
251253
if line == "" {
252254
continue
@@ -272,11 +274,11 @@ scan:
272274

273275
continue
274276
case strings.HasPrefix(line, "client-auth-nt"):
275-
_ = s.writeToClient("SUCCESS: client-auth command succeeded")
277+
cmd = "client-auth"
276278
case strings.HasPrefix(line, "client-pending-auth"):
277-
_ = s.writeToClient("SUCCESS: client-pending-auth command succeeded")
279+
cmd = "client-pending-auth"
278280
case strings.HasPrefix(line, "client-deny"):
279-
_ = s.writeToClient("SUCCESS: client-deny command succeeded")
281+
cmd = "client-deny"
280282
case strings.HasPrefix(line, "client-auth"):
281283
for scanner.Scan() {
282284
line += newline + strings.TrimSpace(scanner.Text())
@@ -286,7 +288,7 @@ scan:
286288
}
287289
}
288290

289-
_ = s.writeToClient("SUCCESS: client-auth command succeeded")
291+
cmd = "client-auth"
290292
default:
291293
_ = s.writeToClient("ERROR: unknown command, enter 'help' for more options")
292294

@@ -301,7 +303,12 @@ scan:
301303
slog.Any("err", err),
302304
slog.String("response", line),
303305
)
306+
_ = s.writeToClient(fmt.Sprintf("ERROR: %s command failed", cmd))
307+
} else {
308+
_ = s.writeToClient(fmt.Sprintf("SUCCESS: %s command succeeded", cmd))
309+
}
304310

311+
if resp == nil {
305312
continue
306313
}
307314

@@ -390,16 +397,27 @@ func (s *Server) parseResponse(response string) (*Response, error) {
390397
// client-deny 0 1 "OpenVPN Client does not support SSO authentication via webauth"
391398
parts := strings.SplitN(message, " ", 2)
392399

400+
var denyReason string
401+
if len(parts) == 2 {
402+
denyReason = strings.Trim(parts[1], `"`)
403+
} else {
404+
denyReason = "access denied"
405+
}
406+
393407
return &Response{
394408
ClientID: uint32(clientID),
395409
ClientAuth: ClientAuthDeny,
396-
Message: strings.Trim(parts[1], `"`),
410+
Message: denyReason,
397411
}, nil
398412
case "client-pending-auth":
399413
// client-pending-auth 0 1 "WEB_AUTH::https://example.com/..." 300
400414
parts := strings.SplitN(message, " ", 3)
401415
if len(parts) != 3 {
402-
return nil, fmt.Errorf("invalid client-pending-auth message: %s", message)
416+
return &Response{
417+
ClientID: uint32(clientID),
418+
ClientAuth: ClientAuthDeny,
419+
Message: "internal error",
420+
}, fmt.Errorf("invalid client-pending-auth message: %s", message)
403421
}
404422

405423
return &Response{
@@ -409,7 +427,11 @@ func (s *Server) parseResponse(response string) (*Response, error) {
409427
Timeout: parts[2],
410428
}, nil
411429
default:
412-
return nil, fmt.Errorf("unknown response command: %s", cmd)
430+
return &Response{
431+
ClientID: uint32(clientID),
432+
ClientAuth: ClientAuthDeny,
433+
Message: "internal error",
434+
}, fmt.Errorf("unknown response command: %s", cmd)
413435
}
414436
}
415437

lib/openvpn-auth-oauth2/management/management_test.go

Lines changed: 246 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import (
55
"fmt"
66
"log/slog"
77
"net"
8+
"strconv"
9+
"strings"
810
"testing"
11+
"time"
912

1013
"github.com/jkroepke/openvpn-auth-oauth2/internal/openvpn"
1114
"github.com/jkroepke/openvpn-auth-oauth2/internal/utils/testutils"
@@ -48,6 +51,10 @@ func TestServer_Listen(t *testing.T) {
4851
client, err := dialer.DialContext(t.Context(), tc.protocol, managementInterface.Addr().String())
4952
require.NoError(t, err)
5053

54+
t.Cleanup(func() {
55+
_ = client.Close()
56+
})
57+
5158
clientReader := bufio.NewReader(client)
5259

5360
testutils.ExpectMessage(t, client, clientReader, openvpn.WelcomeBanner)
@@ -108,7 +115,7 @@ func TestServer_Listen_Invalid_Addr(t *testing.T) {
108115
}
109116
}
110117

111-
func TestServer_Listen_Password(t *testing.T) {
118+
func TestServer_Listen_Password_Correct(t *testing.T) {
112119
t.Parallel()
113120

114121
managementInterface, err := nettest.NewLocalListener("tcp")
@@ -128,6 +135,10 @@ func TestServer_Listen_Password(t *testing.T) {
128135
client, err := dialer.DialContext(t.Context(), "tcp", managementInterface.Addr().String())
129136
require.NoError(t, err)
130137

138+
t.Cleanup(func() {
139+
_ = client.Close()
140+
})
141+
131142
clientReader := bufio.NewReader(client)
132143

133144
resp, err := clientReader.ReadString(':')
@@ -139,3 +150,237 @@ func TestServer_Listen_Password(t *testing.T) {
139150
testutils.ExpectMessage(t, client, clientReader, openvpn.WelcomeBanner)
140151
testutils.SendMessagef(t, client, "quit")
141152
}
153+
154+
func TestServer_Listen_Password_Incorrect(t *testing.T) {
155+
t.Parallel()
156+
157+
managementInterface, err := nettest.NewLocalListener("tcp")
158+
require.NoError(t, err)
159+
160+
err = managementInterface.Close()
161+
require.NoError(t, err)
162+
163+
managementServer := management.NewServer(slog.New(slog.DiscardHandler), testutils.Secret)
164+
err = managementServer.Listen(t.Context(), fmt.Sprintf("%s://%s", managementInterface.Addr().Network(), managementInterface.Addr().String()))
165+
require.NoError(t, err)
166+
167+
t.Cleanup(managementServer.Close)
168+
169+
var dialer net.Dialer
170+
171+
client, err := dialer.DialContext(t.Context(), "tcp", managementInterface.Addr().String())
172+
require.NoError(t, err)
173+
174+
t.Cleanup(func() {
175+
_ = client.Close()
176+
})
177+
178+
clientReader := bufio.NewReader(client)
179+
180+
resp, err := clientReader.ReadString(':')
181+
require.NoError(t, err)
182+
require.Equal(t, "ENTER PASSWORD:", resp)
183+
184+
testutils.SendMessagef(t, client, testutils.Password)
185+
testutils.ExpectMessage(t, client, clientReader, "ERROR: bad password")
186+
testutils.SendMessagef(t, client, "quit")
187+
}
188+
189+
func TestServer_AuthPendingPoller(t *testing.T) {
190+
t.Parallel()
191+
192+
for _, tc := range []struct {
193+
name string
194+
command string
195+
resp string
196+
testFn func(t *testing.T, response *management.Response)
197+
}{
198+
{
199+
name: "client-auth-nt",
200+
command: "client-auth-nt 1 0",
201+
resp: "SUCCESS: client-auth command succeeded",
202+
testFn: func(t *testing.T, response *management.Response) {
203+
t.Helper()
204+
205+
require.Equal(t, uint32(1), response.ClientID)
206+
require.Equal(t, management.ClientAuthAccept, response.ClientAuth)
207+
},
208+
},
209+
{
210+
name: "client-auth-nt invalid",
211+
command: "client-auth-nt A B",
212+
resp: "ERROR: client-auth command failed",
213+
},
214+
{
215+
name: "client-auth",
216+
command: "client-auth 2 0\r\npush \"reneg-sec 0\"\r\nEND",
217+
resp: "SUCCESS: client-auth command succeeded",
218+
testFn: func(t *testing.T, response *management.Response) {
219+
t.Helper()
220+
221+
require.Equal(t, uint32(2), response.ClientID)
222+
require.Equal(t, management.ClientAuthAccept, response.ClientAuth)
223+
require.Equal(t, "push \"reneg-sec 0\"", response.ClientConfig)
224+
},
225+
},
226+
{
227+
name: "client-deny without reason",
228+
command: "client-deny 3 0",
229+
resp: "SUCCESS: client-deny command succeeded",
230+
testFn: func(t *testing.T, response *management.Response) {
231+
t.Helper()
232+
233+
require.Equal(t, uint32(3), response.ClientID)
234+
require.Equal(t, management.ClientAuthDeny, response.ClientAuth)
235+
require.Equal(t, "access denied", response.Message)
236+
},
237+
},
238+
{
239+
name: "client-deny",
240+
command: "client-deny 3 0 \"internal error\"",
241+
resp: "SUCCESS: client-deny command succeeded",
242+
testFn: func(t *testing.T, response *management.Response) {
243+
t.Helper()
244+
245+
require.Equal(t, uint32(3), response.ClientID)
246+
require.Equal(t, management.ClientAuthDeny, response.ClientAuth)
247+
require.Equal(t, "internal error", response.Message)
248+
},
249+
},
250+
{
251+
name: "client-pending-auth",
252+
command: "client-pending-auth 4 0 \"WEB_AUTH::https://sso.example.com/auth?session=xyz\" 300",
253+
resp: "SUCCESS: client-pending-auth command succeeded",
254+
testFn: func(t *testing.T, response *management.Response) {
255+
t.Helper()
256+
257+
require.Equal(t, uint32(4), response.ClientID)
258+
require.Equal(t, management.ClientAuthPending, response.ClientAuth)
259+
require.Equal(t, "WEB_AUTH::https://sso.example.com/auth?session=xyz", response.Message)
260+
require.Equal(t, "300", response.Timeout)
261+
},
262+
},
263+
{
264+
name: "client-pending-auth without timeout",
265+
command: "client-pending-auth 4 0 \"WEB_AUTH::https://sso.example.com/auth?session=xyz\"",
266+
resp: "ERROR: client-pending-auth command failed",
267+
testFn: func(t *testing.T, response *management.Response) {
268+
t.Helper()
269+
270+
require.Equal(t, uint32(4), response.ClientID)
271+
require.Equal(t, management.ClientAuthDeny, response.ClientAuth)
272+
require.Equal(t, "internal error", response.Message)
273+
},
274+
},
275+
{
276+
name: "invalid",
277+
command: "invalid",
278+
resp: "ERROR: unknown command, enter 'help' for more options",
279+
},
280+
} {
281+
t.Run(tc.name, func(t *testing.T) {
282+
t.Parallel()
283+
284+
managementInterface, err := nettest.NewLocalListener("tcp")
285+
require.NoError(t, err)
286+
287+
err = managementInterface.Close()
288+
require.NoError(t, err)
289+
290+
managementServer := management.NewServer(slog.New(slog.DiscardHandler), "")
291+
err = managementServer.Listen(t.Context(), fmt.Sprintf("%s://%s", managementInterface.Addr().Network(), managementInterface.Addr().String()))
292+
require.NoError(t, err)
293+
294+
t.Cleanup(managementServer.Close)
295+
296+
responseCh := make(chan *management.Response, 1)
297+
errCh := make(chan error, 1)
298+
299+
var clientID uint64
300+
301+
if !strings.HasSuffix(tc.name, "invalid") {
302+
clientID, err = strconv.ParseUint(strings.Split(tc.command, " ")[1], 10, 64)
303+
require.NoError(t, err)
304+
305+
go func() {
306+
response, err := managementServer.AuthPendingPoller(clientID, time.Second*5)
307+
308+
errCh <- err
309+
310+
responseCh <- response
311+
}()
312+
}
313+
314+
var dialer net.Dialer
315+
316+
client, err := dialer.DialContext(t.Context(), "tcp", managementInterface.Addr().String())
317+
require.NoError(t, err)
318+
319+
t.Cleanup(func() {
320+
_ = client.Close()
321+
})
322+
323+
clientReader := bufio.NewReader(client)
324+
325+
testutils.ExpectMessage(t, client, clientReader, openvpn.WelcomeBanner)
326+
testutils.SendMessagef(t, client, tc.command)
327+
328+
testutils.ExpectMessage(t, client, clientReader, tc.resp)
329+
330+
if strings.HasSuffix(tc.name, "invalid") {
331+
return
332+
}
333+
334+
require.NoError(t, <-errCh)
335+
336+
response := <-responseCh
337+
require.NotNil(t, response)
338+
339+
if tc.testFn == nil {
340+
return
341+
}
342+
343+
tc.testFn(t, response)
344+
})
345+
}
346+
}
347+
348+
func TestServer_AuthPendingPoller_Twice(t *testing.T) {
349+
t.Parallel()
350+
351+
managementInterface, err := nettest.NewLocalListener("tcp")
352+
require.NoError(t, err)
353+
354+
err = managementInterface.Close()
355+
require.NoError(t, err)
356+
357+
managementServer := management.NewServer(slog.New(slog.DiscardHandler), "")
358+
err = managementServer.Listen(t.Context(), fmt.Sprintf("%s://%s", managementInterface.Addr().Network(), managementInterface.Addr().String()))
359+
require.NoError(t, err)
360+
361+
t.Cleanup(managementServer.Close)
362+
363+
errCh := make(chan error, 1)
364+
365+
go func() {
366+
_, err := managementServer.AuthPendingPoller(0, time.Millisecond*10)
367+
errCh <- err
368+
}()
369+
370+
go func() {
371+
_, err := managementServer.AuthPendingPoller(0, time.Millisecond*10)
372+
errCh <- err
373+
}()
374+
375+
require.EqualError(t, <-errCh, "poller for client ID 0 already exists")
376+
require.EqualError(t, <-errCh, "timeout waiting for client response")
377+
}
378+
379+
func TestClientAuth_String(t *testing.T) {
380+
t.Parallel()
381+
382+
require.Equal(t, "ACCEPT", management.ClientAuthAccept.String())
383+
require.Equal(t, "DENY", management.ClientAuthDeny.String())
384+
require.Equal(t, "PENDING", management.ClientAuthPending.String())
385+
require.Equal(t, "UNKNOWN", management.ClientAuth(4).String())
386+
}

lib/openvpn-auth-oauth2/openvpn/plugin_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,17 @@ func TestPlugin(t *testing.T) {
233233
// PluginClientDestructorV1
234234
PluginClientDestructorV1(args.Handle, clientContext)
235235
}
236+
237+
func TestPluginOpenV3_InvalidArgs(t *testing.T) {
238+
t.Parallel()
239+
240+
status := PluginOpenV3(0, nil, nil)
241+
require.Equal(t, c.OpenVPNPluginFuncError, status)
242+
}
243+
244+
func TestPluginFuncV3_InvalidArgs(t *testing.T) {
245+
t.Parallel()
246+
247+
status := PluginFuncV3(0, nil, nil)
248+
require.Equal(t, c.OpenVPNPluginFuncError, status)
249+
}

0 commit comments

Comments
 (0)