-
Notifications
You must be signed in to change notification settings - Fork 7.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add test WebListener module and tests for Web Cmdlet Certificate Authentication #4622
Conversation
@markekraus, |
@markekraus See as we prepare TestExe.csproj Line 780 in b3e7fd3
|
@iSazonov Thanks, I'll see how I can fit this one in into on another note, the edit: I assume i can modify |
Yes, it is |
@markekraus Can we replace our |
@iSazonov In theory we could move all of the web tests to this (with some slight modifications, such as adding a Non-HTTPS listener to it), but, it would mean rewriting the existing features of HttpListener into ASP.NET. Or doing something that allowed PowerShell to manage the business logic. I was already considering switching the |
@markekraus Please keep the PR as small as possibly - it is better to make cleanups later. If we can move to the new application from |
@iSazonov Should I roll back the baddssl changes? Aside from those, the rest of the changes on this PR are interdependent. Can you restart the failed Travis CI test? it failed an unrelated test due to httpbin not being unavailable. |
@markekraus Main rule is to keep PR as small as possible and don't add unneeded changes. |
39d21a4
to
aea9177
Compare
{ | ||
param([int]$Port) | ||
$initTimeoutSeconds = 15 | ||
$projectRootPath = Resolve-Path (Join-Path $PSScriptRoot "\..\..\..\..\") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function Publish-PSTestTools
already added the path into PATH environment variable - so we don't need build the full path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov That is used to pass to the Job scope where the path changes made by the Publish-PSTestTools
are not available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not available? I wonder. Do you check? Usually env variables is inherited in sub process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov it turns out you were right. I was able to adjust this and pull the parent path from the command.
@@ -296,6 +296,46 @@ function ExecuteWebRequest | |||
return $result | |||
} | |||
|
|||
# Starts the ClientCertificateCheck listener | |||
function StartClientCertificateCheck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe Start-ClientCertificateCheck
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov I tried to match the other private functions in the test already. I originally wanted to use Start-ClientCertificateCheck
, but thought I would get a note for not matching the others. I can't win :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thoughts was about HttpListener style. Maybe somebody else comment this too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we move the function in HelpersCommon.psm1
it is ok rename it to Start-ClientCertificateCheck
.
$initCompleteMessage = 'Now listening on' | ||
|
||
$timeOut = (get-date).AddSeconds($initTimeoutSeconds) | ||
$Job = Start-Job { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we'll use the application in multiple test files we should save time - run it once and check if it already is running.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov I'm open to suggestions on how to implement that. As it stands now, it starts in less than a second. I was just following the current paradigm for how Start-HttpListener
is used in the tests. Currently, StartClientCertificateCheck
is called once at the beginning of the Invoke-WebReqeust
and once at the begining of the Invoke-RestMethod
tests. it is stopped at the end of the tests for those describe blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI VMs is very-very slow. If locally you see no start delay then on CI it is possible the delay will be large. That's why I think it's best to use one tool (remove HttpListener) and run it only once.
how to implement that
Call Invoke-WebRequest and analyze the response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov As for the less than 1 second start time, I was referring to the start time within the CI VMs. The majority of the overhead is in the build stage, which has now been offloaded to Publih-PSTestTools
. I just have the wait
in there to prevent a race condition between the job delay and the first test that requires the resource.
I think the current method for ensuring the process is running is fine. It makes sense to use Invoke-WebRequest
for Start-HttpListener
as there is no other means for ensuring the server is live. But with this I have access to the console output from the server itself saying it is live.
What I was asking about was how to implement it to be running for multiple tests. What mechanism would start it? Start-PSPester
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want run tests without Start-PSPester
as .\file.test.ps1
.
I think we should move the code (1-function to start the app and 2-function check that it is running) in HelpersCommon
- in first file we detect that the app is not running and start it, in second file we detect that the app is already running and use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov Ah. I see. Since it may be decided to move all tests to this, would it make sense to create a module of its own, similar to HttpLitsener
? If we want to have logic detecting if it is running or not and with possibility of running multiple of these on different ports, it might warrant a class
definition to contain the port and job for a specific listener and a module variable to track started listeners. It might also warrant a rename of the app to something more generic than ClientCertificateCheck
For this PR, to keep it small, Moving the start and stop functions to HelpersCommon and renaming them proper Verb-Noun
would be a start.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could convert the app to binary module (and rename it) and get benefits from autoloading. It may require more effort, but it seems worth.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how that would work. This is an executable binary. I've never seen that kind of thing included in a binary module. I'm not saying it's impossible, just that it is beyond me how to do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scripts can still autoload. I think the optimization of converting it to an assembly module can be filed as an issue and let @markekraus prove the concept. Unless someone has time to work with him now on writing an assembly based module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TravisEz13 and @iSazonov Ok, since it appears we may head in the direction of using this for more tests than just the certificate authentication, I have renamed the app HttpsListener
and added an HttpsListener
module to house the Start-
, Stop
, and Get-
commands. There is a module script variable to track the running listeners so if it is already listening on the requested port the running listener is returned. To keep it always running, I have removed the stop commands form the AfterAll
blocks. I think that incorporates everything discussed here except making it a binary module.
} | ||
|
||
# Stops the ClientCertificateCheck listener | ||
function StopClientCertificateCheck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe Stop-ClientCertificateCheck
.
If we'll use the application in multiple test files we should it once and shouldn't stop it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov same as with Start-ClientCertificateCheck
@@ -1183,6 +1200,26 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" { | |||
|
|||
#endregion charset encoding tests | |||
|
|||
#region Client Certificate Authentication |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better use Context {}
. I suggest don't mix HTTP and HTTPS tests and put HTTPS test in separate Context {}
while we use different tools for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov I think we should maybe do that as a separate PR to clean up all of the current HTTPS tests too. Or, I could build the contexts now and move the other HTTPS into them as we update the tests (i.e. tests referencing badssl.com).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have to edit all HTTPS tests so I believe it is ok to add Context in the PR.
But if you want I agree to follow the file's pattern now and make cleanups later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll do a separate followup PR to this one under the guise of HTTPS test adjustments. moving all the tests (including those not touched in this PR) that depend on HTTPS to a single context in each cmdlet section and switching the URL to the new app... assuming this PR get's approved and that is the way we want to go with this.
Issuer: @Model.CertIssuer | ||
Issuer Name: @Model.CertIssuerName | ||
Not After: @Model.CertNotAfter | ||
Not Before: @Model.CertNotBefore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GitHub mark absent new line at EOF as error and suggest GitHub community to fix it. We already fixed most of our files in the repo. So please add new line at EOF in all files you change until it is really need empty file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov will fix
@@ -0,0 +1 @@ | |||
@RenderBody() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov will fix
@@ -0,0 +1 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov it lookslike I don't need this file. i will remove it.
|
||
The `ClientCertificateCheck.dll` takes 3 arguments: the path to the Server Certificate, the Server Certificate Password, and the TCP Port to bind on. | ||
|
||
# Run As a Docker Container |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it actual?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you asking here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. If it is not supported on Mac it make sense to remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov removed
@@ -0,0 +1,18 @@ | |||
# Stage 1: Build the Application |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov If we don't want to try this as a docker container, then no. I can remove the docker parts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is not supported on Mac it make sense to remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov I will remove it and .dockerignore as well as the docker info in the readme.
ce4296a
to
9796ce2
Compare
Hmm... The macOS Build is "passing" but in actuality it is hitting an unhandled exception and exiting without property saying it has failed. https://travis-ci.org/PowerShell/PowerShell/jobs/266586350#L8280 Edit: It looks like macOS has a service that binds to port 8443. I will try switching to port 8083. The bigger mystery to me is how this managed to terminate all tests without pester complaining about it and how this managed to bubble up from a Job process into the parent process and then up to pester. Edit 2: switching the port to 8083 fixed the issue in the macOS VM. |
throw 'Container did not start before the timeout was reached.' | ||
$Job | Stop-Job -PassThru | Receive-Job | ||
$Job | Remove-Job | ||
throw 'Job did not start before the timeout was reached.' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- We should make the message more clear - maybe "ClientCertificateCheck is not started."
- Since we're still throw maybe move the throw into do-while and make the cycle unlimited:
do
Start-Sleep -Milliseconds 100
$containerStatus = $Job.ChildJobs[0].Output | Out-String
if ($containerStatus -match $initCompleteMessage)
{
# Application started.
break;
}
if (get-date) -gt $timeOut)
{
throw "ClientCertificateCheck is not started."
}
while ($true)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov I will update the throw message.
On the other suggestion, I'm not sure it makes sense to introduce flow logic inside a do
block when the while
is intended to manage the do
's exit. I have never been a fan of while ($true)
Is there something wrong with the current logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, only my sample looks more clear for me 😄 because we replace two code blocks with single logical block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😆 Ok so it's just a difference of style. I think of it as 2 separate actions, 1) waiting for initialization with timeout and 2) cleanup and error reporting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
} | ||
# Wait until the app is running or until the initTimeoutSeconds have been reached | ||
do | ||
{ | ||
Start-Sleep -Seconds 1 | ||
Start-Sleep -Milliseconds 100 | ||
$containerStatus = $Job.ChildJobs[0].Output | Out-String |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use container - please rename the variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov I will change it to $initStatus
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
@@ -296,6 +296,46 @@ function ExecuteWebRequest | |||
return $result | |||
} | |||
|
|||
# Starts the ClientCertificateCheck listener | |||
function StartClientCertificateCheck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we move the function in HelpersCommon.psm1
it is ok rename it to Start-ClientCertificateCheck
.
} | ||
|
||
#endregion Certificate Authentication Tests | ||
#edregion Client Certificate Authentication |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@markekraus slight typo on endregion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@GavinEke Thanks. I will correct that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
It looks like the AppVeyor build beta.5-4786 failed due to temporary issue with |
@markekraus It seems I haven't permissions. So you can "rename" last commit and push - it will be restart CI jobs. |
1d09ecc
to
1cbab33
Compare
@iSazonov Now It looks like https://dotnetcli.azureedge.net/dotnet/Sdk/2.0.0/dotnet-dev-osx-x64.2.0.0.tar.gz has disappeared. edit: rerunning the CI was successful, but Travis CI has not updated the status in the PR due to some issues. |
1cbab33
to
1dc95f3
Compare
@iSazonov I'm going to assign this to you as you've been involved while @adityapatwardhan is taking time off |
@SteveL-MSFT We need get approve from test area experts because of significant changes in the PR - please ping anybody if @adityapatwardhan is busy. |
FunctionsToExport = @( | ||
'Start-WebListener' | ||
'Stop-WebListener' | ||
'Get-WebListener' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'G' after 'S' ? 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are "S" class cmdlets... higher priority 😆. I will fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
[OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2])] | ||
param () | ||
process { | ||
$parentPath = Split-Path -parent (get-command WebListener).Path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant we can $MyInvocation.MyCommand.Module.ModuleBase
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ClientCert.pfx is in the WebListener
published directory. If I switch to $MyInvocation.MyCommand.Module.ModuleBase
I would need to move it to the WebListener
module folder. Is that what you would suggest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov for clarity, are you asking me to move it and update the code or leave it as is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree to move ClientCert.pfx to module folder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
* Enum was not used * GetStatus() was not accessing the correct property chain * Added -Test param to make URL generation smoother in test code and to fix double / issues
e807b5b
to
89cabf5
Compare
* Https when it matters. * Expand property... not exclude.. * Remove superfluous and outdated ToString() override
89cabf5
to
2c3cbd3
Compare
* Move the cert * Adjust Get-WebListenerClientCertificate * Remove cert from csproj * ActionResult -> JsonResult (was mistakenly left as ActionResult during testing)..
5a0ff37
to
ced727c
Compare
{ | ||
return $This.Job.JobStateInfo.State | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
Hashtable output = new Hashtable | ||
{ | ||
{"Status", "FAILED"} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still see ';'.
{"IssuerName" , HttpContext.Connection.ClientCertificate.IssuerName.Name}, | ||
{"NotAfter" , HttpContext.Connection.ClientCertificate.NotAfter}, | ||
{"NotBefore" , HttpContext.Connection.ClientCertificate.NotBefore} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still see ';'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Semicolon has to be there. When I tried to remove it I get compile errors saying it is missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. Closed.
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Include="ServerCert.pfx" CopyToPublishDirectory="PreserveNewest" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe put it near client certificate and remove the group?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
@markekraus Thank you for your patience and your speedy work. LGTM. |
@iSazonov Thanks for being patient with me too and for all your suggestions! |
* Move cert * Upate csproj * Update module * Add/Update README.md's CI Retest.
4664b1c
to
b93674e
Compare
@iSazonov can you formally approve? |
It will be good to get a review from test area experts. |
Thanks! I'll merge on next week. |
@markekraus Many thanks for great work! The PR was merged and way is opened for follow work:
Please move the points to a tracking Issue. |
@markekraus #2504 is good. Please leave a message about WebListener there for cross reference. And if you want work on this you should ask Travis there. |
Closes #4609 and replaces tests for #4546.
This test uses an ASP.NET Core 2.0 app to run a simple service to echo back the client certificate details if one was provided. If a client cert is not provided, the details are empty and the status reports FAILED. The app runs in a Job.