Skip to content

Commit eb77c23

Browse files
Merge pull request #38 from tg123/auth_unit_test
add auth unit test
2 parents 65eb2f8 + 8c93486 commit eb77c23

File tree

4 files changed

+373
-1
lines changed

4 files changed

+373
-1
lines changed

tests/AuthTests.cs

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Net.Http.Headers;
6+
using System.Security.Cryptography.X509Certificates;
7+
using k8s.Models;
8+
using k8s.Tests.Mock;
9+
using Microsoft.AspNetCore.Hosting;
10+
using Microsoft.AspNetCore.Server.Kestrel.Https;
11+
using Microsoft.Rest;
12+
using Xunit;
13+
14+
namespace k8s.Tests
15+
{
16+
public class AuthTests
17+
{
18+
private static HttpOperationResponse<Corev1PodList> ExecuteListPods(IKubernetes client)
19+
{
20+
return client.ListNamespacedPodWithHttpMessagesAsync("default").Result;
21+
}
22+
23+
[Fact]
24+
public void TestAnonymous()
25+
{
26+
using (var server = new MockKubeApiServer())
27+
{
28+
var client = new Kubernetes(new KubernetesClientConfiguration
29+
{
30+
Host = server.Uri.ToString()
31+
});
32+
33+
var listTask = ExecuteListPods(client);
34+
35+
Assert.True(listTask.Response.IsSuccessStatusCode);
36+
Assert.Equal(1, listTask.Body.Items.Count);
37+
}
38+
39+
using (var server = new MockKubeApiServer(cxt =>
40+
{
41+
cxt.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
42+
return false;
43+
}))
44+
{
45+
var client = new Kubernetes(new KubernetesClientConfiguration
46+
{
47+
Host = server.Uri.ToString()
48+
});
49+
50+
var listTask = ExecuteListPods(client);
51+
52+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
53+
}
54+
}
55+
56+
[Fact]
57+
public void TestBasicAuth()
58+
{
59+
const string testName = "test_name";
60+
const string testPassword = "test_password";
61+
62+
using (var server = new MockKubeApiServer(cxt =>
63+
{
64+
var header = cxt.Request.Headers["Authorization"].FirstOrDefault();
65+
66+
var expect = new AuthenticationHeaderValue("Basic", Utils.Base64Encode($"{testName}:{testPassword}"))
67+
.ToString();
68+
69+
if (header != expect)
70+
{
71+
cxt.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
72+
return false;
73+
}
74+
75+
return true;
76+
}))
77+
{
78+
{
79+
var client = new Kubernetes(new KubernetesClientConfiguration
80+
{
81+
Host = server.Uri.ToString(),
82+
Username = testName,
83+
Password = testPassword
84+
});
85+
86+
var listTask = ExecuteListPods(client);
87+
Assert.True(listTask.Response.IsSuccessStatusCode);
88+
Assert.Equal(1, listTask.Body.Items.Count);
89+
}
90+
91+
{
92+
var client = new Kubernetes(new KubernetesClientConfiguration
93+
{
94+
Host = server.Uri.ToString(),
95+
Username = "wrong name",
96+
Password = testPassword
97+
});
98+
99+
var listTask = ExecuteListPods(client);
100+
101+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
102+
}
103+
104+
{
105+
var client = new Kubernetes(new KubernetesClientConfiguration
106+
{
107+
Host = server.Uri.ToString(),
108+
Username = testName,
109+
Password = "wrong password"
110+
});
111+
112+
var listTask = ExecuteListPods(client);
113+
114+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
115+
}
116+
117+
{
118+
var client = new Kubernetes(new KubernetesClientConfiguration
119+
{
120+
Host = server.Uri.ToString(),
121+
Username = "both wrong",
122+
Password = "wrong password"
123+
});
124+
125+
var listTask = ExecuteListPods(client);
126+
127+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
128+
}
129+
130+
{
131+
var client = new Kubernetes(new KubernetesClientConfiguration
132+
{
133+
Host = server.Uri.ToString()
134+
});
135+
136+
var listTask = ExecuteListPods(client);
137+
138+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
139+
}
140+
141+
{
142+
var client = new Kubernetes(new KubernetesClientConfiguration
143+
{
144+
Host = server.Uri.ToString(),
145+
Username = "xx"
146+
});
147+
148+
var listTask = ExecuteListPods(client);
149+
150+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
151+
}
152+
}
153+
}
154+
155+
[Fact]
156+
public void TestCert()
157+
{
158+
var serverCertificateData = File.ReadAllText("assets/apiserver-pfx-data.txt");
159+
160+
var clientCertificateKeyData = File.ReadAllText("assets/client-key-data.txt");
161+
var clientCertificateData = File.ReadAllText("assets/client-certificate-data.txt");
162+
163+
var serverCertificate = new X509Certificate2(Convert.FromBase64String(serverCertificateData));
164+
var clientCertificate = new X509Certificate2(Convert.FromBase64String(clientCertificateData));
165+
166+
var clientCertificateValidationCalled = false;
167+
168+
using (var server = new MockKubeApiServer(listenConfigure: options =>
169+
{
170+
options.UseHttps(new HttpsConnectionAdapterOptions
171+
{
172+
ServerCertificate = serverCertificate,
173+
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
174+
ClientCertificateValidation = (certificate, chain, valid) =>
175+
{
176+
clientCertificateValidationCalled = true;
177+
return clientCertificate.Equals(certificate);
178+
}
179+
});
180+
}))
181+
{
182+
{
183+
clientCertificateValidationCalled = false;
184+
var client = new Kubernetes(new KubernetesClientConfiguration
185+
{
186+
Host = server.Uri.ToString(),
187+
ClientCertificateData = clientCertificateData,
188+
ClientCertificateKey = clientCertificateKeyData,
189+
SslCaCert = serverCertificate,
190+
SkipTlsVerify = false
191+
});
192+
193+
var listTask = ExecuteListPods(client);
194+
195+
Assert.True(clientCertificateValidationCalled);
196+
Assert.True(listTask.Response.IsSuccessStatusCode);
197+
Assert.Equal(1, listTask.Body.Items.Count);
198+
}
199+
200+
{
201+
clientCertificateValidationCalled = false;
202+
var client = new Kubernetes(new KubernetesClientConfiguration
203+
{
204+
Host = server.Uri.ToString(),
205+
ClientCertificateData = clientCertificateData,
206+
ClientCertificateKey = clientCertificateKeyData,
207+
SkipTlsVerify = true
208+
});
209+
210+
var listTask = ExecuteListPods(client);
211+
212+
Assert.True(clientCertificateValidationCalled);
213+
Assert.True(listTask.Response.IsSuccessStatusCode);
214+
Assert.Equal(1, listTask.Body.Items.Count);
215+
}
216+
217+
{
218+
clientCertificateValidationCalled = false;
219+
var client = new Kubernetes(new KubernetesClientConfiguration
220+
{
221+
Host = server.Uri.ToString(),
222+
ClientCertificate = "assets/client.crt", // TODO amazoning why client.crt != client-data.txt
223+
ClientKey = "assets/client.key", // TODO bad naming param
224+
SkipTlsVerify = true
225+
});
226+
227+
Assert.ThrowsAny<Exception>(() => ExecuteListPods(client));
228+
Assert.True(clientCertificateValidationCalled);
229+
}
230+
231+
{
232+
clientCertificateValidationCalled = false;
233+
var client = new Kubernetes(new KubernetesClientConfiguration
234+
{
235+
Host = server.Uri.ToString(),
236+
SkipTlsVerify = true
237+
});
238+
239+
Assert.ThrowsAny<Exception>(() => ExecuteListPods(client));
240+
Assert.False(clientCertificateValidationCalled);
241+
}
242+
}
243+
}
244+
245+
[Fact]
246+
public void TestToken()
247+
{
248+
const string token = "testingtoken";
249+
250+
using (var server = new MockKubeApiServer(cxt =>
251+
{
252+
var header = cxt.Request.Headers["Authorization"].FirstOrDefault();
253+
254+
var expect = new AuthenticationHeaderValue("Bearer", token).ToString();
255+
256+
if (header != expect)
257+
{
258+
cxt.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
259+
return false;
260+
}
261+
262+
return true;
263+
}))
264+
{
265+
{
266+
var client = new Kubernetes(new KubernetesClientConfiguration
267+
{
268+
Host = server.Uri.ToString(),
269+
AccessToken = token
270+
});
271+
272+
var listTask = ExecuteListPods(client);
273+
Assert.True(listTask.Response.IsSuccessStatusCode);
274+
Assert.Equal(1, listTask.Body.Items.Count);
275+
}
276+
277+
{
278+
var client = new Kubernetes(new KubernetesClientConfiguration
279+
{
280+
Host = server.Uri.ToString(),
281+
AccessToken = "wrong token"
282+
});
283+
284+
var listTask = ExecuteListPods(client);
285+
286+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
287+
}
288+
289+
290+
{
291+
var client = new Kubernetes(new KubernetesClientConfiguration
292+
{
293+
Host = server.Uri.ToString(),
294+
Username = "wrong name",
295+
Password = "same password"
296+
});
297+
298+
var listTask = ExecuteListPods(client);
299+
300+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
301+
}
302+
303+
{
304+
var client = new Kubernetes(new KubernetesClientConfiguration
305+
{
306+
Host = server.Uri.ToString()
307+
});
308+
309+
var listTask = ExecuteListPods(client);
310+
311+
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
312+
}
313+
}
314+
}
315+
}
316+
}

tests/Mock/MockKubeApiServer.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
using System.Linq;
3+
using System.Net;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore;
6+
using Microsoft.AspNetCore.Builder;
7+
using Microsoft.AspNetCore.Hosting;
8+
using Microsoft.AspNetCore.Hosting.Server.Features;
9+
using Microsoft.AspNetCore.Http;
10+
using Microsoft.AspNetCore.Server.Kestrel.Core;
11+
12+
namespace k8s.Tests.Mock
13+
{
14+
public class MockKubeApiServer : IDisposable
15+
{
16+
// paste from minikube /api/v1/namespaces/default/pods
17+
public const string MockPodResponse =
18+
"{\r\n \"kind\": \"PodList\",\r\n \"apiVersion\": \"v1\",\r\n \"metadata\": {\r\n \"selfLink\": \"/api/v1/namespaces/default/pods\",\r\n \"resourceVersion\": \"1762810\"\r\n },\r\n \"items\": [\r\n {\r\n \"metadata\": {\r\n \"name\": \"nginx-1493591563-xb2v4\",\r\n \"generateName\": \"nginx-1493591563-\",\r\n \"namespace\": \"default\",\r\n \"selfLink\": \"/api/v1/namespaces/default/pods/nginx-1493591563-xb2v4\",\r\n \"uid\": \"ac1abb94-9c58-11e7-aaf5-00155d744505\",\r\n \"resourceVersion\": \"1737928\",\r\n \"creationTimestamp\": \"2017-09-18T10:03:51Z\",\r\n \"labels\": {\r\n \"app\": \"nginx\",\r\n \"pod-template-hash\": \"1493591563\"\r\n },\r\n \"annotations\": {\r\n \"kubernetes.io/created-by\": \"{\\\"kind\\\":\\\"SerializedReference\\\",\\\"apiVersion\\\":\\\"v1\\\",\\\"reference\\\":{\\\"kind\\\":\\\"ReplicaSet\\\",\\\"namespace\\\":\\\"default\\\",\\\"name\\\":\\\"nginx-1493591563\\\",\\\"uid\\\":\\\"ac013b63-9c58-11e7-aaf5-00155d744505\\\",\\\"apiVersion\\\":\\\"extensions\\\",\\\"resourceVersion\\\":\\\"5306\\\"}}\\n\"\r\n },\r\n \"ownerReferences\": [\r\n {\r\n \"apiVersion\": \"extensions/v1beta1\",\r\n \"kind\": \"ReplicaSet\",\r\n \"name\": \"nginx-1493591563\",\r\n \"uid\": \"ac013b63-9c58-11e7-aaf5-00155d744505\",\r\n \"controller\": true,\r\n \"blockOwnerDeletion\": true\r\n }\r\n ]\r\n },\r\n \"spec\": {\r\n \"volumes\": [\r\n {\r\n \"name\": \"default-token-3zzcj\",\r\n \"secret\": {\r\n \"secretName\": \"default-token-3zzcj\",\r\n \"defaultMode\": 420\r\n }\r\n }\r\n ],\r\n \"containers\": [\r\n {\r\n \"name\": \"nginx\",\r\n \"image\": \"nginx\",\r\n \"resources\": {},\r\n \"volumeMounts\": [\r\n {\r\n \"name\": \"default-token-3zzcj\",\r\n \"readOnly\": true,\r\n \"mountPath\": \"/var/run/secrets/kubernetes.io/serviceaccount\"\r\n }\r\n ],\r\n \"terminationMessagePath\": \"/dev/termination-log\",\r\n \"terminationMessagePolicy\": \"File\",\r\n \"imagePullPolicy\": \"Always\"\r\n }\r\n ],\r\n \"restartPolicy\": \"Always\",\r\n \"terminationGracePeriodSeconds\": 30,\r\n \"dnsPolicy\": \"ClusterFirst\",\r\n \"serviceAccountName\": \"default\",\r\n \"serviceAccount\": \"default\",\r\n \"nodeName\": \"ubuntu\",\r\n \"securityContext\": {},\r\n \"schedulerName\": \"default-scheduler\"\r\n },\r\n \"status\": {\r\n \"phase\": \"Running\",\r\n \"conditions\": [\r\n {\r\n \"type\": \"Initialized\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-09-18T10:03:51Z\"\r\n },\r\n {\r\n \"type\": \"Ready\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-10-12T07:09:21Z\"\r\n },\r\n {\r\n \"type\": \"PodScheduled\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-09-18T10:03:51Z\"\r\n }\r\n ],\r\n \"hostIP\": \"192.168.188.42\",\r\n \"podIP\": \"172.17.0.5\",\r\n \"startTime\": \"2017-09-18T10:03:51Z\",\r\n \"containerStatuses\": [\r\n {\r\n \"name\": \"nginx\",\r\n \"state\": {\r\n \"running\": {\r\n \"startedAt\": \"2017-10-12T07:09:20Z\"\r\n }\r\n },\r\n \"lastState\": {\r\n \"terminated\": {\r\n \"exitCode\": 0,\r\n \"reason\": \"Completed\",\r\n \"startedAt\": \"2017-10-10T21:35:51Z\",\r\n \"finishedAt\": \"2017-10-12T07:07:37Z\",\r\n \"containerID\": \"docker://94df3f3965807421ad6dc76618e00b76cb15d024919c4946f3eb46a92659c62a\"\r\n }\r\n },\r\n \"ready\": true,\r\n \"restartCount\": 7,\r\n \"image\": \"nginx:latest\",\r\n \"imageID\": \"docker-pullable://nginx@sha256:004ac1d5e791e705f12a17c80d7bb1e8f7f01aa7dca7deee6e65a03465392072\",\r\n \"containerID\": \"docker://fa11bdd48c9b7d3a6c4c3f9b6d7319743c3455ab8d00c57d59c083b319b88194\"\r\n }\r\n ],\r\n \"qosClass\": \"BestEffort\"\r\n }\r\n }\r\n ]\r\n}"
19+
;
20+
21+
private readonly IWebHost _webHost;
22+
23+
public MockKubeApiServer(Func<HttpContext, bool> shouldNext = null, Action<ListenOptions> listenConfigure = null,
24+
string resp = MockPodResponse)
25+
{
26+
shouldNext = shouldNext ?? (_ => true);
27+
listenConfigure = listenConfigure ?? (_ => { });
28+
29+
_webHost = WebHost.CreateDefaultBuilder()
30+
.Configure(app => app.Run(httpContext =>
31+
{
32+
if (shouldNext(httpContext))
33+
{
34+
httpContext.Response.WriteAsync(resp);
35+
}
36+
37+
return Task.Delay(0);
38+
}))
39+
.UseKestrel(options => { options.Listen(IPAddress.Loopback, 0, listenConfigure); })
40+
.Build();
41+
42+
_webHost.Start();
43+
}
44+
45+
public Uri Uri => _webHost.ServerFeatures.Get<IServerAddressesFeature>().Addresses
46+
.Select(a => new Uri(a)).First();
47+
48+
public void Dispose()
49+
{
50+
_webHost.StopAsync();
51+
_webHost.WaitForShutdown();
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)