Senaste version från LIL-GIT 2017-09-12. MICK
git-tfs-id: [https://tfs.rsv.se/tfs/LoadTestCollection]$/VSTT-Plugins;C1190
Showing
5 changed files
with
286 additions
and
40 deletions
... | @@ -18,10 +18,46 @@ using System.ComponentModel; | ... | @@ -18,10 +18,46 @@ using System.ComponentModel; |
18 | using Microsoft.VisualStudio.TestTools.WebTesting.Rules; | 18 | using Microsoft.VisualStudio.TestTools.WebTesting.Rules; |
19 | using System.Text.RegularExpressions; | 19 | using System.Text.RegularExpressions; |
20 | using System.IO; | 20 | using System.IO; |
21 | using System.IO.Compression; | ||
21 | using Microsoft.VisualStudio.TestTools.LoadTesting; | 22 | using Microsoft.VisualStudio.TestTools.LoadTesting; |
22 | 23 | ||
23 | namespace LIL_VSTT_Plugins | 24 | namespace LIL_VSTT_Plugins |
24 | { | 25 | { |
26 | [DisplayName("Zip File Upload"), Description("Creates an ZIP archive of each of the files to be uploaded using the files name and adding .zip. Warning, uses %TEMP% for temp storage.")] | ||
27 | public class ZipFileUploadBeforePost : WebTestRequestPlugin | ||
28 | { | ||
29 | Queue<String> deleteDirs = new Queue<string>(); | ||
30 | public override void PreRequest(object sender, PreRequestEventArgs e) | ||
31 | { | ||
32 | if (e.Request.Body.GetType() == typeof(FormPostHttpBody)) { | ||
33 | FormPostHttpBody body = (FormPostHttpBody)e.Request.Body; | ||
34 | foreach (FormPostParameter param in body.FormPostParameters) { | ||
35 | if(param.GetType() == typeof(FileUploadParameter)) | ||
36 | { | ||
37 | FileUploadParameter fparam = (FileUploadParameter)param; | ||
38 | String tempDir = Path.GetTempPath() + "\\" + Guid.NewGuid().ToString(); | ||
39 | Directory.CreateDirectory(tempDir); | ||
40 | deleteDirs.Enqueue(tempDir); | ||
41 | Directory.CreateDirectory(tempDir + @"\ZipDir"); | ||
42 | File.Copy(fparam.FileName, tempDir + @"\ZipDir\" + Path.GetFileName(fparam.FileName)); | ||
43 | ZipFile.CreateFromDirectory(tempDir + @"\ZipDir", tempDir + "\\" + Path.GetFileName(fparam.FileName) + ".zip"); | ||
44 | |||
45 | fparam.FileName = tempDir + "\\" + Path.GetFileName(fparam.FileName) + ".zip"; | ||
46 | fparam.FileUploadName = fparam.FileUploadName + ".zip"; | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | base.PreRequest(sender, e); | ||
51 | } | ||
52 | |||
53 | public override void PostRequest(object sender, PostRequestEventArgs e) | ||
54 | { | ||
55 | foreach (String dir in deleteDirs) Directory.Delete(dir, true); | ||
56 | deleteDirs.Clear(); | ||
57 | base.PostRequest(sender, e); | ||
58 | } | ||
59 | } | ||
60 | |||
25 | [DisplayName("Set Request Think Time"), Description("Changes the thinktime on requests with a set thinktime over 0 to the value of the ThinkTime context parameter")] | 61 | [DisplayName("Set Request Think Time"), Description("Changes the thinktime on requests with a set thinktime over 0 to the value of the ThinkTime context parameter")] |
26 | public class SetRequestThinkTime : WebTestPlugin | 62 | public class SetRequestThinkTime : WebTestPlugin |
27 | { | 63 | { |
... | @@ -208,6 +244,9 @@ namespace LIL_VSTT_Plugins | ... | @@ -208,6 +244,9 @@ namespace LIL_VSTT_Plugins |
208 | [DisplayName("File Name"), Description("The file name to use for logging")] | 244 | [DisplayName("File Name"), Description("The file name to use for logging")] |
209 | public String Filename { get; set; } | 245 | public String Filename { get; set; } |
210 | 246 | ||
247 | [DisplayName("Autogenerate File Name"), Description("Automatically generate a filename using the test name and user id from the load test")] | ||
248 | public bool autoFilename { get; set; } | ||
249 | |||
211 | [DisplayName("Write header"), DefaultValue(false), Description("Writes the parameter names as a header. Will write a new header for each user (dont use in loadtest)")] | 250 | [DisplayName("Write header"), DefaultValue(false), Description("Writes the parameter names as a header. Will write a new header for each user (dont use in loadtest)")] |
212 | public bool useHeader { get; set; } | 251 | public bool useHeader { get; set; } |
213 | 252 | ||
... | @@ -221,6 +260,11 @@ namespace LIL_VSTT_Plugins | ... | @@ -221,6 +260,11 @@ namespace LIL_VSTT_Plugins |
221 | 260 | ||
222 | public override void PostWebTest(object sender, PostWebTestEventArgs e) | 261 | public override void PostWebTest(object sender, PostWebTestEventArgs e) |
223 | { | 262 | { |
263 | String completeFileName = Filename + e.WebTest.Context.WebTestUserId + ".log"; | ||
264 | if (autoFilename) | ||
265 | { | ||
266 | completeFileName = e.WebTest.Name + e.WebTest.Context.WebTestUserId + ".log"; | ||
267 | } | ||
224 | if ((e.WebTest.Outcome == Outcome.Pass && logPassed) || (e.WebTest.Outcome == Outcome.Fail && logFailed)) { | 268 | if ((e.WebTest.Outcome == Outcome.Pass && logPassed) || (e.WebTest.Outcome == Outcome.Fail && logFailed)) { |
225 | string row = ""; | 269 | string row = ""; |
226 | string hrow = ""; | 270 | string hrow = ""; |
... | @@ -240,10 +284,10 @@ namespace LIL_VSTT_Plugins | ... | @@ -240,10 +284,10 @@ namespace LIL_VSTT_Plugins |
240 | } | 284 | } |
241 | if (header && useHeader) | 285 | if (header && useHeader) |
242 | { | 286 | { |
243 | File.AppendAllText(Filename + e.WebTest.Context.WebTestUserId + ".log", hrow + "\r\n"); | 287 | File.AppendAllText(completeFileName, hrow + "\r\n"); |
244 | header = false; | 288 | header = false; |
245 | } | 289 | } |
246 | File.AppendAllText(Filename + e.WebTest.Context.WebTestUserId + ".log", row + "\r\n"); | 290 | File.AppendAllText(completeFileName, row + "\r\n"); |
247 | base.PostWebTest(sender, e); | 291 | base.PostWebTest(sender, e); |
248 | } | 292 | } |
249 | } | 293 | } | ... | ... |
... | @@ -13,6 +13,10 @@ | ... | @@ -13,6 +13,10 @@ |
13 | <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | 13 | <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
14 | <FileAlignment>512</FileAlignment> | 14 | <FileAlignment>512</FileAlignment> |
15 | <TargetFrameworkProfile /> | 15 | <TargetFrameworkProfile /> |
16 | <SccProjectName>SAK</SccProjectName> | ||
17 | <SccLocalPath>SAK</SccLocalPath> | ||
18 | <SccAuxPath>SAK</SccAuxPath> | ||
19 | <SccProvider>SAK</SccProvider> | ||
16 | </PropertyGroup> | 20 | </PropertyGroup> |
17 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | 21 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
18 | <DebugSymbols>true</DebugSymbols> | 22 | <DebugSymbols>true</DebugSymbols> |
... | @@ -41,6 +45,8 @@ | ... | @@ -41,6 +45,8 @@ |
41 | <Reference Include="Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> | 45 | <Reference Include="Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> |
42 | <Reference Include="System" /> | 46 | <Reference Include="System" /> |
43 | <Reference Include="System.Core" /> | 47 | <Reference Include="System.Core" /> |
48 | <Reference Include="System.IO.Compression" /> | ||
49 | <Reference Include="System.IO.Compression.FileSystem" /> | ||
44 | <Reference Include="System.Xml.Linq" /> | 50 | <Reference Include="System.Xml.Linq" /> |
45 | <Reference Include="System.Data.DataSetExtensions" /> | 51 | <Reference Include="System.Data.DataSetExtensions" /> |
46 | <Reference Include="Microsoft.CSharp" /> | 52 | <Reference Include="Microsoft.CSharp" /> | ... | ... |
... | @@ -52,6 +52,31 @@ namespace LIL_VSTT_Plugins | ... | @@ -52,6 +52,31 @@ namespace LIL_VSTT_Plugins |
52 | } | 52 | } |
53 | 53 | ||
54 | /// <summary> | 54 | /// <summary> |
55 | /// LoadTest Context Copy | ||
56 | /// </summary> | ||
57 | [DisplayName("Personnummer Generator")] | ||
58 | [Description("(C) Copyright 2017 LIGHTS IN LINE AB\r\nGenererar personnummer och sätter dem som parameter till testernas context.")] | ||
59 | public class LoadTestPnumGen : ILoadTestPlugin | ||
60 | { | ||
61 | //store the load test object. | ||
62 | LoadTest mLoadTest; | ||
63 | |||
64 | public void Initialize(LoadTest loadTest) | ||
65 | { | ||
66 | mLoadTest = loadTest; | ||
67 | |||
68 | //connect to the TestStarting event. | ||
69 | mLoadTest.TestStarting += new EventHandler<TestStartingEventArgs>(mLoadTest_TestStarting); | ||
70 | } | ||
71 | |||
72 | |||
73 | void mLoadTest_TestStarting(object sender, TestStartingEventArgs e) | ||
74 | { | ||
75 | //TODO | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /// <summary> | ||
55 | /// Service Manager Plugin | 80 | /// Service Manager Plugin |
56 | /// </summary> | 81 | /// </summary> |
57 | [DisplayName("Service Manager Config")] | 82 | [DisplayName("Service Manager Config")] |
... | @@ -457,6 +482,7 @@ namespace LIL_VSTT_Plugins | ... | @@ -457,6 +482,7 @@ namespace LIL_VSTT_Plugins |
457 | string input = null; | 482 | string input = null; |
458 | int lineNum = 0; | 483 | int lineNum = 0; |
459 | int dataNum = 0; | 484 | int dataNum = 0; |
485 | char[] trim = { ' ', '\x00', '\t', '\x20' }; | ||
460 | while ((input = re.ReadLine()) != null) | 486 | while ((input = re.ReadLine()) != null) |
461 | { | 487 | { |
462 | // Ignore blank lines and empty lines (just whitespace) or lines with only blank/empty/whitespace columns | 488 | // Ignore blank lines and empty lines (just whitespace) or lines with only blank/empty/whitespace columns |
... | @@ -466,7 +492,7 @@ namespace LIL_VSTT_Plugins | ... | @@ -466,7 +492,7 @@ namespace LIL_VSTT_Plugins |
466 | if (lineNum == 1 && myHasColName == true) | 492 | if (lineNum == 1 && myHasColName == true) |
467 | { | 493 | { |
468 | // First line is column names | 494 | // First line is column names |
469 | myColNames = input.TrimEnd(); | 495 | myColNames = input.TrimEnd(trim); |
470 | } | 496 | } |
471 | else | 497 | else |
472 | { | 498 | { |
... | @@ -477,13 +503,13 @@ namespace LIL_VSTT_Plugins | ... | @@ -477,13 +503,13 @@ namespace LIL_VSTT_Plugins |
477 | else ifAgentId = dataNum + 1; | 503 | else ifAgentId = dataNum + 1; |
478 | if (ifAgentId == agentId) | 504 | if (ifAgentId == agentId) |
479 | { | 505 | { |
480 | myParams.Add(input.TrimEnd()); | 506 | myParams.Add(input.TrimEnd(trim)); |
481 | } | 507 | } |
482 | dataNum++; | 508 | dataNum++; |
483 | } | 509 | } |
484 | else | 510 | else |
485 | { | 511 | { |
486 | myParams.Add(input.TrimEnd()); | 512 | myParams.Add(input.TrimEnd(trim)); |
487 | } | 513 | } |
488 | } | 514 | } |
489 | } | 515 | } | ... | ... |
... | @@ -20,6 +20,7 @@ using System.IO; | ... | @@ -20,6 +20,7 @@ using System.IO; |
20 | using System.ComponentModel; | 20 | using System.ComponentModel; |
21 | using System.Text.RegularExpressions; | 21 | using System.Text.RegularExpressions; |
22 | using System.Security.Cryptography.X509Certificates; | 22 | using System.Security.Cryptography.X509Certificates; |
23 | using System.Diagnostics; | ||
23 | 24 | ||
24 | namespace LIL_VSTT_Plugins | 25 | namespace LIL_VSTT_Plugins |
25 | { | 26 | { |
... | @@ -233,6 +234,54 @@ namespace LIL_VSTT_Plugins | ... | @@ -233,6 +234,54 @@ namespace LIL_VSTT_Plugins |
233 | } | 234 | } |
234 | 235 | ||
235 | /// <summary> | 236 | /// <summary> |
237 | /// Slå på URL encode på query string parametrar | ||
238 | /// </summary> | ||
239 | [DisplayName("URL Encode Query String Parameter")] | ||
240 | [Description("(C) Copyright 2011 LIGHTS IN LINE AB\r\nTvingar en URL Encode på angiven Query String parameter i alla request")] | ||
241 | public class URLEncodeQueryStringParameter : WebTestPlugin | ||
242 | { | ||
243 | [DisplayName("Query String Parameter Name")] | ||
244 | [Description("Name of the query string parameter to URL encode before each request")] | ||
245 | public String paramName { get; set; } | ||
246 | public override void PreRequest(object sender, PreRequestEventArgs e) | ||
247 | { | ||
248 | if (e.Request.HasQueryStringParameters) | ||
249 | { | ||
250 | if (e.Request.QueryStringParameters.Contains(paramName)) | ||
251 | { | ||
252 | foreach (QueryStringParameter qsp in e.Request.QueryStringParameters) { | ||
253 | if (qsp.Name.Equals(paramName)) | ||
254 | { | ||
255 | qsp.UrlEncode = true; | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | |||
263 | /// <summary> | ||
264 | /// Loggar alla transaktioners svarstider som context parametrar | ||
265 | /// </summary> | ||
266 | [DisplayName("Transaction Response Times to Context")] | ||
267 | [Description("(C) Copyright 2016 LIGHTS IN LINE AB\r\nLoggar alla transaktioners svarstider som context parametrar")] | ||
268 | public class TransactionsToContext : WebTestPlugin | ||
269 | { | ||
270 | public override void PostTransaction(object sender, PostTransactionEventArgs e) | ||
271 | { | ||
272 | base.PostTransaction(sender, e); | ||
273 | if (!e.WebTest.Context.ContainsKey(e.TransactionName)) | ||
274 | { | ||
275 | e.WebTest.Context.Add(e.TransactionName, e.Duration.TotalMilliseconds.ToString()); | ||
276 | } | ||
277 | else | ||
278 | { | ||
279 | e.WebTest.Context[e.TransactionName] = e.Duration.TotalMilliseconds.ToString(); | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | |||
284 | /// <summary> | ||
236 | /// Ignorerar status koder under 500. | 285 | /// Ignorerar status koder under 500. |
237 | /// </summary> | 286 | /// </summary> |
238 | [DisplayName("Ignore 4xx status codes")] | 287 | [DisplayName("Ignore 4xx status codes")] |
... | @@ -306,16 +355,70 @@ namespace LIL_VSTT_Plugins | ... | @@ -306,16 +355,70 @@ namespace LIL_VSTT_Plugins |
306 | [Description("Default inte påslaget. Om servern inte stödjer TLS1.2 kommer SSL handskakningen att avbrytas och requestet failar. Kräver .NET 4.5 samt att TLS1.2 är aktiverat i SChannel (använd bifogad schannel_high.reg om det inte är påslaget på äldre windows versioner)")] | 355 | [Description("Default inte påslaget. Om servern inte stödjer TLS1.2 kommer SSL handskakningen att avbrytas och requestet failar. Kräver .NET 4.5 samt att TLS1.2 är aktiverat i SChannel (använd bifogad schannel_high.reg om det inte är påslaget på äldre windows versioner)")] |
307 | public bool useTls12 { get; set; } | 356 | public bool useTls12 { get; set; } |
308 | 357 | ||
358 | [DisplayName("Apply Override"), DefaultValue(false)] | ||
359 | [Description("Ändrar proxy inställningarna för detta webtest enligt detta plugin")] | ||
360 | [Category("Web Proxy")] | ||
361 | public bool proxyOverride { get; set; } | ||
362 | |||
363 | [DisplayName("Proxy URI"), DefaultValue("http://host:port")] | ||
364 | [Description("Anger proxyserverns URI (http://host:port)")] | ||
365 | [Category("Web Proxy")] | ||
366 | public string proxyURI { get; set; } | ||
367 | |||
368 | [DisplayName("Bypass Local"), DefaultValue(false)] | ||
369 | [Description("True för att inte använda proxy på lokala hostnamn, i.e. utan domännamn")] | ||
370 | [Category("Web Proxy")] | ||
371 | public bool proxyBypassLocal { get; set; } | ||
372 | |||
373 | [DisplayName("Bypass RegExp"), DefaultValue("")] | ||
374 | [Description("Sätter ett reguljärt uttryck för URI (hostnamn) som INTE ska gå via proxyn")] | ||
375 | [Category("Web Proxy")] | ||
376 | public string proxyBypass { get; set; } | ||
377 | |||
378 | [DisplayName("User Name"), DefaultValue("")] | ||
379 | [Description("Sätter användarnamn för proxyn")] | ||
380 | [Category("Web Proxy")] | ||
381 | public string proxyUser { get; set; } | ||
382 | |||
383 | [DisplayName("User Password"), DefaultValue("")] | ||
384 | [Description("Sätter swedbanks proxy (temp lösning)")] | ||
385 | [Category("Web Proxy")] | ||
386 | public string proxyPass { get; set; } | ||
387 | |||
388 | System.Net.WebProxy myProxy = null; | ||
389 | |||
309 | public override void PreWebTest(object sender, PreWebTestEventArgs e) | 390 | public override void PreWebTest(object sender, PreWebTestEventArgs e) |
310 | { | 391 | { |
311 | base.PreWebTest(sender, e); | 392 | base.PreWebTest(sender, e); |
393 | |||
394 | if (proxyOverride) | ||
395 | { | ||
396 | if (myProxy == null) | ||
397 | { | ||
398 | myProxy = new System.Net.WebProxy(); | ||
399 | if(!String.IsNullOrWhiteSpace(proxyURI)) myProxy.Address = new Uri(proxyURI); | ||
400 | myProxy.BypassProxyOnLocal = proxyBypassLocal; | ||
401 | if (!String.IsNullOrWhiteSpace(proxyBypass)) myProxy.BypassList = new string[] { proxyBypass }; | ||
402 | if (!String.IsNullOrWhiteSpace(proxyUser)) myProxy.Credentials = new System.Net.NetworkCredential(proxyUser, proxyPass); | ||
403 | } | ||
404 | // Change the webtests proxy setting | ||
405 | e.WebTest.Proxy = "lil"; | ||
406 | e.WebTest.WebProxy = myProxy; | ||
407 | // Set context parameters | ||
408 | e.WebTest.Context["proxyOverride"] = proxyOverride; | ||
409 | e.WebTest.Context["proxyURI"] = proxyURI; | ||
410 | e.WebTest.Context["proxyBypassLocal"] = proxyBypassLocal; | ||
411 | e.WebTest.Context["proxyBypass"] = proxyBypass; | ||
412 | e.WebTest.Context["proxyUser"] = proxyUser; | ||
413 | e.WebTest.Context["proxyPass"] = proxyPass; | ||
414 | } | ||
415 | |||
312 | System.Net.ServicePointManager.Expect100Continue = exp100; | 416 | System.Net.ServicePointManager.Expect100Continue = exp100; |
313 | System.Net.ServicePointManager.MaxServicePointIdleTime = maxIdle; | 417 | System.Net.ServicePointManager.MaxServicePointIdleTime = maxIdle; |
314 | System.Net.ServicePointManager.SetTcpKeepAlive(keepAlive, timeOut, interVal); | 418 | System.Net.ServicePointManager.SetTcpKeepAlive(keepAlive, timeOut, interVal); |
315 | System.Net.ServicePointManager.UseNagleAlgorithm = useNagle; | 419 | System.Net.ServicePointManager.UseNagleAlgorithm = useNagle; |
316 | if(useTls12) System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12; | 420 | if(useTls12) System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12; |
317 | } | 421 | } |
318 | |||
319 | } | 422 | } |
320 | 423 | ||
321 | /// <summary> | 424 | /// <summary> |
... | @@ -558,21 +661,21 @@ namespace LIL_VSTT_Plugins | ... | @@ -558,21 +661,21 @@ namespace LIL_VSTT_Plugins |
558 | /// WebTest Client Certificate | 661 | /// WebTest Client Certificate |
559 | /// </summary> | 662 | /// </summary> |
560 | [DisplayName("Client Certificate")] | 663 | [DisplayName("Client Certificate")] |
561 | [Description("(C) Copyright 2016 LIGHTS IN LINE AB\r\nSätter webtestet att använda ett specifikt client cert för SSL. Certifikatet behöver inte installeras i certstore först.")] | 664 | [Description("(C) Copyright 2016 LIGHTS IN LINE AB\r\nSätter webtestet att använda ett specifikt client cert för SSL. Certifikatet installeras automatiskt i Windows User Certificate Store.")] |
562 | public class ClientCertificatePlugin : WebTestPlugin | 665 | public class ClientCertificatePlugin : WebTestPlugin |
563 | { | 666 | { |
564 | [DisplayName("Certificate Path")] | 667 | [DisplayName("Certificate Path")] |
565 | [Description("Sökvägen till certifikatfilen (.P12/.PFX/.PEM med privat nyckel)")] | 668 | [Description("Sökvägen till certifikatfilen (P12/PFX/PEM med privat nyckel eller CER/DER utan privat nyckel)")] |
566 | [DefaultValue("")] | 669 | [DefaultValue("")] |
567 | public string pCertificatePath { get; set; } | 670 | public string pCertificatePath { get; set; } |
568 | 671 | ||
569 | [DisplayName("Certificate Path Parameter")] | 672 | [DisplayName("Certificate Path Parameter")] |
570 | [Description("Ange namn på parameter som ska användas för sökvägen till certifikatfilen (.P12/.PFX/.PEM). Om parametern saknas eller är tom används Certificate Path")] | 673 | [Description("Ange namn på parameter som ska användas för sökvägen till certifikatfilen. Om parametern saknas eller är tom används Certificate Path")] |
571 | [DefaultValue("")] | 674 | [DefaultValue("")] |
572 | public string pCertificatePathParameter { get; set; } | 675 | public string pCertificatePathParameter { get; set; } |
573 | 676 | ||
574 | [DisplayName("Certificate Password")] | 677 | [DisplayName("Certificate Password")] |
575 | [Description("Ange lösenordet för att öppna certifikatfilen")] | 678 | [Description("Ange lösenordet för att öppna skyddade/krypterade filer")] |
576 | [DefaultValue("")] | 679 | [DefaultValue("")] |
577 | public string pCertificatePassword { get; set; } | 680 | public string pCertificatePassword { get; set; } |
578 | 681 | ||
... | @@ -598,19 +701,25 @@ namespace LIL_VSTT_Plugins | ... | @@ -598,19 +701,25 @@ namespace LIL_VSTT_Plugins |
598 | 701 | ||
599 | private bool haveCert = false; | 702 | private bool haveCert = false; |
600 | private X509Certificate2 myClientCertAndKey; | 703 | private X509Certificate2 myClientCertAndKey; |
704 | private Regex p12RegExp = new Regex(@"p12$|pfx$",RegexOptions.IgnoreCase); | ||
705 | private Regex cerRegExp = new Regex(@"cer$|der$", RegexOptions.IgnoreCase); | ||
706 | private Regex pemRegExp = new Regex(@"pem$", RegexOptions.IgnoreCase); | ||
601 | 707 | ||
602 | public override void PreWebTest(object sender, PreWebTestEventArgs e) | 708 | public override void PreWebTest(object sender, PreWebTestEventArgs e) |
603 | { | 709 | { |
710 | Stopwatch sw = new Stopwatch(); | ||
711 | sw.Start(); | ||
712 | |||
604 | base.PreWebTest(sender, e); | 713 | base.PreWebTest(sender, e); |
605 | String certPath, certPass; | 714 | String certPath, certPass; |
606 | // Ladda in certifikatet och sätt CertPolicy | 715 | // Ladda in certifikatet och sätt CertPolicy |
607 | 716 | ||
608 | if (!String.IsNullOrWhiteSpace(pCertificatePathParameter) && e.WebTest.Context.ContainsKey(pCertificatePathParameter) && !String.IsNullOrWhiteSpace(e.WebTest.Context[pCertificatePathParameter].ToString()) ) | 717 | if (!String.IsNullOrWhiteSpace(pCertificatePathParameter) && e.WebTest.Context.ContainsKey(pCertificatePathParameter) && !String.IsNullOrWhiteSpace(e.WebTest.Context[pCertificatePathParameter].ToString()) ) |
609 | { | 718 | { |
610 | certPath = e.WebTest.Context[pCertificatePathParameter].ToString(); | 719 | certPath = e.WebTest.Context[pCertificatePathParameter].ToString().Trim(); |
611 | } else | 720 | } else |
612 | { | 721 | { |
613 | certPath = pCertificatePath; | 722 | certPath = pCertificatePath.Trim(); |
614 | } | 723 | } |
615 | 724 | ||
616 | if (!String.IsNullOrWhiteSpace(pCertificatePasswordParameter) && e.WebTest.Context.ContainsKey(pCertificatePasswordParameter) && !String.IsNullOrWhiteSpace(e.WebTest.Context[pCertificatePasswordParameter].ToString())) | 725 | if (!String.IsNullOrWhiteSpace(pCertificatePasswordParameter) && e.WebTest.Context.ContainsKey(pCertificatePasswordParameter) && !String.IsNullOrWhiteSpace(e.WebTest.Context[pCertificatePasswordParameter].ToString())) |
... | @@ -629,11 +738,11 @@ namespace LIL_VSTT_Plugins | ... | @@ -629,11 +738,11 @@ namespace LIL_VSTT_Plugins |
629 | return; | 738 | return; |
630 | } | 739 | } |
631 | 740 | ||
632 | // Check what type of container we have. All files are treated as PEM unless the extension is .pfx or .p12 | 741 | // Check what type of container we have. All files are treated as PEM unless the extension matches our winX509regExp regular expression (see above) |
633 | // Read certificate and private key depending on type | 742 | // Read certificate and private key depending on type |
634 | if (certPath.ToLower().EndsWith(".pfx") || certPath.ToLower().EndsWith(".p12")) | 743 | if (p12RegExp.IsMatch(certPath)) |
635 | { | 744 | { |
636 | if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as PFX/PKCS12"); | 745 | if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as PFX/P12"); |
637 | try | 746 | try |
638 | { | 747 | { |
639 | myClientCertAndKey = new X509Certificate2(certPath, certPass, X509KeyStorageFlags.PersistKeySet); | 748 | myClientCertAndKey = new X509Certificate2(certPath, certPass, X509KeyStorageFlags.PersistKeySet); |
... | @@ -643,9 +752,21 @@ namespace LIL_VSTT_Plugins | ... | @@ -643,9 +752,21 @@ namespace LIL_VSTT_Plugins |
643 | e.WebTest.AddCommentToResult("Error during loading of certificate: " + certPath + " Message: " + ex.Message); | 752 | e.WebTest.AddCommentToResult("Error during loading of certificate: " + certPath + " Message: " + ex.Message); |
644 | return; | 753 | return; |
645 | } | 754 | } |
646 | } else | 755 | } else if (cerRegExp.IsMatch(certPath)) |
647 | { | 756 | { |
648 | if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as PEM/PKCS8"); | 757 | if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as CER/DER without private key"); |
758 | try | ||
759 | { | ||
760 | myClientCertAndKey = new X509Certificate2(certPath, certPass); | ||
761 | } | ||
762 | catch (Exception ex) | ||
763 | { | ||
764 | e.WebTest.AddCommentToResult("Error during loading of certificate: " + certPath + " Message: " + ex.Message); | ||
765 | return; | ||
766 | } | ||
767 | } else if (pemRegExp.IsMatch(certPath)) | ||
768 | { | ||
769 | if (pDebug) e.WebTest.AddCommentToResult("Certificate file is treated as OpenSSL encoded PEM"); | ||
649 | 770 | ||
650 | // Use Bouncy Castle to read the certificate and key, then convert to .NET X509Certificate2 and X509Certificate | 771 | // Use Bouncy Castle to read the certificate and key, then convert to .NET X509Certificate2 and X509Certificate |
651 | String text; | 772 | String text; |
... | @@ -698,19 +819,24 @@ namespace LIL_VSTT_Plugins | ... | @@ -698,19 +819,24 @@ namespace LIL_VSTT_Plugins |
698 | } | 819 | } |
699 | keyTextBeginPos = text.IndexOf("-----BEGIN", keyTextEndPos); | 820 | keyTextBeginPos = text.IndexOf("-----BEGIN", keyTextEndPos); |
700 | } | 821 | } |
701 | if (bcKey == null || bcCert == null) | 822 | if (bcCert == null) |
702 | { | 823 | { |
703 | e.WebTest.AddCommentToResult("Error: PEM file has to contain both certificate and private key"); | 824 | e.WebTest.AddCommentToResult("Error: PEM file has to contain an x509 certificate"); |
704 | return; | 825 | return; |
705 | } | 826 | } |
706 | try { | 827 | try { |
707 | myClientCertAndKey = new X509Certificate2(Org.BouncyCastle.Security.DotNetUtilities.ToX509Certificate(bcCert)); | 828 | myClientCertAndKey = new X509Certificate2(Org.BouncyCastle.Security.DotNetUtilities.ToX509Certificate(bcCert)); |
708 | myClientCertAndKey.PrivateKey = Org.BouncyCastle.Security.DotNetUtilities.ToRSA(bcKey.Private as Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters); | 829 | if(bcKey != null) myClientCertAndKey.PrivateKey = Org.BouncyCastle.Security.DotNetUtilities.ToRSA(bcKey.Private as Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters); |
709 | } catch (Exception ex) | 830 | } catch (Exception ex) |
710 | { | 831 | { |
711 | e.WebTest.AddCommentToResult("Error during loading of PEM file: " + certPath + " Message: " + ex.Message); | 832 | e.WebTest.AddCommentToResult("Error during loading of PEM file: " + certPath + " Message: " + ex.Message); |
712 | return; | 833 | return; |
713 | } | 834 | } |
835 | } else | ||
836 | { | ||
837 | // Unknown or unsuported format | ||
838 | e.WebTest.AddCommentToResult("Error during loading of file: " + certPath + " Message: Unsupported format"); | ||
839 | return; | ||
714 | } | 840 | } |
715 | 841 | ||
716 | // Check that we have a certificate | 842 | // Check that we have a certificate |
... | @@ -721,16 +847,17 @@ namespace LIL_VSTT_Plugins | ... | @@ -721,16 +847,17 @@ namespace LIL_VSTT_Plugins |
721 | } | 847 | } |
722 | else | 848 | else |
723 | { | 849 | { |
724 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " loaded successfully."); | 850 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " loaded successfully in " + sw.ElapsedMilliseconds + "ms"); |
725 | } | 851 | } |
726 | 852 | ||
727 | // Check that it seems okey | 853 | // Check that it seems okey |
728 | if (string.IsNullOrWhiteSpace(myClientCertAndKey.GetCertHashString())) | 854 | if (string.IsNullOrWhiteSpace(myClientCertAndKey.Thumbprint)) |
729 | { | 855 | { |
730 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " contains no SHA1 hash. Not using it."); | 856 | if (pDebug) e.WebTest.AddCommentToResult("Certificate File " + certPath + " contains no Thumbprint. Not using it."); |
731 | return; | 857 | return; |
732 | } | 858 | } |
733 | if (pDebug) e.WebTest.AddCommentToResult("Loaded client certificate for Subject: [" + myClientCertAndKey.Subject + "] Issued by: [" + myClientCertAndKey.Issuer + "] Expires: [" + myClientCertAndKey.GetExpirationDateString() + "]"); | 859 | if (pDebug) e.WebTest.AddCommentToResult("Subject: [" + myClientCertAndKey.Subject + "]"); |
860 | if (pDebug) e.WebTest.AddCommentToResult("Issued by: [" + myClientCertAndKey.Issuer + "] Expires: [" + myClientCertAndKey.GetExpirationDateString() + "]"); | ||
734 | 861 | ||
735 | // Check if the certificate is trusted (i.e. chain can be validated) | 862 | // Check if the certificate is trusted (i.e. chain can be validated) |
736 | bool myCertTrusted = false; | 863 | bool myCertTrusted = false; |
... | @@ -747,24 +874,19 @@ namespace LIL_VSTT_Plugins | ... | @@ -747,24 +874,19 @@ namespace LIL_VSTT_Plugins |
747 | // Check if it is expired or about to expire | 874 | // Check if it is expired or about to expire |
748 | if(myClientCertAndKey.NotAfter < DateTime.Now) | 875 | if(myClientCertAndKey.NotAfter < DateTime.Now) |
749 | { | 876 | { |
750 | e.WebTest.AddCommentToResult("Warning: Client Certificate has expired. Might not be trusted on server. Expired " + myClientCertAndKey.NotAfter.ToString()); | 877 | e.WebTest.AddCommentToResult("Warning: Client Certificate has expired. Might not be trusted on server."); |
751 | } else if (myClientCertAndKey.NotBefore > DateTime.Now) | 878 | } else if (myClientCertAndKey.NotBefore > DateTime.Now) |
752 | { | 879 | { |
753 | e.WebTest.AddCommentToResult("Warning: Client Certificate is not valid yet. Might not be trusted on server. Valid " + myClientCertAndKey.NotBefore.ToString()); | 880 | e.WebTest.AddCommentToResult("Warning: Client Certificate is not valid yet. Might not be trusted on server. Valid on " + myClientCertAndKey.NotBefore.ToString()); |
754 | } else if (myClientCertAndKey.NotAfter < DateTime.Now.AddDays(14)) | 881 | } else if (myClientCertAndKey.NotAfter < DateTime.Now.AddDays(14)) |
755 | { | 882 | { |
756 | e.WebTest.AddCommentToResult("Warning: Client Certificate will expire in less than 14 days. Better renew it soon. Expires " + myClientCertAndKey.NotAfter.ToString()); | 883 | e.WebTest.AddCommentToResult("Warning: Client Certificate will expire in less than 14 days. Better renew it soon."); |
757 | } | 884 | } |
758 | 885 | ||
759 | // Check if we have a private key | 886 | // Check if we have a private key |
760 | if (!myClientCertAndKey.HasPrivateKey) | 887 | if (myClientCertAndKey.HasPrivateKey) |
761 | { | 888 | { |
762 | // Cant use it without private key | 889 | if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS PRIVATE KEY in file"); |
763 | e.WebTest.AddCommentToResult("Error: Certificate HAS NO PRIVATE KEY, cannot use it without one."); | ||
764 | return; | ||
765 | } else | ||
766 | { | ||
767 | if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS PRIVATE KEY"); | ||
768 | } | 890 | } |
769 | 891 | ||
770 | // Check that the certificate exists in the cert store | 892 | // Check that the certificate exists in the cert store |
... | @@ -772,31 +894,69 @@ namespace LIL_VSTT_Plugins | ... | @@ -772,31 +894,69 @@ namespace LIL_VSTT_Plugins |
772 | cuStore.Open(OpenFlags.ReadWrite); | 894 | cuStore.Open(OpenFlags.ReadWrite); |
773 | if(cuStore.Certificates.Contains(myClientCertAndKey)) { | 895 | if(cuStore.Certificates.Contains(myClientCertAndKey)) { |
774 | if (pDebug) e.WebTest.AddCommentToResult("Certificate already INSTALLED in Current User Windows Certificate Store"); | 896 | if (pDebug) e.WebTest.AddCommentToResult("Certificate already INSTALLED in Current User Windows Certificate Store"); |
897 | // Try to load the key from store if we dont have it and verify that it belongs to the certificate | ||
898 | if(!myClientCertAndKey.HasPrivateKey) | ||
899 | { | ||
900 | X509Certificate2Collection certCol = cuStore.Certificates.Find(X509FindType.FindByThumbprint, myClientCertAndKey.Thumbprint, false); | ||
901 | if(certCol == null || certCol.Count == 0) | ||
902 | { | ||
903 | e.WebTest.AddCommentToResult("Error: Certificate could not be loaded from store using it's thumbprint which is very strange. Aborting"); | ||
904 | return; | ||
905 | } else | ||
906 | { | ||
907 | if(certCol.Count > 1) | ||
908 | { | ||
909 | if (pDebug) e.WebTest.AddCommentToResult("Certificates thumbprint has more than one match in the Windows User Certificate Store, using the first one."); | ||
910 | } | ||
911 | X509Certificate2 cert = certCol[0]; | ||
912 | if (!cert.HasPrivateKey) | ||
913 | { | ||
914 | e.WebTest.AddCommentToResult("Error: Certificate does not have a corresponding private key in the Windows User Certificate Store. Can not use it for SSL."); | ||
915 | return; | ||
916 | } else | ||
917 | { | ||
918 | if (pDebug) e.WebTest.AddCommentToResult("Certificate was found in Windows User Certificate Store using thumbprint and HAS a corresponding PRIVATE KEY"); | ||
919 | } | ||
920 | } | ||
921 | } | ||
775 | } else | 922 | } else |
776 | { | 923 | { |
777 | if (pDebug) e.WebTest.AddCommentToResult("Certificate is NOT INSTALLED"); | 924 | if (pDebug) e.WebTest.AddCommentToResult("Certificate is NOT INSTALLED"); |
778 | if(pInstallTrusted && myCertTrusted || pInstallUntrusted) | 925 | if (myClientCertAndKey.HasPrivateKey) |
779 | { | 926 | { |
780 | // Try to install certificate | 927 | if (pInstallTrusted && myCertTrusted || pInstallUntrusted) |
781 | if (myCertTrusted || !myCertTrusted) | ||
782 | { | 928 | { |
929 | // Try to install certificate | ||
783 | // Install in user store | 930 | // Install in user store |
784 | try { | 931 | try |
932 | { | ||
785 | myClientCertAndKey.FriendlyName = "VSTT"; | 933 | myClientCertAndKey.FriendlyName = "VSTT"; |
786 | cuStore.Add(myClientCertAndKey); | 934 | cuStore.Add(myClientCertAndKey); |
787 | if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS BEEN INSTALLED in the Current User Windows Certificate Store with Friendly Name: VSTT"); | 935 | if (pDebug) e.WebTest.AddCommentToResult("Certificate HAS BEEN INSTALLED in the Current User Windows Certificate Store with Friendly Name: VSTT"); |
788 | } catch (Exception ex) | 936 | } |
937 | catch (Exception ex) | ||
789 | { | 938 | { |
790 | e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL in the Current User Windows Certificate Store, Message: " + ex.Message); | 939 | e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL in the Current User Windows Certificate Store, Message: " + ex.Message); |
791 | return; | 940 | return; |
792 | } | 941 | } |
793 | } | 942 | } |
943 | else | ||
944 | { | ||
945 | e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL the certificate since you selected NOT to install untrusted certificates."); | ||
946 | return; | ||
947 | } | ||
948 | } else | ||
949 | { | ||
950 | e.WebTest.AddCommentToResult("Error: COULD NOT INSTALL the certificate since the file did not contain a private key and cannot be used without one."); | ||
951 | return; | ||
794 | } | 952 | } |
795 | } | 953 | } |
796 | 954 | ||
797 | // Set the PreRequest method to add the certificate on requests | 955 | // Set the PreRequest method to add the certificate on requests |
798 | haveCert = true; | 956 | haveCert = true; |
799 | if (pDebug) e.WebTest.AddCommentToResult("Certificate will be ADDED TO REQUESTS"); | 957 | if (pDebug) e.WebTest.AddCommentToResult("Certificate will be ADDED TO REQUESTS"); |
958 | sw.Stop(); | ||
959 | if (pDebug) e.WebTest.AddCommentToResult("Certificate processing done in " + sw.ElapsedMilliseconds + "ms"); | ||
800 | } | 960 | } |
801 | 961 | ||
802 | public override void PreRequest(object sender, PreRequestEventArgs e) | 962 | public override void PreRequest(object sender, PreRequestEventArgs e) |
... | @@ -811,7 +971,7 @@ namespace LIL_VSTT_Plugins | ... | @@ -811,7 +971,7 @@ namespace LIL_VSTT_Plugins |
811 | e.WebTest.Context["Client Certificate"] = myClientCertAndKey.Subject; | 971 | e.WebTest.Context["Client Certificate"] = myClientCertAndKey.Subject; |
812 | } else | 972 | } else |
813 | { | 973 | { |
814 | e.WebTest.Context["Client Certificate"] = "No certificate was added"; | 974 | e.WebTest.Context["Client Certificate"] = "No certificate was specified in this request. Windows will try to automatically choose an installed client certificate if requested by the server."; |
815 | } | 975 | } |
816 | } | 976 | } |
817 | } | 977 | } | ... | ... |
-
Please register or sign in to post a comment